mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
e328ae24df
Signed-off-by: Dan Ryan <dan@danryan.co>
148 lines
4.0 KiB
Python
148 lines
4.0 KiB
Python
# -*- coding=utf-8 -*-
|
|
from __future__ import absolute_import, print_function
|
|
|
|
import itertools
|
|
import os
|
|
|
|
from fnmatch import fnmatch
|
|
|
|
import attr
|
|
import six
|
|
|
|
import vistir
|
|
|
|
from .exceptions import InvalidPythonVersion
|
|
|
|
try:
|
|
from functools import lru_cache
|
|
except ImportError:
|
|
from backports.functools_lru_cache import lru_cache
|
|
|
|
six.add_move(six.MovedAttribute("Iterable", "collections", "collections.abc"))
|
|
from six.moves import Iterable
|
|
|
|
|
|
PYTHON_IMPLEMENTATIONS = (
|
|
"python", "ironpython", "jython", "pypy", "anaconda", "miniconda",
|
|
"stackless", "activepython", "micropython"
|
|
)
|
|
RULES_BASE = ["*{0}", "*{0}?", "*{0}?.?", "*{0}?.?m"]
|
|
RULES = [rule.format(impl) for impl in PYTHON_IMPLEMENTATIONS for rule in RULES_BASE]
|
|
|
|
KNOWN_EXTS = {"exe", "py", "fish", "sh", ""}
|
|
KNOWN_EXTS = KNOWN_EXTS | set(
|
|
filter(None, os.environ.get("PATHEXT", "").split(os.pathsep))
|
|
)
|
|
|
|
MATCH_RULES = []
|
|
for rule in RULES:
|
|
MATCH_RULES.extend(
|
|
[
|
|
"{0}.{1}".format(rule, ext) if ext else "{0}".format(rule)
|
|
for ext in KNOWN_EXTS
|
|
]
|
|
)
|
|
|
|
|
|
@lru_cache(maxsize=128)
|
|
def get_python_version(path):
|
|
"""Get python version string using subprocess from a given path."""
|
|
version_cmd = [path, "-c", "import sys; print(sys.version.split()[0])"]
|
|
try:
|
|
c = vistir.misc.run(version_cmd, block=True, nospin=True, return_object=True,
|
|
combine_stderr=False)
|
|
except OSError:
|
|
raise InvalidPythonVersion("%s is not a valid python path" % path)
|
|
if not c.out:
|
|
raise InvalidPythonVersion("%s is not a valid python path" % path)
|
|
return c.out.strip()
|
|
|
|
|
|
def optional_instance_of(cls):
|
|
return attr.validators.optional(attr.validators.instance_of(cls))
|
|
|
|
|
|
def path_and_exists(path):
|
|
return attr.validators.instance_of(vistir.compat.Path) and path.exists()
|
|
|
|
|
|
def path_is_executable(path):
|
|
return os.access(str(path), os.X_OK)
|
|
|
|
|
|
@lru_cache(maxsize=1024)
|
|
def path_is_known_executable(path):
|
|
return (
|
|
path_is_executable(path)
|
|
or os.access(str(path), os.R_OK)
|
|
and path.suffix in KNOWN_EXTS
|
|
)
|
|
|
|
|
|
@lru_cache(maxsize=1024)
|
|
def looks_like_python(name):
|
|
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)
|
|
|
|
|
|
@lru_cache(maxsize=1024)
|
|
def path_is_python(path):
|
|
return path_is_executable(path) and looks_like_python(path.name)
|
|
|
|
|
|
@lru_cache(maxsize=1024)
|
|
def ensure_path(path):
|
|
"""Given a path (either a string or a Path object), expand variables and return a Path object.
|
|
|
|
:param path: A string or a :class:`~pathlib.Path` object.
|
|
:type path: str or :class:`~pathlib.Path`
|
|
:return: A fully expanded Path object.
|
|
:rtype: :class:`~pathlib.Path`
|
|
"""
|
|
|
|
if isinstance(path, vistir.compat.Path):
|
|
return path
|
|
path = vistir.compat.Path(os.path.expandvars(path))
|
|
return path.absolute()
|
|
|
|
|
|
def _filter_none(k, v):
|
|
if v:
|
|
return True
|
|
return False
|
|
|
|
|
|
def normalize_path(path):
|
|
return os.path.normpath(os.path.normcase(os.path.abspath(str(path))))
|
|
|
|
|
|
@lru_cache(maxsize=1024)
|
|
def filter_pythons(path):
|
|
"""Return all valid pythons in a given path"""
|
|
if not isinstance(path, vistir.compat.Path):
|
|
path = vistir.compat.Path(str(path))
|
|
if not path.is_dir():
|
|
return path if path_is_python(path) else None
|
|
return filter(lambda x: path_is_python(x), path.iterdir())
|
|
|
|
|
|
# def unnest(item):
|
|
# if isinstance(next((i for i in item), None), (list, tuple)):
|
|
# return chain(*filter(None, item))
|
|
# return chain(filter(None, item))
|
|
|
|
|
|
def unnest(item):
|
|
if isinstance(item, Iterable) and not isinstance(item, six.string_types):
|
|
item, target = itertools.tee(item, 2)
|
|
else:
|
|
target = item
|
|
for el in target:
|
|
if isinstance(el, Iterable) and not isinstance(el, six.string_types):
|
|
el, el_copy = itertools.tee(el, 2)
|
|
for sub in unnest(el_copy):
|
|
yield sub
|
|
else:
|
|
yield el
|