Пример #1
0
 def _run_and_apply_changes(cls, kwargs, autofix=False):
     changes = collections.defaultdict(list)
     with qcore.override(cls, "_changes_for_fixer", changes):
         try:
             had_failure = cls._run(**kwargs)
         except VisitorError:
             had_failure = True
     # ignore run_fixer if autofix is enabled
     if autofix:
         cls._apply_changes(changes)
     else:
         patches = []
         for filename in changes:
             for change in changes[filename]:
                 linenos = sorted(change.linenos_to_delete)
                 additions = change.lines_to_add
                 if len(linenos) > 1:
                     start_lineno, end_lineno = linenos[0], linenos[-1]
                 else:
                     start_lineno, end_lineno = linenos[0], linenos[0]
                 patches.append(
                     _PatchWithDescription(
                         start_lineno - 1,
                         end_lineno,
                         new_lines=additions,
                         path=filename,
                         description=change.error_str,
                     ))
         if patches:
             # poor man's version of https://github.com/facebook/codemod/pull/113
             with qcore.override(builtins, "print", _flushing_print):
                 codemod.run_interactive(_Query(patches))
     return had_failure
Пример #2
0
 def visit(self, node):
     """Save the node if it is a statement."""
     # This code is also inlined in NameCheckVisitor (a subclass of this class) for speed.
     if isinstance(node, ast.stmt):
         with qcore.override(self, "current_statement", node):
             return super(ReplacingNodeVisitor, self).visit(node)
     else:
         return super(ReplacingNodeVisitor, self).visit(node)
Пример #3
0
def test_override():
    class TestObject(object):
        def __init__(self):
            self.v = None

    o = TestObject()
    o.v = "a"

    with qcore.override(o, "v", "b"):
        assert_eq(o.v, "b")
        try:
            with qcore.override(o, "v", "c"):
                assert_eq(o.v, "c")
                raise NotImplementedError()
        except NotImplementedError:
            pass
        assert_eq(o.v, "b")
    assert_eq(o.v, "a")
Пример #4
0
 def set_func_name(
     self, name, async_kind=AsyncFunctionKind.non_async, is_classmethod=False
 ):
     """Sets the current function name for async data collection."""
     # Override current_func_name only if this is the outermost function, so that data access
     # within nested functions is attributed to the outer function. However, for async inner
     # functions, check batching within the function separately.
     with qcore.override(self, "current_async_kind", async_kind), qcore.override(
         self, "is_classmethod", is_classmethod
     ):
         if (
             self.current_func_name is None
             or async_kind != AsyncFunctionKind.non_async
         ):
             with qcore.override(self, "current_func_name", name):
                 yield
         else:
             yield
Пример #5
0
def test_override():
    class TestObject(object):
        def __init__(self):
            self.v = None

    o = TestObject()
    o.v = 'a'

    with qcore.override(o, 'v', 'b'):
        assert_eq(o.v, 'b')
        try:
            with qcore.override(o, 'v', 'c'):
                assert_eq(o.v, 'c')
                raise NotImplementedError()
        except NotImplementedError:
            pass
        assert_eq(o.v, 'b')
    assert_eq(o.v, 'a')
Пример #6
0
 def loop_scope(self):
     loop_scopes = []
     with self.subscope() as main_scope:
         loop_scopes.append(main_scope)
         with qcore.override(self, "current_loop_scopes", loop_scopes):
             yield
     self.combine_subscopes([{
         name: values
         for name, values in scope.items() if name != LEAVES_LOOP
     } for scope in loop_scopes])
Пример #7
0
 def check_for_test(self, apply_changes=False):
     """Entry point for testing. Also applies all changes if apply_changes is on."""
     if not apply_changes:
         return self.check()
     changes = collections.defaultdict(list)
     with qcore.override(self.__class__, "_changes_for_fixer", changes):
         result = self.check()
     lines = [line + "\n" for line in self.contents.splitlines()]
     if self.filename in changes:
         lines = self._apply_changes_to_lines(changes[self.filename], lines)
     return result, "".join(lines)
Пример #8
0
def with_implementation(fn: object, implementation_fn: Impl) -> Iterable[None]:
    """Temporarily sets the implementation of fn to be implementation_fn.

    This is useful for invoking test_scope to aggregate all calls to a particular function. For
    example, the following can be used to find the names of all scribe categories we log to:

        categories = set()
        def _scribe_log_impl(variables, visitor, node):
            if isinstance(variables['category'], pyanalyze.value.KnownValue):
                categories.add(variables['category'].val)

        with pyanalyze.arg_spec.with_implementation(qclient.scribe.log, _scribe_log_impl):
            test_scope.test_all()

        print(categories)

    """
    if fn in ArgSpecCache.DEFAULT_ARGSPECS:
        with qcore.override(ArgSpecCache.DEFAULT_ARGSPECS[fn], "impl",
                            implementation_fn):
            yield
    else:
        argspec = ArgSpecCache(Config()).get_argspec(fn,
                                                     impl=implementation_fn)
        if argspec is None:
            # builtin or something, just use a generic argspec
            argspec = Signature.make(
                [
                    SigParameter("args", SigParameter.VAR_POSITIONAL),
                    SigParameter("kwargs", SigParameter.VAR_KEYWORD),
                ],
                callable=fn,
                impl=implementation_fn,
            )
        known_argspecs = dict(ArgSpecCache.DEFAULT_ARGSPECS)
        known_argspecs[fn] = argspec
        with qcore.override(ArgSpecCache, "DEFAULT_ARGSPECS", known_argspecs):
            yield
Пример #9
0
 def subscope(self):
     """Create a new subscope, to be used for conditional branches."""
     # Ignore LEAVES_SCOPE if it's already there, so that we type check code after the
     # assert False correctly. Without this, test_after_assert_false fails.
     new_name_to_nodes = defaultdict(
         list,
         {
             key: value
             for key, value in self.name_to_current_definition_nodes.items(
             ) if key != LEAVES_SCOPE
         },
     )
     with qcore.override(self, "name_to_current_definition_nodes",
                         new_name_to_nodes):
         yield new_name_to_nodes
Пример #10
0
    def check_yield(self, node, current_statement):
        assert current_statement is not None
        if self.visitor.async_kind == AsyncFunctionKind.normal:
            self._check_for_duplicate_yields(node, self.visitor.current_statement)

        in_non_async_yield = self.visitor.async_kind == AsyncFunctionKind.non_async
        with qcore.override(self, "in_non_async_yield", in_non_async_yield):
            yield

        if self.visitor.async_kind == AsyncFunctionKind.normal:
            self._maybe_show_unnecessary_yield_error(node, current_statement)

        if self.last_yield_in_aug_assign:
            self._maybe_show_unnecessary_yield_error(node, current_statement)

        self.last_yield_in_aug_assign = self._is_augassign_target(node)
        self.variables_from_yield_result = {}
        self.previous_yield = node
        self.statement_for_previous_yield = current_statement
Пример #11
0
 def check_yield_result_assignment(self, in_yield):
     return qcore.override(self, "in_yield_result_assignment", in_yield)
Пример #12
0
 def _has_import_star_usage(self, module, attr):
     with qcore.override(self, "_recursive_stack", set()):
         return self._has_import_star_usage_inner(module, attr)
Пример #13
0
 def catch_errors(self):
     caught_errors = []
     with qcore.override(self, "caught_errors", caught_errors):
         yield caught_errors
