Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-astroid for openSUSE:Factory checked in at 2022-10-30 18:28:26 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-astroid (Old) and /work/SRC/openSUSE:Factory/.python-astroid.new.2275 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-astroid" Sun Oct 30 18:28:26 2022 rev:39 rq:1032176 version:2.12.12 Changes: -------- --- /work/SRC/openSUSE:Factory/python-astroid/python-astroid.changes 2022-10-14 15:40:12.723685232 +0200 +++ /work/SRC/openSUSE:Factory/.python-astroid.new.2275/python-astroid.changes 2022-10-30 18:28:30.294277051 +0100 @@ -1,0 +2,12 @@ +Thu Oct 27 21:41:48 UTC 2022 - Yogalakshmi Arunachalam <yarunacha...@suse.com> + +- Update to 2.12.12: + * Add the ``length`` parameter to ``hash.digest`` & ``hash.hexdigest`` in the ``hashlib`` brain. + Refs PyCQA/pylint#4039 + * Prevent a crash when a module's ``__path__`` attribute is unexpectedly missing. + Refs PyCQA/pylint#7592 + * Fix inferring attributes with empty annotation assignments if parent + class contains valid assignment. + Refs PyCQA/pylint#7631 + +------------------------------------------------------------------- Old: ---- astroid-2.12.11-gh.tar.gz New: ---- astroid-2.12.12-gh.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-astroid.spec ++++++ --- /var/tmp/diff_new_pack.elEn4w/_old 2022-10-30 18:28:30.858280098 +0100 +++ /var/tmp/diff_new_pack.elEn4w/_new 2022-10-30 18:28:30.862280120 +0100 @@ -17,7 +17,7 @@ Name: python-astroid -Version: 2.12.11 +Version: 2.12.12 Release: 0 Summary: Representation of Python source as an AST for pylint License: LGPL-2.1-or-later ++++++ astroid-2.12.11-gh.tar.gz -> astroid-2.12.12-gh.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/ChangeLog new/astroid-2.12.12/ChangeLog --- old/astroid-2.12.11/ChangeLog 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/ChangeLog 2022-10-19 10:44:58.000000000 +0200 @@ -16,16 +16,42 @@ Closes PyCQA/pylint#7488. -What's New in astroid 2.12.12? +What's New in astroid 2.12.13? ============================== Release date: TBA +What's New in astroid 2.12.12? +============================== +Release date: 2022-10-19 + +* Add the ``length`` parameter to ``hash.digest`` & ``hash.hexdigest`` in the ``hashlib`` brain. + + Refs PyCQA/pylint#4039 + +* Prevent a crash when a module's ``__path__`` attribute is unexpectedly missing. + + Refs PyCQA/pylint#7592 + +* Fix inferring attributes with empty annotation assignments if parent + class contains valid assignment. + + Refs PyCQA/pylint#7631 + + What's New in astroid 2.12.11? ============================== Release date: 2022-10-10 +* Improve detection of namespace packages for the modules with ``__spec__`` set to None. + + Closes PyCQA/pylint#7488. + +* Fixed a regression in the creation of the ``__init__`` of dataclasses with + multiple inheritance. + + Closes PyCQA/pylint#7434 What's New in astroid 2.12.10? diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/astroid/__pkginfo__.py new/astroid-2.12.12/astroid/__pkginfo__.py --- old/astroid-2.12.11/astroid/__pkginfo__.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/astroid/__pkginfo__.py 2022-10-19 10:44:58.000000000 +0200 @@ -2,5 +2,5 @@ # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt -__version__ = "2.12.11" +__version__ = "2.12.12" version = __version__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/astroid/brain/brain_dataclasses.py new/astroid-2.12.12/astroid/brain/brain_dataclasses.py --- old/astroid-2.12.11/astroid/brain/brain_dataclasses.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/astroid/brain/brain_dataclasses.py 2022-10-19 10:44:58.000000000 +0200 @@ -181,9 +181,12 @@ node: nodes.ClassDef, skippable_names: set[str] ) -> tuple[str, str]: """Iterate through all bases and add them to the list of arguments to add to the init.""" - prev_pos_only = "" - prev_kw_only = "" - for base in node.mro(): + pos_only_store: dict[str, tuple[str | None, str | None]] = {} + kw_only_store: dict[str, tuple[str | None, str | None]] = {} + # See TODO down below + # all_have_defaults = True + + for base in reversed(node.mro()): if not base.is_dataclass: continue try: @@ -191,29 +194,41 @@ except KeyError: continue - # Skip the self argument and check for duplicate arguments - arguments = base_init.args.format_args(skippable_names=skippable_names) - try: - new_prev_pos_only, new_prev_kw_only = arguments.split("*, ") - except ValueError: - new_prev_pos_only, new_prev_kw_only = arguments, "" - - if new_prev_pos_only: - # The split on '*, ' can crete a pos_only string that consists only of a comma - if new_prev_pos_only == ", ": - new_prev_pos_only = "" - elif not new_prev_pos_only.endswith(", "): - new_prev_pos_only += ", " - - # Dataclasses put last seen arguments at the front of the init - prev_pos_only = new_prev_pos_only + prev_pos_only - prev_kw_only = new_prev_kw_only + prev_kw_only - - # Add arguments to skippable arguments - skippable_names.update(arg.name for arg in base_init.args.args) - skippable_names.update(arg.name for arg in base_init.args.kwonlyargs) + pos_only, kw_only = base_init.args._get_arguments_data() + for posarg, data in pos_only.items(): + if posarg in skippable_names: + continue + # if data[1] is None: + # if all_have_defaults and pos_only_store: + # # TODO: This should return an Uninferable as this would raise + # # a TypeError at runtime. However, transforms can't return + # # Uninferables currently. + # pass + # all_have_defaults = False + pos_only_store[posarg] = data + + for kwarg, data in kw_only.items(): + if kwarg in skippable_names: + continue + kw_only_store[kwarg] = data + + pos_only, kw_only = "", "" + for pos_arg, data in pos_only_store.items(): + pos_only += pos_arg + if data[0]: + pos_only += ": " + data[0] + if data[1]: + pos_only += " = " + data[1] + pos_only += ", " + for kw_arg, data in kw_only_store.items(): + kw_only += kw_arg + if data[0]: + kw_only += ": " + data[0] + if data[1]: + kw_only += " = " + data[1] + kw_only += ", " - return prev_pos_only, prev_kw_only + return pos_only, kw_only def _generate_dataclass_init( @@ -282,7 +297,7 @@ params_string += ", " if prev_kw_only: - params_string += "*, " + prev_kw_only + ", " + params_string += "*, " + prev_kw_only if kw_only_decorated: params_string += ", ".join(params) + ", " elif kw_only_decorated: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/astroid/brain/brain_hashlib.py new/astroid-2.12.12/astroid/brain/brain_hashlib.py --- old/astroid-2.12.11/astroid/brain/brain_hashlib.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/astroid/brain/brain_hashlib.py 2022-10-19 10:44:58.000000000 +0200 @@ -10,48 +10,86 @@ def _hashlib_transform(): maybe_usedforsecurity = ", usedforsecurity=True" if PY39_PLUS else "" - signature = f"value=''{maybe_usedforsecurity}" + init_signature = f"value=''{maybe_usedforsecurity}" + digest_signature = "self" + shake_digest_signature = "self, length" + template = """ - class %(name)s(object): - def __init__(self, %(signature)s): pass - def digest(self): - return %(digest)s - def copy(self): - return self - def update(self, value): pass - def hexdigest(self): - return '' - @property - def name(self): - return %(name)r - @property - def block_size(self): - return 1 - @property - def digest_size(self): - return 1 + class %(name)s: + def __init__(self, %(init_signature)s): pass + def digest(%(digest_signature)s): + return %(digest)s + def copy(self): + return self + def update(self, value): pass + def hexdigest(%(digest_signature)s): + return '' + @property + def name(self): + return %(name)r + @property + def block_size(self): + return 1 + @property + def digest_size(self): + return 1 """ + algorithms_with_signature = dict.fromkeys( - ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"], signature + [ + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + "sha3_224", + "sha3_256", + "sha3_384", + "sha3_512", + ], + (init_signature, digest_signature), + ) + + blake2b_signature = ( + "data=b'', *, digest_size=64, key=b'', salt=b'', " + "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, " + f"node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}" + ) + + blake2s_signature = ( + "data=b'', *, digest_size=32, key=b'', salt=b'', " + "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, " + f"node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}" ) - blake2b_signature = f"data=b'', *, digest_size=64, key=b'', salt=b'', \ - person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \ - node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}" - blake2s_signature = f"data=b'', *, digest_size=32, key=b'', salt=b'', \ - person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \ - node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}" - new_algorithms = dict.fromkeys( - ["sha3_224", "sha3_256", "sha3_384", "sha3_512", "shake_128", "shake_256"], - signature, + + shake_algorithms = dict.fromkeys( + ["shake_128", "shake_256"], + (init_signature, shake_digest_signature), ) - algorithms_with_signature.update(new_algorithms) + algorithms_with_signature.update(shake_algorithms) + algorithms_with_signature.update( - {"blake2b": blake2b_signature, "blake2s": blake2s_signature} + { + "blake2b": (blake2b_signature, digest_signature), + "blake2s": (blake2s_signature, digest_signature), + } ) + classes = "".join( - template % {"name": hashfunc, "digest": 'b""', "signature": signature} - for hashfunc, signature in algorithms_with_signature.items() + template + % { + "name": hashfunc, + "digest": 'b""', + "init_signature": init_signature, + "digest_signature": digest_signature, + } + for hashfunc, ( + init_signature, + digest_signature, + ) in algorithms_with_signature.items() ) + return parse(classes) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/astroid/interpreter/_import/util.py new/astroid-2.12.12/astroid/interpreter/_import/util.py --- old/astroid-2.12.11/astroid/interpreter/_import/util.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/astroid/interpreter/_import/util.py 2022-10-19 10:44:58.000000000 +0200 @@ -42,6 +42,8 @@ found_spec = _find_spec_from_path( working_modname, path=last_submodule_search_locations ) + except AttributeError: + return False except ValueError: if modname == "__main__": return False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/astroid/nodes/node_classes.py new/astroid-2.12.12/astroid/nodes/node_classes.py --- old/astroid-2.12.11/astroid/nodes/node_classes.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/astroid/nodes/node_classes.py 2022-10-19 10:44:58.000000000 +0200 @@ -834,6 +834,75 @@ result.append(f"**{self.kwarg}") return ", ".join(result) + def _get_arguments_data( + self, + ) -> tuple[ + dict[str, tuple[str | None, str | None]], + dict[str, tuple[str | None, str | None]], + ]: + """Get the arguments as dictionary with information about typing and defaults. + + The return tuple contains a dictionary for positional and keyword arguments with their typing + and their default value, if any. + The method follows a similar order as format_args but instead of formatting into a string it + returns the data that is used to do so. + """ + pos_only: dict[str, tuple[str | None, str | None]] = {} + kw_only: dict[str, tuple[str | None, str | None]] = {} + + # Setup and match defaults with arguments + positional_only_defaults = [] + positional_or_keyword_defaults = self.defaults + if self.defaults: + args = self.args or [] + positional_or_keyword_defaults = self.defaults[-len(args) :] + positional_only_defaults = self.defaults[: len(self.defaults) - len(args)] + + for index, posonly in enumerate(self.posonlyargs): + annotation, default = self.posonlyargs_annotations[index], None + if annotation is not None: + annotation = annotation.as_string() + if positional_only_defaults: + default = positional_only_defaults[index].as_string() + pos_only[posonly.name] = (annotation, default) + + for index, arg in enumerate(self.args): + annotation, default = self.annotations[index], None + if annotation is not None: + annotation = annotation.as_string() + if positional_or_keyword_defaults: + defaults_offset = len(self.args) - len(positional_or_keyword_defaults) + default_index = index - defaults_offset + if ( + default_index > -1 + and positional_or_keyword_defaults[default_index] is not None + ): + default = positional_or_keyword_defaults[default_index].as_string() + pos_only[arg.name] = (annotation, default) + + if self.vararg: + annotation = self.varargannotation + if annotation is not None: + annotation = annotation.as_string() + pos_only[self.vararg] = (annotation, None) + + for index, kwarg in enumerate(self.kwonlyargs): + annotation = self.kwonlyargs_annotations[index] + if annotation is not None: + annotation = annotation.as_string() + default = self.kw_defaults[index] + if default is not None: + default = default.as_string() + kw_only[kwarg.name] = (annotation, default) + + if self.kwarg: + annotation = self.kwargannotation + if annotation is not None: + annotation = annotation.as_string() + kw_only[self.kwarg] = (annotation, None) + + return pos_only, kw_only + def default_value(self, argname): """Get the default value for an argument. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/astroid/nodes/scoped_nodes/scoped_nodes.py new/astroid-2.12.12/astroid/nodes/scoped_nodes/scoped_nodes.py --- old/astroid-2.12.11/astroid/nodes/scoped_nodes/scoped_nodes.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/astroid/nodes/scoped_nodes/scoped_nodes.py 2022-10-19 10:44:58.000000000 +0200 @@ -46,6 +46,7 @@ from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG from astroid.nodes.scoped_nodes.utils import builtin_lookup from astroid.nodes.utils import Position +from astroid.typing import InferenceResult if sys.version_info >= (3, 8): from functools import cached_property @@ -2519,7 +2520,12 @@ pass return bases.Instance(self) - def getattr(self, name, context=None, class_context=True): + def getattr( + self, + name: str, + context: InferenceContext | None = None, + class_context: bool = True, + ) -> list[NodeNG]: """Get an attribute from this class, using Python's attribute semantic. This method doesn't look in the :attr:`instance_attrs` dictionary @@ -2535,13 +2541,10 @@ metaclass will be done. :param name: The attribute to look for. - :type name: str :param class_context: Whether the attribute can be accessed statically. - :type class_context: bool :returns: The attribute. - :rtype: list(NodeNG) :raises AttributeInferenceError: If the attribute cannot be inferred. """ @@ -2564,17 +2567,16 @@ if class_context: values += self._metaclass_lookup_attribute(name, context) - if not values: - raise AttributeInferenceError(target=self, attribute=name, context=context) - - # Look for AnnAssigns, which are not attributes in the purest sense. - for value in values: + # Remove AnnAssigns without value, which are not attributes in the purest sense. + for value in values.copy(): if isinstance(value, node_classes.AssignName): stmt = value.statement(future=True) if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None: - raise AttributeInferenceError( - target=self, attribute=name, context=context - ) + values.pop(values.index(value)) + + if not values: + raise AttributeInferenceError(target=self, attribute=name, context=context) + return values def _metaclass_lookup_attribute(self, name, context): @@ -2616,14 +2618,17 @@ else: yield bases.BoundMethod(attr, self) - def igetattr(self, name, context=None, class_context=True): + def igetattr( + self, + name: str, + context: InferenceContext | None = None, + class_context: bool = True, + ) -> Iterator[InferenceResult]: """Infer the possible values of the given variable. :param name: The name of the variable to infer. - :type name: str :returns: The inferred possible values. - :rtype: iterable(NodeNG or Uninferable) """ # set lookup name since this is necessary to infer on import nodes for # instance diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/script/.contributors_aliases.json new/astroid-2.12.12/script/.contributors_aliases.json --- old/astroid-2.12.11/script/.contributors_aliases.json 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/script/.contributors_aliases.json 2022-10-19 10:44:58.000000000 +0200 @@ -13,6 +13,11 @@ "name": "Marc Mueller", "team": "Maintainers" }, + "31762852+mbyrne...@users.noreply.github.com": { + "mails": ["31762852+mbyrne...@users.noreply.github.com", "mbyrne...@gmail.com"], + "name": "Mark Byrne", + "team": "Maintainers" + }, "adam.grant.hen...@gmail.com": { "mails": ["adam.grant.hen...@gmail.com"], "name": "Adam Hendry" @@ -62,11 +67,6 @@ "name": "Dimitri Prybysh", "team": "Maintainers" }, - "31762852+mbyrne...@users.noreply.github.com": { - "mails": ["31762852+mbyrne...@users.noreply.github.com", "mbyrne...@gmail.com"], - "name": "Mark Byrne", - "team": "Maintainers" - }, "git...@euresti.com": { "mails": ["da...@dropbox.com", "git...@euresti.com"], "name": "David Euresti" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/tbump.toml new/astroid-2.12.12/tbump.toml --- old/astroid-2.12.11/tbump.toml 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/tbump.toml 2022-10-19 10:44:58.000000000 +0200 @@ -1,7 +1,7 @@ github_url = "https://github.com/PyCQA/astroid" [version] -current = "2.12.11" +current = "2.12.12" regex = ''' ^(?P<major>0|[1-9]\d*) \. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/tests/unittest_brain.py new/astroid-2.12.12/tests/unittest_brain.py --- old/astroid-2.12.11/tests/unittest_brain.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/tests/unittest_brain.py 2022-10-19 10:44:58.000000000 +0200 @@ -83,24 +83,44 @@ len(class_obj["__init__"].args.defaults), 2 if PY39_PLUS else 1 ) self.assertEqual(len(class_obj["update"].args.args), 2) - self.assertEqual(len(class_obj["digest"].args.args), 1) - self.assertEqual(len(class_obj["hexdigest"].args.args), 1) def test_hashlib(self) -> None: """Tests that brain extensions for hashlib work.""" hashlib_module = MANAGER.ast_from_module_name("hashlib") - for class_name in ("md5", "sha1"): + for class_name in ( + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + "sha3_224", + "sha3_256", + "sha3_384", + "sha3_512", + ): class_obj = hashlib_module[class_name] self._assert_hashlib_class(class_obj) + self.assertEqual(len(class_obj["digest"].args.args), 1) + self.assertEqual(len(class_obj["hexdigest"].args.args), 1) - def test_hashlib_py36(self) -> None: + def test_shake(self) -> None: + """Tests that the brain extensions for the hashlib shake algorithms work.""" hashlib_module = MANAGER.ast_from_module_name("hashlib") - for class_name in ("sha3_224", "sha3_512", "shake_128"): + for class_name in ("shake_128", "shake_256"): class_obj = hashlib_module[class_name] self._assert_hashlib_class(class_obj) + self.assertEqual(len(class_obj["digest"].args.args), 2) + self.assertEqual(len(class_obj["hexdigest"].args.args), 2) + + def test_blake2(self) -> None: + """Tests that the brain extensions for the hashlib blake2 hash functions work.""" + hashlib_module = MANAGER.ast_from_module_name("hashlib") for class_name in ("blake2b", "blake2s"): class_obj = hashlib_module[class_name] self.assertEqual(len(class_obj["__init__"].args.args), 2) + self.assertEqual(len(class_obj["digest"].args.args), 1) + self.assertEqual(len(class_obj["hexdigest"].args.args), 1) class CollectionsDequeTests(unittest.TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/tests/unittest_brain_dataclasses.py new/astroid-2.12.12/tests/unittest_brain_dataclasses.py --- old/astroid-2.12.11/tests/unittest_brain_dataclasses.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/tests/unittest_brain_dataclasses.py 2022-10-19 10:44:58.000000000 +0200 @@ -918,6 +918,7 @@ """Regression test for dataclasses with multiple inheritance. Reported in https://github.com/PyCQA/pylint/issues/7427 + Reported in https://github.com/PyCQA/pylint/issues/7434 """ first, second, overwritten, overwriting, mixed = astroid.extract_node( """ @@ -991,6 +992,75 @@ assert [a.name for a in mixed_init.args.args] == ["self", "_abc", "ghi"] assert [a.value for a in mixed_init.args.defaults] == [1, 3] + first = astroid.extract_node( + """ + from dataclasses import dataclass + + @dataclass + class BaseParent: + required: bool + + @dataclass + class FirstChild(BaseParent): + ... + + @dataclass + class SecondChild(BaseParent): + optional: bool = False + + @dataclass + class GrandChild(FirstChild, SecondChild): + ... + + GrandChild.__init__ #@ + """ + ) + + first_init: bases.UnboundMethod = next(first.infer()) + assert [a.name for a in first_init.args.args] == ["self", "required", "optional"] + assert [a.value for a in first_init.args.defaults] == [False] + + +@pytest.mark.xfail(reason="Transforms returning Uninferable isn't supported.") +def test_dataclass_non_default_argument_after_default() -> None: + """Test that a non-default argument after a default argument is not allowed. + + This should succeed, but the dataclass brain is a transform + which currently can't return an Uninferable correctly. Therefore, we can't + set the dataclass ClassDef node to be Uninferable currently. + Eventually it can be merged into test_dataclass_with_multiple_inheritance. + """ + + impossible = astroid.extract_node( + """ + from dataclasses import dataclass + + @dataclass + class BaseParent: + required: bool + + @dataclass + class FirstChild(BaseParent): + ... + + @dataclass + class SecondChild(BaseParent): + optional: bool = False + + @dataclass + class ThirdChild: + other: bool = False + + @dataclass + class ImpossibleGrandChild(FirstChild, SecondChild, ThirdChild): + ... + + ImpossibleGrandChild() #@ + """ + ) + + assert next(impossible.infer()) is Uninferable + def test_dataclass_inits_of_non_dataclasses() -> None: """Regression test for __init__ mangling for non dataclasses. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/tests/unittest_manager.py new/astroid-2.12.12/tests/unittest_manager.py --- old/astroid-2.12.11/tests/unittest_manager.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/tests/unittest_manager.py 2022-10-19 10:44:58.000000000 +0200 @@ -9,6 +9,7 @@ import unittest from collections.abc import Iterator from contextlib import contextmanager +from unittest import mock import pytest @@ -144,6 +145,14 @@ finally: astroid_module.__spec__ = original_spec + @mock.patch( + "astroid.interpreter._import.util._find_spec_from_path", + side_effect=AttributeError, + ) + def test_module_unexpectedly_missing_path(self, mocked) -> None: + """https://github.com/PyCQA/pylint/issues/7592""" + self.assertFalse(util.is_namespace("astroid")) + def test_module_unexpectedly_spec_is_none(self) -> None: astroid_module = sys.modules["astroid"] original_spec = astroid_module.__spec__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-2.12.11/tests/unittest_scoped_nodes.py new/astroid-2.12.12/tests/unittest_scoped_nodes.py --- old/astroid-2.12.11/tests/unittest_scoped_nodes.py 2022-10-10 13:58:08.000000000 +0200 +++ new/astroid-2.12.12/tests/unittest_scoped_nodes.py 2022-10-19 10:44:58.000000000 +0200 @@ -1268,6 +1268,22 @@ self.assertIsInstance(attr1, nodes.AssignName) self.assertEqual(attr1.name, "attr") + @staticmethod + def test_getattr_with_enpty_annassign() -> None: + code = """ + class Parent: + attr: int = 2 + + class Child(Parent): #@ + attr: int + """ + child = extract_node(code) + attr = child.getattr("attr") + assert len(attr) == 1 + assert isinstance(attr[0], nodes.AssignName) + assert attr[0].name == "attr" + assert attr[0].lineno == 3 + def test_function_with_decorator_lineno(self) -> None: data = """ @f(a=2,