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
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
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])
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
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])
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
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
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
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
def name_node(draw, name=None): if not name: node = nodes.Name(draw(valid_identifier())) else: node = nodes.Name(draw(name)) return node
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