def _ensure_underscores(self, node: ast.AST, name: str): if access.is_private(name): self._error_callback( naming.PrivateNameViolation(node, text=name), ) if logical.does_contain_underscored_number(name): self._error_callback( naming.UnderscoredNumberNameViolation(node, text=name), ) if logical.does_contain_consecutive_underscores(name): self._error_callback( naming.ConsecutiveUnderscoresInNameViolation( node, text=name, ), ) if builtins.is_wrong_alias(name): self._error_callback( naming.TrailingUnderscoreViolation(node, text=name), ) if access.is_unused(name) and len(name) > 1: self._error_callback( naming.WrongUnusedVariableNameViolation(node, text=name), )
def _exclude_unused(self, names: Set[str]) -> Set[str]: """Removes unused variables from set of names.""" return { var_name # we allow to reuse explicit `_` variables for var_name in names if not access.is_unused(var_name) }
def _ensure_complex_naming( self, node: ast.AST, name: str, *, is_first_argument: bool, ) -> None: if logical.is_wrong_name(name, self._variable_names_blacklist): self._error_callback( naming.WrongVariableNameViolation(node, text=name), ) if not is_first_argument: if logical.is_wrong_name(name, SPECIAL_ARGUMENT_NAMES_WHITELIST): self._error_callback( naming.ReservedArgumentNameViolation(node, text=name), ) if access.is_unused(name) and len(name) > 1: self._error_callback( naming.WrongUnusedVariableNameViolation(node, text=name), ) unreadable_sequence = alphabet.get_unreadable_characters( name, UNREADABLE_CHARACTER_COMBINATIONS, ) if unreadable_sequence: self._error_callback( naming.UnreadableNameViolation(node, text=unreadable_sequence), )
def is_constant(name: str) -> bool: """ Checks whether the given ``name`` is a constant. >>> is_constant('CONST') True >>> is_constant('ALLOWED_EMPTY_LINE_TOKEN') True >>> is_constant('Some') False >>> is_constant('_') False >>> is_constant('lower_case') False """ if is_unused(name): return False return all( # We check that constant names consist of: # UPPERCASE LETTERS and `_` char character.isupper() or character == '_' for character in name )
def is_too_short_name( name: str, min_length: int, *, trim: bool = True, ) -> bool: """ Checks for too short names. >>> is_too_short_name('test', min_length=2) False >>> is_too_short_name('o', min_length=2) True >>> is_too_short_name('_', min_length=2) False >>> is_too_short_name('_', min_length=1) False >>> is_too_short_name('z1', min_length=2) False >>> is_too_short_name('z', min_length=1) False >>> is_too_short_name('_z', min_length=2, trim=True) True >>> is_too_short_name('z_', min_length=2, trim=True) True >>> is_too_short_name('z_', min_length=2, trim=False) False >>> is_too_short_name('__z', min_length=2, trim=True) True >>> is_too_short_name('xy', min_length=2, trim=True) False >>> is_too_short_name('np', min_length=3) False """ if name in ALIAS_NAMES_WHITELIST: return False if access.is_unused(name): return False if trim: name = name.strip(UNUSED_PLACEHOLDER) return len(name) < min_length
def add_to_scope( self, names: Set[str], is_local: bool = False, ) -> None: """Adds a set of names to the specified scope.""" scope = self._get_scope(is_local=is_local) scope[self._context] = scope[self._context].union({ var_name # we allow to reuse explicit `_` variable for var_name in names if not access.is_unused(var_name) })
def visit_any_for(self, node: AnyFor) -> None: """Checks that we cannot create explicit unused loops.""" target_names = name_nodes.get_variables_from_node(node.target) is_target_no_op_variable = (len(target_names) == 1 and access.is_unused(target_names[0])) if not is_target_no_op_variable: # see issue 1406 self._check_assign_unused( node, target_names, is_local=True, ) self.generic_visit(node)
def is_too_short_name( name: str, min_length: int, *, trim: bool = True, ) -> bool: """ Checks for too short names. >>> is_too_short_name('test', min_length=2) False >>> is_too_short_name('o', min_length=2) True >>> is_too_short_name('_', min_length=2) False >>> is_too_short_name('_', min_length=1) False >>> is_too_short_name('z1', min_length=2) False >>> is_too_short_name('z', min_length=1) False >>> is_too_short_name('_z', min_length=2, trim=True) True >>> is_too_short_name('z_', min_length=2, trim=True) True >>> is_too_short_name('z_', min_length=2, trim=False) False >>> is_too_short_name('__z', min_length=2, trim=True) True >>> is_too_short_name('xy', min_length=2, trim=True) False """ if access.is_unused(name): return False if trim: name = name.strip('_') return len(name) < min_length
def _check_variable_used( self, node: ast.AST, assigned_name: Optional[str], *, is_created: bool, ) -> None: if not assigned_name or not access.is_unused(assigned_name): return if not is_created: self.add_violation( naming.UnusedVariableIsUsedViolation(node, text=assigned_name), )
def _check_unique_assignment( self, node: AnyAssign, names: List[str], ) -> None: used_names = filter( lambda assigned_name: not access.is_unused(assigned_name), names, ) for used_name, count in Counter(used_names).items(): if count > 1: self.add_violation( best_practices.ReassigningVariableToItselfViolation( node, text=used_name, ), )
def _check_variable_used( self, node: ast.AST, assigned_name: Optional[str], *, is_created: bool, ) -> None: if not assigned_name or not access.is_unused(assigned_name): return if assigned_name == '_': # This is a special case for django's return # gettext and similar tools. if not is_created: self.add_violation( naming.UnusedVariableIsUsedViolation(node, text=assigned_name), )
def _check_assign_unused( self, node: ast.AST, all_names: Iterable[str], *, is_local: bool = True, ) -> None: all_names = list(all_names) # we are using it twice all_unused = all( is_local if access.is_protected(vn) else access.is_unused(vn) for vn in all_names) if all_names and all_unused: self.add_violation( naming.UnusedVariableIsDefinedViolation( node, text=', '.join(all_names), ), )
def _check_variable_used( self, node: ast.AST, assigned_name: str, *, is_created: bool, ) -> None: if not access.is_unused(assigned_name): return if assigned_name == UNUSED_PLACEHOLDER: # This is a special case for django's # gettext and similar tools. return if not is_created: self.add_violation( naming.UnusedVariableIsUsedViolation(node, text=assigned_name), )
def _maybe_update_variable( self, sub_node: _LocalVariable, var_name: str, local_variables: Dict[str, List[_LocalVariable]], ) -> None: defs = local_variables.get(var_name) if defs is not None: if not var_name or access.is_unused(var_name): # We check unused variable usage in a different place: # see `visitors/ast/naming.py` return defs.append(sub_node) return is_name_def = (isinstance(sub_node, ast.Name) and isinstance(sub_node.ctx, ast.Store)) if is_name_def or isinstance(sub_node, ast.ExceptHandler): local_variables[var_name] = []
def _update_variables( self, function: AnyFunctionDef, variable_def: ast.Name, ) -> None: """ Increases the counter of local variables. What is treated as a local variable? Check ``TooManyLocalsViolation`` documentation. """ function_variables = self.variables[function] if variable_def.id not in function_variables: if access.is_unused(variable_def.id): return if isinstance(get_parent(variable_def), self._not_contain_locals): return function_variables.append(variable_def.id)
def is_wrong_alias(variable_name: str) -> bool: """ Tells whether a variable is wrong builtins alias or not. >>> is_wrong_alias('regular_name_') True >>> is_wrong_alias('_') False >>> is_wrong_alias('_async') False >>> is_wrong_alias('_await') False >>> is_wrong_alias('regular_name') False >>> is_wrong_alias('class_') False >>> is_wrong_alias('list_') False >>> is_wrong_alias('list') False >>> is_wrong_alias('__spec__') False """ if is_magic(variable_name): return False if is_unused(variable_name) or not variable_name.endswith('_'): return False return not is_builtin_name(variable_name[:-1])
def _maybe_update_variable( self, sub_node: LocalVariable, var_name: str, local_variables: Dict[str, List[LocalVariable]], ) -> None: if var_name in local_variables: if access.is_unused(var_name): # We check unused variable usage in a different place: # see `visitors/ast/naming.py` return local_variables[var_name].append(sub_node) return is_name_def = isinstance( sub_node, ast.Name, ) and isinstance( sub_node.ctx, ast.Store, ) if is_name_def or isinstance(sub_node, ast.ExceptHandler): local_variables[var_name] = []
class _SimpleNameValidator(object): """Utility class to separate logic from the naming visitor.""" _naming_predicates: ClassVar[Iterable[_NamingPredicate]] = ( _NamingPredicate( access.is_private, naming.PrivateNameViolation, ), _NamingPredicate( alphabet.does_contain_unicode, naming.UnicodeNameViolation, ), _NamingPredicate( lambda name: access.is_unused(name) and len(name) > 1, naming.WrongUnusedVariableNameViolation, ), ) def __init__( self, error_callback: _ErrorCallback, options: ConfigurationOptions, ) -> None: """Creates new instance of a name validator.""" self._error_callback = error_callback self._options = options self._variable_names_blacklist = ( blacklists.variable_names_blacklist_from(options) ) def check_name( self, node: ast.AST, name: str, *, is_first_argument: bool = False, ) -> None: for predicate in self._naming_predicates: if predicate.is_applicable(node) and predicate.is_correct(name): self._error_callback(predicate.violation(node, text=name)) self._ensure_reserved_name( node, name, is_first_argument=is_first_argument, ) def _ensure_reserved_name( self, node: ast.AST, name: str, *, is_first_argument: bool, ) -> None: if is_first_argument: return if not logical.is_wrong_name(name, SPECIAL_ARGUMENT_NAMES_WHITELIST): return self._error_callback( naming.ReservedArgumentNameViolation(node, text=name), )