From 7db01c1fad6059a7a75b66998a5b958330845846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Wed, 22 Jun 2022 13:27:56 +0200 Subject: [PATCH] Add typing to ``const_factory`` (#1650) --- astroid/nodes/node_classes.py | 79 ++++++++++++----------------------- astroid/raw_building.py | 3 +- 2 files changed, 28 insertions(+), 54 deletions(-) diff --git a/astroid/nodes/node_classes.py b/astroid/nodes/node_classes.py index 4d27e1e59e..a906f1f781 100644 --- a/astroid/nodes/node_classes.py +++ b/astroid/nodes/node_classes.py @@ -5350,7 +5350,9 @@ def postinit(self, *, patterns: list[Pattern]) -> None: # constants ############################################################## -CONST_CLS = { +# The _proxied attribute of all container types (List, Tuple, etc.) +# are set during bootstrapping by _astroid_bootstrapping(). +CONST_CLS: dict[type, type[NodeNG]] = { list: List, tuple: Tuple, dict: Dict, @@ -5358,63 +5360,34 @@ def postinit(self, *, patterns: list[Pattern]) -> None: type(None): Const, type(NotImplemented): Const, type(...): Const, + bool: Const, + int: Const, + float: Const, + complex: Const, + str: Const, + bytes: Const, } -def _update_const_classes(): - """update constant classes, so the keys of CONST_CLS can be reused""" - klasses = (bool, int, float, complex, str, bytes) - for kls in klasses: - CONST_CLS[kls] = Const - - -_update_const_classes() - - -def _two_step_initialization(cls, value): - instance = cls() - instance.postinit(value) - return instance - - -def _dict_initialization(cls, value): - if isinstance(value, dict): - value = tuple(value.items()) - return _two_step_initialization(cls, value) - - -_CONST_CLS_CONSTRUCTORS = { - List: _two_step_initialization, - Tuple: _two_step_initialization, - Dict: _dict_initialization, - Set: _two_step_initialization, - Const: lambda cls, value: cls(value), -} - - -def const_factory(value): - """return an astroid node for a python value""" - # XXX we should probably be stricter here and only consider stuff in - # CONST_CLS or do better treatment: in case where value is not in CONST_CLS, - # we should rather recall the builder on this value than returning an empty - # node (another option being that const_factory shouldn't be called with something - # not in CONST_CLS) +def const_factory(value: Any) -> List | Set | Tuple | Dict | Const | EmptyNode: + """Return an astroid node for a python value.""" assert not isinstance(value, NodeNG) - # Hack for ignoring elements of a sequence - # or a mapping, in order to avoid transforming - # each element to an AST. This is fixed in 2.0 - # and this approach is a temporary hack. - if isinstance(value, (list, set, tuple, dict)): - elts = [] - else: - elts = value - - try: - initializer_cls = CONST_CLS[value.__class__] - initializer = _CONST_CLS_CONSTRUCTORS[initializer_cls] - return initializer(initializer_cls, elts) - except (KeyError, AttributeError): + # This only handles instances of the CONST types. Any + # subclasses get inferred as EmptyNode. + # TODO: See if we should revisit these with the normal builder. + if value.__class__ not in CONST_CLS: node = EmptyNode() node.object = value return node + + # TODO: We pass an empty list as elements for a sequence + # or a mapping, in order to avoid transforming + # each element to an AST. This is fixed in 2.0 + # and this approach is a temporary hack. + initializer_cls = CONST_CLS[value.__class__] + if issubclass(initializer_cls, (Dict, List, Set, Tuple)): + instance = initializer_cls() + instance.postinit([]) + return instance + return Const(value) diff --git a/astroid/raw_building.py b/astroid/raw_building.py index 59d0613ce2..93e3ff55ae 100644 --- a/astroid/raw_building.py +++ b/astroid/raw_building.py @@ -478,7 +478,7 @@ def imported_member(self, node, member, name: str) -> bool: # astroid bootstrapping ###################################################### -_CONST_PROXY = {} +_CONST_PROXY: dict[type, nodes.ClassDef] = {} def _set_proxied(const): @@ -505,6 +505,7 @@ def _astroid_bootstrapping(): proxy.parent = astroid_builtin else: proxy = astroid_builtin.getattr(cls.__name__)[0] + assert isinstance(proxy, nodes.ClassDef) if cls in (dict, list, set, tuple): node_cls._proxied = proxy else: