diff --git a/poetry.lock b/poetry.lock index 7f8dd525f4b..35e64522660 100644 --- a/poetry.lock +++ b/poetry.lock @@ -20,6 +20,14 @@ docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +[[package]] +name = "backports.cached-property" +version = "1.0.2" +description = "cached_property() - computed once per instance, cached as attribute" +category = "main" +optional = false +python-versions = ">=3.6.0" + [[package]] name = "CacheControl" version = "0.12.11" @@ -931,7 +939,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>= [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "3810aef128102dea7cd2797cfcee77f1345831ed057aac5a962916a76e29fb6a" +content-hash = "7e40cde2399843d53e715f657b703397ce236196663788ada896d7e2a53c869a" [metadata.files] atomicwrites = [ @@ -941,6 +949,10 @@ attrs = [ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, ] +"backports.cached-property" = [ + {file = "backports.cached-property-1.0.2.tar.gz", hash = "sha256:9306f9eed6ec55fd156ace6bc1094e2c86fae5fb2bf07b6a9c00745c656e75dd"}, + {file = "backports.cached_property-1.0.2-py3-none-any.whl", hash = "sha256:baeb28e1cd619a3c9ab8941431fe34e8490861fb998c6c4590693d50171db0cc"}, +] CacheControl = [ {file = "CacheControl-0.12.11-py2.py3-none-any.whl", hash = "sha256:2c75d6a8938cb1933c75c50184549ad42728a27e9f6b92fd677c3151aa72555b"}, {file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"}, diff --git a/pyproject.toml b/pyproject.toml index 5f7bbde2e52..8d95d3811c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ python = "^3.7" poetry-core = "^1.1.0" poetry-plugin-export = "^1.0.6" +"backports.cached-property" = { version = "^1.0.2", python = "<3.8" } cachecontrol = { version = "^0.12.9", extras = ["filecache"] } cachy = "^0.3.0" cleo = "^1.0.0a5" diff --git a/src/poetry/repositories/link_sources/base.py b/src/poetry/repositories/link_sources/base.py index 8fd317f67c4..3a0e2a34987 100644 --- a/src/poetry/repositories/link_sources/base.py +++ b/src/poetry/repositories/link_sources/base.py @@ -1,6 +1,5 @@ from __future__ import annotations -import functools import logging import re @@ -40,9 +39,6 @@ class LinkSource: def __init__(self, url: str) -> None: self._url = url - self._get_link_cache_wrapper = functools.lru_cache(maxsize=1)( - self._get_link_cache - ) @property def url(self) -> str: @@ -124,10 +120,5 @@ def yanked(self, name: NormalizedName, version: Version) -> str | bool: @property def _link_cache( self, - ) -> defaultdict[NormalizedName, defaultdict[Version, list[Link]]]: - return self._get_link_cache_wrapper() - - def _get_link_cache( - self, ) -> defaultdict[NormalizedName, defaultdict[Version, list[Link]]]: raise NotImplementedError() diff --git a/src/poetry/repositories/link_sources/html.py b/src/poetry/repositories/link_sources/html.py index 390ae817f1e..e56ba7327ad 100644 --- a/src/poetry/repositories/link_sources/html.py +++ b/src/poetry/repositories/link_sources/html.py @@ -10,6 +10,7 @@ from poetry.core.packages.utils.link import Link from poetry.repositories.link_sources.base import LinkSource +from poetry.utils._compat import cached_property if TYPE_CHECKING: @@ -28,7 +29,8 @@ def __init__(self, url: str, content: str) -> None: self._parsed = html5lib.parse(content, namespaceHTMLElements=False) - def _get_link_cache( + @cached_property + def _link_cache( self, ) -> defaultdict[NormalizedName, defaultdict[Version, list[Link]]]: links: defaultdict[ diff --git a/src/poetry/utils/_compat.py b/src/poetry/utils/_compat.py index a1c81582366..9ee8875f061 100644 --- a/src/poetry/utils/_compat.py +++ b/src/poetry/utils/_compat.py @@ -13,6 +13,12 @@ else: from importlib import metadata +if sys.version_info < (3, 8): + # compatibility for python <3.8 + from backports.cached_property import cached_property +else: + from functools import cached_property + WINDOWS = sys.platform == "win32" @@ -53,4 +59,12 @@ def list_to_shell_command(cmd: list[str]) -> str: ) -__all__ = ["WINDOWS", "decode", "encode", "list_to_shell_command", "metadata", "to_str"] +__all__ = [ + "WINDOWS", + "cached_property", + "decode", + "encode", + "list_to_shell_command", + "metadata", + "to_str", +] diff --git a/tests/repositories/link_sources/test_base.py b/tests/repositories/link_sources/test_base.py index 113d1fbb1c1..ef400a716d7 100644 --- a/tests/repositories/link_sources/test_base.py +++ b/tests/repositories/link_sources/test_base.py @@ -2,10 +2,10 @@ from collections import defaultdict from typing import TYPE_CHECKING +from unittest.mock import PropertyMock import pytest -from packaging.utils import NormalizedName from packaging.utils import canonicalize_name from poetry.core.packages.package import Package from poetry.core.packages.utils.link import Link @@ -23,28 +23,27 @@ @pytest.fixture def link_source(mocker: MockerFixture) -> LinkSource: url = "https://example.org" - - class LinkSourceMock(LinkSource): - def _get_link_cache( - self, - ) -> defaultdict[NormalizedName, defaultdict[Version, list[Link]]]: - return defaultdict( - lambda: defaultdict(list), - { - canonicalize_name("demo"): defaultdict( - list, - { - Version.parse("0.1.0"): [ - Link(f"{url}/demo-0.1.0.tar.gz"), - Link(f"{url}/demo-0.1.0-py2.py3-none-any.whl"), - ], - Version.parse("0.1.1"): [Link(f"{url}/demo-0.1.1.tar.gz")], - }, - ), - }, - ) - - return LinkSourceMock(url) + link_source = LinkSource(url) + mocker.patch( + f"{LinkSource.__module__}.{LinkSource.__qualname__}._link_cache", + new_callable=PropertyMock, + return_value=defaultdict( + lambda: defaultdict(list), + { + canonicalize_name("demo"): defaultdict( + list, + { + Version.parse("0.1.0"): [ + Link(f"{url}/demo-0.1.0.tar.gz"), + Link(f"{url}/demo-0.1.0-py2.py3-none-any.whl"), + ], + Version.parse("0.1.1"): [Link(f"{url}/demo-0.1.1.tar.gz")], + }, + ), + }, + ), + ) + return link_source @pytest.mark.parametrize(