Пример #1
0
    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
Пример #2
0
    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,
        )
Пример #3
0
    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),
            ))
Пример #4
0
    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),
            ))
Пример #5
0
    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