Cached properties!

Signed-off-by: Dan Ryan <dan@danryan.co>
This commit is contained in:
Dan Ryan
2018-07-17 23:13:13 -04:00
parent b7daa2525b
commit 1b69089cb1
2 changed files with 46 additions and 43 deletions
+3 -2
View File
@@ -57,8 +57,9 @@ class BasePath(object):
:rtype: List[:class:`~pythonfinder.models.PathEntry`]
"""
call_method = "find_all_python_versions" if self.is_dir else "find_python_version"
sub_finder = operator.methodcaller(
"find_python_version", major, minor=minor, patch=patch, pre=pre, dev=dev, arch=arch
call_method, major, minor=minor, patch=patch, pre=pre, dev=dev, arch=arch
)
if not self.is_dir:
return sub_finder(self)
@@ -85,7 +86,7 @@ class BasePath(object):
is_py = operator.attrgetter("is_python")
py_version = operator.attrgetter("as_python")
if not self.is_dir:
if self.is_python and version_matcher(self.as_python):
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)
+43 -41
View File
@@ -6,6 +6,7 @@ import operator
import os
import sys
from collections import defaultdict
from cached_property import cached_property
from itertools import chain
from . import BasePath
from .python import PythonVersion
@@ -17,7 +18,8 @@ from ..utils import (
path_is_known_executable,
looks_like_python,
ensure_path,
fs_str
fs_str,
unnest,
)
try:
@@ -37,6 +39,7 @@ class SystemPath(object):
only_python = attr.ib(default=False)
pyenv_finder = attr.ib(default=None, validator=optional_instance_of("PyenvPath"))
system = attr.ib(default=False)
_version_dict = attr.ib(default=attr.Factory(defaultdict))
__finders = attr.ib(default=attr.Factory(dict))
@@ -44,49 +47,48 @@ class SystemPath(object):
if finder_name not in self.__finders:
self.__finders[finder_name] = finder
@property
@cached_property
def executables(self):
if not self._executables:
self._executables = [p for p in chain(*(child.children.values() for child in self.paths.values())) if p.is_executable]
return self._executables
self.executables = [p for p in chain(*(child.children.values() for child in self.paths.values())) if p.is_executable]
return self.executables
@property
@cached_property
def python_executables(self):
python_executables = {}
if not self._python_executables:
for child in self.paths.values():
if child.pythons:
python_executables.update(dict(child.pythons))
for finder_name, finder in self.__finders.items():
if finder.pythons:
python_executables.update(dict(finder.pythons))
self._python_executables = python_executables
for child in self.paths.values():
if child.pythons:
python_executables.update(dict(child.pythons))
for finder_name, finder in self.__finders.items():
if finder.pythons:
python_executables.update(dict(finder.pythons))
self._python_executables = python_executables
return self._python_executables
def get_python_version_dict(self):
version_dict = defaultdict(list)
@cached_property
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 entry not in version_dict[version]:
version_dict[version].append(entry)
if entry not in self._version_dict[version]:
self._version_dict[version].append(entry)
continue
if isinstance(entry, VersionPath):
for path in entry.paths.values():
if path not in version_dict[version] and path.is_python:
version_dict[version].append(path)
if path not in self._version_dict[version] and path.is_python:
self._version_dict[version].append(path)
continue
continue
elif entry not in version_dict[version] and entry.is_python:
version_dict[version].append(entry)
elif entry not in self._version_dict[version] and entry.is_python:
self._version_dict[version].append(entry)
for p, entry in self.python_executables.items():
version = entry.as_python
if not version:
continue
version = version.version_tuple
if version and entry not in version_dict[version]:
version_dict[version].append(entry)
return version_dict
if version and entry not in self._version_dict[version]:
self._version_dict[version].append(entry)
return self._version_dict
def __attrs_post_init__(self):
#: slice in pyenv
@@ -116,7 +118,6 @@ class SystemPath(object):
self.paths[syspath_bin] = PathEntry.create(
path=syspath_bin, is_root=True, only_python=False
)
self.python_version_dict = self.get_python_version_dict()
def _setup_pyenv(self):
from .pyenv import PyenvFinder
@@ -199,14 +200,14 @@ class SystemPath(object):
"""
sub_finder = operator.methodcaller(
"find_python_version", 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, (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)]
@@ -232,11 +233,6 @@ class SystemPath(object):
_tuple_dev = dev if dev is not None else False
version_tuple = (major, minor_, patch, _tuple_pre, _tuple_dev)
version_tuple_pre = (major, minor, patch, True, False)
version = self.python_version_dict.get(version_tuple)
if not version:
version = self.python_version_dict.get(version_tuple_pre)
if version:
return first(version.comes_from)
if os.name == "nt" and self.windows_finder:
windows_finder_version = sub_finder(self.windows_finder)
if windows_finder_version:
@@ -244,9 +240,15 @@ class SystemPath(object):
paths = (self.get_path(k) for k in self.path_order)
path_filter = filter(None, (sub_finder(p) for p in paths if p is not None))
version_sort = operator.attrgetter("as_python.version_sort")
return next(
ver = next(
(c for c in sorted(path_filter, key=version_sort, reverse=True)), None
)
if ver:
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,]
return ver
@classmethod
def create(cls, path=None, system=False, only_python=False, global_search=True):
@@ -300,7 +302,7 @@ class PathEntry(BasePath):
children = self.path.iterdir()
return children
@property
@cached_property
def children(self):
if not self._children and self.is_dir and self.is_root:
self._children = {
@@ -308,7 +310,7 @@ class PathEntry(BasePath):
for child in self._filter_children()
}
elif not self.is_dir:
return {self.path.as_posix(): self}
self._children = {self.path.as_posix(): self}
return self._children
@pythons.default
@@ -325,7 +327,7 @@ class PathEntry(BasePath):
pythons[_path.as_posix()] = copy.deepcopy(self)
return pythons
@property
@cached_property
def as_python(self):
if not self.is_dir and self.is_python:
if not self.py_version:
@@ -371,11 +373,11 @@ class PathEntry(BasePath):
_new._children = children
return _new
@property
@cached_property
def name(self):
return self.path.name
@property
@cached_property
def is_dir(self):
try:
ret_val = self.path.is_dir()
@@ -383,11 +385,11 @@ class PathEntry(BasePath):
ret_val = False
return ret_val
@property
@cached_property
def is_executable(self):
return path_is_known_executable(self.path)
@property
@cached_property
def is_python(self):
return self.is_executable and (
self.py_version or looks_like_python(self.path.name)