Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make constants have synthetic root as their parent #2602

Merged
merged 3 commits into from
Oct 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Release date: TBA
* Removed internal functions ``infer_numpy_member``, ``name_looks_like_numpy_member``, and
``attribute_looks_like_numpy_member`` from ``astroid.brain.brain_numpy_utils``.

* Constants now have a parent of ``nodes.SYNTHETIC_ROOT``.

* Fix crashes with large positive and negative list multipliers.

Closes #2521
Expand Down
2 changes: 1 addition & 1 deletion astroid/brain/brain_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def infer_namespace(node, context: InferenceContext | None = None):
"Namespace",
lineno=node.lineno,
col_offset=node.col_offset,
parent=AstroidManager().synthetic_root, # this class is not real
parent=nodes.SYNTHETIC_ROOT, # this class is not real
end_lineno=node.end_lineno,
end_col_offset=node.end_col_offset,
)
Expand Down
5 changes: 1 addition & 4 deletions astroid/brain/brain_builtin_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes import scoped_nodes
from astroid.raw_building import build_module
from astroid.typing import (
ConstFactoryResult,
InferenceResult,
Expand Down Expand Up @@ -165,8 +164,6 @@ def _extend_builtins(class_transforms):

def on_bootstrap():
"""Called by astroid_bootstrapping()."""
AstroidManager().cache_module(build_module("__astroid_synthetic"))

_extend_builtins(
{
"bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"),
Expand Down Expand Up @@ -653,7 +650,7 @@ def infer_property(
# node.frame. It's somewhere in the builtins module, but we are special
# casing it for each "property()" call, so we are making up the
# definition on the spot, ad-hoc.
parent=AstroidManager().synthetic_root,
parent=scoped_nodes.SYNTHETIC_ROOT,
)
prop_func.postinit(
body=[],
Expand Down
5 changes: 3 additions & 2 deletions astroid/brain/brain_namedtuple_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
)
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.scoped_nodes.scoped_nodes import SYNTHETIC_ROOT

ENUM_QNAME: Final[str] = "enum.Enum"
TYPING_NAMEDTUPLE_QUALIFIED: Final = {
Expand Down Expand Up @@ -194,7 +195,7 @@ def infer_named_tuple(
"""Specific inference function for namedtuple Call node."""
tuple_base: nodes.Name = _extract_single_node("tuple")
class_node, name, attributes = infer_func_form(
node, tuple_base, parent=AstroidManager().synthetic_root, context=context
node, tuple_base, parent=SYNTHETIC_ROOT, context=context
)

call_site = arguments.CallSite.from_call(node, context=context)
Expand Down Expand Up @@ -360,7 +361,7 @@ def value(self):
class_node = infer_func_form(
node,
enum_meta,
parent=AstroidManager().synthetic_root,
parent=SYNTHETIC_ROOT,
context=context,
enum=True,
)[0]
Expand Down
4 changes: 0 additions & 4 deletions astroid/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,6 @@ def unregister_transform(self):
def builtins_module(self) -> nodes.Module:
return self.astroid_cache["builtins"]

@property
def synthetic_root(self) -> nodes.Module:
return self.astroid_cache["__astroid_synthetic"]

@property
def prefer_stubs(self) -> bool:
return AstroidManager.brain["prefer_stubs"]
Expand Down
2 changes: 2 additions & 0 deletions astroid/nodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
unpack_infer,
)
from astroid.nodes.scoped_nodes import (
SYNTHETIC_ROOT,
AsyncFunctionDef,
ClassDef,
ComprehensionScope,
Expand Down Expand Up @@ -276,6 +277,7 @@
"Position",
"Raise",
"Return",
"SYNTHETIC_ROOT",
"Set",
"SetComp",
"Slice",
Expand Down
9 changes: 5 additions & 4 deletions astroid/nodes/node_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from astroid.nodes import _base_nodes
from astroid.nodes.const import OP_PRECEDENCE
from astroid.nodes.node_ng import NodeNG
from astroid.nodes.scoped_nodes import SYNTHETIC_ROOT
from astroid.typing import (
ConstFactoryResult,
InferenceErrorInfo,
Expand Down Expand Up @@ -2038,7 +2039,7 @@ def __init__(
value: Any,
lineno: int | None = None,
col_offset: int | None = None,
parent: NodeNG | None = None,
parent: NodeNG = SYNTHETIC_ROOT,
kind: str | None = None,
*,
end_lineno: int | None = None,
Expand Down Expand Up @@ -2550,7 +2551,7 @@ def __init__(
self,
lineno: None = None,
col_offset: None = None,
parent: None = None,
parent: NodeNG = SYNTHETIC_ROOT,
*,
end_lineno: None = None,
end_col_offset: None = None,
Expand Down Expand Up @@ -5535,7 +5536,7 @@ def const_factory(value: Any) -> ConstFactoryResult:
instance = initializer_cls(
lineno=None,
col_offset=None,
parent=None,
parent=SYNTHETIC_ROOT,
end_lineno=None,
end_col_offset=None,
)
Expand All @@ -5545,7 +5546,7 @@ def const_factory(value: Any) -> ConstFactoryResult:
instance = initializer_cls(
lineno=None,
col_offset=None,
parent=None,
parent=SYNTHETIC_ROOT,
end_lineno=None,
end_col_offset=None,
)
Expand Down
2 changes: 2 additions & 0 deletions astroid/nodes/scoped_nodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG
from astroid.nodes.scoped_nodes.scoped_nodes import (
SYNTHETIC_ROOT,
AsyncFunctionDef,
ClassDef,
DictComp,
Expand All @@ -37,6 +38,7 @@
"ListComp",
"LocalsDictNodeNG",
"Module",
"SYNTHETIC_ROOT",
"SetComp",
"builtin_lookup",
"function_to_method",
Expand Down
64 changes: 28 additions & 36 deletions astroid/nodes/scoped_nodes/scoped_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,7 @@
from astroid.interpreter.dunder_lookup import lookup
from astroid.interpreter.objectmodel import ClassModel, FunctionModel, ModuleModel
from astroid.manager import AstroidManager
from astroid.nodes import (
Arguments,
Const,
NodeNG,
_base_nodes,
const_factory,
node_classes,
)
from astroid.nodes import _base_nodes, node_classes
from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG
from astroid.nodes.scoped_nodes.utils import builtin_lookup
from astroid.nodes.utils import Position
Expand All @@ -60,6 +53,7 @@

if TYPE_CHECKING:
from astroid import nodes, objects
from astroid.nodes import Arguments, Const, NodeNG
from astroid.nodes._base_nodes import LookupMixIn


Expand Down Expand Up @@ -177,6 +171,15 @@ def function_to_method(n, klass):
return n


def _infer_last(
arg: SuccessfulInferenceResult, context: InferenceContext
) -> InferenceResult:
res = util.Uninferable
for b in arg.infer(context=context.clone()):
res = b
return res


class Module(LocalsDictNodeNG):
"""Class representing an :class:`ast.Module` node.

Expand Down Expand Up @@ -353,7 +356,7 @@ def getattr(
if name in self.special_attributes and not ignore_locals and not name_in_locals:
result = [self.special_attributes.lookup(name)]
if name == "__name__":
main_const = const_factory("__main__")
main_const = node_classes.const_factory("__main__")
main_const.parent = AstroidManager().builtins_module
result.append(main_const)
elif not ignore_locals and name_in_locals:
Expand Down Expand Up @@ -607,6 +610,14 @@ def _infer(
yield self


class __SyntheticRoot(Module):
def __init__(self):
super().__init__("__astroid_synthetic", pure_python=False)


SYNTHETIC_ROOT = __SyntheticRoot()


class GeneratorExp(ComprehensionScope):
"""Class representing an :class:`ast.GeneratorExp` node.

Expand Down Expand Up @@ -1538,10 +1549,7 @@ def infer_yield_result(self, context: InferenceContext | None = None):
"""
for yield_ in self.nodes_of_class(node_classes.Yield):
if yield_.value is None:
const = node_classes.Const(None)
const.parent = yield_
const.lineno = yield_.lineno
yield const
yield node_classes.Const(None, parent=yield_, lineno=yield_.lineno)
elif yield_.scope() == self:
yield from yield_.value.infer(context=context)

Expand All @@ -1551,6 +1559,8 @@ def infer_call_result(
context: InferenceContext | None = None,
) -> Iterator[InferenceResult]:
"""Infer what the function returns when called."""
if context is None:
context = InferenceContext()
if self.is_generator():
if isinstance(self, AsyncFunctionDef):
generator_cls: type[bases.Generator] = bases.AsyncGenerator
Expand All @@ -1572,7 +1582,7 @@ def infer_call_result(
and len(self.args.args) == 1
and self.args.vararg is not None
):
if isinstance(caller.args, Arguments):
if isinstance(caller.args, node_classes.Arguments):
assert caller.args.args is not None
metaclass = next(caller.args.args[0].infer(context), None)
elif isinstance(caller.args, list):
Expand All @@ -1582,27 +1592,14 @@ def infer_call_result(
f"caller.args was neither Arguments nor list; got {type(caller.args)}"
)
if isinstance(metaclass, ClassDef):
try:
class_bases = [
# Find the first non-None inferred base value
next(
b
for b in arg.infer(
context=context.clone() if context else context
)
if not (isinstance(b, Const) and b.value is None)
)
for arg in caller.args[1:]
]
except StopIteration as e:
raise InferenceError(node=caller.args[1:], context=context) from e
class_bases = [_infer_last(x, context) for x in caller.args[1:]]
new_class = ClassDef(
name="temporary_class",
lineno=0,
col_offset=0,
end_lineno=0,
end_col_offset=0,
parent=AstroidManager().synthetic_root,
parent=SYNTHETIC_ROOT,
)
new_class.hide = True
new_class.postinit(
Expand Down Expand Up @@ -2827,13 +2824,8 @@ def _inferred_bases(self, context: InferenceContext | None = None):

for stmt in self.bases:
try:
# Find the first non-None inferred base value
baseobj = next(
b
for b in stmt.infer(context=context.clone())
if not (isinstance(b, Const) and b.value is None)
)
except (InferenceError, StopIteration):
baseobj = _infer_last(stmt, context)
except InferenceError:
continue
if isinstance(baseobj, bases.Instance):
baseobj = baseobj._proxied
Expand Down
2 changes: 1 addition & 1 deletion astroid/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def __init__(self, call, name=None, lineno=None, col_offset=None, parent=None):
name,
lineno=lineno,
col_offset=col_offset,
parent=AstroidManager().synthetic_root,
parent=scoped_nodes.SYNTHETIC_ROOT,
end_col_offset=0,
end_lineno=0,
)
Expand Down
22 changes: 12 additions & 10 deletions tests/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,8 @@
AstroidBuildingError,
AstroidSyntaxError,
AttributeInferenceError,
ParentMissingError,
StatementMissing,
)
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import (
AssignAttr,
AssignName,
Expand All @@ -46,7 +44,13 @@
ImportFrom,
Tuple,
)
from astroid.nodes.scoped_nodes import ClassDef, FunctionDef, GeneratorExp, Module
from astroid.nodes.scoped_nodes import (
SYNTHETIC_ROOT,
ClassDef,
FunctionDef,
GeneratorExp,
Module,
)
from tests.testdata.python3.recursion_error import LONG_CHAINED_METHOD_CALL

from . import resources
Expand Down Expand Up @@ -623,12 +627,10 @@ def _test(self, value: Any) -> None:
with self.assertRaises(StatementMissing):
node.statement()

with self.assertRaises(ParentMissingError):
with pytest.warns(DeprecationWarning) as records:
node.frame(future=True)
assert len(records) == 1
with self.assertRaises(ParentMissingError):
node.frame()
with pytest.warns(DeprecationWarning) as records:
assert node.frame(future=True) is SYNTHETIC_ROOT
assert len(records) == 1
assert node.frame() is SYNTHETIC_ROOT

def test_none(self) -> None:
self._test(None)
Expand Down Expand Up @@ -1962,7 +1964,7 @@ def test_str_repr_no_warnings(node):
continue

if name == "parent" and "NodeNG" in param_type.annotation:
args[name] = AstroidManager().synthetic_root
args[name] = SYNTHETIC_ROOT
elif "int" in param_type.annotation:
args[name] = random.randint(0, 50)
elif (
Expand Down
2 changes: 1 addition & 1 deletion tests/test_regrtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def with_metaclass(meta, *bases):
class metaclass(meta):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
return type.__new__(metaclass, 'temporary_class', (), {})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully the crash referred to in the bitbucket issue that has been lost to time didn't depend on this syntax error. 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding, the only part that mattered was the «six.with_metaclass» in the line below. The piece of code above could have been either simplified significantly or omitted altogether

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh. Thanks for looking into it.


import lala

Expand Down
Loading