Skip to content

Commit

Permalink
Update pythonfinder
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Ryan <[email protected]>
  • Loading branch information
techalchemy committed Jul 25, 2018
1 parent f5df34f commit ef060c4
Show file tree
Hide file tree
Showing 14 changed files with 163 additions and 88 deletions.
1 change: 1 addition & 0 deletions news/2582.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed multiple issues with finding the correct system python locations.
4 changes: 4 additions & 0 deletions news/2582.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Greatly enhanced python discovery functionality:

- Added pep514 (windows launcher/finder) support for python discovery.
- Introduced architecture discovery for python installations which support different architectures.
1 change: 1 addition & 0 deletions news/2582.vendor
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update ``pythonfinder`` to major release ``1.0.0`` for integration.
2 changes: 1 addition & 1 deletion pipenv/vendor/pythonfinder/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import print_function, absolute_import

__version__ = "0.1.4.dev0"
__version__ = "1.0.0"

__all__ = ["Finder", "WindowsFinder", "SystemPath", "InvalidPythonVersion"]
from .pythonfinder import Finder
Expand Down
14 changes: 0 additions & 14 deletions pipenv/vendor/pythonfinder/_vendor/Makefile

This file was deleted.

1 change: 0 additions & 1 deletion pipenv/vendor/pythonfinder/_vendor/vendor.txt

This file was deleted.

1 change: 1 addition & 0 deletions pipenv/vendor/pythonfinder/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@

class InvalidPythonVersion(Exception):
"""Raised when parsing an invalid python version"""

pass
43 changes: 36 additions & 7 deletions pipenv/vendor/pythonfinder/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,19 @@ def which(self, name):
for ext in KNOWN_EXTS
]
children = self.children
found = next((children[(self.path / child).as_posix()] for child in valid_names if (self.path / child).as_posix() in children), None)
found = next(
(
children[(self.path / child).as_posix()]
for child in valid_names
if (self.path / child).as_posix() in children
),
None,
)
return found

def find_all_python_versions(self, major=None, minor=None, patch=None, pre=None, dev=None, arch=None):
def find_all_python_versions(
self, major=None, minor=None, patch=None, pre=None, dev=None, arch=None
):
"""Search for a specific python version on the path. Return all copies
:param major: Major python version to search for.
Expand All @@ -57,7 +66,9 @@ def find_all_python_versions(self, major=None, minor=None, patch=None, pre=None,
:rtype: List[:class:`~pythonfinder.models.PathEntry`]
"""

call_method = "find_all_python_versions" if self.is_dir else "find_python_version"
call_method = (
"find_all_python_versions" if self.is_dir else "find_python_version"
)
sub_finder = operator.methodcaller(
call_method, major, minor=minor, patch=patch, pre=pre, dev=dev, arch=arch
)
Expand All @@ -67,7 +78,9 @@ def find_all_python_versions(self, major=None, minor=None, patch=None, pre=None,
version_sort = operator.attrgetter("as_python.version_sort")
return [c for c in sorted(path_filter, key=version_sort, reverse=True)]

def find_python_version(self, major=None, minor=None, patch=None, pre=None, dev=None, arch=None):
def find_python_version(
self, major=None, minor=None, patch=None, pre=None, dev=None, arch=None
):
"""Search or self for the specified Python version and return the first match.
:param major: Major version number.
Expand All @@ -81,21 +94,37 @@ def find_python_version(self, major=None, minor=None, patch=None, pre=None, dev=
"""

version_matcher = operator.methodcaller(
"matches", major=major, minor=minor, patch=patch, pre=pre, dev=dev, arch=arch
"matches",
major=major,
minor=minor,
patch=patch,
pre=pre,
dev=dev,
arch=arch,
)
is_py = operator.attrgetter("is_python")
py_version = operator.attrgetter("as_python")
if not self.is_dir:
if self.is_python and self.as_python and version_matcher(self.as_python):
return self
return
finder = ((child, child.as_python) for child in unnest(self.pythons.values()) if child.as_python)
finder = (
(child, child.as_python)
for child in unnest(self.pythons.values())
if child.as_python
)
py_filter = filter(
None, filter(lambda child: version_matcher(child[1]), finder)
)
version_sort = operator.attrgetter("version_sort")
return next(
(c[0] for c in sorted(py_filter, key=lambda child: child[1].version_sort, reverse=True)), None
(
c[0]
for c in sorted(
py_filter, key=lambda child: child[1].version_sort, reverse=True
)
),
None,
)


Expand Down
73 changes: 48 additions & 25 deletions pipenv/vendor/pythonfinder/models/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ def _register_finder(self, finder_name, finder):

@cached_property
def executables(self):
self.executables = [p for p in chain(*(child.children.values() for child in self.paths.values())) if p.is_executable]
self.executables = [
p
for p in chain(*(child.children.values() for child in self.paths.values()))
if p.is_executable
]
return self.executables

@cached_property
Expand All @@ -69,7 +73,7 @@ def version_dict(self):
self._version_dict = defaultdict(list)
for finder_name, finder in self.__finders.items():
for version, entry in finder.versions.items():
if finder_name == 'windows':
if finder_name == "windows":
if entry not in self._version_dict[version]:
self._version_dict[version].append(entry)
continue
Expand Down Expand Up @@ -98,17 +102,15 @@ def __attrs_post_init__(self):
self._setup_windows()
if PYENV_INSTALLED:
self._setup_pyenv()
venv = os.environ.get('VIRTUAL_ENV')
if os.name == 'nt':
bin_dir = 'Scripts'
venv = os.environ.get("VIRTUAL_ENV")
if os.name == "nt":
bin_dir = "Scripts"
else:
bin_dir = 'bin'
bin_dir = "bin"
if venv and (self.system or self.global_search):
p = ensure_path(venv)
self.path_order = [(p / bin_dir).as_posix()] + self.path_order
self.paths[p] = PathEntry.create(
path=p, is_root=True, only_python=False
)
self.paths[p] = PathEntry.create(path=p, is_root=True, only_python=False)
if self.system:
syspath = Path(sys.executable)
syspath_bin = syspath.parent
Expand Down Expand Up @@ -141,7 +143,7 @@ def _setup_pyenv(self):
before_path + [p.path.as_posix() for p in root_paths] + after_path
)
self.paths.update({p.path: p for p in root_paths})
self._register_finder('pyenv', self.pyenv_finder)
self._register_finder("pyenv", self.pyenv_finder)

