예제 #1
0
def build_function(name, args=None, posonlyargs=None, defaults=None, doc=None):
    """create and initialize an astroid FunctionDef node"""
    args, defaults, posonlyargs = args or [], defaults or [], posonlyargs or []
    # first argument is now a list of decorators
    func = nodes.FunctionDef(name, doc)
    func.args = argsnode = nodes.Arguments()
    argsnode.args = []
    argsnode.posonlyargs = []
    for arg in args:
        argsnode.args.append(nodes.Name())
        argsnode.args[-1].name = arg
        argsnode.args[-1].parent = argsnode
    for arg in posonlyargs:
        argsnode.posonlyargs.append(nodes.Name())
        argsnode.posonlyargs[-1].name = arg
        argsnode.posonlyargs[-1].parent = argsnode
    argsnode.defaults = []
    for default in defaults:
        argsnode.defaults.append(nodes.const_factory(default))
        argsnode.defaults[-1].parent = argsnode
    argsnode.kwarg = None
    argsnode.vararg = None
    argsnode.parent = func
    if args:
        register_arguments(func)
    return func
예제 #2
0
 def visit_name(self, node, parent):
     """visit a Name node by returning a fresh instance of it"""
     context = self._get_context(node)
     # True and False can be assigned to something in py2x, so we have to
     # check first the context.
     if context == astroid.Del:
         newnode = nodes.DelName(node.id, node.lineno, node.col_offset,
                                 parent)
     elif context == astroid.Store:
         newnode = nodes.AssignName(node.id, node.lineno, node.col_offset,
                                    parent)
     elif node.id in CONST_NAME_TRANSFORMS:
         newnode = nodes.Const(
             CONST_NAME_TRANSFORMS[node.id],
             getattr(node, "lineno", None),
             getattr(node, "col_offset", None),
             parent,
         )
         return newnode
     else:
         newnode = nodes.Name(node.id, node.lineno, node.col_offset, parent)
     # XXX REMOVE me :
     if context in (astroid.Del, astroid.Store):  # 'Aug' ??
         self._save_assignment(newnode)
     return newnode
예제 #3
0
def infer_named_tuple(node, context=None):
    """Specific inference function for namedtuple Call node"""
    tuple_base_name = nodes.Name(name="tuple", parent=node.root())
    class_node, name, attributes = infer_func_form(
        node, tuple_base_name, context=context
    )
    call_site = arguments.CallSite.from_call(node, context=context)
    node = extract_node("import collections; collections.namedtuple")
    try:

        func = next(node.infer())
    except StopIteration as e:
        raise InferenceError(node=node) from e
    try:
        rename = next(call_site.infer_argument(func, "rename", context)).bool_value()
    except (InferenceError, StopIteration):
        rename = False

    try:
        attributes = _check_namedtuple_attributes(name, attributes, rename)
    except AstroidTypeError as exc:
        raise UseInferenceDefault("TypeError: " + str(exc)) from exc
    except AstroidValueError as exc:
        raise UseInferenceDefault("ValueError: " + str(exc)) from exc

    replace_args = ", ".join(f"{arg}=None" for arg in attributes)
    field_def = (
        "    {name} = property(lambda self: self[{index:d}], "
        "doc='Alias for field number {index:d}')"
    )
    field_defs = "\n".join(
        field_def.format(name=name, index=index)
        for index, name in enumerate(attributes)
    )
    fake = AstroidBuilder(AstroidManager()).string_build(
        f"""
class {name}(tuple):
    __slots__ = ()
    _fields = {attributes!r}
    def _asdict(self):
        return self.__dict__
    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        return new(cls, iterable)
    def _replace(self, {replace_args}):
        return self
    def __getnewargs__(self):
        return tuple(self)
{field_defs}
    """
    )
    class_node.locals["_asdict"] = fake.body[0].locals["_asdict"]
    class_node.locals["_make"] = fake.body[0].locals["_make"]
    class_node.locals["_replace"] = fake.body[0].locals["_replace"]
    class_node.locals["_fields"] = fake.body[0].locals["_fields"]
    for attr in attributes:
        class_node.locals[attr] = fake.body[0].locals[attr]
    # we use UseInferenceDefault, we can't be a generator so return an iterator
    return iter([class_node])
예제 #4
0
def build_class(name, basenames=(), doc=None):
    """create and initialize an astroid ClassDef node"""
    node = nodes.ClassDef(name, doc)
    for base in basenames:
        basenode = nodes.Name(name=base)
        node.bases.append(basenode)
        basenode.parent = node
    return node
예제 #5
0
def infer_named_tuple(node, context=None):
    """Specific inference function for namedtuple Call node"""
    tuple_base_name = nodes.Name(name="tuple", parent=node.root())
    class_node, name, attributes = infer_func_form(
        node, tuple_base_name, context=context
    )
    call_site = arguments.CallSite.from_call(node)
    func = next(extract_node("import collections; collections.namedtuple").infer())
    try:
        rename = next(call_site.infer_argument(func, "rename", context)).bool_value()
    except InferenceError:
        rename = False

    if rename:
        attributes = _get_renamed_namedtuple_attributes(attributes)

    replace_args = ", ".join("{arg}=None".format(arg=arg) for arg in attributes)

    field_def = (
        "    {name} = property(lambda self: self[{index:d}], "
        "doc='Alias for field number {index:d}')"
    )
    field_defs = "\n".join(
        field_def.format(name=name, index=index)
        for index, name in enumerate(attributes)
    )
    fake = AstroidBuilder(MANAGER).string_build(
        """
class %(name)s(tuple):
    __slots__ = ()
    _fields = %(fields)r
    def _asdict(self):
        return self.__dict__
    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        return new(cls, iterable)
    def _replace(self, %(replace_args)s):
        return self
    def __getnewargs__(self):
        return tuple(self)
%(field_defs)s
    """
        % {
            "name": name,
            "fields": attributes,
            "field_defs": field_defs,
            "replace_args": replace_args,
        }
    )
    class_node.locals["_asdict"] = fake.body[0].locals["_asdict"]
    class_node.locals["_make"] = fake.body[0].locals["_make"]
    class_node.locals["_replace"] = fake.body[0].locals["_replace"]
    class_node.locals["_fields"] = fake.body[0].locals["_fields"]
    for attr in attributes:
        class_node.locals[attr] = fake.body[0].locals[attr]
    # we use UseInferenceDefault, we can't be a generator so return an iterator
    return iter([class_node])
예제 #6
0
def build_class(name: str,
                basenames: Iterable[str] = (),
                doc: str | None = None) -> nodes.ClassDef:
    """Create and initialize an astroid ClassDef node."""
    node = nodes.ClassDef(name)
    node.postinit(
        bases=[nodes.Name(name=base, parent=node) for base in basenames],
        body=[],
        decorators=None,
        doc_node=nodes.Const(value=doc) if doc else None,
    )
    return node
예제 #7
0
 def visit_name(self, node, parent):
     """visit a Name node by returning a fresh instance of it"""
     # True and False can be assigned to something in py2x, so we have to
     # check first the asscontext
     if self.asscontext == "Del":
         newnode = new.DelName()
     elif self.asscontext is not None: # Ass
         assert self.asscontext == "Ass"
         newnode = new.AssName()
     elif node.id in CONST_NAME_TRANSFORMS:
         newnode = new.Const(CONST_NAME_TRANSFORMS[node.id])
         _set_infos(node, newnode, parent)
         return newnode
     else:
         newnode = new.Name()
     _lineno_parent(node, newnode, parent)
     newnode.name = node.id
     # XXX REMOVE me :
     if self.asscontext in ('Del', 'Ass'): # 'Aug' ??
         self._save_assignment(newnode)
     return newnode
예제 #8
0
 def visit_name(self, node, parent, assign_ctx=None):
     """visit a Name node by returning a fresh instance of it"""
     # True and False can be assigned to something in py2x, so we have to
     # check first the asscontext
     # pylint: disable=redefined-variable-type
     if assign_ctx == "Del":
         newnode = new.DelName()
     elif assign_ctx is not None: # Ass
         newnode = new.AssName()
     elif node.id in CONST_NAME_TRANSFORMS:
         newnode = new.Const(CONST_NAME_TRANSFORMS[node.id])
         _set_infos(node, newnode, parent)
         return newnode
     else:
         newnode = new.Name()
     _lineno_parent(node, newnode, parent)
     newnode.name = node.id
     # XXX REMOVE me :
     if assign_ctx in ('Del', 'Assign'): # 'Aug' ??
         self._save_assignment(newnode)
     return newnode
