|
26 | 26 | from typing import Union |
27 | 27 | from urllib.parse import quote_plus |
28 | 28 | from urllib.parse import unquote |
| 29 | +from urllib.parse import urljoin |
29 | 30 | from urllib.parse import urlparse |
30 | 31 | from urllib.parse import urlunparse |
31 | 32 |
|
@@ -1631,25 +1632,27 @@ async def fetch_links( |
1631 | 1632 |
|
1632 | 1633 | def resolve_relative_url(package_url, url): |
1633 | 1634 | """ |
1634 | | - Return the resolved `url` URLstring given a `package_url` base URL string |
| 1635 | + Return the resolved `url` URL string given a `package_url` base URL string |
1635 | 1636 | of a package. |
1636 | 1637 |
|
1637 | 1638 | For example: |
1638 | 1639 | >>> resolve_relative_url("https://example.com/package", "../path/file.txt") |
1639 | 1640 | 'https://example.com/path/file.txt' |
| 1641 | + >>> resolve_relative_url("https://example.com/simple/pkg/", "../../packages/file.whl") |
| 1642 | + 'https://example.com/packages/file.whl' |
1640 | 1643 | """ |
1641 | 1644 | if not url.startswith(("http://", "https://")): |
1642 | 1645 | base_url_parts = urlparse(package_url) |
1643 | 1646 | url_parts = urlparse(url) |
1644 | | - # If the relative URL starts with '..', remove the last directory from the base URL |
| 1647 | + # If the relative URL starts with '..', use urljoin to handle multi-level '../' |
1645 | 1648 | if url_parts.path.startswith(".."): |
1646 | | - path = base_url_parts.path.rstrip("/").rsplit("/", 1)[0] + url_parts.path[2:] |
| 1649 | + url = urljoin(package_url, url) |
1647 | 1650 | else: |
1648 | 1651 | path = urlunparse( |
1649 | 1652 | ("", "", url_parts.path, url_parts.params, url_parts.query, url_parts.fragment) |
1650 | 1653 | ) |
1651 | | - resolved_url_parts = base_url_parts._replace(path=path) |
1652 | | - url = urlunparse(resolved_url_parts) |
| 1654 | + resolved_url_parts = base_url_parts._replace(path=path) |
| 1655 | + url = urlunparse(resolved_url_parts) |
1653 | 1656 | return url |
1654 | 1657 |
|
1655 | 1658 |
|
|
0 commit comments