summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--astroid/brain/brain_dataclasses.py12
-rw-r--r--astroid/brain/brain_namedtuple_enum.py14
-rw-r--r--astroid/manager.py2
-rw-r--r--tests/unittest_brain.py21
-rw-r--r--tests/unittest_brain_dataclasses.py18
-rw-r--r--tests/unittest_manager.py15
7 files changed, 78 insertions, 10 deletions
diff --git a/ChangeLog b/ChangeLog
index 4fe21c5f..4d2ea83a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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():