1
1
""" Parse arguments from command line and configuration files. """
2
-
3
2
import fnmatch
3
+ import sys
4
4
from os import getcwd , path
5
5
from re import compile as re
6
6
7
7
import logging
8
- from argparse import ArgumentParser , Namespace as Options
8
+ from argparse import ArgumentParser
9
9
10
10
from . import version
11
- from .core import LOGGER , STREAM
12
11
from .libs .inirama import Namespace
13
12
from .lint .extensions import LINTERS
14
13
15
14
15
+ # Setup a logger
16
+ LOGGER = logging .getLogger ('pylama' )
17
+ LOGGER .propagate = False
18
+ STREAM = logging .StreamHandler (sys .stdout )
19
+ LOGGER .addHandler (STREAM )
20
+
16
21
#: A default checkers
17
22
DEFAULT_LINTERS = 'pep8' , 'pyflakes' , 'mccabe'
18
23
19
24
CURDIR = getcwd ()
20
25
DEFAULT_INI_PATH = path .join (CURDIR , 'pylama.ini' )
21
26
22
27
23
- def parse_options (
24
- args = None , async = False , select = '' , ignore = '' , linters = DEFAULT_LINTERS ,
25
- options = DEFAULT_INI_PATH ):
28
+ class _Default (object ):
29
+
30
+ def __init__ (self , value = None ):
31
+ self .value = value
32
+
33
+ def __str__ (self ):
34
+ return str (self .value )
35
+
36
+ __repr__ = lambda s : "<_Default [%s]>" % s .value
37
+
38
+
39
+ def split_csp_str (s ):
40
+ """ Split commaseparated string.
41
+
42
+ :returns: list of splitted values
43
+
44
+ """
45
+ if isinstance (s , (list , tuple )):
46
+ return s
47
+ return list (set (i for i in s .strip ().split (',' ) if i ))
48
+
49
+
50
+ def parse_linters (linters ):
51
+ """ Initialize choosen linters.
52
+
53
+ :returns: list of inited linters
54
+
55
+ """
56
+ result = list ()
57
+ for name in split_csp_str (linters ):
58
+ linter = LINTERS .get (name )
59
+ if linter :
60
+ result .append ((name , linter ))
61
+ else :
62
+ logging .warn ("Linter `%s` not found." , name )
63
+ return result
64
+
65
+
66
+ PARSER = ArgumentParser (description = "Code audit tool for python." )
67
+ PARSER .add_argument (
68
+ "path" , nargs = '?' , default = _Default (CURDIR ),
69
+ help = "Path on file or directory." )
70
+
71
+ PARSER .add_argument (
72
+ "--verbose" , "-v" , action = 'store_true' , help = "Verbose mode." )
73
+
74
+ PARSER .add_argument ('--version' , action = 'version' ,
75
+ version = '%(prog)s ' + version )
76
+
77
+ PARSER .add_argument (
78
+ "--format" , "-f" , default = _Default ('pep8' ), choices = ['pep8' , 'pylint' ],
79
+ help = "Error format." )
80
+
81
+ PARSER .add_argument (
82
+ "--select" , "-s" , default = _Default ('' ), type = split_csp_str ,
83
+ help = "Select errors and warnings. (comma-separated)" )
84
+
85
+
86
+ PARSER .add_argument (
87
+ "--linters" , "-l" , default = _Default (',' .join (DEFAULT_LINTERS )),
88
+ type = parse_linters , help = (
89
+ "Select linters. (comma-separated). Choices are %s."
90
+ % ',' .join (s for s in LINTERS .keys ())
91
+ ))
92
+
93
+ PARSER .add_argument (
94
+ "--ignore" , "-i" , default = _Default ('' ), type = split_csp_str ,
95
+ help = "Ignore errors and warnings. (comma-separated)" )
96
+
97
+ PARSER .add_argument (
98
+ "--skip" , default = _Default ('' ),
99
+ type = lambda s : [re (fnmatch .translate (p )) for p in s .split (',' ) if p ],
100
+ help = "Skip files by masks (comma-separated, Ex. */messages.py)" )
101
+
102
+ PARSER .add_argument ("--report" , "-r" , help = "Filename for report." )
103
+ PARSER .add_argument (
104
+ "--hook" , action = "store_true" , help = "Install Git (Mercurial) hook." )
105
+
106
+ PARSER .add_argument (
107
+ "--async" , action = "store_true" ,
108
+ help = "Enable async mode. Usefull for checking a lot of files. "
109
+ "Dont supported with pylint." )
110
+
111
+ PARSER .add_argument (
112
+ "--options" , "-o" , default = _Default (DEFAULT_INI_PATH ),
113
+ help = "Select configuration file. By default is '<CURDIR>/pylama.ini'" )
114
+
115
+
116
+ ACTIONS = dict ((a .dest , a ) for a in PARSER ._actions )
117
+
118
+
119
+ def parse_options (args = None , ** overrides ): # noqa
26
120
""" Parse options from command line and configuration files.
27
121
28
122
:return argparse.Namespace:
29
123
30
124
"""
31
- # Parse args from command string
32
- parser = get_parser ()
33
- actions = dict ((a .dest , a ) for a in parser ._actions )
34
- options = Options (
35
- async = _Default (async ), format = _Default ('pep8' ),
36
- select = _Default (select ), ignore = _Default (ignore ),
37
- report = _Default (None ), verbose = _Default (False ),
38
- linters = _Default (',' .join (linters )), options = _Default (options ))
125
+ if args is None :
126
+ args = []
39
127
40
- if not args is None :
41
- options = parser .parse_args (args )
128
+ # Parse args from command string
129
+ options = PARSER .parse_args (args )
42
130
43
131
# Parse options from ini file
44
- config = get_config (str (options .options ))
132
+ cfg = get_config (str (options .options ))
45
133
46
134
# Compile options from ini
47
- for k , v in config .default .items ():
48
- value = getattr (options , k , _Default (None ))
49
- if not isinstance (value , _Default ):
50
- continue
51
-
52
- action = actions .get (k )
135
+ for k , v in cfg .default .items ():
53
136
LOGGER .info ('Find option %s (%s)' , k , v )
54
- name , value = action .dest , action .type (v )\
55
- if callable (action .type ) else v
56
- if action .const :
57
- value = bool (int (value ))
58
- setattr (options , name , value )
137
+ passed_value = getattr (options , k , _Default ())
138
+ if isinstance (passed_value , _Default ):
139
+ setattr (options , k , _Default (v ))
140
+
141
+ # Override options
142
+ for k , v in overrides .items ():
143
+ passed_value = getattr (options , k , _Default ())
144
+ if isinstance (passed_value , _Default ):
145
+ setattr (options , k , _Default (v ))
59
146
60
147
# Postprocess options
61
148
opts = dict (options .__dict__ .items ())
62
149
for name , value in opts .items ():
63
150
if isinstance (value , _Default ):
64
- action = actions .get (name )
65
- if action and callable (action .type ):
66
- value .value = action .type (value .value )
67
-
68
- setattr (options , name , value .value )
151
+ setattr (options , name , process_value (name , value .value ))
69
152
70
153
# Parse file related options
71
154
options .file_params = dict ()
72
155
options .linter_params = dict ()
73
- for k , s in config .sections .items ():
74
- if k == config .default_section :
156
+ for k , s in cfg .sections .items ():
157
+ if k == cfg .default_section :
75
158
continue
76
159
if k in LINTERS :
77
160
options .linter_params [k ] = dict (s )
@@ -85,84 +168,19 @@ def parse_options(
85
168
return options
86
169
87
170
88
- def setup_logger (options ):
89
- """ Setup logger with options. """
90
-
91
- LOGGER .setLevel (logging .INFO if options .verbose else logging .WARN )
92
- if options .report :
93
- LOGGER .removeHandler (STREAM )
94
- LOGGER .addHandler (logging .FileHandler (options .report , mode = 'w' ))
95
- LOGGER .info ('Try to read configuration from: ' + options .options )
96
-
171
+ def process_value (name , value ):
172
+ """ Compile option value. """
173
+ action = ACTIONS .get (name )
174
+ if not action :
175
+ return value
97
176
98
- def get_parser ( ):
99
- """ Make command parser for pylama.
177
+ if callable ( action . type ):
178
+ return action . type ( value )
100
179
101
- :return ArgumentParser:
180
+ if action .const :
181
+ return bool (int (value ))
102
182
103
- """
104
- split_csp_str = lambda s : list (
105
- set (i for i in s .strip ().split (',' ) if i ))
106
-
107
- parser = ArgumentParser (description = "Code audit tool for python." )
108
- parser .add_argument (
109
- "path" , nargs = '?' , default = _Default (CURDIR ),
110
- help = "Path on file or directory." )
111
-
112
- parser .add_argument (
113
- "--verbose" , "-v" , action = 'store_true' , help = "Verbose mode." )
114
-
115
- parser .add_argument ('--version' , action = 'version' ,
116
- version = '%(prog)s ' + version )
117
-
118
- parser .add_argument (
119
- "--format" , "-f" , default = _Default ('pep8' ), choices = ['pep8' , 'pylint' ],
120
- help = "Error format." )
121
-
122
- parser .add_argument (
123
- "--select" , "-s" , default = _Default ('' ), type = split_csp_str ,
124
- help = "Select errors and warnings. (comma-separated)" )
125
-
126
- def parse_linters (csp_str ):
127
- result = list ()
128
- for name in split_csp_str (csp_str ):
129
- linter = LINTERS .get (name )
130
- if linter :
131
- result .append ((name , linter ))
132
- else :
133
- logging .warn ("Linter `%s` not found." , name )
134
- return result
135
-
136
- parser .add_argument (
137
- "--linters" , "-l" , default = _Default (',' .join (DEFAULT_LINTERS )),
138
- type = parse_linters , help = (
139
- "Select linters. (comma-separated). Choices are %s."
140
- % ',' .join (s for s in LINTERS .keys ())
141
- ))
142
-
143
- parser .add_argument (
144
- "--ignore" , "-i" , default = _Default ('' ), type = split_csp_str ,
145
- help = "Ignore errors and warnings. (comma-separated)" )
146
-
147
- parser .add_argument (
148
- "--skip" , default = _Default ('' ),
149
- <
10000
span class=pl-s1>type= lambda s : [re (fnmatch .translate (p )) for p in s .split (',' ) if p ],
150
- help = "Skip files by masks (comma-separated, Ex. */messages.py)" )
151
-
152
- parser .add_argument ("--report" , "-r" , help = "Filename for report." )
153
- parser .add_argument (
154
- "--hook" , action = "store_true" , help = "Install Git (Mercurial) hook." )
155
-
156
- parser .add_argument (
157
- "--async" , action = "store_true" ,
158
- help = "Enable async mode. Usefull for checking a lot of files. "
159
- "Dont supported with pylint." )
160
-
161
- parser .add_argument (
162
- "--options" , "-o" , default = _Default (DEFAULT_INI_PATH ),
163
- help = "Select configuration file. By default is '<CURDIR>/pylama.ini'" )
164
-
165
- return parser
183
+ return value
166
184
167
185
168
186
def get_config (ini_path = DEFAULT_INI_PATH ):
@@ -178,16 +196,10 @@ def get_config(ini_path=DEFAULT_INI_PATH):
178
196
return config
179
197
180
198
181
- class _Default (object ):
182
-
183
- def __init__ (self , value ):
184
- self .value = value
185
-
186
- def __getattr__ (self , name ):
187
- return getattr (self .value , name )
188
-
189
- def __str__ (self ):
190
- return str (self .value )
191
-
192
-
193
- # lint_ignore=R0914,W0212,E1103,C901
199
+ def setup_logger (options ):
200
+ """ Setup logger with options. """
201
+ LOGGER .setLevel (logging .INFO if options .verbose else logging .WARN )
202
+ if options .report :
203
+ LOGGER .removeHandler (STREAM )
204
+ LOGGER .addHandler (logging .FileHandler (options .report , mode = 'w' ))
205
+ LOGGER .info ('Try to read configuration from: ' + options .options )