From f19fc0a52040293bede19d0ab83cd0613b73fb78 Mon Sep 17 00:00:00 2001 From: Artem Yurchenko Date: Thu, 26 Sep 2024 19:56:45 -0700 Subject: [PATCH 1/4] disable AsyncGeneratorModel from inheriting Generator attributes for example, usual generators have "send", but async don't. They have "async" instead. --- astroid/bases.py | 2 +- astroid/interpreter/objectmodel.py | 25 ++++++++----------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/astroid/bases.py b/astroid/bases.py index 7611a77c0d..d91a4c9faf 100644 --- a/astroid/bases.py +++ b/astroid/bases.py @@ -679,7 +679,7 @@ class Generator(BaseInstance): # We defer initialization of special_attributes to the __init__ method since the constructor # of GeneratorModel requires the raw_building to be complete # TODO: This should probably be refactored. - special_attributes: objectmodel.GeneratorModel + special_attributes: objectmodel.GeneratorBaseModel def __init__( self, diff --git a/astroid/interpreter/objectmodel.py b/astroid/interpreter/objectmodel.py index 7532db5edb..fd8c0c051b 100644 --- a/astroid/interpreter/objectmodel.py +++ b/astroid/interpreter/objectmodel.py @@ -694,12 +694,10 @@ def attr___self__(self): return self._instance.bound -class GeneratorModel(FunctionModel, ContextManagerModel): - def __init__(self): - # Append the values from the GeneratorType unto this object. +class GeneratorBaseModel(FunctionModel, ContextManagerModel): + def __init__(self, gen_module: nodes.Module): super().__init__() - generator = AstroidManager().builtins_module["generator"] - for name, values in generator.locals.items(): + for name, values in gen_module.locals.items(): method = values[0] if isinstance(method, nodes.FunctionDef): method = bases.BoundMethod(method, _get_bound_node(self)) @@ -723,21 +721,14 @@ def attr___doc__(self): ) -class AsyncGeneratorModel(GeneratorModel): +class GeneratorModel(GeneratorBaseModel): def __init__(self): - # Append the values from the AGeneratorType unto this object. - super().__init__() - astroid_builtins = AstroidManager().builtins_module - generator = astroid_builtins["async_generator"] - for name, values in generator.locals.items(): - method = values[0] - if isinstance(method, nodes.FunctionDef): - method = bases.BoundMethod(method, _get_bound_node(self)) + super().__init__(AstroidManager().builtins_module["generator"]) - def patched(cls, meth=method): - return meth - setattr(type(self), IMPL_PREFIX + name, property(patched)) +class AsyncGeneratorModel(GeneratorBaseModel): + def __init__(self): + super().__init__(AstroidManager().builtins_module["async_generator"]) class InstanceModel(ObjectModel): From c7ea1e941b4b88ecf0470c9a5e5fd03215f84249 Mon Sep 17 00:00:00 2001 From: correctmost <134317971+correctmost@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:47:47 -0400 Subject: [PATCH 2/4] Consolidate numpy member transforms to reduce function calls --- .../brain/brain_numpy_core_function_base.py | 19 ++++----- astroid/brain/brain_numpy_core_multiarray.py | 31 ++++++++------- astroid/brain/brain_numpy_core_numeric.py | 19 ++++----- astroid/brain/brain_numpy_utils.py | 39 +++++++++++++++++++ 4 files changed, 75 insertions(+), 33 deletions(-) diff --git a/astroid/brain/brain_numpy_core_function_base.py b/astroid/brain/brain_numpy_core_function_base.py index 17e1ad11d2..2bfe97080c 100644 --- a/astroid/brain/brain_numpy_core_function_base.py +++ b/astroid/brain/brain_numpy_core_function_base.py @@ -7,8 +7,8 @@ import functools from astroid.brain.brain_numpy_utils import ( - attribute_looks_like_numpy_member, - infer_numpy_member, + attribute_name_looks_like_numpy_member, + infer_numpy_attribute, ) from astroid.inference_tip import inference_tip from astroid.manager import AstroidManager @@ -25,10 +25,11 @@ def register(manager: AstroidManager) -> None: - for func_name, func_src in METHODS_TO_BE_INFERRED.items(): - inference_function = functools.partial(infer_numpy_member, func_src) - manager.register_transform( - Attribute, - inference_tip(inference_function), - functools.partial(attribute_looks_like_numpy_member, func_name), - ) + manager.register_transform( + Attribute, + inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)), + functools.partial( + attribute_name_looks_like_numpy_member, + frozenset(METHODS_TO_BE_INFERRED.keys()), + ), + ) diff --git a/astroid/brain/brain_numpy_core_multiarray.py b/astroid/brain/brain_numpy_core_multiarray.py index 16cdd6f918..95ef0a3402 100644 --- a/astroid/brain/brain_numpy_core_multiarray.py +++ b/astroid/brain/brain_numpy_core_multiarray.py @@ -8,9 +8,10 @@ from astroid import nodes from astroid.brain.brain_numpy_utils import ( - attribute_looks_like_numpy_member, - infer_numpy_member, - name_looks_like_numpy_member, + attribute_name_looks_like_numpy_member, + infer_numpy_attribute, + infer_numpy_name, + member_name_looks_like_numpy_member, ) from astroid.brain.helpers import register_module_extender from astroid.builder import parse @@ -92,15 +93,15 @@ def register(manager: AstroidManager) -> None: manager, "numpy.core.multiarray", numpy_core_multiarray_transform ) - for method_name, function_src in METHODS_TO_BE_INFERRED.items(): - inference_function = functools.partial(infer_numpy_member, function_src) - manager.register_transform( - Attribute, - inference_tip(inference_function), - functools.partial(attribute_looks_like_numpy_member, method_name), - ) - manager.register_transform( - Name, - inference_tip(inference_function), - functools.partial(name_looks_like_numpy_member, method_name), - ) + method_names = frozenset(METHODS_TO_BE_INFERRED.keys()) + + manager.register_transform( + Attribute, + inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)), + functools.partial(attribute_name_looks_like_numpy_member, method_names), + ) + manager.register_transform( + Name, + inference_tip(functools.partial(infer_numpy_name, METHODS_TO_BE_INFERRED)), + functools.partial(member_name_looks_like_numpy_member, method_names), + ) diff --git a/astroid/brain/brain_numpy_core_numeric.py b/astroid/brain/brain_numpy_core_numeric.py index df4ae0cbb1..72d4cc9c14 100644 --- a/astroid/brain/brain_numpy_core_numeric.py +++ b/astroid/brain/brain_numpy_core_numeric.py @@ -8,8 +8,8 @@ from astroid import nodes from astroid.brain.brain_numpy_utils import ( - attribute_looks_like_numpy_member, - infer_numpy_member, + attribute_name_looks_like_numpy_member, + infer_numpy_attribute, ) from astroid.brain.helpers import register_module_extender from astroid.builder import parse @@ -41,10 +41,11 @@ def register(manager: AstroidManager) -> None: manager, "numpy.core.numeric", numpy_core_numeric_transform ) - for method_name, function_src in METHODS_TO_BE_INFERRED.items(): - inference_function = functools.partial(infer_numpy_member, function_src) - manager.register_transform( - Attribute, - inference_tip(inference_function), - functools.partial(attribute_looks_like_numpy_member, method_name), - ) + manager.register_transform( + Attribute, + inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)), + functools.partial( + attribute_name_looks_like_numpy_member, + frozenset(METHODS_TO_BE_INFERRED.keys()), + ), + ) diff --git a/astroid/brain/brain_numpy_utils.py b/astroid/brain/brain_numpy_utils.py index 47f24433bd..8ce7144372 100644 --- a/astroid/brain/brain_numpy_utils.py +++ b/astroid/brain/brain_numpy_utils.py @@ -34,6 +34,21 @@ def _get_numpy_version() -> tuple[str, str, str]: return ("0", "0", "0") +def infer_numpy_name( + sources: dict[str, str], node: Name, context: InferenceContext | None = None +): + extracted_node = extract_node(sources[node.name]) + return extracted_node.infer(context=context) + + +def infer_numpy_attribute( + sources: dict[str, str], node: Attribute, context: InferenceContext | None = None +): + extracted_node = extract_node(sources[node.attrname]) + return extracted_node.infer(context=context) + + +# TODO: Deprecate and remove this function def infer_numpy_member(src, node, context: InferenceContext | None = None): node = extract_node(src) return node.infer(context=context) @@ -61,6 +76,29 @@ def _is_a_numpy_module(node: Name) -> bool: ) +def member_name_looks_like_numpy_member( + member_names: frozenset[str], node: Name +) -> bool: + """ + Returns True if the Name node's name matches a member name from numpy + """ + return node.name in member_names and node.root().name.startswith("numpy") + + +def attribute_name_looks_like_numpy_member( + member_names: frozenset[str], node: Attribute +) -> bool: + """ + Returns True if the Attribute node's name matches a member name from numpy + """ + return ( + node.attrname in member_names + and isinstance(node.expr, Name) + and _is_a_numpy_module(node.expr) + ) + + +# TODO: Deprecate and remove this function def name_looks_like_numpy_member(member_name: str, node: Name) -> bool: """ Returns True if the Name is a member of numpy whose @@ -69,6 +107,7 @@ def name_looks_like_numpy_member(member_name: str, node: Name) -> bool: return node.name == member_name and node.root().name.startswith("numpy") +# TODO: Deprecate and remove this function def attribute_looks_like_numpy_member(member_name: str, node: Attribute) -> bool: """ Returns True if the Attribute is a member of numpy whose From d394fb942eb18e690d0453bdd0a1e58c5beb219b Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Mon, 30 Sep 2024 08:52:35 -0400 Subject: [PATCH 3/4] Bump astroid to 4.0.0-dev0, update changelog --- ChangeLog | 15 ++------------- astroid/__pkginfo__.py | 2 +- tbump.toml | 2 +- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index a9eccd8858..18f2c1607a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,7 +3,7 @@ astroid's ChangeLog =================== -What's New in astroid 3.4.0? +What's New in astroid 4.0.0? ============================ Release date: TBA @@ -13,17 +13,6 @@ What's New in astroid 3.3.5? ============================ Release date: TBA -* Fix regression with f-string inference. - - Closes pylint-dev/pylint#9947 - -* Fix bug with ``manager.clear_cache()`` not fully clearing cache - - Refs https://github.com/pylint-dev/pylint/pull/9932#issuecomment-2364985551 - -* Fix a crash from inferring empty format specs. - - Closes pylint-dev/pylint#9945 What's New in astroid 3.3.4? @@ -34,7 +23,7 @@ Release date: 2024-09-23 Closes pylint-dev/pylint#9947 -* Fix bug with ``manager.clear_cache()`` not fully clearing cache +* Fix bug with ``manager.clear_cache()`` not fully clearing cache. Refs https://github.com/pylint-dev/pylint/pull/9932#issuecomment-2364985551 diff --git a/astroid/__pkginfo__.py b/astroid/__pkginfo__.py index 6707429079..18f0d9b196 100644 --- a/astroid/__pkginfo__.py +++ b/astroid/__pkginfo__.py @@ -2,5 +2,5 @@ # For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE # Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt -__version__ = "3.4.0-dev0" +__version__ = "4.0.0-dev0" version = __version__ diff --git a/tbump.toml b/tbump.toml index 2001b94c79..0c98eaefb5 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/pylint-dev/astroid" [version] -current = "3.4.0-dev0" +current = "4.0.0-dev0" regex = ''' ^(?P0|[1-9]\d*) \. From ca256f2a06ae1d873cc6fd14538ea16838617e68 Mon Sep 17 00:00:00 2001 From: Nick Drozd Date: Mon, 30 Sep 2024 11:34:11 -0400 Subject: [PATCH 4/4] Enable design complexity checks --- astroid/arguments.py | 2 ++ astroid/brain/brain_builtin_inference.py | 1 + astroid/brain/brain_dataclasses.py | 4 +++- astroid/brain/brain_gi.py | 2 ++ astroid/brain/brain_namedtuple_enum.py | 1 + astroid/decorators.py | 1 + astroid/filter_statements.py | 2 ++ astroid/nodes/_base_nodes.py | 1 + astroid/nodes/node_ng.py | 2 ++ astroid/protocols.py | 3 ++- pylintrc | 3 --- tests/test_inference.py | 1 + tests/test_nodes_lineno.py | 2 ++ 13 files changed, 20 insertions(+), 5 deletions(-) diff --git a/astroid/arguments.py b/astroid/arguments.py index 29336cf8f1..3781889b7c 100644 --- a/astroid/arguments.py +++ b/astroid/arguments.py @@ -142,6 +142,8 @@ def infer_argument( self, funcnode: InferenceResult, name: str, context: InferenceContext ): # noqa: C901 """Infer a function argument value according to the call context.""" + # pylint: disable = too-many-branches + if not isinstance(funcnode, (nodes.FunctionDef, nodes.Lambda)): raise InferenceError( f"Can not infer function argument value for non-function node {funcnode!r}.", diff --git a/astroid/brain/brain_builtin_inference.py b/astroid/brain/brain_builtin_inference.py index c60510d0b9..821261933b 100644 --- a/astroid/brain/brain_builtin_inference.py +++ b/astroid/brain/brain_builtin_inference.py @@ -176,6 +176,7 @@ def on_bootstrap(): def _builtin_filter_predicate(node, builtin_name) -> bool: + # pylint: disable = too-many-boolean-expressions if ( builtin_name == "type" and node.root().name == "re" diff --git a/astroid/brain/brain_dataclasses.py b/astroid/brain/brain_dataclasses.py index 845295bf9b..92d983e2b0 100644 --- a/astroid/brain/brain_dataclasses.py +++ b/astroid/brain/brain_dataclasses.py @@ -238,10 +238,12 @@ def _get_previous_field_default(node: nodes.ClassDef, name: str) -> nodes.NodeNG return None -def _generate_dataclass_init( # pylint: disable=too-many-locals +def _generate_dataclass_init( node: nodes.ClassDef, assigns: list[nodes.AnnAssign], kw_only_decorated: bool ) -> str: """Return an init method for a dataclass given the targets.""" + # pylint: disable = too-many-locals, too-many-branches, too-many-statements + params: list[str] = [] kw_only_params: list[str] = [] assignments: list[str] = [] diff --git a/astroid/brain/brain_gi.py b/astroid/brain/brain_gi.py index 4ebbdde2ab..fa600775dc 100644 --- a/astroid/brain/brain_gi.py +++ b/astroid/brain/brain_gi.py @@ -59,6 +59,8 @@ def _gi_build_stub(parent): # noqa: C901 Inspect the passed module recursively and build stubs for functions, classes, etc. """ + # pylint: disable = too-many-branches, too-many-statements + classes = {} functions = {} constants = {} diff --git a/astroid/brain/brain_namedtuple_enum.py b/astroid/brain/brain_namedtuple_enum.py index 72a07c1187..b39d177b10 100644 --- a/astroid/brain/brain_namedtuple_enum.py +++ b/astroid/brain/brain_namedtuple_enum.py @@ -268,6 +268,7 @@ def _get_renamed_namedtuple_attributes(field_names): names = list(field_names) seen = set() for i, name in enumerate(field_names): + # pylint: disable = too-many-boolean-expressions if ( not all(c.isalnum() or c == "_" for c in name) or keyword.iskeyword(name) diff --git a/astroid/decorators.py b/astroid/decorators.py index 6c8b1bac32..cd0f5a7961 100644 --- a/astroid/decorators.py +++ b/astroid/decorators.py @@ -137,6 +137,7 @@ def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R: raise ValueError( f"Can't find argument '{arg}' for '{args[0].__class__.__qualname__}'" ) from None + # pylint: disable = too-many-boolean-expressions if ( # Check kwargs # - if found, check it's not None diff --git a/astroid/filter_statements.py b/astroid/filter_statements.py index 627e68edc9..a48b6e7d9f 100644 --- a/astroid/filter_statements.py +++ b/astroid/filter_statements.py @@ -67,6 +67,8 @@ def _filter_stmts( :returns: The filtered statements. """ + # pylint: disable = too-many-branches, too-many-statements + # if offset == -1, my actual frame is not the inner frame but its parent # # class A(B): pass diff --git a/astroid/nodes/_base_nodes.py b/astroid/nodes/_base_nodes.py index 2d210f17af..65b703d042 100644 --- a/astroid/nodes/_base_nodes.py +++ b/astroid/nodes/_base_nodes.py @@ -603,6 +603,7 @@ def _get_binop_flow( ), ] + # pylint: disable = too-many-boolean-expressions if ( PY310_PLUS and op == "|" diff --git a/astroid/nodes/node_ng.py b/astroid/nodes/node_ng.py index 6d149e3840..99120bb65f 100644 --- a/astroid/nodes/node_ng.py +++ b/astroid/nodes/node_ng.py @@ -656,6 +656,8 @@ def repr_tree( :rtype: str """ + # pylint: disable = too-many-statements + @_singledispatch def _repr_tree(node, result, done, cur_indent="", depth=1): """Outputs a representation of a non-tuple/list, non-node that's diff --git a/astroid/protocols.py b/astroid/protocols.py index bacb786a99..0bac32a29a 100644 --- a/astroid/protocols.py +++ b/astroid/protocols.py @@ -691,7 +691,8 @@ def starred_assigned_stmts( # noqa: C901 the inference results. """ - # pylint: disable=too-many-locals,too-many-statements + # pylint: disable = too-many-locals, too-many-statements, too-many-branches + def _determine_starred_iteration_lookups( starred: nodes.Starred, target: nodes.Tuple, lookups: list[tuple[int, int]] ) -> None: diff --git a/pylintrc b/pylintrc index 12a01eb568..64218020ce 100644 --- a/pylintrc +++ b/pylintrc @@ -92,9 +92,6 @@ disable=fixme, missing-docstring, too-few-public-methods, too-many-public-methods, - too-many-boolean-expressions, - too-many-branches, - too-many-statements, # We know about it and we're doing our best to remove it in 2.0 (oups) cyclic-import, # Requires major redesign for fixing this (and private diff --git a/tests/test_inference.py b/tests/test_inference.py index acc8a2b1f8..6c80ee98a7 100644 --- a/tests/test_inference.py +++ b/tests/test_inference.py @@ -1262,6 +1262,7 @@ def randint(maximum): def test_binary_op_or_union_type(self) -> None: """Binary or union is only defined for Python 3.10+.""" + # pylint: disable = too-many-statements code = """ class A: ... diff --git a/tests/test_nodes_lineno.py b/tests/test_nodes_lineno.py index c8a8839e21..875d21d565 100644 --- a/tests/test_nodes_lineno.py +++ b/tests/test_nodes_lineno.py @@ -557,6 +557,7 @@ def test_end_lineno_const() -> None: @staticmethod def test_end_lineno_function() -> None: """FunctionDef, AsyncFunctionDef, Decorators, Lambda, Arguments.""" + # pylint: disable = too-many-statements code = textwrap.dedent( """ def func( #@ @@ -991,6 +992,7 @@ def test_end_lineno_match() -> None: """Match, MatchValue, MatchSingleton, MatchSequence, MatchMapping, MatchClass, MatchStar, MatchOr, MatchAs. """ + # pylint: disable = too-many-statements code = textwrap.dedent( """ match x: #@