Skip to content

Commit 10315fd

Browse files
DanielNoordPierre-Sassoulascdce8p
authored
Fix inference of living container elements (#1663)
Co-authored-by: Pierre Sassoulas <[email protected]> Co-authored-by: Marc Mueller <[email protected]>
1 parent 07ab176 commit 10315fd

File tree

3 files changed

+75
-7
lines changed

3 files changed

+75
-7
lines changed

ChangeLog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ Release date: TBA
7878

7979
Closes #1403
8080

81+
* Fixed inference of elements of living container objects such as tuples and sets in the
82+
``sys`` and ``ssl`` modules.
83+
8184
* Add ``pathlib`` brain to handle ``pathlib.PurePath.parents`` inference.
8285

8386
Closes PyCQA/pylint#5783

astroid/nodes/node_classes.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import sys
1212
import typing
1313
import warnings
14-
from collections.abc import Generator
14+
from collections.abc import Generator, Iterable, Mapping
1515
from functools import lru_cache
1616
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, TypeVar, Union
1717

@@ -5375,6 +5375,32 @@ def postinit(self, *, patterns: list[Pattern]) -> None:
53755375
}
53765376

53775377

5378+
def _create_basic_elements(
5379+
value: Iterable[Any], node: List | Set | Tuple
5380+
) -> list[NodeNG]:
5381+
"""Create a list of nodes to function as the elements of a new node."""
5382+
elements: list[NodeNG] = []
5383+
for element in value:
5384+
element_node = const_factory(element)
5385+
element_node.parent = node
5386+
elements.append(element_node)
5387+
return elements
5388+
5389+
5390+
def _create_dict_items(
5391+
values: Mapping[Any, Any], node: Dict
5392+
) -> list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]]:
5393+
"""Create a list of node pairs to function as the items of a new dict node."""
5394+
elements: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] = []
5395+
for key, value in values.items():
5396+
key_node = const_factory(key)
5397+
key_node.parent = node
5398+
value_node = const_factory(value)
5399+
value_node.parent = node
5400+
elements.append((key_node, value_node))
5401+
return elements
5402+
5403+
53785404
def const_factory(value: Any) -> List | Set | Tuple | Dict | Const | EmptyNode:
53795405
"""Return an astroid node for a python value."""
53805406
assert not isinstance(value, NodeNG)
@@ -5387,13 +5413,14 @@ def const_factory(value: Any) -> List | Set | Tuple | Dict | Const | EmptyNode:
53875413
node.object = value
53885414
return node
53895415

5390-
# TODO: We pass an empty list as elements for a sequence
5391-
# or a mapping, in order to avoid transforming
5392-
# each element to an AST. This is fixed in 2.0
5393-
# and this approach is a temporary hack.
5416+
instance: List | Set | Tuple | Dict
53945417
initializer_cls = CONST_CLS[value.__class__]
5395-
if issubclass(initializer_cls, (Dict, List, Set, Tuple)):
5418+
if issubclass(initializer_cls, (List, Set, Tuple)):
5419+
instance = initializer_cls()
5420+
instance.postinit(_create_basic_elements(value, instance))
5421+
return instance
5422+
if issubclass(initializer_cls, Dict):
53965423
instance = initializer_cls()
5397-
instance.postinit([])
5424+
instance.postinit(_create_dict_items(value, instance))
53985425
return instance
53995426
return Const(value)

tests/test_stdlib.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
2+
# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
3+
# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
4+
5+
"""Tests for modules in the stdlib."""
6+
7+
from astroid import nodes
8+
from astroid.builder import _extract_single_node
9+
10+
11+
class TestSys:
12+
"""Tests for the sys module."""
13+
14+
def test_sys_builtin_module_names(self) -> None:
15+
"""Test that we can gather the elements of a living tuple object."""
16+
node = _extract_single_node(
17+
"""
18+
import sys
19+
sys.builtin_module_names
20+
"""
21+
)
22+
inferred = list(node.infer())
23+
assert len(inferred) == 1
24+
assert isinstance(inferred[0], nodes.Tuple)
25+
assert inferred[0].elts
26+
27+
def test_sys_modules(self) -> None:
28+
"""Test that we can gather the items of a living dict object."""
29+
node = _extract_single_node(
30+
"""
31+
import sys
32+
sys.modules
33+
"""
34+
)
35+
inferred = list(node.infer())
36+
assert len(inferred) == 1
37+
assert isinstance(inferred[0], nodes.Dict)
38+
assert inferred[0].items

0 commit comments

Comments
 (0)