Пример #14
0
def _type_from_runtime(val, ctx):
    if isinstance(val, str):
        return _eval_forward_ref(val, ctx)
    elif isinstance(val, tuple):
        # This happens under some Python versions for types
        # nested in tuples, e.g. on 3.6:
        # > typing_inspect.get_args(Union[Set[int], List[str]])
        # ((typing.Set, int), (typing.List, str))
        origin = val[0]
        if len(val) == 2:
            args = (val[1], )
        else:
            args = val[1:]
        return _value_of_origin_args(origin, args, val, ctx)
    elif typing_inspect.is_literal_type(val):
        args = typing_inspect.get_args(val)
        if len(args) == 0:
            return KnownValue(args[0])
        else:
            return unite_values(*[KnownValue(arg) for arg in args])
    elif typing_inspect.is_union_type(val):
        args = typing_inspect.get_args(val)
        return unite_values(*[_type_from_runtime(arg, ctx) for arg in args])
    elif typing_inspect.is_tuple_type(val):
        args = typing_inspect.get_args(val)
        if not args:
            return TypedValue(tuple)
        elif len(args) == 2 and args[1] is Ellipsis:
            return GenericValue(tuple, [_type_from_runtime(args[0], ctx)])
        else:
            args_vals = [_type_from_runtime(arg, ctx) for arg in args]
            return SequenceIncompleteValue(tuple, args_vals)
    elif is_instance_of_typing_name(val, "_TypedDictMeta"):
        return TypedDictValue({
            key: _type_from_runtime(value, ctx)
            for key, value in val.__annotations__.items()
        })
    elif typing_inspect.is_callable_type(val):
        return TypedValue(Callable)
    elif typing_inspect.is_generic_type(val):
        origin = typing_inspect.get_origin(val)
        args = typing_inspect.get_args(val)
        return _value_of_origin_args(origin, args, val, ctx)
    elif GenericAlias is not None and isinstance(val, GenericAlias):
        origin = get_origin(val)
        args = get_args(val)
        return GenericValue(origin,
                            [_type_from_runtime(arg, ctx) for arg in args])
    elif isinstance(val, type):
        if val is type(None):
            return KnownValue(None)
        return TypedValue(val)
    elif val is None:
        return KnownValue(None)
    elif is_typing_name(val, "NoReturn"):
        return NO_RETURN_VALUE
    elif val is typing.Any:
        return UNRESOLVED_VALUE
    elif hasattr(val, "__supertype__"):
        if isinstance(val.__supertype__, type):
            # NewType
            return NewTypeValue(val)
        elif typing_inspect.is_tuple_type(val.__supertype__):
            # TODO figure out how to make NewTypes over tuples work
            return UNRESOLVED_VALUE
        else:
            ctx.show_error("Invalid NewType %s" % (val, ))
            return UNRESOLVED_VALUE
    elif typing_inspect.is_typevar(val):
        # TypeVar; not supported yet
        return UNRESOLVED_VALUE
    elif typing_inspect.is_classvar(val):
        return UNRESOLVED_VALUE
    elif is_instance_of_typing_name(
            val, "_ForwardRef") or is_instance_of_typing_name(
                val, "ForwardRef"):
        # This has issues because the forward ref may be defined in a different file, in
        # which case we don't know which names are valid in it.
        with qcore.override(ctx, "suppress_undefined_name", True):
            return UNRESOLVED_VALUE
    elif val is Ellipsis:
        # valid in Callable[..., ]
        return UNRESOLVED_VALUE
    elif is_instance_of_typing_name(val, "_TypeAlias"):
        # typing.Pattern and Match, which are not normal generic types for some reason
        return GenericValue(val.impl_type,
                            [_type_from_runtime(val.type_var, ctx)])
    else:
        origin = get_origin(val)
        if origin is not None:
            return TypedValue(origin)
        ctx.show_error("Invalid type annotation %s" % (val, ))
        return UNRESOLVED_VALUE
Пример #15
0
 def check_yield_result_assignment(self,
                                   in_yield: bool) -> ContextManager[None]:
     return qcore.override(self, "in_yield_result_assignment", in_yield)