diff --git a/pipenv/utils.py b/pipenv/utils.py index 265c5797..2f48905a 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -1142,8 +1142,7 @@ def normalize_path(path): def get_url_name(url): if not isinstance(url, six.string_types): return - from urllib3.util import parse as urllib3_parse - return urllib3_parse(url).host + return urllib3_util.parse_url(url).host def get_canonical_names(packages): diff --git a/pipenv/vendor/pythonfinder/models/asdf.py b/pipenv/vendor/pythonfinder/models/asdf.py deleted file mode 100644 index 3ba6e4fa..00000000 --- a/pipenv/vendor/pythonfinder/models/asdf.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding=utf-8 -*- -import attr - -from .pyenv import PyenvFinder - - -@attr.s -class AsdfFinder(PyenvFinder): - version_root = attr.ib(default="installs/python/*") diff --git a/pipenv/vendor/pythonfinder/models/path.py b/pipenv/vendor/pythonfinder/models/path.py index d3cdd9d1..53c2360c 100644 --- a/pipenv/vendor/pythonfinder/models/path.py +++ b/pipenv/vendor/pythonfinder/models/path.py @@ -131,7 +131,8 @@ class SystemPath(object): ) def _get_last_instance(self, path): - paths = [normalize_path(p) for p in reversed(self.path_order)] + reversed_paths = reversed(self.path_order) + paths = [normalize_path(p) for p in reversed_paths] normalized_target = normalize_path(path) last_instance = next( iter(p for p in paths if normalized_target in p), None @@ -150,7 +151,7 @@ class SystemPath(object): ) def _remove_path(self, path): - path_copy = reversed(self.path_order[:]) + path_copy = [p for p in reversed(self.path_order[:])] new_order = [] target = normalize_path(path) path_map = { @@ -163,22 +164,23 @@ class SystemPath(object): normalized = normalize_path(current_path) if normalized != target: new_order.append(normalized) - new_order = reversed(new_order) + new_order = [p for p in reversed(new_order)] self.path_order = new_order def _setup_asdf(self): from .python import PythonFinder + self.asdf_finder = PythonFinder.create( + root=ASDF_DATA_DIR, ignore_unsupported=True, + sort_function=parse_asdf_version_order, version_glob_path="installs/python/*") asdf_index = self._get_last_instance(ASDF_DATA_DIR) if not asdf_index: # we are in a virtualenv without global pyenv on the path, so we should # not write pyenv to the path here return - self.asdf_finder = PythonFinder.create( - root=ASDF_DATA_DIR, ignore_unsupported=True, - sort_function=parse_asdf_version_order, version_glob_path="installs/python/*") root_paths = [p for p in self.asdf_finder.roots] self._slice_in_paths(asdf_index, root_paths) self.paths.update(self.asdf_finder.roots) + self._remove_path(normalize_path(os.path.join(ASDF_DATA_DIR, "shims"))) self._register_finder("asdf", self.asdf_finder) def _setup_pyenv(self): @@ -452,7 +454,7 @@ class PathEntry(BasePath): only_python = attr.ib(default=False) name = attr.ib() py_version = attr.ib() - pythons = attr.ib() + _pythons = attr.ib(default=attr.Factory(defaultdict)) def __str__(self): return fs_str("{0}".format(self.path.as_posix())) @@ -506,19 +508,19 @@ class PathEntry(BasePath): return py_version return - @pythons.default - def get_pythons(self): - pythons = defaultdict() - if self.is_dir: - for path, entry in self.children.items(): - _path = ensure_path(entry.path) - if entry.is_python: - pythons[_path.as_posix()] = entry - else: - if self.is_python: - _path = ensure_path(self.path) - pythons[_path.as_posix()] = self - return pythons + @property + def pythons(self): + if not self._pythons: + if self.is_dir: + for path, entry in self.children.items(): + _path = ensure_path(entry.path) + if entry.is_python: + self._pythons[_path.as_posix()] = entry + else: + if self.is_python: + _path = ensure_path(self.path) + self._pythons[_path.as_posix()] = self + return self._pythons @cached_property def as_python(self): diff --git a/pipenv/vendor/pythonfinder/models/pyenv.py b/pipenv/vendor/pythonfinder/models/pyenv.py deleted file mode 100644 index 6f2d6422..00000000 --- a/pipenv/vendor/pythonfinder/models/pyenv.py +++ /dev/null @@ -1,237 +0,0 @@ -# -*- coding=utf-8 -*- -from __future__ import absolute_import, print_function - -import logging -import operator - -from collections import defaultdict - -import attr -import sysconfig - -from vistir.compat import Path - -from ..utils import ( - ensure_path, - optional_instance_of, - unnest, -) -from .mixins import BaseFinder, BasePath -from .path import SystemPath, PathEntry -from .python import PythonVersion - - -logger = logging.getLogger(__name__) - - -@attr.s(slots=True) -class PyenvFinder(BaseFinder, BasePath): - root = attr.ib(default=None, validator=optional_instance_of(Path)) - #: ignore_unsupported should come before versions, because its value is used - #: in versions's default initializer. - ignore_unsupported = attr.ib(default=True) - paths = attr.ib(default=attr.Factory(list)) - roots = attr.ib(default=attr.Factory(defaultdict)) - version_root = attr.ib(default="versions/*") - versions = attr.ib() - pythons = attr.ib() - - @property - def expanded_paths(self): - return ( - path for path in unnest(p for p in self.versions.values()) - if path is not None - ) - - def get_version_order(self): - version_order_file, version_order_lines = self.root.joinpath("version"), [] - if version_order_file.exists(): - version_order_lines = version_order_file.read_text(encoding="utf-8").splitlines() - - version_paths = [ - p for p in self.root.glob(self.version_root) - if not (p.parent.name == "envs" or p.name == "envs") - ] - versions = {v.name: v for v in version_paths} - version_order = [versions[v] for v in version_order_lines if v in versions] - for version in version_order: - version_paths.remove(version) - version_order += version_paths - return version_order - - @classmethod - def version_from_bin_dir(cls, base_dir, name=None): - py_version = None - version_path = PathEntry.create( - path=base_dir.absolute().as_posix(), - only_python=True, - name=base_dir.parent.name, - ) - py_version = next(iter(version_path.find_all_python_versions()), None) - return py_version - - @versions.default - def get_versions(self): - versions = defaultdict() - bin_ = "{base}/bin" - for p in self.get_version_order(): - bin_dir = Path(bin_.format(base=p.as_posix())) - version_path = None - if bin_dir.exists(): - version_path = PathEntry.create( - path=bin_dir.absolute().as_posix(), - only_python=False, - name=p.name, - is_root=True, - ) - version = None - try: - version = PythonVersion.parse(p.name) - except ValueError: - entry = next(iter(version_path.find_all_python_versions()), None) - if not entry: - if self.ignore_unsupported: - continue - raise - else: - version = entry.py_version.as_dict() - except Exception: - if not self.ignore_unsupported: - raise - logger.warning( - "Unsupported Python version %r, ignoring...", p.name, exc_info=True - ) - continue - if not version: - continue - version_tuple = ( - version.get("major"), - version.get("minor"), - version.get("patch"), - version.get("is_prerelease"), - version.get("is_devrelease"), - version.get("is_debug"), - ) - self.roots[p] = version_path - versions[version_tuple] = version_path - self.paths.append(version_path) - return versions - - @pythons.default - def get_pythons(self): - pythons = defaultdict() - for p in self.paths: - pythons.update(p.pythons) - return pythons - - @classmethod - def create(cls, root, ignore_unsupported=True): - root = ensure_path(root) - return cls(root=root, ignore_unsupported=ignore_unsupported) - - def find_all_python_versions( - self, - major=None, - minor=None, - patch=None, - pre=None, - dev=None, - arch=None, - name=None, - ): - """Search for a specific python version on the path. Return all copies - - :param major: Major python version to search for. - :type major: int - :param int minor: Minor python version to search for, defaults to None - :param int patch: Patch python version to search for, defaults to None - :param bool pre: Search for prereleases (default None) - prioritize releases if None - :param bool dev: Search for devreleases (default None) - prioritize releases if None - :param str arch: Architecture to include, e.g. '64bit', defaults to None - :param str name: The name of a python version, e.g. ``anaconda3-5.3.0`` - :return: A list of :class:`~pythonfinder.models.PathEntry` instances matching the version requested. - :rtype: List[:class:`~pythonfinder.models.PathEntry`] - """ - - version_matcher = operator.methodcaller( - "matches", - major=major, - minor=minor, - patch=patch, - pre=pre, - dev=dev, - arch=arch, - name=name, - ) - py = operator.attrgetter("as_python") - pythons = ( - py_ver for py_ver in (py(p) for p in self.pythons.values() if p is not None) - if py_ver is not None - ) - # pythons = filter(None, [p.as_python for p in self.pythons.values()]) - matching_versions = filter(lambda py: version_matcher(py), pythons) - version_sort = operator.attrgetter("version_sort") - return sorted(matching_versions, key=version_sort, reverse=True) - - def find_python_version( - self, - major=None, - minor=None, - patch=None, - pre=None, - dev=None, - arch=None, - name=None, - ): - """Search or self for the specified Python version and return the first match. - - :param major: Major version number. - :type major: int - :param int minor: Minor python version to search for, defaults to None - :param int patch: Patch python version to search for, defaults to None - :param bool pre: Search for prereleases (default None) - prioritize releases if None - :param bool dev: Search for devreleases (default None) - prioritize releases if None - :param str arch: Architecture to include, e.g. '64bit', defaults to None - :param str name: The name of a python version, e.g. ``anaconda3-5.3.0`` - :returns: A :class:`~pythonfinder.models.PathEntry` instance matching the version requested. - """ - - version_matcher = operator.methodcaller( - "matches", - major=major, - minor=minor, - patch=patch, - pre=pre, - dev=dev, - arch=arch, - name=name, - ) - pythons = filter(None, [p.as_python for p in self.pythons.values()]) - matching_versions = filter(lambda py: version_matcher(py), pythons) - version_sort = operator.attrgetter("version_sort") - return next(iter(c for c in sorted(matching_versions, key=version_sort, reverse=True)), None) - - -@attr.s -class VersionPath(SystemPath): - base = attr.ib(default=None, validator=optional_instance_of(Path)) - name = attr.ib(default=None) - - @classmethod - def create(cls, path, only_python=True, pythons=None, name=None): - """Accepts a path to a base python version directory. - - Generates the pyenv version listings for it""" - path = ensure_path(path) - path_entries = defaultdict(PathEntry) - bin_ = sysconfig._INSTALL_SCHEMES[sysconfig._get_default_scheme()]["scripts"] - if path.as_posix().endswith(Path(bin_).name): - path = path.parent - bin_dir = ensure_path(bin_.format(base=path.as_posix())) - if not name: - name = path.name - current_entry = PathEntry.create( - bin_dir, is_root=True, only_python=True, pythons=pythons, name=name - ) - path_entries[bin_dir.as_posix()] = current_entry - return cls(name=name, base=bin_dir, paths=path_entries) diff --git a/pipenv/vendor/pythonfinder/utils.py b/pipenv/vendor/pythonfinder/utils.py index 619fadef..a26d0548 100644 --- a/pipenv/vendor/pythonfinder/utils.py +++ b/pipenv/vendor/pythonfinder/utils.py @@ -12,7 +12,7 @@ import six import vistir -from .environment import PYENV_INSTALLED, PYENV_ROOT, ASDF_INSTALLED, ASDF_DATA_DIR +from .environment import PYENV_ROOT from .exceptions import InvalidPythonVersion try: @@ -90,7 +90,7 @@ def looks_like_python(name): @lru_cache(maxsize=1024) def path_is_python(path): - return path_is_known_executable(path) and looks_like_python(path.name) + return path_is_executable(path) and looks_like_python(path.name) @lru_cache(maxsize=1024) @@ -116,7 +116,9 @@ def _filter_none(k, v): def normalize_path(path): - return os.path.normpath(os.path.normcase(os.path.abspath(str(path)))) + return os.path.normpath(os.path.normcase( + os.path.abspath(os.path.expandvars(os.path.expanduser(str(path)))) + )) @lru_cache(maxsize=1024) @@ -161,7 +163,7 @@ def parse_asdf_version_order(filename=".tool-versions"): line for line in contents.splitlines() if line.startswith("python") ), None) if python_section: - python_key, versions = python_section.partition() + python_key, _, versions = python_section.partition(" ") if versions: return versions.split()