diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | astroid/brain/brain_dataclasses.py | 12 | ||||
-rw-r--r-- | astroid/brain/brain_namedtuple_enum.py | 14 | ||||
-rw-r--r-- | astroid/manager.py | 2 | ||||
-rw-r--r-- | tests/unittest_brain.py | 21 | ||||
-rw-r--r-- | tests/unittest_brain_dataclasses.py | 18 | ||||
-rw-r--r-- | tests/unittest_manager.py | 15 |
7 files changed, 78 insertions, 10 deletions
@@ -15,11 +15,17 @@ Release date: TBA * ``op`` (``str``) for ``AugAssign``, ``BinOp``, ``BoolOp``, ``UnaryOp`` * ``names`` (``list[tuple[str, str | None]]``) for ``Import`` +* Support pyz imports + + Closes PyCQA/pylint#3887 What's New in astroid 2.7.4? ============================ Release date: TBA +* Fixed bug in inference of dataclass field calls. + + Closes PyCQA/pylint#4963 What's New in astroid 2.7.3? diff --git a/astroid/brain/brain_dataclasses.py b/astroid/brain/brain_dataclasses.py index e010a514..0bd394e3 100644 --- a/astroid/brain/brain_dataclasses.py +++ b/astroid/brain/brain_dataclasses.py @@ -12,10 +12,16 @@ from typing import Generator, List, Optional, Tuple from astroid import context, inference_tip from astroid.builder import parse from astroid.const import PY37_PLUS, PY39_PLUS -from astroid.exceptions import AstroidSyntaxError, InferenceError, MroError +from astroid.exceptions import ( + AstroidSyntaxError, + InferenceError, + MroError, + UseInferenceDefault, +) from astroid.manager import AstroidManager from astroid.nodes.node_classes import ( AnnAssign, + Assign, AssignName, Attribute, Call, @@ -231,9 +237,11 @@ def infer_dataclass_attribute( def infer_dataclass_field_call( - node: AssignName, ctx: context.InferenceContext = None + node: Call, ctx: Optional[context.InferenceContext] = None ) -> Generator: """Inference tip for dataclass field calls.""" + if not isinstance(node.parent, (AnnAssign, Assign)): + raise UseInferenceDefault field_call = node.parent.value default_type, default = _get_field_default(field_call) if not default_type: diff --git a/astroid/brain/brain_namedtuple_enum.py b/astroid/brain/brain_namedtuple_enum.py index f79e44cc..4ed105ca 100644 --- a/astroid/brain/brain_namedtuple_enum.py +++ b/astroid/brain/brain_namedtuple_enum.py @@ -484,13 +484,13 @@ def infer_typing_namedtuple_class(class_node, context=None): for method in class_node.mymethods(): generated_class_node.locals[method.name] = [method] - for assign in class_node.body: - if not isinstance(assign, nodes.Assign): - continue - - for target in assign.targets: - attr = target.name - generated_class_node.locals[attr] = class_node.locals[attr] + for body_node in class_node.body: + if isinstance(body_node, nodes.Assign): + for target in body_node.targets: + attr = target.name + generated_class_node.locals[attr] = class_node.locals[attr] + elif isinstance(body_node, nodes.ClassDef): + generated_class_node.locals[body_node.name] = [body_node] return iter((generated_class_node,)) diff --git a/astroid/manager.py b/astroid/manager.py index 5575151e..89ef2ac6 100644 --- a/astroid/manager.py +++ b/astroid/manager.py @@ -46,7 +46,7 @@ from astroid.modutils import ( ) from astroid.transforms import TransformVisitor -ZIP_IMPORT_EXTS = (".zip", ".egg", ".whl") +ZIP_IMPORT_EXTS = (".zip", ".egg", ".whl", ".pyz", ".pyzw") def safe_repr(obj): diff --git a/tests/unittest_brain.py b/tests/unittest_brain.py index fd3cfae7..c87fa0d0 100644 --- a/tests/unittest_brain.py +++ b/tests/unittest_brain.py @@ -1644,6 +1644,27 @@ class TypingBrain(unittest.TestCase): inferred = next(node.infer()) self.assertIsInstance(inferred, nodes.ClassDef, node.as_string()) + def test_namedtuple_nested_class(self): + result = builder.extract_node( + """ + from typing import NamedTuple + + class Example(NamedTuple): + class Foo: + bar = "bar" + + Example + """ + ) + inferred = next(result.infer()) + self.assertIsInstance(inferred, astroid.ClassDef) + + class_def_attr = inferred.getattr("Foo")[0] + self.assertIsInstance(class_def_attr, astroid.ClassDef) + attr_def = class_def_attr.getattr("bar")[0] + attr = next(attr_def.infer()) + self.assertEqual(attr.value, "bar") + @test_utils.require_version(minver="3.7") def test_tuple_type(self): node = builder.extract_node( diff --git a/tests/unittest_brain_dataclasses.py b/tests/unittest_brain_dataclasses.py index 97ec18ec..62c0af7a 100644 --- a/tests/unittest_brain_dataclasses.py +++ b/tests/unittest_brain_dataclasses.py @@ -646,3 +646,21 @@ def test_invalid_init(module: str): ) init = next(node.infer()) assert init._proxied.parent.name == "object" + + +@parametrize_module +def test_annotated_enclosed_field_call(module: str): + """Test inference of dataclass attribute with a field call in another function call""" + node = astroid.extract_node( + f""" + from {module} import dataclass, field + from typing import cast + + @dataclass + class A: + attribute: int = cast(int, field(default_factory=dict)) + """ + ) + inferred = node.inferred() + assert len(inferred) == 1 and isinstance(inferred[0], nodes.ClassDef) + assert "attribute" in inferred[0].instance_attrs diff --git a/tests/unittest_manager.py b/tests/unittest_manager.py index 43563de6..5db1740f 100644 --- a/tests/unittest_manager.py +++ b/tests/unittest_manager.py @@ -221,6 +221,21 @@ class AstroidManagerTest( os.path.sep.join(["data", os.path.normcase("MyPyPa-0.1.0-py2.5.zip")]) ) + def test_ast_from_module_name_pyz(self) -> None: + try: + linked_file_name = os.path.join( + resources.RESOURCE_PATH, "MyPyPa-0.1.0-py2.5.pyz" + ) + os.symlink( + os.path.join(resources.RESOURCE_PATH, "MyPyPa-0.1.0-py2.5.zip"), + linked_file_name, + ) + + with self._restore_package_cache(): + self._test_ast_from_zip(linked_file_name) + finally: + os.remove(linked_file_name) + def test_zip_import_data(self) -> None: """check if zip_import_data works""" with self._restore_package_cache(): |