From b2b90755ec94c731d6b7497314c931e8d2672a4a Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Mon, 17 Aug 2020 18:31:57 +0200 Subject: [PATCH] Handle (*) constraint for pre-release only packages This change ensures that packages that only have a pre-release versions are pragmatically discovered via `find_packages` when a constraint (*) is specified. Resolves: #2819 --- poetry/repositories/legacy_repository.py | 36 +++-- poetry/repositories/pypi_repository.py | 6 +- poetry/repositories/repository.py | 7 +- tests/repositories/fixtures/legacy/black.html | 10 ++ .../dists/black-19.10b0-py36-none-any.whl | Bin 0 -> 1940 bytes .../fixtures/pypi.org/json/black.json | 146 ++++++++++++++++++ tests/repositories/test_legacy_repository.py | 21 +++ tests/repositories/test_pypi_repository.py | 8 + 8 files changed, 220 insertions(+), 14 deletions(-) create mode 100644 tests/repositories/fixtures/legacy/black.html create mode 100644 tests/repositories/fixtures/pypi.org/dists/black-19.10b0-py36-none-any.whl create mode 100644 tests/repositories/fixtures/pypi.org/json/black.json diff --git a/poetry/repositories/legacy_repository.py b/poetry/repositories/legacy_repository.py index 901e144f956..fed921e5f77 100644 --- a/poetry/repositories/legacy_repository.py +++ b/poetry/repositories/legacy_repository.py @@ -246,6 +246,8 @@ def find_packages( if not constraint.is_any(): key = "{}:{}".format(key, str(constraint)) + ignored_pre_release_versions = [] + if self._cache.store("matches").has(key): versions = self._cache.store("matches").get(key) else: @@ -256,6 +258,9 @@ def find_packages( versions = [] for version in page.versions: if version.is_prerelease() and not allow_prereleases: + if constraint.is_any(): + # we need this when all versions of the package are pre-releases + ignored_pre_release_versions.append(version) continue if constraint.allows(version): @@ -263,21 +268,28 @@ def find_packages( self._cache.store("matches").put(key, versions, 5) - for version in versions: - package = Package(name, version) - package.source_type = "legacy" - package.source_reference = self.name - package.source_url = self._url + for package_versions in (versions, ignored_pre_release_versions): + for version in package_versions: + package = Package(name, version) + package.source_type = "legacy" + package.source_reference = self.name + package.source_url = self._url - if extras is not None: - package.requires_extras = extras + if extras is not None: + package.requires_extras = extras - packages.append(package) + packages.append(package) - self._log( - "{} packages found for {} {}".format(len(packages), name, str(constraint)), - level="debug", - ) + self._log( + "{} packages found for {} {}".format( + len(packages), name, str(constraint) + ), + level="debug", + ) + + if packages or not constraint.is_any(): + # we have matching packages, or constraint is not (*) + break return packages diff --git a/poetry/repositories/pypi_repository.py b/poetry/repositories/pypi_repository.py index c9f320400c5..6ea50bd84dc 100644 --- a/poetry/repositories/pypi_repository.py +++ b/poetry/repositories/pypi_repository.py @@ -114,6 +114,7 @@ def find_packages( return [] packages = [] + ignored_pre_release_packages = [] for version, release in info["releases"].items(): if not release: @@ -138,6 +139,9 @@ def find_packages( continue if package.is_prerelease() and not allow_prereleases: + if constraint.is_any(): + # we need this when all versions of the package are pre-releases + ignored_pre_release_packages.append(package) continue if not constraint or (constraint and constraint.allows(package.version)): @@ -151,7 +155,7 @@ def find_packages( level="debug", ) - return packages + return packages or ignored_pre_release_packages def package( self, diff --git a/poetry/repositories/repository.py b/poetry/repositories/repository.py index cea554ef47d..85aac6ddabc 100644 --- a/poetry/repositories/repository.py +++ b/poetry/repositories/repository.py @@ -44,6 +44,8 @@ def find_packages( ): name = name.lower() packages = [] + ignored_pre_release_packages = [] + if extras is None: extras = [] @@ -71,6 +73,9 @@ def find_packages( ): # If prereleases are not allowed and the package is a prerelease # and is a standard package then we skip it + if constraint.is_any(): + # we need this when all versions of the package are pre-releases + ignored_pre_release_packages.append(package) continue if constraint.allows(package.version): @@ -89,7 +94,7 @@ def find_packages( packages.append(package) - return packages + return packages or ignored_pre_release_packages def has_package(self, package): package_id = package.unique_name diff --git a/tests/repositories/fixtures/legacy/black.html b/tests/repositories/fixtures/legacy/black.html new file mode 100644 index 00000000000..333fd93ef02 --- /dev/null +++ b/tests/repositories/fixtures/legacy/black.html @@ -0,0 +1,10 @@ + + + + Links for black + +

