21
21
from itertools import takewhile , dropwhile , chain
22
22
from optparse import OptionParser
23
23
from re import compile as re
24
+ import itertools
25
+
24
26
try : # Python 3.x
25
27
from ConfigParser import RawConfigParser
26
28
except ImportError : # Python 2.x
@@ -54,6 +56,9 @@ def next(obj, default=nothing):
54
56
__all__ = ('check' , 'collect' )
55
57
56
58
PROJECT_CONFIG = ('setup.cfg' , 'tox.ini' , '.pep257' )
59
+ NO_VIOLATIONS_RETURN_CODE = 0
60
+ VIOLATIONS_RETURN_CODE = 1
61
+ INVALID_OPTIONS_RETURN_CODE = 2
57
62
58
63
59
64
def humanize (string ):
@@ -478,7 +483,7 @@ def create_group(cls, prefix, name):
478
483
def get_error_codes (cls ):
479
484
for group in cls .groups :
480
485
for error in group .errors :
481
- yield error
486
+ yield error . code
482
487
483
488
@classmethod
484
489
def to_rst (cls ):
@@ -541,11 +546,16 @@ def to_rst(cls):
541
546
'"signature"' )
542
547
543
548
549
+ class Conventions (object ):
550
+ pep257 = set (ErrorRegistry .get_error_codes ())
551
+
552
+
544
553
def get_option_parser ():
545
554
parser = OptionParser (version = __version__ ,
546
555
usage = 'Usage: pep257 [options] [<file|dir>...]' )
547
- parser .config_options = ('explain' , 'source' , 'ignore' , 'match' ,
548
- 'match-dir' , 'debug' , 'verbose' , 'count' )
556
+ parser .config_options = ('explain' , 'source' , 'ignore' , 'match' , 'select' ,
557
+ 'match-dir' , 'debug' , 'verbose' , 'count' ,
558
+ 'convention' )
549
559
option = parser .add_option
550
560
option ('-e' , '--explain' , action = 'store_true' ,
551
561
help = 'show explanation of each error' )
@@ -559,6 +569,9 @@ def get_option_parser():
559
569
help = 'choose the basic list of checked errors by specifying which '
560
570
'errors to ignore (with a list of comma-separated error '
561
571
'codes). for example: --ignore=D101,D202' )
572
+ option ('--convention' , metavar = '<name>' , default = '' ,
573
+ help = 'choose the basic list of checked errors by specifying an '
574
+ 'existing convention. for example: --convention=pep257' )
562
575
option ('--add-select' , metavar = '<codes>' , default = '' ,
563
576
help = 'amend the list of errors to check for by specifying more '
564
577
'error codes to check.' )
@@ -605,7 +618,7 @@ def collect(names, match=lambda name: True, match_dir=lambda name: True):
605
618
yield name
606
619
607
620
608
- def check (filenames , checked_codes = () ):
621
+ def check (filenames , select = None , ignore = None ):
609
622
"""Generate PEP 257 errors that exist in `filenames` iterable.
610
623
611
624
Only returns errors with error-codes defined in `checked_codes` iterable.
@@ -616,6 +629,15 @@ def check(filenames, checked_codes=()):
616
629
<generator object check at 0x...>
617
630
618
631
"""
632
+ if select and ignore :
633
+ raise ValueError ('Cannot pass both select and ignore. They are '
634
+ 'mutually exclusive.' )
635
+ elif select or ignore :
636
+ checked_codes = (select or
637
+ set (ErrorRegistry .get_error_codes ()) - set (ignore ))
638
+ else :
639
+ checked_codes = Conventions .pep257
640
+
619
641
for filename in filenames :
620
642
log .info ('Checking file %s.' , filename )
621
643
try :
@@ -654,7 +676,7 @@ def get_options(args, opt_parser):
654
676
pep257_section = 'pep257'
655
677
for opt in config .options (pep257_section ):
656
678
if opt .replace ('_' , '-' ) not in opt_parser .config_options :
657
- print ("Unknown option '{}' ignored" .format (opt ))
679
+ log . warning ("Unknown option '{}' ignored" .format (opt ))
658
680
continue
659
681
normalized_opt = opt .replace ('-' , '_' )
660
682
opt_type = option_list [normalized_opt ]
@@ -673,36 +695,73 @@ def get_options(args, opt_parser):
673
695
return options
674
696
675
697
676
- def setup_stream_handler (options ):
698
+ def setup_stream_handlers (options ):
699
+ """Setup logging stream handlers according to the options."""
700
+ class StdoutFilter (logging .Filter ):
701
+ def filter (self , record ):
702
+ return record .levelno in (logging .DEBUG , logging .INFO )
703
+
677
704
if log .handlers :
678
705
for handler in log .handlers :
679
706
log .removeHandler (handler )
680
- stream_handler = logging .StreamHandler (sys .stdout )
681
- stream_handler .setLevel (logging .WARNING )
707
+
708
+ stdout_handler = logging .StreamHandler (sys .stdout )
709
+ stdout_handler .setLevel (logging .WARNING )
710
+ stdout_handler .addFilter (StdoutFilter ())
682
711
if options .debug :
683
- stream_handler .setLevel (logging .DEBUG )
712
+ stdout_handler .setLevel (logging .DEBUG )
684
713
elif options .verbose :
685
- stream_handler .setLevel (logging .INFO )
714
+ stdout_handler .setLevel (logging .INFO )
686
715
else :
687
- stream_handler .setLevel (logging .WARNING )
688
- log .addHandler (stream_handler )
716
+ stdout_handler .setLevel (logging .WARNING )
717
+ log .addHandler (stdout_handler )
718
+
719
+ stderr_handler = logging .StreamHandler (sys .stderr )
720
+ stderr_handler .setLevel (logging .WARNING )
721
+ log .addHandler (stderr_handler )
689
722
690
723
691
724
def get_checked_error_codes (options ):
692
- return ['D100' ]
725
+ codes = set (ErrorRegistry .get_error_codes ())
726
+ if options .ignore :
727
+ checked_codes = codes - set (options .ignore .split (',' ))
728
+ elif options .select :
729
+ checked_codes = set (options .select .split (',' ))
730
+ elif options .convention :
731
+ checked_codes = getattr (Conventions , options .convention )
732
+ else :
733
+ checked_codes = Conventions .pep257
734
+ checked_codes -= set (options .add_ignore .split (',' ))
735
+ checked_codes |= set (options .add_select .split (',' ))
736
+ return checked_codes - set ('' )
737
+
738
+
739
+ def validate_options (options ):
740
+ mutually_exclusive = ('ignore' , 'select' , 'convention' )
741
+ for opt1 , opt2 in itertools .permutations (mutually_exclusive , 2 ):
742
+ if getattr (options , opt1 ) and getattr (options , opt2 ):
743
+ log .error ('Cannot pass both {} and {}. They are '
744
+ 'mutually exclusive.' .format (opt1 , opt2 ))
745
+ return False
746
+ if options .convention and not hasattr (Conventions , options .convention ):
747
+ return False
748
+ return True
749
+
693
750
694
751
def run_pep257 ():
695
752
log .setLevel (logging .DEBUG )
696
753
opt_parser = get_option_parser ()
697
754
# setup the logger before parsing the config file, so that command line
698
755
# arguments for debug / verbose will be printed.
699
756
options , arguments = opt_parser .parse_args ()
700
- setup_stream_handler (options )
757
+ setup_stream_handlers (options )
701
758
# We parse the files before opening the config file, since it changes where
702
759
# we look for the file.
703
760
options = get_options (arguments , opt_parser )
761
+ if not validate_options (options ):
762
+ return INVALID_OPTIONS_RETURN_CODE
704
763
# Setup the handler again with values from the config file.
705
- setup_stream_handler (options )
764
+ setup_stream_handlers (options )
706
765
707
766
collected = collect (arguments or ['.' ],
708
767
match = re (options .match + '$' ).match ,
@@ -714,13 +773,12 @@ def run_pep257():
714
773
Error .source = options .source
715
774
collected = list (collected )
716
775
checked_codes = get_checked_error_codes (options )
717
- # options.ignore.split(',')
718
- errors = check (collected , checked_codes = checked_codes )
719
- code = 0
776
+ errors = check (collected , select = checked_codes )
777
+ code = NO_VIOLATIONS_RETURN_CODE
720
778
count = 0
721
779
for error in errors :
722
780
sys .stderr .write ('%s\n ' % error )
723
- code = 1
781
+ code = VIOLATIONS_RETURN_CODE
724
782
count += 1
725
783
if options .count :
726
784
print (count )
0 commit comments