Skip to content

Commit e5043a8

Browse files
authored
Merge pull request #134 from gmathiou4/Relative-links-confuse-PyPi
relative links_fix
2 parents 1831e01 + b74a7c3 commit e5043a8

18 files changed

+1413
-1269
lines changed

src/python_inspector/utils_pypi.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from typing import List
2121
from typing import NamedTuple
2222
from urllib.parse import quote_plus
23+
from urllib.parse import urlparse
24+
from urllib.parse import urlunparse
2325

2426
import attr
2527
import packageurl
@@ -949,7 +951,6 @@ def get_sdist_name_ver_ext(filename):
949951

950952
@attr.attributes
951953
class Sdist(Distribution):
952-
953954
extension = attr.ib(
954955
repr=False,
955956
type=str,
@@ -1595,11 +1596,37 @@ def fetch_links(
15951596
url, _, _sha256 = anchor_tag["href"].partition("#sha256=")
15961597
if "data-requires-python" in anchor_tag.attrs:
15971598
python_requires = anchor_tag.attrs["data-requires-python"]
1599+
# Resolve relative URL
1600+
url = resolve_relative_url(package_url, url)
15981601
links.append(Link(url=url, python_requires=python_requires))
15991602
# TODO: keep sha256
16001603
return links
16011604

16021605

1606+
def resolve_relative_url(package_url, url):
1607+
"""
1608+
Return the resolved `url` URLstring given a `package_url` base URL string
1609+
of a package.
1610+
1611+
For example:
1612+
>>> resolve_relative_url("https://example.com/package", "../path/file.txt")
1613+
'https://example.com/path/file.txt'
1614+
"""
1615+
if not url.startswith(("http://", "https://")):
1616+
base_url_parts = urlparse(package_url)
1617+
url_parts = urlparse(url)
1618+
# If the relative URL starts with '..', remove the last directory from the base URL
1619+
if url_parts.path.startswith(".."):
1620+
path = base_url_parts.path.rstrip("/").rsplit("/", 1)[0] + url_parts.path[2:]
1621+
else:
1622+
path = urlunparse(
1623+
("", "", url_parts.path, url_parts.params, url_parts.query, url_parts.fragment)
1624+
)
1625+
resolved_url_parts = base_url_parts._replace(path=path)
1626+
url = urlunparse(resolved_url_parts)
1627+
return url
1628+
1629+
16031630
PYPI_PUBLIC_REPO = PypiSimpleRepository(index_url=PYPI_SIMPLE_URL)
16041631
DEFAULT_PYPI_REPOS = (PYPI_PUBLIC_REPO,)
16051632
DEFAULT_PYPI_REPOS_BY_URL = {r.index_url: r for r in DEFAULT_PYPI_REPOS}

tests/data/azure-devops.req-310-expected.json

+342-265
Large diffs are not rendered by default.

tests/data/azure-devops.req-38-expected.json

+342-265
Large diffs are not rendered by default.

tests/data/fetch_links_test.html

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!DOCTYPE html>
2+
<html><head><title>Simple Index</title><meta name="api-version" value="2" /></head><body>
3+
<a href="../sources.whl#sha256=a4e66406acabbb0a6606c89c5e61bacd2847f5393818b90490021a3929b47807" rel="internal">sources.whl</a><br />
4+
</body></html>

tests/data/frozen-requirements.txt-expected.json

+21-19
Large diffs are not rendered by default.

tests/data/insecure-setup-2/setup.py-expected.json

+62-66
Large diffs are not rendered by default.

tests/data/pinned-pdt-requirements.txt-expected.json

+38-52
Large diffs are not rendered by default.

tests/data/pinned-requirements.txt-expected.json

+40-54
Large diffs are not rendered by default.
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[
2+
[
3+
"https://pypi.org/simple/sources.whl",
4+
null
5+
]
6+
]

tests/data/single-url-except-simple-expected.json

+269-174
Large diffs are not rendered by default.

tests/data/test-api-expected.json

+67-109
Large diffs are not rendered by default.

tests/data/test-api-pdt-expected.json

+64-106
Large diffs are not rendered by default.

tests/data/test-api-with-partial-setup-py.json

+9-9
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@
5959
"type": "pypi",
6060
"namespace": null,
6161
"name": "semver",
62-
"version": "3.0.0",
62+
"version": "3.0.1",
6363
"qualifiers": {},
6464
"subpath": null,
6565
"primary_language": "Python",
6666
"description": "Python helper for Semantic Versioning (https://semver.org)\nQuickstart\n==========\n\n.. teaser-begin\n\nA Python module for `semantic versioning`_. Simplifies comparing versions.\n\n|GHAction| |python-support| |downloads| |license| |docs| |black|\n|openissues| |GHDiscussion|\n\n.. teaser-end\n\n.. note::\n\n This project works for Python 3.7 and greater only. If you are\n looking for a compatible version for Python 2, use the\n maintenance branch |MAINT|_.\n\n The last version of semver which supports Python 2.7 to 3.5 will be\n 2.x.y However, keep in mind, the major 2 release is frozen: no new\n features nor backports will be integrated.\n\n We recommend to upgrade your workflow to Python 3 to gain support,\n bugfixes, and new features.\n\n.. |MAINT| replace:: ``maint/v2``\n.. _MAINT: https://github.com/python-semver/python-semver/tree/maint/v2\n\n\nThe module follows the ``MAJOR.MINOR.PATCH`` style:\n\n* ``MAJOR`` version when you make incompatible API changes,\n* ``MINOR`` version when you add functionality in a backwards compatible manner, and\n* ``PATCH`` version when you make backwards compatible bug fixes.\n\nAdditional labels for pre-release and build metadata are supported.\n\nTo import this library, use:\n\n.. code-block:: python\n\n >>> import semver\n\nWorking with the library is quite straightforward. To turn a version string into the\ndifferent parts, use the ``semver.Version.parse`` function:\n\n.. code-block:: python\n\n >>> ver = semver.Version.parse('1.2.3-pre.2+build.4')\n >>> ver.major\n 1\n >>> ver.minor\n 2\n >>> ver.patch\n 3\n >>> ver.prerelease\n 'pre.2'\n >>> ver.build\n 'build.4'\n\nTo raise parts of a version, there are a couple of functions available for\nyou. The function ``semver.Version.bump_major`` leaves the original object untouched, but\nreturns a new ``semver.Version`` instance with the raised major part:\n\n.. code-block:: python\n\n >>> ver = semver.Version.parse(\"3.4.5\")\n >>> ver.bump_major()\n Version(major=4, minor=0, patch=0, prerelease=None, build=None)\n\nIt is allowed to concatenate different \"bump functions\":\n\n.. code-block:: python\n\n >>> ver.bump_major().bump_minor()\n Version(major=4, minor=1, patch=0, prerelease=None, build=None)\n\nTo compare two versions, semver provides the ``semver.compare`` function.\nThe return value indicates the relationship between the first and second\nversion:\n\n.. code-block:: python\n\n >>> semver.compare(\"1.0.0\", \"2.0.0\")\n -1\n >>> semver.compare(\"2.0.0\", \"1.0.0\")\n 1\n >>> semver.compare(\"2.0.0\", \"2.0.0\")\n 0\n\n\nThere are other functions to discover. Read on!\n\n\n.. |latest-version| image:: https://img.shields.io/pypi/v/semver.svg\n :alt: Latest version on PyPI\n :target: https://pypi.org/project/semver\n.. |python-support| image:: https://img.shields.io/pypi/pyversions/semver.svg\n :target: https://pypi.org/project/semver\n :alt: Python versions\n.. |downloads| image:: https://img.shields.io/pypi/dm/semver.svg\n :alt: Monthly downloads from PyPI\n :target: https://pypi.org/project/semver\n.. |license| image:: https://img.shields.io/pypi/l/semver.svg\n :alt: Software license\n :target: https://github.com/python-semver/python-semver/blob/master/LICENSE.txt\n.. |docs| image:: https://readthedocs.org/projects/python-semver/badge/?version=latest\n :target: http://python-semver.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n.. _semantic versioning: https://semver.org/\n.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/psf/black\n :alt: Black Formatter\n.. |Gitter| image:: https://badges.gitter.im/python-semver/community.svg\n :target: https://gitter.im/python-semver/community\n :alt: Gitter\n.. |openissues| image:: http://isitmaintained.com/badge/open/python-semver/python-semver.svg\n :target: http://isitmaintained.com/project/python-semver/python-semver\n :alt: Percentage of open issues\n.. |GHAction| image:: https://github.com/python-semver/python-semver/workflows/Python/badge.svg\n :alt: Python\n.. |GHDiscussion| image:: https://shields.io/badge/GitHub-%20Discussions-green?logo=github\n :target: https://github.com/python-semver/python-semver/discussions\n :alt: GitHub Discussion",
67-
"release_date": "2023-04-02T13:20:12",
67+
"release_date": "2023-06-14T11:43:22",
6868
"parties": [
6969
{
7070
"type": "person",
@@ -96,11 +96,11 @@
9696
"Topic :: Software Development :: Libraries :: Python Modules"
9797
],
9898
"homepage_url": "https://github.com/python-semver/python-semver",
99-
"download_url": "https://files.pythonhosted.org/packages/9f/93/b7389cdd7e573e70cfbeb4b0bbe101af1050a6681342f5d2bc6f1bf2d150/semver-3.0.0.tar.gz",
100-
"size": 204359,
99+
"download_url": "https://files.pythonhosted.org/packages/46/30/a14b56e500e8eabf8c349edd0583d736b231e652b7dce776e85df11e9e0b/semver-3.0.1.tar.gz",
100+
"size": 205762,
101101
"sha1": null,
102-
"md5": "0d36e4e2b2c4366f2b4b5c53b2abe3c0",
103-
"sha256": "94df43924c4521ec7d307fc86da1531db6c2c33d9d5cdc3e64cca0eb68569269",
102+
"md5": "b7502c12ce325ffffeab694fed52f6f5",
103+
"sha256": "9ec78c5447883c67b97f98c3b6212796708191d22e4ad30f4570f840171cbce1",
104104
"sha512": null,
105105
"bug_tracking_url": "https://github.com/python-semver/python-semver/issues",
106106
"code_view_url": null,
@@ -120,14 +120,14 @@
120120
"dependencies": [],
121121
"repository_homepage_url": null,
122122
"repository_download_url": null,
123-
"api_data_url": "https://pypi.org/pypi/semver/3.0.0/json",
123+
"api_data_url": "https://pypi.org/pypi/semver/3.0.1/json",
124124
"datasource_id": null,
125-
"purl": "pkg:pypi/[email protected].0"
125+
"purl": "pkg:pypi/[email protected].1"
126126
}
127127
],
128128
"resolution": [
129129
{
130-
"package": "pkg:pypi/[email protected].0",
130+
"package": "pkg:pypi/[email protected].1",
131131
"dependencies": []
132132
}
133133
]

0 commit comments

Comments
 (0)