login page
This commit is contained in:
20
Lib/site-packages/pipenv/vendor/pythonfinder/LICENSE.txt
vendored
Normal file
20
Lib/site-packages/pipenv/vendor/pythonfinder/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2018 Dan Ryan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
Lib/site-packages/pipenv/vendor/pythonfinder/__init__.py
vendored
Normal file
19
Lib/site-packages/pipenv/vendor/pythonfinder/__init__.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
# Add NullHandler to "pythonfinder" logger, because Python2's default root
|
||||
# logger has no handler and warnings like this would be reported:
|
||||
#
|
||||
# > No handlers could be found for logger "pythonfinder.models.pyenv"
|
||||
import logging
|
||||
|
||||
from .exceptions import InvalidPythonVersion
|
||||
from .models import SystemPath, WindowsFinder
|
||||
from .pythonfinder import Finder
|
||||
|
||||
__version__ = "1.2.4"
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.addHandler(logging.NullHandler())
|
||||
|
||||
__all__ = ["Finder", "WindowsFinder", "SystemPath", "InvalidPythonVersion"]
|
||||
17
Lib/site-packages/pipenv/vendor/pythonfinder/__main__.py
vendored
Normal file
17
Lib/site-packages/pipenv/vendor/pythonfinder/__main__.py
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
#!env python
|
||||
# -*- coding=utf-8 -*-
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pythonfinder.cli import cli
|
||||
|
||||
|
||||
PYTHONFINDER_MAIN = os.path.dirname(os.path.abspath(__file__))
|
||||
PYTHONFINDER_PACKAGE = os.path.dirname(PYTHONFINDER_MAIN)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(cli())
|
||||
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/__main__.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/__main__.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/cli.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/cli.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/compat.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/compat.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/environment.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/environment.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/exceptions.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/exceptions.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/pythonfinder.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/pythonfinder.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/utils.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/__pycache__/utils.cpython-38.pyc
vendored
Normal file
Binary file not shown.
14
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/Makefile
vendored
Normal file
14
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/Makefile
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Taken from pip: https://github.com/pypa/pip/blob/95bcf8c5f6394298035a7332c441868f3b0169f4/src/pip/_vendor/Makefile
|
||||
all: clean vendor
|
||||
|
||||
clean:
|
||||
@# Delete vendored items
|
||||
find . -maxdepth 1 -mindepth 1 -type d -exec rm -rf {} \;
|
||||
|
||||
vendor:
|
||||
@# Install vendored libraries
|
||||
pip install -t . -r vendor.txt
|
||||
|
||||
@# Cleanup .egg-info directories
|
||||
rm -rf *.egg-info
|
||||
rm -rf *.dist-info
|
||||
0
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/__init__.py
vendored
Normal file
0
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/__init__.py
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
Binary file not shown.
21
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/LICENSE
vendored
Normal file
21
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Steve Dower
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
11
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/__init__.py
vendored
Normal file
11
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/__init__.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
#-------------------------------------------------------------------------
|
||||
# Copyright (c) Steve Dower
|
||||
# All rights reserved.
|
||||
#
|
||||
# Distributed under the terms of the MIT License
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
__author__ = 'Steve Dower <steve.dower@python.org>'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
from pythonfinder._vendor.pep514tools.environment import findall, find, findone
|
||||
7
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/__main__.py
vendored
Normal file
7
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/__main__.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
#-------------------------------------------------------------------------
|
||||
# Copyright (c) Steve Dower
|
||||
# All rights reserved.
|
||||
#
|
||||
# Distributed under the terms of the MIT License
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/__pycache__/__main__.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/__pycache__/__main__.cpython-38.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
198
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/_registry.py
vendored
Normal file
198
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/_registry.py
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
#-------------------------------------------------------------------------
|
||||
# Copyright (c) Steve Dower
|
||||
# All rights reserved.
|
||||
#
|
||||
# Distributed under the terms of the MIT License
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
__all__ = ['open_source', 'REGISTRY_SOURCE_LM', 'REGISTRY_SOURCE_LM_WOW6432', 'REGISTRY_SOURCE_CU']
|
||||
|
||||
from itertools import count
|
||||
import re
|
||||
try:
|
||||
import winreg
|
||||
except ImportError:
|
||||
import _winreg as winreg
|
||||
|
||||
REGISTRY_SOURCE_LM = 1
|
||||
REGISTRY_SOURCE_LM_WOW6432 = 2
|
||||
REGISTRY_SOURCE_CU = 3
|
||||
|
||||
_REG_KEY_INFO = {
|
||||
REGISTRY_SOURCE_LM: (winreg.HKEY_LOCAL_MACHINE, r'Software\Python', winreg.KEY_WOW64_64KEY),
|
||||
REGISTRY_SOURCE_LM_WOW6432: (winreg.HKEY_LOCAL_MACHINE, r'Software\Python', winreg.KEY_WOW64_32KEY),
|
||||
REGISTRY_SOURCE_CU: (winreg.HKEY_CURRENT_USER, r'Software\Python', 0),
|
||||
}
|
||||
|
||||
def get_value_from_tuple(value, vtype):
|
||||
if vtype == winreg.REG_SZ:
|
||||
if '\0' in value:
|
||||
return value[:value.index('\0')]
|
||||
return value
|
||||
return None
|
||||
|
||||
def join(x, y):
|
||||
return x + '\\' + y
|
||||
|
||||
_VALID_ATTR = re.compile('^[a-z_]+$')
|
||||
_VALID_KEY = re.compile('^[A-Za-z]+$')
|
||||
_KEY_TO_ATTR = re.compile('([A-Z]+[a-z]+)')
|
||||
|
||||
class PythonWrappedDict(object):
|
||||
@staticmethod
|
||||
def _attr_to_key(attr):
|
||||
if not attr:
|
||||
return ''
|
||||
if not _VALID_ATTR.match(attr):
|
||||
return attr
|
||||
return ''.join(c.capitalize() for c in attr.split('_'))
|
||||
|
||||
@staticmethod
|
||||
def _key_to_attr(key):
|
||||
if not key:
|
||||
return ''
|
||||
if not _VALID_KEY.match(key):
|
||||
return key
|
||||
return '_'.join(k for k in _KEY_TO_ATTR.split(key) if k).lower()
|
||||
|
||||
def __init__(self, d):
|
||||
self._d = d
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr.startswith('_'):
|
||||
return object.__getattribute__(self, attr)
|
||||
|
||||
if attr == 'value':
|
||||
attr = ''
|
||||
|
||||
key = self._attr_to_key(attr)
|
||||
try:
|
||||
return self._d[key]
|
||||
except KeyError:
|
||||
pass
|
||||
except Exception:
|
||||
raise AttributeError(attr)
|
||||
raise AttributeError(attr)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
if attr.startswith('_'):
|
||||
return object.__setattr__(self, attr, value)
|
||||
|
||||
if attr == 'value':
|
||||
attr = ''
|
||||
self._d[self._attr_to_key(attr)] = value
|
||||
|
||||
def __dir__(self):
|
||||
k2a = self._key_to_attr
|
||||
return list(map(k2a, self._d))
|
||||
|
||||
def _setdefault(self, key, value):
|
||||
self._d.setdefault(key, value)
|
||||
|
||||
def _items(self):
|
||||
return self._d.items()
|
||||
|
||||
def __repr__(self):
|
||||
k2a = self._key_to_attr
|
||||
return 'info(' + ', '.join('{}={!r}'.format(k2a(k), v) for k, v in self._d.items()) + ')'
|
||||
|
||||
class RegistryAccessor(object):
|
||||
def __init__(self, root, subkey, flags):
|
||||
self._root = root
|
||||
self.subkey = subkey
|
||||
_, _, self.name = subkey.rpartition('\\')
|
||||
self._flags = flags
|
||||
|
||||
def __iter__(self):
|
||||
subkey_names = []
|
||||
try:
|
||||
with winreg.OpenKeyEx(self._root, self.subkey, 0, winreg.KEY_READ | self._flags) as key:
|
||||
for i in count():
|
||||
subkey_names.append(winreg.EnumKey(key, i))
|
||||
except OSError:
|
||||
pass
|
||||
return iter(self[k] for k in subkey_names)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return RegistryAccessor(self._root, join(self.subkey, key), self._flags)
|
||||
|
||||
def get_value(self, value_name):
|
||||
try:
|
||||
with winreg.OpenKeyEx(self._root, self.subkey, 0, winreg.KEY_READ | self._flags) as key:
|
||||
return get_value_from_tuple(*winreg.QueryValueEx(key, value_name))
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
def get_all_values(self):
|
||||
schema = {}
|
||||
for subkey in self:
|
||||
schema[subkey.name] = subkey.get_all_values()
|
||||
|
||||
key = winreg.OpenKeyEx(self._root, self.subkey, 0, winreg.KEY_READ | self._flags)
|
||||
try:
|
||||
with key:
|
||||
for i in count():
|
||||
vname, value, vtype = winreg.EnumValue(key, i)
|
||||
value = get_value_from_tuple(value, vtype)
|
||||
if value:
|
||||
schema[vname or ''] = value
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return PythonWrappedDict(schema)
|
||||
|
||||
def set_value(self, value_name, value):
|
||||
with winreg.CreateKeyEx(self._root, self.subkey, 0, winreg.KEY_WRITE | self._flags) as key:
|
||||
if value is None:
|
||||
winreg.DeleteValue(key, value_name)
|
||||
elif isinstance(value, str):
|
||||
winreg.SetValueEx(key, value_name, 0, winreg.REG_SZ, value)
|
||||
else:
|
||||
raise TypeError('cannot write {} to registry'.format(type(value)))
|
||||
|
||||
def _set_all_values(self, rootkey, name, info, errors):
|
||||
with winreg.CreateKeyEx(rootkey, name, 0, winreg.KEY_WRITE | self._flags) as key:
|
||||
for k, v in info:
|
||||
if isinstance(v, PythonWrappedDict):
|
||||
self._set_all_values(key, k, v._items(), errors)
|
||||
elif isinstance(v, dict):
|
||||
self._set_all_values(key, k, v.items(), errors)
|
||||
elif v is None:
|
||||
winreg.DeleteValue(key, k)
|
||||
elif isinstance(v, str):
|
||||
winreg.SetValueEx(key, k, 0, winreg.REG_SZ, v)
|
||||
else:
|
||||
errors.append('cannot write {} to registry'.format(type(v)))
|
||||
|
||||
def set_all_values(self, info):
|
||||
errors = []
|
||||
if isinstance(info, PythonWrappedDict):
|
||||
items = info._items()
|
||||
elif isinstance(info, dict):
|
||||
items = info.items()
|
||||
else:
|
||||
raise TypeError('info must be a dictionary')
|
||||
|
||||
self._set_all_values(self._root, self.subkey, items, errors)
|
||||
if len(errors) == 1:
|
||||
raise ValueError(errors[0])
|
||||
elif errors:
|
||||
raise ValueError(errors)
|
||||
|
||||
def delete(self):
|
||||
for k in self:
|
||||
k.delete()
|
||||
try:
|
||||
key = winreg.OpenKeyEx(self._root, None, 0, winreg.KEY_READ | self._flags)
|
||||
except OSError:
|
||||
return
|
||||
with key:
|
||||
winreg.DeleteKeyEx(key, self.subkey)
|
||||
|
||||
|
||||
def open_source(registry_source):
|
||||
info = _REG_KEY_INFO.get(registry_source)
|
||||
if not info:
|
||||
raise ValueError("unsupported registry source")
|
||||
root, subkey, flags = info
|
||||
return RegistryAccessor(root, subkey, flags)
|
||||
124
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/environment.py
vendored
Normal file
124
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/pep514tools/environment.py
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
#-------------------------------------------------------------------------
|
||||
# Copyright (c) Steve Dower
|
||||
# All rights reserved.
|
||||
#
|
||||
# Distributed under the terms of the MIT License
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
__all__ = ['Environment', 'findall', 'find', 'findone']
|
||||
|
||||
from itertools import count
|
||||
from pythonfinder._vendor.pep514tools._registry import open_source, REGISTRY_SOURCE_LM, REGISTRY_SOURCE_LM_WOW6432, REGISTRY_SOURCE_CU
|
||||
import re
|
||||
import sys
|
||||
|
||||
# These tags are treated specially when the Company is 'PythonCore'
|
||||
_PYTHONCORE_COMPATIBILITY_TAGS = {
|
||||
'2.0', '2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7',
|
||||
'3.0', '3.1', '3.2', '3.3', '3.4', '3.5', '3.6', '3.7',
|
||||
'3.8', '3.9'
|
||||
}
|
||||
|
||||
_IS_64BIT_OS = None
|
||||
def _is_64bit_os():
|
||||
global _IS_64BIT_OS
|
||||
if _IS_64BIT_OS is None:
|
||||
if sys.maxsize > 2**32:
|
||||
import platform
|
||||
_IS_64BIT_OS = (platform.machine() == 'AMD64')
|
||||
else:
|
||||
_IS_64BIT_OS = False
|
||||
return _IS_64BIT_OS
|
||||
|
||||
class Environment(object):
|
||||
def __init__(self, source, company, tag, guessed_arch=None):
|
||||
self._source = source
|
||||
self.company = company
|
||||
self.tag = tag
|
||||
self._guessed_arch = guessed_arch
|
||||
self._orig_info = company, tag
|
||||
self.info = {}
|
||||
|
||||
def load(self):
|
||||
if not self._source:
|
||||
raise ValueError('Environment not initialized with a source')
|
||||
self.info = info = self._source[self.company][self.tag].get_all_values()
|
||||
if self.company == 'PythonCore':
|
||||
info._setdefault('DisplayName', 'Python ' + self.tag)
|
||||
info._setdefault('SupportUrl', 'http://www.python.org/')
|
||||
info._setdefault('Version', self.tag[:3])
|
||||
info._setdefault('SysVersion', self.tag[:3])
|
||||
if self._guessed_arch:
|
||||
info._setdefault('SysArchitecture', self._guessed_arch)
|
||||
|
||||
def save(self, copy=False):
|
||||
if not self._source:
|
||||
raise ValueError('Environment not initialized with a source')
|
||||
if (self.company, self.tag) != self._orig_info:
|
||||
if not copy:
|
||||
self._source[self._orig_info[0]][self._orig_info[1]].delete()
|
||||
self._orig_info = self.company, self.tag
|
||||
|
||||
src = self._source[self.company][self.tag]
|
||||
src.set_all_values(self.info)
|
||||
|
||||
self.info = src.get_all_values()
|
||||
|
||||
def delete(self):
|
||||
if (self.company, self.tag) != self._orig_info:
|
||||
raise ValueError("cannot delete Environment when company/tag have been modified")
|
||||
|
||||
if not self._source:
|
||||
raise ValueError('Environment not initialized with a source')
|
||||
self._source.delete()
|
||||
|
||||
def __repr__(self):
|
||||
return '<environment {}\\{}>'.format(self.company, self.tag)
|
||||
|
||||
def _get_sources(include_per_machine=True, include_per_user=True):
|
||||
if _is_64bit_os():
|
||||
if include_per_user:
|
||||
yield open_source(REGISTRY_SOURCE_CU), None
|
||||
if include_per_machine:
|
||||
yield open_source(REGISTRY_SOURCE_LM), '64bit'
|
||||
yield open_source(REGISTRY_SOURCE_LM_WOW6432), '32bit'
|
||||
else:
|
||||
if include_per_user:
|
||||
yield open_source(REGISTRY_SOURCE_CU), '32bit'
|
||||
if include_per_machine:
|
||||
yield open_source(REGISTRY_SOURCE_LM), '32bit'
|
||||
|
||||
def findall(include_per_machine=True, include_per_user=True):
|
||||
for src, arch in _get_sources(include_per_machine=include_per_machine, include_per_user=include_per_user):
|
||||
for company in src:
|
||||
for tag in company:
|
||||
try:
|
||||
env = Environment(src, company.name, tag.name, arch)
|
||||
env.load()
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
yield env
|
||||
|
||||
def find(company_or_tag, tag=None, include_per_machine=True, include_per_user=True, maxcount=None):
|
||||
if not tag:
|
||||
env = Environment(None, 'PythonCore', company_or_tag)
|
||||
else:
|
||||
env = Environment(None, company_or_tag, tag)
|
||||
|
||||
results = []
|
||||
for src, arch in _get_sources(include_per_machine=include_per_machine, include_per_user=include_per_user):
|
||||
try:
|
||||
env._source = src
|
||||
env._guessed_arch = arch
|
||||
env.load()
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
results.append(env)
|
||||
return results
|
||||
|
||||
def findone(company_or_tag, tag=None, include_per_machine=True, include_per_user=True):
|
||||
found = find(company_or_tag, tag, include_per_machine, include_per_user, maxcount=1)
|
||||
if found:
|
||||
return found[0]
|
||||
1
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/vendor.txt
vendored
Normal file
1
Lib/site-packages/pipenv/vendor/pythonfinder/_vendor/vendor.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
git+https://github.com/zooba/pep514tools.git@master#egg=pep514tools
|
||||
98
Lib/site-packages/pipenv/vendor/pythonfinder/cli.py
vendored
Normal file
98
Lib/site-packages/pipenv/vendor/pythonfinder/cli.py
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import click
|
||||
|
||||
from . import __version__
|
||||
from .pythonfinder import Finder
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--find", default=False, nargs=1, help="Find a specific python version.")
|
||||
@click.option("--which", default=False, nargs=1, help="Run the which command.")
|
||||
@click.option("--findall", is_flag=True, default=False, help="Find all python versions.")
|
||||
@click.option(
|
||||
"--version", is_flag=True, default=False, help="Display PythonFinder version."
|
||||
)
|
||||
@click.option(
|
||||
"--ignore-unsupported/--no-unsupported",
|
||||
is_flag=True,
|
||||
default=True,
|
||||
envvar="PYTHONFINDER_IGNORE_UNSUPPORTED",
|
||||
help="Ignore unsupported python versions.",
|
||||
)
|
||||
@click.version_option(prog_name="pyfinder", version=__version__)
|
||||
@click.pass_context
|
||||
def cli(
|
||||
ctx, find=False, which=False, findall=False, version=False, ignore_unsupported=True
|
||||
):
|
||||
if version:
|
||||
click.echo(
|
||||
"{0} version {1}".format(
|
||||
click.style("PythonFinder", fg="white", bold=True),
|
||||
click.style(str(__version__), fg="yellow")
|
||||
)
|
||||
)
|
||||
ctx.exit()
|
||||
finder = Finder(ignore_unsupported=ignore_unsupported)
|
||||
if findall:
|
||||
versions = [v for v in finder.find_all_python_versions()]
|
||||
if versions:
|
||||
click.secho("Found python at the following locations:", fg="green")
|
||||
for v in versions:
|
||||
py = v.py_version
|
||||
comes_from = getattr(py, "comes_from", None)
|
||||
if comes_from is not None:
|
||||
comes_from_path = getattr(comes_from, "path", v.path)
|
||||
else:
|
||||
comes_from_path = v.path
|
||||
click.secho(
|
||||
"{py.name!s}: {py.version!s} ({py.architecture!s}) @ {comes_from!s}".format(
|
||||
py=py, comes_from=comes_from_path
|
||||
),
|
||||
fg="yellow",
|
||||
)
|
||||
ctx.exit()
|
||||
else:
|
||||
click.secho(
|
||||
"ERROR: No valid python versions found! Check your path and try again.",
|
||||
fg="red",
|
||||
)
|
||||
if find:
|
||||
click.secho("Searching for python: {0!s}".format(find.strip()), fg="yellow")
|
||||
found = finder.find_python_version(find.strip())
|
||||
if found:
|
||||
py = found.py_version
|
||||
comes_from = getattr(py, "comes_from", None)
|
||||
if comes_from is not None:
|
||||
comes_from_path = getattr(comes_from, "path", found.path)
|
||||
else:
|
||||
comes_from_path = found.path
|
||||
arch = getattr(py, "architecture", None)
|
||||
click.secho("Found python at the following locations:", fg="green")
|
||||
click.secho(
|
||||
"{py.name!s}: {py.version!s} ({py.architecture!s}) @ {comes_from!s}".format(
|
||||
py=py, comes_from=comes_from_path
|
||||
),
|
||||
fg="yellow",
|
||||
)
|
||||
ctx.exit()
|
||||
else:
|
||||
click.secho("Failed to find matching executable...", fg="yellow")
|
||||
ctx.exit(1)
|
||||
elif which:
|
||||
found = finder.system_path.which(which.strip())
|
||||
if found:
|
||||
click.secho("Found Executable: {0}".format(found), fg="white")
|
||||
ctx.exit()
|
||||
else:
|
||||
click.secho("Failed to find matching executable...", fg="yellow")
|
||||
ctx.exit(1)
|
||||
else:
|
||||
click.echo("Please provide a command", color="red")
|
||||
ctx.exit(1)
|
||||
ctx.exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
42
Lib/site-packages/pipenv/vendor/pythonfinder/compat.py
vendored
Normal file
42
Lib/site-packages/pipenv/vendor/pythonfinder/compat.py
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
import sys
|
||||
|
||||
import six
|
||||
|
||||
if sys.version_info[:2] <= (3, 4):
|
||||
from pipenv.vendor.pathlib2 import Path # type: ignore # noqa
|
||||
else:
|
||||
from pathlib import Path
|
||||
|
||||
if six.PY3:
|
||||
from functools import lru_cache
|
||||
from builtins import TimeoutError
|
||||
else:
|
||||
from backports.functools_lru_cache import lru_cache # type: ignore # noqa
|
||||
|
||||
class TimeoutError(OSError):
|
||||
pass
|
||||
|
||||
|
||||
def getpreferredencoding():
|
||||
import locale
|
||||
# Borrowed from Invoke
|
||||
# (see https://github.com/pyinvoke/invoke/blob/93af29d/invoke/runners.py#L881)
|
||||
_encoding = locale.getpreferredencoding(False)
|
||||
if six.PY2 and not sys.platform == "win32":
|
||||
_default_encoding = locale.getdefaultlocale()[1]
|
||||
if _default_encoding is not None:
|
||||
_encoding = _default_encoding
|
||||
return _encoding
|
||||
|
||||
|
||||
DEFAULT_ENCODING = getpreferredencoding()
|
||||
|
||||
|
||||
def fs_str(string):
|
||||
"""Encodes a string into the proper filesystem encoding"""
|
||||
|
||||
if isinstance(string, str):
|
||||
return string
|
||||
assert not isinstance(string, bytes)
|
||||
return string.encode(DEFAULT_ENCODING)
|
||||
54
Lib/site-packages/pipenv/vendor/pythonfinder/environment.py
vendored
Normal file
54
Lib/site-packages/pipenv/vendor/pythonfinder/environment.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
|
||||
def is_type_checking():
|
||||
try:
|
||||
from typing import TYPE_CHECKING
|
||||
except ImportError:
|
||||
return False
|
||||
return TYPE_CHECKING
|
||||
|
||||
|
||||
PYENV_INSTALLED = bool(os.environ.get("PYENV_SHELL")) or bool(
|
||||
os.environ.get("PYENV_ROOT")
|
||||
)
|
||||
ASDF_INSTALLED = bool(os.environ.get("ASDF_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]
|
||||
|
||||
if sys.maxsize > 2 ** 32:
|
||||
IS_64BIT_OS = platform.machine() == "AMD64"
|
||||
else:
|
||||
IS_64BIT_OS = False
|
||||
|
||||
|
||||
IGNORE_UNSUPPORTED = bool(os.environ.get("PYTHONFINDER_IGNORE_UNSUPPORTED", False))
|
||||
MYPY_RUNNING = os.environ.get("MYPY_RUNNING", is_type_checking())
|
||||
SUBPROCESS_TIMEOUT = os.environ.get("PYTHONFINDER_SUBPROCESS_TIMEOUT", 5)
|
||||
"""The default subprocess timeout for determining python versions
|
||||
|
||||
Set to **5** by default.
|
||||
"""
|
||||
|
||||
|
||||
def get_shim_paths():
|
||||
shim_paths = []
|
||||
if ASDF_INSTALLED:
|
||||
shim_paths.append(os.path.join(ASDF_DATA_DIR, "shims"))
|
||||
if PYENV_INSTALLED:
|
||||
shim_paths.append(os.path.join(PYENV_ROOT, "shims"))
|
||||
return [os.path.normpath(os.path.normcase(p)) for p in shim_paths]
|
||||
|
||||
|
||||
SHIM_PATHS = get_shim_paths()
|
||||
8
Lib/site-packages/pipenv/vendor/pythonfinder/exceptions.py
vendored
Normal file
8
Lib/site-packages/pipenv/vendor/pythonfinder/exceptions.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
|
||||
class InvalidPythonVersion(Exception):
|
||||
"""Raised when parsing an invalid python version"""
|
||||
|
||||
pass
|
||||
14
Lib/site-packages/pipenv/vendor/pythonfinder/models/__init__.py
vendored
Normal file
14
Lib/site-packages/pipenv/vendor/pythonfinder/models/__init__.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import abc
|
||||
import operator
|
||||
|
||||
from itertools import chain
|
||||
|
||||
import six
|
||||
|
||||
from ..utils import KNOWN_EXTS, unnest
|
||||
from .path import SystemPath
|
||||
from .python import PythonVersion
|
||||
from .windows import WindowsFinder
|
||||
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/__init__.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/mixins.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/mixins.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/path.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/path.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/python.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/python.cpython-38.pyc
vendored
Normal file
Binary file not shown.
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/windows.cpython-38.pyc
vendored
Normal file
BIN
Lib/site-packages/pipenv/vendor/pythonfinder/models/__pycache__/windows.cpython-38.pyc
vendored
Normal file
Binary file not shown.
417
Lib/site-packages/pipenv/vendor/pythonfinder/models/mixins.py
vendored
Normal file
417
Lib/site-packages/pipenv/vendor/pythonfinder/models/mixins.py
vendored
Normal file
@@ -0,0 +1,417 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import abc
|
||||
import operator
|
||||
from collections import defaultdict
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import six
|
||||
|
||||
from ..compat import fs_str
|
||||
from ..environment import MYPY_RUNNING
|
||||
from ..exceptions import InvalidPythonVersion
|
||||
from ..utils import (
|
||||
KNOWN_EXTS,
|
||||
Sequence,
|
||||
expand_paths,
|
||||
looks_like_python,
|
||||
path_is_known_executable,
|
||||
)
|
||||
|
||||
if MYPY_RUNNING:
|
||||
from .path import PathEntry
|
||||
from .python import PythonVersion
|
||||
from typing import (
|
||||
Optional,
|
||||
Union,
|
||||
Any,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
DefaultDict,
|
||||
Generator,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
Type,
|
||||
)
|
||||
from ..compat import Path # noqa
|
||||
|
||||
BaseFinderType = TypeVar("BaseFinderType")
|
||||
|
||||
|
||||
@attr.s(slots=True)
|
||||
class BasePath(object):
|
||||
path = attr.ib(default=None) # type: Path
|
||||
_children = attr.ib(
|
||||
default=attr.Factory(dict), cmp=False
|
||||
) # type: Dict[str, PathEntry]
|
||||
only_python = attr.ib(default=False) # type: bool
|
||||
name = attr.ib(type=str)
|
||||
_py_version = attr.ib(default=None, cmp=False) # type: Optional[PythonVersion]
|
||||
_pythons = attr.ib(
|
||||
default=attr.Factory(defaultdict), cmp=False
|
||||
) # type: DefaultDict[str, PathEntry]
|
||||
_is_dir = attr.ib(default=None, cmp=False) # type: Optional[bool]
|
||||
_is_executable = attr.ib(default=None, cmp=False) # type: Optional[bool]
|
||||
_is_python = attr.ib(default=None, cmp=False) # type: Optional[bool]
|
||||
|
||||
def __str__(self):
|
||||
# type: () -> str
|
||||
return fs_str("{0}".format(self.path.as_posix()))
|
||||
|
||||
def __lt__(self, other):
|
||||
# type: ("BasePath") -> bool
|
||||
return self.path.as_posix() < other.path.as_posix()
|
||||
|
||||
def __lte__(self, other):
|
||||
# type: ("BasePath") -> bool
|
||||
return self.path.as_posix() <= other.path.as_posix()
|
||||
|
||||
def __gt__(self, other):
|
||||
# type: ("BasePath") -> bool
|
||||
return self.path.as_posix() > other.path.as_posix()
|
||||
|
||||
def __gte__(self, other):
|
||||
# type: ("BasePath") -> bool
|
||||
return self.path.as_posix() >= other.path.as_posix()
|
||||
|
||||
def which(self, name):
|
||||
# type: (str) -> Optional[PathEntry]
|
||||
"""Search in this path for an executable.
|
||||
|
||||
:param executable: The name of an executable to search for.
|
||||
:type executable: str
|
||||
:returns: :class:`~pythonfinder.models.PathEntry` instance.
|
||||
"""
|
||||
|
||||
valid_names = [name] + [
|
||||
"{0}.{1}".format(name, ext).lower() if ext else "{0}".format(name).lower()
|
||||
for ext in KNOWN_EXTS
|
||||
]
|
||||
children = self.children
|
||||
found = None
|
||||
if self.path is not 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 __del__(self):
|
||||
for key in ["_is_dir", "_is_python", "_is_executable", "_py_version"]:
|
||||
if getattr(self, key, None):
|
||||
try:
|
||||
delattr(self, key)
|
||||
except Exception:
|
||||
print("failed deleting key: {0}".format(key))
|
||||
self._children = {}
|
||||
for key in list(self._pythons.keys()):
|
||||
del self._pythons[key]
|
||||
self._pythons = None
|
||||
self._py_version = None
|
||||
self.path = None
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
# type: () -> Dict[str, PathEntry]
|
||||
if not self.is_dir:
|
||||
return {}
|
||||
return self._children
|
||||
|
||||
@property
|
||||
def as_python(self):
|
||||
# type: () -> PythonVersion
|
||||
py_version = None
|
||||
if self.py_version:
|
||||
return self.py_version
|
||||
if not self.is_dir and self.is_python:
|
||||
try:
|
||||
from .python import PythonVersion
|
||||
|
||||
py_version = PythonVersion.from_path( # type: ignore
|
||||
path=self, name=self.name
|
||||
)
|
||||
except (ValueError, InvalidPythonVersion):
|
||||
pass
|
||||
if py_version is None:
|
||||
pass
|
||||
self.py_version = py_version
|
||||
return py_version # type: ignore
|
||||
|
||||
@name.default
|
||||
def get_name(self):
|
||||
# type: () -> Optional[str]
|
||||
if self.path:
|
||||
return self.path.name
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_dir(self):
|
||||
# type: () -> bool
|
||||
if self._is_dir is None:
|
||||
if not self.path:
|
||||
ret_val = False
|
||||
try:
|
||||
ret_val = self.path.is_dir()
|
||||
except OSError:
|
||||
ret_val = False
|
||||
self._is_dir = ret_val
|
||||
return self._is_dir
|
||||
|
||||
@is_dir.setter
|
||||
def is_dir(self, val):
|
||||
# type: (bool) -> None
|
||||
self._is_dir = val
|
||||
|
||||
@is_dir.deleter
|
||||
def is_dir(self):
|
||||
# type: () -> None
|
||||
self._is_dir = None
|
||||
|
||||
@property
|
||||
def is_executable(self):
|
||||
# type: () -> bool
|
||||
if self._is_executable is None:
|
||||
if not self.path:
|
||||
self._is_executable = False
|
||||
else:
|
||||
self._is_executable = path_is_known_executable(self.path)
|
||||
return self._is_executable
|
||||
|
||||
@is_executable.setter
|
||||
def is_executable(self, val):
|
||||
# type: (bool) -> None
|
||||
self._is_executable = val
|
||||
|
||||
@is_executable.deleter
|
||||
def is_executable(self):
|
||||
# type: () -> None
|
||||
self._is_executable = None
|
||||
|
||||
@property
|
||||
def is_python(self):
|
||||
# type: () -> bool
|
||||
if self._is_python is None:
|
||||
if not self.path:
|
||||
self._is_python = False
|
||||
else:
|
||||
self._is_python = self.is_executable and (
|
||||
looks_like_python(self.path.name)
|
||||
)
|
||||
return self._is_python
|
||||
|
||||
@is_python.setter
|
||||
def is_python(self, val):
|
||||
# type: (bool) -> None
|
||||
self._is_python = val
|
||||
|
||||
@is_python.deleter
|
||||
def is_python(self):
|
||||
# type: () -> None
|
||||
self._is_python = None
|
||||
|
||||
def get_py_version(self):
|
||||
# type: () -> Optional[PythonVersion]
|
||||
from ..environment import IGNORE_UNSUPPORTED
|
||||
|
||||
if self.is_dir:
|
||||
return None
|
||||
if self.is_python:
|
||||
py_version = None
|
||||
from .python import PythonVersion
|
||||
|
||||
try:
|
||||
py_version = PythonVersion.from_path( # type: ignore
|
||||
path=self, name=self.name
|
||||
)
|
||||
except (InvalidPythonVersion, ValueError):
|
||||
py_version = None
|
||||
except Exception:
|
||||
if not IGNORE_UNSUPPORTED:
|
||||
raise
|
||||
return py_version
|
||||
return None
|
||||
|
||||
@property
|
||||
def py_version(self):
|
||||
# type: () -> Optional[PythonVersion]
|
||||
if not self._py_version:
|
||||
py_version = self.get_py_version()
|
||||
self._py_version = py_version
|
||||
else:
|
||||
py_version = self._py_version
|
||||
return py_version
|
||||
|
||||
@py_version.setter
|
||||
def py_version(self, val):
|
||||
# type: (Optional[PythonVersion]) -> None
|
||||
self._py_version = val
|
||||
|
||||
@py_version.deleter
|
||||
def py_version(self):
|
||||
# type: () -> None
|
||||
self._py_version = None
|
||||
|
||||
def _iter_pythons(self):
|
||||
# type: () -> Iterator
|
||||
if self.is_dir:
|
||||
for entry in self.children.values():
|
||||
if entry is None:
|
||||
continue
|
||||
elif entry.is_dir:
|
||||
for python in entry._iter_pythons():
|
||||
yield python
|
||||
elif entry.is_python and entry.as_python is not None:
|
||||
yield entry
|
||||
elif self.is_python and self.as_python is not None:
|
||||
yield self # type: ignore
|
||||
|
||||
@property
|
||||
def pythons(self):
|
||||
# type: () -> DefaultDict[Union[str, Path], PathEntry]
|
||||
if not self._pythons:
|
||||
from .path import PathEntry
|
||||
|
||||
self._pythons = defaultdict(PathEntry)
|
||||
for python in self._iter_pythons():
|
||||
python_path = python.path.as_posix() # type: ignore
|
||||
self._pythons[python_path] = python
|
||||
return self._pythons
|
||||
|
||||
def __iter__(self):
|
||||
# type: () -> Iterator
|
||||
for entry in self.children.values():
|
||||
yield entry
|
||||
|
||||
def __next__(self):
|
||||
# type: () -> Generator
|
||||
return next(iter(self))
|
||||
|
||||
def next(self):
|
||||
# type: () -> Generator
|
||||
return self.__next__()
|
||||
|
||||
def find_all_python_versions(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> List[PathEntry]
|
||||
"""Search for a specific python version on the path. Return all copies
|
||||
|
||||
:param major: Major python version to search for.
|
||||
:type major: int
|
||||
:param int minor: Minor python version to search for, defaults to None
|
||||
:param int patch: Patch python version to search for, defaults to None
|
||||
:param bool pre: Search for prereleases (default None) - prioritize releases if None
|
||||
:param bool dev: Search for devreleases (default None) - prioritize releases if None
|
||||
:param str arch: Architecture to include, e.g. '64bit', defaults to None
|
||||
:param str name: The name of a python version, e.g. ``anaconda3-5.3.0``
|
||||
:return: A list of :class:`~pythonfinder.models.PathEntry` instances matching the version requested.
|
||||
:rtype: List[:class:`~pythonfinder.models.PathEntry`]
|
||||
"""
|
||||
|
||||
call_method = "find_all_python_versions" if self.is_dir else "find_python_version"
|
||||
sub_finder = operator.methodcaller(
|
||||
call_method, major, minor, patch, pre, dev, arch, name
|
||||
)
|
||||
if not self.is_dir:
|
||||
return sub_finder(self)
|
||||
unnested = [sub_finder(path) for path in expand_paths(self)]
|
||||
version_sort = operator.attrgetter("as_python.version_sort")
|
||||
unnested = [p for p in unnested if p is not None and p.as_python is not None]
|
||||
paths = sorted(unnested, key=version_sort, reverse=True)
|
||||
return list(paths)
|
||||
|
||||
def find_python_version(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> Optional[PathEntry]
|
||||
"""Search or self for the specified Python version and return the first match.
|
||||
|
||||
:param major: Major version number.
|
||||
:type major: int
|
||||
:param int minor: Minor python version to search for, defaults to None
|
||||
:param int patch: Patch python version to search for, defaults to None
|
||||
:param bool pre: Search for prereleases (default None) - prioritize releases if None
|
||||
:param bool dev: Search for devreleases (default None) - prioritize releases if None
|
||||
:param str arch: Architecture to include, e.g. '64bit', defaults to None
|
||||
:param str name: The name of a python version, e.g. ``anaconda3-5.3.0``
|
||||
:returns: A :class:`~pythonfinder.models.PathEntry` instance matching the version requested.
|
||||
"""
|
||||
|
||||
version_matcher = operator.methodcaller(
|
||||
"matches", major, minor, patch, pre, dev, arch, python_name=name
|
||||
)
|
||||
if not self.is_dir:
|
||||
if self.is_python and self.as_python and version_matcher(self.py_version):
|
||||
return self # type: ignore
|
||||
|
||||
matching_pythons = [
|
||||
[entry, entry.as_python.version_sort]
|
||||
for entry in self._iter_pythons()
|
||||
if (
|
||||
entry is not None
|
||||
and entry.as_python is not None
|
||||
and version_matcher(entry.py_version)
|
||||
)
|
||||
]
|
||||
results = sorted(matching_pythons, key=operator.itemgetter(1, 0), reverse=True)
|
||||
return next(iter(r[0] for r in results if r is not None), None)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseFinder(object):
|
||||
def __init__(self):
|
||||
#: Maps executable paths to PathEntries
|
||||
from .path import PathEntry
|
||||
|
||||
self._pythons = defaultdict(PathEntry) # type: DefaultDict[str, PathEntry]
|
||||
self._versions = defaultdict(PathEntry) # type: Dict[Tuple, PathEntry]
|
||||
|
||||
def get_versions(self):
|
||||
# type: () -> DefaultDict[Tuple, PathEntry]
|
||||
"""Return the available versions from the finder"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs):
|
||||
# type: (Any, Any) -> BaseFinderType
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def version_paths(self):
|
||||
# type: () -> Any
|
||||
return self._versions.values()
|
||||
|
||||
@property
|
||||
def expanded_paths(self):
|
||||
# type: () -> Any
|
||||
return (p.paths.values() for p in self.version_paths)
|
||||
|
||||
@property
|
||||
def pythons(self):
|
||||
# type: () -> DefaultDict[str, PathEntry]
|
||||
return self._pythons
|
||||
|
||||
@pythons.setter
|
||||
def pythons(self, value):
|
||||
# type: (DefaultDict[str, PathEntry]) -> None
|
||||
self._pythons = value
|
||||
834
Lib/site-packages/pipenv/vendor/pythonfinder/models/path.py
vendored
Normal file
834
Lib/site-packages/pipenv/vendor/pythonfinder/models/path.py
vendored
Normal file
@@ -0,0 +1,834 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import operator
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from itertools import chain
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import six
|
||||
from cached_property import cached_property
|
||||
from ..compat import Path, fs_str
|
||||
|
||||
from ..environment import (
|
||||
ASDF_DATA_DIR,
|
||||
ASDF_INSTALLED,
|
||||
MYPY_RUNNING,
|
||||
PYENV_INSTALLED,
|
||||
PYENV_ROOT,
|
||||
SHIM_PATHS,
|
||||
get_shim_paths,
|
||||
)
|
||||
from ..exceptions import InvalidPythonVersion
|
||||
from ..utils import (
|
||||
Iterable,
|
||||
Sequence,
|
||||
dedup,
|
||||
ensure_path,
|
||||
filter_pythons,
|
||||
is_in_path,
|
||||
normalize_path,
|
||||
optional_instance_of,
|
||||
parse_asdf_version_order,
|
||||
parse_pyenv_version_order,
|
||||
path_is_known_executable,
|
||||
split_version_and_name,
|
||||
unnest,
|
||||
)
|
||||
from .mixins import BaseFinder, BasePath
|
||||
|
||||
if MYPY_RUNNING:
|
||||
from typing import (
|
||||
Optional,
|
||||
Dict,
|
||||
DefaultDict,
|
||||
Iterator,
|
||||
List,
|
||||
Union,
|
||||
Tuple,
|
||||
Generator,
|
||||
Callable,
|
||||
Type,
|
||||
Any,
|
||||
TypeVar,
|
||||
)
|
||||
from .python import PythonFinder, PythonVersion
|
||||
from .windows import WindowsFinder
|
||||
|
||||
FinderType = TypeVar("FinderType", BaseFinder, PythonFinder, WindowsFinder)
|
||||
ChildType = Union[PythonFinder, "PathEntry"]
|
||||
PathType = Union[PythonFinder, "PathEntry"]
|
||||
|
||||
|
||||
@attr.s
|
||||
class SystemPath(object):
|
||||
global_search = attr.ib(default=True)
|
||||
paths = attr.ib(
|
||||
default=attr.Factory(defaultdict)
|
||||
) # type: DefaultDict[str, Union[PythonFinder, PathEntry]]
|
||||
_executables = attr.ib(default=attr.Factory(list)) # type: List[PathEntry]
|
||||
_python_executables = attr.ib(
|
||||
default=attr.Factory(dict)
|
||||
) # type: Dict[str, PathEntry]
|
||||
path_order = attr.ib(default=attr.Factory(list)) # type: List[str]
|
||||
python_version_dict = attr.ib() # type: DefaultDict[Tuple, List[PythonVersion]]
|
||||
only_python = attr.ib(default=False, type=bool)
|
||||
pyenv_finder = attr.ib(default=None) # type: Optional[PythonFinder]
|
||||
asdf_finder = attr.ib(default=None) # type: Optional[PythonFinder]
|
||||
windows_finder = attr.ib(default=None) # type: Optional[WindowsFinder]
|
||||
system = attr.ib(default=False, type=bool)
|
||||
_version_dict = attr.ib(
|
||||
default=attr.Factory(defaultdict)
|
||||
) # type: DefaultDict[Tuple, List[PathEntry]]
|
||||
ignore_unsupported = attr.ib(default=False, type=bool)
|
||||
|
||||
__finders = attr.ib(
|
||||
default=attr.Factory(dict)
|
||||
) # type: Dict[str, Union[WindowsFinder, PythonFinder]]
|
||||
|
||||
def _register_finder(self, finder_name, finder):
|
||||
# type: (str, Union[WindowsFinder, PythonFinder]) -> "SystemPath"
|
||||
if finder_name not in self.__finders:
|
||||
self.__finders[finder_name] = finder
|
||||
return self
|
||||
|
||||
def clear_caches(self):
|
||||
for key in ["executables", "python_executables", "version_dict", "path_entries"]:
|
||||
if key in self.__dict__:
|
||||
del self.__dict__[key]
|
||||
for finder in list(self.__finders.keys()):
|
||||
del self.__finders[finder]
|
||||
self.__finders = {}
|
||||
return attr.evolve(
|
||||
self,
|
||||
executables=[],
|
||||
python_executables={},
|
||||
python_version_dict=defaultdict(list),
|
||||
version_dict=defaultdict(list),
|
||||
pyenv_finder=None,
|
||||
windows_finder=None,
|
||||
asdf_finder=None,
|
||||
path_order=[],
|
||||
paths=defaultdict(PathEntry),
|
||||
)
|
||||
|
||||
def __del__(self):
|
||||
for key in ["executables", "python_executables", "version_dict", "path_entries"]:
|
||||
try:
|
||||
del self.__dict__[key]
|
||||
except KeyError:
|
||||
pass
|
||||
for finder in list(self.__finders.keys()):
|
||||
del self.__finders[finder]
|
||||
self.__finders = {}
|
||||
self._python_executables = {}
|
||||
self._executables = []
|
||||
self.python_version_dict = defaultdict(list)
|
||||
self._version_dict = defaultdict(list)
|
||||
self.path_order = []
|
||||
self.pyenv_finder = None
|
||||
self.asdf_finder = None
|
||||
self.paths = defaultdict(PathEntry)
|
||||
self.__finders = {}
|
||||
|
||||
@property
|
||||
def finders(self):
|
||||
# type: () -> List[str]
|
||||
return [k for k in self.__finders.keys()]
|
||||
|
||||
@staticmethod
|
||||
def check_for_pyenv():
|
||||
return PYENV_INSTALLED or os.path.exists(normalize_path(PYENV_ROOT))
|
||||
|
||||
@staticmethod
|
||||
def check_for_asdf():
|
||||
return ASDF_INSTALLED or os.path.exists(normalize_path(ASDF_DATA_DIR))
|
||||
|
||||
@python_version_dict.default
|
||||
def create_python_version_dict(self):
|
||||
# type: () -> DefaultDict[Tuple, List[PythonVersion]]
|
||||
return defaultdict(list)
|
||||
|
||||
@cached_property
|
||||
def executables(self):
|
||||
# type: () -> List[PathEntry]
|
||||
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
|
||||
def python_executables(self):
|
||||
# type: () -> Dict[str, PathEntry]
|
||||
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
|
||||
|
||||
@cached_property
|
||||
def version_dict(self):
|
||||
# type: () -> DefaultDict[Tuple, List[PathEntry]]
|
||||
self._version_dict = defaultdict(
|
||||
list
|
||||
) # type: DefaultDict[Tuple, List[PathEntry]]
|
||||
for finder_name, finder in self.__finders.items():
|
||||
for version, entry in finder.versions.items():
|
||||
if finder_name == "windows":
|
||||
if entry not in self._version_dict[version]:
|
||||
self._version_dict[version].append(entry)
|
||||
continue
|
||||
if 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 # type: PythonVersion
|
||||
if not version:
|
||||
continue
|
||||
if not isinstance(version, tuple):
|
||||
version = version.version_tuple
|
||||
if version and entry not in self._version_dict[version]:
|
||||
self._version_dict[version].append(entry)
|
||||
return self._version_dict
|
||||
|
||||
def _run_setup(self):
|
||||
# type: () -> "SystemPath"
|
||||
if not self.__class__ == SystemPath:
|
||||
return self
|
||||
new_instance = self
|
||||
path_order = new_instance.path_order[:]
|
||||
path_entries = self.paths.copy()
|
||||
if self.global_search and "PATH" in os.environ:
|
||||
path_order = path_order + os.environ["PATH"].split(os.pathsep)
|
||||
path_order = list(dedup(path_order))
|
||||
path_instances = [
|
||||
ensure_path(p.strip('"'))
|
||||
for p in path_order
|
||||
if not any(
|
||||
is_in_path(normalize_path(str(p)), normalize_path(shim))
|
||||
for shim in SHIM_PATHS
|
||||
)
|
||||
]
|
||||
path_entries.update(
|
||||
{
|
||||
p.as_posix(): PathEntry.create(
|
||||
path=p.absolute(), is_root=True, only_python=self.only_python
|
||||
)
|
||||
for p in path_instances
|
||||
}
|
||||
)
|
||||
new_instance = attr.evolve(
|
||||
new_instance,
|
||||
path_order=[p.as_posix() for p in path_instances],
|
||||
paths=path_entries,
|
||||
)
|
||||
if os.name == "nt" and "windows" not in self.finders:
|
||||
new_instance = new_instance._setup_windows()
|
||||
#: slice in pyenv
|
||||
if self.check_for_pyenv() and "pyenv" not in self.finders:
|
||||
new_instance = new_instance._setup_pyenv()
|
||||
#: slice in asdf
|
||||
if self.check_for_asdf() and "asdf" not in self.finders:
|
||||
new_instance = new_instance._setup_asdf()
|
||||
venv = os.environ.get("VIRTUAL_ENV")
|
||||
if os.name == "nt":
|
||||
bin_dir = "Scripts"
|
||||
else:
|
||||
bin_dir = "bin"
|
||||
if venv and (new_instance.system or new_instance.global_search):
|
||||
p = ensure_path(venv)
|
||||
path_order = [(p / bin_dir).as_posix()] + new_instance.path_order
|
||||
new_instance = attr.evolve(new_instance, path_order=path_order)
|
||||
paths = new_instance.paths.copy()
|
||||
paths[p] = new_instance.get_path(p.joinpath(bin_dir))
|
||||
new_instance = attr.evolve(new_instance, paths=paths)
|
||||
if new_instance.system:
|
||||
syspath = Path(sys.executable)
|
||||
syspath_bin = syspath.parent
|
||||
if syspath_bin.name != bin_dir and syspath_bin.joinpath(bin_dir).exists():
|
||||
syspath_bin = syspath_bin / bin_dir
|
||||
path_order = [syspath_bin.as_posix()] + new_instance.path_order
|
||||
paths = new_instance.paths.copy()
|
||||
paths[syspath_bin] = PathEntry.create(
|
||||
path=syspath_bin, is_root=True, only_python=False
|
||||
)
|
||||
new_instance = attr.evolve(new_instance, path_order=path_order, paths=paths)
|
||||
return new_instance
|
||||
|
||||
def _get_last_instance(self, path):
|
||||
# type: (str) -> int
|
||||
reversed_paths = reversed(self.path_order)
|
||||
paths = [normalize_path(p) for p in reversed_paths]
|
||||
normalized_target = normalize_path(path)
|
||||
last_instance = next(iter(p for p in paths if normalized_target in p), None)
|
||||
if last_instance is None:
|
||||
raise ValueError("No instance found on path for target: {0!s}".format(path))
|
||||
path_index = self.path_order.index(last_instance)
|
||||
return path_index
|
||||
|
||||
def _slice_in_paths(self, start_idx, paths):
|
||||
# type: (int, List[Path]) -> "SystemPath"
|
||||
before_path = [] # type: List[str]
|
||||
after_path = [] # type: List[str]
|
||||
if start_idx == 0:
|
||||
after_path = self.path_order[:]
|
||||
elif start_idx == -1:
|
||||
before_path = self.path_order[:]
|
||||
else:
|
||||
before_path = self.path_order[: start_idx + 1]
|
||||
after_path = self.path_order[start_idx + 2 :]
|
||||
path_order = before_path + [p.as_posix() for p in paths] + after_path
|
||||
if path_order == self.path_order:
|
||||
return self
|
||||
return attr.evolve(self, path_order=path_order)
|
||||
|
||||
def _remove_path(self, path):
|
||||
# type: (str) -> "SystemPath"
|
||||
path_copy = [p for p in reversed(self.path_order[:])]
|
||||
new_order = []
|
||||
target = normalize_path(path)
|
||||
path_map = {normalize_path(pth): pth for pth in self.paths.keys()}
|
||||
new_paths = self.paths.copy()
|
||||
if target in path_map:
|
||||
del new_paths[path_map[target]]
|
||||
for current_path in path_copy:
|
||||
normalized = normalize_path(current_path)
|
||||
if normalized != target:
|
||||
new_order.append(normalized)
|
||||
new_order = [p for p in reversed(new_order)]
|
||||
return attr.evolve(self, path_order=new_order, paths=new_paths)
|
||||
|
||||
def _setup_asdf(self):
|
||||
# type: () -> "SystemPath"
|
||||
if "asdf" in self.finders and self.asdf_finder is not None:
|
||||
return self
|
||||
from .python import PythonFinder
|
||||
|
||||
os_path = os.environ["PATH"].split(os.pathsep)
|
||||
asdf_finder = PythonFinder.create(
|
||||
root=ASDF_DATA_DIR,
|
||||
ignore_unsupported=True,
|
||||
sort_function=parse_asdf_version_order,
|
||||
version_glob_path="installs/python/*",
|
||||
)
|
||||
asdf_index = None
|
||||
try:
|
||||
asdf_index = self._get_last_instance(ASDF_DATA_DIR)
|
||||
except ValueError:
|
||||
asdf_index = 0 if is_in_path(next(iter(os_path), ""), ASDF_DATA_DIR) else -1
|
||||
if asdf_index is None:
|
||||
# we are in a virtualenv without global pyenv on the path, so we should
|
||||
# not write pyenv to the path here
|
||||
return self
|
||||
# * These are the root paths for the finder
|
||||
_ = [p for p in asdf_finder.roots]
|
||||
new_instance = self._slice_in_paths(asdf_index, [asdf_finder.root])
|
||||
paths = self.paths.copy()
|
||||
paths[asdf_finder.root] = asdf_finder
|
||||
paths.update(asdf_finder.roots)
|
||||
return (
|
||||
attr.evolve(new_instance, paths=paths, asdf_finder=asdf_finder)
|
||||
._remove_path(normalize_path(os.path.join(ASDF_DATA_DIR, "shims")))
|
||||
._register_finder("asdf", asdf_finder)
|
||||
)
|
||||
|
||||
def reload_finder(self, finder_name):
|
||||
# type: (str) -> "SystemPath"
|
||||
if finder_name is None:
|
||||
raise TypeError("Must pass a string as the name of the target finder")
|
||||
finder_attr = "{0}_finder".format(finder_name)
|
||||
setup_attr = "_setup_{0}".format(finder_name)
|
||||
try:
|
||||
current_finder = getattr(self, finder_attr) # type: Any
|
||||
except AttributeError:
|
||||
raise ValueError("Must pass a valid finder to reload.")
|
||||
try:
|
||||
setup_fn = getattr(self, setup_attr)
|
||||
except AttributeError:
|
||||
raise ValueError("Finder has no valid setup function: %s" % finder_name)
|
||||
if current_finder is None:
|
||||
# TODO: This is called 'reload', should we load a new finder for the first
|
||||
# time here? lets just skip that for now to avoid unallowed finders
|
||||
pass
|
||||
if (finder_name == "pyenv" and not PYENV_INSTALLED) or (
|
||||
finder_name == "asdf" and not ASDF_INSTALLED
|
||||
):
|
||||
# Don't allow loading of finders that aren't explicitly 'installed' as it were
|
||||
return self
|
||||
setattr(self, finder_attr, None)
|
||||
if finder_name in self.__finders:
|
||||
del self.__finders[finder_name]
|
||||
return setup_fn()
|
||||
|
||||
def _setup_pyenv(self):
|
||||
# type: () -> "SystemPath"
|
||||
if "pyenv" in self.finders and self.pyenv_finder is not None:
|
||||
return self
|
||||
from .python import PythonFinder
|
||||
|
||||
os_path = os.environ["PATH"].split(os.pathsep)
|
||||
|
||||
pyenv_finder = PythonFinder.create(
|
||||
root=PYENV_ROOT,
|
||||
sort_function=parse_pyenv_version_order,
|
||||
version_glob_path="versions/*",
|
||||
ignore_unsupported=self.ignore_unsupported,
|
||||
)
|
||||
pyenv_index = None
|
||||
try:
|
||||
pyenv_index = self._get_last_instance(PYENV_ROOT)
|
||||
except ValueError:
|
||||
pyenv_index = 0 if is_in_path(next(iter(os_path), ""), PYENV_ROOT) else -1
|
||||
if pyenv_index is None:
|
||||
# we are in a virtualenv without global pyenv on the path, so we should
|
||||
# not write pyenv to the path here
|
||||
return self
|
||||
# * These are the root paths for the finder
|
||||
_ = [p for p in pyenv_finder.roots]
|
||||
new_instance = self._slice_in_paths(pyenv_index, [pyenv_finder.root])
|
||||
paths = new_instance.paths.copy()
|
||||
paths[pyenv_finder.root] = pyenv_finder
|
||||
paths.update(pyenv_finder.roots)
|
||||
return (
|
||||
attr.evolve(new_instance, paths=paths, pyenv_finder=pyenv_finder)
|
||||
._remove_path(os.path.join(PYENV_ROOT, "shims"))
|
||||
._register_finder("pyenv", pyenv_finder)
|
||||
)
|
||||
|
||||
def _setup_windows(self):
|
||||
# type: () -> "SystemPath"
|
||||
if "windows" in self.finders and self.windows_finder is not None:
|
||||
return self
|
||||
from .windows import WindowsFinder
|
||||
|
||||
windows_finder = WindowsFinder.create()
|
||||
root_paths = (p for p in windows_finder.paths if p.is_root)
|
||||
path_addition = [p.path.as_posix() for p in root_paths]
|
||||
new_path_order = self.path_order[:] + path_addition
|
||||
new_paths = self.paths.copy()
|
||||
new_paths.update({p.path: p for p in root_paths})
|
||||
return attr.evolve(
|
||||
self,
|
||||
windows_finder=windows_finder,
|
||||
path_order=new_path_order,
|
||||
paths=new_paths,
|
||||
)._register_finder("windows", windows_finder)
|
||||
|
||||
def get_path(self, path):
|
||||
# type: (Union[str, Path]) -> PathType
|
||||
if path is None:
|
||||
raise TypeError("A path must be provided in order to generate a path entry.")
|
||||
path = ensure_path(path)
|
||||
_path = self.paths.get(path)
|
||||
if not _path:
|
||||
_path = self.paths.get(path.as_posix())
|
||||
if not _path and path.as_posix() in self.path_order:
|
||||
_path = PathEntry.create(
|
||||
path=path.absolute(), is_root=True, only_python=self.only_python
|
||||
)
|
||||
self.paths[path.as_posix()] = _path
|
||||
if not _path:
|
||||
raise ValueError("Path not found or generated: {0!r}".format(path))
|
||||
return _path
|
||||
|
||||
def _get_paths(self):
|
||||
# type: () -> Generator[Union[PathType, WindowsFinder], None, None]
|
||||
for path in self.path_order:
|
||||
try:
|
||||
entry = self.get_path(path)
|
||||
except ValueError:
|
||||
continue
|
||||
else:
|
||||
yield entry
|
||||
|
||||
@cached_property
|
||||
def path_entries(self):
|
||||
# type: () -> List[Union[PathType, WindowsFinder]]
|
||||
paths = list(self._get_paths())
|
||||
return paths
|
||||
|
||||
def find_all(self, executable):
|
||||
# type: (str) -> List[Union[PathEntry, FinderType]]
|
||||
"""
|
||||
Search the path for an executable. Return all copies.
|
||||
|
||||
:param executable: Name of the executable
|
||||
:type executable: str
|
||||
:returns: List[PathEntry]
|
||||
"""
|
||||
|
||||
sub_which = operator.methodcaller("which", executable)
|
||||
filtered = (sub_which(self.get_path(k)) for k in self.path_order)
|
||||
return list(filtered)
|
||||
|
||||
def which(self, executable):
|
||||
# type: (str) -> Union[PathEntry, None]
|
||||
"""
|
||||
Search for an executable on the path.
|
||||
|
||||
:param executable: Name of the executable to be located.
|
||||
:type executable: str
|
||||
:returns: :class:`~pythonfinder.models.PathEntry` object.
|
||||
"""
|
||||
|
||||
sub_which = operator.methodcaller("which", executable)
|
||||
filtered = (sub_which(self.get_path(k)) for k in self.path_order)
|
||||
return next(iter(f for f in filtered if f is not None), None)
|
||||
|
||||
def _filter_paths(self, finder):
|
||||
# type: (Callable) -> Iterator
|
||||
for path in self._get_paths():
|
||||
if path is None:
|
||||
continue
|
||||
python_versions = finder(path)
|
||||
if python_versions is not None:
|
||||
for python in python_versions:
|
||||
if python is not None:
|
||||
yield python
|
||||
|
||||
def _get_all_pythons(self, finder):
|
||||
# type: (Callable) -> Iterator
|
||||
for python in self._filter_paths(finder):
|
||||
if python is not None and python.is_python:
|
||||
yield python
|
||||
|
||||
def get_pythons(self, finder):
|
||||
# type: (Callable) -> Iterator
|
||||
sort_key = operator.attrgetter("as_python.version_sort")
|
||||
pythons = [entry for entry in self._get_all_pythons(finder)]
|
||||
for python in sorted(pythons, key=sort_key, reverse=True):
|
||||
if python is not None:
|
||||
yield python
|
||||
|
||||
def find_all_python_versions(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type (...) -> List[PathEntry]
|
||||
"""Search for a specific python version on the path. Return all copies
|
||||
|
||||
:param major: Major python version to search for.
|
||||
:type major: int
|
||||
:param int minor: Minor python version to search for, defaults to None
|
||||
:param int patch: Patch python version to search for, defaults to None
|
||||
:param bool pre: Search for prereleases (default None) - prioritize releases if None
|
||||
:param bool dev: Search for devreleases (default None) - prioritize releases if None
|
||||
:param str arch: Architecture to include, e.g. '64bit', defaults to None
|
||||
:param str name: The name of a python version, e.g. ``anaconda3-5.3.0``
|
||||
:return: A list of :class:`~pythonfinder.models.PathEntry` instances matching the version requested.
|
||||
:rtype: List[:class:`~pythonfinder.models.PathEntry`]
|
||||
"""
|
||||
|
||||
sub_finder = operator.methodcaller(
|
||||
"find_all_python_versions", major, minor, patch, pre, dev, arch, name
|
||||
)
|
||||
alternate_sub_finder = None
|
||||
if major and not (minor or patch or pre or dev or arch or name):
|
||||
alternate_sub_finder = operator.methodcaller(
|
||||
"find_all_python_versions", None, None, None, None, None, None, major
|
||||
)
|
||||
if os.name == "nt" and self.windows_finder:
|
||||
windows_finder_version = sub_finder(self.windows_finder)
|
||||
if windows_finder_version:
|
||||
return windows_finder_version
|
||||
values = list(self.get_pythons(sub_finder))
|
||||
if not values and alternate_sub_finder is not None:
|
||||
values = list(self.get_pythons(alternate_sub_finder))
|
||||
return values
|
||||
|
||||
def find_python_version(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[Union[str, int]]
|
||||
patch=None, # type: Optional[Union[str, int]]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
sort_by_path=False, # type: bool
|
||||
):
|
||||
# type: (...) -> PathEntry
|
||||
"""Search for a specific python version on the path.
|
||||
|
||||
:param major: Major python version to search for.
|
||||
:type major: int
|
||||
:param int minor: Minor python version to search for, defaults to None
|
||||
:param int patch: Patch python version to search for, defaults to None
|
||||
:param bool pre: Search for prereleases (default None) - prioritize releases if None
|
||||
:param bool dev: Search for devreleases (default None) - prioritize releases if None
|
||||
:param str arch: Architecture to include, e.g. '64bit', defaults to None
|
||||
:param str name: The name of a python version, e.g. ``anaconda3-5.3.0``
|
||||
:param bool sort_by_path: Whether to sort by path -- default sort is by version(default: False)
|
||||
:return: A :class:`~pythonfinder.models.PathEntry` instance matching the version requested.
|
||||
:rtype: :class:`~pythonfinder.models.PathEntry`
|
||||
"""
|
||||
|
||||
major, minor, patch, name = split_version_and_name(major, minor, patch, name)
|
||||
sub_finder = operator.methodcaller(
|
||||
"find_python_version", major, minor, patch, pre, dev, arch, name
|
||||
)
|
||||
alternate_sub_finder = None
|
||||
if name and not (minor or patch or pre or dev or arch or major):
|
||||
alternate_sub_finder = operator.methodcaller(
|
||||
"find_all_python_versions", None, None, None, None, None, None, name
|
||||
)
|
||||
if major and minor and patch:
|
||||
_tuple_pre = pre if pre is not None else False
|
||||
_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)
|
||||
if os.name == "nt" and self.windows_finder:
|
||||
windows_finder_version = sub_finder(self.windows_finder)
|
||||
if windows_finder_version:
|
||||
return windows_finder_version
|
||||
if sort_by_path:
|
||||
paths = [self.get_path(k) for k in self.path_order]
|
||||
for path in paths:
|
||||
found_version = sub_finder(path)
|
||||
if found_version:
|
||||
return found_version
|
||||
if alternate_sub_finder:
|
||||
for path in paths:
|
||||
found_version = alternate_sub_finder(path)
|
||||
if found_version:
|
||||
return found_version
|
||||
|
||||
ver = next(iter(self.get_pythons(sub_finder)), None)
|
||||
if not ver and alternate_sub_finder is not None:
|
||||
ver = next(iter(self.get_pythons(alternate_sub_finder)), 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, # type: str
|
||||
system=False, # type: bool
|
||||
only_python=False, # type: bool
|
||||
global_search=True, # type: bool
|
||||
ignore_unsupported=True, # type: bool
|
||||
):
|
||||
# type: (...) -> SystemPath
|
||||
"""Create a new :class:`pythonfinder.models.SystemPath` instance.
|
||||
|
||||
:param path: Search path to prepend when searching, defaults to None
|
||||
:param path: str, optional
|
||||
:param bool system: Whether to use the running python by default instead of searching, defaults to False
|
||||
:param bool only_python: Whether to search only for python executables, defaults to False
|
||||
:param bool ignore_unsupported: Whether to ignore unsupported python versions, if False, an error is raised, defaults to True
|
||||
:return: A new :class:`pythonfinder.models.SystemPath` instance.
|
||||
:rtype: :class:`pythonfinder.models.SystemPath`
|
||||
"""
|
||||
|
||||
path_entries = defaultdict(
|
||||
PathEntry
|
||||
) # type: DefaultDict[str, Union[PythonFinder, PathEntry]]
|
||||
paths = [] # type: List[str]
|
||||
if ignore_unsupported:
|
||||
os.environ["PYTHONFINDER_IGNORE_UNSUPPORTED"] = fs_str("1")
|
||||
if global_search:
|
||||
if "PATH" in os.environ:
|
||||
paths = os.environ["PATH"].split(os.pathsep)
|
||||
path_order = [] # type: List[str]
|
||||
if path:
|
||||
path_order = [path]
|
||||
path_instance = ensure_path(path)
|
||||
path_entries.update(
|
||||
{
|
||||
path_instance.as_posix(): PathEntry.create(
|
||||
path=path_instance.absolute(),
|
||||
is_root=True,
|
||||
only_python=only_python,
|
||||
)
|
||||
}
|
||||
)
|
||||
paths = [path] + paths
|
||||
paths = [p for p in paths if not any(is_in_path(p, shim) for shim in SHIM_PATHS)]
|
||||
_path_objects = [ensure_path(p.strip('"')) for p in paths]
|
||||
paths = [p.as_posix() for p in _path_objects]
|
||||
path_entries.update(
|
||||
{
|
||||
p.as_posix(): PathEntry.create(
|
||||
path=p.absolute(), is_root=True, only_python=only_python
|
||||
)
|
||||
for p in _path_objects
|
||||
}
|
||||
)
|
||||
instance = cls(
|
||||
paths=path_entries,
|
||||
path_order=path_order,
|
||||
only_python=only_python,
|
||||
system=system,
|
||||
global_search=global_search,
|
||||
ignore_unsupported=ignore_unsupported,
|
||||
)
|
||||
instance = instance._run_setup()
|
||||
return instance
|
||||
|
||||
|
||||
@attr.s(slots=True)
|
||||
class PathEntry(BasePath):
|
||||
is_root = attr.ib(default=True, type=bool, cmp=False)
|
||||
|
||||
def __lt__(self, other):
|
||||
# type: (BasePath) -> bool
|
||||
return self.path.as_posix() < other.path.as_posix()
|
||||
|
||||
def __lte__(self, other):
|
||||
# type: (BasePath) -> bool
|
||||
return self.path.as_posix() <= other.path.as_posix()
|
||||
|
||||
def __gt__(self, other):
|
||||
# type: (BasePath) -> bool
|
||||
return self.path.as_posix() > other.path.as_posix()
|
||||
|
||||
def __gte__(self, other):
|
||||
# type: (BasePath) -> bool
|
||||
return self.path.as_posix() >= other.path.as_posix()
|
||||
|
||||
def __del__(self):
|
||||
if getattr(self, "_children"):
|
||||
del self._children
|
||||
BasePath.__del__(self)
|
||||
|
||||
def _filter_children(self):
|
||||
# type: () -> Iterator[Path]
|
||||
if self.only_python:
|
||||
children = filter_pythons(self.path)
|
||||
else:
|
||||
children = self.path.iterdir()
|
||||
return children
|
||||
|
||||
def _gen_children(self):
|
||||
# type: () -> Iterator
|
||||
shim_paths = get_shim_paths()
|
||||
pass_name = self.name != self.path.name
|
||||
pass_args = {"is_root": False, "only_python": self.only_python}
|
||||
if pass_name:
|
||||
if self.name is not None and isinstance(self.name, six.string_types):
|
||||
pass_args["name"] = self.name # type: ignore
|
||||
elif self.path is not None and isinstance(self.path.name, six.string_types):
|
||||
pass_args["name"] = self.path.name # type: ignore
|
||||
|
||||
if not self.is_dir:
|
||||
yield (self.path.as_posix(), self)
|
||||
elif self.is_root:
|
||||
for child in self._filter_children():
|
||||
if any(is_in_path(str(child), shim) for shim in shim_paths):
|
||||
continue
|
||||
if self.only_python:
|
||||
try:
|
||||
entry = PathEntry.create(path=child, **pass_args) # type: ignore
|
||||
except (InvalidPythonVersion, ValueError):
|
||||
continue
|
||||
else:
|
||||
entry = PathEntry.create(path=child, **pass_args) # type: ignore
|
||||
yield (child.as_posix(), entry)
|
||||
return
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
# type: () -> Dict[str, PathEntry]
|
||||
children = getattr(self, "_children", {}) # type: Dict[str, PathEntry]
|
||||
if not children:
|
||||
for child_key, child_val in self._gen_children():
|
||||
children[child_key] = child_val
|
||||
self.children = children
|
||||
return self._children
|
||||
|
||||
@children.setter
|
||||
def children(self, val):
|
||||
# type: (Dict[str, PathEntry]) -> None
|
||||
self._children = val
|
||||
|
||||
@children.deleter
|
||||
def children(self):
|
||||
# type: () -> None
|
||||
del self._children
|
||||
|
||||
@classmethod
|
||||
def create(cls, path, is_root=False, only_python=False, pythons=None, name=None):
|
||||
# type: (Union[str, Path], bool, bool, Dict[str, PythonVersion], Optional[str]) -> PathEntry
|
||||
"""Helper method for creating new :class:`pythonfinder.models.PathEntry` instances.
|
||||
|
||||
:param str path: Path to the specified location.
|
||||
:param bool is_root: Whether this is a root from the environment PATH variable, defaults to False
|
||||
:param bool only_python: Whether to search only for python executables, defaults to False
|
||||
:param dict pythons: A dictionary of existing python objects (usually from a finder), defaults to None
|
||||
:param str name: Name of the python version, e.g. ``anaconda3-5.3.0``
|
||||
:return: A new instance of the class.
|
||||
:rtype: :class:`pythonfinder.models.PathEntry`
|
||||
"""
|
||||
|
||||
target = ensure_path(path)
|
||||
guessed_name = False
|
||||
if not name:
|
||||
guessed_name = True
|
||||
name = target.name
|
||||
creation_args = {
|
||||
"path": target,
|
||||
"is_root": is_root,
|
||||
"only_python": only_python,
|
||||
"name": name,
|
||||
}
|
||||
if pythons:
|
||||
creation_args["pythons"] = pythons
|
||||
_new = cls(**creation_args)
|
||||
if pythons and only_python:
|
||||
children = {}
|
||||
child_creation_args = {"is_root": False, "only_python": only_python}
|
||||
if not guessed_name:
|
||||
child_creation_args["name"] = _new.name # type: ignore
|
||||
for pth, python in pythons.items():
|
||||
if any(shim in normalize_path(str(pth)) for shim in SHIM_PATHS):
|
||||
continue
|
||||
pth = ensure_path(pth)
|
||||
children[pth.as_posix()] = PathEntry( # type: ignore
|
||||
py_version=python, path=pth, **child_creation_args
|
||||
)
|
||||
_new._children = children
|
||||
return _new
|
||||
|
||||
|
||||
@attr.s
|
||||
class VersionPath(SystemPath):
|
||||
base = attr.ib(default=None, validator=optional_instance_of(Path)) # type: Path
|
||||
name = attr.ib(default=None) # type: str
|
||||
|
||||
@classmethod
|
||||
def create(cls, path, only_python=True, pythons=None, name=None):
|
||||
"""Accepts a path to a base python version directory.
|
||||
|
||||
Generates the version listings for it"""
|
||||
from .path import PathEntry
|
||||
|
||||
path = ensure_path(path)
|
||||
path_entries = defaultdict(PathEntry)
|
||||
bin_ = "{base}/bin"
|
||||
if path.as_posix().endswith(Path(bin_).name):
|
||||
path = path.parent
|
||||
bin_dir = ensure_path(bin_.format(base=path.as_posix()))
|
||||
if not name:
|
||||
name = path.name
|
||||
current_entry = PathEntry.create(
|
||||
bin_dir, is_root=True, only_python=True, pythons=pythons, name=name
|
||||
)
|
||||
path_entries[bin_dir.as_posix()] = current_entry
|
||||
return cls(name=name, base=bin_dir, paths=path_entries)
|
||||
704
Lib/site-packages/pipenv/vendor/pythonfinder/models/python.py
vendored
Normal file
704
Lib/site-packages/pipenv/vendor/pythonfinder/models/python.py
vendored
Normal file
@@ -0,0 +1,704 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import logging
|
||||
import operator
|
||||
import platform
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import six
|
||||
from packaging.version import Version
|
||||
|
||||
from ..compat import Path, lru_cache
|
||||
from ..environment import ASDF_DATA_DIR, MYPY_RUNNING, PYENV_ROOT, SYSTEM_ARCH
|
||||
from ..exceptions import InvalidPythonVersion
|
||||
from ..utils import (
|
||||
RE_MATCHER,
|
||||
_filter_none,
|
||||
ensure_path,
|
||||
expand_paths,
|
||||
get_python_version,
|
||||
guess_company,
|
||||
is_in_path,
|
||||
looks_like_python,
|
||||
optional_instance_of,
|
||||
parse_asdf_version_order,
|
||||
parse_pyenv_version_order,
|
||||
parse_python_version,
|
||||
path_is_pythoncore,
|
||||
unnest,
|
||||
)
|
||||
from .mixins import BaseFinder, BasePath
|
||||
|
||||
if MYPY_RUNNING:
|
||||
from typing import (
|
||||
DefaultDict,
|
||||
Optional,
|
||||
Callable,
|
||||
Generator,
|
||||
Any,
|
||||
Union,
|
||||
Tuple,
|
||||
List,
|
||||
Dict,
|
||||
Type,
|
||||
TypeVar,
|
||||
Iterator,
|
||||
overload,
|
||||
)
|
||||
from .path import PathEntry
|
||||
from .._vendor.pep514tools.environment import Environment
|
||||
else:
|
||||
|
||||
def overload(f):
|
||||
return f
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@attr.s(slots=True)
|
||||
class PythonFinder(BaseFinder, BasePath):
|
||||
root = attr.ib(default=None, validator=optional_instance_of(Path), type=Path)
|
||||
# should come before versions, because its value is used in versions's default initializer.
|
||||
#: Whether to ignore any paths which raise exceptions and are not actually python
|
||||
ignore_unsupported = attr.ib(default=True, type=bool)
|
||||
#: Glob path for python versions off of the root directory
|
||||
version_glob_path = attr.ib(default="versions/*", type=str)
|
||||
#: The function to use to sort version order when returning an ordered verion set
|
||||
sort_function = attr.ib(default=None) # type: Callable
|
||||
#: The root locations used for discovery
|
||||
roots = attr.ib(default=attr.Factory(defaultdict), type=defaultdict)
|
||||
#: List of paths discovered during search
|
||||
paths = attr.ib(type=list)
|
||||
#: shim directory
|
||||
shim_dir = attr.ib(default="shims", type=str)
|
||||
#: Versions discovered in the specified paths
|
||||
_versions = attr.ib(default=attr.Factory(defaultdict), type=defaultdict)
|
||||
_pythons = attr.ib(default=attr.Factory(defaultdict), type=defaultdict)
|
||||
|
||||
def __del__(self):
|
||||
# type: () -> None
|
||||
self._versions = defaultdict()
|
||||
self._pythons = defaultdict()
|
||||
self.roots = defaultdict()
|
||||
self.paths = []
|
||||
|
||||
@property
|
||||
def expanded_paths(self):
|
||||
# type: () -> Generator
|
||||
return (
|
||||
path for path in unnest(p for p in self.versions.values()) if path is not None
|
||||
)
|
||||
|
||||
@property
|
||||
def is_pyenv(self):
|
||||
# type: () -> bool
|
||||
return is_in_path(str(self.root), PYENV_ROOT)
|
||||
|
||||
@property
|
||||
def is_asdf(self):
|
||||
# type: () -> bool
|
||||
return is_in_path(str(self.root), ASDF_DATA_DIR)
|
||||
|
||||
def get_version_order(self):
|
||||
# type: () -> List[Path]
|
||||
version_paths = [
|
||||
p
|
||||
for p in self.root.glob(self.version_glob_path)
|
||||
if not (p.parent.name == "envs" or p.name == "envs")
|
||||
]
|
||||
versions = {v.name: v for v in version_paths}
|
||||
version_order = [] # type: List[Path]
|
||||
if self.is_pyenv:
|
||||
version_order = [
|
||||
versions[v] for v in parse_pyenv_version_order() if v in versions
|
||||
]
|
||||
elif self.is_asdf:
|
||||
version_order = [
|
||||
versions[v] for v in parse_asdf_version_order() if v in versions
|
||||
]
|
||||
for version in version_order:
|
||||
if version in version_paths:
|
||||
version_paths.remove(version)
|
||||
if version_order:
|
||||
version_order += version_paths
|
||||
else:
|
||||
version_order = version_paths
|
||||
return version_order
|
||||
|
||||
def get_bin_dir(self, base):
|
||||
# type: (Union[Path, str]) -> Path
|
||||
if isinstance(base, six.string_types):
|
||||
base = Path(base)
|
||||
return base / "bin"
|
||||
|
||||
@classmethod
|
||||
def version_from_bin_dir(cls, entry):
|
||||
# type: (PathEntry) -> Optional[PathEntry]
|
||||
py_version = None
|
||||
py_version = next(iter(entry.find_all_python_versions()), None)
|
||||
return py_version
|
||||
|
||||
def _iter_version_bases(self):
|
||||
# type: () -> Iterator[Tuple[Path, PathEntry]]
|
||||
from .path import PathEntry
|
||||
|
||||
for p in self.get_version_order():
|
||||
bin_dir = self.get_bin_dir(p)
|
||||
if bin_dir.exists() and bin_dir.is_dir():
|
||||
entry = PathEntry.create(
|
||||
path=bin_dir.absolute(), only_python=False, name=p.name, is_root=True
|
||||
)
|
||||
self.roots[p] = entry
|
||||
yield (p, entry)
|
||||
|
||||
def _iter_versions(self):
|
||||
# type: () -> Iterator[Tuple[Path, PathEntry, Tuple]]
|
||||
for base_path, entry in self._iter_version_bases():
|
||||
version = None
|
||||
version_entry = None
|
||||
try:
|
||||
version = PythonVersion.parse(entry.name)
|
||||
except (ValueError, InvalidPythonVersion):
|
||||
version_entry = next(iter(entry.find_all_python_versions()), None)
|
||||
if version is None:
|
||||
if not self.ignore_unsupported:
|
||||
raise
|
||||
continue
|
||||
if version_entry is not None:
|
||||
version = version_entry.py_version.as_dict()
|
||||
except Exception:
|
||||
if not self.ignore_unsupported:
|
||||
raise
|
||||
logger.warning(
|
||||
"Unsupported Python version %r, ignoring...",
|
||||
base_path.name,
|
||||
exc_info=True,
|
||||
)
|
||||
continue
|
||||
if version is not None:
|
||||
version_tuple = (
|
||||
version.get("major"),
|
||||
version.get("minor"),
|
||||
version.get("patch"),
|
||||
version.get("is_prerelease"),
|
||||
version.get("is_devrelease"),
|
||||
version.get("is_debug"),
|
||||
)
|
||||
yield (base_path, entry, version_tuple)
|
||||
|
||||
@property
|
||||
def versions(self):
|
||||
# type: () -> DefaultDict[Tuple, PathEntry]
|
||||
if not self._versions:
|
||||
for base_path, entry, version_tuple in self._iter_versions():
|
||||
self._versions[version_tuple] = entry
|
||||
return self._versions
|
||||
|
||||
def _iter_pythons(self):
|
||||
# type: () -> Iterator
|
||||
for path, entry, version_tuple in self._iter_versions():
|
||||
if path.as_posix() in self._pythons:
|
||||
yield self._pythons[path.as_posix()]
|
||||
elif version_tuple not in self.versions:
|
||||
for python in entry.find_all_python_versions():
|
||||
yield python
|
||||
else:
|
||||
yield self.versions[version_tuple]
|
||||
|
||||
@paths.default
|
||||
def get_paths(self):
|
||||
# type: () -> List[PathEntry]
|
||||
_paths = [base for _, base in self._iter_version_bases()]
|
||||
return _paths
|
||||
|
||||
@property
|
||||
def pythons(self):
|
||||
# type: () -> DefaultDict[str, PathEntry]
|
||||
if not self._pythons:
|
||||
from .path import PathEntry
|
||||
|
||||
self._pythons = defaultdict(PathEntry) # type: DefaultDict[str, PathEntry]
|
||||
for python in self._iter_pythons():
|
||||
python_path = python.path.as_posix() # type: ignore
|
||||
self._pythons[python_path] = python
|
||||
return self._pythons
|
||||
|
||||
@pythons.setter
|
||||
def pythons(self, value):
|
||||
# type: (DefaultDict[str, PathEntry]) -> None
|
||||
self._pythons = value
|
||||
|
||||
def get_pythons(self):
|
||||
# type: () -> DefaultDict[str, PathEntry]
|
||||
return self.pythons
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def create(cls, root, sort_function, version_glob_path=None, ignore_unsupported=True):
|
||||
# type: (str, Callable, Optional[str], bool) -> PythonFinder
|
||||
root = ensure_path(root)
|
||||
if not version_glob_path:
|
||||
version_glob_path = "versions/*"
|
||||
return cls(
|
||||
root=root,
|
||||
path=root,
|
||||
ignore_unsupported=ignore_unsupported, # type: ignore
|
||||
sort_function=sort_function,
|
||||
version_glob_path=version_glob_path,
|
||||
)
|
||||
|
||||
def find_all_python_versions(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> List[PathEntry]
|
||||
"""Search for a specific python version on the path. Return all copies
|
||||
|
||||
:param major: Major python version to search for.
|
||||
:type major: int
|
||||
:param int minor: Minor python version to search for, defaults to None
|
||||
:param int patch: Patch python version to search for, defaults to None
|
||||
:param bool pre: Search for prereleases (default None) - prioritize releases if None
|
||||
:param bool dev: Search for devreleases (default None) - prioritize releases if None
|
||||
:param str arch: Architecture to include, e.g. '64bit', defaults to None
|
||||
:param str name: The name of a python version, e.g. ``anaconda3-5.3.0``
|
||||
:return: A list of :class:`~pythonfinder.models.PathEntry` instances matching the version requested.
|
||||
:rtype: List[:class:`~pythonfinder.models.PathEntry`]
|
||||
"""
|
||||
|
||||
call_method = "find_all_python_versions" if self.is_dir else "find_python_version"
|
||||
sub_finder = operator.methodcaller(
|
||||
call_method, major, minor, patch, pre, dev, arch, name
|
||||
)
|
||||
if not any([major, minor, patch, name]):
|
||||
pythons = [
|
||||
next(iter(py for py in base.find_all_python_versions()), None)
|
||||
for _, base in self._iter_version_bases()
|
||||
]
|
||||
else:
|
||||
pythons = [sub_finder(path) for path in self.paths]
|
||||
pythons = expand_paths(pythons, True)
|
||||
version_sort = operator.attrgetter("as_python.version_sort")
|
||||
paths = [
|
||||
p for p in sorted(pythons, key=version_sort, reverse=True) if p is not None
|
||||
]
|
||||
return paths
|
||||
|
||||
def find_python_version(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> Optional[PathEntry]
|
||||
"""Search or self for the specified Python version and return the first match.
|
||||
|
||||
:param major: Major version number.
|
||||
:type major: int
|
||||
:param int minor: Minor python version to search for, defaults to None
|
||||
:param int patch: Patch python version to search for, defaults to None
|
||||
:param bool pre: Search for prereleases (default None) - prioritize releases if None
|
||||
:param bool dev: Search for devreleases (default None) - prioritize releases if None
|
||||
:param str arch: Architecture to include, e.g. '64bit', defaults to None
|
||||
:param str name: The name of a python version, e.g. ``anaconda3-5.3.0``
|
||||
:returns: A :class:`~pythonfinder.models.PathEntry` instance matching the version requested.
|
||||
"""
|
||||
|
||||
sub_finder = operator.methodcaller(
|
||||
"find_python_version", major, minor, patch, pre, dev, arch, name
|
||||
)
|
||||
version_sort = operator.attrgetter("as_python.version_sort")
|
||||
unnested = [sub_finder(self.roots[path]) for path in self.roots]
|
||||
unnested = [
|
||||
p
|
||||
for p in unnested
|
||||
if p is not None and p.is_python and p.as_python is not None
|
||||
]
|
||||
paths = sorted(list(unnested), key=version_sort, reverse=True)
|
||||
return next(iter(p for p in paths if p is not None), None)
|
||||
|
||||
def which(self, name):
|
||||
# type: (str) -> Optional[PathEntry]
|
||||
"""Search in this path for an executable.
|
||||
|
||||
:param executable: The name of an executable to search for.
|
||||
:type executable: str
|
||||
:returns: :class:`~pythonfinder.models.PathEntry` instance.
|
||||
"""
|
||||
|
||||
matches = (p.which(name) for p in self.paths)
|
||||
non_empty_match = next(iter(m for m in matches if m is not None), None)
|
||||
return non_empty_match
|
||||
|
||||
|
||||
@attr.s(slots=True)
|
||||
class PythonVersion(object):
|
||||
major = attr.ib(default=0, type=int)
|
||||
minor = attr.ib(default=None) # type: Optional[int]
|
||||
patch = attr.ib(default=None) # type: Optional[int]
|
||||
is_prerelease = attr.ib(default=False, type=bool)
|
||||
is_postrelease = attr.ib(default=False, type=bool)
|
||||
is_devrelease = attr.ib(default=False, type=bool)
|
||||
is_debug = attr.ib(default=False, type=bool)
|
||||
version = attr.ib(default=None) # type: Version
|
||||
architecture = attr.ib(default=None) # type: Optional[str]
|
||||
comes_from = attr.ib(default=None) # type: Optional[PathEntry]
|
||||
executable = attr.ib(default=None) # type: Optional[str]
|
||||
company = attr.ib(default=None) # type: Optional[str]
|
||||
name = attr.ib(default=None, type=str)
|
||||
|
||||
def __getattribute__(self, key):
|
||||
result = super(PythonVersion, self).__getattribute__(key)
|
||||
if key in ["minor", "patch"] and result is None:
|
||||
executable = None # type: Optional[str]
|
||||
if self.executable:
|
||||
executable = self.executable
|
||||
elif self.comes_from:
|
||||
executable = self.comes_from.path.as_posix()
|
||||
if executable is not None:
|
||||
if not isinstance(executable, six.string_types):
|
||||
executable = executable.as_posix()
|
||||
instance_dict = self.parse_executable(executable)
|
||||
for k in instance_dict.keys():
|
||||
try:
|
||||
super(PythonVersion, self).__getattribute__(k)
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
setattr(self, k, instance_dict[k])
|
||||
result = instance_dict.get(key)
|
||||
return result
|
||||
|
||||
@property
|
||||
def version_sort(self):
|
||||
# type: () -> Tuple[int, int, Optional[int], int, int]
|
||||
"""
|
||||
A tuple for sorting against other instances of the same class.
|
||||
|
||||
Returns a tuple of the python version but includes points for core python,
|
||||
non-dev, and non-prerelease versions. So released versions will have 2 points
|
||||
for this value. E.g. ``(1, 3, 6, 6, 2)`` is a release, ``(1, 3, 6, 6, 1)`` is a
|
||||
prerelease, ``(1, 3, 6, 6, 0)`` is a dev release, and ``(1, 3, 6, 6, 3)`` is a
|
||||
postrelease. ``(0, 3, 7, 3, 2)`` represents a non-core python release, e.g. by
|
||||
a repackager of python like Continuum.
|
||||
"""
|
||||
company_sort = 1 if (self.company and self.company == "PythonCore") else 0
|
||||
release_sort = 2
|
||||
if self.is_postrelease:
|
||||
release_sort = 3
|
||||
elif self.is_prerelease:
|
||||
release_sort = 1
|
||||
elif self.is_devrelease:
|
||||
release_sort = 0
|
||||
elif self.is_debug:
|
||||
release_sort = 1
|
||||
return (
|
||||
company_sort,
|
||||
self.major,
|
||||
self.minor,
|
||||
self.patch if self.patch else 0,
|
||||
release_sort,
|
||||
)
|
||||
|
||||
@property
|
||||
def version_tuple(self):
|
||||
# type: () -> Tuple[int, Optional[int], Optional[int], bool, bool, bool]
|
||||
"""
|
||||
Provides a version tuple for using as a dictionary key.
|
||||
|
||||
:return: A tuple describing the python version meetadata contained.
|
||||
:rtype: tuple
|
||||
"""
|
||||
|
||||
return (
|
||||
self.major,
|
||||
self.minor,
|
||||
self.patch,
|
||||
self.is_prerelease,
|
||||
self.is_devrelease,
|
||||
self.is_debug,
|
||||
)
|
||||
|
||||
def matches(
|
||||
self,
|
||||
major=None, # type: Optional[int]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=False, # type: bool
|
||||
dev=False, # type: bool
|
||||
arch=None, # type: Optional[str]
|
||||
debug=False, # type: bool
|
||||
python_name=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> bool
|
||||
result = False
|
||||
if arch:
|
||||
own_arch = self.get_architecture()
|
||||
if arch.isdigit():
|
||||
arch = "{0}bit".format(arch)
|
||||
if (
|
||||
(major is None or self.major and self.major == major)
|
||||
and (minor is None or self.minor and self.minor == minor)
|
||||
and (patch is None or self.patch and self.patch == patch)
|
||||
and (pre is None or self.is_prerelease == pre)
|
||||
and (dev is None or self.is_devrelease == dev)
|
||||
and (arch is None or own_arch == arch)
|
||||
and (debug is None or self.is_debug == debug)
|
||||
and (
|
||||
python_name is None
|
||||
or (python_name and self.name)
|
||||
and (self.name == python_name or self.name.startswith(python_name))
|
||||
)
|
||||
):
|
||||
result = True
|
||||
return result
|
||||
|
||||
def as_major(self):
|
||||
# type: () -> PythonVersion
|
||||
self_dict = attr.asdict(self, recurse=False, filter=_filter_none).copy()
|
||||
self_dict.update({"minor": None, "patch": None})
|
||||
return self.create(**self_dict)
|
||||
|
||||
def as_minor(self):
|
||||
# type: () -> PythonVersion
|
||||
self_dict = attr.asdict(self, recurse=False, filter=_filter_none).copy()
|
||||
self_dict.update({"patch": None})
|
||||
return self.create(**self_dict)
|
||||
|
||||
def as_dict(self):
|
||||
# type: () -> Dict[str, Union[int, bool, Version, None]]
|
||||
return {
|
||||
"major": self.major,
|
||||
"minor": self.minor,
|
||||
"patch": self.patch,
|
||||
"is_prerelease": self.is_prerelease,
|
||||
"is_postrelease": self.is_postrelease,
|
||||
"is_devrelease": self.is_devrelease,
|
||||
"is_debug": self.is_debug,
|
||||
"version": self.version,
|
||||
"company": self.company,
|
||||
}
|
||||
|
||||
def update_metadata(self, metadata):
|
||||
# type: (Dict[str, Union[str, int, Version]]) -> None
|
||||
"""
|
||||
Update the metadata on the current :class:`pythonfinder.models.python.PythonVersion`
|
||||
|
||||
Given a parsed version dictionary from :func:`pythonfinder.utils.parse_python_version`,
|
||||
update the instance variables of the current version instance to reflect the newly
|
||||
supplied values.
|
||||
"""
|
||||
|
||||
for key in metadata:
|
||||
try:
|
||||
_ = getattr(self, key)
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
setattr(self, key, metadata[key])
|
||||
|
||||
@classmethod
|
||||
@lru_cache(maxsize=1024)
|
||||
def parse(cls, version):
|
||||
# type: (str) -> Dict[str, Union[str, int, Version]]
|
||||
"""
|
||||
Parse a valid version string into a dictionary
|
||||
|
||||
Raises:
|
||||
ValueError -- Unable to parse version string
|
||||
ValueError -- Not a valid python version
|
||||
TypeError -- NoneType or unparseable type passed in
|
||||
|
||||
:param str version: A valid version string
|
||||
:return: A dictionary with metadata about the specified python version.
|
||||
:rtype: dict
|
||||
"""
|
||||
|
||||
if version is None:
|
||||
raise TypeError("Must pass a value to parse!")
|
||||
version_dict = parse_python_version(str(version))
|
||||
if not version_dict:
|
||||
raise ValueError("Not a valid python version: %r" % version)
|
||||
return version_dict
|
||||
|
||||
def get_architecture(self):
|
||||
# type: () -> str
|
||||
if self.architecture:
|
||||
return self.architecture
|
||||
arch = None
|
||||
if self.comes_from is not None:
|
||||
arch, _ = platform.architecture(self.comes_from.path.as_posix())
|
||||
elif self.executable is not None:
|
||||
arch, _ = platform.architecture(self.executable)
|
||||
if arch is None:
|
||||
arch, _ = platform.architecture(sys.executable)
|
||||
self.architecture = arch
|
||||
return self.architecture
|
||||
|
||||
@classmethod
|
||||
def from_path(cls, path, name=None, ignore_unsupported=True, company=None):
|
||||
# type: (Union[str, PathEntry], Optional[str], bool, Optional[str]) -> PythonVersion
|
||||
"""
|
||||
Parses a python version from a system path.
|
||||
|
||||
Raises:
|
||||
ValueError -- Not a valid python path
|
||||
|
||||
:param path: A string or :class:`~pythonfinder.models.path.PathEntry`
|
||||
:type path: str or :class:`~pythonfinder.models.path.PathEntry` instance
|
||||
:param str name: Name of the python distribution in question
|
||||
:param bool ignore_unsupported: Whether to ignore or error on unsupported paths.
|
||||
:param Optional[str] company: The company or vendor packaging the distribution.
|
||||
:return: An instance of a PythonVersion.
|
||||
:rtype: :class:`~pythonfinder.models.python.PythonVersion`
|
||||
"""
|
||||
|
||||
from .path import PathEntry
|
||||
|
||||
if not isinstance(path, PathEntry):
|
||||
path = PathEntry.create(path, is_root=False, only_python=True, name=name)
|
||||
from ..environment import IGNORE_UNSUPPORTED
|
||||
|
||||
ignore_unsupported = ignore_unsupported or IGNORE_UNSUPPORTED
|
||||
path_name = getattr(path, "name", path.path.name) # str
|
||||
if not path.is_python:
|
||||
if not (ignore_unsupported or IGNORE_UNSUPPORTED):
|
||||
raise ValueError("Not a valid python path: %s" % path.path)
|
||||
try:
|
||||
instance_dict = cls.parse(path_name)
|
||||
except Exception:
|
||||
instance_dict = cls.parse_executable(path.path.absolute().as_posix())
|
||||
else:
|
||||
if instance_dict.get("minor") is None and looks_like_python(path.path.name):
|
||||
instance_dict = cls.parse_executable(path.path.absolute().as_posix())
|
||||
|
||||
if (
|
||||
not isinstance(instance_dict.get("version"), Version)
|
||||
and not ignore_unsupported
|
||||
):
|
||||
raise ValueError("Not a valid python path: %s" % path)
|
||||
if instance_dict.get("patch") is None:
|
||||
instance_dict = cls.parse_executable(path.path.absolute().as_posix())
|
||||
if name is None:
|
||||
name = path_name
|
||||
if company is None:
|
||||
company = guess_company(path.path.as_posix())
|
||||
instance_dict.update(
|
||||
{"comes_from": path, "name": name, "executable": path.path.as_posix()}
|
||||
)
|
||||
return cls(**instance_dict) # type: ignore
|
||||
|
||||
@classmethod
|
||||
@lru_cache(maxsize=1024)
|
||||
def parse_executable(cls, path):
|
||||
# type: (str) -> Dict[str, Optional[Union[str, int, Version]]]
|
||||
result_dict = {} # type: Dict[str, Optional[Union[str, int, Version]]]
|
||||
result_version = None # type: Optional[str]
|
||||
if path is None:
|
||||
raise TypeError("Must pass a valid path to parse.")
|
||||
if not isinstance(path, six.string_types):
|
||||
path = path.as_posix()
|
||||
# if not looks_like_python(path):
|
||||
# raise ValueError("Path %r does not look like a valid python path" % path)
|
||||
try:
|
||||
result_version = get_python_version(path)
|
||||
except Exception:
|
||||
raise ValueError("Not a valid python path: %r" % path)
|
||||
if result_version is None:
|
||||
raise ValueError("Not a valid python path: %s" % path)
|
||||
result_dict = cls.parse(result_version.strip())
|
||||
return result_dict
|
||||
|
||||
@classmethod
|
||||
def from_windows_launcher(cls, launcher_entry, name=None, company=None):
|
||||
# type: (Environment, Optional[str], Optional[str]) -> PythonVersion
|
||||
"""Create a new PythonVersion instance from a Windows Launcher Entry
|
||||
|
||||
:param launcher_entry: A python launcher environment object.
|
||||
:param Optional[str] name: The name of the distribution.
|
||||
:param Optional[str] company: The name of the distributing company.
|
||||
:return: An instance of a PythonVersion.
|
||||
:rtype: :class:`~pythonfinder.models.python.PythonVersion`
|
||||
"""
|
||||
|
||||
from .path import PathEntry
|
||||
|
||||
creation_dict = cls.parse(launcher_entry.info.version)
|
||||
base_path = ensure_path(launcher_entry.info.install_path.__getattr__(""))
|
||||
default_path = base_path / "python.exe"
|
||||
if not default_path.exists():
|
||||
default_path = base_path / "Scripts" / "python.exe"
|
||||
exe_path = ensure_path(
|
||||
getattr(launcher_entry.info.install_path, "executable_path", default_path)
|
||||
)
|
||||
company = getattr(launcher_entry, "company", guess_company(exe_path.as_posix()))
|
||||
creation_dict.update(
|
||||
{
|
||||
"architecture": getattr(
|
||||
launcher_entry.info, "sys_architecture", SYSTEM_ARCH
|
||||
),
|
||||
"executable": exe_path,
|
||||
"name": name,
|
||||
"company": company,
|
||||
}
|
||||
)
|
||||
py_version = cls.create(**creation_dict)
|
||||
comes_from = PathEntry.create(exe_path, only_python=True, name=name)
|
||||
py_version.comes_from = comes_from
|
||||
py_version.name = comes_from.name
|
||||
return py_version
|
||||
|
||||
@classmethod
|
||||
def create(cls, **kwargs):
|
||||
# type: (...) -> PythonVersion
|
||||
if "architecture" in kwargs:
|
||||
if kwargs["architecture"].isdigit():
|
||||
kwargs["architecture"] = "{0}bit".format(kwargs["architecture"])
|
||||
return cls(**kwargs)
|
||||
|
||||
|
||||
@attr.s
|
||||
class VersionMap(object):
|
||||
versions = attr.ib(
|
||||
factory=defaultdict
|
||||
) # type: DefaultDict[Tuple[int, Optional[int], Optional[int], bool, bool, bool], List[PathEntry]]
|
||||
|
||||
def add_entry(self, entry):
|
||||
# type: (...) -> None
|
||||
version = entry.as_python # type: PythonVersion
|
||||
if version:
|
||||
_ = self.versions[version.version_tuple]
|
||||
paths = {p.path for p in self.versions.get(version.version_tuple, [])}
|
||||
if entry.path not in paths:
|
||||
self.versions[version.version_tuple].append(entry)
|
||||
|
||||
def merge(self, target):
|
||||
# type: (VersionMap) -> None
|
||||
for version, entries in target.versions.items():
|
||||
if version not in self.versions:
|
||||
self.versions[version] = entries
|
||||
else:
|
||||
current_entries = {
|
||||
p.path
|
||||
for p in self.versions[version] # type: ignore
|
||||
if version in self.versions
|
||||
}
|
||||
new_entries = {p.path for p in entries}
|
||||
new_entries -= current_entries
|
||||
self.versions[version].extend(
|
||||
[e for e in entries if e.path in new_entries]
|
||||
)
|
||||
146
Lib/site-packages/pipenv/vendor/pythonfinder/models/windows.py
vendored
Normal file
146
Lib/site-packages/pipenv/vendor/pythonfinder/models/windows.py
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import operator
|
||||
from collections import defaultdict
|
||||
|
||||
from pipenv.vendor import attr
|
||||
|
||||
from ..environment import MYPY_RUNNING
|
||||
from ..exceptions import InvalidPythonVersion
|
||||
from ..utils import ensure_path
|
||||
from .mixins import BaseFinder
|
||||
from .path import PathEntry
|
||||
from .python import PythonVersion, VersionMap
|
||||
|
||||
if MYPY_RUNNING:
|
||||
from typing import DefaultDict, Tuple, List, Optional, Union, TypeVar, Type, Any
|
||||
|
||||
FinderType = TypeVar("FinderType")
|
||||
|
||||
|
||||
@attr.s
|
||||
class WindowsFinder(BaseFinder):
|
||||
paths = attr.ib(default=attr.Factory(list), type=list)
|
||||
version_list = attr.ib(default=attr.Factory(list), type=list)
|
||||
_versions = attr.ib() # type: DefaultDict[Tuple, PathEntry]
|
||||
_pythons = attr.ib() # type: DefaultDict[str, PathEntry]
|
||||
|
||||
def find_all_python_versions(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type (...) -> List[PathEntry]
|
||||
version_matcher = operator.methodcaller(
|
||||
"matches", major, minor, patch, pre, dev, arch, python_name=name
|
||||
)
|
||||
pythons = [py for py in self.version_list if version_matcher(py)]
|
||||
version_sort = operator.attrgetter("version_sort")
|
||||
return [
|
||||
c.comes_from for c in sorted(pythons, key=version_sort, reverse=True)
|
||||
if c.comes_from
|
||||
]
|
||||
|
||||
def find_python_version(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> Optional[PathEntry]
|
||||
return next(
|
||||
iter(
|
||||
v
|
||||
for v in self.find_all_python_versions(
|
||||
major=major,
|
||||
minor=minor,
|
||||
patch=patch,
|
||||
pre=pre,
|
||||
dev=dev,
|
||||
arch=arch,
|
||||
name=name,
|
||||
)
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
@_versions.default
|
||||
def get_versions(self):
|
||||
# type: () -> DefaultDict[Tuple, PathEntry]
|
||||
versions = defaultdict(PathEntry) # type: DefaultDict[Tuple, PathEntry]
|
||||
from pythonfinder._vendor.pep514tools import environment as pep514env
|
||||
|
||||
env_versions = pep514env.findall()
|
||||
path = None
|
||||
for version_object in env_versions:
|
||||
install_path = getattr(version_object.info, "install_path", None)
|
||||
name = getattr(version_object, "tag", None)
|
||||
company = getattr(version_object, "company", None)
|
||||
if install_path is None:
|
||||
continue
|
||||
try:
|
||||
path = ensure_path(install_path.__getattr__(""))
|
||||
except AttributeError:
|
||||
continue
|
||||
try:
|
||||
py_version = PythonVersion.from_windows_launcher(
|
||||
version_object, name=name, company=company
|
||||
)
|
||||
except InvalidPythonVersion:
|
||||
continue
|
||||
if py_version is None:
|
||||
continue
|
||||
self.version_list.append(py_version)
|
||||
python_path = (
|
||||
py_version.comes_from.path
|
||||
if py_version.comes_from
|
||||
else py_version.executable
|
||||
)
|
||||
python_kwargs = {python_path: py_version} if python_path is not None else {}
|
||||
base_dir = PathEntry.create(
|
||||
path, is_root=True, only_python=True, pythons=python_kwargs
|
||||
)
|
||||
versions[py_version.version_tuple[:5]] = base_dir
|
||||
self.paths.append(base_dir)
|
||||
return versions
|
||||
|
||||
@property
|
||||
def versions(self):
|
||||
# type: () -> DefaultDict[Tuple, PathEntry]
|
||||
if not self._versions:
|
||||
self._versions = self.get_versions()
|
||||
return self._versions
|
||||
|
||||
@_pythons.default
|
||||
def get_pythons(self):
|
||||
# type: () -> DefaultDict[str, PathEntry]
|
||||
pythons = defaultdict() # type: DefaultDict[str, PathEntry]
|
||||
for version in self.version_list:
|
||||
_path = ensure_path(version.comes_from.path)
|
||||
pythons[_path.as_posix()] = version.comes_from
|
||||
return pythons
|
||||
|
||||
@property
|
||||
def pythons(self):
|
||||
# type: () -> DefaultDict[str, PathEntry]
|
||||
return self._pythons
|
||||
|
||||
@pythons.setter
|
||||
def pythons(self, value):
|
||||
# type: (DefaultDict[str, PathEntry]) -> None
|
||||
self._pythons = value
|
||||
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs):
|
||||
# type: (Type[FinderType], Any, Any) -> FinderType
|
||||
return cls()
|
||||
21
Lib/site-packages/pipenv/vendor/pythonfinder/pep514tools.LICENSE
vendored
Normal file
21
Lib/site-packages/pipenv/vendor/pythonfinder/pep514tools.LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Steve Dower
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
337
Lib/site-packages/pipenv/vendor/pythonfinder/pythonfinder.py
vendored
Normal file
337
Lib/site-packages/pipenv/vendor/pythonfinder/pythonfinder.py
vendored
Normal file
@@ -0,0 +1,337 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import importlib
|
||||
import operator
|
||||
import os
|
||||
|
||||
import six
|
||||
from click import secho
|
||||
|
||||
from . import environment
|
||||
from .compat import lru_cache
|
||||
from .exceptions import InvalidPythonVersion
|
||||
from .utils import Iterable, filter_pythons, version_re
|
||||
|
||||
if environment.MYPY_RUNNING:
|
||||
from typing import Optional, Dict, Any, Union, List, Iterator, Text
|
||||
from .models.path import Path, PathEntry
|
||||
from .models.windows import WindowsFinder
|
||||
from .models.path import SystemPath
|
||||
|
||||
STRING_TYPE = Union[str, Text, bytes]
|
||||
|
||||
|
||||
class Finder(object):
|
||||
|
||||
"""
|
||||
A cross-platform Finder for locating python and other executables.
|
||||
|
||||
Searches for python and other specified binaries starting in *path*, if supplied,
|
||||
but searching the bin path of ``sys.executable`` if *system* is ``True``, and then
|
||||
searching in the ``os.environ['PATH']`` if *global_search* is ``True``. When *global_search*
|
||||
is ``False``, this search operation is restricted to the allowed locations of
|
||||
*path* and *system*.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
path=None,
|
||||
system=False,
|
||||
global_search=True,
|
||||
ignore_unsupported=True,
|
||||
sort_by_path=False,
|
||||
):
|
||||
# type: (Optional[str], bool, bool, bool, bool) -> None
|
||||
"""Create a new :class:`~pythonfinder.pythonfinder.Finder` instance.
|
||||
|
||||
:param path: A bin-directory search location, defaults to None
|
||||
:param path: str, optional
|
||||
:param system: Whether to include the bin-dir of ``sys.executable``, defaults to False
|
||||
:param system: bool, optional
|
||||
:param global_search: Whether to search the global path from os.environ, defaults to True
|
||||
:param global_search: bool, optional
|
||||
:param ignore_unsupported: Whether to ignore unsupported python versions, if False, an
|
||||
error is raised, defaults to True
|
||||
:param ignore_unsupported: bool, optional
|
||||
:param bool sort_by_path: Whether to always sort by path
|
||||
:returns: a :class:`~pythonfinder.pythonfinder.Finder` object.
|
||||
"""
|
||||
|
||||
self.path_prepend = path # type: Optional[str]
|
||||
self.global_search = global_search # type: bool
|
||||
self.system = system # type: bool
|
||||
self.sort_by_path = sort_by_path # type: bool
|
||||
self.ignore_unsupported = ignore_unsupported # type: bool
|
||||
self._system_path = None # type: Optional[SystemPath]
|
||||
self._windows_finder = None # type: Optional[WindowsFinder]
|
||||
|
||||
def __hash__(self):
|
||||
# type: () -> int
|
||||
return hash(
|
||||
(self.path_prepend, self.system, self.global_search, self.ignore_unsupported)
|
||||
)
|
||||
|
||||
def __eq__(self, other):
|
||||
# type: (Any) -> bool
|
||||
return self.__hash__() == other.__hash__()
|
||||
|
||||
def create_system_path(self):
|
||||
# type: () -> SystemPath
|
||||
pyfinder_path = importlib.import_module("pythonfinder.models.path")
|
||||
return pyfinder_path.SystemPath.create(
|
||||
path=self.path_prepend,
|
||||
system=self.system,
|
||||
global_search=self.global_search,
|
||||
ignore_unsupported=self.ignore_unsupported,
|
||||
)
|
||||
|
||||
def reload_system_path(self):
|
||||
# type: () -> None
|
||||
"""
|
||||
Rebuilds the base system path and all of the contained finders within it.
|
||||
|
||||
This will re-apply any changes to the environment or any version changes on the system.
|
||||
"""
|
||||
|
||||
if self._system_path is not None:
|
||||
self._system_path = self._system_path.clear_caches()
|
||||
self._system_path = None
|
||||
pyfinder_path = importlib.import_module("pythonfinder.models.path")
|
||||
six.moves.reload_module(pyfinder_path)
|
||||
self._system_path = self.create_system_path()
|
||||
|
||||
def rehash(self):
|
||||
# type: () -> "Finder"
|
||||
if not self._system_path:
|
||||
self._system_path = self.create_system_path()
|
||||
self.find_all_python_versions.cache_clear()
|
||||
self.find_python_version.cache_clear()
|
||||
if self._windows_finder is not None:
|
||||
self._windows_finder = None
|
||||
filter_pythons.cache_clear()
|
||||
self.reload_system_path()
|
||||
return self
|
||||
|
||||
@property
|
||||
def system_path(self):
|
||||
# type: () -> SystemPath
|
||||
if self._system_path is None:
|
||||
self._system_path = self.create_system_path()
|
||||
return self._system_path
|
||||
|
||||
@property
|
||||
def windows_finder(self):
|
||||
# type: () -> Optional[WindowsFinder]
|
||||
if os.name == "nt" and not self._windows_finder:
|
||||
from .models import WindowsFinder
|
||||
|
||||
self._windows_finder = WindowsFinder()
|
||||
return self._windows_finder
|
||||
|
||||
def which(self, exe):
|
||||
# type: (str) -> Optional[PathEntry]
|
||||
return self.system_path.which(exe)
|
||||
|
||||
@classmethod
|
||||
def parse_major(
|
||||
cls,
|
||||
major, # type: Optional[str]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> Dict[str, Union[int, str, bool, None]]
|
||||
from .models import PythonVersion
|
||||
|
||||
major_is_str = major and isinstance(major, six.string_types)
|
||||
is_num = (
|
||||
major
|
||||
and major_is_str
|
||||
and all(part.isdigit() for part in major.split(".")[:2])
|
||||
)
|
||||
major_has_arch = (
|
||||
arch is None
|
||||
and major
|
||||
and major_is_str
|
||||
and "-" in major
|
||||
and major[0].isdigit()
|
||||
)
|
||||
name = None
|
||||
if major and major_has_arch:
|
||||
orig_string = "{0!s}".format(major)
|
||||
major, _, arch = major.rpartition("-")
|
||||
if arch:
|
||||
arch = arch.lower().lstrip("x").replace("bit", "")
|
||||
if not (arch.isdigit() and (int(arch) & int(arch) - 1) == 0):
|
||||
major = orig_string
|
||||
arch = None
|
||||
else:
|
||||
arch = "{0}bit".format(arch)
|
||||
try:
|
||||
version_dict = PythonVersion.parse(major)
|
||||
except (ValueError, InvalidPythonVersion):
|
||||
if name is None:
|
||||
name = "{0!s}".format(major)
|
||||
major = None
|
||||
version_dict = {}
|
||||
elif major and major[0].isalpha():
|
||||
return {"major": None, "name": major, "arch": arch}
|
||||
elif major and is_num:
|
||||
match = version_re.match(major)
|
||||
version_dict = match.groupdict() if match else {} # type: ignore
|
||||
version_dict.update(
|
||||
{
|
||||
"is_prerelease": bool(version_dict.get("prerel", False)),
|
||||
"is_devrelease": bool(version_dict.get("dev", False)),
|
||||
}
|
||||
)
|
||||
else:
|
||||
version_dict = {
|
||||
"major": major,
|
||||
"minor": minor,
|
||||
"patch": patch,
|
||||
"pre": pre,
|
||||
"dev": dev,
|
||||
"arch": arch,
|
||||
}
|
||||
if not version_dict.get("arch") and arch:
|
||||
version_dict["arch"] = arch
|
||||
version_dict["minor"] = (
|
||||
int(version_dict["minor"]) if version_dict.get("minor") is not None else minor
|
||||
)
|
||||
version_dict["patch"] = (
|
||||
int(version_dict["patch"]) if version_dict.get("patch") is not None else patch
|
||||
)
|
||||
version_dict["major"] = (
|
||||
int(version_dict["major"]) if version_dict.get("major") is not None else major
|
||||
)
|
||||
if not (version_dict["major"] or version_dict.get("name")):
|
||||
version_dict["major"] = major
|
||||
if name:
|
||||
version_dict["name"] = name
|
||||
return version_dict
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def find_python_version(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
sort_by_path=False, # type: bool
|
||||
):
|
||||
# type: (...) -> Optional[PathEntry]
|
||||
"""
|
||||
Find the python version which corresponds most closely to the version requested.
|
||||
|
||||
:param Union[str, int] major: The major version to look for, or the full version, or the name of the target version.
|
||||
:param Optional[int] minor: The minor version. If provided, disables string-based lookups from the major version field.
|
||||
:param Optional[int] patch: The patch version.
|
||||
:param Optional[bool] pre: If provided, specifies whether to search pre-releases.
|
||||
:param Optional[bool] dev: If provided, whether to search dev-releases.
|
||||
:param Optional[str] arch: If provided, which architecture to search.
|
||||
:param Optional[str] name: *Name* of the target python, e.g. ``anaconda3-5.3.0``
|
||||
:param bool sort_by_path: Whether to sort by path -- default sort is by version(default: False)
|
||||
:return: A new *PathEntry* pointer at a matching python version, if one can be located.
|
||||
:rtype: :class:`pythonfinder.models.path.PathEntry`
|
||||
"""
|
||||
|
||||
minor = int(minor) if minor is not None else minor
|
||||
patch = int(patch) if patch is not None else patch
|
||||
|
||||
version_dict = {
|
||||
"minor": minor,
|
||||
"patch": patch,
|
||||
"name": name,
|
||||
"arch": arch,
|
||||
} # type: Dict[str, Union[str, int, Any]]
|
||||
|
||||
if (
|
||||
isinstance(major, six.string_types)
|
||||
and pre is None
|
||||
and minor is None
|
||||
and dev is None
|
||||
and patch is None
|
||||
):
|
||||
version_dict = self.parse_major(major, minor=minor, patch=patch, arch=arch)
|
||||
major = version_dict["major"]
|
||||
minor = version_dict.get("minor", minor) # type: ignore
|
||||
patch = version_dict.get("patch", patch) # type: ignore
|
||||
arch = version_dict.get("arch", arch) # type: ignore
|
||||
name = version_dict.get("name", name) # type: ignore
|
||||
_pre = version_dict.get("is_prerelease", pre)
|
||||
pre = bool(_pre) if _pre is not None else pre
|
||||
_dev = version_dict.get("is_devrelease", dev)
|
||||
dev = bool(_dev) if _dev is not None else dev
|
||||
if "architecture" in version_dict and isinstance(
|
||||
version_dict["architecture"], six.string_types
|
||||
):
|
||||
arch = version_dict["architecture"] # type: ignore
|
||||
if os.name == "nt" and self.windows_finder is not None:
|
||||
found = self.windows_finder.find_python_version(
|
||||
major=major,
|
||||
minor=minor,
|
||||
patch=patch,
|
||||
pre=pre,
|
||||
dev=dev,
|
||||
arch=arch,
|
||||
name=name,
|
||||
)
|
||||
if found:
|
||||
return found
|
||||
return self.system_path.find_python_version(
|
||||
major=major,
|
||||
minor=minor,
|
||||
patch=patch,
|
||||
pre=pre,
|
||||
dev=dev,
|
||||
arch=arch,
|
||||
name=name,
|
||||
sort_by_path=self.sort_by_path,
|
||||
)
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def find_all_python_versions(
|
||||
self,
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[int]
|
||||
patch=None, # type: Optional[int]
|
||||
pre=None, # type: Optional[bool]
|
||||
dev=None, # type: Optional[bool]
|
||||
arch=None, # type: Optional[str]
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> List[PathEntry]
|
||||
version_sort = operator.attrgetter("as_python.version_sort")
|
||||
python_version_dict = getattr(self.system_path, "python_version_dict", {})
|
||||
if python_version_dict:
|
||||
paths = (
|
||||
path
|
||||
for version in python_version_dict.values()
|
||||
for path in version
|
||||
if path is not None and path.as_python
|
||||
)
|
||||
path_list = sorted(paths, key=version_sort, reverse=True)
|
||||
return path_list
|
||||
versions = self.system_path.find_all_python_versions(
|
||||
major=major, minor=minor, patch=patch, pre=pre, dev=dev, arch=arch, name=name
|
||||
)
|
||||
if not isinstance(versions, Iterable):
|
||||
versions = [versions]
|
||||
# This list has already been mostly sorted on windows, we don't need to reverse it again
|
||||
path_list = sorted(versions, key=version_sort, reverse=True)
|
||||
path_map = {} # type: Dict[str, PathEntry]
|
||||
for path in path_list:
|
||||
try:
|
||||
resolved_path = path.path.resolve()
|
||||
except OSError:
|
||||
resolved_path = path.path.absolute()
|
||||
if not path_map.get(resolved_path.as_posix()):
|
||||
path_map[resolved_path.as_posix()] = path
|
||||
return path_list
|
||||
451
Lib/site-packages/pipenv/vendor/pythonfinder/utils.py
vendored
Normal file
451
Lib/site-packages/pipenv/vendor/pythonfinder/utils.py
vendored
Normal file
@@ -0,0 +1,451 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import io
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from collections import OrderedDict
|
||||
from fnmatch import fnmatch
|
||||
from threading import Timer
|
||||
|
||||
from pipenv.vendor import attr
|
||||
import six
|
||||
from packaging.version import LegacyVersion, Version
|
||||
|
||||
from .compat import Path, lru_cache, TimeoutError # noqa
|
||||
from .environment import MYPY_RUNNING, PYENV_ROOT, SUBPROCESS_TIMEOUT
|
||||
from .exceptions import InvalidPythonVersion
|
||||
|
||||
six.add_move(
|
||||
six.MovedAttribute("Iterable", "collections", "collections.abc")
|
||||
) # type: ignore # noqa
|
||||
six.add_move(
|
||||
six.MovedAttribute("Sequence", "collections", "collections.abc")
|
||||
) # type: ignore # noqa
|
||||
# fmt: off
|
||||
from six.moves import Iterable # type: ignore # noqa # isort:skip
|
||||
from six.moves import Sequence # type: ignore # noqa # isort:skip
|
||||
# fmt: on
|
||||
|
||||
if MYPY_RUNNING:
|
||||
from typing import Any, Union, List, Callable, Set, Tuple, Dict, Optional, Iterator
|
||||
from attr.validators import _OptionalValidator # type: ignore
|
||||
from .models.path import PathEntry
|
||||
|
||||
|
||||
version_re_str = (
|
||||
r"(?P<major>\d+)(?:\.(?P<minor>\d+))?(?:\.(?P<patch>(?<=\.)[0-9]+))?\.?"
|
||||
r"(?:(?P<prerel>[abc]|rc|dev)(?:(?P<prerelversion>\d+(?:\.\d+)*))?)"
|
||||
r"?(?P<postdev>(\.post(?P<post>\d+))?(\.dev(?P<dev>\d+))?)?"
|
||||
)
|
||||
version_re = re.compile(version_re_str)
|
||||
|
||||
|
||||
PYTHON_IMPLEMENTATIONS = (
|
||||
"python",
|
||||
"ironpython",
|
||||
"jython",
|
||||
"pypy",
|
||||
"anaconda",
|
||||
"miniconda",
|
||||
"stackless",
|
||||
"activepython",
|
||||
"pyston",
|
||||
"micropython",
|
||||
)
|
||||
if os.name == "nt":
|
||||
KNOWN_EXTS = {"exe", "py", "bat", ""}
|
||||
else:
|
||||
KNOWN_EXTS = {"sh", "bash", "csh", "zsh", "fish", "py", ""}
|
||||
KNOWN_EXTS = KNOWN_EXTS | set(
|
||||
filter(None, os.environ.get("PATHEXT", "").split(os.pathsep))
|
||||
)
|
||||
PY_MATCH_STR = r"((?P<implementation>{0})(?:\d?(?:\.\d[cpm]{{0,3}}))?(?:-?[\d\.]+)*[^zw])".format(
|
||||
"|".join(PYTHON_IMPLEMENTATIONS)
|
||||
)
|
||||
EXE_MATCH_STR = r"{0}(?:\.(?P<ext>{1}))?".format(PY_MATCH_STR, "|".join(KNOWN_EXTS))
|
||||
RE_MATCHER = re.compile(r"({0}|{1})".format(version_re_str, PY_MATCH_STR))
|
||||
EXE_MATCHER = re.compile(EXE_MATCH_STR)
|
||||
RULES_BASE = [
|
||||
"*{0}",
|
||||
"*{0}?",
|
||||
"*{0}?.?",
|
||||
"*{0}?.?m",
|
||||
"{0}?-?.?",
|
||||
"{0}?-?.?.?",
|
||||
"{0}?.?-?.?.?",
|
||||
]
|
||||
RULES = [rule.format(impl) for impl in PYTHON_IMPLEMENTATIONS for rule in RULES_BASE]
|
||||
|
||||
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=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('.'.join([str(i) for i in sys.version_info[:3]]))",
|
||||
]
|
||||
subprocess_kwargs = {
|
||||
"env": os.environ.copy(),
|
||||
"universal_newlines": True,
|
||||
"stdout": subprocess.PIPE,
|
||||
"stderr": subprocess.PIPE,
|
||||
"shell": False,
|
||||
}
|
||||
c = subprocess.Popen(version_cmd, **subprocess_kwargs)
|
||||
timer = Timer(SUBPROCESS_TIMEOUT, c.kill)
|
||||
try:
|
||||
out, _ = c.communicate()
|
||||
except (SystemExit, KeyboardInterrupt, TimeoutError):
|
||||
c.terminate()
|
||||
out, _ = c.communicate()
|
||||
raise
|
||||
except OSError:
|
||||
raise InvalidPythonVersion("%s is not a valid python path" % path)
|
||||
if not out:
|
||||
raise InvalidPythonVersion("%s is not a valid python path" % path)
|
||||
return out.strip()
|
||||
|
||||
|
||||
@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_str.rpartition("-")
|
||||
match = version_re.match(version_str)
|
||||
if not match:
|
||||
raise InvalidPythonVersion("%s is not a python version" % version_str)
|
||||
version_dict = match.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 = None
|
||||
if isinstance(version, LegacyVersion) or version is None:
|
||||
v_dict = version_dict.copy()
|
||||
pre = ""
|
||||
if v_dict.get("prerel") and v_dict.get("prerelversion"):
|
||||
pre = v_dict.pop("prerel")
|
||||
pre = "{0}{1}".format(pre, v_dict.pop("prerelversion"))
|
||||
v_dict["pre"] = pre
|
||||
keys = ["major", "minor", "patch", "pre", "postdev", "post", "dev"]
|
||||
values = [v_dict.get(val) for val in keys]
|
||||
version_str = ".".join([str(v) for v in values if v])
|
||||
version = parse_version(version_str)
|
||||
return {
|
||||
"major": major,
|
||||
"minor": minor,
|
||||
"patch": patch,
|
||||
"is_postrelease": is_postrelease,
|
||||
"is_prerelease": is_prerelease,
|
||||
"is_devrelease": is_devrelease,
|
||||
"is_debug": is_debug,
|
||||
"version": version,
|
||||
}
|
||||
|
||||
|
||||
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: (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:`~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)
|
||||
and path.suffix in KNOWN_EXTS
|
||||
)
|
||||
|
||||
|
||||
@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
|
||||
match = RE_MATCHER.match(name)
|
||||
if match:
|
||||
return any(fnmatch(name, rule) for rule in MATCH_RULES)
|
||||
return False
|
||||
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def path_is_python(path):
|
||||
# type: (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:`~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 guess_company(path):
|
||||
# type: (str) -> Optional[str]
|
||||
"""Given a path to python, guess the company who created it
|
||||
|
||||
:param str path: The path to guess about
|
||||
:return: The guessed company
|
||||
:rtype: Optional[str]
|
||||
"""
|
||||
non_core_pythons = [impl for impl in PYTHON_IMPLEMENTATIONS if impl != "python"]
|
||||
return next(
|
||||
iter(impl for impl in non_core_pythons if impl in path.lower()), "PythonCore"
|
||||
)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def path_is_pythoncore(path):
|
||||
# type: (str) -> bool
|
||||
"""Given a path, determine whether it appears to be pythoncore.
|
||||
|
||||
Does not verify whether the path is in fact a path to python, but simply
|
||||
does an exclusionary check on the possible known python implementations
|
||||
to see if their names are present in the path (fairly dumb check).
|
||||
|
||||
:param str path: The path to check
|
||||
:return: Whether that path is a PythonCore path or not
|
||||
:rtype: bool
|
||||
"""
|
||||
company = guess_company(path)
|
||||
if company:
|
||||
return company == "PythonCore"
|
||||
return False
|
||||
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def ensure_path(path):
|
||||
# type: (Union[Path, str]) -> 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, Path):
|
||||
return path
|
||||
path = Path(os.path.expandvars(path))
|
||||
return path.absolute()
|
||||
|
||||
|
||||
def _filter_none(k, v):
|
||||
# type: (Any, Any) -> bool
|
||||
if v:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# 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))))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def filter_pythons(path):
|
||||
# type: (Union[str, Path]) -> Iterable
|
||||
"""Return all valid pythons in a given path"""
|
||||
if not isinstance(path, Path):
|
||||
path = Path(str(path))
|
||||
if not path.is_dir():
|
||||
return path if path_is_python(path) else None
|
||||
return filter(path_is_python, path.iterdir())
|
||||
|
||||
|
||||
# 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:
|
||||
target = item
|
||||
if getattr(target, "__iter__", None):
|
||||
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
|
||||
else:
|
||||
yield target
|
||||
|
||||
|
||||
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:
|
||||
contents = fh.read()
|
||||
version_order = [v for v in contents.splitlines()]
|
||||
return version_order
|
||||
return []
|
||||
|
||||
|
||||
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:
|
||||
contents = fh.read()
|
||||
python_section = next(
|
||||
iter(line for line in contents.splitlines() if line.startswith("python")),
|
||||
None,
|
||||
)
|
||||
if python_section:
|
||||
# python_key, _, versions
|
||||
_, _, versions = python_section.partition(" ")
|
||||
if versions:
|
||||
return versions.split()
|
||||
return []
|
||||
|
||||
|
||||
def split_version_and_name(
|
||||
major=None, # type: Optional[Union[str, int]]
|
||||
minor=None, # type: Optional[Union[str, int]]
|
||||
patch=None, # type: Optional[Union[str, int]]
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> Tuple[Optional[Union[str, int]], Optional[Union[str, int]], Optional[Union[str, int]], Optional[str]] # noqa
|
||||
if isinstance(major, six.string_types) and not minor and not patch:
|
||||
# Only proceed if this is in the format "x.y.z" or similar
|
||||
if major.isdigit() or (major.count(".") > 0 and major[0].isdigit()):
|
||||
version = major.split(".", 2)
|
||||
if isinstance(version, (tuple, list)):
|
||||
if len(version) > 3:
|
||||
major, minor, patch, _ = version
|
||||
elif len(version) == 3:
|
||||
major, minor, patch = version
|
||||
elif len(version) == 2:
|
||||
major, minor = version
|
||||
else:
|
||||
major = major[0]
|
||||
else:
|
||||
major = major
|
||||
name = None
|
||||
else:
|
||||
name = "{0!s}".format(major)
|
||||
major = None
|
||||
return (major, minor, patch, name)
|
||||
|
||||
|
||||
# TODO: Reimplement in vistir
|
||||
def is_in_path(path, parent):
|
||||
return normalize_path(str(path)).startswith(normalize_path(str(parent)))
|
||||
|
||||
|
||||
def expand_paths(path, only_python=True):
|
||||
# type: (Union[Sequence, PathEntry], bool) -> Iterator
|
||||
"""
|
||||
Recursively expand a list or :class:`~pythonfinder.models.path.PathEntry` instance
|
||||
|
||||
:param Union[Sequence, PathEntry] path: The path or list of paths to expand
|
||||
:param bool only_python: Whether to filter to include only python paths, default True
|
||||
:returns: An iterator over the expanded set of path entries
|
||||
:rtype: Iterator[PathEntry]
|
||||
"""
|
||||
|
||||
if path is not None and (
|
||||
isinstance(path, Sequence)
|
||||
and not getattr(path.__class__, "__name__", "") == "PathEntry"
|
||||
):
|
||||
for p in path:
|
||||
if p is None:
|
||||
continue
|
||||
for expanded in itertools.chain.from_iterable(
|
||||
expand_paths(p, only_python=only_python)
|
||||
):
|
||||
yield expanded
|
||||
elif path is not None and path.is_dir:
|
||||
for p in path.children.values():
|
||||
if p is not None and p.is_python and p.as_python is not None:
|
||||
for sub_path in itertools.chain.from_iterable(
|
||||
expand_paths(p, only_python=only_python)
|
||||
):
|
||||
yield sub_path
|
||||
else:
|
||||
if path is not None and (
|
||||
not only_python or (path.is_python and path.as_python is not None)
|
||||
):
|
||||
yield path
|
||||
|
||||
|
||||
def dedup(iterable):
|
||||
# type: (Iterable) -> Iterable
|
||||
"""Deduplicate an iterable object like iter(set(iterable)) but
|
||||
order-reserved.
|
||||
"""
|
||||
return iter(OrderedDict.fromkeys(iterable))
|
||||
Reference in New Issue
Block a user