예제 #9
0
def infer_enum_class(node):
    """Specific inference for enums."""
    for basename in (b for cls in node.mro() for b in cls.basenames):
        if basename not in ENUM_BASE_NAMES:
            continue
        if node.root().name == "enum":
            # Skip if the class is directly from enum module.
            break
        dunder_members = {}
        target_names = set()
        for local, values in node.locals.items():
            if any(not isinstance(value, nodes.AssignName) for value in values):
                continue

            stmt = values[0].statement(future=True)
            if isinstance(stmt, nodes.Assign):
                if isinstance(stmt.targets[0], nodes.Tuple):
                    targets = stmt.targets[0].itered()
                else:
                    targets = stmt.targets
            elif isinstance(stmt, nodes.AnnAssign):
                targets = [stmt.target]
            else:
                continue

            inferred_return_value = None
            if isinstance(stmt, nodes.Assign):
                if isinstance(stmt.value, nodes.Const):
                    if isinstance(stmt.value.value, str):
                        inferred_return_value = repr(stmt.value.value)
                    else:
                        inferred_return_value = stmt.value.value
                else:
                    inferred_return_value = stmt.value.as_string()

            new_targets = []
            for target in targets:
                if isinstance(target, nodes.Starred):
                    continue
                target_names.add(target.name)
                # Replace all the assignments with our mocked class.
                classdef = dedent(
                    """
                class {name}({types}):
                    @property
                    def value(self):
                        return {return_value}
                    @property
                    def name(self):
                        return "{name}"
                """.format(
                        name=target.name,
                        types=", ".join(node.basenames),
                        return_value=inferred_return_value,
                    )
                )
                if "IntFlag" in basename:
                    # Alright, we need to add some additional methods.
                    # Unfortunately we still can't infer the resulting objects as
                    # Enum members, but once we'll be able to do that, the following
                    # should result in some nice symbolic execution
                    classdef += INT_FLAG_ADDITION_METHODS.format(name=target.name)

                fake = AstroidBuilder(
                    AstroidManager(), apply_transforms=False
                ).string_build(classdef)[target.name]
                fake.parent = target.parent
                for method in node.mymethods():
                    fake.locals[method.name] = [method]
                new_targets.append(fake.instantiate_class())
                dunder_members[local] = fake
            node.locals[local] = new_targets
        members = nodes.Dict(parent=node)
        members.postinit(
            [
                (nodes.Const(k, parent=members), nodes.Name(v.name, parent=members))
                for k, v in dunder_members.items()
            ]
        )
        node.locals["__members__"] = [members]
        # The enum.Enum class itself defines two @DynamicClassAttribute data-descriptors
        # "name" and "value" (which we override in the mocked class for each enum member
        # above). When dealing with inference of an arbitrary instance of the enum
        # class, e.g. in a method defined in the class body like:
        #     class SomeEnum(enum.Enum):
        #         def method(self):
        #             self.name  # <- here
        # In the absence of an enum member called "name" or "value", these attributes
        # should resolve to the descriptor on that particular instance, i.e. enum member.
        # For "value", we have no idea what that should be, but for "name", we at least
        # know that it should be a string, so infer that as a guess.
        if "name" not in target_names:
            code = dedent(
                """
            @property
            def name(self):
                return ''
            """
            )
            name_dynamicclassattr = AstroidBuilder(AstroidManager()).string_build(code)[
                "name"
            ]
            node.locals["name"] = [name_dynamicclassattr]
        break
    return node
예제 #10
0
def name_node(draw, name=None):
    if not name:
        node = nodes.Name(draw(valid_identifier()))
    else:
        node = nodes.Name(draw(name))
    return node
예제 #11
0
def infer_enum_class(node):
    """Specific inference for enums."""
    for basename in node.basenames:
        # TODO: doesn't handle subclasses yet. This implementation
        # is a hack to support enums.
        if basename not in ENUM_BASE_NAMES:
            continue
        if node.root().name == "enum":
            # Skip if the class is directly from enum module.
            break
        dunder_members = {}
        for local, values in node.locals.items():
            if any(not isinstance(value, nodes.AssignName)
                   for value in values):
                continue

            stmt = values[0].statement()
            if isinstance(stmt, nodes.Assign):
                if isinstance(stmt.targets[0], nodes.Tuple):
                    targets = stmt.targets[0].itered()
                else:
                    targets = stmt.targets
            elif isinstance(stmt, nodes.AnnAssign):
                targets = [stmt.target]
            else:
                continue

            inferred_return_value = None
            if isinstance(stmt, nodes.Assign):
                if isinstance(stmt.value, nodes.Const):
                    if isinstance(stmt.value.value, str):
                        inferred_return_value = repr(stmt.value.value)
                    else:
                        inferred_return_value = stmt.value.value
                else:
                    inferred_return_value = stmt.value.as_string()

            new_targets = []
            for target in targets:
                if isinstance(target, nodes.Starred):
                    continue
                # Replace all the assignments with our mocked class.
                classdef = dedent("""
                class {name}({types}):
                    @property
                    def value(self):
                        return {return_value}
                    @property
                    def name(self):
                        return "{name}"
                """.format(
                    name=target.name,
                    types=", ".join(node.basenames),
                    return_value=inferred_return_value,
                ))
                if "IntFlag" in basename:
                    # Alright, we need to add some additional methods.
                    # Unfortunately we still can't infer the resulting objects as
                    # Enum members, but once we'll be able to do that, the following
                    # should result in some nice symbolic execution
                    classdef += INT_FLAG_ADDITION_METHODS.format(
                        name=target.name)

                fake = AstroidBuilder(MANAGER).string_build(classdef)[
                    target.name]
                fake.parent = target.parent
                for method in node.mymethods():
                    fake.locals[method.name] = [method]
                new_targets.append(fake.instantiate_class())
                dunder_members[local] = fake
            node.locals[local] = new_targets
        members = nodes.Dict(parent=node)
        members.postinit([(nodes.Const(k, parent=members),
                           nodes.Name(v.name, parent=members))
                          for k, v in dunder_members.items()])
        node.locals["__members__"] = [members]
        break
    return node