Skip to content

Commit

Permalink
Merge pull request #137 from scrapinghub/async_lru_compat
Browse files Browse the repository at this point in the history
fixed compatibility with async_lru >= 2
  • Loading branch information
kmike authored Feb 21, 2023
2 parents a8a6982 + d77296e commit 9196916
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 7 deletions.
11 changes: 11 additions & 0 deletions docs/page-objects/fields.rst
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,17 @@ with async versions of ``@property`` and ``@cached_property`` decorators; unlike

.. _async_property: https://github.com/ryananguiano/async_property

Exceptions caching
~~~~~~~~~~~~~~~~~~

Note that exceptions are not cached - neither by :func:`~.cached_method`,
nor by `@field(cached=True)`, nor by :func:`functools.lru_cache`, nor by
:func:`functools.cached_property`.

Usually it's not an issue, because an exception is usually propagated,
and so there are no duplicate calls anyways. But, just in case, keep this
in mind.

Field metadata
--------------

Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"andi",
"python-dateutil",
"time-machine",
"packaging",
"backports.zoneinfo; python_version < '3.9' and platform_system != 'Windows'",
],
classifiers=[
Expand Down
9 changes: 4 additions & 5 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,6 @@ async def meth(self):
assert await foo.meth() == 1


@pytest.mark.xfail
def test_cached_method_exception() -> None:
class Error(Exception):
pass
Expand All @@ -420,10 +419,10 @@ def meth(self):

foo = Foo()

for _ in range(2):
for idx in range(2):
with pytest.raises(Error):
foo.meth()
assert foo.n_called == 1
assert foo.n_called == idx + 1


@pytest.mark.asyncio
Expand All @@ -441,10 +440,10 @@ async def meth(self):

foo = Foo()

for _ in range(2):
for idx in range(2):
with pytest.raises(Error):
await foo.meth()
assert foo.n_called == 1
assert foo.n_called == idx + 1


@pytest.mark.asyncio
Expand Down
16 changes: 14 additions & 2 deletions web_poet/utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import inspect
import weakref
from collections.abc import Iterable
from functools import lru_cache, wraps
from functools import lru_cache, partial, wraps
from types import MethodType
from typing import Any, Callable, List, Optional, TypeVar, Union
from warnings import warn

import packaging.version
from async_lru import __version__ as async_lru_version
from async_lru import alru_cache
from url_matcher import Patterns

Expand Down Expand Up @@ -203,7 +205,7 @@ async def inner(self, *args, **kwargs):
# on a first call, create an alru_cache-wrapped method,
# and store it on the instance
bound_method = MethodType(method, self)
cached_meth = alru_cache(maxsize=None)(bound_method)
cached_meth = _alru_cache(maxsize=None)(bound_method)
setattr(self, cached_method_name, cached_meth)
else:
cached_meth = getattr(self, cached_method_name)
Expand All @@ -212,6 +214,16 @@ async def inner(self, *args, **kwargs):
return inner


# async_lru >= 2.0.0 removed cache_exceptions argument, and changed
# its default value. `_alru_cache` is a compatibility function which works with
# all async_lru versions and uses the same approach for exception caching
# as async_lru >= 2.0.0.
_alru_cache: Callable = alru_cache
_async_lru_version = packaging.version.parse(async_lru_version)
if _async_lru_version.major < 2:
_alru_cache = partial(alru_cache, cache_exceptions=False)


def as_list(value: Optional[Any]) -> List[Any]:
"""Normalizes the value input as a list.
Expand Down

0 comments on commit 9196916

Please sign in to comment.