mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Further updates to pythonfinder
Signed-off-by: Dan Ryan <dan@danryan.co>
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
from __future__ import print_function, absolute_import
|
||||
|
||||
__version__ = '1.1.9'
|
||||
__version__ = '1.1.10'
|
||||
|
||||
# Add NullHandler to "pythonfinder" logger, because Python2's default root
|
||||
# logger has no handler and warnings like this would be reported:
|
||||
|
||||
Vendored
+70
-7
@@ -13,7 +13,9 @@ import six
|
||||
|
||||
import vistir
|
||||
|
||||
from .environment import PYENV_ROOT, ASDF_DATA_DIR
|
||||
from packaging.version import LegacyVersion, Version
|
||||
|
||||
from .environment import PYENV_ROOT, ASDF_DATA_DIR, MYPY_RUNNING
|
||||
from .exceptions import InvalidPythonVersion
|
||||
|
||||
six.add_move(six.MovedAttribute("Iterable", "collections", "collections.abc"))
|
||||
@@ -24,6 +26,10 @@ try:
|
||||
except ImportError:
|
||||
from backports.functools_lru_cache import lru_cache
|
||||
|
||||
if MYPY_RUNNING:
|
||||
from typing import Any, Union, List, Callable, Iterable, Set, Tuple, Dict, Optional
|
||||
from attr.validators import _OptionalValidator
|
||||
|
||||
|
||||
version_re = re.compile(r"(?P<major>\d+)\.(?P<minor>\d+)\.?(?P<patch>(?<=\.)[0-9]+)?\.?"
|
||||
r"(?:(?P<prerel>[abc]|rc|dev)(?:(?P<prerelversion>\d+(?:\.\d+)*))?)"
|
||||
@@ -54,6 +60,7 @@ for rule in RULES:
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def get_python_version(path):
|
||||
# type: (str) -> str
|
||||
"""Get python version string using subprocess from a given path."""
|
||||
version_cmd = [path, "-c", "import sys; print(sys.version.split()[0])"]
|
||||
try:
|
||||
@@ -68,28 +75,30 @@ def get_python_version(path):
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def parse_python_version(version_str):
|
||||
# type: (str) -> Dict[str, Union[str, int, Version]]
|
||||
from packaging.version import parse as parse_version
|
||||
is_debug = False
|
||||
if version_str.endswith("-debug"):
|
||||
is_debug = True
|
||||
version_str, _, _ = version.rpartition("-")
|
||||
version_str, _, _ = version_str.rpartition("-")
|
||||
m = version_re.match(version_str)
|
||||
if not m:
|
||||
raise InvalidPythonVersion("%s is not a python version" % version_str)
|
||||
version_dict = m.groupdict()
|
||||
major = int(version_dict.get("major"))
|
||||
minor = int(version_dict.get("minor"))
|
||||
patch = version_dict.get("patch")
|
||||
version_dict = m.groupdict() # type: Dict[str, str]
|
||||
major = int(version_dict.get("major", 0)) if version_dict.get("major") else None
|
||||
minor = int(version_dict.get("minor", 0)) if version_dict.get("minor") else None
|
||||
patch = int(version_dict.get("patch", 0)) if version_dict.get("patch") else None
|
||||
is_postrelease = True if version_dict.get("post") else False
|
||||
is_prerelease = True if version_dict.get("prerel") else False
|
||||
is_devrelease = True if version_dict.get("dev") else False
|
||||
if patch:
|
||||
patch = int(patch)
|
||||
version = None # type: Optional[Union[Version, LegacyVersion]]
|
||||
try:
|
||||
version = parse_version(version_str)
|
||||
except TypeError:
|
||||
version_parts = [str(v) for v in [major, minor, patch] if v is not None]
|
||||
version = parse_version(".".join(v))
|
||||
version = parse_version(".".join(version_parts))
|
||||
return {
|
||||
"major": major,
|
||||
"minor": minor,
|
||||
@@ -103,15 +112,42 @@ def parse_python_version(version_str):
|
||||
|
||||
|
||||
def optional_instance_of(cls):
|
||||
# type: (Any) -> _OptionalValidator
|
||||
"""
|
||||
Return an validator to determine whether an input is an optional instance of a class.
|
||||
|
||||
:return: A validator to determine optional instance membership.
|
||||
:rtype: :class:`~attr.validators._OptionalValidator`
|
||||
"""
|
||||
|
||||
return attr.validators.optional(attr.validators.instance_of(cls))
|
||||
|
||||
|
||||
def path_is_executable(path):
|
||||
# type: (str) -> bool
|
||||
"""
|
||||
Determine whether the supplied path is executable.
|
||||
|
||||
:return: Whether the provided path is executable.
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
return os.access(str(path), os.X_OK)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def path_is_known_executable(path):
|
||||
# type: (vistir.compat.Path) -> bool
|
||||
"""
|
||||
Returns whether a given path is a known executable from known executable extensions
|
||||
or has the executable bit toggled.
|
||||
|
||||
:param path: The path to the target executable.
|
||||
:type path: :class:`~vistir.compat.Path`
|
||||
:return: True if the path has chmod +x, or is a readable, known executable extension.
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
return (
|
||||
path_is_executable(path)
|
||||
or os.access(str(path), os.R_OK)
|
||||
@@ -121,6 +157,15 @@ def path_is_known_executable(path):
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def looks_like_python(name):
|
||||
# type: (str) -> bool
|
||||
"""
|
||||
Determine whether the supplied filename looks like a possible name of python.
|
||||
|
||||
:param str name: The name of the provided file.
|
||||
:return: Whether the provided name looks like python.
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
if not any(name.lower().startswith(py_name) for py_name in PYTHON_IMPLEMENTATIONS):
|
||||
return False
|
||||
return any(fnmatch(name, rule) for rule in MATCH_RULES)
|
||||
@@ -128,11 +173,22 @@ def looks_like_python(name):
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def path_is_python(path):
|
||||
# type: (vistir.compat.Path) -> bool
|
||||
"""
|
||||
Determine whether the supplied path is executable and looks like a possible path to python.
|
||||
|
||||
:param path: The path to an executable.
|
||||
:type path: :class:`~vistir.compat.Path`
|
||||
:return: Whether the provided path is an executable path to python.
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
return path_is_executable(path) and looks_like_python(path.name)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def ensure_path(path):
|
||||
# type: (Union[vistir.compat.Path, str]) -> bool
|
||||
"""
|
||||
Given a path (either a string or a Path object), expand variables and return a Path object.
|
||||
|
||||
@@ -149,6 +205,7 @@ def ensure_path(path):
|
||||
|
||||
|
||||
def _filter_none(k, v):
|
||||
# type: (Any, Any) -> bool
|
||||
if v:
|
||||
return True
|
||||
return False
|
||||
@@ -156,6 +213,7 @@ def _filter_none(k, v):
|
||||
|
||||
# TODO: Reimplement in vistir
|
||||
def normalize_path(path):
|
||||
# type: (str) -> str
|
||||
return os.path.normpath(os.path.normcase(
|
||||
os.path.abspath(os.path.expandvars(os.path.expanduser(str(path))))
|
||||
))
|
||||
@@ -163,6 +221,7 @@ def normalize_path(path):
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def filter_pythons(path):
|
||||
# type: (Union[str, vistir.compat.Path]) -> Iterable
|
||||
"""Return all valid pythons in a given path"""
|
||||
if not isinstance(path, vistir.compat.Path):
|
||||
path = vistir.compat.Path(str(path))
|
||||
@@ -173,6 +232,8 @@ def filter_pythons(path):
|
||||
|
||||
# TODO: Port to vistir
|
||||
def unnest(item):
|
||||
# type: (Any) -> Iterable[Any]
|
||||
target = None # type: Optional[Iterable]
|
||||
if isinstance(item, Iterable) and not isinstance(item, six.string_types):
|
||||
item, target = itertools.tee(item, 2)
|
||||
else:
|
||||
@@ -187,6 +248,7 @@ def unnest(item):
|
||||
|
||||
|
||||
def parse_pyenv_version_order(filename="version"):
|
||||
# type: (str) -> List[str]
|
||||
version_order_file = normalize_path(os.path.join(PYENV_ROOT, filename))
|
||||
if os.path.exists(version_order_file) and os.path.isfile(version_order_file):
|
||||
with io.open(version_order_file, encoding="utf-8") as fh:
|
||||
@@ -197,6 +259,7 @@ def parse_pyenv_version_order(filename="version"):
|
||||
|
||||
|
||||
def parse_asdf_version_order(filename=".tool-versions"):
|
||||
# type: (str) -> List[str]
|
||||
version_order_file = normalize_path(os.path.join("~", filename))
|
||||
if os.path.exists(version_order_file) and os.path.isfile(version_order_file):
|
||||
with io.open(version_order_file, encoding="utf-8") as fh:
|
||||
|
||||
Reference in New Issue
Block a user