Skip to content
This repository was archived by the owner on Nov 3, 2023. It is now read-only.

Commit aa60d3a

Browse files
committed
Working on error selection.
1 parent 3304450 commit aa60d3a

File tree

3 files changed

+190
-40
lines changed

3 files changed

+190
-40
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ docs/_*
3838
.pydevproject
3939
.settings
4040

41+
# PyCharm files
42+
.idea
43+
4144
# virtualenv
4245
venv/
4346

pep257.py

Lines changed: 77 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
from itertools import takewhile, dropwhile, chain
2222
from optparse import OptionParser
2323
from re import compile as re
24+
import itertools
25+
2426
try: # Python 3.x
2527
from ConfigParser import RawConfigParser
2628
except ImportError: # Python 2.x
@@ -54,6 +56,9 @@ def next(obj, default=nothing):
5456
__all__ = ('check', 'collect')
5557

5658
PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep257')
59+
NO_VIOLATIONS_RETURN_CODE = 0
60+
VIOLATIONS_RETURN_CODE = 1
61+
INVALID_OPTIONS_RETURN_CODE = 2
5762

5863

5964
def humanize(string):
@@ -478,7 +483,7 @@ def create_group(cls, prefix, name):
478483
def get_error_codes(cls):
479484
for group in cls.groups:
480485
for error in group.errors:
481-
yield error
486+
yield error.code
482487

483488
@classmethod
484489
def to_rst(cls):
@@ -541,11 +546,16 @@ def to_rst(cls):
541546
'"signature"')
542547

543548

549+
class Conventions(object):
550+
pep257 = set(ErrorRegistry.get_error_codes())
551+
552+
544553
def get_option_parser():
545554
parser = OptionParser(version=__version__,
546555
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')
549559
option = parser.add_option
550560
option('-e', '--explain', action='store_true',
551561
help='show explanation of each error')
@@ -559,6 +569,9 @@ def get_option_parser():
559569
help='choose the basic list of checked errors by specifying which '
560570
'errors to ignore (with a list of comma-separated error '
561571
'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')
562575
option('--add-select', metavar='<codes>', default='',
563576
help='amend the list of errors to check for by specifying more '
564577
'error codes to check.')
@@ -605,7 +618,7 @@ def collect(names, match=lambda name: True, match_dir=lambda name: True):
605618
yield name
606619

607620

608-
def check(filenames, checked_codes=()):
621+
def check(filenames, select=None, ignore=None):
609622
"""Generate PEP 257 errors that exist in `filenames` iterable.
610623
611624
Only returns errors with error-codes defined in `checked_codes` iterable.
@@ -616,6 +629,15 @@ def check(filenames, checked_codes=()):
616629
<generator object check at 0x...>
617630
618631
"""
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+
619641
for filename in filenames:
620642
log.info('Checking file %s.', filename)
621643
try:
@@ -654,7 +676,7 @@ def get_options(args, opt_parser):
654676
pep257_section = 'pep257'
655677
for opt in config.options(pep257_section):
656678
if opt.replace('_', '-') not in opt_parser.config_options:
657-
print("Unknown option '{}' ignored".format(opt))
679+
log.warning("Unknown option '{}' ignored".format(opt))
658680
continue
659681
normalized_opt = opt.replace('-', '_')
660682
opt_type = option_list[normalized_opt]
@@ -673,36 +695,73 @@ def get_options(args, opt_parser):
673695
return options
674696

675697

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+
677704
if log.handlers:
678705
for handler in log.handlers:
679706
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())
682711
if options.debug:
683-
stream_handler.setLevel(logging.DEBUG)
712+
stdout_handler.setLevel(logging.DEBUG)
684713
elif options.verbose:
685-
stream_handler.setLevel(logging.INFO)
714+
stdout_handler.setLevel(logging.INFO)
686715
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)
689722

690723

691724
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+
693750

694751
def run_pep257():
695752
log.setLevel(logging.DEBUG)
696753
opt_parser = get_option_parser()
697754
# setup the logger before parsing the config file, so that command line
698755
# arguments for debug / verbose will be printed.
699756
options, arguments = opt_parser.parse_args()
700-
setup_stream_handler(options)
757+
setup_stream_handlers(options)
701758
# We parse the files before opening the config file, since it changes where
702759
# we look for the file.
703760
options = get_options(arguments, opt_parser)
761+
if not validate_options(options):
762+
return INVALID_OPTIONS_RETURN_CODE
704763
# Setup the handler again with values from the config file.
705-
setup_stream_handler(options)
764+
setup_stream_handlers(options)
706765

707766
collected = collect(arguments or ['.'],
708767
match=re(options.match + '$').match,
@@ -714,13 +773,12 @@ def run_pep257():
714773
Error.source = options.source
715774
collected = list(collected)
716775
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
720778
count = 0
721779
for error in errors:
722780
sys.stderr.write('%s\n' % error)
723-
code = 1
781+
code = VIOLATIONS_RETURN_CODE
724782
count += 1
725783
if options.count:
726784
print(count)

0 commit comments

Comments
 (0)