20
20
from itertools import takewhile , dropwhile , chain
21
21
from optparse import OptionParser
22
22
from re import compile as re
23
+ try : # Python 3.x
24
+ from ConfigParser import RawConfigParser
25
+ except ImportError : # Python 2.x
26
+ from configparser import RawConfigParser
23
27
24
28
log = logging .getLogger ()
25
- log .addHandler (logging .StreamHandler () )
29
+ log .setLevel (logging .DEBUG )
26
30
27
31
28
32
try :
@@ -49,6 +53,7 @@ def next(obj, default=nothing):
49
53
__version__ = '0.3.3-alpha'
50
54
__all__ = ('check' , 'collect' )
51
55
56
+ PROJECT_CONFIG = ('setup.cfg' , 'tox.ini' , '.pep257' )
52
57
53
58
humanize = lambda string : re (r'(.)([A-Z]+)' ).sub (r'\1 \2' , string ).lower ()
54
59
is_magic = lambda name : name .startswith ('__' ) and name .endswith ('__' )
@@ -403,9 +408,11 @@ def __lt__(self, other):
403
408
return (self .filename , self .line ) < (other .filename , other .line )
404
409
405
410
406
- def parse_options ():
411
+ def get_option_parser ():
407
412
parser = OptionParser (version = __version__ ,
408
413
usage = 'Usage: pep257 [options] [<file|dir>...]' )
414
+ parser .config_options = ('explain' , 'source' , 'ignore' , 'match' ,
415
+ 'match-dir' , 'debug' , 'verbose' )
409
416
option = parser .add_option
410
417
option ('-e' , '--explain' , action = 'store_true' ,
411
418
help = 'show explanation of each error' )
@@ -425,7 +432,9 @@ def parse_options():
425
432
"all dirs that don't start with a dot" )
426
433
option ('-d' , '--debug' , action = 'store_true' ,
427
434
help = 'print debug information' )
428
- return parser .parse_args ()
435
+ option ('-v' , '--verbose' , action = 'store_true' ,
436
+ help = 'print status information' )
437
+ return parser
429
438
430
439
431
440
def collect (names , match = lambda name : True , match_dir = lambda name : True ):
@@ -463,6 +472,7 @@ def check(filenames, ignore=()):
463
472
464
473
"""
465
474
for filename in filenames :
475
+ log .info ('Checking file %s.' , filename )
466
476
try :
467
477
with open (filename ) as file :
468
478
source = file .read ()
@@ -476,15 +486,84 @@ def check(filenames, ignore=()):
476
486
yield SyntaxError ('invalid syntax in file %s' % filename )
477
487
478
488
479
- def main (options , arguments ):
489
+ def get_options (args , opt_parser ):
490
+ config = RawConfigParser ()
491
+ parent = tail = args and os .path .abspath (os .path .commonprefix (args ))
492
+ while tail :
493
+ for fn in PROJECT_CONFIG :
494
+ full_path = os .path .join (parent , fn )
495
+ if config .read (full_path ):
496
+ log .info ('local configuration: in %s.' , full_path )
497
+ break
498
+ parent , tail = os .path .split (parent )
499
+
500
+ new_options = None
501
+ if config .has_section ('pep257' ):
502
+ option_list = dict ([(o .dest , o .type or o .action )
503
+ for o in opt_parser .option_list ])
504
+
505
+ # First, read the default values
506
+ new_options , _ = opt_parser .parse_args ([])
507
+
508
+ # Second, parse the configuration
509
+ pep257_section = 'pep257'
510
+ for opt in config .options (pep257_section ):
511
+ if opt .replace ('_' , '-' ) not in opt_parser .config_options :
512
+ print ("Unknown option '{}' ignored" .format (opt ))
513
+ continue
514
+ normalized_opt = opt .replace ('-' , '_' )
515
+ opt_type = option_list [normalized_opt ]
516
+ if opt_type in ('int' , 'count' ):
517
+ value = config .getint (pep257_section , opt )
518
+ elif opt_type == 'string' :
519
+ value = config .get (pep257_section , opt )
520
+ else :
521
+ assert opt_type in ('store_true' , 'store_false' )
522
+ value = config .getboolean (pep257_section , opt )
523
+ setattr (new_options , normalized_opt , value )
524
+
525
+ # Third, overwrite with the command-line options
526
+ options , _ = opt_parser .parse_args (values = new_options )
527
+ log .debug ("options: %s" , options )
528
+ return options
529
+
530
+
531
+ def setup_stream_handler (options ):
532
+ if log .handlers :
533
+ for handler in log .handlers :
534
+ log .removeHandler (handler )
535
+ stream_handler = logging .StreamHandler (sys .stdout )
536
+ stream_handler .setLevel (logging .WARNING )
480
537
if options .debug :
481
- log .setLevel (logging .DEBUG )
482
- log .debug ("starting pep257 in debug mode." )
483
- Error .explain = options .explain
484
- Error .source = options .source
538
+ stream_handler .setLevel (logging .DEBUG )
539
+ elif options .verbose :
540
+ stream_handler .setLevel (logging .INFO )
541
+ else :
542
+ stream_handler .setLevel (logging .WARNING )
543
+ log .addHandler (stream_handler )
544
+
545
+
546
+ def main ():
547
+ opt_parser = get_option_parser ()
548
+ # setup the logger before parsing the config file, so that command line
549
+ # arguments for debug / verbose will be printed.
550
+ options , arguments = opt_parser .parse_args ()
551
+ setup_stream_handler (options )
552
+ # We parse the files before opening the config file, since it changes where
553
+ # we look for the file.
554
+ options = get_options (arguments , opt_parser )
555
+ # Setup the handler again with values from the config file.
556
+ setup_stream_handler (options )
557
+
485
558
collected = collect (arguments or ['.' ],
486
559
match = re (options .match + '$' ).match ,
487
560
match_dir = re (options .match_dir + '$' ).match )
561
+
562
+ log .debug ("starting pep257 in debug mode." )
563
+
564
+ Error .explain = options .explain
565
+ Error .source = options .source
566
+ collected = list (collected )
488
567
code = 0
489
568
for error in check (collected , ignore = options .ignore .split (',' )):
490
569
sys .stderr .write ('%s\n ' % error )
@@ -799,6 +878,6 @@ def SKIP_check_return_type(self, function, docstring):
799
878
800
879
if __name__ == '__main__' :
801
880
try :
802
- sys .exit (main (* parse_options () ))
881
+ sys .exit (main ())
803
882
except KeyboardInterrupt :
804
883
pass
0 commit comments