diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0333e3f..7791a8c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,9 @@ Changelog ========= +- Fix issue where too many arguments were passed to stdlib's ``operator.attrgetter``, ``operator.itemgetter``, and ``operator.methodcaller`` when instances of those classes were used as callbacks to functions like ``map_``, ``filter_``, etc. due to a bug introduced in Python 3.12.3 and 3.11.9 that reported an incorrect signature for those ``operator`` class instances. + + v8.0.0 (2024-03-26) ------------------- diff --git a/src/pydash/helpers.py b/src/pydash/helpers.py index ed67f06..af4cc45 100644 --- a/src/pydash/helpers.py +++ b/src/pydash/helpers.py @@ -5,7 +5,7 @@ from decimal import Decimal from functools import wraps import inspect -from inspect import getfullargspec +import operator import warnings import pydash as pyd @@ -72,19 +72,25 @@ def _getargcount(iteratee, maxargs): # getting the iteratee argcount since it takes into account the "self" argument in callable # classes. sig = inspect.signature(iteratee) - except (TypeError, ValueError, AttributeError): + except (TypeError, ValueError, AttributeError): # pragma: no cover pass - else: # pragma: no cover + else: if not any( param.kind == inspect.Parameter.VAR_POSITIONAL for param in sig.parameters.values() ): argcount = len(sig.parameters) if argcount is None: - argspec = getfullargspec(iteratee) - if argspec and not argspec.varargs: # pragma: no cover - # Use inspected arg count. - argcount = len(argspec.args) + # Signatures were added these operator methods in Python 3.12.3 and 3.11.9 but their + # instance objects are incorrectly reported as accepting varargs when they only accept a + # single argument. + if isinstance(iteratee, (operator.itemgetter, operator.attrgetter, operator.methodcaller)): + argcount = 1 + else: + argspec = inspect.getfullargspec(iteratee) + if argspec and not argspec.varargs: # pragma: no cover + # Use inspected arg count. + argcount = len(argspec.args) if argcount is None: # Assume all args are handleable. diff --git a/tests/test_collections.py b/tests/test_collections.py index dd48213..929bd43 100644 --- a/tests/test_collections.py +++ b/tests/test_collections.py @@ -1,6 +1,6 @@ from collections import namedtuple import math -from operator import itemgetter +from operator import attrgetter, itemgetter, methodcaller import pytest @@ -323,6 +323,22 @@ def test_key_by(case, expected): [(1, 2), (3, 4), (5, 6)], False, ), + ( + ( + [helpers.Object(a=1, b=2, c=-1), helpers.Object(a=3, b=4, c=-1)], + attrgetter("a", "b"), + ), + [(1, 2), (3, 4)], + False, + ), + ( + ( + [{"a": 1, "b": 2, "c": -1}, {"a": 3, "b": 4}, {"a": 5}], + methodcaller("__len__"), + ), + [3, 2, 1], + False, + ), ], ) def test_map_(case, expected, sort_results):