Commit b7aa9c2a by Arun Babu Neelicattu

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.
parent 658f6410
import itertools
from typing import Set from typing import Set
from typing import Union
from poetry.core.packages import Package 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 Path
from poetry.utils._compat import metadata from poetry.utils._compat import metadata
from poetry.utils.env import Env from poetry.utils.env import Env
...@@ -11,11 +15,17 @@ from .repository import Repository ...@@ -11,11 +15,17 @@ from .repository import Repository
_VENDORS = Path(__file__).parent.parent.joinpath("_vendor") _VENDORS = Path(__file__).parent.parent.joinpath("_vendor")
try:
FileNotFoundError
except NameError:
FileNotFoundError = OSError
class InstalledRepository(Repository): class InstalledRepository(Repository):
@classmethod @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 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 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 to a directory rather than a file, however, in order to maintain backwards
...@@ -24,26 +34,73 @@ class InstalledRepository(Repository): ...@@ -24,26 +34,73 @@ class InstalledRepository(Repository):
Reference: https://docs.python.org/3.8/library/site.html 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. :param name: The name of the package to search .pth file for.
:return: A `Set` of valid `Path` objects. :return: A `Set` of valid `Path` objects.
""" """
paths = set() paths = set()
pth_file = sitedir.joinpath("{}.pth".format(name)) # we identify the candidate pth files to check, this is done so to handle cases
if pth_file.exists(): # 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: with pth_file.open() as f:
for line in f: for line in f:
line = line.strip() line = line.strip()
if line and not line.startswith(("#", "import ", "import\t")): if line and not line.startswith(("#", "import ", "import\t")):
path = Path(line) path = Path(line)
if not path.is_absolute(): 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) paths.add(path)
return paths return paths
@classmethod @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 def load(cls, env): # type: (Env) -> InstalledRepository
""" """
Load installed packages. Load installed packages.
...@@ -79,33 +136,22 @@ class InstalledRepository(Repository): ...@@ -79,33 +136,22 @@ class InstalledRepository(Repository):
if is_standard_package: if is_standard_package:
if path.name.endswith(".dist-info"): if path.name.endswith(".dist-info"):
paths = cls.get_package_paths( paths = cls.get_package_paths(env=env, name=package.pretty_name)
sitedir=env.site_packages, name=package.pretty_name
)
if paths: if paths:
# TODO: handle multiple source directories? for src in paths:
package.source_type = "directory" if cls.is_vcs_package(src, env):
package.source_url = paths.pop().as_posix() 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 continue
src_path = env.path / "src" if cls.is_vcs_package(path, env):
cls.set_package_vcs_properties(package, env)
# A VCS dependency should have been installed else:
# in the src directory. If not, it's a path dependency # 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:
package.source_type = "directory" package.source_type = "directory"
package.source_url = str(path.parent) package.source_url = str(path.parent)
......
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
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
...@@ -28,6 +28,7 @@ INSTALLED_RESULTS = [ ...@@ -28,6 +28,7 @@ INSTALLED_RESULTS = [
metadata.PathDistribution(SITE_PURELIB / "editable-2.3.4.dist-info"), 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_PURELIB / "editable-with-import-2.3.4.dist-info"),
metadata.PathDistribution(SITE_PLATLIB / "lib64-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"),
] ]
...@@ -110,10 +111,21 @@ def test_load_git_package(repository): ...@@ -110,10 +111,21 @@ def test_load_git_package(repository):
assert pendulum.version.text == "2.0.5" assert pendulum.version.text == "2.0.5"
assert pendulum.description == "Python datetimes made easy" assert pendulum.description == "Python datetimes made easy"
assert pendulum.source_type == "git" 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" 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): def test_load_platlib_package(repository):
lib64 = get_package_from_repository("lib64", repository) lib64 = get_package_from_repository("lib64", repository)
assert lib64 is not None assert lib64 is not None
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment