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

Improve detection of installed packages in an environment #2722

Merged
merged 3 commits into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Fix git installed package detection
A package installed from git is sometimes mistaken as a standard
package since it has a `.dist-info` directory. This change ensures
that the pth file sources are also taken into consideration when
determining if a package is git sourced.

Additionally, this change ensures that a pth file is searched for in
both platlib and purelib site directories.
  • Loading branch information
abn committed Jul 30, 2020
commit b7aa9c2ab444bacd544b1872c3a0740dc064e8e9
108 changes: 77 additions & 31 deletions poetry/repositories/installed_repository.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import itertools

from typing import Set
from typing import Union

from poetry.core.packages import Package
from poetry.core.utils.helpers import module_name
from poetry.utils._compat import Path
from poetry.utils._compat import metadata
from poetry.utils.env import Env
Expand All @@ -11,11 +15,17 @@
_VENDORS = Path(__file__).parent.parent.joinpath("_vendor")


try:
FileNotFoundError
except NameError:
FileNotFoundError = OSError


class InstalledRepository(Repository):
@classmethod
def get_package_paths(cls, sitedir, name): # type: (Path, str) -> Set[Path]
def get_package_paths(cls, env, name): # type: (Env, str) -> Set[Path]
"""
Process a .pth file within the site-packages directory, and return any valid
Process a .pth file within the site-packages directories, 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
Expand All @@ -24,25 +34,72 @@ def get_package_paths(cls, sitedir, name): # type: (Path, str) -> Set[Path]

Reference: https://docs.python.org/3.8/library/site.html

:param sitedir: The site-packages directory to search for .pth file.
:param env: The environment to search for the .pth file in.
: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():
# we identify the candidate pth files to check, this is done so to handle cases
# where the pth file for foo-bar might have been installed as either foo-bar.pth or
# foo_bar.pth (expected) in either pure or platform lib directories.
candidates = itertools.product(
{env.purelib, env.platlib}, {name, module_name(name)},
)

for lib, module in candidates:
pth_file = lib.joinpath(module).with_suffix(".pth")
if not pth_file.exists():
continue

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)
try:
path = lib.joinpath(path).resolve()
except FileNotFoundError:
# this is required to handle pathlib oddity on win32 python==3.5
path = lib.joinpath(path)
paths.add(path)

return paths

@classmethod
def set_package_vcs_properties_from_path(
cls, src, package
): # type: (Path, Package) -> None
from poetry.core.vcs.git import Git

git = Git()
revision = git.rev_parse("HEAD", src).strip()
url = git.remote_url(src)

package.source_type = "git"
package.source_url = url
package.source_reference = revision

@classmethod
def set_package_vcs_properties(cls, package, env): # type: (Package, Env) -> None
src = env.path / "src" / package.name
cls.set_package_vcs_properties_from_path(src, package)

@classmethod
def is_vcs_package(cls, package, env): # type: (Union[Path, Package], Env) -> bool
# A VCS dependency should have been installed
# in the src directory.
src = env.path / "src"
if isinstance(package, Package):
return src.joinpath(package.name).is_dir()

try:
package.relative_to(env.path / "src")
except ValueError:
return False
else:
return True

@classmethod
def load(cls, env): # type: (Env) -> InstalledRepository
"""
Expand Down Expand Up @@ -79,33 +136,22 @@ def load(cls, env): # type: (Env) -> InstalledRepository

if is_standard_package:
if path.name.endswith(".dist-info"):
paths = cls.get_package_paths(
sitedir=env.site_packages, name=package.pretty_name
)
paths = cls.get_package_paths(env=env, name=package.pretty_name)
if paths:
# TODO: handle multiple source directories?
package.source_type = "directory"
package.source_url = paths.pop().as_posix()

for src in paths:
if cls.is_vcs_package(src, env):
cls.set_package_vcs_properties(package, env)
break
else:
# TODO: handle multiple source directories?
package.source_type = "directory"
package.source_url = paths.pop().as_posix()
continue

src_path = env.path / "src"

# A VCS dependency should have been installed
# in the src directory. If not, it's a path dependency
try:
path.relative_to(src_path)

from poetry.core.vcs.git import Git

git = Git()
revision = git.rev_parse("HEAD", src_path / package.name).strip()
url = git.remote_url(src_path / package.name)

package.source_type = "git"
package.source_url = url
package.source_reference = revision
except ValueError:
if cls.is_vcs_package(path, env):
cls.set_package_vcs_properties(package, env)
else:
# If not, it's a path dependency
package.source_type = "directory"
package.source_url = str(path.parent)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Metadata-Version: 2.1
Name: bender
Version: 2.0.5
Summary: Python datetimes made easy
License: MIT
Keywords: cli,commands
Author: Leela
Author-email: leela@planetexpress.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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
../../../src/bender
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Metadata-Version: 2.1
Name: bender
Version: 2.0.5
Summary: Python datetimes made easy
License: MIT
Keywords: cli,commands
Author: Leela
Author-email: leela@planetexpress.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
14 changes: 13 additions & 1 deletion tests/repositories/test_installed_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
metadata.PathDistribution(SITE_PURELIB / "editable-2.3.4.dist-info"),
metadata.PathDistribution(SITE_PURELIB / "editable-with-import-2.3.4.dist-info"),
metadata.PathDistribution(SITE_PLATLIB / "lib64-2.3.4.dist-info"),
metadata.PathDistribution(SITE_PLATLIB / "bender-2.0.5.dist-info"),
]


Expand Down Expand Up @@ -110,10 +111,21 @@ def test_load_git_package(repository):
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_url in [
"git@github.com:sdispater/pendulum.git",
"https://github.com/sdispater/pendulum.git",
]
assert pendulum.source_reference == "bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6"


def test_load_git_package_pth(repository):
bender = get_package_from_repository("bender", repository)
assert bender is not None
assert bender.name == "bender"
assert bender.version.text == "2.0.5"
assert bender.source_type == "git"


def test_load_platlib_package(repository):
lib64 = get_package_from_repository("lib64", repository)
assert lib64 is not None
Expand Down