Links for black

+ black-19.10b0.tar.gz + + + \ No newline at end of file diff --git a/tests/repositories/fixtures/pypi.org/dists/black-19.10b0-py36-none-any.whl b/tests/repositories/fixtures/pypi.org/dists/black-19.10b0-py36-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..3d031c0f3eff898e8010833d3df25ca84eaeeb8c GIT binary patch literal 1940 zcmWIWW@h1H0D;Ut!9XwrN^mg9FeK$9CTHsvRECCdGBB4dw~7bh(h6<{MwS=M3=Ci* z0BC{;P!9(~@4guS`P(NpHZd_USgeCg>Vk>KPg&8R(^C7MJK|=B4H9`*=FL z`UShj26^}2G7#AJJG{r@t=GkGg{!KJJ0sV$Sri=#>Qgg3lG_oO)3t5;za9V9pJUl} zdu>b0vyXd^t+eLWj&yz}WzE7}9&yKb+ONx>U+~4Y?0zO^x9|T4&bt3r_2v)NpQKqx z9P_f4i{&`S|A48aY|HL+i=MQOZN3TiQQUR>zUjxBeHE$<4ja#TBO&mh{Nj#-yzB-S z76=_adqdIp!|w{i#Ydlt`EcwjWc#_`>)D?f?HNih-^S)M-*Meyw>q!m=3kXdQJ0O) z8P3MJ6%=m#{G4MkZ(#{jJWRc_d+}~fftoIPiAnRA)fDD)%GAF8_9cIL z#PZMU$`^VsD0SIu?ViJy<#*jjf4Lc}`sXS2M1;FRO3dynQf0{CxJmPUA^69X~#t`%>`f@sjWLXV|aAbFH|t=w5-Y zLs&r8R~6%l2YbRJ&I{c*QMK}#>4Uasu6tRxGQ{OP?AF=Da^!kY_bR5PGh?Ri6uB|k zb=G?0NSi}4g{_D9BOi(PEZNE=v?tIrQ1GVLi;_;&`KM#9-EPrMi4}P6l=&c_DE4&8 z++yCRQPWomY0Ov5RqSqc6Wu7}`nO@#4d#z6tRdd>o~s=FA$2$V6Z=Z7?NN4m*%7O3 zgm28@E;;sm=MJyL6(-M%raBx8{}bq=(W*1+;;pN)In$b=mK^^${aTQyh;pdw+KoO2 zOV|B(GT$V`S)i+Jb;4D;3!TlkX>B@>PIk04&MZ*EPh^#WBPYoH70ZGX)5vWsIpIcY}lt zMfTlaZc%=yXZE%Rj*CWN32tl}8LQ)#&%T_y?54`Ik_RZfm7lyu&462+nQ+{Tja=Rth*}pBfEsmX2<$#`f4jPTkTKfy)2pJCmz1aN%+LiUrA9hN!96ld1PF=rkx5t?ceL5eW#|^ zqQw2dtxDmA+kReNF7R^qj;l&(cJ1dMpZ<9EdST%zSB|W86Mk787R`DsWg@5+ctoV3 z*rGw`UT}l#tDTXW`DbT&%?ZwKx_V<_%#04VMJ<7oxO`h@{)lM0^sYcYf2O4QWNryd zu4>nyefz%W?@->UD9qJx%rMjW$TLUpP}N;A*N^c1y>|On_p*~sbE+2=rk>38emPrr z#n0<@)_0;lR%IW(xUH*{#cjLn-78k1sVPa%Kl)X?>6!WbO?q5K>qGtR+1itjZJ2xU zxXP9;-5w{8xC;lm{eS3c_Ajj2@U+0_%*DBhEA|vsR(s!)iJBbY7wzsX_tEa#i@Ul@ z_Zs_uG8R>8_gbCBX{~kp`53`-iFV5VWoj?mf!;xve1n7J9^G)AB? xOBxx0wHL0c36ZyfPR5e2(VdJQH3%oqV#RF>IHCf)S=m6!S%A<7=rSWP4*>ZM4V?f0 literal 0 HcmV?d00001 diff --git a/tests/repositories/fixtures/pypi.org/json/black.json b/tests/repositories/fixtures/pypi.org/json/black.json new file mode 100644 index 00000000000..f4e244d1b22 --- /dev/null +++ b/tests/repositories/fixtures/pypi.org/json/black.json @@ -0,0 +1,146 @@ +{ + "info": { + "author": "\u0141ukasz Langa", + "author_email": "lukasz@langa.pl", + "bugtrack_url": null, + "classifiers": [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Quality Assurance" + ], + "description": "", + "description_content_type": "text/markdown", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "home_page": "https://github.com/psf/black", + "keywords": "automation formatter yapf autopep8 pyfmt gofmt rustfmt", + "license": "MIT", + "maintainer": "", + "maintainer_email": "", + "name": "black", + "package_url": "https://pypi.org/project/black/", + "platform": "", + "project_url": "https://pypi.org/project/black/", + "project_urls": { + "Homepage": "https://github.com/psf/black" + }, + "release_url": "https://pypi.org/project/black/19.10b0/", + "requires_dist": [ + "click (>=6.5)", + "attrs (>=18.1.0)", + "appdirs", + "toml (>=0.9.4)", + "typed-ast (>=1.4.0)", + "regex", + "pathspec (<1,>=0.6)", + "aiohttp (>=3.3.2) ; extra == 'd'", + "aiohttp-cors ; extra == 'd'" + ], + "requires_python": ">=3.6", + "summary": "The uncompromising code formatter.", + "version": "19.10b0", + "yanked": false, + "yanked_reason": null + }, + "last_serial": 6044498, + "releases": { + "19.10b0": [ + { + "comment_text": "", + "digests": { + "md5": "067efd0498107b5fb2299fbfb000b0b6", + "sha256": "1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b" + }, + "downloads": -1, + "filename": "black-19.10b0-py36-none-any.whl", + "has_sig": true, + "md5_digest": "067efd0498107b5fb2299fbfb000b0b6", + "packagetype": "bdist_wheel", + "python_version": "py36", + "requires_python": ">=3.6", + "size": 97525, + "upload_time": "2019-10-28T23:53:54", + "upload_time_iso_8601": "2019-10-28T23:53:54.000711Z", + "url": "https://files.pythonhosted.org/packages/fd/bb/ad34bbc93d1bea3de086d7c59e528d4a503ac8fe318bd1fa48605584c3d2/black-19.10b0-py36-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "496632a95b73b8f5c5081d795a4e6af1", + "sha256": "c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539" + }, + "downloads": -1, + "filename": "black-19.10b0.tar.gz", + "has_sig": true, + "md5_digest": "496632a95b73b8f5c5081d795a4e6af1", + "packagetype": "sdist", + "python_version": "source", + "requires_python": ">=3.6", + "size": 1019740, + "upload_time": "2019-10-28T23:54:05", + "upload_time_iso_8601": "2019-10-28T23:54:05.455213Z", + "url": "https://files.pythonhosted.org/packages/b0/dc/ecd83b973fb7b82c34d828aad621a6e5865764d52375b8ac1d7a45e23c8d/black-19.10b0.tar.gz", + "yanked": false, + "yanked_reason": null + } + ] + }, + "urls": [ + { + "comment_text": "", + "digests": { + "md5": "067efd0498107b5fb2299fbfb000b0b6", + "sha256": "1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b" + }, + "downloads": -1, + "filename": "black-19.10b0-py36-none-any.whl", + "has_sig": true, + "md5_digest": "067efd0498107b5fb2299fbfb000b0b6", + "packagetype": "bdist_wheel", + "python_version": "py36", + "requires_python": ">=3.6", + "size": 97525, + "upload_time": "2019-10-28T23:53:54", + "upload_time_iso_8601": "2019-10-28T23:53:54.000711Z", + "url": "https://files.pythonhosted.org/packages/fd/bb/ad34bbc93d1bea3de086d7c59e528d4a503ac8fe318bd1fa48605584c3d2/black-19.10b0-py36-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "496632a95b73b8f5c5081d795a4e6af1", + "sha256": "c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539" + }, + "downloads": -1, + "filename": "black-19.10b0.tar.gz", + "has_sig": true, + "md5_digest": "496632a95b73b8f5c5081d795a4e6af1", + "packagetype": "sdist", + "python_version": "source", + "requires_python": ">=3.6", + "size": 1019740, + "upload_time": "2019-10-28T23:54:05", + "upload_time_iso_8601": "2019-10-28T23:54:05.455213Z", + "url": "https://files.pythonhosted.org/packages/b0/dc/ecd83b973fb7b82c34d828aad621a6e5865764d52375b8ac1d7a45e23c8d/black-19.10b0.tar.gz", + "yanked": false, + "yanked_reason": null + } + ] +} diff --git a/tests/repositories/test_legacy_repository.py b/tests/repositories/test_legacy_repository.py index 1b29cc4c36f..189de7fd294 100644 --- a/tests/repositories/test_legacy_repository.py +++ b/tests/repositories/test_legacy_repository.py @@ -151,6 +151,27 @@ def test_find_packages_no_prereleases(): assert packages[0].source_url == repo.url +@pytest.mark.parametrize("constraint,count", [("*", 1), (">=1", 0), (">=19.0.0a0", 1)]) +def test_find_packages_only_prereleases(constraint, count): + repo = MockRepository() + packages = repo.find_packages("black", constraint=constraint) + + assert len(packages) == count + + if count >= 0: + for package in packages: + assert package.source_type == "legacy" + assert package.source_reference == repo.name + assert package.source_url == repo.url + + +def test_find_packages_only_prereleases_empty_when_not_any(): + repo = MockRepository() + packages = repo.find_packages("black", constraint=">=1") + + assert len(packages) == 0 + + def test_get_package_information_chooses_correct_distribution(): repo = MockRepository() diff --git a/tests/repositories/test_pypi_repository.py b/tests/repositories/test_pypi_repository.py index e095e9c10cf..61c588860ba 100644 --- a/tests/repositories/test_pypi_repository.py +++ b/tests/repositories/test_pypi_repository.py @@ -75,6 +75,14 @@ def test_find_packages_does_not_select_prereleases_if_not_allowed(): assert len(packages) == 1 +@pytest.mark.parametrize("constraint,count", [("*", 1), (">=1", 0), (">=19.0.0a0", 1)]) +def test_find_packages_only_prereleases(constraint, count): + repo = MockRepository() + packages = repo.find_packages("black", constraint=constraint) + + assert len(packages) == count + + def test_package(): repo = MockRepository()