Skip to content

Commit bc1a7e4

Browse files
authored
Refactor ignored name handling (#240)
We previously constructed a frozen set of names that should be ignored at setup-time and then passed that set as an argument to each visitor function. A helper function -- _ignored(name, set) -- was used to test whether a given name should be ignored, matched using fnmatchcase(). This change introduces a `NameSet` type, which inherits from frozenset and implements `__contains__` using fnmatchcase(). This allows us to simply use the `in` operator in visitor functions. The previous code also (sometimes) treated the `ignores` argument as optional (defaulting to None). This value always exists so remove the optional notation.
1 parent 77da152 commit bc1a7e4

File tree

1 file changed

+39
-36
lines changed

1 file changed

+39
-36
lines changed

src/pep8ext_naming.py

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,11 @@ def err(self, node, code: str, **kwargs):
5454
return lineno, col_offset + 1, f'{code} {code_str}', self
5555

5656

57-
def _ignored(name, ignore):
58-
return any(fnmatchcase(name, i) for i in ignore)
57+
class NameSet(frozenset[str]):
58+
"""A set of names that are matched using fnmatchcase."""
59+
60+
def __contains__(self, item) -> bool:
61+
return any(fnmatchcase(item, name) for name in self)
5962

6063

6164
class _FunctionType:
@@ -65,7 +68,7 @@ class _FunctionType:
6568
METHOD = 'method'
6669

6770

68-
_default_ignore_names = [
71+
_default_ignored_names = [
6972
'setUp',
7073
'tearDown',
7174
'setUpClass',
@@ -98,7 +101,7 @@ class NamingChecker:
98101
visitors = BaseASTCheck.all
99102
decorator_to_type = _build_decorator_to_type(
100103
_default_classmethod_decorators, _default_staticmethod_decorators)
101-
ignore_names = frozenset(_default_ignore_names)
104+
ignored = NameSet(_default_ignored_names)
102105

103106
def __init__(self, tree, filename):
104107
self.tree = tree
@@ -107,7 +110,7 @@ def __init__(self, tree, filename):
107110
def add_options(cls, parser):
108111
parser.add_option(
109112
'--ignore-names',
110-
default=_default_ignore_names,
113+
default=_default_ignored_names,
111114
parse_from_config=True,
112115
comma_separated_list=True,
113116
help='List of names or glob patterns the pep8-naming '
@@ -135,14 +138,14 @@ def add_options(cls, parser):
135138

136139
@classmethod
137140
def parse_options(cls, options):
138-
cls.ignore_names = frozenset(options.ignore_names)
141+
cls.ignored = NameSet(options.ignore_names)
139142
cls.decorator_to_type = _build_decorator_to_type(
140143
options.classmethod_decorators,
141144
options.staticmethod_decorators)
142145

143-
# Build a list of node visitors based the error codes that have been
144-
# selected in the style guide. Only the checks that have been selected
145-
# will be evaluated as a performance optimization.
146+
# Rebuild the list of node visitors based the error codes that have
147+
# been selected in the style guide. Only the checks that have been
148+
# selected will be evaluated as a performance optimization.
146149
engine = style_guide.DecisionEngine(options)
147150
cls.visitors = frozenset(
148151
visitor for visitor in BaseASTCheck.all for code in visitor.codes
@@ -166,12 +169,12 @@ def visit_node(self, node, parents: Iterable):
166169
self.find_global_defs(node)
167170

168171
method = 'visit_' + node.__class__.__name__.lower()
169-
ignore_names = self.ignore_names
172+
ignored = self.ignored
170173
for visitor in self.visitors:
171174
visitor_method = getattr(visitor, method, None)
172175
if visitor_method is None:
173176
continue
174-
yield from visitor_method(node, parents, ignore_names)
177+
yield from visitor_method(node, parents, ignored)
175178

176179
def tag_class_functions(self, cls_node):
177180
"""Tag functions if they are methods, classmethods, staticmethods"""
@@ -273,9 +276,9 @@ def superclass_names(cls, name, parents: Iterable, _names=None):
273276
names.update(cls.superclass_names(base.id, parents, names))
274277
return names
275278

276-
def visit_classdef(self, node, parents: Iterable, ignore=None):
279+
def visit_classdef(self, node, parents: Iterable, ignored: NameSet):
277280
name = node.name
278-
if _ignored(name, ignore):
281+
if name in ignored:
279282
return
280283
name = name.strip('_')
281284
if not name[:1].isupper() or '_' in name:
@@ -308,10 +311,10 @@ def has_override_decorator(node):
308311
return True
309312
return False
310313

311-
def visit_functiondef(self, node, parents: Iterable, ignore=None):
314+
def visit_functiondef(self, node, parents: Iterable, ignored: NameSet):
312315
function_type = getattr(node, 'function_type', _FunctionType.FUNCTION)
313316
name = node.name
314-
if _ignored(name, ignore):
317+
if name in ignored:
315318
return
316319
if name in ('__dir__', '__getattr__'):
317320
return
@@ -339,18 +342,18 @@ class FunctionArgNamesCheck(BaseASTCheck):
339342
N804 = "first argument of a classmethod should be named 'cls'"
340343
N805 = "first argument of a method should be named 'self'"
341344

342-
def visit_functiondef(self, node, parents: Iterable, ignore=None):
345+
def visit_functiondef(self, node, parents: Iterable, ignored: NameSet):
343346
args = node.args.posonlyargs + node.args.args + node.args.kwonlyargs
344347

345348
# Start by applying checks that are specific to the first argument.
346349
#
347-
# Note: The `ignore` check shouldn't be necessary here because we'd
350+
# Note: The `ignored` check shouldn't be necessary here because we'd
348351
# expect users to explicitly ignore N804/N805 when using names
349352
# other than `self` and `cls` rather than ignoring names like
350353
# `klass` to get around these checks. However, a previous
351354
# implementation allowed for that, so we retain that behavior
352355
# for backwards compatibility.
353-
if args and (name := args[0].arg) and not _ignored(name, ignore):
356+
if args and (name := args[0].arg) and name not in ignored:
354357
function_type = getattr(node, 'function_type', None)
355358
if function_type == _FunctionType.METHOD and name != 'self':
356359
yield self.err(args[0], 'N805')
@@ -368,7 +371,7 @@ def visit_functiondef(self, node, parents: Iterable, ignore=None):
368371

369372
for arg in args:
370373
name = arg.arg
371-
if name.lower() != name and not _ignored(name, ignore):
374+
if name.lower() != name and name not in ignored:
372375
yield self.err(arg, 'N803', name=name)
373376

374377
visit_asyncfunctiondef = visit_functiondef
@@ -384,7 +387,7 @@ class ImportAsCheck(BaseASTCheck):
384387
N814 = "camelcase '{name}' imported as constant '{asname}'"
385388
N817 = "camelcase '{name}' imported as acronym '{asname}'"
386389

387-
def visit_importfrom(self, node, parents: Iterable, ignore=None):
390+
def visit_importfrom(self, node, parents: Iterable, ignored: NameSet):
388391
for name in node.names:
389392
asname = name.asname
390393
if not asname:
@@ -416,7 +419,7 @@ class VariablesCheck(BaseASTCheck):
416419
N815 = "variable '{name}' in class scope should not be mixedCase"
417420
N816 = "variable '{name}' in global scope should not be mixedCase"
418421

419-
def _find_errors(self, assignment_target, parents: Iterable, ignore):
422+
def _find_errors(self, assignment_target, parents: Iterable, ignored: NameSet):
420423
for parent_func in reversed(parents):
421424
if isinstance(parent_func, ast.ClassDef):
422425
checker = self.class_variable_check
@@ -427,7 +430,7 @@ def _find_errors(self, assignment_target, parents: Iterable, ignore):
427430
else:
428431
checker = self.global_variable_check
429432
for name in _extract_names(assignment_target):
430-
if _ignored(name, ignore):
433+
if name in ignored:
431434
continue
432435
error_code = checker(name)
433436
if error_code:
@@ -444,38 +447,38 @@ def is_namedtupe(node_value):
444447
return True
445448
return False
446449

447-
def visit_assign(self, node, parents: Iterable, ignore=None):
450+
def visit_assign(self, node, parents: Iterable, ignored: NameSet):
448451
if self.is_namedtupe(node.value):
449452
return
450453
for target in node.targets:
451-
yield from self._find_errors(target, parents, ignore)
454+
yield from self._find_errors(target, parents, ignored)
452455

453-
def visit_namedexpr(self, node, parents: Iterable, ignore):
456+
def visit_namedexpr(self, node, parents: Iterable, ignored: NameSet):
454457
if self.is_namedtupe(node.value):
455458
return
456-
yield from self._find_errors(node.target, parents, ignore)
459+
yield from self._find_errors(node.target, parents, ignored)
457460

458461
visit_annassign = visit_namedexpr
459462

460-
def visit_with(self, node, parents: Iterable, ignore):
463+
def visit_with(self, node, parents: Iterable, ignored: NameSet):
461464
for item in node.items:
462465
yield from self._find_errors(
463-
item.optional_vars, parents, ignore)
466+
item.optional_vars, parents, ignored)
464467

465468
visit_asyncwith = visit_with
466469

467-
def visit_for(self, node, parents: Iterable, ignore):
468-
yield from self._find_errors(node.target, parents, ignore)
470+
def visit_for(self, node, parents: Iterable, ignored: NameSet):
471+
yield from self._find_errors(node.target, parents, ignored)
469472

470473
visit_asyncfor = visit_for
471474

472-
def visit_excepthandler(self, node, parents: Iterable, ignore):
475+
def visit_excepthandler(self, node, parents: Iterable, ignored: NameSet):
473476
if node.name:
474-
yield from self._find_errors(node, parents, ignore)
477+
yield from self._find_errors(node, parents, ignored)
475478

476-
def visit_generatorexp(self, node, parents: Iterable, ignore):
479+
def visit_generatorexp(self, node, parents: Iterable, ignored: NameSet):
477480
for gen in node.generators:
478-
yield from self._find_errors(gen.target, parents, ignore)
481+
yield from self._find_errors(gen.target, parents, ignored)
479482

480483
visit_listcomp = visit_dictcomp = visit_setcomp = visit_generatorexp
481484

@@ -502,7 +505,7 @@ class TypeVarNameCheck(BaseASTCheck):
502505
N808 = "type variable name '{name}' should use CapWords convention " \
503506
"and an optional '_co' or '_contra' suffix"
504507

505-
def visit_module(self, node, parents: Iterable, ignore=None):
508+
def visit_module(self, node, parents: Iterable, ignored: NameSet):
506509
for body in node.body:
507510
try:
508511
if len(body.targets) != 1:
@@ -514,7 +517,7 @@ def visit_module(self, node, parents: Iterable, ignore=None):
514517
except AttributeError:
515518
continue
516519

517-
if func_name != "TypeVar" or _ignored(name, ignore):
520+
if func_name != "TypeVar" or name in ignored:
518521
continue
519522

520523
if len(args) == 0 or args[0] != name:

0 commit comments

Comments
 (0)