8th day of python challenges 111-117
This commit is contained in:
28
venv/lib/python3.6/site-packages/isort/__init__.py
Normal file
28
venv/lib/python3.6/site-packages/isort/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
||||
"""__init__.py.
|
||||
|
||||
Defines the isort module to include the SortImports utility class as well as any defined settings.
|
||||
|
||||
Copyright (C) 2013 Timothy Edmund Crosley
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from . import settings # noqa: F401
|
||||
from .isort import SortImports # noqa: F401
|
||||
|
||||
__version__ = "4.3.21"
|
||||
9
venv/lib/python3.6/site-packages/isort/__main__.py
Normal file
9
venv/lib/python3.6/site-packages/isort/__main__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from isort.pie_slice import apply_changes_to_python_environment
|
||||
|
||||
apply_changes_to_python_environment()
|
||||
|
||||
from isort.main import main # noqa: E402 isort:skip
|
||||
|
||||
main()
|
||||
382
venv/lib/python3.6/site-packages/isort/finders.py
Normal file
382
venv/lib/python3.6/site-packages/isort/finders.py
Normal file
@@ -0,0 +1,382 @@
|
||||
"""Finders try to find right section for passed module name
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
import sysconfig
|
||||
from fnmatch import fnmatch
|
||||
from glob import glob
|
||||
|
||||
from .pie_slice import PY2
|
||||
from .utils import chdir, exists_case_sensitive
|
||||
|
||||
try:
|
||||
from pipreqs import pipreqs
|
||||
except ImportError:
|
||||
pipreqs = None
|
||||
|
||||
try:
|
||||
from pip_api import parse_requirements
|
||||
except ImportError:
|
||||
parse_requirements = None
|
||||
|
||||
try:
|
||||
from requirementslib import Pipfile
|
||||
except ImportError:
|
||||
Pipfile = None
|
||||
|
||||
try:
|
||||
from functools import lru_cache
|
||||
except ImportError:
|
||||
from backports.functools_lru_cache import lru_cache
|
||||
|
||||
|
||||
KNOWN_SECTION_MAPPING = {
|
||||
'STDLIB': 'STANDARD_LIBRARY',
|
||||
'FUTURE': 'FUTURE_LIBRARY',
|
||||
'FIRSTPARTY': 'FIRST_PARTY',
|
||||
'THIRDPARTY': 'THIRD_PARTY',
|
||||
}
|
||||
|
||||
|
||||
class BaseFinder(object):
|
||||
def __init__(self, config, sections):
|
||||
self.config = config
|
||||
self.sections = sections
|
||||
|
||||
|
||||
class ForcedSeparateFinder(BaseFinder):
|
||||
def find(self, module_name):
|
||||
for forced_separate in self.config['forced_separate']:
|
||||
# Ensure all forced_separate patterns will match to end of string
|
||||
path_glob = forced_separate
|
||||
if not forced_separate.endswith('*'):
|
||||
path_glob = '%s*' % forced_separate
|
||||
|
||||
if fnmatch(module_name, path_glob) or fnmatch(module_name, '.' + path_glob):
|
||||
return forced_separate
|
||||
|
||||
|
||||
class LocalFinder(BaseFinder):
|
||||
def find(self, module_name):
|
||||
if module_name.startswith("."):
|
||||
return self.sections.LOCALFOLDER
|
||||
|
||||
|
||||
class KnownPatternFinder(BaseFinder):
|
||||
def __init__(self, config, sections):
|
||||
super(KnownPatternFinder, self).__init__(config, sections)
|
||||
|
||||
self.known_patterns = []
|
||||
for placement in reversed(self.sections):
|
||||
known_placement = KNOWN_SECTION_MAPPING.get(placement, placement)
|
||||
config_key = 'known_{0}'.format(known_placement.lower())
|
||||
known_patterns = self.config.get(config_key, [])
|
||||
known_patterns = [
|
||||
pattern
|
||||
for known_pattern in known_patterns
|
||||
for pattern in self._parse_known_pattern(known_pattern)
|
||||
]
|
||||
for known_pattern in known_patterns:
|
||||
regexp = '^' + known_pattern.replace('*', '.*').replace('?', '.?') + '$'
|
||||
self.known_patterns.append((re.compile(regexp), placement))
|
||||
|
||||
@staticmethod
|
||||
def _is_package(path):
|
||||
"""
|
||||
Evaluates if path is a python package
|
||||
"""
|
||||
if PY2:
|
||||
return os.path.exists(os.path.join(path, '__init__.py'))
|
||||
else:
|
||||
return os.path.isdir(path)
|
||||
|
||||
def _parse_known_pattern(self, pattern):
|
||||
"""
|
||||
Expand pattern if identified as a directory and return found sub packages
|
||||
"""
|
||||
if pattern.endswith(os.path.sep):
|
||||
patterns = [
|
||||
filename
|
||||
for filename in os.listdir(pattern)
|
||||
if self._is_package(os.path.join(pattern, filename))
|
||||
]
|
||||
else:
|
||||
patterns = [pattern]
|
||||
|
||||
return patterns
|
||||
|
||||
def find(self, module_name):
|
||||
# Try to find most specific placement instruction match (if any)
|
||||
parts = module_name.split('.')
|
||||
module_names_to_check = ('.'.join(parts[:first_k]) for first_k in range(len(parts), 0, -1))
|
||||
for module_name_to_check in module_names_to_check:
|
||||
for pattern, placement in self.known_patterns:
|
||||
if pattern.match(module_name_to_check):
|
||||
return placement
|
||||
|
||||
|
||||
class PathFinder(BaseFinder):
|
||||
def __init__(self, config, sections):
|
||||
super(PathFinder, self).__init__(config, sections)
|
||||
|
||||
# restore the original import path (i.e. not the path to bin/isort)
|
||||
self.paths = [os.getcwd()]
|
||||
|
||||
# virtual env
|
||||
self.virtual_env = self.config.get('virtual_env') or os.environ.get('VIRTUAL_ENV')
|
||||
if self.virtual_env:
|
||||
self.virtual_env = os.path.realpath(self.virtual_env)
|
||||
self.virtual_env_src = False
|
||||
if self.virtual_env:
|
||||
self.virtual_env_src = '{0}/src/'.format(self.virtual_env)
|
||||
for path in glob('{0}/lib/python*/site-packages'.format(self.virtual_env)):
|
||||
if path not in self.paths:
|
||||
self.paths.append(path)
|
||||
for path in glob('{0}/lib/python*/*/site-packages'.format(self.virtual_env)):
|
||||
if path not in self.paths:
|
||||
self.paths.append(path)
|
||||
for path in glob('{0}/src/*'.format(self.virtual_env)):
|
||||
if os.path.isdir(path):
|
||||
self.paths.append(path)
|
||||
|
||||
# conda
|
||||
self.conda_env = self.config.get('conda_env') or os.environ.get('CONDA_PREFIX')
|
||||
if self.conda_env:
|
||||
self.conda_env = os.path.realpath(self.conda_env)
|
||||
for path in glob('{0}/lib/python*/site-packages'.format(self.conda_env)):
|
||||
if path not in self.paths:
|
||||
self.paths.append(path)
|
||||
for path in glob('{0}/lib/python*/*/site-packages'.format(self.conda_env)):
|
||||
if path not in self.paths:
|
||||
self.paths.append(path)
|
||||
|
||||
# handle case-insensitive paths on windows
|
||||
self.stdlib_lib_prefix = os.path.normcase(sysconfig.get_paths()['stdlib'])
|
||||
if self.stdlib_lib_prefix not in self.paths:
|
||||
self.paths.append(self.stdlib_lib_prefix)
|
||||
|
||||
# handle compiled libraries
|
||||
self.ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") or ".so"
|
||||
|
||||
# add system paths
|
||||
for path in sys.path[1:]:
|
||||
if path not in self.paths:
|
||||
self.paths.append(path)
|
||||
|
||||
def find(self, module_name):
|
||||
for prefix in self.paths:
|
||||
package_path = "/".join((prefix, module_name.split(".")[0]))
|
||||
is_module = (exists_case_sensitive(package_path + ".py") or
|
||||
exists_case_sensitive(package_path + ".so") or
|
||||
exists_case_sensitive(package_path + self.ext_suffix) or
|
||||
exists_case_sensitive(package_path + "/__init__.py"))
|
||||
is_package = exists_case_sensitive(package_path) and os.path.isdir(package_path)
|
||||
if is_module or is_package:
|
||||
if 'site-packages' in prefix:
|
||||
return self.sections.THIRDPARTY
|
||||
if 'dist-packages' in prefix:
|
||||
return self.sections.THIRDPARTY
|
||||
if self.virtual_env and self.virtual_env_src in prefix:
|
||||
return self.sections.THIRDPARTY
|
||||
if self.conda_env and self.conda_env in prefix:
|
||||
return self.sections.THIRDPARTY
|
||||
if os.path.normcase(prefix).startswith(self.stdlib_lib_prefix):
|
||||
return self.sections.STDLIB
|
||||
return self.config['default_section']
|
||||
|
||||
|
||||
class ReqsBaseFinder(BaseFinder):
|
||||
def __init__(self, config, sections, path='.'):
|
||||
super(ReqsBaseFinder, self).__init__(config, sections)
|
||||
self.path = path
|
||||
if self.enabled:
|
||||
self.mapping = self._load_mapping()
|
||||
self.names = self._load_names()
|
||||
|
||||
@staticmethod
|
||||
def _load_mapping():
|
||||
"""Return list of mappings `package_name -> module_name`
|
||||
|
||||
Example:
|
||||
django-haystack -> haystack
|
||||
"""
|
||||
if not pipreqs:
|
||||
return
|
||||
path = os.path.dirname(inspect.getfile(pipreqs))
|
||||
path = os.path.join(path, 'mapping')
|
||||
with open(path) as f:
|
||||
# pypi_name: import_name
|
||||
return dict(line.strip().split(":")[::-1] for line in f)
|
||||
|
||||
def _load_names(self):
|
||||
"""Return list of thirdparty modules from requirements
|
||||
"""
|
||||
names = []
|
||||
for path in self._get_files():
|
||||
for name in self._get_names(path):
|
||||
names.append(self._normalize_name(name))
|
||||
return names
|
||||
|
||||
@staticmethod
|
||||
def _get_parents(path):
|
||||
prev = ''
|
||||
while path != prev:
|
||||
prev = path
|
||||
yield path
|
||||
path = os.path.dirname(path)
|
||||
|
||||
def _get_files(self):
|
||||
"""Return paths to all requirements files
|
||||
"""
|
||||
path = os.path.abspath(self.path)
|
||||
if os.path.isfile(path):
|
||||
path = os.path.dirname(path)
|
||||
|
||||
for path in self._get_parents(path):
|
||||
for file_path in self._get_files_from_dir(path):
|
||||
yield file_path
|
||||
|
||||
def _normalize_name(self, name):
|
||||
"""Convert package name to module name
|
||||
|
||||
Examples:
|
||||
Django -> django
|
||||
django-haystack -> haystack
|
||||
Flask-RESTFul -> flask_restful
|
||||
"""
|
||||
if self.mapping:
|
||||
name = self.mapping.get(name, name)
|
||||
return name.lower().replace('-', '_')
|
||||
|
||||
def find(self, module_name):
|
||||
# required lib not installed yet
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
module_name, _sep, _submodules = module_name.partition('.')
|
||||
module_name = module_name.lower()
|
||||
if not module_name:
|
||||
return
|
||||
|
||||
for name in self.names:
|
||||
if module_name == name:
|
||||
return self.sections.THIRDPARTY
|
||||
|
||||
|
||||
class RequirementsFinder(ReqsBaseFinder):
|
||||
exts = ('.txt', '.in')
|
||||
enabled = bool(parse_requirements)
|
||||
|
||||
def _get_files_from_dir(self, path):
|
||||
"""Return paths to requirements files from passed dir.
|
||||
"""
|
||||
return RequirementsFinder._get_files_from_dir_cached(path)
|
||||
|
||||
@classmethod
|
||||
@lru_cache(maxsize=16)
|
||||
def _get_files_from_dir_cached(cls, path):
|
||||
result = []
|
||||
|
||||
for fname in os.listdir(path):
|
||||
if 'requirements' not in fname:
|
||||
continue
|
||||
full_path = os.path.join(path, fname)
|
||||
|
||||
# *requirements*/*.{txt,in}
|
||||
if os.path.isdir(full_path):
|
||||
for subfile_name in os.listdir(path):
|
||||
for ext in cls.exts:
|
||||
if subfile_name.endswith(ext):
|
||||
result.append(os.path.join(path, subfile_name))
|
||||
continue
|
||||
|
||||
# *requirements*.{txt,in}
|
||||
if os.path.isfile(full_path):
|
||||
for ext in cls.exts:
|
||||
if fname.endswith(ext):
|
||||
result.append(full_path)
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
def _get_names(self, path):
|
||||
"""Load required packages from path to requirements file
|
||||
"""
|
||||
return RequirementsFinder._get_names_cached(path)
|
||||
|
||||
@classmethod
|
||||
@lru_cache(maxsize=16)
|
||||
def _get_names_cached(cls, path):
|
||||
results = []
|
||||
|
||||
with chdir(os.path.dirname(path)):
|
||||
requirements = parse_requirements(path)
|
||||
for req in requirements.values():
|
||||
if req.name:
|
||||
results.append(req.name)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
class PipfileFinder(ReqsBaseFinder):
|
||||
enabled = bool(Pipfile)
|
||||
|
||||
def _get_names(self, path):
|
||||
with chdir(path):
|
||||
project = Pipfile.load(path)
|
||||
for req in project.packages:
|
||||
yield req.name
|
||||
|
||||
def _get_files_from_dir(self, path):
|
||||
if 'Pipfile' in os.listdir(path):
|
||||
yield path
|
||||
|
||||
|
||||
class DefaultFinder(BaseFinder):
|
||||
def find(self, module_name):
|
||||
return self.config['default_section']
|
||||
|
||||
|
||||
class FindersManager(object):
|
||||
finders = (
|
||||
ForcedSeparateFinder,
|
||||
LocalFinder,
|
||||
KnownPatternFinder,
|
||||
PathFinder,
|
||||
PipfileFinder,
|
||||
RequirementsFinder,
|
||||
DefaultFinder,
|
||||
)
|
||||
|
||||
def __init__(self, config, sections, finders=None):
|
||||
self.verbose = config.get('verbose', False)
|
||||
|
||||
finders = self.finders if finders is None else finders
|
||||
self.finders = []
|
||||
for finder in finders:
|
||||
try:
|
||||
self.finders.append(finder(config, sections))
|
||||
except Exception as exception:
|
||||
# if one finder fails to instantiate isort can continue using the rest
|
||||
if self.verbose:
|
||||
print('{} encountered an error ({}) during instantiation and cannot be used'.format(finder.__name__,
|
||||
str(exception)))
|
||||
self.finders = tuple(self.finders)
|
||||
|
||||
def find(self, module_name):
|
||||
for finder in self.finders:
|
||||
try:
|
||||
section = finder.find(module_name)
|
||||
except Exception as exception:
|
||||
# isort has to be able to keep trying to identify the correct import section even if one approach fails
|
||||
if self.verbose:
|
||||
print('{} encountered an error ({}) while trying to identify the {} module'.format(finder.__name__,
|
||||
str(exception),
|
||||
module_name))
|
||||
if section is not None:
|
||||
return section
|
||||
91
venv/lib/python3.6/site-packages/isort/hooks.py
Normal file
91
venv/lib/python3.6/site-packages/isort/hooks.py
Normal file
@@ -0,0 +1,91 @@
|
||||
"""isort.py.
|
||||
|
||||
Defines a git hook to allow pre-commit warnings and errors about import order.
|
||||
|
||||
usage:
|
||||
exit_code = git_hook(strict=True|False, modify=True|False)
|
||||
|
||||
Copyright (C) 2015 Helen Sherwood-Taylor
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
import subprocess
|
||||
|
||||
from isort import SortImports
|
||||
|
||||
|
||||
def get_output(command):
|
||||
"""
|
||||
Run a command and return raw output
|
||||
|
||||
:param str command: the command to run
|
||||
:returns: the stdout output of the command
|
||||
"""
|
||||
return subprocess.check_output(command.split())
|
||||
|
||||
|
||||
def get_lines(command):
|
||||
"""
|
||||
Run a command and return lines of output
|
||||
|
||||
:param str command: the command to run
|
||||
:returns: list of whitespace-stripped lines output by command
|
||||
"""
|
||||
stdout = get_output(command)
|
||||
return [line.strip().decode('utf-8') for line in stdout.splitlines()]
|
||||
|
||||
|
||||
def git_hook(strict=False, modify=False):
|
||||
"""
|
||||
Git pre-commit hook to check staged files for isort errors
|
||||
|
||||
:param bool strict - if True, return number of errors on exit,
|
||||
causing the hook to fail. If False, return zero so it will
|
||||
just act as a warning.
|
||||
:param bool modify - if True, fix the sources if they are not
|
||||
sorted properly. If False, only report result without
|
||||
modifying anything.
|
||||
|
||||
:return number of errors if in strict mode, 0 otherwise.
|
||||
"""
|
||||
|
||||
# Get list of files modified and staged
|
||||
diff_cmd = "git diff-index --cached --name-only --diff-filter=ACMRTUXB HEAD"
|
||||
files_modified = get_lines(diff_cmd)
|
||||
|
||||
errors = 0
|
||||
for filename in files_modified:
|
||||
if filename.endswith('.py'):
|
||||
# Get the staged contents of the file
|
||||
staged_cmd = "git show :%s" % filename
|
||||
staged_contents = get_output(staged_cmd)
|
||||
|
||||
sort = SortImports(
|
||||
file_path=filename,
|
||||
file_contents=staged_contents.decode(),
|
||||
check=True
|
||||
)
|
||||
|
||||
if sort.incorrectly_sorted:
|
||||
errors += 1
|
||||
if modify:
|
||||
SortImports(
|
||||
file_path=filename,
|
||||
file_contents=staged_contents.decode(),
|
||||
check=False,
|
||||
)
|
||||
|
||||
return errors if strict else 0
|
||||
1060
venv/lib/python3.6/site-packages/isort/isort.py
Normal file
1060
venv/lib/python3.6/site-packages/isort/isort.py
Normal file
File diff suppressed because it is too large
Load Diff
401
venv/lib/python3.6/site-packages/isort/main.py
Normal file
401
venv/lib/python3.6/site-packages/isort/main.py
Normal file
@@ -0,0 +1,401 @@
|
||||
''' Tool for sorting imports alphabetically, and automatically separated into sections.
|
||||
|
||||
Copyright (C) 2013 Timothy Edmund Crosley
|
||||
|
||||
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.
|
||||
|
||||
'''
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import setuptools
|
||||
|
||||
from isort import SortImports, __version__
|
||||
from isort.settings import DEFAULT_SECTIONS, WrapModes, default, from_path, should_skip
|
||||
|
||||
INTRO = r"""
|
||||
/#######################################################################\
|
||||
|
||||
`sMMy`
|
||||
.yyyy- `
|
||||
##soos## ./o.
|
||||
` ``..-..` ``...`.`` ` ```` ``-ssso```
|
||||
.s:-y- .+osssssso/. ./ossss+:so+:` :+o-`/osso:+sssssssso/
|
||||
.s::y- osss+.``.`` -ssss+-.`-ossso` ssssso/::..::+ssss:::.
|
||||
.s::y- /ssss+//:-.` `ssss+ `ssss+ sssso` :ssss`
|
||||
.s::y- `-/+oossssso/ `ssss/ sssso ssss/ :ssss`
|
||||
.y-/y- ````:ssss` ossso. :ssss: ssss/ :ssss.
|
||||
`/so:` `-//::/osss+ `+ssss+-/ossso: /sso- `osssso/.
|
||||
\/ `-/oooo++/- .:/++:/++/-` .. `://++/.
|
||||
|
||||
|
||||
isort your Python imports for you so you don't have to
|
||||
|
||||
VERSION {0}
|
||||
|
||||
\########################################################################/
|
||||
""".format(__version__)
|
||||
|
||||
shebang_re = re.compile(br'^#!.*\bpython[23w]?\b')
|
||||
|
||||
|
||||
def is_python_file(path):
|
||||
_root, ext = os.path.splitext(path)
|
||||
if ext in ('.py', '.pyi'):
|
||||
return True
|
||||
if ext in ('.pex', ):
|
||||
return False
|
||||
|
||||
# Skip editor backup files.
|
||||
if path.endswith('~'):
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(path, 'rb') as fp:
|
||||
line = fp.readline(100)
|
||||
except IOError:
|
||||
return False
|
||||
else:
|
||||
return bool(shebang_re.match(line))
|
||||
|
||||
|
||||
class SortAttempt(object):
|
||||
def __init__(self, incorrectly_sorted, skipped):
|
||||
self.incorrectly_sorted = incorrectly_sorted
|
||||
self.skipped = skipped
|
||||
|
||||
|
||||
def sort_imports(file_name, **arguments):
|
||||
try:
|
||||
result = SortImports(file_name, **arguments)
|
||||
return SortAttempt(result.incorrectly_sorted, result.skipped)
|
||||
except IOError as e:
|
||||
print("WARNING: Unable to parse file {0} due to {1}".format(file_name, e))
|
||||
return None
|
||||
|
||||
|
||||
def iter_source_code(paths, config, skipped):
|
||||
"""Iterate over all Python source files defined in paths."""
|
||||
if 'not_skip' in config:
|
||||
config['skip'] = list(set(config['skip']).difference(config['not_skip']))
|
||||
|
||||
for path in paths:
|
||||
if os.path.isdir(path):
|
||||
for dirpath, dirnames, filenames in os.walk(path, topdown=True, followlinks=True):
|
||||
for dirname in list(dirnames):
|
||||
if should_skip(dirname, config, dirpath):
|
||||
skipped.append(dirname)
|
||||
dirnames.remove(dirname)
|
||||
for filename in filenames:
|
||||
filepath = os.path.join(dirpath, filename)
|
||||
if is_python_file(filepath):
|
||||
relative_file = os.path.relpath(filepath, path)
|
||||
if should_skip(relative_file, config, path):
|
||||
skipped.append(filename)
|
||||
else:
|
||||
yield filepath
|
||||
else:
|
||||
yield path
|
||||
|
||||
|
||||
class ISortCommand(setuptools.Command):
|
||||
"""The :class:`ISortCommand` class is used by setuptools to perform
|
||||
imports checks on registered modules.
|
||||
"""
|
||||
|
||||
description = "Run isort on modules registered in setuptools"
|
||||
user_options = []
|
||||
|
||||
def initialize_options(self):
|
||||
default_settings = default.copy()
|
||||
for key, value in default_settings.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def finalize_options(self):
|
||||
"Get options from config files."
|
||||
self.arguments = {}
|
||||
computed_settings = from_path(os.getcwd())
|
||||
for key, value in computed_settings.items():
|
||||
self.arguments[key] = value
|
||||
|
||||
def distribution_files(self):
|
||||
"""Find distribution packages."""
|
||||
# This is verbatim from flake8
|
||||
if self.distribution.packages:
|
||||
package_dirs = self.distribution.package_dir or {}
|
||||
for package in self.distribution.packages:
|
||||
pkg_dir = package
|
||||
if package in package_dirs:
|
||||
pkg_dir = package_dirs[package]
|
||||
elif '' in package_dirs:
|
||||
pkg_dir = package_dirs[''] + os.path.sep + pkg_dir
|
||||
yield pkg_dir.replace('.', os.path.sep)
|
||||
|
||||
if self.distribution.py_modules:
|
||||
for filename in self.distribution.py_modules:
|
||||
yield "%s.py" % filename
|
||||
# Don't miss the setup.py file itself
|
||||
yield "setup.py"
|
||||
|
||||
def run(self):
|
||||
arguments = self.arguments
|
||||
wrong_sorted_files = False
|
||||
arguments['check'] = True
|
||||
for path in self.distribution_files():
|
||||
for python_file in glob.iglob(os.path.join(path, '*.py')):
|
||||
try:
|
||||
incorrectly_sorted = SortImports(python_file, **arguments).incorrectly_sorted
|
||||
if incorrectly_sorted:
|
||||
wrong_sorted_files = True
|
||||
except IOError as e:
|
||||
print("WARNING: Unable to parse file {0} due to {1}".format(python_file, e))
|
||||
if wrong_sorted_files:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def parse_args(argv=None):
|
||||
parser = argparse.ArgumentParser(description='Sort Python import definitions alphabetically '
|
||||
'within logical sections. Run with no arguments to run '
|
||||
'interactively. Run with `-` as the first argument to read from '
|
||||
'stdin. Otherwise provide a list of files to sort.')
|
||||
inline_args_group = parser.add_mutually_exclusive_group()
|
||||
parser.add_argument('-a', '--add-import', dest='add_imports', action='append',
|
||||
help='Adds the specified import line to all files, '
|
||||
'automatically determining correct placement.')
|
||||
parser.add_argument('-ac', '--atomic', dest='atomic', action='store_true',
|
||||
help="Ensures the output doesn't save if the resulting file contains syntax errors.")
|
||||
parser.add_argument('-af', '--force-adds', dest='force_adds', action='store_true',
|
||||
help='Forces import adds even if the original file is empty.')
|
||||
parser.add_argument('-b', '--builtin', dest='known_standard_library', action='append',
|
||||
help='Force sortImports to recognize a module as part of the python standard library.')
|
||||
parser.add_argument('-c', '--check-only', action='store_true', dest="check",
|
||||
help='Checks the file for unsorted / unformatted imports and prints them to the '
|
||||
'command line without modifying the file.')
|
||||
parser.add_argument('-ca', '--combine-as', dest='combine_as_imports', action='store_true',
|
||||
help="Combines as imports on the same line.")
|
||||
parser.add_argument('-cs', '--combine-star', dest='combine_star', action='store_true',
|
||||
help="Ensures that if a star import is present, nothing else is imported from that namespace.")
|
||||
parser.add_argument('-d', '--stdout', help='Force resulting output to stdout, instead of in-place.',
|
||||
dest='write_to_stdout', action='store_true')
|
||||
parser.add_argument('-df', '--diff', dest='show_diff', action='store_true',
|
||||
help="Prints a diff of all the changes isort would make to a file, instead of "
|
||||
"changing it in place")
|
||||
parser.add_argument('-ds', '--no-sections', help='Put all imports into the same section bucket', dest='no_sections',
|
||||
action='store_true')
|
||||
parser.add_argument('-dt', '--dont-order-by-type', dest='dont_order_by_type',
|
||||
action='store_true', help='Only order imports alphabetically, do not attempt type ordering')
|
||||
parser.add_argument('-e', '--balanced', dest='balanced_wrapping', action='store_true',
|
||||
help='Balances wrapping to produce the most consistent line length possible')
|
||||
parser.add_argument('-f', '--future', dest='known_future_library', action='append',
|
||||
help='Force sortImports to recognize a module as part of the future compatibility libraries.')
|
||||
parser.add_argument('-fas', '--force-alphabetical-sort', action='store_true', dest="force_alphabetical_sort",
|
||||
help='Force all imports to be sorted as a single section')
|
||||
parser.add_argument('-fass', '--force-alphabetical-sort-within-sections', action='store_true',
|
||||
dest="force_alphabetical_sort", help='Force all imports to be sorted alphabetically within a '
|
||||
'section')
|
||||
parser.add_argument('-ff', '--from-first', dest='from_first',
|
||||
help="Switches the typical ordering preference, showing from imports first then straight ones.")
|
||||
parser.add_argument('-fgw', '--force-grid-wrap', nargs='?', const=2, type=int, dest="force_grid_wrap",
|
||||
help='Force number of from imports (defaults to 2) to be grid wrapped regardless of line '
|
||||
'length')
|
||||
parser.add_argument('-fss', '--force-sort-within-sections', action='store_true', dest="force_sort_within_sections",
|
||||
help='Force imports to be sorted by module, independent of import_type')
|
||||
parser.add_argument('-i', '--indent', help='String to place for indents defaults to " " (4 spaces).',
|
||||
dest='indent', type=str)
|
||||
parser.add_argument('-j', '--jobs', help='Number of files to process in parallel.',
|
||||
dest='jobs', type=int)
|
||||
parser.add_argument('-k', '--keep-direct-and-as', dest='keep_direct_and_as_imports', action='store_true',
|
||||
help="Turns off default behavior that removes direct imports when as imports exist.")
|
||||
parser.add_argument('-l', '--lines', help='[Deprecated] The max length of an import line (used for wrapping '
|
||||
'long imports).',
|
||||
dest='line_length', type=int)
|
||||
parser.add_argument('-lai', '--lines-after-imports', dest='lines_after_imports', type=int)
|
||||
parser.add_argument('-lbt', '--lines-between-types', dest='lines_between_types', type=int)
|
||||
parser.add_argument('-le', '--line-ending', dest='line_ending',
|
||||
help="Forces line endings to the specified value. If not set, values will be guessed per-file.")
|
||||
parser.add_argument('-ls', '--length-sort', help='Sort imports by their string length.',
|
||||
dest='length_sort', action='store_true')
|
||||
parser.add_argument('-m', '--multi-line', dest='multi_line_output', type=int, choices=range(len(WrapModes)),
|
||||
help='Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, '
|
||||
'5-vert-grid-grouped, 6-vert-grid-grouped-no-comma).')
|
||||
inline_args_group.add_argument('-nis', '--no-inline-sort', dest='no_inline_sort', action='store_true',
|
||||
help='Leaves `from` imports with multiple imports \'as-is\' (e.g. `from foo import a, c ,b`).')
|
||||
parser.add_argument('-nlb', '--no-lines-before', help='Sections which should not be split with previous by empty lines',
|
||||
dest='no_lines_before', action='append')
|
||||
parser.add_argument('-ns', '--dont-skip', help='Files that sort imports should never skip over.',
|
||||
dest='not_skip', action='append')
|
||||
parser.add_argument('-o', '--thirdparty', dest='known_third_party', action='append',
|
||||
help='Force sortImports to recognize a module as being part of a third party library.')
|
||||
parser.add_argument('-ot', '--order-by-type', dest='order_by_type',
|
||||
action='store_true', help='Order imports by type in addition to alphabetically')
|
||||
parser.add_argument('-p', '--project', dest='known_first_party', action='append',
|
||||
help='Force sortImports to recognize a module as being part of the current python project.')
|
||||
parser.add_argument('-q', '--quiet', action='store_true', dest="quiet",
|
||||
help='Shows extra quiet output, only errors are outputted.')
|
||||
parser.add_argument('-r', dest='ambiguous_r_flag', action='store_true')
|
||||
parser.add_argument('-rm', '--remove-import', dest='remove_imports', action='append',
|
||||
help='Removes the specified import from all files.')
|
||||
parser.add_argument('-rr', '--reverse-relative', dest='reverse_relative', action='store_true',
|
||||
help='Reverse order of relative imports.')
|
||||
parser.add_argument('-rc', '--recursive', dest='recursive', action='store_true',
|
||||
help='Recursively look for Python files of which to sort imports')
|
||||
parser.add_argument('-s', '--skip', help='Files that sort imports should skip over. If you want to skip multiple '
|
||||
'files you should specify twice: --skip file1 --skip file2.', dest='skip', action='append')
|
||||
parser.add_argument('-sd', '--section-default', dest='default_section',
|
||||
help='Sets the default section for imports (by default FIRSTPARTY) options: ' +
|
||||
str(DEFAULT_SECTIONS))
|
||||
parser.add_argument('-sg', '--skip-glob', help='Files that sort imports should skip over.', dest='skip_glob',
|
||||
action='append')
|
||||
inline_args_group.add_argument('-sl', '--force-single-line-imports', dest='force_single_line', action='store_true',
|
||||
help='Forces all from imports to appear on their own line')
|
||||
parser.add_argument('-sp', '--settings-path', dest="settings_path",
|
||||
help='Explicitly set the settings path instead of auto determining based on file location.')
|
||||
parser.add_argument('-t', '--top', help='Force specific imports to the top of their appropriate section.',
|
||||
dest='force_to_top', action='append')
|
||||
parser.add_argument('-tc', '--trailing-comma', dest='include_trailing_comma', action='store_true',
|
||||
help='Includes a trailing comma on multi line imports that include parentheses.')
|
||||
parser.add_argument('-up', '--use-parentheses', dest='use_parentheses', action='store_true',
|
||||
help='Use parenthesis for line continuation on length limit instead of slashes.')
|
||||
parser.add_argument('-v', '--version', action='store_true', dest='show_version')
|
||||
parser.add_argument('-vb', '--verbose', action='store_true', dest="verbose",
|
||||
help='Shows verbose output, such as when files are skipped or when a check is successful.')
|
||||
parser.add_argument('--virtual-env', dest='virtual_env',
|
||||
help='Virtual environment to use for determining whether a package is third-party')
|
||||
parser.add_argument('--conda-env', dest='conda_env',
|
||||
help='Conda environment to use for determining whether a package is third-party')
|
||||
parser.add_argument('-vn', '--version-number', action='version', version=__version__,
|
||||
help='Returns just the current version number without the logo')
|
||||
parser.add_argument('-w', '--line-width', help='The max length of an import line (used for wrapping long imports).',
|
||||
dest='line_length', type=int)
|
||||
parser.add_argument('-wl', '--wrap-length', dest='wrap_length',
|
||||
help="Specifies how long lines that are wrapped should be, if not set line_length is used.")
|
||||
parser.add_argument('-ws', '--ignore-whitespace', action='store_true', dest="ignore_whitespace",
|
||||
help='Tells isort to ignore whitespace differences when --check-only is being used.')
|
||||
parser.add_argument('-y', '--apply', dest='apply', action='store_true',
|
||||
help='Tells isort to apply changes recursively without asking')
|
||||
parser.add_argument('--unsafe', dest='unsafe', action='store_true',
|
||||
help='Tells isort to look for files in standard library directories, etc. '
|
||||
'where it may not be safe to operate in')
|
||||
parser.add_argument('--case-sensitive', dest='case_sensitive', action='store_true',
|
||||
help='Tells isort to include casing when sorting module names')
|
||||
parser.add_argument('--filter-files', dest='filter_files', action='store_true',
|
||||
help='Tells isort to filter files even when they are explicitly passed in as part of the command')
|
||||
parser.add_argument('files', nargs='*', help='One or more Python source files that need their imports sorted.')
|
||||
|
||||
arguments = {key: value for key, value in vars(parser.parse_args(argv)).items() if value}
|
||||
if 'dont_order_by_type' in arguments:
|
||||
arguments['order_by_type'] = False
|
||||
if arguments.pop('unsafe', False):
|
||||
arguments['safety_excludes'] = False
|
||||
return arguments
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
arguments = parse_args(argv)
|
||||
if arguments.get('show_version'):
|
||||
print(INTRO)
|
||||
return
|
||||
|
||||
if arguments.get('ambiguous_r_flag'):
|
||||
print('ERROR: Deprecated -r flag set. This flag has been replaced with -rm to remove ambiguity between it and '
|
||||
'-rc for recursive')
|
||||
sys.exit(1)
|
||||
|
||||
arguments['check_skip'] = False
|
||||
if 'settings_path' in arguments:
|
||||
sp = arguments['settings_path']
|
||||
arguments['settings_path'] = os.path.abspath(sp) if os.path.isdir(sp) else os.path.dirname(os.path.abspath(sp))
|
||||
if not os.path.isdir(arguments['settings_path']):
|
||||
print("WARNING: settings_path dir does not exist: {0}".format(arguments['settings_path']))
|
||||
|
||||
if 'virtual_env' in arguments:
|
||||
venv = arguments['virtual_env']
|
||||
arguments['virtual_env'] = os.path.abspath(venv)
|
||||
if not os.path.isdir(arguments['virtual_env']):
|
||||
print("WARNING: virtual_env dir does not exist: {0}".format(arguments['virtual_env']))
|
||||
|
||||
file_names = arguments.pop('files', [])
|
||||
if file_names == ['-']:
|
||||
try:
|
||||
# python 3
|
||||
file_ = sys.stdin.buffer
|
||||
except AttributeError:
|
||||
# python 2
|
||||
file_ = sys.stdin
|
||||
SortImports(file_=file_, write_to_stdout=True, **arguments)
|
||||
else:
|
||||
if not file_names:
|
||||
file_names = ['.']
|
||||
arguments['recursive'] = True
|
||||
if not arguments.get('apply', False):
|
||||
arguments['ask_to_apply'] = True
|
||||
|
||||
config = from_path(arguments.get('settings_path', '') or os.path.abspath(file_names[0]) or os.getcwd()).copy()
|
||||
config.update(arguments)
|
||||
wrong_sorted_files = False
|
||||
skipped = []
|
||||
|
||||
if config.get('filter_files'):
|
||||
filtered_files = []
|
||||
for file_name in file_names:
|
||||
if should_skip(file_name, config):
|
||||
skipped.append(file_name)
|
||||
else:
|
||||
filtered_files.append(file_name)
|
||||
file_names = filtered_files
|
||||
|
||||
if arguments.get('recursive', False):
|
||||
file_names = iter_source_code(file_names, config, skipped)
|
||||
num_skipped = 0
|
||||
if config['verbose'] or config.get('show_logo', False):
|
||||
print(INTRO)
|
||||
|
||||
jobs = arguments.get('jobs')
|
||||
if jobs:
|
||||
import multiprocessing
|
||||
executor = multiprocessing.Pool(jobs)
|
||||
attempt_iterator = executor.imap(functools.partial(sort_imports, **arguments), file_names)
|
||||
else:
|
||||
attempt_iterator = (sort_imports(file_name, **arguments) for file_name in file_names)
|
||||
|
||||
for sort_attempt in attempt_iterator:
|
||||
if not sort_attempt:
|
||||
continue
|
||||
incorrectly_sorted = sort_attempt.incorrectly_sorted
|
||||
if arguments.get('check', False) and incorrectly_sorted:
|
||||
wrong_sorted_files = True
|
||||
if sort_attempt.skipped:
|
||||
num_skipped += 1
|
||||
|
||||
if wrong_sorted_files:
|
||||
sys.exit(1)
|
||||
|
||||
num_skipped += len(skipped)
|
||||
if num_skipped and not arguments.get('quiet', False):
|
||||
if config['verbose']:
|
||||
for was_skipped in skipped:
|
||||
print("WARNING: {0} was skipped as it's listed in 'skip' setting"
|
||||
" or matches a glob in 'skip_glob' setting".format(was_skipped))
|
||||
print("Skipped {0} files".format(num_skipped))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
47
venv/lib/python3.6/site-packages/isort/natural.py
Normal file
47
venv/lib/python3.6/site-packages/isort/natural.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""isort/natural.py.
|
||||
|
||||
Enables sorting strings that contain numbers naturally
|
||||
|
||||
usage:
|
||||
natural.nsorted(list)
|
||||
|
||||
Copyright (C) 2013 Timothy Edmund Crosley
|
||||
|
||||
Implementation originally from @HappyLeapSecond stack overflow user in response to:
|
||||
https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
import re
|
||||
|
||||
|
||||
def _atoi(text):
|
||||
return int(text) if text.isdigit() else text
|
||||
|
||||
|
||||
def _natural_keys(text):
|
||||
return [_atoi(c) for c in re.split(r'(\d+)', text)]
|
||||
|
||||
|
||||
def nsorted(to_sort, key=None):
|
||||
"""Returns a naturally sorted list"""
|
||||
if key is None:
|
||||
key_callback = _natural_keys
|
||||
else:
|
||||
def key_callback(item):
|
||||
return _natural_keys(key(item))
|
||||
|
||||
return sorted(to_sort, key=key_callback)
|
||||
154
venv/lib/python3.6/site-packages/isort/pie_slice.py
Normal file
154
venv/lib/python3.6/site-packages/isort/pie_slice.py
Normal file
@@ -0,0 +1,154 @@
|
||||
"""pie_slice/overrides.py.
|
||||
|
||||
Overrides Python syntax to conform to the Python3 version as much as possible using a '*' import
|
||||
|
||||
Copyright (C) 2013 Timothy Edmund Crosley
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import collections
|
||||
import sys
|
||||
|
||||
__version__ = "1.1.0"
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY3 = sys.version_info[0] == 3
|
||||
VERSION = sys.version_info
|
||||
|
||||
__all__ = ['PY2', 'PY3', 'lru_cache', 'apply_changes_to_python_environment']
|
||||
|
||||
|
||||
if PY3:
|
||||
input = input
|
||||
|
||||
def apply_changes_to_python_environment():
|
||||
pass
|
||||
else:
|
||||
input = raw_input # noqa: F821
|
||||
|
||||
python_environment_changes_applied = False
|
||||
|
||||
import sys
|
||||
stdout = sys.stdout
|
||||
stderr = sys.stderr
|
||||
|
||||
def apply_changes_to_python_environment():
|
||||
global python_environment_changes_applied
|
||||
if python_environment_changes_applied or sys.getdefaultencoding() == 'utf-8':
|
||||
python_environment_changes_applied = True
|
||||
return
|
||||
|
||||
try:
|
||||
reload(sys)
|
||||
sys.stdout = stdout
|
||||
sys.stderr = stderr
|
||||
sys.setdefaultencoding('utf-8')
|
||||
except NameError: # Python 3
|
||||
sys.exit('This should not happen!')
|
||||
|
||||
python_environment_changes_applied = True
|
||||
|
||||
|
||||
if sys.version_info < (3, 2):
|
||||
try:
|
||||
from threading import Lock
|
||||
except ImportError:
|
||||
from dummy_threading import Lock
|
||||
|
||||
from functools import wraps
|
||||
|
||||
_CacheInfo = collections.namedtuple("CacheInfo", "hits misses maxsize currsize")
|
||||
|
||||
def lru_cache(maxsize=100):
|
||||
"""Least-recently-used cache decorator.
|
||||
Taking from: https://github.com/MiCHiLU/python-functools32/blob/master/functools32/functools32.py
|
||||
with slight modifications.
|
||||
If *maxsize* is set to None, the LRU features are disabled and the cache
|
||||
can grow without bound.
|
||||
Arguments to the cached function must be hashable.
|
||||
View the cache statistics named tuple (hits, misses, maxsize, currsize) with
|
||||
f.cache_info(). Clear the cache and statistics with f.cache_clear().
|
||||
Access the underlying function with f.__wrapped__.
|
||||
See: https://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
|
||||
|
||||
"""
|
||||
def decorating_function(user_function, tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
|
||||
hits, misses = [0], [0]
|
||||
kwd_mark = (object(),) # separates positional and keyword args
|
||||
lock = Lock()
|
||||
|
||||
if maxsize is None:
|
||||
CACHE = {}
|
||||
|
||||
@wraps(user_function)
|
||||
def wrapper(*args, **kwds):
|
||||
key = args
|
||||
if kwds:
|
||||
key += kwd_mark + tuple(sorted(kwds.items()))
|
||||
try:
|
||||
result = CACHE[key]
|
||||
hits[0] += 1
|
||||
return result
|
||||
except KeyError:
|
||||
pass
|
||||
result = user_function(*args, **kwds)
|
||||
CACHE[key] = result
|
||||
misses[0] += 1
|
||||
return result
|
||||
else:
|
||||
CACHE = collections.OrderedDict()
|
||||
|
||||
@wraps(user_function)
|
||||
def wrapper(*args, **kwds):
|
||||
key = args
|
||||
if kwds:
|
||||
key += kwd_mark + tuple(sorted(kwds.items()))
|
||||
with lock:
|
||||
cached = CACHE.get(key, None)
|
||||
if cached:
|
||||
del CACHE[key]
|
||||
CACHE[key] = cached
|
||||
hits[0] += 1
|
||||
return cached
|
||||
result = user_function(*args, **kwds)
|
||||
with lock:
|
||||
CACHE[key] = result # record recent use of this key
|
||||
misses[0] += 1
|
||||
while len(CACHE) > maxsize:
|
||||
CACHE.popitem(last=False)
|
||||
return result
|
||||
|
||||
def cache_info():
|
||||
"""Report CACHE statistics."""
|
||||
with lock:
|
||||
return _CacheInfo(hits[0], misses[0], maxsize, len(CACHE))
|
||||
|
||||
def cache_clear():
|
||||
"""Clear the CACHE and CACHE statistics."""
|
||||
with lock:
|
||||
CACHE.clear()
|
||||
hits[0] = misses[0] = 0
|
||||
|
||||
wrapper.cache_info = cache_info
|
||||
wrapper.cache_clear = cache_clear
|
||||
return wrapper
|
||||
|
||||
return decorating_function
|
||||
|
||||
else:
|
||||
from functools import lru_cache
|
||||
29
venv/lib/python3.6/site-packages/isort/pylama_isort.py
Normal file
29
venv/lib/python3.6/site-packages/isort/pylama_isort.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pylama.lint import Linter as BaseLinter
|
||||
|
||||
from .isort import SortImports
|
||||
|
||||
|
||||
class Linter(BaseLinter):
|
||||
|
||||
def allow(self, path):
|
||||
"""Determine if this path should be linted."""
|
||||
return path.endswith('.py')
|
||||
|
||||
def run(self, path, **meta):
|
||||
"""Lint the file. Return an array of error dicts if appropriate."""
|
||||
with open(os.devnull, 'w') as devnull:
|
||||
# Suppress isort messages
|
||||
sys.stdout = devnull
|
||||
|
||||
if SortImports(path, check=True).incorrectly_sorted:
|
||||
return [{
|
||||
'lnum': 0,
|
||||
'col': 0,
|
||||
'text': 'Incorrectly sorted imports.',
|
||||
'type': 'ISORT'
|
||||
}]
|
||||
else:
|
||||
return []
|
||||
356
venv/lib/python3.6/site-packages/isort/settings.py
Normal file
356
venv/lib/python3.6/site-packages/isort/settings.py
Normal file
@@ -0,0 +1,356 @@
|
||||
"""isort/settings.py.
|
||||
|
||||
Defines how the default settings for isort should be loaded
|
||||
|
||||
(First from the default setting dictionary at the top of the file, then overridden by any settings
|
||||
in ~/.isort.cfg or $XDG_CONFIG_HOME/isort.cfg if there are any)
|
||||
|
||||
Copyright (C) 2013 Timothy Edmund Crosley
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import fnmatch
|
||||
import io
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from collections import namedtuple
|
||||
from distutils.util import strtobool
|
||||
|
||||
from .pie_slice import lru_cache
|
||||
from .utils import difference, union
|
||||
|
||||
try:
|
||||
import configparser
|
||||
except ImportError:
|
||||
import ConfigParser as configparser
|
||||
|
||||
try:
|
||||
import toml
|
||||
except ImportError:
|
||||
toml = False
|
||||
|
||||
try:
|
||||
import appdirs
|
||||
if appdirs.system == 'darwin':
|
||||
appdirs.system = 'linux2'
|
||||
except ImportError:
|
||||
appdirs = None
|
||||
|
||||
MAX_CONFIG_SEARCH_DEPTH = 25 # The number of parent directories isort will look for a config file within
|
||||
DEFAULT_SECTIONS = ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER')
|
||||
|
||||
safety_exclude_re = re.compile(
|
||||
r"/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist|\.pants\.d"
|
||||
r"|lib/python[0-9].[0-9]+)/"
|
||||
)
|
||||
|
||||
WrapModes = ('GRID', 'VERTICAL', 'HANGING_INDENT', 'VERTICAL_HANGING_INDENT', 'VERTICAL_GRID', 'VERTICAL_GRID_GROUPED',
|
||||
'VERTICAL_GRID_GROUPED_NO_COMMA', 'NOQA')
|
||||
WrapModes = namedtuple('WrapModes', WrapModes)(*range(len(WrapModes)))
|
||||
|
||||
# Note that none of these lists must be complete as they are simply fallbacks for when included auto-detection fails.
|
||||
default = {'force_to_top': [],
|
||||
'skip': [],
|
||||
'skip_glob': [],
|
||||
'line_length': 79,
|
||||
'wrap_length': 0,
|
||||
'line_ending': None,
|
||||
'sections': DEFAULT_SECTIONS,
|
||||
'no_sections': False,
|
||||
'known_future_library': ['__future__'],
|
||||
'known_standard_library': ['AL', 'BaseHTTPServer', 'Bastion', 'CGIHTTPServer', 'Carbon', 'ColorPicker',
|
||||
'ConfigParser', 'Cookie', 'DEVICE', 'DocXMLRPCServer', 'EasyDialogs', 'FL',
|
||||
'FrameWork', 'GL', 'HTMLParser', 'MacOS', 'MimeWriter', 'MiniAEFrame', 'Nav',
|
||||
'PixMapWrapper', 'Queue', 'SUNAUDIODEV', 'ScrolledText', 'SimpleHTTPServer',
|
||||
'SimpleXMLRPCServer', 'SocketServer', 'StringIO', 'Tix', 'Tkinter', 'UserDict',
|
||||
'UserList', 'UserString', 'W', '__builtin__', 'abc', 'aepack', 'aetools',
|
||||
'aetypes', 'aifc', 'al', 'anydbm', 'applesingle', 'argparse', 'array', 'ast',
|
||||
'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'autoGIL', 'base64',
|
||||
'bdb', 'binascii', 'binhex', 'bisect', 'bsddb', 'buildtools', 'builtins',
|
||||
'bz2', 'cPickle', 'cProfile', 'cStringIO', 'calendar', 'cd', 'cfmfile', 'cgi',
|
||||
'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections',
|
||||
'colorsys', 'commands', 'compileall', 'compiler', 'concurrent', 'configparser',
|
||||
'contextlib', 'contextvars', 'cookielib', 'copy', 'copy_reg', 'copyreg', 'crypt', 'csv',
|
||||
'ctypes', 'curses', 'dataclasses', 'datetime', 'dbhash', 'dbm', 'decimal', 'difflib',
|
||||
'dircache', 'dis', 'distutils', 'dl', 'doctest', 'dumbdbm', 'dummy_thread',
|
||||
'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno',
|
||||
'exceptions', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'findertools',
|
||||
'fl', 'flp', 'fm', 'fnmatch', 'formatter', 'fpectl', 'fpformat', 'fractions',
|
||||
'ftplib', 'functools', 'future_builtins', 'gc', 'gdbm', 'gensuitemodule',
|
||||
'getopt', 'getpass', 'gettext', 'gl', 'glob', 'grp', 'gzip', 'hashlib',
|
||||
'heapq', 'hmac', 'hotshot', 'html', 'htmlentitydefs', 'htmllib', 'http',
|
||||
'httplib', 'ic', 'icopen', 'imageop', 'imaplib', 'imgfile', 'imghdr', 'imp',
|
||||
'importlib', 'imputil', 'inspect', 'io', 'ipaddress', 'itertools', 'jpeg',
|
||||
'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma',
|
||||
'macerrors', 'macostools', 'macpath', 'macresource', 'mailbox', 'mailcap',
|
||||
'marshal', 'math', 'md5', 'mhlib', 'mimetools', 'mimetypes', 'mimify', 'mmap',
|
||||
'modulefinder', 'msilib', 'msvcrt', 'multifile', 'multiprocessing', 'mutex',
|
||||
'netrc', 'new', 'nis', 'nntplib', 'numbers', 'operator', 'optparse', 'os',
|
||||
'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes',
|
||||
'pkgutil', 'platform', 'plistlib', 'popen2', 'poplib', 'posix', 'posixfile',
|
||||
'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc',
|
||||
'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rexec',
|
||||
'rfc822', 'rlcompleter', 'robotparser', 'runpy', 'sched', 'secrets', 'select',
|
||||
'selectors', 'sets', 'sgmllib', 'sha', 'shelve', 'shlex', 'shutil', 'signal',
|
||||
'site', 'sitecustomize', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver',
|
||||
'spwd', 'sqlite3', 'ssl', 'stat', 'statistics', 'statvfs', 'string', 'stringprep',
|
||||
'struct', 'subprocess', 'sunau', 'sunaudiodev', 'symbol', 'symtable', 'sys',
|
||||
'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios',
|
||||
'test', 'textwrap', 'this', 'thread', 'threading', 'time', 'timeit', 'tkinter',
|
||||
'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'ttk', 'tty', 'turtle',
|
||||
'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'urllib2',
|
||||
'urlparse', 'usercustomize', 'uu', 'uuid', 'venv', 'videoreader',
|
||||
'warnings', 'wave', 'weakref', 'webbrowser', 'whichdb', 'winreg', 'winsound',
|
||||
'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'xmlrpclib', 'zipapp', 'zipfile',
|
||||
'zipimport', 'zlib'],
|
||||
'known_third_party': ['google.appengine.api'],
|
||||
'known_first_party': [],
|
||||
'multi_line_output': WrapModes.GRID,
|
||||
'forced_separate': [],
|
||||
'indent': ' ' * 4,
|
||||
'comment_prefix': ' #',
|
||||
'length_sort': False,
|
||||
'add_imports': [],
|
||||
'remove_imports': [],
|
||||
'reverse_relative': False,
|
||||
'force_single_line': False,
|
||||
'default_section': 'FIRSTPARTY',
|
||||
'import_heading_future': '',
|
||||
'import_heading_stdlib': '',
|
||||
'import_heading_thirdparty': '',
|
||||
'import_heading_firstparty': '',
|
||||
'import_heading_localfolder': '',
|
||||
'balanced_wrapping': False,
|
||||
'use_parentheses': False,
|
||||
'order_by_type': True,
|
||||
'atomic': False,
|
||||
'lines_after_imports': -1,
|
||||
'lines_between_sections': 1,
|
||||
'lines_between_types': 0,
|
||||
'combine_as_imports': False,
|
||||
'combine_star': False,
|
||||
'keep_direct_and_as_imports': False,
|
||||
'include_trailing_comma': False,
|
||||
'from_first': False,
|
||||
'verbose': False,
|
||||
'quiet': False,
|
||||
'force_adds': False,
|
||||
'force_alphabetical_sort_within_sections': False,
|
||||
'force_alphabetical_sort': False,
|
||||
'force_grid_wrap': 0,
|
||||
'force_sort_within_sections': False,
|
||||
'show_diff': False,
|
||||
'ignore_whitespace': False,
|
||||
'no_lines_before': [],
|
||||
'no_inline_sort': False,
|
||||
'ignore_comments': False,
|
||||
'safety_excludes': True,
|
||||
'case_sensitive': False}
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def from_path(path):
|
||||
computed_settings = default.copy()
|
||||
isort_defaults = ['~/.isort.cfg']
|
||||
if appdirs:
|
||||
isort_defaults = [appdirs.user_config_dir('isort.cfg')] + isort_defaults
|
||||
|
||||
_update_settings_with_config(path, '.editorconfig', ['~/.editorconfig'], ('*', '*.py', '**.py'), computed_settings)
|
||||
_update_settings_with_config(path, 'pyproject.toml', [], ('tool.isort', ), computed_settings)
|
||||
_update_settings_with_config(path, '.isort.cfg', isort_defaults, ('settings', 'isort'), computed_settings)
|
||||
_update_settings_with_config(path, 'setup.cfg', [], ('isort', 'tool:isort'), computed_settings)
|
||||
_update_settings_with_config(path, 'tox.ini', [], ('isort', 'tool:isort'), computed_settings)
|
||||
return computed_settings
|
||||
|
||||
|
||||
def _update_settings_with_config(path, name, default, sections, computed_settings):
|
||||
editor_config_file = None
|
||||
for potential_settings_path in default:
|
||||
expanded = os.path.expanduser(potential_settings_path)
|
||||
if os.path.exists(expanded):
|
||||
editor_config_file = expanded
|
||||
break
|
||||
|
||||
tries = 0
|
||||
current_directory = path
|
||||
while current_directory and tries < MAX_CONFIG_SEARCH_DEPTH:
|
||||
potential_path = os.path.join(current_directory, str(name))
|
||||
if os.path.exists(potential_path):
|
||||
editor_config_file = potential_path
|
||||
break
|
||||
|
||||
new_directory = os.path.split(current_directory)[0]
|
||||
if current_directory == new_directory:
|
||||
break
|
||||
current_directory = new_directory
|
||||
tries += 1
|
||||
|
||||
if editor_config_file and os.path.exists(editor_config_file):
|
||||
_update_with_config_file(editor_config_file, sections, computed_settings)
|
||||
|
||||
|
||||
def _update_with_config_file(file_path, sections, computed_settings):
|
||||
cwd = os.path.dirname(file_path)
|
||||
settings = _get_config_data(file_path, sections).copy()
|
||||
if not settings:
|
||||
return
|
||||
|
||||
if file_path.endswith('.editorconfig'):
|
||||
indent_style = settings.pop('indent_style', '').strip()
|
||||
indent_size = settings.pop('indent_size', '').strip()
|
||||
if indent_size == "tab":
|
||||
indent_size = settings.pop('tab_width', '').strip()
|
||||
|
||||
if indent_style == 'space':
|
||||
computed_settings['indent'] = ' ' * (indent_size and int(indent_size) or 4)
|
||||
elif indent_style == 'tab':
|
||||
computed_settings['indent'] = '\t' * (indent_size and int(indent_size) or 1)
|
||||
|
||||
max_line_length = settings.pop('max_line_length', '').strip()
|
||||
if max_line_length:
|
||||
computed_settings['line_length'] = float('inf') if max_line_length == 'off' else int(max_line_length)
|
||||
|
||||
for key, value in settings.items():
|
||||
access_key = key.replace('not_', '').lower()
|
||||
existing_value_type = type(default.get(access_key, ''))
|
||||
if existing_value_type in (list, tuple):
|
||||
# sections has fixed order values; no adding or substraction from any set
|
||||
if access_key == 'sections':
|
||||
computed_settings[access_key] = tuple(_as_list(value))
|
||||
else:
|
||||
existing_data = set(computed_settings.get(access_key, default.get(access_key)))
|
||||
if key.startswith('not_'):
|
||||
computed_settings[access_key] = difference(existing_data, _as_list(value))
|
||||
elif key.startswith('known_'):
|
||||
computed_settings[access_key] = union(existing_data, _abspaths(cwd, _as_list(value)))
|
||||
else:
|
||||
computed_settings[access_key] = union(existing_data, _as_list(value))
|
||||
elif existing_value_type == bool:
|
||||
# Only some configuration formats support native boolean values.
|
||||
if not isinstance(value, bool):
|
||||
value = bool(strtobool(value))
|
||||
computed_settings[access_key] = value
|
||||
elif key.startswith('known_'):
|
||||
computed_settings[access_key] = list(_abspaths(cwd, _as_list(value)))
|
||||
elif key == 'force_grid_wrap':
|
||||
try:
|
||||
result = existing_value_type(value)
|
||||
except ValueError:
|
||||
# backwards compat
|
||||
result = default.get(access_key) if value.lower().strip() == 'false' else 2
|
||||
computed_settings[access_key] = result
|
||||
else:
|
||||
computed_settings[access_key] = existing_value_type(value)
|
||||
|
||||
|
||||
def _as_list(value):
|
||||
if not isinstance(value, list):
|
||||
value = value.replace('\n', ',').split(',')
|
||||
|
||||
return filter(bool, [item.strip() for item in value])
|
||||
|
||||
|
||||
def _abspaths(cwd, values):
|
||||
paths = [
|
||||
os.path.join(cwd, value)
|
||||
if not value.startswith(os.path.sep) and value.endswith(os.path.sep)
|
||||
else value
|
||||
for value in values
|
||||
]
|
||||
return paths
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def _get_config_data(file_path, sections):
|
||||
settings = {}
|
||||
|
||||
with io.open(file_path) as config_file:
|
||||
if file_path.endswith('.toml'):
|
||||
if toml:
|
||||
config = toml.load(config_file)
|
||||
for section in sections:
|
||||
config_section = config
|
||||
for key in section.split('.'):
|
||||
config_section = config_section.get(key, {})
|
||||
settings.update(config_section)
|
||||
else:
|
||||
if '[tool.isort]' in config_file.read():
|
||||
warnings.warn("Found {} with [tool.isort] section, but toml package is not installed. "
|
||||
"To configure isort with {}, install with 'isort[pyproject]'.".format(file_path,
|
||||
file_path))
|
||||
else:
|
||||
if file_path.endswith('.editorconfig'):
|
||||
line = '\n'
|
||||
last_position = config_file.tell()
|
||||
while line:
|
||||
line = config_file.readline()
|
||||
if '[' in line:
|
||||
config_file.seek(last_position)
|
||||
break
|
||||
last_position = config_file.tell()
|
||||
|
||||
if sys.version_info >= (3, 2):
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read_file(config_file)
|
||||
else:
|
||||
config = configparser.SafeConfigParser()
|
||||
config.readfp(config_file)
|
||||
|
||||
for section in sections:
|
||||
if config.has_section(section):
|
||||
settings.update(config.items(section))
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
def should_skip(filename, config, path=''):
|
||||
"""Returns True if the file and/or folder should be skipped based on the passed in settings."""
|
||||
os_path = os.path.join(path, filename)
|
||||
|
||||
normalized_path = os_path.replace('\\', '/')
|
||||
if normalized_path[1:2] == ':':
|
||||
normalized_path = normalized_path[2:]
|
||||
|
||||
if path and config['safety_excludes']:
|
||||
check_exclude = '/' + filename.replace('\\', '/') + '/'
|
||||
if path and os.path.basename(path) in ('lib', ):
|
||||
check_exclude = '/' + os.path.basename(path) + check_exclude
|
||||
if safety_exclude_re.search(check_exclude):
|
||||
return True
|
||||
|
||||
for skip_path in config['skip']:
|
||||
if posixpath.abspath(normalized_path) == posixpath.abspath(skip_path.replace('\\', '/')):
|
||||
return True
|
||||
|
||||
position = os.path.split(filename)
|
||||
while position[1]:
|
||||
if position[1] in config['skip']:
|
||||
return True
|
||||
position = os.path.split(position[0])
|
||||
|
||||
for glob in config['skip_glob']:
|
||||
if fnmatch.fnmatch(filename, glob) or fnmatch.fnmatch('/' + filename, glob):
|
||||
return True
|
||||
|
||||
if not (os.path.isfile(os_path) or os.path.isdir(os_path) or os.path.islink(os_path)):
|
||||
return True
|
||||
|
||||
return False
|
||||
53
venv/lib/python3.6/site-packages/isort/utils.py
Normal file
53
venv/lib/python3.6/site-packages/isort/utils.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import os
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
def exists_case_sensitive(path):
|
||||
"""
|
||||
Returns if the given path exists and also matches the case on Windows.
|
||||
|
||||
When finding files that can be imported, it is important for the cases to match because while
|
||||
file os.path.exists("module.py") and os.path.exists("MODULE.py") both return True on Windows, Python
|
||||
can only import using the case of the real file.
|
||||
"""
|
||||
result = os.path.exists(path)
|
||||
if (sys.platform.startswith('win') or sys.platform == 'darwin') and result:
|
||||
directory, basename = os.path.split(path)
|
||||
result = basename in os.listdir(directory)
|
||||
return result
|
||||
|
||||
|
||||
@contextmanager
|
||||
def chdir(path):
|
||||
"""Context manager for changing dir and restoring previous workdir after exit.
|
||||
"""
|
||||
curdir = os.getcwd()
|
||||
os.chdir(path)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(curdir)
|
||||
|
||||
|
||||
def union(a, b):
|
||||
""" Return a list of items that are in `a` or `b`
|
||||
"""
|
||||
u = []
|
||||
for item in a:
|
||||
if item not in u:
|
||||
u.append(item)
|
||||
for item in b:
|
||||
if item not in u:
|
||||
u.append(item)
|
||||
return u
|
||||
|
||||
|
||||
def difference(a, b):
|
||||
""" Return a list of items from `a` that are not in `b`.
|
||||
"""
|
||||
d = []
|
||||
for item in a:
|
||||
if item not in b:
|
||||
d.append(item)
|
||||
return d
|
||||
Reference in New Issue
Block a user