Completed
Push — 0.5.3 ( 51e496...fe5108 )
by Felipe A.
16:24
created

PluginAction.__call__()   B

Complexity

Conditions 6

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 8
c 0
b 0
f 0
cc 6
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
import re
5
import sys
6
import os
7
import os.path
8
import argparse
9
import warnings
10
11
import flask
12
13
from . import app, compat
14
from .__meta__ import __app__ as app_name
15
from .compat import PY_LEGACY, getdebug, get_terminal_size
16
from .transform.glob import translate
17
18
19
class HelpFormatter(argparse.RawTextHelpFormatter):
20
    def __init__(self, prog, indent_increment=2, max_help_position=24,
21
                 width=None):
22
        if width is None:
23
            width = get_terminal_size().columns - 2
24
        super(HelpFormatter, self).__init__(
25
            prog, indent_increment, max_help_position, width)
26
27
28
class PluginAction(argparse.Action):
29
    def __call__(self, parser, namespace, value, option_string=None):
30
        warned = '%s_warning' % self.dest
31
        if ',' in value and not getattr(namespace, warned, False):
32
            setattr(namespace, warned, True)
33
            warnings.warn(
34
                'Comma-separated --plugin value is deprecated, '
35
                'use multiple --plugin options instead.'
36
                )
37
        values = value.split(',')
38
        prev = getattr(namespace, self.dest, None)
39
        if isinstance(prev, list):
40
            values = prev + [p for p in values if p not in prev]
41
        setattr(namespace, self.dest, values)
42
43
44
class ArgParse(argparse.ArgumentParser):
45
    default_directory = os.path.abspath(compat.getcwd())
46
    default_host = os.getenv('BROWSEPY_HOST', '127.0.0.1')
47
    default_port = os.getenv('BROWSEPY_PORT', '8080')
48
    plugin_action_class = PluginAction
49
50
    defaults = {
51
        'prog': app_name,
52
        'formatter_class': HelpFormatter,
53
        'description': 'description: starts a %s web file browser' % app_name
54
        }
55
56
    def __init__(self, sep=os.sep):
57
        super(ArgParse, self).__init__(**self.defaults)
58
        self.add_argument(
59
            'host', nargs='?',
60
            default=self.default_host,
61
            help='address to listen (default: %(default)s)')
62
        self.add_argument(
63
            'port', nargs='?', type=int,
64
            default=self.default_port,
65
            help='port to listen (default: %(default)s)')
66
        self.add_argument(
67
            '--directory', metavar='PATH', type=self._directory,
68
            default=self.default_directory,
69
            help='serving directory (default: current path)')
70
        self.add_argument(
71
            '--initial', metavar='PATH',
72
            type=lambda x: self._directory(x) if x else None,
73
            help='default directory (default: same as --directory)')
74
        self.add_argument(
75
            '--removable', metavar='PATH', type=self._directory,
76
            default=None,
77
            help='base directory allowing remove (default: none)')
78
        self.add_argument(
79
            '--upload', metavar='PATH', type=self._directory,
80
            default=None,
81
            help='base directory allowing upload (default: none)')
82
        self.add_argument(
83
            '--exclude', metavar='PATTERN',
84
            action='append',
85
            default=[],
86
            help='exclude paths by pattern (multiple)')
87
        self.add_argument(
88
            '--exclude-from', metavar='PATH', type=self._file,
89
            action='append',
90
            default=[],
91
            help='exclude paths by pattern file (multiple)')
92
        self.add_argument(
93
            '--plugin', metavar='MODULE',
94
            action=self.plugin_action_class,
95
            default=[],
96
            help='load plugin module (multiple)')
97
        self.add_argument(
98
            '--debug', action='store_true',
99
            help=argparse.SUPPRESS)
100
101
    def _path(self, arg):
102
        if PY_LEGACY and hasattr(sys.stdin, 'encoding'):
103
            encoding = sys.stdin.encoding or sys.getdefaultencoding()
104
            arg = arg.decode(encoding)
105
        return os.path.abspath(arg)
106
107
    def _file(self, arg):
108
        path = self._path(arg)
109
        if os.path.isfile(path):
110
            return path
111
        self.error('%s is not a valid file' % arg)
112
113
    def _directory(self, arg):
114
        path = self._path(arg)
115
        if os.path.isdir(path):
116
            return path
117
        self.error('%s is not a valid directory' % arg)
118
119
120
def create_exclude_fnc(patterns, base):
121
    if patterns:
122
        regex = '|'.join(
123
            translate(pattern, base=base)
124
            for pattern in patterns
125
            )
126
        return re.compile(regex).search
127
    return None
128
129
130
def collect_exclude_patterns(paths):
131
    patterns = []
132
    for path in paths:
133
        with open(path, 'r') as f:
134
            for line in f:
135
                line = line.split('#')[0].strip()
136
                if line:
137
                    patterns.append(line)
138
    return patterns
139
140
141
def main(argv=sys.argv[1:], app=app, parser=ArgParse, run_fnc=flask.Flask.run):
142
    plugin_manager = app.extensions['plugin_manager']
143
    args = plugin_manager.load_arguments(argv, parser())
144
    patterns = args.exclude + collect_exclude_patterns(args.exclude_from)
145
    if args.debug:
146
        os.environ['DEBUG'] = 'true'
147
    app.config.update(
148
        directory_base=args.directory,
149
        directory_start=args.initial or args.directory,
150
        directory_remove=args.removable,
151
        directory_upload=args.upload,
152
        plugin_modules=args.plugin,
153
        exclude_fnc=create_exclude_fnc(patterns, args.directory)
154
        )
155
    plugin_manager.reload()
156
    run_fnc(
157
        app,
158
        host=args.host,
159
        port=args.port,
160
        debug=getdebug(),
161
        use_reloader=False,
162
        threaded=True
163
        )
164
165
166
if __name__ == '__main__':
167
    main()
168