11""" Parse arguments from command line and configuration files. """
2-
32import fnmatch
3+ import sys
44from os import getcwd , path
55from re import compile as re
66
77import logging
8- from argparse import ArgumentParser , Namespace as Options
8+ from argparse import ArgumentParser
99
1010from . import version
11- from .core import LOGGER , STREAM
1211from .libs .inirama import Namespace
1312from .lint .extensions import LINTERS
1413
1514
15+ # Setup a logger
16+ LOGGER = logging .getLogger ('pylama' )
17+ LOGGER .propagate = False
18+ STREAM = logging .StreamHandler (sys .stdout )
19+ LOGGER .addHandler (STREAM )
20+
1621#: A default checkers
1722DEFAULT_LINTERS = 'pep8' , 'pyflakes' , 'mccabe'
1823
1924CURDIR = getcwd ()
2025DEFAULT_INI_PATH = path .join (CURDIR , 'pylama.ini' )
2126
2227
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_I
38BA
NI_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
26120 """ Parse options from command line and configuration files.
27121
28122 :return argparse.Namespace:
29123
30124 """
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 = []
39127
40- if not args is None :
41- options = parser .parse_args (args )
128+ # Parse args from command string
129+ options = PARSER .parse_args (args )
42130
43131 # Parse options from ini file
44- config = get_config (str (options .options ))
132+ cfg = get_config (str (options .options ))
45133
46134 # 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 ():
53136 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 ))
59146
60147 # Postprocess options
61148 opts = dict (options .__dict__ .items ())
62149 for name , value in opts .items ():
63150 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 ))
69152
70153 # Parse file related options
71154 options .file_params = dict ()
72155 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 :
75158 continue
76159 if k in LINTERS :
77160 options .linter_params [k ] = dict (s )
@@ -85,84 +168,19 @@ def parse_options(
85168 return options
86169
87170
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
97176
98- def get_parser ( ):
99- """ Make command parser for pylama.
177+ if callable ( action . type ):
178+ return action . type ( value )
100179
101- :return ArgumentParser:
180+ if action .const :
181+ return bool (int (value ))
102182
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- 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
166184
167185
168186def get_config (ini_path = DEFAULT_INI_PATH ):
@@ -178,16 +196,10 @@ def get_config(ini_path=DEFAULT_INI_PATH):
178196 return config
179197
180198
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 )
0 commit comments