Skip to content

unittest.mock.ANY is unusable in a files lookup #276

Open
@mikenerone

Description

@mikenerone

Python 3.12.7
respx 0.21.1

The documentation includes both of the following examples for files lookups:

respx.post("https://example.org/", files={"some_file": ANY})
respx.post("https://example.org/", files={"some_file": ("filename.txt", ANY)})

These don't actually work because since e670690, routes are compared via their hash(), and adding a route triggers a comparison to existing routes. ANY is unhashable, so any route containing it (at least in a files lookup) is also unhashable. Here's a minimal test to demonstrate (but to be clear, the same thing happens with the tuple form if it contains an ANY):

from unittest.mock import ANY

import respx


def test_example():
    # This first one succeeds because there are no existing routes yet, so no comparisons happen.
    respx.post("/path", files={"filename1": ANY})
    # This one causes a hash error when a comparison to the existing route is attempted
    respx.post("/path", files={"filename2": ANY})

Output:

F                                                                                                                                                                                                        [100%]
=================================================================================================== FAILURES ===================================================================================================
_________________________________________________________________________________________________ test_example _________________________________________________________________________________________________

    def test_example():
        # This first one succeeds because there are no existing routes yet, so no comparisons happen.
        respx.post("/path", files={"filename1": ANY})
        # This one causes a hash error when a comparison to the existing route is attempted
>       respx.post("/path", files={"filename2": ANY})

tests/test_example.py:10:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.venv/lib/python3.12/site-packages/respx/api.py:81: in post
    return mock.post(url, name=name, **lookups)
.venv/lib/python3.12/site-packages/respx/router.py:182: in post
    return self.request(method="POST", url=url, name=name, **lookups)
.venv/lib/python3.12/site-packages/respx/router.py:164: in request
    return self.route(method=method, url=url, name=name, **lookups)
.venv/lib/python3.12/site-packages/respx/router.py:132: in route
    return self.add(route, name=name)
.venv/lib/python3.12/site-packages/respx/router.py:145: in add
    route = self.routes.add(route, name=name)
.venv/lib/python3.12/site-packages/respx/models.py:480: in add
    if route in self._routes:
.venv/lib/python3.12/site-packages/respx/models.py:149: in __eq__
    return self.pattern == other.pattern
.venv/lib/python3.12/site-packages/respx/patterns.py:133: in __eq__
    return hash(self) == hash(other)
.venv/lib/python3.12/site-packages/respx/patterns.py:130: in __hash__
    return hash((self.__class__, self.lookup, self.value))
.venv/lib/python3.12/site-packages/respx/patterns.py:130: in __hash__
    return hash((self.__class__, self.lookup, self.value))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <Files contains {'filename1': (<ANY>, <ANY>)}>

    def __hash__(self):
>       return hash((self.__class__, self.lookup, self._multi_items(self.value)))
E       TypeError: unhashable type: '_ANY'

.venv/lib/python3.12/site-packages/respx/patterns.py:306: TypeError
=========================================================================================== short test summary info ============================================================================================
FAILED tests/test_example.py::test_example - TypeError: unhashable type: '_ANY'
1 failed in 0.18s

Note that this problem doesn't appear to strike when using ANY with a params lookup instead of files, so however it's handled there may be applicable here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions