def infer_named_tuple( node: nodes.Call, context: InferenceContext | None = None) -> Iterator[nodes.ClassDef]: """Specific inference function for namedtuple Call node""" tuple_base_name: list[nodes.NodeNG] = [ 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 visit_call(self, node: nodes.Call) -> None: if isinstance(node.func, nodes.Name): name = node.func.name # ignore the name if it's not a builtin (i.e. not defined in the # locals nor globals scope) if not (name in node.frame(future=True) or name in node.root()): if name in self.linter.config.bad_functions: hint = BUILTIN_HINTS.get(name) args = f"{name!r}. {hint}" if hint else repr(name) self.add_message("bad-builtin", node=node, args=args)
def _check_use_maxsplit_arg(self, node: nodes.Call) -> None: """Add message when accessing first or last elements of a str.split() or str.rsplit().""" # Check if call is split() or rsplit() if not (isinstance(node.func, nodes.Attribute) and node.func.attrname in {"split", "rsplit"} and isinstance( utils.safe_infer(node.func), astroid.BoundMethod)): return try: utils.get_argument_from_call(node, 0, "sep") except utils.NoSuchArgumentError: return try: # Ignore if maxsplit arg has been set utils.get_argument_from_call(node, 1, "maxsplit") return except utils.NoSuchArgumentError: pass if isinstance(node.parent, nodes.Subscript): try: subscript_value = utils.get_subscript_const_value( node.parent).value except utils.InferredTypeError: return # Check for cases where variable (Name) subscripts may be mutated within a loop if isinstance(node.parent.slice, nodes.Name): # Check if loop present within the scope of the node scope = node.scope() for loop_node in scope.nodes_of_class( (nodes.For, nodes.While)): if not loop_node.parent_of(node): continue # Check if var is mutated within loop (Assign/AugAssign) for assignment_node in loop_node.nodes_of_class( nodes.AugAssign): if node.parent.slice.name == assignment_node.target.name: return for assignment_node in loop_node.nodes_of_class( nodes.Assign): if node.parent.slice.name in [ n.name for n in assignment_node.targets ]: return if subscript_value in (-1, 0): fn_name = node.func.attrname new_fn = "rsplit" if subscript_value == -1 else "split" new_name = ( node.func.as_string().rsplit(fn_name, maxsplit=1)[0] + new_fn + f"({node.args[0].as_string()}, maxsplit=1)[{subscript_value}]" ) self.add_message("use-maxsplit-arg", node=node, args=(new_name, ))
def visit_call(self, node: nodes.Call) -> None: """Visit a Call node -> check if this is not a disallowed builtin call and check for * or ** use. """ self._check_misplaced_format_function(node) if isinstance(node.func, nodes.Name): name = node.func.name # ignore the name if it's not a builtin (i.e. not defined in the # locals nor globals scope) if not (name in node.frame(future=True) or name in node.root()): if name == "exec": self.add_message("exec-used", node=node) elif name == "reversed": self._check_reversed(node) elif name == "eval": self.add_message("eval-used", node=node)
def visit_call(self, node: nodes.Call) -> None: """Called when a :class:`.nodes.Call` node is visited. See :mod:`astroid` for the description of available nodes. """ if not (isinstance(node.func, nodes.Attribute) and isinstance(node.func.expr, nodes.Name) and node.func.expr.name == self.config.store_locals_indicator and node.func.attrname == "create"): return in_class = node.frame() for param in node.args: in_class.locals[param.name] = node
def _looks_like_pattern_or_match(node: nodes.Call) -> bool: """Check for re.Pattern or re.Match call in stdlib. Match these patterns from stdlib/re.py ```py Pattern = type(...) Match = type(...) ``` """ return (node.root().name == "re" and isinstance(node.func, nodes.Name) and node.func.name == "type" and isinstance(node.parent, nodes.Assign) and len(node.parent.targets) == 1 and isinstance(node.parent.targets[0], nodes.AssignName) and node.parent.targets[0].name in ("Pattern", "Match"))
def _check_consider_iterating_dictionary(self, node: nodes.Call) -> None: if not isinstance(node.func, nodes.Attribute): return if node.func.attrname != "keys": return comp_ancestor = utils.get_node_first_ancestor_of_type( node, nodes.Compare) if (isinstance( node.parent, (nodes.For, nodes.Comprehension)) or comp_ancestor and any( op for op, comparator in comp_ancestor.ops if op in {"in", "not in"} and (comparator in node.node_ancestors() or comparator is node))): inferred = utils.safe_infer(node.func) if not isinstance(inferred, astroid.BoundMethod) or not isinstance( inferred.bound, nodes.Dict): return self.add_message("consider-iterating-dictionary", node=node)