def _setup_windows(self):
from .windows import WindowsFinder
Expand All @@ -151,13 +153,13 @@ def _setup_windows(self):
path_addition = [p.path.as_posix() for p in root_paths]
self.path_order = self.path_order[:] + path_addition
self.paths.update({p.path: p for p in root_paths})
self._register_finder('windows', self.windows_finder)
self._register_finder("windows", self.windows_finder)

def get_path(self, path):
path = ensure_path(path)
_path = self.paths.get(path.as_posix())
if not _path and path.as_posix() in self.path_order:
_path = PathEntry.create(
_path = PathEntry.create(
path=path.absolute(), is_root=True, only_python=self.only_python
)
self.paths[path.as_posix()] = _path
Expand Down Expand Up @@ -185,7 +187,9 @@ def which(self, executable):
filtered = filter(None, (sub_which(self.get_path(k)) for k in self.path_order))
return next((f for f in filtered), None)

def find_all_python_versions(self, major=None, minor=None, patch=None, pre=None, dev=None, arch=None):
def find_all_python_versions(
self, major=None, minor=None, patch=None, pre=None, dev=None, arch=None
):
"""Search for a specific python version on the path. Return all copies
:param major: Major python version to search for.
Expand All @@ -200,18 +204,28 @@ def find_all_python_versions(self, major=None, minor=None, patch=None, pre=None,
"""

sub_finder = operator.methodcaller(
"find_all_python_versions", major, minor=minor, patch=patch, pre=pre, dev=dev, arch=arch
"find_all_python_versions",
major,
minor=minor,
patch=patch,
pre=pre,
dev=dev,
arch=arch,
)
if os.name == "nt" and self.windows_finder:
windows_finder_version = sub_finder(self.windows_finder)
if windows_finder_version:
return windows_finder_version
paths = (self.get_path(k) for k in self.path_order)
path_filter = filter(None, unnest((sub_finder(p) for p in paths if p is not None)))
path_filter = filter(
None, unnest((sub_finder(p) for p in paths if p is not None))
)
version_sort = operator.attrgetter("as_python.version_sort")
return [c for c in sorted(path_filter, key=version_sort, reverse=True)]

def find_python_version(self, major=None, minor=None, patch=None, pre=None, dev=None, arch=None):
def find_python_version(
self, major=None, minor=None, patch=None, pre=None, dev=None, arch=None
):
"""Search for a specific python version on the path.
:param major: Major python version to search for.
Expand All @@ -226,7 +240,13 @@ def find_python_version(self, major=None, minor=None, patch=None, pre=None, dev=
"""

sub_finder = operator.methodcaller(
"find_python_version", major, minor=minor, patch=patch, pre=pre, dev=dev, arch=arch
"find_python_version",
major,
minor=minor,
patch=patch,
pre=pre,
dev=dev,
arch=arch,
)
if major and minor and patch:
_tuple_pre = pre if pre is not None else False
Expand All @@ -247,7 +267,7 @@ def find_python_version(self, major=None, minor=None, patch=None, pre=None, dev=
if ver.as_python.version_tuple[:5] in self.python_version_dict:
self.python_version_dict[ver.as_python.version_tuple[:5]].append(ver)
else:
self.python_version_dict[ver.as_python.version_tuple[:5]] = [ver,]
self.python_version_dict[ver.as_python.version_tuple[:5]] = [ver]
return ver

@classmethod
Expand Down Expand Up @@ -280,7 +300,13 @@ def create(cls, path=None, system=False, only_python=False, global_search=True):
for p in _path_objects
}
)
return cls(paths=path_entries, path_order=paths, only_python=only_python, system=system, global_search=global_search)
return cls(
paths=path_entries,
path_order=paths,
only_python=only_python,
system=system,
global_search=global_search,
)


