def _broken_callable_location(self, node: nodes.Name | nodes.Attribute) -> bool: """Check if node would be a broken location for collections.abc.Callable.""" if (in_type_checking_block(node) or is_postponed_evaluation_enabled(node) and is_node_in_type_annotation_context(node)): return False # Check first Callable arg is a list of arguments -> Callable[[int], None] if not (isinstance(node.parent, nodes.Subscript) and isinstance(node.parent.slice, nodes.Tuple) and len(node.parent.slice.elts) == 2 and isinstance(node.parent.slice.elts[0], nodes.List)): return False # Check nested inside Optional or Union parent_subscript = node.parent.parent if isinstance(parent_subscript, nodes.BaseContainer): parent_subscript = parent_subscript.parent if not (isinstance(parent_subscript, nodes.Subscript) and isinstance(parent_subscript.value, (nodes.Name, nodes.Attribute))): return False inferred_parent = safe_infer(parent_subscript.value) if not (isinstance(inferred_parent, nodes.FunctionDef) and inferred_parent.qname() in {"typing.Optional", "typing.Union"} or isinstance(inferred_parent, astroid.bases.Instance) and inferred_parent.qname() == "typing._SpecialForm"): return False return True
def _check_for_alternative_union_syntax( self, node: nodes.Name | nodes.Attribute, name: str, ) -> None: """Check if alternative union syntax could be used. Requires - Python 3.10 - OR: Python 3.7+ with postponed evaluation in a type annotation context """ inferred = safe_infer(node) if not (isinstance(inferred, nodes.FunctionDef) and inferred.qname() in {"typing.Optional", "typing.Union"} or isinstance(inferred, astroid.bases.Instance) and inferred.qname() == "typing._SpecialForm"): return if not (self._py310_plus or is_node_in_type_annotation_context(node)): return self.add_message( "consider-alternative-union-syntax", node=node, args=(name, self._msg_postponed_eval_hint(node)), confidence=INFERENCE, )
def _check_for_typing_alias( self, node: nodes.Name | nodes.Attribute, ) -> None: """Check if typing alias is deprecated or could be replaced. Requires - Python 3.9 - OR: Python 3.7+ with postponed evaluation in a type annotation context For Python 3.7+: Only emit message if change doesn't create any name collisions, only ever used in a type annotation context, and can safely be replaced. """ inferred = safe_infer(node) if not isinstance(inferred, nodes.ClassDef): return alias = DEPRECATED_TYPING_ALIASES.get(inferred.qname(), None) if alias is None: return if self._py39_plus: if inferred.qname( ) == "typing.Callable" and self._broken_callable_location(node): self._found_broken_callable_location = True self._deprecated_typing_alias_msgs.append( DeprecatedTypingAliasMsg( node, inferred.qname(), alias.name, )) return # For PY37+, check for type annotation context first if not is_node_in_type_annotation_context(node) and isinstance( node.parent, nodes.Subscript): if alias.name_collision is True: self._alias_name_collisions.add(inferred.qname()) return self._consider_using_alias_msgs.append( DeprecatedTypingAliasMsg( node, inferred.qname(), alias.name, isinstance(node.parent, nodes.Subscript), ))
def _check_for_typing_alias( self, node: Union[astroid.Name, astroid.Attribute], ) -> None: """Check if typing alias is depecated or could be replaced. Requires - Python 3.9 - OR: Python 3.7+ with postponed evaluation in a type annotation context For Python 3.7+: Only emitt message if change doesn't create any name collisions, only ever used in a type annotation context, and can safely be replaced. """ inferred = safe_infer(node) if not isinstance(inferred, astroid.ClassDef): return alias = DEPRECATED_TYPING_ALIASES.get(inferred.qname(), None) if alias is None: return if self._py39_plus(): self.add_message( "deprecated-typing-alias", node=node, args=(inferred.qname(), alias.name), ) return # For PY37+, check for type annotation context first if not is_node_in_type_annotation_context(node) and isinstance( node.parent, astroid.Subscript): if alias.name_collision is True: self._alias_name_collisions.add(inferred.qname()) return self._consider_using_alias_msgs.append( DeprecatedTypingAliasMsg( node, inferred.qname(), alias.name, isinstance(node.parent, astroid.Subscript), ))
def _check_broken_noreturn(self, node: nodes.Name | nodes.Attribute) -> None: """Check for 'NoReturn' inside compound types.""" if not isinstance(node.parent, nodes.BaseContainer): # NoReturn not part of a Union or Callable type return if (in_type_checking_block(node) or is_postponed_evaluation_enabled(node) and is_node_in_type_annotation_context(node)): return for inferred in node.infer(): # To deal with typing_extensions, don't use safe_infer if (isinstance(inferred, (nodes.FunctionDef, nodes.ClassDef)) and inferred.qname() in TYPING_NORETURN # In Python 3.7 - 3.8, NoReturn is alias of '_SpecialForm' or isinstance(inferred, astroid.bases.BaseInstance) and isinstance(inferred._proxied, nodes.ClassDef) and inferred._proxied.qname() == "typing._SpecialForm"): self.add_message("broken-noreturn", node=node, confidence=INFERENCE) break