Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PR #9394/e9edd04 backport][3.11] Migrate to using propcache for property caching #9434

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
aiohttp/_find_header.c
aiohttp/_headers.html
aiohttp/_headers.pxi
aiohttp/_helpers.c
aiohttp/_helpers.html
aiohttp/_http_parser.c
aiohttp/_http_parser.html
aiohttp/_http_writer.c
Expand Down
6 changes: 6 additions & 0 deletions CHANGES/9394.packaging.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Switched to using the :mod:`propcache <propcache.api>` package for property caching
-- by :user:`bdraco`.

The :mod:`propcache <propcache.api>` package is derived from the property caching
code in :mod:`yarl` and has been broken out to avoid maintaining it for multiple
projects.
6 changes: 0 additions & 6 deletions aiohttp/_helpers.pyi

This file was deleted.

35 changes: 0 additions & 35 deletions aiohttp/_helpers.pyx

This file was deleted.

50 changes: 2 additions & 48 deletions aiohttp/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@

import attr
from multidict import MultiDict, MultiDictProxy, MultiMapping
from propcache.api import under_cached_property as reify
from yarl import URL

from . import hdrs
Expand All @@ -57,7 +58,7 @@
else:
import async_timeout

__all__ = ("BasicAuth", "ChainMapProxy", "ETag")
__all__ = ("BasicAuth", "ChainMapProxy", "ETag", "reify")

IS_MACOS = platform.system() == "Darwin"
IS_WINDOWS = platform.system() == "Windows"
Expand Down Expand Up @@ -425,53 +426,6 @@ def content_disposition_header(
return value


class _TSelf(Protocol, Generic[_T]):
_cache: Dict[str, _T]


class reify(Generic[_T]):
"""Use as a class method decorator.

It operates almost exactly like
the Python `@property` decorator, but it puts the result of the
method it decorates into the instance dict after the first call,
effectively replacing the function it decorates with an instance
variable. It is, in Python parlance, a data descriptor.
"""

def __init__(self, wrapped: Callable[..., _T]) -> None:
self.wrapped = wrapped
self.__doc__ = wrapped.__doc__
self.name = wrapped.__name__

def __get__(self, inst: _TSelf[_T], owner: Optional[Type[Any]] = None) -> _T:
try:
try:
return inst._cache[self.name]
except KeyError:
val = self.wrapped(inst)
inst._cache[self.name] = val
return val
except AttributeError:
if inst is None:
return self
raise

def __set__(self, inst: _TSelf[_T], value: _T) -> None:
raise AttributeError("reified property is read-only")


reify_py = reify

try:
from ._helpers import reify as reify_c

if not NO_EXTENSIONS:
reify = reify_c # type: ignore[misc,assignment]
except ImportError:
pass


def is_ip_address(host: Optional[str]) -> bool:
"""Check if host looks like an IP Address.

Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"pytest": ("http://docs.pytest.org/en/latest/", None),
"python": ("http://docs.python.org/3", None),
"multidict": ("https://multidict.readthedocs.io/en/stable/", None),
"propcache": ("https://propcache.aio-libs.org/en/stable", None),
"yarl": ("https://yarl.readthedocs.io/en/stable/", None),
"aiosignal": ("https://aiosignal.readthedocs.io/en/stable/", None),
"aiohttpjinja2": ("https://aiohttp-jinja2.readthedocs.io/en/stable/", None),
Expand Down
2 changes: 1 addition & 1 deletion requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ multidict==6.1.0
packaging==24.1
# via gunicorn
propcache==0.2.0
# via yarl
# via -r requirements/runtime-deps.in
bdraco marked this conversation as resolved.
Show resolved Hide resolved
pycares==4.4.0
# via aiodns
pycparser==2.22
Expand Down
2 changes: 1 addition & 1 deletion requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ pluggy==1.5.0
pre-commit==3.5.0
# via -r requirements/lint.in
propcache==0.2.0
# via yarl
# via -r requirements/runtime-deps.in
bdraco marked this conversation as resolved.
Show resolved Hide resolved
proxy-py==2.4.8
# via -r requirements/test.in
pycares==4.4.0
Expand Down
2 changes: 1 addition & 1 deletion requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ pluggy==1.5.0
pre-commit==3.5.0
# via -r requirements/lint.in
propcache==0.2.0
# via yarl
# via -r requirements/runtime-deps.in
bdraco marked this conversation as resolved.
Show resolved Hide resolved
proxy-py==2.4.8
# via -r requirements/test.in
pycares==4.4.0
Expand Down
1 change: 1 addition & 0 deletions requirements/runtime-deps.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ Brotli; platform_python_implementation == 'CPython'
brotlicffi; platform_python_implementation != 'CPython'
frozenlist >= 1.1.1
multidict >=4.5, < 7.0
propcache >= 0.2.0
yarl >= 1.13.0, < 2.0
2 changes: 1 addition & 1 deletion requirements/runtime-deps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ multidict==6.1.0
# -r requirements/runtime-deps.in
# yarl
propcache==0.2.0
# via yarl
# via -r requirements/runtime-deps.in
bdraco marked this conversation as resolved.
Show resolved Hide resolved
pycares==4.4.0
# via aiodns
pycparser==2.22
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ install_requires =
attrs >= 17.3.0
frozenlist >= 1.1.1
multidict >=4.5, < 7.0
propcache >= 0.2.0
yarl >= 1.13.0, < 2.0

[options.exclude_package_data]
Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
define_macros=[("LLHTTP_STRICT_MODE", 0)],
include_dirs=["vendor/llhttp/build"],
),
Extension("aiohttp._helpers", ["aiohttp/_helpers.c"]),
Extension("aiohttp._http_writer", ["aiohttp/_http_writer.c"]),
]

Expand Down
57 changes: 0 additions & 57 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import base64
import datetime
import gc
import platform
import sys
import weakref
from math import ceil, modf
Expand All @@ -22,9 +21,6 @@
should_remove_content_length,
)

IS_PYPY = platform.python_implementation() == "PyPy"


# ------------------- parse_mimetype ----------------------------------


Expand Down Expand Up @@ -208,59 +204,6 @@ def test_basic_auth_from_not_url() -> None:
helpers.BasicAuth.from_url("http://user:pass@example.com")


class ReifyMixin:
reify = NotImplemented

def test_reify(self) -> None:
class A:
def __init__(self):
self._cache = {}

@self.reify
def prop(self):
return 1

a = A()
assert 1 == a.prop

def test_reify_class(self) -> None:
class A:
def __init__(self):
self._cache = {}

@self.reify
def prop(self):
"""Docstring."""
return 1

assert isinstance(A.prop, self.reify)
assert "Docstring." == A.prop.__doc__

def test_reify_assignment(self) -> None:
class A:
def __init__(self):
self._cache = {}

@self.reify
def prop(self):
return 1

a = A()

with pytest.raises(AttributeError):
a.prop = 123


class TestPyReify(ReifyMixin):
reify = helpers.reify_py


if not helpers.NO_EXTENSIONS and not IS_PYPY and hasattr(helpers, "reify_c"):

class TestCReify(ReifyMixin):
reify = helpers.reify_c


# ----------------------------------- is_ip_address() ----------------------


Expand Down
Loading