dgilland / pydash

The kitchen sink of Python utility libraries for doing "stuff" in a functional way. Based on the Lo-Dash Javascript library.
http://pydash.readthedocs.io
MIT License
1.28k stars 89 forks source link

Fail test with new Python 3.11.9 or Python 3.12.3 #225

Closed shadchin closed 2 months ago

shadchin commented 2 months ago
__________________________________________________________________________________________ test_map_[case8-expected8-False] __________________________________________________________________________________________

case = ([{'a': 1, 'b': 2, 'c': -1}, {'a': 3, 'b': 4, 'c': -1}, {'a': 5, 'b': 6, 'c': -1}], operator.itemgetter('a', 'b')), expected = [(1, 2), (3, 4), (5, 6)], sort_results = False

    @parametrize(
        "case,expected,sort_results",
        [
            (([1, 2, 3],), [1, 2, 3], False),
            (([1.1, 2.1, 3.1], int), [1, 2, 3], False),
            (([1, 2, 3], lambda num: num * 3), [3, 6, 9], False),
            (([[1], [2, 3], [4, 5, 6]], len), [1, 2, 3], False),
            (({"one": 1, "two": 2, "three": 3}, lambda num: num * 3), [3, 6, 9], True),
            (
                ([{"name": "moe", "age": 40}, {"name": "larry", "age": 50}], "name"),
                ["moe", "larry"],
                False,
            ),
            (
                (
                    [
                        {"level1": {"level2": {"level3": {"value": 1}}}},
                        {"level1": {"level2": {"level3": {"value": 2}}}},
                        {"level1": {"level2": {"level3": {"value": 3}}}},
                        {"level1": {"level2": {"level3": {"value": 4}}}},
                        {"level1": {"level2": {}}},
                        {},
                    ],
                    "level1.level2.level3.value",
                ),
                [1, 2, 3, 4, None, None],
                False,
            ),
            (([[0, 1], [2, 3], [4, 5]], 1), [1, 3, 5], False),
            (
                (
                    [{"a": 1, "b": 2, "c": -1}, {"a": 3, "b": 4, "c": -1}, {"a": 5, "b": 6, "c": -1}],
                    itemgetter("a", "b"),
                ),
                [(1, 2), (3, 4), (5, 6)],
                False,
            ),
        ],
    )
    def test_map_(case, expected, sort_results):
>       actual = _.map_(*case)

tests/test_collections.py:329:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.tox/py312/lib/python3.12/site-packages/pydash/collections.py:1029: in map_
    return list(itermap(collection, iteratee))
.tox/py312/lib/python3.12/site-packages/pydash/collections.py:2170: in itermap
    for result in iteriteratee(collection, iteratee):
.tox/py312/lib/python3.12/site-packages/pydash/helpers.py:111: in iteriteratee
    yield callit(cbk, item, key, obj, argcount=argcount), item, key, obj
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

iteratee = operator.itemgetter('a', 'b'), args = ({'a': 1, 'b': 2, 'c': -1}, 0, [{'a': 1, 'b': 2, 'c': -1}, {'a': 3, 'b': 4, 'c': -1}, {'a': 5, 'b': 6, 'c': -1}]), kwargs = {'argcount': 3}, maxargs = 3
argcount = 3, argstop = 3

    def callit(iteratee, *args, **kwargs):
        """Inspect argspec of `iteratee` function and only pass the supported arguments when calling
        it."""
        maxargs = len(args)
        argcount = kwargs["argcount"] if "argcount" in kwargs else getargcount(iteratee, maxargs)
        argstop = min([maxargs, argcount])

>       return iteratee(*args[:argstop])
E       TypeError: itemgetter expected 1 argument, got 3

.tox/py312/lib/python3.12/site-packages/pydash/helpers.py:38: TypeError
dgilland commented 2 months ago

After looking into this, it seems like a bug in Python where the signature of operator.{attrgetter,itemgetter,methodcaller} instances are reported as being able to handle *args even though their .__call__ methods only accept a single argument. I filed a bug https://github.com/python/cpython/issues/118285 to hopefully get that resolved.

In the meantime, I've added a workaround in pydash v8.0.1 to handle it.

dgilland commented 2 months ago

Thanks for reporting!