Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #2597 by checking that the directory exists #2602

Merged
merged 2 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@

abn marked this conversation as resolved.
Show resolved Hide resolved

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:
abn marked this conversation as resolved.
Show resolved Hide resolved
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: [email protected]
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
59 changes: 48 additions & 11 deletions tests/repositories/test_installed_repository.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import pytest

from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils._compat import PY36
from poetry.utils._compat import Path
from poetry.utils._compat import metadata
from poetry.utils._compat import zipp
from poetry.utils.env import MockEnv as BaseMockEnv
from pytest_mock.plugin import MockFixture


FIXTURES_DIR = Path(__file__).parent / "fixtures"
Expand All @@ -18,6 +22,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 All @@ -27,7 +32,13 @@ def site_packages(self): # type: () -> Path
return SITE_PACKAGES


def test_load(mocker):
@pytest.fixture
def env(): # type: () -> MockEnv
return MockEnv(path=ENV_DIR)


@pytest.fixture
def repository(mocker, env): # type: (MockFixture, MockEnv) -> InstalledRepository
mocker.patch(
"poetry.utils._compat.metadata.Distribution.discover",
return_value=INSTALLED_RESULTS,
Expand All @@ -44,10 +55,19 @@ def test_load(mocker):
],
)
mocker.patch("poetry.repositories.installed_repository._VENDORS", str(VENDOR_DIR))
repository = InstalledRepository.load(MockEnv(path=ENV_DIR))
return InstalledRepository.load(env)


def test_load_successful(repository):
assert len(repository.packages) == 5


def test_load_ensure_isolation(repository):
for pkg in repository.packages:
assert pkg.name != "attrs"

assert len(repository.packages) == 4

def test_load_standard_package(repository):
cleo = repository.packages[0]
assert cleo.name == "cleo"
assert cleo.version.text == "0.7.6"
Expand All @@ -56,23 +76,40 @@ 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]

def test_load_git_package(repository):
pendulum = repository.packages[4]
assert pendulum.name == "pendulum"
assert pendulum.version.text == "2.0.5"
assert pendulum.description == "Python datetimes made easy"
assert pendulum.source_type == "git"
assert pendulum.source_url == "https://github.com/sdispater/pendulum.git"
assert pendulum.source_reference == "bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6"

for pkg in repository.packages:
assert pkg.name != "attrs"

@pytest.mark.skipif(
not PY36, reason="pathlib.resolve() does not support strict argument"
)
def test_load_editable_package(repository):
# 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").resolve(strict=False).as_posix()
)


def test_load_editable_with_import_package(repository):
# 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 == ""