Skip to content

Commit 3fa3b42

Browse files
committed
Read PEP-610-compliant files when loading installed packages
1 parent 426f0a3 commit 3fa3b42

File tree

12 files changed

+262
-52
lines changed

12 files changed

+262
-52
lines changed

poetry/repositories/installed_repository.py

+140-52
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import itertools
2+
import json
23

34
from pathlib import Path
5+
from typing import TYPE_CHECKING
46
from typing import Set
7+
from typing import Tuple
58
from typing import Union
69

710
from poetry.core.packages.package import Package
11+
from poetry.core.packages.utils.utils import url_to_path
12+
from poetry.core.utils.helpers import canonicalize_name
813
from poetry.core.utils.helpers import module_name
914
from poetry.utils._compat import metadata
1015
from poetry.utils.env import Env
@@ -14,6 +19,9 @@
1419

1520
_VENDORS = Path(__file__).parent.parent.joinpath("_vendor")
1621

22+
if TYPE_CHECKING:
23+
from importlib.metadata import Distribution
24+
1725

1826
try:
1927
FileNotFoundError
@@ -68,21 +76,14 @@ def get_package_paths(cls, env: Env, name: str) -> Set[Path]:
6876
return paths
6977

7078
@classmethod
71-
def set_package_vcs_properties_from_path(cls, src: Path, package: Package) -> None:
79+
def get_package_vcs_properties_from_path(cls, src: Path) -> Tuple[str, str, str]:
7280
from poetry.core.vcs.git import Git
7381

7482
git = Git()
7583
revision = git.rev_parse("HEAD", src).strip()
7684
url = git.remote_url(src)
7785

78-
package._source_type = "git"
79-
package._source_url = url
80-
package._source_reference = revision
81-
82-
@classmethod
83-
def set_package_vcs_properties(cls, package: Package, env: Env) -> None:
84-
src = env.path / "src" / package.name
85-
cls.set_package_vcs_properties_from_path(src, package)
86+
return "git", url, revision
8687

8788
@classmethod
8889
def is_vcs_package(cls, package: Union[Path, Package], env: Env) -> bool:
@@ -99,6 +100,125 @@ def is_vcs_package(cls, package: Union[Path, Package], env: Env) -> bool:
99100
else:
100101
return True
101102

