Skip to content

Commit

Permalink
installed repository: handle executable .pth files
Browse files Browse the repository at this point in the history
Resolves: #2597
  • Loading branch information
PetterS authored and abn committed Jun 30, 2020
1 parent 687a897 commit b827c2a
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 19 deletions.
52 changes: 40 additions & 12 deletions poetry/repositories/installed_repository.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Set

from poetry.core.packages import Package
from poetry.utils._compat import Path
from poetry.utils._compat import metadata
Expand All @@ -10,6 +12,37 @@


class InstalledRepository(Repository):
@classmethod
def get_package_paths(cls, sitedir, name): # type: (Path, str) -> Set[Path]
"""
Process a .pth file within the site-packages directory, and return any valid
paths. We skip executable .pth files as there is no reliable means to do this
without side-effects to current run-time. Mo check is made that the item refers
to a directory rather than a file, however, in order to maintain backwards
compatibility, we allow non-existing paths to be discovered. The latter
behaviour is different to how Python's site-specific hook configuration works.
Reference: https://docs.python.org/3.8/library/site.html
:param sitedir: The site-packages directory to search for .pth file.
:param name: The name of the package to search .pth file for.
:return: A `Set` of valid `Path` objects.
"""
paths = set()

pth_file = sitedir.joinpath("{}.pth".format(name))
if pth_file.exists():
with pth_file.open() as f:
for line in f:
line = line.strip()
if line and not line.startswith(("#", "import ", "import\t")):
path = Path(line)
if not path.is_absolute():
path = sitedir.joinpath(path)
paths.add(path)

return paths

@classmethod
def load(cls, env): # type: (Env) -> InstalledRepository
"""
Expand Down Expand Up @@ -49,19 +82,14 @@ def load(cls, env): # type: (Env) -> InstalledRepository
is_standard_package = False

if is_standard_package:
if (
path.name.endswith(".dist-info")
and env.site_packages.joinpath(
"{}.pth".format(package.pretty_name)
).exists()
):
with env.site_packages.joinpath(
"{}.pth".format(package.pretty_name)
).open() as f:
directory = Path(f.readline().strip())
if path.name.endswith(".dist-info"):
paths = cls.get_package_paths(
sitedir=env.site_packages, name=package.pretty_name
)
if paths:
# TODO: handle multiple source directories?
package.source_type = "directory"
package.source_url = directory.as_posix()

package.source_url = paths.pop().as_posix()
continue

src_path = env.path / "src"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Metadata-Version: 2.1
Name: editable-with-import
Version: 2.3.4
Summary: Editable description.
License: MIT
Keywords: cli,commands
Author: Foo Bar
Author-email: foo@bar.com
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Description-Content-Type: text/x-rst

Editable
####
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import os
23 changes: 16 additions & 7 deletions tests/repositories/test_installed_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
),
metadata.PathDistribution(VENDOR_DIR / "attrs-19.3.0.dist-info"),
metadata.PathDistribution(SITE_PACKAGES / "editable-2.3.4.dist-info"),
metadata.PathDistribution(SITE_PACKAGES / "editable-with-import-2.3.4.dist-info"),
]


Expand Down Expand Up @@ -46,7 +47,7 @@ def test_load(mocker):
mocker.patch("poetry.repositories.installed_repository._VENDORS", str(VENDOR_DIR))
repository = InstalledRepository.load(MockEnv(path=ENV_DIR))

assert len(repository.packages) == 4
assert len(repository.packages) == 5

cleo = repository.packages[0]
assert cleo.name == "cleo"
Expand All @@ -56,11 +57,11 @@ def test_load(mocker):
== "Cleo allows you to create beautiful and testable command-line interfaces."
)

foo = repository.packages[2]
foo = repository.packages[3]
assert foo.name == "foo"
assert foo.version.text == "0.1.0"

pendulum = repository.packages[3]
pendulum = repository.packages[4]
assert pendulum.name == "pendulum"
assert pendulum.version.text == "2.0.5"
assert pendulum.description == "Python datetimes made easy"
Expand All @@ -71,8 +72,16 @@ def test_load(mocker):
for pkg in repository.packages:
assert pkg.name != "attrs"

# test editable package with text .pth file
editable = repository.packages[1]
assert "editable" == editable.name
assert "2.3.4" == editable.version.text
assert "directory" == editable.source_type
assert "/path/to/editable" == editable.source_url
assert editable.name == "editable"
assert editable.version.text == "2.3.4"
assert editable.source_type == "directory"
assert editable.source_url == Path("/path/to/editable").as_posix()

# test editable package with executable .pth file
editable = repository.packages[2]
assert editable.name == "editable-with-import"
assert editable.version.text == "2.3.4"
assert editable.source_type == ""
assert editable.source_url == ""

0 comments on commit b827c2a

Please sign in to comment.