Skip to content

Commit

Permalink
Add support for ASDF
Browse files Browse the repository at this point in the history
- Fixes #35

Signed-off-by: Dan Ryan <dan@danryan.co>
  • Loading branch information
techalchemy committed Nov 13, 2018
1 parent 971799b commit 58019b7
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 22 deletions.
1 change: 1 addition & 0 deletions news/35.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for ``asdf`` installations via the ``ASDF_DATA_DIR`` environment variable.
4 changes: 4 additions & 0 deletions src/pythonfinder/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
PYENV_INSTALLED = bool(os.environ.get("PYENV_SHELL")) or bool(
os.environ.get("PYENV_ROOT")
)
ASDF_INSTALLED = bool(os.environ.get("ASDF_DATA_DIR"))
PYENV_ROOT = os.path.expanduser(
os.path.expandvars(os.environ.get("PYENV_ROOT", "~/.pyenv"))
)
ASDF_DATA_DIR = os.path.expanduser(
os.path.expandvars(os.environ.get("ASDF_DATA_DIR", "~/.asdf"))
)
IS_64BIT_OS = None
SYSTEM_ARCH = platform.architecture()[0]

Expand Down
9 changes: 9 additions & 0 deletions src/pythonfinder/models/asdf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# -*- coding=utf-8 -*-
import attr

from .pyenv import PyenvFinder


@attr.s
class AsdfFinder(PyenvFinder):
version_root = attr.ib(default="installs/python/*")
69 changes: 51 additions & 18 deletions src/pythonfinder/models/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from vistir.compat import Path, fs_str

from .mixins import BasePath
from ..environment import PYENV_INSTALLED, PYENV_ROOT
from ..environment import PYENV_INSTALLED, PYENV_ROOT, ASDF_INSTALLED, ASDF_DATA_DIR
from ..exceptions import InvalidPythonVersion
from ..utils import (
ensure_path,
Expand All @@ -40,6 +40,7 @@ class SystemPath(object):
python_version_dict = attr.ib(default=attr.Factory(defaultdict))
only_python = attr.ib(default=False)
pyenv_finder = attr.ib(default=None, validator=optional_instance_of("PyenvPath"))
asdf_finder = attr.ib(default=None)
system = attr.ib(default=False)
_version_dict = attr.ib(default=attr.Factory(defaultdict))
ignore_unsupported = attr.ib(default=False)
Expand Down Expand Up @@ -105,6 +106,8 @@ def __attrs_post_init__(self):
self._setup_windows()
if PYENV_INSTALLED:
self._setup_pyenv()
if ASDF_INSTALLED:
self._setup_asdf()
venv = os.environ.get("VIRTUAL_ENV")
if os.name == "nt":
bin_dir = "Scripts"
Expand All @@ -124,32 +127,62 @@ def __attrs_post_init__(self):
path=syspath_bin, is_root=True, only_python=False
)

def _setup_pyenv(self):
from .pyenv import PyenvFinder

last_pyenv = next(
(p for p in reversed(self.path_order) if PYENV_ROOT.lower() in p.lower()),
def _get_last_instance(self, path):
last_instance = next(iter(
(p for p in reversed(self.path_order) if path.lower() in p.lower())),
None,
)
try:
pyenv_index = self.path_order.index(last_pyenv)
path_index = self.path_order.index(last_instance)
except ValueError:
return
return path_index

def _slice_in_paths(self, start_idx, paths):
before_path = self.path_order[: start_idx + 1]
after_path = self.path_order[start_idx + 2 :]
self.path_order = (
before_path + [p.as_posix() for p in paths] + after_path
)

def _remove_path(self, path):
path_copy = reversed(self.path_order[:])
new_order = []
target = os.path.normcase(os.path.normpath(os.path.abspath(path)))
path_map = {
os.path.normcase(os.path.normpath(os.path.abspath(pth))): pth
for pth in self.paths.keys()
}
if target in path_map:
del self.paths[path_map.get(target)]
for current_path in path_copy:
normalized = os.path.normcase(os.path.normpath(os.path.abspath(current_path)))
if normalized != target:
new_order.append(normalized)
new_order = reversed(new_order)
self.path_order = new_order

def _setup_asdf(self):
from .asdf import AsdfFinder
asdf_index = self._get_last_instance(ASDF_DATA_DIR)
self.asdf_finder = AsdfFinder.create(root=ASDF_DATA_DIR, ignore_unsupported=True)
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._register_finder("asdf", self.asdf_finder)

def _setup_pyenv(self):
from .pyenv import PyenvFinder

pyenv_index = self._get_last_instance(PYENV_ROOT)
self.pyenv_finder = PyenvFinder.create(
root=PYENV_ROOT, ignore_unsupported=self.ignore_unsupported
)
root_paths = [p for p in self.pyenv_finder.roots]
before_path = self.path_order[: pyenv_index + 1]
after_path = self.path_order[pyenv_index + 2 :]
self.path_order = (
before_path + [p.as_posix() for p in root_paths] + after_path
)
pyenv_shim_path = os.path.join(PYENV_ROOT, "shims")
if pyenv_shim_path in self.path_order:
self.path_order.remove(pyenv_shim_path)
self._slice_in_paths(pyenv_index, root_paths)

self.paths.update(self.pyenv_finder.roots)
if pyenv_shim_path in self.paths:
del self.paths[pyenv_shim_path]
self._remove_path(os.path.join(PYENV_ROOT, "shims"))
self._register_finder("pyenv", self.pyenv_finder)

def _setup_windows(self):
Expand Down Expand Up @@ -396,7 +429,7 @@ def create(
)


@attr.s
@attr.s(slots=True)
class PathEntry(BasePath):
path = attr.ib(default=None, validator=optional_instance_of(Path))
_children = attr.ib(default=attr.Factory(dict))
Expand Down
7 changes: 4 additions & 3 deletions src/pythonfinder/models/pyenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@
logger = logging.getLogger(__name__)


@attr.s
@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()

Expand All @@ -50,7 +51,7 @@ def get_version_order(self):
version_order_lines = version_order_file.read_text(encoding="utf-8").splitlines()

version_paths = [
p for p in self.root.glob("versions/*")
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}
Expand All @@ -74,7 +75,7 @@ def version_from_bin_dir(cls, base_dir, name=None):
@versions.default
def get_versions(self):
versions = defaultdict()
bin_ = sysconfig._INSTALL_SCHEMES['posix_prefix']["scripts"]
bin_ = "{base}/bin"
for p in self.get_version_order():
bin_dir = Path(bin_.format(base=p.as_posix()))
version_path = None
Expand Down
2 changes: 1 addition & 1 deletion src/pythonfinder/models/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
)


@attr.s
@attr.s(slots=True)
class PythonVersion(object):
major = attr.ib(default=0)
minor = attr.ib(default=None)
Expand Down

0 comments on commit 58019b7

Please sign in to comment.