init
This commit is contained in:
commit
38355d2442
9083 changed files with 1225834 additions and 0 deletions
257
.venv/lib/python3.8/site-packages/mypy/stubutil.py
Normal file
257
.venv/lib/python3.8/site-packages/mypy/stubutil.py
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
"""Utilities for mypy.stubgen, mypy.stubgenc, and mypy.stubdoc modules."""
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
import json
|
||||
import subprocess
|
||||
import re
|
||||
from contextlib import contextmanager
|
||||
|
||||
from typing import Optional, Tuple, List, Iterator, Union
|
||||
from typing_extensions import overload
|
||||
|
||||
from mypy.moduleinspect import ModuleInspect, InspectError
|
||||
from mypy.modulefinder import ModuleNotFoundReason
|
||||
|
||||
|
||||
# Modules that may fail when imported, or that may have side effects (fully qualified).
|
||||
NOT_IMPORTABLE_MODULES = ()
|
||||
|
||||
|
||||
class CantImport(Exception):
|
||||
def __init__(self, module: str, message: str):
|
||||
self.module = module
|
||||
self.message = message
|
||||
|
||||
|
||||
def default_py2_interpreter() -> str:
|
||||
"""Find a system Python 2 interpreter.
|
||||
|
||||
Return full path or exit if failed.
|
||||
"""
|
||||
# TODO: Make this do something reasonable in Windows.
|
||||
for candidate in ('/usr/bin/python2', '/usr/bin/python'):
|
||||
if not os.path.exists(candidate):
|
||||
continue
|
||||
output = subprocess.check_output([candidate, '--version'],
|
||||
stderr=subprocess.STDOUT).strip()
|
||||
if b'Python 2' in output:
|
||||
return candidate
|
||||
raise SystemExit("Can't find a Python 2 interpreter -- "
|
||||
"please use the --python-executable option")
|
||||
|
||||
|
||||
def walk_packages(inspect: ModuleInspect,
|
||||
packages: List[str],
|
||||
verbose: bool = False) -> Iterator[str]:
|
||||
"""Iterates through all packages and sub-packages in the given list.
|
||||
|
||||
This uses runtime imports (in another process) to find both Python and C modules.
|
||||
For Python packages we simply pass the __path__ attribute to pkgutil.walk_packages() to
|
||||
get the content of the package (all subpackages and modules). However, packages in C
|
||||
extensions do not have this attribute, so we have to roll out our own logic: recursively
|
||||
find all modules imported in the package that have matching names.
|
||||
"""
|
||||
for package_name in packages:
|
||||
if package_name in NOT_IMPORTABLE_MODULES:
|
||||
print('%s: Skipped (blacklisted)' % package_name)
|
||||
continue
|
||||
if verbose:
|
||||
print('Trying to import %r for runtime introspection' % package_name)
|
||||
try:
|
||||
prop = inspect.get_package_properties(package_name)
|
||||
except InspectError:
|
||||
report_missing(package_name)
|
||||
continue
|
||||
yield prop.name
|
||||
if prop.is_c_module:
|
||||
# Recursively iterate through the subpackages
|
||||
for submodule in walk_packages(inspect, prop.subpackages, verbose):
|
||||
yield submodule
|
||||
else:
|
||||
for submodule in prop.subpackages:
|
||||
yield submodule
|
||||
|
||||
|
||||
def find_module_path_and_all_py2(module: str,
|
||||
interpreter: str) -> Optional[Tuple[Optional[str],
|
||||
Optional[List[str]]]]:
|
||||
"""Return tuple (module path, module __all__) for a Python 2 module.
|
||||
|
||||
The path refers to the .py/.py[co] file. The second tuple item is
|
||||
None if the module doesn't define __all__.
|
||||
|
||||
Raise CantImport if the module can't be imported, or exit if it's a C extension module.
|
||||
"""
|
||||
cmd_template = '{interpreter} -c "%s"'.format(interpreter=interpreter)
|
||||
code = ("import importlib, json; mod = importlib.import_module('%s'); "
|
||||
"print(mod.__file__); print(json.dumps(getattr(mod, '__all__', None)))") % module
|
||||
try:
|
||||
output_bytes = subprocess.check_output(cmd_template % code, shell=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
path = find_module_path_using_py2_sys_path(module, interpreter)
|
||||
if path is None:
|
||||
raise CantImport(module, str(e)) from e
|
||||
return path, None
|
||||
output = output_bytes.decode('ascii').strip().splitlines()
|
||||
module_path = output[0]
|
||||
if not module_path.endswith(('.py', '.pyc', '.pyo')):
|
||||
raise SystemExit('%s looks like a C module; they are not supported for Python 2' %
|
||||
module)
|
||||
if module_path.endswith(('.pyc', '.pyo')):
|
||||
module_path = module_path[:-1]
|
||||
module_all = json.loads(output[1])
|
||||
return module_path, module_all
|
||||
|
||||
|
||||
def find_module_path_using_py2_sys_path(module: str,
|
||||
interpreter: str) -> Optional[str]:
|
||||
"""Try to find the path of a .py file for a module using Python 2 sys.path.
|
||||
|
||||
Return None if no match was found.
|
||||
"""
|
||||
out = subprocess.run(
|
||||
[interpreter, '-c', 'import sys; import json; print(json.dumps(sys.path))'],
|
||||
check=True,
|
||||
stdout=subprocess.PIPE
|
||||
).stdout
|
||||
sys_path = json.loads(out.decode('utf-8'))
|
||||
return find_module_path_using_sys_path(module, sys_path)
|
||||
|
||||
|
||||
def find_module_path_using_sys_path(module: str, sys_path: List[str]) -> Optional[str]:
|
||||
relative_candidates = (
|
||||
module.replace('.', '/') + '.py',
|
||||
os.path.join(module.replace('.', '/'), '__init__.py')
|
||||
)
|
||||
for base in sys_path:
|
||||
for relative_path in relative_candidates:
|
||||
path = os.path.join(base, relative_path)
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
return None
|
||||
|
||||
|
||||
def find_module_path_and_all_py3(inspect: ModuleInspect,
|
||||
module: str,
|
||||
verbose: bool) -> Optional[Tuple[Optional[str],
|
||||
Optional[List[str]]]]:
|
||||
"""Find module and determine __all__ for a Python 3 module.
|
||||
|
||||
Return None if the module is a C module. Return (module_path, __all__) if
|
||||
it is a Python module. Raise CantImport if import failed.
|
||||
"""
|
||||
if module in NOT_IMPORTABLE_MODULES:
|
||||
raise CantImport(module, '')
|
||||
|
||||
# TODO: Support custom interpreters.
|
||||
if verbose:
|
||||
print('Trying to import %r for runtime introspection' % module)
|
||||
try:
|
||||
mod = inspect.get_package_properties(module)
|
||||
except InspectError as e:
|
||||
# Fall back to finding the module using sys.path.
|
||||
path = find_module_path_using_sys_path(module, sys.path)
|
||||
if path is None:
|
||||
raise CantImport(module, str(e)) from e
|
||||
return path, None
|
||||
if mod.is_c_module:
|
||||
return None
|
||||
return mod.file, mod.all
|
||||
|
||||
|
||||
@contextmanager
|
||||
def generate_guarded(mod: str, target: str,
|
||||
ignore_errors: bool = True, verbose: bool = False) -> Iterator[None]:
|
||||
"""Ignore or report errors during stub generation.
|
||||
|
||||
Optionally report success.
|
||||
"""
|
||||
if verbose:
|
||||
print('Processing %s' % mod)
|
||||
try:
|
||||
yield
|
||||
except Exception as e:
|
||||
if not ignore_errors:
|
||||
raise e
|
||||
else:
|
||||
# --ignore-errors was passed
|
||||
print("Stub generation failed for", mod, file=sys.stderr)
|
||||
else:
|
||||
if verbose:
|
||||
print('Created %s' % target)
|
||||
|
||||
|
||||
PY2_MODULES = {'cStringIO', 'urlparse', 'collections.UserDict'}
|
||||
|
||||
|
||||
def report_missing(mod: str, message: Optional[str] = '', traceback: str = '') -> None:
|
||||
if message:
|
||||
message = ' with error: ' + message
|
||||
print('{}: Failed to import, skipping{}'.format(mod, message))
|
||||
m = re.search(r"ModuleNotFoundError: No module named '([^']*)'", traceback)
|
||||
if m:
|
||||
missing_module = m.group(1)
|
||||
if missing_module in PY2_MODULES:
|
||||
print('note: Try --py2 for Python 2 mode')
|
||||
|
||||
|
||||
def fail_missing(mod: str, reason: ModuleNotFoundReason) -> None:
|
||||
if reason is ModuleNotFoundReason.NOT_FOUND:
|
||||
clarification = "(consider using --search-path)"
|
||||
elif reason is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS:
|
||||
clarification = "(module likely exists, but is not PEP 561 compatible)"
|
||||
else:
|
||||
clarification = "(unknown reason '{}')".format(reason)
|
||||
raise SystemExit("Can't find module '{}' {}".format(mod, clarification))
|
||||
|
||||
|
||||
@overload
|
||||
def remove_misplaced_type_comments(source: bytes) -> bytes: ...
|
||||
|
||||
|
||||
@overload
|
||||
def remove_misplaced_type_comments(source: str) -> str: ...
|
||||
|
||||
|
||||
def remove_misplaced_type_comments(source: Union[str, bytes]) -> Union[str, bytes]:
|
||||
"""Remove comments from source that could be understood as misplaced type comments.
|
||||
|
||||
Normal comments may look like misplaced type comments, and since they cause blocking
|
||||
parse errors, we want to avoid them.
|
||||
"""
|
||||
if isinstance(source, bytes):
|
||||
# This gives us a 1-1 character code mapping, so it's roundtrippable.
|
||||
text = source.decode('latin1')
|
||||
else:
|
||||
text = source
|
||||
|
||||
# Remove something that looks like a variable type comment but that's by itself
|
||||
# on a line, as it will often generate a parse error (unless it's # type: ignore).
|
||||
text = re.sub(r'^[ \t]*# +type: +["\'a-zA-Z_].*$', '', text, flags=re.MULTILINE)
|
||||
|
||||
# Remove something that looks like a function type comment after docstring,
|
||||
# which will result in a parse error.
|
||||
text = re.sub(r'""" *\n[ \t\n]*# +type: +\(.*$', '"""\n', text, flags=re.MULTILINE)
|
||||
text = re.sub(r"''' *\n[ \t\n]*# +type: +\(.*$", "'''\n", text, flags=re.MULTILINE)
|
||||
|
||||
# Remove something that looks like a badly formed function type comment.
|
||||
text = re.sub(r'^[ \t]*# +type: +\([^()]+(\)[ \t]*)?$', '', text, flags=re.MULTILINE)
|
||||
|
||||
if isinstance(source, bytes):
|
||||
return text.encode('latin1')
|
||||
else:
|
||||
return text
|
||||
|
||||
|
||||
def common_dir_prefix(paths: List[str]) -> str:
|
||||
if not paths:
|
||||
return '.'
|
||||
cur = os.path.dirname(os.path.normpath(paths[0]))
|
||||
for path in paths[1:]:
|
||||
while True:
|
||||
path = os.path.dirname(os.path.normpath(path))
|
||||
if (cur + os.sep).startswith(path + os.sep):
|
||||
cur = path
|
||||
break
|
||||
return cur or '.'
|
||||
Loading…
Add table
Add a link
Reference in a new issue