103+
@classmethod
104+
def create_package_from_distribution(
105+
cls, distribution: "Distribution", env: "Env"
106+
) -> Package:
107+
# We first check for a direct_url.json file to determine
108+
# the type of package.
109+
path = Path(str(distribution._path))
110+
111+
if (
112+
path.name.endswith(".dist-info")
113+
and path.joinpath("direct_url.json").exists()
114+
):
115+
return cls.create_package_from_pep610(distribution)
116+
117+
is_standard_package = env.is_path_relative_to_lib(path)
118+
119+
source_type = None
120+
source_url = None
121+
source_reference = None
122+
source_resolved_reference = None
123+
if is_standard_package:
124+
if path.name.endswith(".dist-info"):
125+
paths = cls.get_package_paths(
126+
env=env, name=distribution.metadata["name"]
127+
)
128+
if paths:
129+
is_editable_package = False
130+
for src in paths:
131+
if cls.is_vcs_package(src, env):
132+
(
133+
source_type,
134+
source_url,
135+
source_reference,
136+
) = cls.get_package_vcs_properties_from_path(src)
137+
break
138+
139+
if not (
140+
is_editable_package or env.is_path_relative_to_lib(src)
141+
):
142+
is_editable_package = True
143+
else:
144+
# TODO: handle multiple source directories?
145+
if is_editable_package:
146+
source_type = "directory"
147+
source_url = paths.pop().as_posix()
148+
else:
149+
if cls.is_vcs_package(path, env):
150+
(
151+
source_type,
152+
source_url,
153+
source_reference,
154+
) = cls.get_package_vcs_properties_from_path(
155+
env.path / "src" / canonicalize_name(distribution.metadata["name"])
156+
)
157+
else:
158+
# If not, it's a path dependency
159+
source_type = "directory"
160+
source_url = str(path.parent)
161+
162+
package = Package(
163+
distribution.metadata["name"],
164+
distribution.metadata["version"],
165+
source_type=source_type,
166+
source_url=source_url,
167+
source_reference=source_reference,
168+
source_resolved_reference=source_resolved_reference,
169+
)
170+
package.description = distribution.metadata.get("summary", "")
171+
172+
return package
173+
174+
@classmethod
175+
def create_package_from_pep610(cls, distribution: "Distribution") -> Package:
176+
path = Path(str(distribution._path))
177+
source_type = None
178+
source_url = None
179+
source_reference = None
180+
source_resolved_reference = None
181+
develop = False
182+
183+
url_reference = json.loads(
184+
path.joinpath("direct_url.json").read_text(encoding="utf-8")
185+
)
186+
if "archive_info" in url_reference:
187+
# File or URL distribution
188+
if url_reference["url"].startswith("file:"):
189+
# File distribution
190+
source_type = "file"
191+
source_url = url_to_path(url_reference["url"]).as_posix()
192+
else:
193+
# URL distribution
194+
source_type = "url"
195+
source_url = url_reference["url"]
196+
elif "dir_info" in url_reference:
197+
# Directory distribution
198+
source_type = "directory"
199+
source_url = url_to_path(url_reference["url"]).as_posix()
200+
develop = url_reference["dir_info"].get("editable", False)
201+
elif "vcs_info" in url_reference:
202+
# VCS distribution
203+
source_type = url_reference["vcs_info"]["vcs"]
204+
source_url = url_reference["url"]
205+
source_reference = url_reference["vcs_info"]["requested_revision"]
206+
source_resolved_reference = url_reference["vcs_info"]["commit_id"]
207+
208+
package = Package(
209+
distribution.metadata["name"],
210+
distribution.metadata["version"],
211+
source_type=source_type,
212+
source_url=source_url,
213+
source_reference=source_reference,
214+
source_resolved_reference=source_resolved_reference,
215+
develop=develop,
216+
)
217+
218+
package.description = distribution.metadata.get("summary", "")
219+
220+
return package
221+
102222
@classmethod
103223
def load(cls, env: Env, with_dependencies: bool = False) -> "InstalledRepository":
104224
"""
@@ -114,60 +234,28 @@ def load(cls, env: Env, with_dependencies: bool = False) -> "InstalledRepository
114234
metadata.distributions(path=[entry]),
115235
key=lambda d: str(d._path),
116236
):
117-
name = distribution.metadata["name"]
118-
path = Path(str(distribution._path))
119-
version = distribution.metadata["version"]
120-
package = Package(name, version, version)
121-
package.description = distribution.metadata.get("summary", "")
237+
name = canonicalize_name(distribution.metadata["name"])
122238

123-
if with_dependencies:
124-
for require in distribution.metadata.get_all("requires-dist", []):
125-
dep = Dependency.create_from_pep_508(require)
126-
package.add_dependency(dep)
127-
128-
if package.name in seen:
239+
if name in seen:
129240
continue
130241

242+
path = Path(str(distribution._path))
243+
131244
try:
132245
path.relative_to(_VENDORS)
133246
except ValueError:
134247
pass
135248
else:
136249
continue
137250

138-
seen.add(package.name)
139-
140-
repo.add_package(package)
251+
package = cls.create_package_from_distribution(distribution, env)
141252

142-
is_standard_package = env.is_path_relative_to_lib(path)
143-
144-
if is_standard_package:
145-
if path.name.endswith(".dist-info"):
146-
paths = cls.get_package_paths(env=env, name=package.pretty_name)
147-
if paths:
148-
is_editable_package = False
149-
for src in paths:
150-
if cls.is_vcs_package(src, env):
151-
cls.set_package_vcs_properties(package, env)
152-
break
153-
154-
if not (
155-
is_editable_package
156-
or env.is_path_relative_to_lib(src)
157-
):
158-
is_editable_package = True
159-
else:
160-
# TODO: handle multiple source directories?
161-
if is_editable_package:
162-
package._source_type = "directory"
163-
package._source_url = paths.pop().as_posix()
164-
continue
253+
if with_dependencies:
254+
for require in distribution.metadata.get_all("requires-dist", []):
255+
dep = Dependency.create_from_pep_508(require)
256+
package.add_dependency(dep)
165257

166-
if cls.is_vcs_package(path, env):
167-
cls.set_package_vcs_properties(package, env)
168-
else:
169-
# If not, it's a path dependency
170-
package._source_type = "directory"
171-
package._source_url = str(path.parent)
258+
seen.add(package.name)
259+
repo.add_package(package)
172260

173261
return repo
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Metadata-Version: 2.1
2+
Name: directory-pep-610
3+
Version: 1.2.3
4+
Summary: Foo
5+
License: MIT
6+
Requires-Python: >=3.6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"url": "file:///path/to/distributions/directory-pep-610",
3+
"dir_info": {}
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Metadata-Version: 2.1
2+
Name: editable-directory-pep-610
3+
Version: 1.2.3
4+
Summary: Foo
5+
License: MIT
6+
Requires-Python: >=3.6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"url": "file:///path/to/distributions/directory-pep-610",
3+
"dir_info": {
4+
"editable": true
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Metadata-Version: 2.1
2+
Name: file-pep-610
3+
Version: 1.2.3
4+
Summary: Foo
5+
License: MIT
6+
Requires-Python: >=3.6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"url": "file:///path/to/distributions/file-pep-610-1.2.3.tar.gz",
3+
"archive_info": {
4+
"hash": "sha256=2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Metadata-Version: 2.1
2+
Name: git-pep-610
3+
Version: 1.2.3
4+
Summary: Foo
5+
License: MIT
6+
Requires-Python: >=3.6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"url": "https://github.com/demo/git-pep-610.git",
3+
"vcs_info": {
4+
"vcs": "git",
5+
"requested_revision": "my-branch",
6+
"commit_id": "123456"
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Metadata-Version: 2.1
2+
Name: url-pep-610
3+
Version: 1.2.3
4+
Summary: Foo
5+
License: MIT
6+
Requires-Python: >=3.6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"url": "https://python-poetry.org/distributions/url-pep-610-1.2.3.tar.gz",
3+
"archive_info": {}
4+
}

tests/repositories/test_installed_repository.py

+64
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@
3030
metadata.PathDistribution(SITE_PURELIB / "editable-with-import-2.3.4.dist-info"),
3131
metadata.PathDistribution(SITE_PLATLIB / "lib64-2.3.4.dist-info"),
3232
metadata.PathDistribution(SITE_PLATLIB / "bender-2.0.5.dist-info"),
33+
metadata.PathDistribution(SITE_PURELIB / "git_pep_610-1.2.3.dist-info"),
34+
metadata.PathDistribution(SITE_PURELIB / "url_pep_610-1.2.3.dist-info"),
35+
metadata.PathDistribution(SITE_PURELIB / "file_pep_610-1.2.3.dist-info"),
36+
metadata.PathDistribution(SITE_PURELIB / "directory_pep_610-1.2.3.dist-info"),
37+
metadata.PathDistribution(
38+
SITE_PURELIB / "editable_directory_pep_610-1.2.3.dist-info"
39+
),
3340
]
3441

3542

@@ -165,3 +172,60 @@ def test_load_standard_package_with_pth_file(repository):
165172
assert standard.version.text == "1.2.3"
166173
assert standard.source_type is None
167174
assert standard.source_url is None
175+
176+
177+
def test_load_pep_610_compliant_git_packages(repository):
178+
package = get_package_from_repository("git-pep-610", repository)
179+
180+
assert package is not None
181+
assert package.name == "git-pep-610"
182+
assert package.version.text == "1.2.3"
183+
assert package.source_type == "git"
184+
assert package.source_url == "https://github.com/demo/git-pep-610.git"
185+
assert package.source_reference == "my-branch"
186+
assert package.source_resolved_reference == "123456"
187+
188+
189+
def test_load_pep_610_compliant_url_packages(repository):
190+
package = get_package_from_repository("url-pep-610", repository)
191+
192+
assert package is not None
193+
assert package.name == "url-pep-610"
194+
assert package.version.text == "1.2.3"
195+
assert package.source_type == "url"
196+
assert (
197+
package.source_url
198+
== "https://python-poetry.org/distributions/url-pep-610-1.2.3.tar.gz"
199+
)
200+
201+
202+
def test_load_pep_610_compliant_file_packages(repository):
203+
package = get_package_from_repository("file-pep-610", repository)
204+
205+
assert package is not None
206+
assert package.name == "file-pep-610"
207+
assert package.version.text == "1.2.3"
208+
assert package.source_type == "file"
209+
assert package.source_url == "/path/to/distributions/file-pep-610-1.2.3.tar.gz"
210+
211+
212+
def test_load_pep_610_compliant_directory_packages(repository):
213+
package = get_package_from_repository("directory-pep-610", repository)
214+
215+
assert package is not None
216+
assert package.name == "directory-pep-610"
217+
assert package.version.text == "1.2.3"
218+
assert package.source_type == "directory"
219+
assert package.source_url == "/path/to/distributions/directory-pep-610"
220+
assert not package.develop
221+
222+
223+
def test_load_pep_610_compliant_editable_directory_packages(repository):
224+
package = get_package_from_repository("editable-directory-pep-610", repository)
225+
226+
assert package is not None
227+
assert package.name == "editable-directory-pep-610"
228+
assert package.version.text == "1.2.3"
229+
assert package.source_type == "directory"
230+
assert package.source_url == "/path/to/distributions/directory-pep-610"
231+
assert package.develop

0 commit comments

Comments
 (0)