Skip to content

Commit

Permalink
support for file dependencies with subdirectories (analogous to git a…
Browse files Browse the repository at this point in the history
…nd url dependencies)
  • Loading branch information
radoering committed Sep 27, 2022
1 parent 115ef5e commit 1962c43
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/poetry/core/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ def create_dependency(
dependency = FileDependency(
name,
file_path,
directory=constraint.get("subdirectory", None),
groups=groups,
base=root_dir,
extras=constraint.get("extras", []),
Expand All @@ -305,12 +306,16 @@ def create_dependency(
dependency = FileDependency(
name,
path,
directory=constraint.get("subdirectory", None),
groups=groups,
optional=optional,
base=root_dir,
extras=constraint.get("extras", []),
)
else:
subdirectory = constraint.get("subdirectory", None)
if subdirectory:
path = path / subdirectory
dependency = DirectoryDependency(
name,
path,
Expand Down
8 changes: 8 additions & 0 deletions src/poetry/core/json/schemas/poetry-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,10 @@
"type": "string",
"description": "The path 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."
Expand Down Expand Up @@ -464,6 +468,10 @@
"type": "string",
"description": "The path to the dependency."
},
"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."
Expand Down
11 changes: 9 additions & 2 deletions src/poetry/core/packages/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,11 @@ def create_from_pep_508(
# handle RFC 8089 references
path = url_to_path(req.url)
dep = _make_file_or_dir_dep(
name=name, path=path, base=relative_to, extras=req.extras
name=name,
path=path,
base=relative_to,
subdirectory=link.subdirectory_fragment,
extras=req.extras,
)
else:
with suppress(ValueError):
Expand Down Expand Up @@ -509,6 +513,7 @@ def _make_file_or_dir_dep(
name: str,
path: Path,
base: Path | None = None,
subdirectory: str | None = None,
extras: list[str] | None = None,
) -> FileDependency | DirectoryDependency | None:
"""
Expand All @@ -524,7 +529,9 @@ def _make_file_or_dir_dep(
_path = Path(base) / path

if _path.is_file():
return FileDependency(name, path, base=base, extras=extras)
return FileDependency(
name, path, base=base, directory=subdirectory, extras=extras
)
elif _path.is_dir():
return DirectoryDependency(name, path, base=base, extras=extras)

Expand Down
11 changes: 11 additions & 0 deletions src/poetry/core/packages/file_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def __init__(
self,
name: str,
path: Path,
*,
directory: str | None = None,
groups: Iterable[str] | None = None,
optional: bool = False,
base: Path | None = None,
Expand All @@ -23,6 +25,7 @@ def __init__(
self._path = path
self._base = base or Path.cwd()
self._full_path = path
self._directory = directory

if not self._path.is_absolute():
try:
Expand All @@ -44,6 +47,7 @@ def __init__(
allows_prereleases=True,
source_type="file",
source_url=self._full_path.as_posix(),
source_subdirectory=directory,
extras=extras,
)

Expand All @@ -59,6 +63,10 @@ def path(self) -> Path:
def full_path(self) -> Path:
return self._full_path

@property
def directory(self) -> str | None:
return self._directory

def is_file(self) -> bool:
return True

Expand All @@ -83,4 +91,7 @@ def base_pep_508_name(self) -> str:
)
requirement += f" @ {path}"

if self.directory:
requirement += f"#subdirectory={self.directory}"

return requirement
1 change: 1 addition & 0 deletions src/poetry/core/packages/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ def to_dependency(self) -> Dependency:
dep = FileDependency(
self._name,
Path(self._source_url),
directory=self.source_subdirectory,
groups=list(self._dependency_groups.keys()),
optional=self.optional,
base=self.root_dir,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
My Package
==========
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[tool.poetry]
name = "my-package"
version = "1.2.3"
description = "Some description."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
]
license = "MIT"

readme = "README.rst"

homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"

keywords = ["packaging", "dependency", "poetry"]

classifiers = [
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Libraries :: Python Modules"
]

# Requirements
[tool.poetry.dependencies]
python = "~2.7 || ^3.6"

# Git dependency with subdirectory
pendulum = { git = "https://github.com/sdispater/pendulum.git", subdirectory = "sub", branch = "2.0" }

# File dependency with subdirectory
demo = [
{ path = "../distributions/demo-0.1.0-py2.py3-none-any.whl", subdirectory = "sub", platform = "linux" },
{ file = "../distributions/demo-0.1.0-py2.py3-none-any.whl", subdirectory = "sub", platform = "win32" }
]

# Dir dependency with subdirectory (same as path "../simple_project" without subdirectory)
simple-project = { path = "..", subdirectory = "simple_project" }

# Url dependency with subdirectory
foo = { url = "https://example.com/foo.zip", subdirectory = "sub" }
10 changes: 9 additions & 1 deletion tests/packages/test_file_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,15 @@ def test_file_dependency_pep_508_local_file_relative_path(
_test_file_dependency_pep_508(mocker, "demo", path, requirement, expected)


def test_absolute_file_dependency_to_pep_508_with_marker(mocker: MockerFixture) -> None:
def test_file_dependency_pep_508_with_subdirectory(mocker: MockerFixture) -> None:
path = DIST_PATH / "demo.zip"
expected = f"demo @ {path.as_uri()}#subdirectory=sub"

requirement = f"demo @ file://{path.as_posix()}#subdirectory=sub"
_test_file_dependency_pep_508(mocker, "demo", path, requirement, expected)


def test_to_pep_508_with_marker(mocker: MockerFixture) -> None:
wheel = "demo-0.1.0-py2.py3-none-any.whl"

abs_path = DIST_PATH / wheel
Expand Down
2 changes: 2 additions & 0 deletions tests/packages/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ def test_to_dependency_for_file() -> None:
"1.2.3",
source_type="file",
source_url=path.as_posix(),
source_subdirectory="qux",
features=["baz", "bar"],
)
dep = package.to_dependency()
Expand All @@ -377,6 +378,7 @@ def test_to_dependency_for_file() -> None:
assert dep.path == path
assert dep.source_type == "file"
assert dep.source_url == path.as_posix()
assert dep.source_subdirectory == "qux"


def test_to_dependency_for_url() -> None:
Expand Down
50 changes: 50 additions & 0 deletions tests/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

from poetry.core.constraints.version import parse_constraint
from poetry.core.factory import Factory
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency
from poetry.core.toml import TOMLFile
Expand Down Expand Up @@ -146,6 +148,54 @@ def test_create_poetry() -> None:
]


def test_create_poetry_with_dependencies_with_subdirectory() -> None:
poetry = Factory().create_poetry(
fixtures_dir / "project_with_dependencies_with_subdirectory"
)
package = poetry.package
dependencies = {str(dep.name): dep for dep in package.requires}

# git dependency
pendulum = dependencies["pendulum"]
assert pendulum.is_vcs()
assert pendulum.pretty_constraint == "branch 2.0"
pendulum = cast(VCSDependency, pendulum)
assert pendulum.source == "https://github.com/sdispater/pendulum.git"
assert pendulum.directory == "sub"

# file dependency
demo = dependencies["demo"]
assert demo.is_file()
assert demo.pretty_constraint == "*"
demo = cast(FileDependency, demo)
assert demo.path == Path("../distributions/demo-0.1.0-py2.py3-none-any.whl")
assert demo.directory == "sub"
demo_dependencies = [dep for dep in package.requires if dep.name == "demo"]
assert len(demo_dependencies) == 2
assert demo_dependencies[0] == demo_dependencies[1]
assert {str(dep.marker) for dep in demo_dependencies} == {
'sys_platform == "win32"',
'sys_platform == "linux"',
}

# directory dependency
simple_project = dependencies["simple-project"]
assert simple_project.is_directory()
assert simple_project.pretty_constraint == "*"
simple_project = cast(DirectoryDependency, simple_project)
assert simple_project.path == Path("../simple_project")
with pytest.raises(AttributeError):
simple_project.directory # type: ignore[attr-defined]

# url dependency
foo = dependencies["foo"]
assert foo.is_url()
assert foo.pretty_constraint == "*"
foo = cast(URLDependency, foo)
assert foo.url == "https://example.com/foo.zip"
assert foo.directory == "sub"


def test_create_poetry_with_packages_and_includes() -> None:
poetry = Factory().create_poetry(
fixtures_dir.parent / "masonry" / "builders" / "fixtures" / "with-include"
Expand Down

0 comments on commit 1962c43

Please sign in to comment.