@attr.s
Expand All @@ -293,7 +319,7 @@ class PathEntry(BasePath):
pythons = attr.ib()

def __str__(self):
return fs_str('{0}'.format(self.path.as_posix()))
return fs_str("{0}".format(self.path.as_posix()))

def _filter_children(self):
if self.only_python:
Expand Down Expand Up @@ -333,6 +359,7 @@ def as_python(self):
if not self.py_version:
try:
from .python import PythonVersion

self.py_version = PythonVersion.from_path(self.path)
except (ValueError, InvalidPythonVersion):
self.py_version = None
Expand All @@ -355,11 +382,7 @@ def create(cls, path, is_root=False, only_python=False, pythons=None):
"""

target = ensure_path(path)
creation_args = {
"path": target,
"is_root": is_root,
"only_python": only_python
}
creation_args = {"path": target, "is_root": is_root, "only_python": only_python}
if pythons:
creation_args["pythons"] = pythons
_new = cls(**creation_args)
Expand Down
4 changes: 3 additions & 1 deletion pipenv/vendor/pythonfinder/models/pyenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ def get_versions(self):
version.get("is_prerelease"),
version.get("is_devrelease"),
)
versions[version_tuple] = VersionPath.create(path=p.resolve(), only_python=True)
versions[version_tuple] = VersionPath.create(
path=p.resolve(), only_python=True
)
return versions

@pythons.default
Expand Down
26 changes: 12 additions & 14 deletions pipenv/vendor/pythonfinder/models/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,7 @@ def version_sort(self):
release_sort = 1
elif self.is_devrelease:
release_sort = 0
return (
self.major,
self.minor,
self.patch if self.patch else 0,
release_sort
)

return (self.major, self.minor, self.patch if self.patch else 0, release_sort)

@property
def version_tuple(self):
Expand All @@ -68,9 +62,11 @@ def version_tuple(self):
self.is_devrelease,
)

def matches(self, major=None, minor=None, patch=None, pre=False, dev=False, arch=None):
if arch and arch.isdigit():
arch = '{0}bit'.format(arch)
def matches(
self, major=None, minor=None, patch=None, pre=False, dev=False, arch=None
):
if arch and arch.isnumeric():
arch = "{0}bit".format(arch)
return (
(major is None or self.major == major)
and (minor is None or self.minor == minor)
Expand Down Expand Up @@ -195,9 +191,9 @@ def from_windows_launcher(cls, launcher_entry):

@classmethod
def create(cls, **kwargs):
if 'architecture' in kwargs:
if kwargs['architecture'].isdigit():
kwargs['architecture'] = '{0}bit'.format(kwargs['architecture'])
if "architecture" in kwargs:
if kwargs["architecture"].isnumeric():
kwargs["architecture"] = "{0}bit".format(kwargs["architecture"])
return cls(**kwargs)


Expand All @@ -221,4 +217,6 @@ def merge(self, target):
current_entries = {p.path for p in self.versions.get(version)}
new_entries = {p.path for p in entries}
new_entries -= current_entries
self.versions[version].append([e for e in entries if e.path in new_entries])
self.versions[version].append(
[e for e in entries if e.path in new_entries]
)
Loading

0 comments on commit ef060c4

Please sign in to comment.