diff --git a/src/poetry/core/factory.py b/src/poetry/core/factory.py index 90b8974df..b47eb294c 100644 --- a/src/poetry/core/factory.py +++ b/src/poetry/core/factory.py @@ -323,6 +323,7 @@ def create_dependency( dependency = URLDependency( name, constraint["url"], + directory=constraint.get("subdirectory", None), groups=groups, optional=optional, extras=constraint.get("extras", []), diff --git a/src/poetry/core/json/schemas/poetry-schema.json b/src/poetry/core/json/schemas/poetry-schema.json index 6c5199105..790367fe2 100644 --- a/src/poetry/core/json/schemas/poetry-schema.json +++ b/src/poetry/core/json/schemas/poetry-schema.json @@ -504,6 +504,10 @@ "type": "string", "description": "The url to the file." }, + "subdirectory": { + "type": "string", + "description": "The relative path to the directory where the package is located." + }, "python": { "type": "string", "description": "The python versions for which the dependency should be installed." diff --git a/src/poetry/core/packages/dependency.py b/src/poetry/core/packages/dependency.py index 4310d6953..419bed4d5 100644 --- a/src/poetry/core/packages/dependency.py +++ b/src/poetry/core/packages/dependency.py @@ -526,7 +526,12 @@ def create_from_pep_508( name, "git", link.url_without_fragment, extras=req.extras ) elif link.scheme in ["http", "https"]: - dep = URLDependency(name, link.url, extras=req.extras) + dep = URLDependency( + name, + link.url_without_fragment, + directory=link.subdirectory_fragment, + extras=req.extras, + ) elif is_file_uri: # handle RFC 8089 references path = url_to_path(req.url) diff --git a/src/poetry/core/packages/package.py b/src/poetry/core/packages/package.py index aa04880d7..90839c04d 100644 --- a/src/poetry/core/packages/package.py +++ b/src/poetry/core/packages/package.py @@ -506,6 +506,7 @@ def to_dependency(self) -> Dependency: dep = URLDependency( self._name, cast(str, self._source_url), + directory=self.source_subdirectory, groups=list(self._dependency_groups.keys()), optional=self.optional, extras=self.features, diff --git a/src/poetry/core/packages/url_dependency.py b/src/poetry/core/packages/url_dependency.py index fd020d595..610eb3158 100644 --- a/src/poetry/core/packages/url_dependency.py +++ b/src/poetry/core/packages/url_dependency.py @@ -11,11 +11,14 @@ def __init__( self, name: str, url: str, + *, + directory: str | None = None, groups: Iterable[str] | None = None, optional: bool = False, extras: Iterable[str] | None = None, ) -> None: self._url = url + self._directory = directory parsed = urlparse(url) if not parsed.scheme or not parsed.netloc: @@ -29,6 +32,7 @@ def __init__( allows_prereleases=True, source_type="url", source_url=self._url, + source_subdirectory=directory, extras=extras, ) @@ -36,6 +40,10 @@ def __init__( def url(self) -> str: return self._url + @property + def directory(self) -> str | None: + return self._directory + @property def base_pep_508_name(self) -> str: requirement = self.pretty_name @@ -46,6 +54,9 @@ def base_pep_508_name(self) -> str: requirement += f" @ {self._url}" + if self.directory: + requirement += f"#subdirectory={self.directory}" + return requirement def is_url(self) -> bool: diff --git a/tests/packages/test_main.py b/tests/packages/test_main.py index f284ccc91..0c43597c2 100644 --- a/tests/packages/test_main.py +++ b/tests/packages/test_main.py @@ -237,6 +237,21 @@ def test_dependency_from_pep_508_with_url() -> None: assert dep.url == "https://example.com/django-utils-1.0.0.tar.gz" +def test_dependency_from_pep_508_with_url_and_subdirectory() -> None: + name = ( + "django-utils @" + " https://example.com/django-utils-1.0.0.tar.gz#subdirectory=django" + ) + + dep = Dependency.create_from_pep_508(name) + + assert dep.name == "django-utils" + assert dep.is_url() + dep = cast(URLDependency, dep) + assert dep.url == "https://example.com/django-utils-1.0.0.tar.gz" + assert dep.directory == "django" + + def test_dependency_from_pep_508_with_wheel_url() -> None: name = ( "example_wheel @ https://example.com/example_wheel-14.0.2-py2.py3-none-any.whl" diff --git a/tests/packages/test_package.py b/tests/packages/test_package.py index ffaec8316..46864fd8f 100644 --- a/tests/packages/test_package.py +++ b/tests/packages/test_package.py @@ -333,6 +333,7 @@ def test_to_dependency_for_url() -> None: "1.2.3", source_type="url", source_url="https://example.com/path.tar.gz", + source_subdirectory="qux", features=["baz", "bar"], ) dep = package.to_dependency() @@ -345,6 +346,7 @@ def test_to_dependency_for_url() -> None: assert dep.url == "https://example.com/path.tar.gz" assert dep.source_type == "url" assert dep.source_url == "https://example.com/path.tar.gz" + assert dep.source_subdirectory == "qux" def test_to_dependency_for_vcs() -> None: diff --git a/tests/packages/test_url_dependency.py b/tests/packages/test_url_dependency.py index 08697f85f..bc8ce2a80 100644 --- a/tests/packages/test_url_dependency.py +++ b/tests/packages/test_url_dependency.py @@ -33,6 +33,17 @@ def test_to_pep_508_with_extras() -> None: assert expected == dependency.to_pep_508() +def test_to_pep_508_with_subdirectory() -> None: + dependency = URLDependency( + "demo", + "https://github.com/foo/bar/archive/0.1.0.zip", + directory="baz", + ) + + expected = "demo @ https://github.com/foo/bar/archive/0.1.0.zip#subdirectory=baz" + assert expected == dependency.to_pep_508() + + def test_to_pep_508_with_marker() -> None: dependency = URLDependency( "pytorch",