diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 29bd4af..937696b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,4 +39,4 @@ repos: rev: v1.9.0 hooks: - id: mypy - additional_dependencies: [types-all] \ No newline at end of file + additional_dependencies: [types-all] diff --git a/.venv/bin/Activate.ps1 b/.venv/bin/Activate.ps1 index b49d77b..354eb42 100644 --- a/.venv/bin/Activate.ps1 +++ b/.venv/bin/Activate.ps1 @@ -44,7 +44,7 @@ command: PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -For more information on Execution Policies: +For more information on Execution Policies: https://go.microsoft.com/fwlink/?LinkID=135170 #> diff --git a/.venv/bin/coverage b/.venv/bin/coverage index f1bc7bf..3ce3fa2 100755 --- a/.venv/bin/coverage +++ b/.venv/bin/coverage @@ -1,7 +1,9 @@ #!/Users/admin/Git_repos/pre-commit-hooks/.venv/bin/python3 -# -*- coding: utf-8 -*- +from __future__ import annotations + import re import sys + from coverage.cmdline import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) diff --git a/.venv/bin/coverage-3.10 b/.venv/bin/coverage-3.10 index f1bc7bf..3ce3fa2 100755 --- a/.venv/bin/coverage-3.10 +++ b/.venv/bin/coverage-3.10 @@ -1,7 +1,9 @@ #!/Users/admin/Git_repos/pre-commit-hooks/.venv/bin/python3 -# -*- coding: utf-8 -*- +from __future__ import annotations + import re import sys + from coverage.cmdline import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) diff --git a/.venv/bin/coverage3 b/.venv/bin/coverage3 index f1bc7bf..3ce3fa2 100755 --- a/.venv/bin/coverage3 +++ b/.venv/bin/coverage3 @@ -1,7 +1,9 @@ #!/Users/admin/Git_repos/pre-commit-hooks/.venv/bin/python3 -# -*- coding: utf-8 -*- +from __future__ import annotations + import re import sys + from coverage.cmdline import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) diff --git a/.venv/bin/pip b/.venv/bin/pip index 95f5226..f2be4ca 100755 --- a/.venv/bin/pip +++ b/.venv/bin/pip @@ -1,7 +1,9 @@ #!/Users/admin/Git_repos/pre-commit-hooks/.venv/bin/python3 -# -*- coding: utf-8 -*- +from __future__ import annotations + import re import sys + from pip._internal.cli.main import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 index 95f5226..f2be4ca 100755 --- a/.venv/bin/pip3 +++ b/.venv/bin/pip3 @@ -1,7 +1,9 @@ #!/Users/admin/Git_repos/pre-commit-hooks/.venv/bin/python3 -# -*- coding: utf-8 -*- +from __future__ import annotations + import re import sys + from pip._internal.cli.main import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) diff --git a/.venv/bin/pip3.10 b/.venv/bin/pip3.10 index 95f5226..f2be4ca 100755 --- a/.venv/bin/pip3.10 +++ b/.venv/bin/pip3.10 @@ -1,7 +1,9 @@ #!/Users/admin/Git_repos/pre-commit-hooks/.venv/bin/python3 -# -*- coding: utf-8 -*- +from __future__ import annotations + import re import sys + from pip._internal.cli.main import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) diff --git a/.venv/bin/py.test b/.venv/bin/py.test index 3dbdb4f..c1df613 100755 --- a/.venv/bin/py.test +++ b/.venv/bin/py.test @@ -1,7 +1,9 @@ #!/Users/admin/Git_repos/pre-commit-hooks/.venv/bin/python3 -# -*- coding: utf-8 -*- +from __future__ import annotations + import re import sys + from pytest import console_main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) diff --git a/.venv/bin/pytest b/.venv/bin/pytest index 3dbdb4f..c1df613 100755 --- a/.venv/bin/pytest +++ b/.venv/bin/pytest @@ -1,7 +1,9 @@ #!/Users/admin/Git_repos/pre-commit-hooks/.venv/bin/python3 -# -*- coding: utf-8 -*- +from __future__ import annotations + import re import sys + from pytest import console_main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) diff --git a/.venv/lib/python3.10/site-packages/_distutils_hack/__init__.py b/.venv/lib/python3.10/site-packages/_distutils_hack/__init__.py index 5f40996..84f7c19 100644 --- a/.venv/lib/python3.10/site-packages/_distutils_hack/__init__.py +++ b/.venv/lib/python3.10/site-packages/_distutils_hack/__init__.py @@ -1,16 +1,20 @@ -import sys +from __future__ import annotations + +import importlib import os import re -import importlib +import sys import warnings is_pypy = '__pypy__' in sys.builtin_module_names -warnings.filterwarnings('ignore', - r'.+ distutils\b.+ deprecated', - DeprecationWarning) +warnings.filterwarnings( + 'ignore', + r'.+ distutils\b.+ deprecated', + DeprecationWarning, +) def warn_distutils_present(): @@ -21,18 +25,19 @@ def warn_distutils_present(): # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 return warnings.warn( - "Distutils was imported before Setuptools, but importing Setuptools " - "also replaces the `distutils` module in `sys.modules`. This may lead " - "to undesirable behaviors or errors. To avoid these issues, avoid " - "using distutils directly, ensure that setuptools is installed in the " - "traditional way (e.g. not an editable install), and/or make sure " - "that setuptools is always imported before distutils.") + 'Distutils was imported before Setuptools, but importing Setuptools ' + 'also replaces the `distutils` module in `sys.modules`. This may lead ' + 'to undesirable behaviors or errors. To avoid these issues, avoid ' + 'using distutils directly, ensure that setuptools is installed in the ' + 'traditional way (e.g. not an editable install), and/or make sure ' + 'that setuptools is always imported before distutils.', + ) def clear_distutils(): if 'distutils' not in sys.modules: return - warnings.warn("Setuptools is replacing distutils.") + warnings.warn('Setuptools is replacing distutils.') mods = [name for name in sys.modules if re.match(r'distutils\b', name)] for name in mods: del sys.modules[name] @@ -74,7 +79,7 @@ class DistutilsMetaFinder: if path is not None: return - method_name = 'spec_for_{fullname}'.format(**locals()) + method_name = f'spec_for_{fullname}' method = getattr(self, method_name, lambda: None) return method() diff --git a/.venv/lib/python3.10/site-packages/_distutils_hack/override.py b/.venv/lib/python3.10/site-packages/_distutils_hack/override.py index 2cc433a..d427a73 100644 --- a/.venv/lib/python3.10/site-packages/_distutils_hack/override.py +++ b/.venv/lib/python3.10/site-packages/_distutils_hack/override.py @@ -1 +1,2 @@ +from __future__ import annotations __import__('_distutils_hack').do_override() diff --git a/.venv/lib/python3.10/site-packages/_pytest/__init__.py b/.venv/lib/python3.10/site-packages/_pytest/__init__.py index 9062768..906d4b0 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/__init__.py +++ b/.venv/lib/python3.10/site-packages/_pytest/__init__.py @@ -1,4 +1,5 @@ -__all__ = ["__version__", "version_tuple"] +from __future__ import annotations +__all__ = ['__version__', 'version_tuple'] try: from ._version import version as __version__ @@ -6,5 +7,5 @@ try: except ImportError: # pragma: no cover # broken installation, we don't even try # unknown only works because we do poor mans version compare - __version__ = "unknown" - version_tuple = (0, 0, "unknown") # type:ignore[assignment] + __version__ = 'unknown' + version_tuple = (0, 0, 'unknown') # type:ignore[assignment] diff --git a/.venv/lib/python3.10/site-packages/_pytest/_argcomplete.py b/.venv/lib/python3.10/site-packages/_pytest/_argcomplete.py index c24f925..3663177 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_argcomplete.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_argcomplete.py @@ -61,11 +61,12 @@ If things do not work right away: which should throw a KeyError: 'COMPLINE' (which is properly set by the global argcomplete script). """ +from __future__ import annotations import argparse -from glob import glob import os import sys +from glob import glob from typing import Any from typing import List from typing import Optional @@ -77,7 +78,7 @@ class FastFilesCompleter: def __init__(self, directories: bool = True) -> None: self.directories = directories - def __call__(self, prefix: str, **kwargs: Any) -> List[str]: + def __call__(self, prefix: str, **kwargs: Any) -> list[str]: # Only called on non option completions. if os.sep in prefix[1:]: prefix_dir = len(os.path.dirname(prefix) + os.sep) @@ -85,26 +86,26 @@ class FastFilesCompleter: prefix_dir = 0 completion = [] globbed = [] - if "*" not in prefix and "?" not in prefix: + if '*' not in prefix and '?' not in prefix: # We are on unix, otherwise no bash. if not prefix or prefix[-1] == os.sep: - globbed.extend(glob(prefix + ".*")) - prefix += "*" + globbed.extend(glob(prefix + '.*')) + prefix += '*' globbed.extend(glob(prefix)) for x in sorted(globbed): if os.path.isdir(x): - x += "/" + x += '/' # Append stripping the prefix (like bash, not like compgen). completion.append(x[prefix_dir:]) return completion -if os.environ.get("_ARGCOMPLETE"): +if os.environ.get('_ARGCOMPLETE'): try: import argcomplete.completers except ImportError: sys.exit(-1) - filescompleter: Optional[FastFilesCompleter] = FastFilesCompleter() + filescompleter: FastFilesCompleter | None = FastFilesCompleter() def try_argcomplete(parser: argparse.ArgumentParser) -> None: argcomplete.autocomplete(parser, always_complete_options=False) diff --git a/.venv/lib/python3.10/site-packages/_pytest/_code/__init__.py b/.venv/lib/python3.10/site-packages/_pytest/_code/__init__.py index b0a418e..4f3721d 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_code/__init__.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_code/__init__.py @@ -1,4 +1,5 @@ """Python inspection/code generation API.""" +from __future__ import annotations from .code import Code from .code import ExceptionInfo @@ -12,13 +13,13 @@ from .source import Source __all__ = [ - "Code", - "ExceptionInfo", - "filter_traceback", - "Frame", - "getfslineno", - "getrawcode", - "Traceback", - "TracebackEntry", - "Source", + 'Code', + 'ExceptionInfo', + 'filter_traceback', + 'Frame', + 'getfslineno', + 'getrawcode', + 'Traceback', + 'TracebackEntry', + 'Source', ] diff --git a/.venv/lib/python3.10/site-packages/_pytest/_code/code.py b/.venv/lib/python3.10/site-packages/_pytest/_code/code.py index 12168be..bc5b68a 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_code/code.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_code/code.py @@ -1,15 +1,17 @@ # mypy: allow-untyped-defs +from __future__ import annotations + import ast import dataclasses import inspect -from inspect import CO_VARARGS -from inspect import CO_VARKEYWORDS -from io import StringIO import os -from pathlib import Path import re import sys import traceback +from inspect import CO_VARARGS +from inspect import CO_VARKEYWORDS +from io import StringIO +from pathlib import Path from traceback import format_exception_only from types import CodeType from types import FrameType @@ -36,9 +38,8 @@ from typing import Type from typing import TypeVar from typing import Union -import pluggy - import _pytest +import pluggy from _pytest._code.source import findsource from _pytest._code.source import getrawcode from _pytest._code.source import getstatementrange_ast @@ -55,19 +56,19 @@ from _pytest.pathlib import bestrelpath if sys.version_info[:2] < (3, 11): from exceptiongroup import BaseExceptionGroup -_TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"] +_TracebackStyle = Literal['long', 'short', 'line', 'no', 'native', 'value', 'auto'] class Code: """Wrapper around Python code objects.""" - __slots__ = ("raw",) + __slots__ = ('raw',) def __init__(self, obj: CodeType) -> None: self.raw = obj @classmethod - def from_function(cls, obj: object) -> "Code": + def from_function(cls, obj: object) -> Code: return cls(getrawcode(obj)) def __eq__(self, other): @@ -85,16 +86,16 @@ class Code: return self.raw.co_name @property - def path(self) -> Union[Path, str]: + def path(self) -> Path | str: """Return a path object pointing to source code, or an ``str`` in case of ``OSError`` / non-existing file.""" if not self.raw.co_filename: - return "" + return '' try: p = absolutepath(self.raw.co_filename) # maybe don't try this checking if not p.exists(): - raise OSError("path check failed.") + raise OSError('path check failed.') return p except OSError: # XXX maybe try harder like the weird logic @@ -102,17 +103,17 @@ class Code: return self.raw.co_filename @property - def fullsource(self) -> Optional["Source"]: + def fullsource(self) -> Source | None: """Return a _pytest._code.Source object for the full source file of the code.""" full, _ = findsource(self.raw) return full - def source(self) -> "Source": + def source(self) -> Source: """Return a _pytest._code.Source object for the code object's source only.""" # return source only for that part of code return Source(self.raw) - def getargs(self, var: bool = False) -> Tuple[str, ...]: + def getargs(self, var: bool = False) -> tuple[str, ...]: """Return a tuple with the argument names for the code object. If 'var' is set True also return the names of the variable and @@ -131,7 +132,7 @@ class Frame: """Wrapper around a Python frame holding f_locals and f_globals in which expressions can be evaluated.""" - __slots__ = ("raw",) + __slots__ = ('raw',) def __init__(self, frame: FrameType) -> None: self.raw = frame @@ -141,11 +142,11 @@ class Frame: return self.raw.f_lineno - 1 @property - def f_globals(self) -> Dict[str, Any]: + def f_globals(self) -> dict[str, Any]: return self.raw.f_globals @property - def f_locals(self) -> Dict[str, Any]: + def f_locals(self) -> dict[str, Any]: return self.raw.f_locals @property @@ -153,10 +154,10 @@ class Frame: return Code(self.raw.f_code) @property - def statement(self) -> "Source": + def statement(self) -> Source: """Statement this frame is at.""" if self.code.fullsource is None: - return Source("") + return Source('') return self.code.fullsource.getstatement(self.lineno) def eval(self, code, **vars): @@ -192,19 +193,19 @@ class Frame: class TracebackEntry: """A single entry in a Traceback.""" - __slots__ = ("_rawentry", "_repr_style") + __slots__ = ('_rawentry', '_repr_style') def __init__( self, rawentry: TracebackType, - repr_style: Optional['Literal["short", "long"]'] = None, + repr_style: Literal["short", "long"] | None = None, ) -> None: - self._rawentry: "Final" = rawentry - self._repr_style: "Final" = repr_style + self._rawentry: Final = rawentry + self._repr_style: Final = repr_style def with_repr_style( - self, repr_style: Optional['Literal["short", "long"]'] - ) -> "TracebackEntry": + self, repr_style: Literal["short", "long"] | None, + ) -> TracebackEntry: return TracebackEntry(self._rawentry, repr_style) @property @@ -220,22 +221,22 @@ class TracebackEntry: return self.lineno - self.frame.code.firstlineno def __repr__(self) -> str: - return "" % (self.frame.code.path, self.lineno + 1) + return '' % (self.frame.code.path, self.lineno + 1) @property - def statement(self) -> "Source": + def statement(self) -> Source: """_pytest._code.Source object for the current statement.""" source = self.frame.code.fullsource assert source is not None return source.getstatement(self.lineno) @property - def path(self) -> Union[Path, str]: + def path(self) -> Path | str: """Path to the source code.""" return self.frame.code.path @property - def locals(self) -> Dict[str, Any]: + def locals(self) -> dict[str, Any]: """Locals of underlying frame.""" return self.frame.f_locals @@ -243,8 +244,8 @@ class TracebackEntry: return self.frame.code.firstlineno def getsource( - self, astcache: Optional[Dict[Union[str, Path], ast.AST]] = None - ) -> Optional["Source"]: + self, astcache: dict[str | Path, ast.AST] | None = None, + ) -> Source | None: """Return failing source code.""" # we use the passed in astcache to not reparse asttrees # within exception info printing @@ -259,7 +260,7 @@ class TracebackEntry: start = self.getfirstlinesource() try: astnode, _, end = getstatementrange_ast( - self.lineno, source, astnode=astnode + self.lineno, source, astnode=astnode, ) except SyntaxError: end = self.lineno + 1 @@ -270,7 +271,7 @@ class TracebackEntry: source = property(getsource) - def ishidden(self, excinfo: Optional["ExceptionInfo[BaseException]"]) -> bool: + def ishidden(self, excinfo: ExceptionInfo[BaseException] | None) -> bool: """Return True if the current frame has a var __tracebackhide__ resolving to True. @@ -279,16 +280,16 @@ class TracebackEntry: Mostly for internal use. """ - tbh: Union[ - bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool] - ] = False + tbh: ( + bool | Callable[[ExceptionInfo[BaseException] | None], bool] + ) = False for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals): # in normal cases, f_locals and f_globals are dictionaries # however via `exec(...)` / `eval(...)` they can be other types # (even incorrect types!). # as such, we suppress all exceptions while accessing __tracebackhide__ try: - tbh = maybe_ns_dct["__tracebackhide__"] + tbh = maybe_ns_dct['__tracebackhide__'] except Exception: pass else: @@ -304,11 +305,11 @@ class TracebackEntry: except KeyboardInterrupt: raise except BaseException: - line = "???" + line = '???' # This output does not quite match Python's repr for traceback entries, # but changing it to do so would break certain plugins. See # https://github.com/pytest-dev/pytest/pull/7535/ for details. - return " File %r:%d in %s\n %s\n" % ( + return ' File %r:%d in %s\n %s\n' % ( str(self.path), self.lineno + 1, name, @@ -326,13 +327,13 @@ class Traceback(List[TracebackEntry]): def __init__( self, - tb: Union[TracebackType, Iterable[TracebackEntry]], + tb: TracebackType | Iterable[TracebackEntry], ) -> None: """Initialize from given python traceback object and ExceptionInfo.""" if isinstance(tb, TracebackType): def f(cur: TracebackType) -> Iterable[TracebackEntry]: - cur_: Optional[TracebackType] = cur + cur_: TracebackType | None = cur while cur_ is not None: yield TracebackEntry(cur_) cur_ = cur_.tb_next @@ -343,11 +344,11 @@ class Traceback(List[TracebackEntry]): def cut( self, - path: Optional[Union["os.PathLike[str]", str]] = None, - lineno: Optional[int] = None, - firstlineno: Optional[int] = None, - excludepath: Optional["os.PathLike[str]"] = None, - ) -> "Traceback": + path: os.PathLike[str] | str | None = None, + lineno: int | None = None, + firstlineno: int | None = None, + excludepath: os.PathLike[str] | None = None, + ) -> Traceback: """Return a Traceback instance wrapping part of this Traceback. By providing any combination of path, lineno and firstlineno, the @@ -365,9 +366,9 @@ class Traceback(List[TracebackEntry]): if path is not None and str(codepath) != path_: continue if ( - excludepath is not None - and isinstance(codepath, Path) - and excludepath_ in (str(p) for p in codepath.parents) # type: ignore[operator] + excludepath is not None and + isinstance(codepath, Path) and + excludepath_ in (str(p) for p in codepath.parents) # type: ignore[operator] ): continue if lineno is not None and x.lineno != lineno: @@ -378,16 +379,16 @@ class Traceback(List[TracebackEntry]): return self @overload - def __getitem__(self, key: "SupportsIndex") -> TracebackEntry: + def __getitem__(self, key: SupportsIndex) -> TracebackEntry: ... @overload - def __getitem__(self, key: slice) -> "Traceback": + def __getitem__(self, key: slice) -> Traceback: ... def __getitem__( - self, key: Union["SupportsIndex", slice] - ) -> Union[TracebackEntry, "Traceback"]: + self, key: SupportsIndex | slice, + ) -> TracebackEntry | Traceback: if isinstance(key, slice): return self.__class__(super().__getitem__(key)) else: @@ -395,12 +396,12 @@ class Traceback(List[TracebackEntry]): def filter( self, - excinfo_or_fn: Union[ - "ExceptionInfo[BaseException]", - Callable[[TracebackEntry], bool], - ], + excinfo_or_fn: ( + ExceptionInfo[BaseException] | + Callable[[TracebackEntry], bool] + ), /, - ) -> "Traceback": + ) -> Traceback: """Return a Traceback instance with certain items removed. If the filter is an `ExceptionInfo`, removes all the ``TracebackEntry``s @@ -411,15 +412,15 @@ class Traceback(List[TracebackEntry]): be added to the ``Traceback``, False when not. """ if isinstance(excinfo_or_fn, ExceptionInfo): - fn = lambda x: not x.ishidden(excinfo_or_fn) # noqa: E731 + def fn(x): return not x.ishidden(excinfo_or_fn) # noqa: E731 else: fn = excinfo_or_fn return Traceback(filter(fn, self)) - def recursionindex(self) -> Optional[int]: + def recursionindex(self) -> int | None: """Return the index of the frame/TracebackEntry where recursion originates if appropriate, None if no recursion occurred.""" - cache: Dict[Tuple[Any, int, int], List[Dict[str, Any]]] = {} + cache: dict[tuple[Any, int, int], list[dict[str, Any]]] = {} for i, entry in enumerate(self): # id for the code.raw is needed to work around # the strange metaprogramming in the decorator lib from pypi @@ -438,7 +439,7 @@ class Traceback(List[TracebackEntry]): return None -E = TypeVar("E", bound=BaseException, covariant=True) +E = TypeVar('E', bound=BaseException, covariant=True) @final @@ -448,15 +449,15 @@ class ExceptionInfo(Generic[E]): _assert_start_repr: ClassVar = "AssertionError('assert " - _excinfo: Optional[Tuple[Type["E"], "E", TracebackType]] + _excinfo: tuple[type[E], E, TracebackType] | None _striptext: str - _traceback: Optional[Traceback] + _traceback: Traceback | None def __init__( self, - excinfo: Optional[Tuple[Type["E"], "E", TracebackType]], - striptext: str = "", - traceback: Optional[Traceback] = None, + excinfo: tuple[type[E], E, TracebackType] | None, + striptext: str = '', + traceback: Traceback | None = None, *, _ispytest: bool = False, ) -> None: @@ -472,8 +473,8 @@ class ExceptionInfo(Generic[E]): # This is OK to ignore because this class is (conceptually) readonly. # See https://github.com/python/mypy/issues/7049. exception: E, # type: ignore[misc] - exprinfo: Optional[str] = None, - ) -> "ExceptionInfo[E]": + exprinfo: str | None = None, + ) -> ExceptionInfo[E]: """Return an ExceptionInfo for an existing exception. The exception must have a non-``None`` ``__traceback__`` attribute, @@ -489,8 +490,8 @@ class ExceptionInfo(Generic[E]): .. versionadded:: 7.4 """ assert exception.__traceback__, ( - "Exceptions passed to ExcInfo.from_exception(...)" - " must have a non-None __traceback__." + 'Exceptions passed to ExcInfo.from_exception(...)' + ' must have a non-None __traceback__.' ) exc_info = (type(exception), exception, exception.__traceback__) return cls.from_exc_info(exc_info, exprinfo) @@ -498,24 +499,24 @@ class ExceptionInfo(Generic[E]): @classmethod def from_exc_info( cls, - exc_info: Tuple[Type[E], E, TracebackType], - exprinfo: Optional[str] = None, - ) -> "ExceptionInfo[E]": + exc_info: tuple[type[E], E, TracebackType], + exprinfo: str | None = None, + ) -> ExceptionInfo[E]: """Like :func:`from_exception`, but using old-style exc_info tuple.""" - _striptext = "" + _striptext = '' if exprinfo is None and isinstance(exc_info[1], AssertionError): - exprinfo = getattr(exc_info[1], "msg", None) + exprinfo = getattr(exc_info[1], 'msg', None) if exprinfo is None: exprinfo = saferepr(exc_info[1]) if exprinfo and exprinfo.startswith(cls._assert_start_repr): - _striptext = "AssertionError: " + _striptext = 'AssertionError: ' return cls(exc_info, _striptext, _ispytest=True) @classmethod def from_current( - cls, exprinfo: Optional[str] = None - ) -> "ExceptionInfo[BaseException]": + cls, exprinfo: str | None = None, + ) -> ExceptionInfo[BaseException]: """Return an ExceptionInfo matching the current traceback. .. warning:: @@ -528,28 +529,28 @@ class ExceptionInfo(Generic[E]): message/``__str__()``. """ tup = sys.exc_info() - assert tup[0] is not None, "no current exception" - assert tup[1] is not None, "no current exception" - assert tup[2] is not None, "no current exception" + assert tup[0] is not None, 'no current exception' + assert tup[1] is not None, 'no current exception' + assert tup[2] is not None, 'no current exception' exc_info = (tup[0], tup[1], tup[2]) return ExceptionInfo.from_exc_info(exc_info, exprinfo) @classmethod - def for_later(cls) -> "ExceptionInfo[E]": + def for_later(cls) -> ExceptionInfo[E]: """Return an unfilled ExceptionInfo.""" return cls(None, _ispytest=True) - def fill_unfilled(self, exc_info: Tuple[Type[E], E, TracebackType]) -> None: + def fill_unfilled(self, exc_info: tuple[type[E], E, TracebackType]) -> None: """Fill an unfilled ExceptionInfo created with ``for_later()``.""" - assert self._excinfo is None, "ExceptionInfo was already filled" + assert self._excinfo is None, 'ExceptionInfo was already filled' self._excinfo = exc_info @property - def type(self) -> Type[E]: + def type(self) -> type[E]: """The exception class.""" assert ( self._excinfo is not None - ), ".type can only be used after the context manager exits" + ), '.type can only be used after the context manager exits' return self._excinfo[0] @property @@ -557,7 +558,7 @@ class ExceptionInfo(Generic[E]): """The exception value.""" assert ( self._excinfo is not None - ), ".value can only be used after the context manager exits" + ), '.value can only be used after the context manager exits' return self._excinfo[1] @property @@ -565,7 +566,7 @@ class ExceptionInfo(Generic[E]): """The exception raw traceback.""" assert ( self._excinfo is not None - ), ".tb can only be used after the context manager exits" + ), '.tb can only be used after the context manager exits' return self._excinfo[2] @property @@ -573,7 +574,7 @@ class ExceptionInfo(Generic[E]): """The type name of the exception.""" assert ( self._excinfo is not None - ), ".typename can only be used after the context manager exits" + ), '.typename can only be used after the context manager exits' return self.type.__name__ @property @@ -589,8 +590,8 @@ class ExceptionInfo(Generic[E]): def __repr__(self) -> str: if self._excinfo is None: - return "" - return f"<{self.__class__.__name__} {saferepr(self._excinfo[1])} tblen={len(self.traceback)}>" + return '' + return f'<{self.__class__.__name__} {saferepr(self._excinfo[1])} tblen={len(self.traceback)}>' def exconly(self, tryshort: bool = False) -> str: """Return the exception as a string. @@ -601,15 +602,15 @@ class ExceptionInfo(Generic[E]): the beginning). """ lines = format_exception_only(self.type, self.value) - text = "".join(lines) + text = ''.join(lines) text = text.rstrip() if tryshort: if text.startswith(self._striptext): - text = text[len(self._striptext) :] + text = text[len(self._striptext):] return text def errisinstance( - self, exc: Union[Type[BaseException], Tuple[Type[BaseException], ...]] + self, exc: type[BaseException] | tuple[type[BaseException], ...], ) -> bool: """Return True if the exception is an instance of exc. @@ -617,7 +618,7 @@ class ExceptionInfo(Generic[E]): """ return isinstance(self.value, exc) - def _getreprcrash(self) -> Optional["ReprFileLocation"]: + def _getreprcrash(self) -> ReprFileLocation | None: # Find last non-hidden traceback entry that led to the exception of the # traceback, or None if all hidden. for i in range(-1, -len(self.traceback) - 1, -1): @@ -631,15 +632,15 @@ class ExceptionInfo(Generic[E]): def getrepr( self, showlocals: bool = False, - style: _TracebackStyle = "long", + style: _TracebackStyle = 'long', abspath: bool = False, - tbfilter: Union[ - bool, Callable[["ExceptionInfo[BaseException]"], Traceback] - ] = True, + tbfilter: ( + bool | Callable[[ExceptionInfo[BaseException]], Traceback] + ) = True, funcargs: bool = False, truncate_locals: bool = True, chain: bool = True, - ) -> Union["ReprExceptionInfo", "ExceptionChainRepr"]: + ) -> ReprExceptionInfo | ExceptionChainRepr: """Return str()able representation of this exception info. :param bool showlocals: @@ -675,14 +676,14 @@ class ExceptionInfo(Generic[E]): Added the ``chain`` parameter. """ - if style == "native": + if style == 'native': return ReprExceptionInfo( reprtraceback=ReprTracebackNative( traceback.format_exception( self.type, self.value, self.traceback[0]._rawentry if self.traceback else None, - ) + ), ), reprcrash=self._getreprcrash(), ) @@ -700,24 +701,24 @@ class ExceptionInfo(Generic[E]): def _stringify_exception(self, exc: BaseException) -> str: try: - notes = getattr(exc, "__notes__", []) + notes = getattr(exc, '__notes__', []) except KeyError: # Workaround for https://github.com/python/cpython/issues/98778 on # Python <= 3.9, and some 3.10 and 3.11 patch versions. - HTTPError = getattr(sys.modules.get("urllib.error", None), "HTTPError", ()) + HTTPError = getattr(sys.modules.get('urllib.error', None), 'HTTPError', ()) if sys.version_info[:2] <= (3, 11) and isinstance(exc, HTTPError): notes = [] else: raise - return "\n".join( + return '\n'.join( [ str(exc), *notes, - ] + ], ) - def match(self, regexp: Union[str, Pattern[str]]) -> "Literal[True]": + def match(self, regexp: str | Pattern[str]) -> Literal[True]: """Check whether the regular expression `regexp` matches the string representation of the exception using :func:`python:re.search`. @@ -725,9 +726,9 @@ class ExceptionInfo(Generic[E]): """ __tracebackhide__ = True value = self._stringify_exception(self.value) - msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}" + msg = f'Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}' if regexp == value: - msg += "\n Did you mean to `re.escape()` the regex?" + msg += '\n Did you mean to `re.escape()` the regex?' assert re.search(regexp, value), msg # Return True to allow for "assert excinfo.match()". return True @@ -735,9 +736,9 @@ class ExceptionInfo(Generic[E]): def _group_contains( self, exc_group: BaseExceptionGroup[BaseException], - expected_exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]], - match: Union[str, Pattern[str], None], - target_depth: Optional[int] = None, + expected_exception: type[BaseException] | tuple[type[BaseException], ...], + match: str | Pattern[str] | None, + target_depth: int | None = None, current_depth: int = 1, ) -> bool: """Return `True` if a `BaseExceptionGroup` contains a matching exception.""" @@ -747,7 +748,7 @@ class ExceptionInfo(Generic[E]): for exc in exc_group.exceptions: if isinstance(exc, BaseExceptionGroup): if self._group_contains( - exc, expected_exception, match, target_depth, current_depth + 1 + exc, expected_exception, match, target_depth, current_depth + 1, ): return True if (target_depth is not None) and (current_depth != target_depth): @@ -764,10 +765,10 @@ class ExceptionInfo(Generic[E]): def group_contains( self, - expected_exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]], + expected_exception: type[BaseException] | tuple[type[BaseException], ...], *, - match: Union[str, Pattern[str], None] = None, - depth: Optional[int] = None, + match: str | Pattern[str] | None = None, + depth: int | None = None, ) -> bool: """Check whether a captured exception group contains a matching exception. @@ -789,9 +790,9 @@ class ExceptionInfo(Generic[E]): If >= 1, will only match an exception if it's at the specified depth (depth = 1 being the exceptions contained within the topmost exception group). """ - msg = "Captured exception is not an instance of `BaseExceptionGroup`" + msg = 'Captured exception is not an instance of `BaseExceptionGroup`' assert isinstance(self.value, BaseExceptionGroup), msg - msg = "`depth` must be >= 1 if specified" + msg = '`depth` must be >= 1 if specified' assert (depth is None) or (depth >= 1), msg return self._group_contains(self.value, expected_exception, match, depth) @@ -801,21 +802,21 @@ class FormattedExcinfo: """Presenting information about failing Functions and Generators.""" # for traceback entries - flow_marker: ClassVar = ">" - fail_marker: ClassVar = "E" + flow_marker: ClassVar = '>' + fail_marker: ClassVar = 'E' showlocals: bool = False - style: _TracebackStyle = "long" + style: _TracebackStyle = 'long' abspath: bool = True - tbfilter: Union[bool, Callable[[ExceptionInfo[BaseException]], Traceback]] = True + tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] = True funcargs: bool = False truncate_locals: bool = True chain: bool = True - astcache: Dict[Union[str, Path], ast.AST] = dataclasses.field( - default_factory=dict, init=False, repr=False + astcache: dict[str | Path, ast.AST] = dataclasses.field( + default_factory=dict, init=False, repr=False, ) - def _getindent(self, source: "Source") -> int: + def _getindent(self, source: Source) -> int: # Figure out indent for the given source. try: s = str(source.getstatement(len(source) - 1)) @@ -830,13 +831,13 @@ class FormattedExcinfo: return 0 return 4 + (len(s) - len(s.lstrip())) - def _getentrysource(self, entry: TracebackEntry) -> Optional["Source"]: + def _getentrysource(self, entry: TracebackEntry) -> Source | None: source = entry.getsource(self.astcache) if source is not None: source = source.deindent() return source - def repr_args(self, entry: TracebackEntry) -> Optional["ReprFuncArgs"]: + def repr_args(self, entry: TracebackEntry) -> ReprFuncArgs | None: if self.funcargs: args = [] for argname, argvalue in entry.frame.getargs(var=True): @@ -846,11 +847,11 @@ class FormattedExcinfo: def get_source( self, - source: Optional["Source"], + source: Source | None, line_index: int = -1, - excinfo: Optional[ExceptionInfo[BaseException]] = None, + excinfo: ExceptionInfo[BaseException] | None = None, short: bool = False, - ) -> List[str]: + ) -> list[str]: """Return formatted and marked up source lines.""" lines = [] if source is not None and line_index < 0: @@ -858,16 +859,16 @@ class FormattedExcinfo: if source is None or line_index >= len(source.lines) or line_index < 0: # `line_index` could still be outside `range(len(source.lines))` if # we're processing AST with pathological position attributes. - source = Source("???") + source = Source('???') line_index = 0 - space_prefix = " " + space_prefix = ' ' if short: lines.append(space_prefix + source.lines[line_index].strip()) else: for line in source.lines[:line_index]: lines.append(space_prefix + line) - lines.append(self.flow_marker + " " + source.lines[line_index]) - for line in source.lines[line_index + 1 :]: + lines.append(self.flow_marker + ' ' + source.lines[line_index]) + for line in source.lines[line_index + 1:]: lines.append(space_prefix + line) if excinfo is not None: indent = 4 if short else self._getindent(source) @@ -879,11 +880,11 @@ class FormattedExcinfo: excinfo: ExceptionInfo[BaseException], indent: int = 4, markall: bool = False, - ) -> List[str]: + ) -> list[str]: lines = [] - indentstr = " " * indent + indentstr = ' ' * indent # Get the real exception information out. - exlines = excinfo.exconly(tryshort=True).split("\n") + exlines = excinfo.exconly(tryshort=True).split('\n') failindent = self.fail_marker + indentstr[1:] for line in exlines: lines.append(failindent + line) @@ -891,15 +892,15 @@ class FormattedExcinfo: failindent = indentstr return lines - def repr_locals(self, locals: Mapping[str, object]) -> Optional["ReprLocals"]: + def repr_locals(self, locals: Mapping[str, object]) -> ReprLocals | None: if self.showlocals: lines = [] - keys = [loc for loc in locals if loc[0] != "@"] + keys = [loc for loc in locals if loc[0] != '@'] keys.sort() for name in keys: value = locals[name] - if name == "__builtins__": - lines.append("__builtins__ = ") + if name == '__builtins__': + lines.append('__builtins__ = ') else: # This formatting could all be handled by the # _repr() function, which is only reprlib.Repr in @@ -909,7 +910,7 @@ class FormattedExcinfo: else: str_repr = safeformat(value) # if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)): - lines.append(f"{name:<10} = {str_repr}") + lines.append(f'{name:<10} = {str_repr}') # else: # self._line("%-10s =\\" % (name,)) # # XXX @@ -919,45 +920,45 @@ class FormattedExcinfo: def repr_traceback_entry( self, - entry: Optional[TracebackEntry], - excinfo: Optional[ExceptionInfo[BaseException]] = None, - ) -> "ReprEntry": - lines: List[str] = [] + entry: TracebackEntry | None, + excinfo: ExceptionInfo[BaseException] | None = None, + ) -> ReprEntry: + lines: list[str] = [] style = ( entry._repr_style if entry is not None and entry._repr_style is not None else self.style ) - if style in ("short", "long") and entry is not None: + if style in ('short', 'long') and entry is not None: source = self._getentrysource(entry) if source is None: - source = Source("???") + source = Source('???') line_index = 0 else: line_index = entry.lineno - entry.getfirstlinesource() - short = style == "short" + short = style == 'short' reprargs = self.repr_args(entry) if not short else None s = self.get_source(source, line_index, excinfo, short=short) lines.extend(s) if short: - message = "in %s" % (entry.name) + message = 'in %s' % (entry.name) else: - message = excinfo and excinfo.typename or "" + message = excinfo and excinfo.typename or '' entry_path = entry.path path = self._makepath(entry_path) reprfileloc = ReprFileLocation(path, entry.lineno + 1, message) localsrepr = self.repr_locals(entry.locals) return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style) - elif style == "value": + elif style == 'value': if excinfo: - lines.extend(str(excinfo.value).split("\n")) + lines.extend(str(excinfo.value).split('\n')) return ReprEntry(lines, None, None, None, style) else: if excinfo: lines.extend(self.get_exconly(excinfo, indent=4)) return ReprEntry(lines, None, None, None, style) - def _makepath(self, path: Union[Path, str]) -> str: + def _makepath(self, path: Path | str) -> str: if not self.abspath and isinstance(path, Path): try: np = bestrelpath(Path.cwd(), path) @@ -967,7 +968,7 @@ class FormattedExcinfo: return np return str(path) - def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback": + def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> ReprTraceback: traceback = excinfo.traceback if callable(self.tbfilter): traceback = self.tbfilter(excinfo) @@ -981,12 +982,12 @@ class FormattedExcinfo: if not traceback: if extraline is None: - extraline = "All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames." + extraline = 'All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.' entries = [self.repr_traceback_entry(None, excinfo)] return ReprTraceback(entries, extraline, style=self.style) last = traceback[-1] - if self.style == "value": + if self.style == 'value': entries = [self.repr_traceback_entry(last, excinfo)] return ReprTraceback(entries, None, style=self.style) @@ -997,8 +998,8 @@ class FormattedExcinfo: return ReprTraceback(entries, extraline, style=self.style) def _truncate_recursive_traceback( - self, traceback: Traceback - ) -> Tuple[Traceback, Optional[str]]: + self, traceback: Traceback, + ) -> tuple[Traceback, str | None]: """Truncate the given recursive traceback trying to find the starting point of the recursion. @@ -1015,18 +1016,18 @@ class FormattedExcinfo: recursionindex = traceback.recursionindex() except Exception as e: max_frames = 10 - extraline: Optional[str] = ( - "!!! Recursion error detected, but an error occurred locating the origin of recursion.\n" - " The following exception happened when comparing locals in the stack frame:\n" - f" {type(e).__name__}: {e!s}\n" - f" Displaying first and last {max_frames} stack frames out of {len(traceback)}." + extraline: str | None = ( + '!!! Recursion error detected, but an error occurred locating the origin of recursion.\n' + ' The following exception happened when comparing locals in the stack frame:\n' + f' {type(e).__name__}: {e!s}\n' + f' Displaying first and last {max_frames} stack frames out of {len(traceback)}.' ) # Type ignored because adding two instances of a List subtype # currently incorrectly has type List instead of the subtype. traceback = traceback[:max_frames] + traceback[-max_frames:] # type: ignore else: if recursionindex is not None: - extraline = "!!! Recursion detected (same locals & position)" + extraline = '!!! Recursion detected (same locals & position)' traceback = traceback[: recursionindex + 1] else: extraline = None @@ -1034,15 +1035,15 @@ class FormattedExcinfo: return traceback, extraline def repr_excinfo( - self, excinfo: ExceptionInfo[BaseException] - ) -> "ExceptionChainRepr": - repr_chain: List[ - Tuple[ReprTraceback, Optional[ReprFileLocation], Optional[str]] + self, excinfo: ExceptionInfo[BaseException], + ) -> ExceptionChainRepr: + repr_chain: list[ + tuple[ReprTraceback, ReprFileLocation | None, str | None] ] = [] - e: Optional[BaseException] = excinfo.value - excinfo_: Optional[ExceptionInfo[BaseException]] = excinfo + e: BaseException | None = excinfo.value + excinfo_: ExceptionInfo[BaseException] | None = excinfo descr = None - seen: Set[int] = set() + seen: set[int] = set() while e is not None and id(e) not in seen: seen.add(id(e)) @@ -1051,14 +1052,14 @@ class FormattedExcinfo: # full support for exception groups added to ExceptionInfo. # See https://github.com/pytest-dev/pytest/issues/9159 if isinstance(e, BaseExceptionGroup): - reprtraceback: Union[ - ReprTracebackNative, ReprTraceback - ] = ReprTracebackNative( + reprtraceback: ( + ReprTracebackNative | ReprTraceback + ) = ReprTracebackNative( traceback.format_exception( type(excinfo_.value), excinfo_.value, excinfo_.traceback[0]._rawentry, - ) + ), ) else: reprtraceback = self.repr_traceback(excinfo_) @@ -1067,7 +1068,7 @@ class FormattedExcinfo: # Fallback to native repr if the exception doesn't have a traceback: # ExceptionInfo objects require a full traceback to work. reprtraceback = ReprTracebackNative( - traceback.format_exception(type(e), e, None) + traceback.format_exception(type(e), e, None), ) reprcrash = None repr_chain += [(reprtraceback, reprcrash, descr)] @@ -1075,13 +1076,13 @@ class FormattedExcinfo: if e.__cause__ is not None and self.chain: e = e.__cause__ excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None - descr = "The above exception was the direct cause of the following exception:" + descr = 'The above exception was the direct cause of the following exception:' elif ( e.__context__ is not None and not e.__suppress_context__ and self.chain ): e = e.__context__ excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None - descr = "During handling of the above exception, another exception occurred:" + descr = 'During handling of the above exception, another exception occurred:' else: e = None repr_chain.reverse() @@ -1099,7 +1100,7 @@ class TerminalRepr: return io.getvalue().strip() def __repr__(self) -> str: - return f"<{self.__class__} instance at {id(self):0x}>" + return f'<{self.__class__} instance at {id(self):0x}>' def toterminal(self, tw: TerminalWriter) -> None: raise NotImplementedError() @@ -1109,13 +1110,13 @@ class TerminalRepr: @dataclasses.dataclass(eq=False) class ExceptionRepr(TerminalRepr): # Provided by subclasses. - reprtraceback: "ReprTraceback" - reprcrash: Optional["ReprFileLocation"] - sections: List[Tuple[str, str, str]] = dataclasses.field( - init=False, default_factory=list + reprtraceback: ReprTraceback + reprcrash: ReprFileLocation | None + sections: list[tuple[str, str, str]] = dataclasses.field( + init=False, default_factory=list, ) - def addsection(self, name: str, content: str, sep: str = "-") -> None: + def addsection(self, name: str, content: str, sep: str = '-') -> None: self.sections.append((name, content, sep)) def toterminal(self, tw: TerminalWriter) -> None: @@ -1126,12 +1127,12 @@ class ExceptionRepr(TerminalRepr): @dataclasses.dataclass(eq=False) class ExceptionChainRepr(ExceptionRepr): - chain: Sequence[Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]] + chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]] def __init__( self, chain: Sequence[ - Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]] + tuple[ReprTraceback, ReprFileLocation | None, str | None] ], ) -> None: # reprcrash and reprtraceback of the outermost (the newest) exception @@ -1146,15 +1147,15 @@ class ExceptionChainRepr(ExceptionRepr): for element in self.chain: element[0].toterminal(tw) if element[2] is not None: - tw.line("") + tw.line('') tw.line(element[2], yellow=True) super().toterminal(tw) @dataclasses.dataclass(eq=False) class ReprExceptionInfo(ExceptionRepr): - reprtraceback: "ReprTraceback" - reprcrash: Optional["ReprFileLocation"] + reprtraceback: ReprTraceback + reprcrash: ReprFileLocation | None def toterminal(self, tw: TerminalWriter) -> None: self.reprtraceback.toterminal(tw) @@ -1163,24 +1164,24 @@ class ReprExceptionInfo(ExceptionRepr): @dataclasses.dataclass(eq=False) class ReprTraceback(TerminalRepr): - reprentries: Sequence[Union["ReprEntry", "ReprEntryNative"]] - extraline: Optional[str] + reprentries: Sequence[ReprEntry | ReprEntryNative] + extraline: str | None style: _TracebackStyle - entrysep: ClassVar = "_ " + entrysep: ClassVar = '_ ' def toterminal(self, tw: TerminalWriter) -> None: # The entries might have different styles. for i, entry in enumerate(self.reprentries): - if entry.style == "long": - tw.line("") + if entry.style == 'long': + tw.line('') entry.toterminal(tw) if i < len(self.reprentries) - 1: next_entry = self.reprentries[i + 1] if ( - entry.style == "long" - or entry.style == "short" - and next_entry.style == "long" + entry.style == 'long' or + entry.style == 'short' and + next_entry.style == 'long' ): tw.sep(self.entrysep) @@ -1192,25 +1193,25 @@ class ReprTracebackNative(ReprTraceback): def __init__(self, tblines: Sequence[str]) -> None: self.reprentries = [ReprEntryNative(tblines)] self.extraline = None - self.style = "native" + self.style = 'native' @dataclasses.dataclass(eq=False) class ReprEntryNative(TerminalRepr): lines: Sequence[str] - style: ClassVar[_TracebackStyle] = "native" + style: ClassVar[_TracebackStyle] = 'native' def toterminal(self, tw: TerminalWriter) -> None: - tw.write("".join(self.lines)) + tw.write(''.join(self.lines)) @dataclasses.dataclass(eq=False) class ReprEntry(TerminalRepr): lines: Sequence[str] - reprfuncargs: Optional["ReprFuncArgs"] - reprlocals: Optional["ReprLocals"] - reprfileloc: Optional["ReprFileLocation"] + reprfuncargs: ReprFuncArgs | None + reprlocals: ReprLocals | None + reprfileloc: ReprFileLocation | None style: _TracebackStyle def _write_entry_lines(self, tw: TerminalWriter) -> None: @@ -1232,11 +1233,11 @@ class ReprEntry(TerminalRepr): # separate indents and source lines that are not failures: we want to # highlight the code but not the indentation, which may contain markers # such as "> assert 0" - fail_marker = f"{FormattedExcinfo.fail_marker} " + fail_marker = f'{FormattedExcinfo.fail_marker} ' indent_size = len(fail_marker) - indents: List[str] = [] - source_lines: List[str] = [] - failure_lines: List[str] = [] + indents: list[str] = [] + source_lines: list[str] = [] + failure_lines: list[str] = [] for index, line in enumerate(self.lines): is_failure_line = line.startswith(fail_marker) if is_failure_line: @@ -1244,7 +1245,7 @@ class ReprEntry(TerminalRepr): failure_lines.extend(self.lines[index:]) break else: - if self.style == "value": + if self.style == 'value': source_lines.append(line) else: indents.append(line[:indent_size]) @@ -1257,12 +1258,12 @@ class ReprEntry(TerminalRepr): tw.line(line, bold=True, red=True) def toterminal(self, tw: TerminalWriter) -> None: - if self.style == "short": + if self.style == 'short': if self.reprfileloc: self.reprfileloc.toterminal(tw) self._write_entry_lines(tw) if self.reprlocals: - self.reprlocals.toterminal(tw, indent=" " * 8) + self.reprlocals.toterminal(tw, indent=' ' * 8) return if self.reprfuncargs: @@ -1271,16 +1272,16 @@ class ReprEntry(TerminalRepr): self._write_entry_lines(tw) if self.reprlocals: - tw.line("") + tw.line('') self.reprlocals.toterminal(tw) if self.reprfileloc: if self.lines: - tw.line("") + tw.line('') self.reprfileloc.toterminal(tw) def __str__(self) -> str: - return "{}\n{}\n{}".format( - "\n".join(self.lines), self.reprlocals, self.reprfileloc + return '{}\n{}\n{}'.format( + '\n'.join(self.lines), self.reprlocals, self.reprfileloc, ) @@ -1297,46 +1298,46 @@ class ReprFileLocation(TerminalRepr): # Filename and lineno output for each entry, using an output format # that most editors understand. msg = self.message - i = msg.find("\n") + i = msg.find('\n') if i != -1: msg = msg[:i] tw.write(self.path, bold=True, red=True) - tw.line(f":{self.lineno}: {msg}") + tw.line(f':{self.lineno}: {msg}') @dataclasses.dataclass(eq=False) class ReprLocals(TerminalRepr): lines: Sequence[str] - def toterminal(self, tw: TerminalWriter, indent="") -> None: + def toterminal(self, tw: TerminalWriter, indent='') -> None: for line in self.lines: tw.line(indent + line) @dataclasses.dataclass(eq=False) class ReprFuncArgs(TerminalRepr): - args: Sequence[Tuple[str, object]] + args: Sequence[tuple[str, object]] def toterminal(self, tw: TerminalWriter) -> None: if self.args: - linesofar = "" + linesofar = '' for name, value in self.args: - ns = f"{name} = {value}" + ns = f'{name} = {value}' if len(ns) + len(linesofar) + 2 > tw.fullwidth: if linesofar: tw.line(linesofar) linesofar = ns else: if linesofar: - linesofar += ", " + ns + linesofar += ', ' + ns else: linesofar = ns if linesofar: tw.line(linesofar) - tw.line("") + tw.line('') -def getfslineno(obj: object) -> Tuple[Union[str, Path], int]: +def getfslineno(obj: object) -> tuple[str | Path, int]: """Return source location (path, lineno) for the given object. If the source cannot be determined return ("", -1). @@ -1347,7 +1348,7 @@ def getfslineno(obj: object) -> Tuple[Union[str, Path], int]: # NOTE: this used to be done in _pytest.compat.getfslineno, initially added # in 6ec13a2b9. It ("place_as") appears to be something very custom. obj = get_real_func(obj) - if hasattr(obj, "place_as"): + if hasattr(obj, 'place_as'): obj = obj.place_as # type: ignore[attr-defined] try: @@ -1356,9 +1357,9 @@ def getfslineno(obj: object) -> Tuple[Union[str, Path], int]: try: fn = inspect.getsourcefile(obj) or inspect.getfile(obj) # type: ignore[arg-type] except TypeError: - return "", -1 + return '', -1 - fspath = fn and absolutepath(fn) or "" + fspath = fn and absolutepath(fn) or '' lineno = -1 if fspath: try: @@ -1375,9 +1376,9 @@ def getfslineno(obj: object) -> Tuple[Union[str, Path], int]: # note: if we need to add more paths than what we have now we should probably use a list # for better maintenance. -_PLUGGY_DIR = Path(pluggy.__file__.rstrip("oc")) +_PLUGGY_DIR = Path(pluggy.__file__.rstrip('oc')) # pluggy is either a package or a single module depending on the version -if _PLUGGY_DIR.name == "__init__.py": +if _PLUGGY_DIR.name == '__init__.py': _PLUGGY_DIR = _PLUGGY_DIR.parent _PYTEST_DIR = Path(_pytest.__file__).parent @@ -1394,7 +1395,7 @@ def filter_traceback(entry: TracebackEntry) -> bool: # points to dynamically generated code. # See https://bitbucket.org/pytest-dev/py/issues/71. raw_filename = entry.frame.code.raw.co_filename - is_generated = "<" in raw_filename and ">" in raw_filename + is_generated = '<' in raw_filename and '>' in raw_filename if is_generated: return False diff --git a/.venv/lib/python3.10/site-packages/_pytest/_code/source.py b/.venv/lib/python3.10/site-packages/_pytest/_code/source.py index dac3c38..8b09927 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_code/source.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_code/source.py @@ -1,10 +1,13 @@ # mypy: allow-untyped-defs +from __future__ import annotations + import ast -from bisect import bisect_right import inspect import textwrap import tokenize import types +import warnings +from bisect import bisect_right from typing import Iterable from typing import Iterator from typing import List @@ -12,7 +15,6 @@ from typing import Optional from typing import overload from typing import Tuple from typing import Union -import warnings class Source: @@ -23,20 +25,20 @@ class Source: def __init__(self, obj: object = None) -> None: if not obj: - self.lines: List[str] = [] + self.lines: list[str] = [] elif isinstance(obj, Source): self.lines = obj.lines elif isinstance(obj, (tuple, list)): - self.lines = deindent(x.rstrip("\n") for x in obj) + self.lines = deindent(x.rstrip('\n') for x in obj) elif isinstance(obj, str): - self.lines = deindent(obj.split("\n")) + self.lines = deindent(obj.split('\n')) else: try: rawcode = getrawcode(obj) src = inspect.getsource(rawcode) except TypeError: src = inspect.getsource(obj) # type: ignore[arg-type] - self.lines = deindent(src.split("\n")) + self.lines = deindent(src.split('\n')) def __eq__(self, other: object) -> bool: if not isinstance(other, Source): @@ -51,17 +53,17 @@ class Source: ... @overload - def __getitem__(self, key: slice) -> "Source": + def __getitem__(self, key: slice) -> Source: ... - def __getitem__(self, key: Union[int, slice]) -> Union[str, "Source"]: + def __getitem__(self, key: int | slice) -> str | Source: if isinstance(key, int): return self.lines[key] else: if key.step not in (None, 1): - raise IndexError("cannot slice a Source with a step") + raise IndexError('cannot slice a Source with a step') newsource = Source() - newsource.lines = self.lines[key.start : key.stop] + newsource.lines = self.lines[key.start: key.stop] return newsource def __iter__(self) -> Iterator[str]: @@ -70,7 +72,7 @@ class Source: def __len__(self) -> int: return len(self.lines) - def strip(self) -> "Source": + def strip(self) -> Source: """Return new Source object with trailing and leading blank lines removed.""" start, end = 0, len(self) while start < end and not self.lines[start].strip(): @@ -81,35 +83,35 @@ class Source: source.lines[:] = self.lines[start:end] return source - def indent(self, indent: str = " " * 4) -> "Source": + def indent(self, indent: str = ' ' * 4) -> Source: """Return a copy of the source object with all lines indented by the given indent-string.""" newsource = Source() newsource.lines = [(indent + line) for line in self.lines] return newsource - def getstatement(self, lineno: int) -> "Source": + def getstatement(self, lineno: int) -> Source: """Return Source statement which contains the given linenumber (counted from 0).""" start, end = self.getstatementrange(lineno) return self[start:end] - def getstatementrange(self, lineno: int) -> Tuple[int, int]: + def getstatementrange(self, lineno: int) -> tuple[int, int]: """Return (start, end) tuple which spans the minimal statement region which containing the given lineno.""" if not (0 <= lineno < len(self)): - raise IndexError("lineno out of range") + raise IndexError('lineno out of range') ast, start, end = getstatementrange_ast(lineno, self) return start, end - def deindent(self) -> "Source": + def deindent(self) -> Source: """Return a new Source object deindented.""" newsource = Source() newsource.lines[:] = deindent(self.lines) return newsource def __str__(self) -> str: - return "\n".join(self.lines) + return '\n'.join(self.lines) # @@ -117,7 +119,7 @@ class Source: # -def findsource(obj) -> Tuple[Optional[Source], int]: +def findsource(obj) -> tuple[Source | None, int]: try: sourcelines, lineno = inspect.findsource(obj) except Exception: @@ -134,20 +136,20 @@ def getrawcode(obj: object, trycall: bool = True) -> types.CodeType: except AttributeError: pass if trycall: - call = getattr(obj, "__call__", None) + call = getattr(obj, '__call__', None) if call and not isinstance(obj, type): return getrawcode(call, trycall=False) - raise TypeError(f"could not get code object for {obj!r}") + raise TypeError(f'could not get code object for {obj!r}') -def deindent(lines: Iterable[str]) -> List[str]: - return textwrap.dedent("\n".join(lines)).splitlines() +def deindent(lines: Iterable[str]) -> list[str]: + return textwrap.dedent('\n'.join(lines)).splitlines() -def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[int]]: +def get_statement_startend2(lineno: int, node: ast.AST) -> tuple[int, int | None]: # Flatten all statements and except handlers into one lineno-list. # AST's line numbers start indexing at 1. - values: List[int] = [] + values: list[int] = [] for x in ast.walk(node): if isinstance(x, (ast.stmt, ast.ExceptHandler)): # The lineno points to the class/def, so need to include the decorators. @@ -155,8 +157,8 @@ def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[i for d in x.decorator_list: values.append(d.lineno - 1) values.append(x.lineno - 1) - for name in ("finalbody", "orelse"): - val: Optional[List[ast.stmt]] = getattr(x, name, None) + for name in ('finalbody', 'orelse'): + val: list[ast.stmt] | None = getattr(x, name, None) if val: # Treat the finally/orelse part as its own statement. values.append(val[0].lineno - 1 - 1) @@ -174,15 +176,15 @@ def getstatementrange_ast( lineno: int, source: Source, assertion: bool = False, - astnode: Optional[ast.AST] = None, -) -> Tuple[ast.AST, int, int]: + astnode: ast.AST | None = None, +) -> tuple[ast.AST, int, int]: if astnode is None: content = str(source) # See #4260: # Don't produce duplicate warnings when compiling source to find AST. with warnings.catch_warnings(): - warnings.simplefilter("ignore") - astnode = ast.parse(content, "source", "exec") + warnings.simplefilter('ignore') + astnode = ast.parse(content, 'source', 'exec') start, end = get_statement_startend2(lineno, astnode) # We need to correct the end: @@ -200,7 +202,7 @@ def getstatementrange_ast( block_finder.started = ( bool(source.lines[start]) and source.lines[start][0].isspace() ) - it = ((x + "\n") for x in source.lines[start:end]) + it = ((x + '\n') for x in source.lines[start:end]) try: for tok in tokenize.generate_tokens(lambda: next(it)): block_finder.tokeneater(*tok) @@ -212,7 +214,7 @@ def getstatementrange_ast( # The end might still point to a comment or empty line, correct it. while end: line = source.lines[end - 1].lstrip() - if line.startswith("#") or not line: + if line.startswith('#') or not line: end -= 1 else: break diff --git a/.venv/lib/python3.10/site-packages/_pytest/_io/__init__.py b/.venv/lib/python3.10/site-packages/_pytest/_io/__init__.py index db001e9..368fa11 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_io/__init__.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_io/__init__.py @@ -1,8 +1,10 @@ +from __future__ import annotations + from .terminalwriter import get_terminal_width from .terminalwriter import TerminalWriter __all__ = [ - "TerminalWriter", - "get_terminal_width", + 'TerminalWriter', + 'get_terminal_width', ] diff --git a/.venv/lib/python3.10/site-packages/_pytest/_io/pprint.py b/.venv/lib/python3.10/site-packages/_pytest/_io/pprint.py index 75e9a71..9ebb076 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_io/pprint.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_io/pprint.py @@ -13,11 +13,13 @@ # tuples with fairly non-descriptive content. This is modeled very much # after Lisp/Scheme - style pretty-printing of lists. If you find it # useful, thank small children who sleep at night. +from __future__ import annotations + import collections as _collections import dataclasses as _dataclasses -from io import StringIO as _StringIO import re import types as _types +from io import StringIO as _StringIO from typing import Any from typing import Callable from typing import Dict @@ -39,7 +41,7 @@ class _safe_key: """ - __slots__ = ["obj"] + __slots__ = ['obj'] def __init__(self, obj): self.obj = obj @@ -64,7 +66,7 @@ class PrettyPrinter: self, indent: int = 4, width: int = 80, - depth: Optional[int] = None, + depth: int | None = None, ) -> None: """Handle pretty printing operations onto a stream using a set of configured parameters. @@ -80,11 +82,11 @@ class PrettyPrinter: """ if indent < 0: - raise ValueError("indent must be >= 0") + raise ValueError('indent must be >= 0') if depth is not None and depth <= 0: - raise ValueError("depth must be > 0") + raise ValueError('depth must be > 0') if not width: - raise ValueError("width must be != 0") + raise ValueError('width must be != 0') self._depth = depth self._indent_per_level = indent self._width = width @@ -100,7 +102,7 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: objid = id(object) @@ -114,17 +116,16 @@ class PrettyPrinter: p(self, object, stream, indent, allowance, context, level + 1) context.remove(objid) elif ( - _dataclasses.is_dataclass(object) - and not isinstance(object, type) - and object.__dataclass_params__.repr - and + _dataclasses.is_dataclass(object) and + not isinstance(object, type) and + object.__dataclass_params__.repr and # Check dataclass has generated repr method. - hasattr(object.__repr__, "__wrapped__") - and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ + hasattr(object.__repr__, '__wrapped__') and + '__create_fn__' in object.__repr__.__wrapped__.__qualname__ ): context.add(objid) self._pprint_dataclass( - object, stream, indent, allowance, context, level + 1 + object, stream, indent, allowance, context, level + 1, ) context.remove(objid) else: @@ -136,7 +137,7 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: cls_name = object.__class__.__name__ @@ -145,13 +146,13 @@ class PrettyPrinter: for f in _dataclasses.fields(object) if f.repr ] - stream.write(cls_name + "(") + stream.write(cls_name + '(') self._format_namespace_items(items, stream, indent, allowance, context, level) - stream.write(")") + stream.write(')') - _dispatch: Dict[ + _dispatch: dict[ Callable[..., str], - Callable[["PrettyPrinter", Any, IO[str], int, int, Set[int], int], None], + Callable[[PrettyPrinter, Any, IO[str], int, int, set[int], int], None], ] = {} def _pprint_dict( @@ -160,14 +161,14 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: write = stream.write - write("{") + write('{') items = sorted(object.items(), key=_safe_tuple) self._format_dict_items(items, stream, indent, allowance, context, level) - write("}") + write('}') _dispatch[dict.__repr__] = _pprint_dict @@ -177,16 +178,16 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: if not len(object): stream.write(repr(object)) return cls = object.__class__ - stream.write(cls.__name__ + "(") + stream.write(cls.__name__ + '(') self._pprint_dict(object, stream, indent, allowance, context, level) - stream.write(")") + stream.write(')') _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict @@ -196,12 +197,12 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: - stream.write("[") + stream.write('[') self._format_items(object, stream, indent, allowance, context, level) - stream.write("]") + stream.write(']') _dispatch[list.__repr__] = _pprint_list @@ -211,12 +212,12 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: - stream.write("(") + stream.write('(') self._format_items(object, stream, indent, allowance, context, level) - stream.write(")") + stream.write(')') _dispatch[tuple.__repr__] = _pprint_tuple @@ -226,7 +227,7 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: if not len(object): @@ -234,11 +235,11 @@ class PrettyPrinter: return typ = object.__class__ if typ is set: - stream.write("{") - endchar = "}" + stream.write('{') + endchar = '}' else: - stream.write(typ.__name__ + "({") - endchar = "})" + stream.write(typ.__name__ + '({') + endchar = '})' object = sorted(object, key=_safe_key) self._format_items(object, stream, indent, allowance, context, level) stream.write(endchar) @@ -252,7 +253,7 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: write = stream.write @@ -273,12 +274,12 @@ class PrettyPrinter: chunks.append(rep) else: # A list of alternating (non-space, space) strings - parts = re.findall(r"\S*\s*", line) + parts = re.findall(r'\S*\s*', line) assert parts assert not parts[-1] parts.pop() # drop empty last part max_width2 = max_width - current = "" + current = '' for j, part in enumerate(parts): candidate = current + part if j == len(parts) - 1 and i == len(lines) - 1: @@ -295,13 +296,13 @@ class PrettyPrinter: write(rep) return if level == 1: - write("(") + write('(') for i, rep in enumerate(chunks): if i > 0: - write("\n" + " " * indent) + write('\n' + ' ' * indent) write(rep) if level == 1: - write(")") + write(')') _dispatch[str.__repr__] = _pprint_str @@ -311,7 +312,7 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: write = stream.write @@ -322,15 +323,15 @@ class PrettyPrinter: if parens: indent += 1 allowance += 1 - write("(") - delim = "" + write('(') + delim = '' for rep in _wrap_bytes_repr(object, self._width - indent, allowance): write(delim) write(rep) if not delim: - delim = "\n" + " " * indent + delim = '\n' + ' ' * indent if parens: - write(")") + write(')') _dispatch[bytes.__repr__] = _pprint_bytes @@ -340,15 +341,15 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: write = stream.write - write("bytearray(") + write('bytearray(') self._pprint_bytes( - bytes(object), stream, indent + 10, allowance + 1, context, level + 1 + bytes(object), stream, indent + 10, allowance + 1, context, level + 1, ) - write(")") + write(')') _dispatch[bytearray.__repr__] = _pprint_bytearray @@ -358,12 +359,12 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: - stream.write("mappingproxy(") + stream.write('mappingproxy(') self._format(object.copy(), stream, indent, allowance, context, level) - stream.write(")") + stream.write(')') _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy @@ -373,29 +374,29 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: if type(object) is _types.SimpleNamespace: # The SimpleNamespace repr is "namespace" instead of the class # name, so we do the same here. For subclasses; use the class name. - cls_name = "namespace" + cls_name = 'namespace' else: cls_name = object.__class__.__name__ items = object.__dict__.items() - stream.write(cls_name + "(") + stream.write(cls_name + '(') self._format_namespace_items(items, stream, indent, allowance, context, level) - stream.write(")") + stream.write(')') _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace def _format_dict_items( self, - items: List[Tuple[Any, Any]], + items: list[tuple[Any, Any]], stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: if not items: @@ -403,23 +404,23 @@ class PrettyPrinter: write = stream.write item_indent = indent + self._indent_per_level - delimnl = "\n" + " " * item_indent + delimnl = '\n' + ' ' * item_indent for key, ent in items: write(delimnl) write(self._repr(key, context, level)) - write(": ") + write(': ') self._format(ent, stream, item_indent, 1, context, level) - write(",") + write(',') - write("\n" + " " * indent) + write('\n' + ' ' * indent) def _format_namespace_items( self, - items: List[Tuple[Any, Any]], + items: list[tuple[Any, Any]], stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: if not items: @@ -427,15 +428,15 @@ class PrettyPrinter: write = stream.write item_indent = indent + self._indent_per_level - delimnl = "\n" + " " * item_indent + delimnl = '\n' + ' ' * item_indent for key, ent in items: write(delimnl) write(key) - write("=") + write('=') if id(ent) in context: # Special-case representation of recursion to match standard # recursive dataclass repr. - write("...") + write('...') else: self._format( ent, @@ -446,17 +447,17 @@ class PrettyPrinter: level, ) - write(",") + write(',') - write("\n" + " " * indent) + write('\n' + ' ' * indent) def _format_items( self, - items: List[Any], + items: list[Any], stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: if not items: @@ -464,16 +465,16 @@ class PrettyPrinter: write = stream.write item_indent = indent + self._indent_per_level - delimnl = "\n" + " " * item_indent + delimnl = '\n' + ' ' * item_indent for item in items: write(delimnl) self._format(item, stream, item_indent, 1, context, level) - write(",") + write(',') - write("\n" + " " * indent) + write('\n' + ' ' * indent) - def _repr(self, object: Any, context: Set[int], level: int) -> str: + def _repr(self, object: Any, context: set[int], level: int) -> str: return self._safe_repr(object, context.copy(), self._depth, level) def _pprint_default_dict( @@ -482,13 +483,13 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: rdf = self._repr(object.default_factory, context, level) - stream.write(f"{object.__class__.__name__}({rdf}, ") + stream.write(f'{object.__class__.__name__}({rdf}, ') self._pprint_dict(object, stream, indent, allowance, context, level) - stream.write(")") + stream.write(')') _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict @@ -498,18 +499,18 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: - stream.write(object.__class__.__name__ + "(") + stream.write(object.__class__.__name__ + '(') if object: - stream.write("{") + stream.write('{') items = object.most_common() self._format_dict_items(items, stream, indent, allowance, context, level) - stream.write("}") + stream.write('}') - stream.write(")") + stream.write(')') _dispatch[_collections.Counter.__repr__] = _pprint_counter @@ -519,16 +520,16 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: if not len(object.maps) or (len(object.maps) == 1 and not len(object.maps[0])): stream.write(repr(object)) return - stream.write(object.__class__.__name__ + "(") + stream.write(object.__class__.__name__ + '(') self._format_items(object.maps, stream, indent, allowance, context, level) - stream.write(")") + stream.write(')') _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map @@ -538,16 +539,16 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: - stream.write(object.__class__.__name__ + "(") + stream.write(object.__class__.__name__ + '(') if object.maxlen is not None: - stream.write("maxlen=%d, " % object.maxlen) - stream.write("[") + stream.write('maxlen=%d, ' % object.maxlen) + stream.write('[') self._format_items(object, stream, indent, allowance + 1, context, level) - stream.write("])") + stream.write('])') _dispatch[_collections.deque.__repr__] = _pprint_deque @@ -557,7 +558,7 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: self._format(object.data, stream, indent, allowance, context, level - 1) @@ -570,7 +571,7 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: self._format(object.data, stream, indent, allowance, context, level - 1) @@ -583,7 +584,7 @@ class PrettyPrinter: stream: IO[str], indent: int, allowance: int, - context: Set[int], + context: set[int], level: int, ) -> None: self._format(object.data, stream, indent, allowance, context, level - 1) @@ -591,49 +592,49 @@ class PrettyPrinter: _dispatch[_collections.UserString.__repr__] = _pprint_user_string def _safe_repr( - self, object: Any, context: Set[int], maxlevels: Optional[int], level: int + self, object: Any, context: set[int], maxlevels: int | None, level: int, ) -> str: typ = type(object) if typ in _builtin_scalars: return repr(object) - r = getattr(typ, "__repr__", None) + r = getattr(typ, '__repr__', None) if issubclass(typ, dict) and r is dict.__repr__: if not object: - return "{}" + return '{}' objid = id(object) if maxlevels and level >= maxlevels: - return "{...}" + return '{...}' if objid in context: return _recursion(object) context.add(objid) - components: List[str] = [] + components: list[str] = [] append = components.append level += 1 for k, v in sorted(object.items(), key=_safe_tuple): krepr = self._safe_repr(k, context, maxlevels, level) vrepr = self._safe_repr(v, context, maxlevels, level) - append(f"{krepr}: {vrepr}") + append(f'{krepr}: {vrepr}') context.remove(objid) - return "{%s}" % ", ".join(components) + return '{%s}' % ', '.join(components) if (issubclass(typ, list) and r is list.__repr__) or ( issubclass(typ, tuple) and r is tuple.__repr__ ): if issubclass(typ, list): if not object: - return "[]" - format = "[%s]" + return '[]' + format = '[%s]' elif len(object) == 1: - format = "(%s,)" + format = '(%s,)' else: if not object: - return "()" - format = "(%s)" + return '()' + format = '(%s)' objid = id(object) if maxlevels and level >= maxlevels: - return format % "..." + return format % '...' if objid in context: return _recursion(object) context.add(objid) @@ -644,25 +645,25 @@ class PrettyPrinter: orepr = self._safe_repr(o, context, maxlevels, level) append(orepr) context.remove(objid) - return format % ", ".join(components) + return format % ', '.join(components) return repr(object) _builtin_scalars = frozenset( - {str, bytes, bytearray, float, complex, bool, type(None), int} + {str, bytes, bytearray, float, complex, bool, type(None), int}, ) def _recursion(object: Any) -> str: - return f"" + return f'' def _wrap_bytes_repr(object: Any, width: int, allowance: int) -> Iterator[str]: - current = b"" + current = b'' last = len(object) // 4 * 4 for i in range(0, len(object), 4): - part = object[i : i + 4] + part = object[i: i + 4] candidate = current + part if i == last: width -= allowance diff --git a/.venv/lib/python3.10/site-packages/_pytest/_io/saferepr.py b/.venv/lib/python3.10/site-packages/_pytest/_io/saferepr.py index 9f33fce..63059eb 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_io/saferepr.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_io/saferepr.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pprint import reprlib from typing import Optional @@ -18,9 +20,9 @@ def _format_repr_exception(exc: BaseException, obj: object) -> str: except (KeyboardInterrupt, SystemExit): raise except BaseException as exc: - exc_info = f"unpresentable exception ({_try_repr_or_str(exc)})" + exc_info = f'unpresentable exception ({_try_repr_or_str(exc)})' return ( - f"<[{exc_info} raised in repr()] {type(obj).__name__} object at 0x{id(obj):x}>" + f'<[{exc_info} raised in repr()] {type(obj).__name__} object at 0x{id(obj):x}>' ) @@ -28,7 +30,7 @@ def _ellipsize(s: str, maxsize: int) -> str: if len(s) > maxsize: i = max(0, (maxsize - 3) // 2) j = max(0, maxsize - 3 - i) - return s[:i] + "..." + s[len(s) - j :] + return s[:i] + '...' + s[len(s) - j:] return s @@ -38,7 +40,7 @@ class SafeRepr(reprlib.Repr): information on exceptions raised during the call. """ - def __init__(self, maxsize: Optional[int], use_ascii: bool = False) -> None: + def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: """ :param maxsize: If not None, will truncate the resulting repr to that specific size, using ellipsis @@ -97,7 +99,7 @@ DEFAULT_REPR_MAX_SIZE = 240 def saferepr( - obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False + obj: object, maxsize: int | None = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False, ) -> str: """Return a size-limited safe repr-string for the given object. diff --git a/.venv/lib/python3.10/site-packages/_pytest/_io/terminalwriter.py b/.venv/lib/python3.10/site-packages/_pytest/_io/terminalwriter.py index badbb7e..59d5b19 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_io/terminalwriter.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_io/terminalwriter.py @@ -1,4 +1,5 @@ """Helper functions for writing to terminals and files.""" +from __future__ import annotations import os import shutil @@ -26,16 +27,16 @@ def get_terminal_width() -> int: def should_do_markup(file: TextIO) -> bool: - if os.environ.get("PY_COLORS") == "1": + if os.environ.get('PY_COLORS') == '1': return True - if os.environ.get("PY_COLORS") == "0": + if os.environ.get('PY_COLORS') == '0': return False - if os.environ.get("NO_COLOR"): + if os.environ.get('NO_COLOR'): return False - if os.environ.get("FORCE_COLOR"): + if os.environ.get('FORCE_COLOR'): return True return ( - hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb" + hasattr(file, 'isatty') and file.isatty() and os.environ.get('TERM') != 'dumb' ) @@ -64,10 +65,10 @@ class TerminalWriter: invert=7, ) - def __init__(self, file: Optional[TextIO] = None) -> None: + def __init__(self, file: TextIO | None = None) -> None: if file is None: file = sys.stdout - if hasattr(file, "isatty") and file.isatty() and sys.platform == "win32": + if hasattr(file, 'isatty') and file.isatty() and sys.platform == 'win32': try: import colorama except ImportError: @@ -77,8 +78,8 @@ class TerminalWriter: assert file is not None self._file = file self.hasmarkup = should_do_markup(file) - self._current_line = "" - self._terminal_width: Optional[int] = None + self._current_line = '' + self._terminal_width: int | None = None self.code_highlight = True @property @@ -99,25 +100,25 @@ class TerminalWriter: def markup(self, text: str, **markup: bool) -> str: for name in markup: if name not in self._esctable: - raise ValueError(f"unknown markup: {name!r}") + raise ValueError(f'unknown markup: {name!r}') if self.hasmarkup: esc = [self._esctable[name] for name, on in markup.items() if on] if esc: - text = "".join("\x1b[%sm" % cod for cod in esc) + text + "\x1b[0m" + text = ''.join('\x1b[%sm' % cod for cod in esc) + text + '\x1b[0m' return text def sep( self, sepchar: str, - title: Optional[str] = None, - fullwidth: Optional[int] = None, + title: str | None = None, + fullwidth: int | None = None, **markup: bool, ) -> None: if fullwidth is None: fullwidth = self.fullwidth # The goal is to have the line be as long as possible # under the condition that len(line) <= fullwidth. - if sys.platform == "win32": + if sys.platform == 'win32': # If we print in the last column on windows we are on a # new line but there is no way to verify/neutralize this # (we may not know the exact line width). @@ -130,7 +131,7 @@ class TerminalWriter: # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1) fill = sepchar * N - line = f"{fill} {title} {fill}" + line = f'{fill} {title} {fill}' else: # we want len(sepchar)*N <= fullwidth # i.e. N <= fullwidth // len(sepchar) @@ -145,8 +146,8 @@ class TerminalWriter: def write(self, msg: str, *, flush: bool = False, **markup: bool) -> None: if msg: - current_line = msg.rsplit("\n", 1)[-1] - if "\n" in msg: + current_line = msg.rsplit('\n', 1)[-1] + if '\n' in msg: self._current_line = current_line else: self._current_line += current_line @@ -162,15 +163,15 @@ class TerminalWriter: # When the Unicode situation improves we should consider # letting the error propagate instead of masking it (see #7475 # for one brief attempt). - msg = msg.encode("unicode-escape").decode("ascii") + msg = msg.encode('unicode-escape').decode('ascii') self._file.write(msg) if flush: self.flush() - def line(self, s: str = "", **markup: bool) -> None: + def line(self, s: str = '', **markup: bool) -> None: self.write(s, **markup) - self.write("\n") + self.write('\n') def flush(self) -> None: self._file.flush() @@ -184,17 +185,17 @@ class TerminalWriter: """ if indents and len(indents) != len(lines): raise ValueError( - f"indents size ({len(indents)}) should have same size as lines ({len(lines)})" + f'indents size ({len(indents)}) should have same size as lines ({len(lines)})', ) if not indents: - indents = [""] * len(lines) - source = "\n".join(lines) + indents = [''] * len(lines) + source = '\n'.join(lines) new_lines = self._highlight(source).splitlines() for indent, new_line in zip(indents, new_lines): self.line(indent + new_line) def _highlight( - self, source: str, lexer: Literal["diff", "python"] = "python" + self, source: str, lexer: Literal['diff', 'python'] = 'python', ) -> str: """Highlight the given source if we have markup support.""" from _pytest.config.exceptions import UsageError @@ -205,9 +206,9 @@ class TerminalWriter: try: from pygments.formatters.terminal import TerminalFormatter - if lexer == "python": + if lexer == 'python': from pygments.lexers.python import PythonLexer as Lexer - elif lexer == "diff": + elif lexer == 'diff': from pygments.lexers.diff import DiffLexer as Lexer from pygments import highlight import pygments.util @@ -219,30 +220,30 @@ class TerminalWriter: source, Lexer(), TerminalFormatter( - bg=os.getenv("PYTEST_THEME_MODE", "dark"), - style=os.getenv("PYTEST_THEME"), + bg=os.getenv('PYTEST_THEME_MODE', 'dark'), + style=os.getenv('PYTEST_THEME'), ), ) # pygments terminal formatter may add a newline when there wasn't one. # We don't want this, remove. - if highlighted[-1] == "\n" and source[-1] != "\n": + if highlighted[-1] == '\n' and source[-1] != '\n': highlighted = highlighted[:-1] # Some lexers will not set the initial color explicitly # which may lead to the previous color being propagated to the # start of the expression, so reset first. - return "\x1b[0m" + highlighted + return '\x1b[0m' + highlighted except pygments.util.ClassNotFound as e: raise UsageError( "PYTEST_THEME environment variable had an invalid value: '{}'. " - "Only valid pygment styles are allowed.".format( - os.getenv("PYTEST_THEME") - ) + 'Only valid pygment styles are allowed.'.format( + os.getenv('PYTEST_THEME'), + ), ) from e except pygments.util.OptionError as e: raise UsageError( "PYTEST_THEME_MODE environment variable had an invalid value: '{}'. " "The only allowed values are 'dark' and 'light'.".format( - os.getenv("PYTEST_THEME_MODE") - ) + os.getenv('PYTEST_THEME_MODE'), + ), ) from e diff --git a/.venv/lib/python3.10/site-packages/_pytest/_io/wcwidth.py b/.venv/lib/python3.10/site-packages/_pytest/_io/wcwidth.py index 5380313..2a3bfbb 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_io/wcwidth.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_io/wcwidth.py @@ -1,5 +1,7 @@ -from functools import lru_cache +from __future__ import annotations + import unicodedata +from functools import lru_cache @lru_cache(100) @@ -17,25 +19,25 @@ def wcwidth(c: str) -> int: # Some Cf/Zp/Zl characters which should be zero-width. if ( - o == 0x0000 - or 0x200B <= o <= 0x200F - or 0x2028 <= o <= 0x202E - or 0x2060 <= o <= 0x2063 + o == 0x0000 or + 0x200B <= o <= 0x200F or + 0x2028 <= o <= 0x202E or + 0x2060 <= o <= 0x2063 ): return 0 category = unicodedata.category(c) # Control characters. - if category == "Cc": + if category == 'Cc': return -1 # Combining characters with zero width. - if category in ("Me", "Mn"): + if category in ('Me', 'Mn'): return 0 # Full/Wide east asian characters. - if unicodedata.east_asian_width(c) in ("F", "W"): + if unicodedata.east_asian_width(c) in ('F', 'W'): return 2 return 1 @@ -47,7 +49,7 @@ def wcswidth(s: str) -> int: Returns -1 if the string contains non-printable characters. """ width = 0 - for c in unicodedata.normalize("NFC", s): + for c in unicodedata.normalize('NFC', s): wc = wcwidth(c) if wc < 0: return -1 diff --git a/.venv/lib/python3.10/site-packages/_pytest/_py/error.py b/.venv/lib/python3.10/site-packages/_pytest/_py/error.py index 68f1eed..157d76d 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_py/error.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_py/error.py @@ -1,5 +1,4 @@ """create errno-specific classes for IO or os calls.""" - from __future__ import annotations import errno @@ -13,25 +12,25 @@ from typing import TypeVar if TYPE_CHECKING: from typing_extensions import ParamSpec - P = ParamSpec("P") + P = ParamSpec('P') -R = TypeVar("R") +R = TypeVar('R') class Error(EnvironmentError): def __repr__(self) -> str: - return "{}.{} {!r}: {} ".format( + return '{}.{} {!r}: {} '.format( self.__class__.__module__, self.__class__.__name__, self.__class__.__doc__, - " ".join(map(str, self.args)), + ' '.join(map(str, self.args)), # repr(self.args) ) def __str__(self) -> str: - s = "[{}]: {}".format( + s = '[{}]: {}'.format( self.__class__.__doc__, - " ".join(map(str, self.args)), + ' '.join(map(str, self.args)), ) return s @@ -58,7 +57,7 @@ class ErrorMaker: _errno2class: dict[int, type[Error]] = {} def __getattr__(self, name: str) -> type[Error]: - if name[0] == "_": + if name[0] == '_': raise AttributeError(name) eno = getattr(errno, name) cls = self._geterrnoclass(eno) @@ -69,17 +68,17 @@ class ErrorMaker: try: return self._errno2class[eno] except KeyError: - clsname = errno.errorcode.get(eno, "UnknownErrno%d" % (eno,)) + clsname = errno.errorcode.get(eno, 'UnknownErrno%d' % (eno,)) errorcls = type( clsname, (Error,), - {"__module__": "py.error", "__doc__": os.strerror(eno)}, + {'__module__': 'py.error', '__doc__': os.strerror(eno)}, ) self._errno2class[eno] = errorcls return errorcls def checked_call( - self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs + self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs, ) -> R: """Call a function and raise an errno-exception if applicable.""" __tracebackhide__ = True @@ -88,10 +87,10 @@ class ErrorMaker: except Error: raise except OSError as value: - if not hasattr(value, "errno"): + if not hasattr(value, 'errno'): raise errno = value.errno - if sys.platform == "win32": + if sys.platform == 'win32': try: cls = self._geterrnoclass(_winerrnomap[errno]) except KeyError: @@ -100,7 +99,7 @@ class ErrorMaker: # we are not on Windows, or we got a proper OSError cls = self._geterrnoclass(errno) - raise cls(f"{func.__name__}{args!r}") + raise cls(f'{func.__name__}{args!r}') _error_maker = ErrorMaker() diff --git a/.venv/lib/python3.10/site-packages/_pytest/_py/path.py b/.venv/lib/python3.10/site-packages/_pytest/_py/path.py index 7701561..2ee3e35 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_py/path.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_py/path.py @@ -3,11 +3,15 @@ from __future__ import annotations import atexit -from contextlib import contextmanager import fnmatch import importlib.util import io import os +import posixpath +import sys +import uuid +import warnings +from contextlib import contextmanager from os.path import abspath from os.path import dirname from os.path import exists @@ -16,39 +20,35 @@ from os.path import isdir from os.path import isfile from os.path import islink from os.path import normpath -import posixpath from stat import S_ISDIR from stat import S_ISLNK from stat import S_ISREG -import sys from typing import Any from typing import Callable from typing import cast from typing import Literal from typing import overload from typing import TYPE_CHECKING -import uuid -import warnings from . import error # Moved from local.py. -iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt") +iswin32 = sys.platform == 'win32' or (getattr(os, '_name', False) == 'nt') class Checkers: - _depend_on_existence = "exists", "link", "dir", "file" + _depend_on_existence = 'exists', 'link', 'dir', 'file' def __init__(self, path): self.path = path def dotfile(self): - return self.path.basename.startswith(".") + return self.path.basename.startswith('.') def ext(self, arg): - if not arg.startswith("."): - arg = "." + arg + if not arg.startswith('.'): + arg = '.' + arg return self.path.ext == arg def basename(self, arg): @@ -75,14 +75,14 @@ class Checkers: try: meth = getattr(self, name) except AttributeError: - if name[:3] == "not": + if name[:3] == 'not': invert = True try: meth = getattr(self, name[3:]) except AttributeError: pass if meth is None: - raise TypeError(f"no {name!r} checker available for {self.path!r}") + raise TypeError(f'no {name!r} checker available for {self.path!r}') try: if getrawcode(meth).co_argcount > 1: if (not meth(value)) ^ invert: @@ -98,7 +98,7 @@ class Checkers: if name in kw: if kw.get(name): return False - name = "not" + name + name = 'not' + name if name in kw: if not kw.get(name): return False @@ -140,7 +140,7 @@ class Visitor: fil = FNMatcher(fil) if isinstance(rec, str): self.rec: Callable[[LocalPath], bool] = FNMatcher(rec) - elif not hasattr(rec, "__call__") and rec: + elif not hasattr(rec, '__call__') and rec: self.rec = lambda path: True else: self.rec = rec @@ -156,7 +156,7 @@ class Visitor: return rec = self.rec dirs = self.optsort( - [p for p in entries if p.check(dir=1) and (rec is None or rec(p))] + [p for p in entries if p.check(dir=1) and (rec is None or rec(p))], ) if not self.breadthfirst: for subdir in dirs: @@ -179,9 +179,9 @@ class FNMatcher: pattern = self.pattern if ( - pattern.find(path.sep) == -1 - and iswin32 - and pattern.find(posixpath.sep) != -1 + pattern.find(path.sep) == -1 and + iswin32 and + pattern.find(posixpath.sep) != -1 ): # Running on Windows, the pattern has no Windows path separators, # and the pattern has one or more Posix path separators. Replace @@ -193,7 +193,7 @@ class FNMatcher: else: name = str(path) # path.strpath # XXX svn? if not os.path.isabs(pattern): - pattern = "*" + path.sep + pattern + pattern = '*' + path.sep + pattern return fnmatch.fnmatch(name, pattern) @@ -213,7 +213,7 @@ class Stat: ... def __getattr__(self, name: str) -> Any: - return getattr(self._osstatresult, "st_" + name) + return getattr(self._osstatresult, 'st_' + name) def __init__(self, path, osstatresult): self.path = path @@ -222,7 +222,7 @@ class Stat: @property def owner(self): if iswin32: - raise NotImplementedError("XXX win32") + raise NotImplementedError('XXX win32') import pwd entry = error.checked_call(pwd.getpwuid, self.uid) # type:ignore[attr-defined] @@ -232,7 +232,7 @@ class Stat: def group(self): """Return group name of file.""" if iswin32: - raise NotImplementedError("XXX win32") + raise NotImplementedError('XXX win32') import grp entry = error.checked_call(grp.getgrgid, self.gid) # type:ignore[attr-defined] @@ -292,14 +292,14 @@ class LocalPath: path = os.fspath(path) except TypeError: raise ValueError( - "can only pass None, Path instances " - "or non-empty strings to LocalPath" + 'can only pass None, Path instances ' + 'or non-empty strings to LocalPath', ) if expanduser: path = os.path.expanduser(path) self.strpath = abspath(path) - if sys.platform != "win32": + if sys.platform != 'win32': def chown(self, user, group, rec=0): """Change ownership to the given user and group. @@ -334,7 +334,7 @@ class LocalPath: relsource = self.__class__(value).relto(base) reldest = self.relto(base) n = reldest.count(self.sep) - target = self.sep.join(("..",) * n + (relsource,)) + target = self.sep.join(('..',) * n + (relsource,)) error.checked_call(os.symlink, target, self.strpath) def __div__(self, other): @@ -345,34 +345,34 @@ class LocalPath: @property def basename(self): """Basename part of path.""" - return self._getbyspec("basename")[0] + return self._getbyspec('basename')[0] @property def dirname(self): """Dirname part of path.""" - return self._getbyspec("dirname")[0] + return self._getbyspec('dirname')[0] @property def purebasename(self): """Pure base name of the path.""" - return self._getbyspec("purebasename")[0] + return self._getbyspec('purebasename')[0] @property def ext(self): """Extension of the path (including the '.').""" - return self._getbyspec("ext")[0] + return self._getbyspec('ext')[0] def read_binary(self): """Read and return a bytestring from reading the path.""" - with self.open("rb") as f: + with self.open('rb') as f: return f.read() def read_text(self, encoding): """Read and return a Unicode string from reading the path.""" - with self.open("r", encoding=encoding) as f: + with self.open('r', encoding=encoding) as f: return f.read() - def read(self, mode="r"): + def read(self, mode='r'): """Read and return a bytestring from reading the path.""" with self.open(mode) as f: return f.read() @@ -380,11 +380,11 @@ class LocalPath: def readlines(self, cr=1): """Read and return a list of lines from the path. if cr is False, the newline will be removed from the end of each line.""" - mode = "r" + mode = 'r' if not cr: content = self.read(mode) - return content.split("\n") + return content.split('\n') else: f = self.open(mode) try: @@ -394,7 +394,7 @@ class LocalPath: def load(self): """(deprecated) return object unpickled from self.read()""" - f = self.open("rb") + f = self.open('rb') try: import pickle @@ -405,7 +405,7 @@ class LocalPath: def move(self, target): """Move this path to target.""" if target.relto(self): - raise error.EINVAL(target, "cannot move path into a subdirectory of itself") + raise error.EINVAL(target, 'cannot move path into a subdirectory of itself') try: self.rename(target) except error.EXDEV: # invalid cross-device link @@ -436,19 +436,19 @@ class LocalPath: to the given 'relpath'. """ if not isinstance(relpath, (str, LocalPath)): - raise TypeError(f"{relpath!r}: not a string or path object") + raise TypeError(f'{relpath!r}: not a string or path object') strrelpath = str(relpath) if strrelpath and strrelpath[-1] != self.sep: strrelpath += self.sep # assert strrelpath[-1] == self.sep # assert strrelpath[-2] != self.sep strself = self.strpath - if sys.platform == "win32" or getattr(os, "_name", None) == "nt": + if sys.platform == 'win32' or getattr(os, '_name', None) == 'nt': if os.path.normcase(strself).startswith(os.path.normcase(strrelpath)): - return strself[len(strrelpath) :] + return strself[len(strrelpath):] elif strself.startswith(strrelpath): - return strself[len(strrelpath) :] - return "" + return strself[len(strrelpath):] + return '' def ensure_dir(self, *args): """Ensure the path joined with args is a directory.""" @@ -542,10 +542,10 @@ class LocalPath: def _sortlist(self, res, sort): if sort: - if hasattr(sort, "__call__"): + if hasattr(sort, '__call__'): warnings.warn( DeprecationWarning( - "listdir(sort=callable) is deprecated and breaks on python3" + 'listdir(sort=callable) is deprecated and breaks on python3', ), stacklevel=3, ) @@ -592,7 +592,7 @@ class LocalPath: other = abspath(other) if self == other: return True - if not hasattr(os.path, "samefile"): + if not hasattr(os.path, 'samefile'): return False return error.checked_call(os.path.samefile, self.strpath, other) @@ -609,7 +609,7 @@ class LocalPath: import shutil error.checked_call( - shutil.rmtree, self.strpath, ignore_errors=ignore_errors + shutil.rmtree, self.strpath, ignore_errors=ignore_errors, ) else: error.checked_call(os.rmdir, self.strpath) @@ -618,19 +618,19 @@ class LocalPath: self.chmod(0o700) error.checked_call(os.remove, self.strpath) - def computehash(self, hashtype="md5", chunksize=524288): + def computehash(self, hashtype='md5', chunksize=524288): """Return hexdigest of hashvalue for this file.""" try: try: import hashlib as mod except ImportError: - if hashtype == "sha1": - hashtype = "sha" + if hashtype == 'sha1': + hashtype = 'sha' mod = __import__(hashtype) hash = getattr(mod, hashtype)() except (AttributeError, ImportError): raise ValueError(f"Don't know how to compute {hashtype!r} hash") - f = self.open("rb") + f = self.open('rb') try: while 1: buf = f.read(chunksize) @@ -656,28 +656,28 @@ class LocalPath: obj.strpath = self.strpath return obj drive, dirname, basename, purebasename, ext = self._getbyspec( - "drive,dirname,basename,purebasename,ext" + 'drive,dirname,basename,purebasename,ext', ) - if "basename" in kw: - if "purebasename" in kw or "ext" in kw: - raise ValueError("invalid specification %r" % kw) + if 'basename' in kw: + if 'purebasename' in kw or 'ext' in kw: + raise ValueError('invalid specification %r' % kw) else: - pb = kw.setdefault("purebasename", purebasename) + pb = kw.setdefault('purebasename', purebasename) try: - ext = kw["ext"] + ext = kw['ext'] except KeyError: pass else: - if ext and not ext.startswith("."): - ext = "." + ext - kw["basename"] = pb + ext + if ext and not ext.startswith('.'): + ext = '.' + ext + kw['basename'] = pb + ext - if "dirname" in kw and not kw["dirname"]: - kw["dirname"] = drive + if 'dirname' in kw and not kw['dirname']: + kw['dirname'] = drive else: - kw.setdefault("dirname", dirname) - kw.setdefault("sep", self.sep) - obj.strpath = normpath("{dirname}{sep}{basename}".format(**kw)) + kw.setdefault('dirname', dirname) + kw.setdefault('sep', self.sep) + obj.strpath = normpath('{dirname}{sep}{basename}'.format(**kw)) return obj def _getbyspec(self, spec: str) -> list[str]: @@ -685,28 +685,28 @@ class LocalPath: res = [] parts = self.strpath.split(self.sep) - args = filter(None, spec.split(",")) + args = filter(None, spec.split(',')) for name in args: - if name == "drive": + if name == 'drive': res.append(parts[0]) - elif name == "dirname": + elif name == 'dirname': res.append(self.sep.join(parts[:-1])) else: basename = parts[-1] - if name == "basename": + if name == 'basename': res.append(basename) else: - i = basename.rfind(".") + i = basename.rfind('.') if i == -1: - purebasename, ext = basename, "" + purebasename, ext = basename, '' else: purebasename, ext = basename[:i], basename[i:] - if name == "purebasename": + if name == 'purebasename': res.append(purebasename) - elif name == "ext": + elif name == 'ext': res.append(ext) else: - raise ValueError("invalid part specification %r" % name) + raise ValueError('invalid part specification %r' % name) return res def dirpath(self, *args, **kwargs): @@ -717,7 +717,7 @@ class LocalPath: if args: path = path.join(*args) return path - return self.new(basename="").join(*args, **kwargs) + return self.new(basename='').join(*args, **kwargs) def join(self, *args: os.PathLike[str], abs: bool = False) -> LocalPath: """Return a new path by appending all 'args' as path @@ -736,20 +736,20 @@ class LocalPath: break newargs.insert(0, arg) # special case for when we have e.g. strpath == "/" - actual_sep = "" if strpath.endswith(sep) else sep + actual_sep = '' if strpath.endswith(sep) else sep for arg in strargs: arg = arg.strip(sep) if iswin32: # allow unix style paths even on windows. - arg = arg.strip("/") - arg = arg.replace("/", sep) + arg = arg.strip('/') + arg = arg.replace('/', sep) strpath = strpath + actual_sep + arg actual_sep = sep obj = object.__new__(self.__class__) obj.strpath = normpath(strpath) return obj - def open(self, mode="r", ensure=False, encoding=None): + def open(self, mode='r', ensure=False, encoding=None): """Return an opened file with the given mode. If ensure is True, create parent directories if needed. @@ -797,15 +797,15 @@ class LocalPath: if not kw: return exists(self.strpath) if len(kw) == 1: - if "dir" in kw: - return not kw["dir"] ^ isdir(self.strpath) - if "file" in kw: - return not kw["file"] ^ isfile(self.strpath) + if 'dir' in kw: + return not kw['dir'] ^ isdir(self.strpath) + if 'file' in kw: + return not kw['file'] ^ isfile(self.strpath) if not kw: - kw = {"exists": 1} + kw = {'exists': 1} return Checkers(self)._evaluate(kw) - _patternchars = set("*?[" + os.sep) + _patternchars = set('*?[' + os.sep) def listdir(self, fil=None, sort=None): """List directory contents, possibly filter by the given fil func @@ -882,7 +882,7 @@ class LocalPath: def dump(self, obj, bin=1): """Pickle object into path location""" - f = self.open("wb") + f = self.open('wb') import pickle try: @@ -902,7 +902,7 @@ class LocalPath: """ if ensure: self.dirpath().ensure(dir=1) - with self.open("wb") as f: + with self.open('wb') as f: f.write(data) def write_text(self, data, encoding, ensure=False): @@ -911,18 +911,18 @@ class LocalPath: """ if ensure: self.dirpath().ensure(dir=1) - with self.open("w", encoding=encoding) as f: + with self.open('w', encoding=encoding) as f: f.write(data) - def write(self, data, mode="w", ensure=False): + def write(self, data, mode='w', ensure=False): """Write data into path. If ensure is True create missing parent directories. """ if ensure: self.dirpath().ensure(dir=1) - if "b" in mode: + if 'b' in mode: if not isinstance(data, bytes): - raise ValueError("can only process bytes") + raise ValueError('can only process bytes') else: if not isinstance(data, str): if not isinstance(data, bytes): @@ -957,12 +957,12 @@ class LocalPath: then the path is forced to be a directory path. """ p = self.join(*args) - if kwargs.get("dir", 0): + if kwargs.get('dir', 0): return p._ensuredirs() else: p.dirpath()._ensuredirs() if not p.check(file=1): - p.open("wb").close() + p.open('wb').close() return p @overload @@ -1033,7 +1033,7 @@ class LocalPath: return self.stat().atime def __repr__(self): - return "local(%r)" % self.strpath + return 'local(%r)' % self.strpath def __str__(self): """Return string representation of the Path.""" @@ -1045,7 +1045,7 @@ class LocalPath: if rec is True perform recursively. """ if not isinstance(mode, int): - raise TypeError(f"mode {mode!r} must be an integer") + raise TypeError(f'mode {mode!r} must be an integer') if rec: for x in self.visit(rec=rec): error.checked_call(os.chmod, str(x), mode) @@ -1059,7 +1059,7 @@ class LocalPath: pkgpath = None for parent in self.parts(reverse=True): if parent.isdir(): - if not parent.join("__init__.py").exists(): + if not parent.join('__init__.py').exists(): break if not isimportable(parent.basename): break @@ -1069,7 +1069,7 @@ class LocalPath: def _ensuresyspath(self, ensuremode, path): if ensuremode: s = str(path) - if ensuremode == "append": + if ensuremode == 'append': if s not in sys.path: sys.path.append(s) else: @@ -1100,7 +1100,7 @@ class LocalPath: if not self.check(): raise error.ENOENT(self) - if ensuresyspath == "importlib": + if ensuresyspath == 'importlib': if modname is None: modname = self.purebasename spec = importlib.util.spec_from_file_location(modname, str(self)) @@ -1115,10 +1115,10 @@ class LocalPath: pkgpath = self.pypkgpath() if pkgpath is not None: pkgroot = pkgpath.dirpath() - names = self.new(ext="").relto(pkgroot).split(self.sep) - if names[-1] == "__init__": + names = self.new(ext='').relto(pkgroot).split(self.sep) + if names[-1] == '__init__': names.pop() - modname = ".".join(names) + modname = '.'.join(names) else: pkgroot = self.dirpath() modname = self.purebasename @@ -1126,25 +1126,25 @@ class LocalPath: self._ensuresyspath(ensuresyspath, pkgroot) __import__(modname) mod = sys.modules[modname] - if self.basename == "__init__.py": + if self.basename == '__init__.py': return mod # we don't check anything as we might # be in a namespace package ... too icky to check modfile = mod.__file__ assert modfile is not None - if modfile[-4:] in (".pyc", ".pyo"): + if modfile[-4:] in ('.pyc', '.pyo'): modfile = modfile[:-1] - elif modfile.endswith("$py.class"): - modfile = modfile[:-9] + ".py" - if modfile.endswith(os.sep + "__init__.py"): - if self.basename != "__init__.py": + elif modfile.endswith('$py.class'): + modfile = modfile[:-9] + '.py' + if modfile.endswith(os.sep + '__init__.py'): + if self.basename != '__init__.py': modfile = modfile[:-12] try: issame = self.samefile(modfile) except error.ENOENT: issame = False if not issame: - ignore = os.getenv("PY_IGNORE_IMPORTMISMATCH") - if ignore != "1": + ignore = os.getenv('PY_IGNORE_IMPORTMISMATCH') + if ignore != '1': raise self.ImportMismatchError(modname, modfile, self) return mod else: @@ -1158,7 +1158,7 @@ class LocalPath: mod.__file__ = str(self) sys.modules[modname] = mod try: - with open(str(self), "rb") as f: + with open(str(self), 'rb') as f: exec(f.read(), mod.__dict__) except BaseException: del sys.modules[modname] @@ -1173,8 +1173,8 @@ class LocalPath: from subprocess import PIPE from subprocess import Popen - popen_opts.pop("stdout", None) - popen_opts.pop("stderr", None) + popen_opts.pop('stdout', None) + popen_opts.pop('stderr', None) proc = Popen( [str(self)] + [str(arg) for arg in argv], **popen_opts, @@ -1214,23 +1214,23 @@ class LocalPath: else: if paths is None: if iswin32: - paths = os.environ["Path"].split(";") - if "" not in paths and "." not in paths: - paths.append(".") + paths = os.environ['Path'].split(';') + if '' not in paths and '.' not in paths: + paths.append('.') try: - systemroot = os.environ["SYSTEMROOT"] + systemroot = os.environ['SYSTEMROOT'] except KeyError: pass else: paths = [ - path.replace("%SystemRoot%", systemroot) for path in paths + path.replace('%SystemRoot%', systemroot) for path in paths ] else: - paths = os.environ["PATH"].split(":") + paths = os.environ['PATH'].split(':') tryadd = [] if iswin32: - tryadd += os.environ["PATHEXT"].split(os.pathsep) - tryadd.append("") + tryadd += os.environ['PATHEXT'].split(os.pathsep) + tryadd.append('') for x in paths: for addext in tryadd: @@ -1248,10 +1248,10 @@ class LocalPath: @classmethod def _gethomedir(cls): try: - x = os.environ["HOME"] + x = os.environ['HOME'] except KeyError: try: - x = os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"] + x = os.environ['HOMEDRIVE'] + os.environ['HOMEPATH'] except KeyError: return None return cls(x) @@ -1288,7 +1288,7 @@ class LocalPath: @classmethod def make_numbered_dir( - cls, prefix="session-", rootdir=None, keep=3, lock_timeout=172800 + cls, prefix='session-', rootdir=None, keep=3, lock_timeout=172800, ): # two days """Return unique directory with a number greater than the current maximum one. The number is assumed to start directly after prefix. @@ -1306,21 +1306,21 @@ class LocalPath: nbasename = path.basename.lower() if nbasename.startswith(nprefix): try: - return int(nbasename[len(nprefix) :]) + return int(nbasename[len(nprefix):]) except ValueError: pass def create_lockfile(path): """Exclusively create lockfile. Throws when failed""" mypid = os.getpid() - lockfile = path.join(".lock") - if hasattr(lockfile, "mksymlinkto"): + lockfile = path.join('.lock') + if hasattr(lockfile, 'mksymlinkto'): lockfile.mksymlinkto(str(mypid)) else: fd = error.checked_call( - os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644 + os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644, ) - with os.fdopen(fd, "w") as f: + with os.fdopen(fd, 'w') as f: f.write(str(mypid)) return lockfile @@ -1380,7 +1380,7 @@ class LocalPath: except error.Error: pass - garbage_prefix = prefix + "garbage-" + garbage_prefix = prefix + 'garbage-' def is_garbage(path): """Check if path denotes directory scheduled for removal""" @@ -1428,15 +1428,15 @@ class LocalPath: # make link... try: - username = os.environ["USER"] # linux, et al + username = os.environ['USER'] # linux, et al except KeyError: try: - username = os.environ["USERNAME"] # windows + username = os.environ['USERNAME'] # windows except KeyError: - username = "current" + username = 'current' src = str(udir) - dest = src[: src.rfind("-")] + "-" + username + dest = src[: src.rfind('-')] + '-' + username try: os.unlink(dest) except OSError: @@ -1466,9 +1466,9 @@ def copystat(src, dest): def copychunked(src, dest): chunksize = 524288 # half a meg of bytes - fsrc = src.open("rb") + fsrc = src.open('rb') try: - fdest = dest.open("wb") + fdest = dest.open('wb') try: while 1: buf = fsrc.read(chunksize) @@ -1482,8 +1482,8 @@ def copychunked(src, dest): def isimportable(name): - if name and (name[0].isalpha() or name[0] == "_"): - name = name.replace("_", "") + if name and (name[0].isalpha() or name[0] == '_'): + name = name.replace('_', '') return not name or name.isalnum() diff --git a/.venv/lib/python3.10/site-packages/_pytest/_version.py b/.venv/lib/python3.10/site-packages/_pytest/_version.py index c08a531..7fb57b0 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/_version.py +++ b/.venv/lib/python3.10/site-packages/_pytest/_version.py @@ -1,5 +1,6 @@ # file generated by setuptools_scm # don't change, don't track in version control +from __future__ import annotations TYPE_CHECKING = False if TYPE_CHECKING: from typing import Tuple, Union diff --git a/.venv/lib/python3.10/site-packages/_pytest/assertion/__init__.py b/.venv/lib/python3.10/site-packages/_pytest/assertion/__init__.py index ea71230..9d3ac88 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/assertion/__init__.py +++ b/.venv/lib/python3.10/site-packages/_pytest/assertion/__init__.py @@ -1,5 +1,7 @@ # mypy: allow-untyped-defs """Support for presenting detailed information in failing assertions.""" +from __future__ import annotations + import sys from typing import Any from typing import Generator @@ -22,34 +24,34 @@ if TYPE_CHECKING: def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("debugconfig") + group = parser.getgroup('debugconfig') group.addoption( - "--assert", - action="store", - dest="assertmode", - choices=("rewrite", "plain"), - default="rewrite", - metavar="MODE", + '--assert', + action='store', + dest='assertmode', + choices=('rewrite', 'plain'), + default='rewrite', + metavar='MODE', help=( - "Control assertion debugging tools.\n" + 'Control assertion debugging tools.\n' "'plain' performs no assertion debugging.\n" "'rewrite' (the default) rewrites assert statements in test modules" - " on import to provide assert expression information." + ' on import to provide assert expression information.' ), ) parser.addini( - "enable_assertion_pass_hook", - type="bool", + 'enable_assertion_pass_hook', + type='bool', default=False, - help="Enables the pytest_assertion_pass hook. " - "Make sure to delete any previously generated pyc cache files.", + help='Enables the pytest_assertion_pass hook. ' + 'Make sure to delete any previously generated pyc cache files.', ) Config._add_verbosity_ini( parser, Config.VERBOSITY_ASSERTIONS, help=( - "Specify a verbosity level for assertions, overriding the main level. " - "Higher levels will provide more detailed explanation when an assertion fails." + 'Specify a verbosity level for assertions, overriding the main level. ' + 'Higher levels will provide more detailed explanation when an assertion fails.' ), ) @@ -67,7 +69,7 @@ def register_assert_rewrite(*names: str) -> None: """ for name in names: if not isinstance(name, str): - msg = "expected module names as *args, got {0} instead" # type: ignore[unreachable] + msg = 'expected module names as *args, got {0} instead' # type: ignore[unreachable] raise TypeError(msg.format(repr(names))) for hook in sys.meta_path: if isinstance(hook, rewrite.AssertionRewritingHook): @@ -92,16 +94,16 @@ class AssertionState: def __init__(self, config: Config, mode) -> None: self.mode = mode - self.trace = config.trace.root.get("assertion") - self.hook: Optional[rewrite.AssertionRewritingHook] = None + self.trace = config.trace.root.get('assertion') + self.hook: rewrite.AssertionRewritingHook | None = None def install_importhook(config: Config) -> rewrite.AssertionRewritingHook: """Try to install the rewrite hook, raise SystemError if it fails.""" - config.stash[assertstate_key] = AssertionState(config, "rewrite") + config.stash[assertstate_key] = AssertionState(config, 'rewrite') config.stash[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config) sys.meta_path.insert(0, hook) - config.stash[assertstate_key].trace("installed rewrite import hook") + config.stash[assertstate_key].trace('installed rewrite import hook') def undo() -> None: hook = config.stash[assertstate_key].hook @@ -112,7 +114,7 @@ def install_importhook(config: Config) -> rewrite.AssertionRewritingHook: return hook -def pytest_collection(session: "Session") -> None: +def pytest_collection(session: Session) -> None: # This hook is only called when test modules are collected # so for example not in the managing process of pytest-xdist # (which does not collect test modules). @@ -132,7 +134,7 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: """ ihook = item.ihook - def callbinrepr(op, left: object, right: object) -> Optional[str]: + def callbinrepr(op, left: object, right: object) -> str | None: """Call the pytest_assertrepr_compare hook and prepare the result. This uses the first result from the hook and then ensures the @@ -148,15 +150,15 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: pretty printing. """ hook_result = ihook.pytest_assertrepr_compare( - config=item.config, op=op, left=left, right=right + config=item.config, op=op, left=left, right=right, ) for new_expl in hook_result: if new_expl: new_expl = truncate.truncate_if_required(new_expl, item) - new_expl = [line.replace("\n", "\\n") for line in new_expl] - res = "\n~".join(new_expl) - if item.config.getvalue("assertmode") == "rewrite": - res = res.replace("%", "%%") + new_expl = [line.replace('\n', '\\n') for line in new_expl] + res = '\n~'.join(new_expl) + if item.config.getvalue('assertmode') == 'rewrite': + res = res.replace('%', '%%') return res return None @@ -178,7 +180,7 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: util._config = None -def pytest_sessionfinish(session: "Session") -> None: +def pytest_sessionfinish(session: Session) -> None: assertstate = session.config.stash.get(assertstate_key, None) if assertstate: if assertstate.hook is not None: @@ -186,6 +188,6 @@ def pytest_sessionfinish(session: "Session") -> None: def pytest_assertrepr_compare( - config: Config, op: str, left: Any, right: Any -) -> Optional[List[str]]: + config: Config, op: str, left: Any, right: Any, +) -> list[str] | None: return util.assertrepr_compare(config=config, op=op, left=left, right=right) diff --git a/.venv/lib/python3.10/site-packages/_pytest/assertion/rewrite.py b/.venv/lib/python3.10/site-packages/_pytest/assertion/rewrite.py index ddae34c..105785b 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/assertion/rewrite.py +++ b/.venv/lib/python3.10/site-packages/_pytest/assertion/rewrite.py @@ -1,7 +1,7 @@ """Rewrite assertion AST to produce nice error messages.""" +from __future__ import annotations import ast -from collections import defaultdict import errno import functools import importlib.abc @@ -11,12 +11,13 @@ import io import itertools import marshal import os -from pathlib import Path -from pathlib import PurePath import struct import sys import tokenize import types +from collections import defaultdict +from pathlib import Path +from pathlib import PurePath from typing import Callable from typing import Dict from typing import IO @@ -34,15 +35,13 @@ from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest._io.saferepr import saferepr from _pytest._version import version from _pytest.assertion import util +from _pytest.assertion.util import format_explanation as _format_explanation # noqa:F401, isort:skip from _pytest.config import Config from _pytest.main import Session from _pytest.pathlib import absolutepath from _pytest.pathlib import fnmatch_ex from _pytest.stash import StashKey - - # fmt: off -from _pytest.assertion.util import format_explanation as _format_explanation # noqa:F401, isort:skip # fmt:on if TYPE_CHECKING: @@ -53,12 +52,12 @@ class Sentinel: pass -assertstate_key = StashKey["AssertionState"]() +assertstate_key = StashKey['AssertionState']() # pytest caches rewritten pycs in pycache dirs -PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}" -PYC_EXT = ".py" + (__debug__ and "c" or "o") -PYC_TAIL = "." + PYTEST_TAG + PYC_EXT +PYTEST_TAG = f'{sys.implementation.cache_tag}-pytest-{version}' +PYC_EXT = '.py' + (__debug__ and 'c' or 'o') +PYC_TAIL = '.' + PYTEST_TAG + PYC_EXT # Special marker that denotes we have just left a scope definition _SCOPE_END_MARKER = Sentinel() @@ -70,20 +69,20 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) def __init__(self, config: Config) -> None: self.config = config try: - self.fnpats = config.getini("python_files") + self.fnpats = config.getini('python_files') except ValueError: - self.fnpats = ["test_*.py", "*_test.py"] - self.session: Optional[Session] = None - self._rewritten_names: Dict[str, Path] = {} - self._must_rewrite: Set[str] = set() + self.fnpats = ['test_*.py', '*_test.py'] + self.session: Session | None = None + self._rewritten_names: dict[str, Path] = {} + self._must_rewrite: set[str] = set() # flag to guard against trying to rewrite a pyc file while we are already writing another pyc file, # which might result in infinite recursion (#3506) self._writing_pyc = False - self._basenames_to_check_rewrite = {"conftest"} - self._marked_for_rewrite_cache: Dict[str, bool] = {} + self._basenames_to_check_rewrite = {'conftest'} + self._marked_for_rewrite_cache: dict[str, bool] = {} self._session_paths_checked = False - def set_session(self, session: Optional[Session]) -> None: + def set_session(self, session: Session | None) -> None: self.session = session self._session_paths_checked = False @@ -93,28 +92,28 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) def find_spec( self, name: str, - path: Optional[Sequence[Union[str, bytes]]] = None, - target: Optional[types.ModuleType] = None, - ) -> Optional[importlib.machinery.ModuleSpec]: + path: Sequence[str | bytes] | None = None, + target: types.ModuleType | None = None, + ) -> importlib.machinery.ModuleSpec | None: if self._writing_pyc: return None state = self.config.stash[assertstate_key] if self._early_rewrite_bailout(name, state): return None - state.trace("find_module called for: %s" % name) + state.trace('find_module called for: %s' % name) # Type ignored because mypy is confused about the `self` binding here. spec = self._find_spec(name, path) # type: ignore if ( # the import machinery could not find a file to import - spec is None + spec is None or # this is a namespace package (without `__init__.py`) # there's nothing to rewrite there - or spec.origin is None + spec.origin is None or # we can only rewrite source files - or not isinstance(spec.loader, importlib.machinery.SourceFileLoader) + not isinstance(spec.loader, importlib.machinery.SourceFileLoader) or # if the file doesn't exist, we can't rewrite it - or not os.path.exists(spec.origin) + not os.path.exists(spec.origin) ): return None else: @@ -131,8 +130,8 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) ) def create_module( - self, spec: importlib.machinery.ModuleSpec - ) -> Optional[types.ModuleType]: + self, spec: importlib.machinery.ModuleSpec, + ) -> types.ModuleType | None: return None # default behaviour is fine def exec_module(self, module: types.ModuleType) -> None: @@ -157,7 +156,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) ok = try_makedirs(cache_dir) if not ok: write = False - state.trace(f"read only directory: {cache_dir}") + state.trace(f'read only directory: {cache_dir}') cache_name = fn.name[:-3] + PYC_TAIL pyc = cache_dir / cache_name @@ -165,7 +164,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) # to check for a cached pyc. This may not be optimal... co = _read_pyc(fn, pyc, state.trace) if co is None: - state.trace(f"rewriting {fn!r}") + state.trace(f'rewriting {fn!r}') source_stat, co = _rewrite_test(fn, self.config) if write: self._writing_pyc = True @@ -174,10 +173,10 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) finally: self._writing_pyc = False else: - state.trace(f"found cached rewritten pyc for {fn}") + state.trace(f'found cached rewritten pyc for {fn}') exec(co, module.__dict__) - def _early_rewrite_bailout(self, name: str, state: "AssertionState") -> bool: + def _early_rewrite_bailout(self, name: str, state: AssertionState) -> bool: """A fast way to get out of rewriting modules. Profiling has shown that the call to PathFinder.find_spec (inside of @@ -195,12 +194,12 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) self._basenames_to_check_rewrite.add(os.path.splitext(parts[-1])[0]) # Note: conftest already by default in _basenames_to_check_rewrite. - parts = name.split(".") + parts = name.split('.') if parts[-1] in self._basenames_to_check_rewrite: return False # For matching the name it must be as if it was a filename. - path = PurePath(*parts).with_suffix(".py") + path = PurePath(*parts).with_suffix('.py') for pat in self.fnpats: # if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based @@ -213,18 +212,18 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) if self._is_marked_for_rewrite(name, state): return False - state.trace(f"early skip of rewriting module: {name}") + state.trace(f'early skip of rewriting module: {name}') return True - def _should_rewrite(self, name: str, fn: str, state: "AssertionState") -> bool: + def _should_rewrite(self, name: str, fn: str, state: AssertionState) -> bool: # always rewrite conftest files - if os.path.basename(fn) == "conftest.py": - state.trace(f"rewriting conftest file: {fn!r}") + if os.path.basename(fn) == 'conftest.py': + state.trace(f'rewriting conftest file: {fn!r}') return True if self.session is not None: if self.session.isinitpath(absolutepath(fn)): - state.trace(f"matched test file (was specified on cmdline): {fn!r}") + state.trace(f'matched test file (was specified on cmdline): {fn!r}') return True # modules not passed explicitly on the command line are only @@ -232,18 +231,18 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) fn_path = PurePath(fn) for pat in self.fnpats: if fnmatch_ex(pat, fn_path): - state.trace(f"matched test file {fn!r}") + state.trace(f'matched test file {fn!r}') return True return self._is_marked_for_rewrite(name, state) - def _is_marked_for_rewrite(self, name: str, state: "AssertionState") -> bool: + def _is_marked_for_rewrite(self, name: str, state: AssertionState) -> bool: try: return self._marked_for_rewrite_cache[name] except KeyError: for marked in self._must_rewrite: - if name == marked or name.startswith(marked + "."): - state.trace(f"matched marked file {name!r} (from {marked!r})") + if name == marked or name.startswith(marked + '.'): + state.trace(f'matched marked file {name!r} (from {marked!r})') self._marked_for_rewrite_cache[name] = True return True @@ -262,7 +261,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) for name in already_imported: mod = sys.modules[name] if not AssertionRewriter.is_rewrite_disabled( - mod.__doc__ or "" + mod.__doc__ or '', ) and not isinstance(mod.__loader__, type(self)): self._warn_already_imported(name) self._must_rewrite.update(names) @@ -273,14 +272,14 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) self.config.issue_config_time_warning( PytestAssertRewriteWarning( - "Module already imported so cannot be rewritten: %s" % name + 'Module already imported so cannot be rewritten: %s' % name, ), stacklevel=5, ) - def get_data(self, pathname: Union[str, bytes]) -> bytes: + def get_data(self, pathname: str | bytes) -> bytes: """Optional PEP302 get_data API.""" - with open(pathname, "rb") as f: + with open(pathname, 'rb') as f: return f.read() if sys.version_info >= (3, 10): @@ -296,46 +295,46 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) from importlib.resources.readers import FileReader return FileReader( # type:ignore[no-any-return] - types.SimpleNamespace(path=self._rewritten_names[name]) + types.SimpleNamespace(path=self._rewritten_names[name]), ) def _write_pyc_fp( - fp: IO[bytes], source_stat: os.stat_result, co: types.CodeType + fp: IO[bytes], source_stat: os.stat_result, co: types.CodeType, ) -> None: # Technically, we don't have to have the same pyc format as # (C)Python, since these "pycs" should never be seen by builtin # import. However, there's little reason to deviate. fp.write(importlib.util.MAGIC_NUMBER) # https://www.python.org/dev/peps/pep-0552/ - flags = b"\x00\x00\x00\x00" + flags = b'\x00\x00\x00\x00' fp.write(flags) # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) mtime = int(source_stat.st_mtime) & 0xFFFFFFFF size = source_stat.st_size & 0xFFFFFFFF # " bool: - proc_pyc = f"{pyc}.{os.getpid()}" + proc_pyc = f'{pyc}.{os.getpid()}' try: - with open(proc_pyc, "wb") as fp: + with open(proc_pyc, 'wb') as fp: _write_pyc_fp(fp, source_stat, co) except OSError as e: - state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}") + state.trace(f'error writing pyc file at {proc_pyc}: errno={e.errno}') return False try: os.replace(proc_pyc, pyc) except OSError as e: - state.trace(f"error writing pyc file at {pyc}: {e}") + state.trace(f'error writing pyc file at {pyc}: {e}') # we ignore any failure to write the cache file # there are many reasons, permission-denied, pycache dir being a # file etc. @@ -343,26 +342,26 @@ def _write_pyc( return True -def _rewrite_test(fn: Path, config: Config) -> Tuple[os.stat_result, types.CodeType]: +def _rewrite_test(fn: Path, config: Config) -> tuple[os.stat_result, types.CodeType]: """Read and rewrite *fn* and return the code object.""" stat = os.stat(fn) source = fn.read_bytes() strfn = str(fn) tree = ast.parse(source, filename=strfn) rewrite_asserts(tree, source, strfn, config) - co = compile(tree, strfn, "exec", dont_inherit=True) + co = compile(tree, strfn, 'exec', dont_inherit=True) return stat, co def _read_pyc( - source: Path, pyc: Path, trace: Callable[[str], None] = lambda x: None -) -> Optional[types.CodeType]: + source: Path, pyc: Path, trace: Callable[[str], None] = lambda x: None, +) -> types.CodeType | None: """Possibly read a pytest pyc containing rewritten code. Return rewritten code if successful or None if not. """ try: - fp = open(pyc, "rb") + fp = open(pyc, 'rb') except OSError: return None with fp: @@ -372,33 +371,33 @@ def _read_pyc( size = stat_result.st_size data = fp.read(16) except OSError as e: - trace(f"_read_pyc({source}): OSError {e}") + trace(f'_read_pyc({source}): OSError {e}') return None # Check for invalid or out of date pyc file. if len(data) != (16): - trace("_read_pyc(%s): invalid pyc (too short)" % source) + trace('_read_pyc(%s): invalid pyc (too short)' % source) return None if data[:4] != importlib.util.MAGIC_NUMBER: - trace("_read_pyc(%s): invalid pyc (bad magic number)" % source) + trace('_read_pyc(%s): invalid pyc (bad magic number)' % source) return None - if data[4:8] != b"\x00\x00\x00\x00": - trace("_read_pyc(%s): invalid pyc (unsupported flags)" % source) + if data[4:8] != b'\x00\x00\x00\x00': + trace('_read_pyc(%s): invalid pyc (unsupported flags)' % source) return None mtime_data = data[8:12] - if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF: - trace("_read_pyc(%s): out of date" % source) + if int.from_bytes(mtime_data, 'little') != mtime & 0xFFFFFFFF: + trace('_read_pyc(%s): out of date' % source) return None size_data = data[12:16] - if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF: - trace("_read_pyc(%s): invalid pyc (incorrect size)" % source) + if int.from_bytes(size_data, 'little') != size & 0xFFFFFFFF: + trace('_read_pyc(%s): invalid pyc (incorrect size)' % source) return None try: co = marshal.load(fp) except Exception as e: - trace(f"_read_pyc({source}): marshal.load error {e}") + trace(f'_read_pyc({source}): marshal.load error {e}') return None if not isinstance(co, types.CodeType): - trace("_read_pyc(%s): not a code object" % source) + trace('_read_pyc(%s): not a code object' % source) return None return co @@ -406,8 +405,8 @@ def _read_pyc( def rewrite_asserts( mod: ast.Module, source: bytes, - module_path: Optional[str] = None, - config: Optional[Config] = None, + module_path: str | None = None, + config: Config | None = None, ) -> None: """Rewrite the assert statements in mod.""" AssertionRewriter(module_path, config, source).run(mod) @@ -424,10 +423,10 @@ def _saferepr(obj: object) -> str: JSON reprs. """ maxsize = _get_maxsize_for_saferepr(util._config) - return saferepr(obj, maxsize=maxsize).replace("\n", "\\n") + return saferepr(obj, maxsize=maxsize).replace('\n', '\\n') -def _get_maxsize_for_saferepr(config: Optional[Config]) -> Optional[int]: +def _get_maxsize_for_saferepr(config: Config | None) -> int | None: """Get `maxsize` configuration for saferepr based on the given config object.""" if config is None: verbosity = 0 @@ -451,10 +450,10 @@ def _format_assertmsg(obj: object) -> str: # contains a newline it gets escaped, however if an object has a # .__repr__() which contains newlines it does not get escaped. # However in either case we want to preserve the newline. - replaces = [("\n", "\n~"), ("%", "%%")] + replaces = [('\n', '\n~'), ('%', '%%')] if not isinstance(obj, str): obj = saferepr(obj) - replaces.append(("\\n", "\n~")) + replaces.append(('\\n', '\n~')) for r1, r2 in replaces: obj = obj.replace(r1, r2) @@ -467,14 +466,14 @@ def _should_repr_global_name(obj: object) -> bool: return False try: - return not hasattr(obj, "__name__") + return not hasattr(obj, '__name__') except Exception: return True def _format_boolop(explanations: Iterable[str], is_or: bool) -> str: - explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")" - return explanation.replace("%", "%%") + explanation = '(' + (is_or and ' or ' or ' and ').join(explanations) + ')' + return explanation.replace('%', '%%') def _call_reprcompare( @@ -508,32 +507,32 @@ def _check_if_assertion_pass_impl() -> bool: return True if util._assertion_pass else False -UNARY_MAP = {ast.Not: "not %s", ast.Invert: "~%s", ast.USub: "-%s", ast.UAdd: "+%s"} +UNARY_MAP = {ast.Not: 'not %s', ast.Invert: '~%s', ast.USub: '-%s', ast.UAdd: '+%s'} BINOP_MAP = { - ast.BitOr: "|", - ast.BitXor: "^", - ast.BitAnd: "&", - ast.LShift: "<<", - ast.RShift: ">>", - ast.Add: "+", - ast.Sub: "-", - ast.Mult: "*", - ast.Div: "/", - ast.FloorDiv: "//", - ast.Mod: "%%", # escaped for string formatting - ast.Eq: "==", - ast.NotEq: "!=", - ast.Lt: "<", - ast.LtE: "<=", - ast.Gt: ">", - ast.GtE: ">=", - ast.Pow: "**", - ast.Is: "is", - ast.IsNot: "is not", - ast.In: "in", - ast.NotIn: "not in", - ast.MatMult: "@", + ast.BitOr: '|', + ast.BitXor: '^', + ast.BitAnd: '&', + ast.LShift: '<<', + ast.RShift: '>>', + ast.Add: '+', + ast.Sub: '-', + ast.Mult: '*', + ast.Div: '/', + ast.FloorDiv: '//', + ast.Mod: '%%', # escaped for string formatting + ast.Eq: '==', + ast.NotEq: '!=', + ast.Lt: '<', + ast.LtE: '<=', + ast.Gt: '>', + ast.GtE: '>=', + ast.Pow: '**', + ast.Is: 'is', + ast.IsNot: 'is not', + ast.In: 'in', + ast.NotIn: 'not in', + ast.MatMult: '@', } @@ -545,19 +544,19 @@ def traverse_node(node: ast.AST) -> Iterator[ast.AST]: @functools.lru_cache(maxsize=1) -def _get_assertion_exprs(src: bytes) -> Dict[int, str]: +def _get_assertion_exprs(src: bytes) -> dict[int, str]: """Return a mapping from {lineno: "assertion test expression"}.""" - ret: Dict[int, str] = {} + ret: dict[int, str] = {} depth = 0 - lines: List[str] = [] - assert_lineno: Optional[int] = None - seen_lines: Set[int] = set() + lines: list[str] = [] + assert_lineno: int | None = None + seen_lines: set[int] = set() def _write_and_reset() -> None: nonlocal depth, lines, assert_lineno, seen_lines assert assert_lineno is not None - ret[assert_lineno] = "".join(lines).rstrip().rstrip("\\") + ret[assert_lineno] = ''.join(lines).rstrip().rstrip('\\') depth = 0 lines = [] assert_lineno = None @@ -565,20 +564,20 @@ def _get_assertion_exprs(src: bytes) -> Dict[int, str]: tokens = tokenize.tokenize(io.BytesIO(src).readline) for tp, source, (lineno, offset), _, line in tokens: - if tp == tokenize.NAME and source == "assert": + if tp == tokenize.NAME and source == 'assert': assert_lineno = lineno elif assert_lineno is not None: # keep track of depth for the assert-message `,` lookup - if tp == tokenize.OP and source in "([{": + if tp == tokenize.OP and source in '([{': depth += 1 - elif tp == tokenize.OP and source in ")]}": + elif tp == tokenize.OP and source in ')]}': depth -= 1 if not lines: lines.append(line[offset:]) seen_lines.add(lineno) # a non-nested comma separates the expression from the message - elif depth == 0 and tp == tokenize.OP and source == ",": + elif depth == 0 and tp == tokenize.OP and source == ',': # one line assert with message if lineno in seen_lines and len(lines) == 1: offset_in_trimmed = offset + len(lines[-1]) - len(line) @@ -659,21 +658,21 @@ class AssertionRewriter(ast.NodeVisitor): """ def __init__( - self, module_path: Optional[str], config: Optional[Config], source: bytes + self, module_path: str | None, config: Config | None, source: bytes, ) -> None: super().__init__() self.module_path = module_path self.config = config if config is not None: self.enable_assertion_pass_hook = config.getini( - "enable_assertion_pass_hook" + 'enable_assertion_pass_hook', ) else: self.enable_assertion_pass_hook = False self.source = source self.scope: tuple[ast.AST, ...] = () self.variables_overwrite: defaultdict[ - tuple[ast.AST, ...], Dict[str, str] + tuple[ast.AST, ...], dict[str, str], ] = defaultdict(dict) def run(self, mod: ast.Module) -> None: @@ -684,7 +683,7 @@ class AssertionRewriter(ast.NodeVisitor): # We'll insert some special imports at the top of the module, but after any # docstrings and __future__ imports, so first figure out where that is. - doc = getattr(mod, "docstring", None) + doc = getattr(mod, 'docstring', None) expect_docstring = doc is None if doc is not None and self.is_rewrite_disabled(doc): return @@ -692,19 +691,19 @@ class AssertionRewriter(ast.NodeVisitor): item = None for item in mod.body: if ( - expect_docstring - and isinstance(item, ast.Expr) - and isinstance(item.value, ast.Constant) - and isinstance(item.value.value, str) + expect_docstring and + isinstance(item, ast.Expr) and + isinstance(item.value, ast.Constant) and + isinstance(item.value.value, str) ): doc = item.value.value if self.is_rewrite_disabled(doc): return expect_docstring = False elif ( - isinstance(item, ast.ImportFrom) - and item.level == 0 - and item.module == "__future__" + isinstance(item, ast.ImportFrom) and + item.level == 0 and + item.module == '__future__' ): pass else: @@ -719,18 +718,18 @@ class AssertionRewriter(ast.NodeVisitor): # Now actually insert the special imports. if sys.version_info >= (3, 10): aliases = [ - ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), + ast.alias('builtins', '@py_builtins', lineno=lineno, col_offset=0), ast.alias( - "_pytest.assertion.rewrite", - "@pytest_ar", + '_pytest.assertion.rewrite', + '@pytest_ar', lineno=lineno, col_offset=0, ), ] else: aliases = [ - ast.alias("builtins", "@py_builtins"), - ast.alias("_pytest.assertion.rewrite", "@pytest_ar"), + ast.alias('builtins', '@py_builtins'), + ast.alias('_pytest.assertion.rewrite', '@pytest_ar'), ] imports = [ ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases @@ -739,7 +738,7 @@ class AssertionRewriter(ast.NodeVisitor): # Collect asserts. self.scope = (mod,) - nodes: List[Union[ast.AST, Sentinel]] = [mod] + nodes: list[ast.AST | Sentinel] = [mod] while nodes: node = nodes.pop() if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): @@ -751,7 +750,7 @@ class AssertionRewriter(ast.NodeVisitor): assert isinstance(node, ast.AST) for name, field in ast.iter_fields(node): if isinstance(field, list): - new: List[ast.AST] = [] + new: list[ast.AST] = [] for i, child in enumerate(field): if isinstance(child, ast.Assert): # Transform assert. @@ -762,21 +761,21 @@ class AssertionRewriter(ast.NodeVisitor): nodes.append(child) setattr(node, name, new) elif ( - isinstance(field, ast.AST) + isinstance(field, ast.AST) and # Don't recurse into expressions as they can't contain # asserts. - and not isinstance(field, ast.expr) + not isinstance(field, ast.expr) ): nodes.append(field) @staticmethod def is_rewrite_disabled(docstring: str) -> bool: - return "PYTEST_DONT_REWRITE" in docstring + return 'PYTEST_DONT_REWRITE' in docstring def variable(self) -> str: """Get a new variable.""" # Use a character invalid in python identifiers to avoid clashing. - name = "@py_assert" + str(next(self.variable_counter)) + name = '@py_assert' + str(next(self.variable_counter)) self.variables.append(name) return name @@ -788,17 +787,17 @@ class AssertionRewriter(ast.NodeVisitor): def display(self, expr: ast.expr) -> ast.expr: """Call saferepr on the expression.""" - return self.helper("_saferepr", expr) + return self.helper('_saferepr', expr) def helper(self, name: str, *args: ast.expr) -> ast.expr: """Call a helper in this module.""" - py_name = ast.Name("@pytest_ar", ast.Load()) + py_name = ast.Name('@pytest_ar', ast.Load()) attr = ast.Attribute(py_name, name, ast.Load()) return ast.Call(attr, list(args), []) def builtin(self, name: str) -> ast.Attribute: """Return the builtin called *name*.""" - builtin_name = ast.Name("@py_builtins", ast.Load()) + builtin_name = ast.Name('@py_builtins', ast.Load()) return ast.Attribute(builtin_name, name, ast.Load()) def explanation_param(self, expr: ast.expr) -> str: @@ -809,9 +808,9 @@ class AssertionRewriter(ast.NodeVisitor): and expr are placed in the current format context so that it can be used on the next call to .pop_format_context(). """ - specifier = "py" + str(next(self.variable_counter)) + specifier = 'py' + str(next(self.variable_counter)) self.explanation_specifiers[specifier] = expr - return "%(" + specifier + ")s" + return '%(' + specifier + ')s' def push_format_context(self) -> None: """Create a new formatting context. @@ -823,7 +822,7 @@ class AssertionRewriter(ast.NodeVisitor): to format a string of %-formatted values as added by .explanation_param(). """ - self.explanation_specifiers: Dict[str, ast.expr] = {} + self.explanation_specifiers: dict[str, ast.expr] = {} self.stack.append(self.explanation_specifiers) def pop_format_context(self, expl_expr: ast.expr) -> ast.Name: @@ -840,19 +839,19 @@ class AssertionRewriter(ast.NodeVisitor): keys = [ast.Constant(key) for key in current.keys()] format_dict = ast.Dict(keys, list(current.values())) form = ast.BinOp(expl_expr, ast.Mod(), format_dict) - name = "@py_format" + str(next(self.variable_counter)) + name = '@py_format' + str(next(self.variable_counter)) if self.enable_assertion_pass_hook: self.format_variables.append(name) self.expl_stmts.append(ast.Assign([ast.Name(name, ast.Store())], form)) return ast.Name(name, ast.Load()) - def generic_visit(self, node: ast.AST) -> Tuple[ast.Name, str]: + def generic_visit(self, node: ast.AST) -> tuple[ast.Name, str]: """Handle expressions we don't have custom code for.""" assert isinstance(node, ast.expr) res = self.assign(node) return res, self.explanation_param(self.display(res)) - def visit_Assert(self, assert_: ast.Assert) -> List[ast.stmt]: + def visit_Assert(self, assert_: ast.Assert) -> list[ast.stmt]: """Return the AST statements to replace the ast.Assert instance. This rewrites the test of an assertion to provide @@ -869,22 +868,22 @@ class AssertionRewriter(ast.NodeVisitor): assert self.module_path is not None warnings.warn_explicit( PytestAssertRewriteWarning( - "assertion is always true, perhaps remove parentheses?" + 'assertion is always true, perhaps remove parentheses?', ), category=None, filename=self.module_path, lineno=assert_.lineno, ) - self.statements: List[ast.stmt] = [] - self.variables: List[str] = [] + self.statements: list[ast.stmt] = [] + self.variables: list[str] = [] self.variable_counter = itertools.count() if self.enable_assertion_pass_hook: - self.format_variables: List[str] = [] + self.format_variables: list[str] = [] - self.stack: List[Dict[str, ast.expr]] = [] - self.expl_stmts: List[ast.stmt] = [] + self.stack: list[dict[str, ast.expr]] = [] + self.expl_stmts: list[ast.stmt] = [] self.push_format_context() # Rewrite assert into a bunch of statements. top_condition, explanation = self.visit(assert_.test) @@ -896,15 +895,15 @@ class AssertionRewriter(ast.NodeVisitor): # Failed if assert_.msg: - assertmsg = self.helper("_format_assertmsg", assert_.msg) - gluestr = "\n>assert " + assertmsg = self.helper('_format_assertmsg', assert_.msg) + gluestr = '\n>assert ' else: - assertmsg = ast.Constant("") - gluestr = "assert " + assertmsg = ast.Constant('') + gluestr = 'assert ' err_explanation = ast.BinOp(ast.Constant(gluestr), ast.Add(), msg) err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation) - err_name = ast.Name("AssertionError", ast.Load()) - fmt = self.helper("_format_explanation", err_msg) + err_name = ast.Name('AssertionError', ast.Load()) + fmt = self.helper('_format_explanation', err_msg) exc = ast.Call(err_name, [fmt], []) raise_ = ast.Raise(exc, None) statements_fail = [] @@ -912,19 +911,19 @@ class AssertionRewriter(ast.NodeVisitor): statements_fail.append(raise_) # Passed - fmt_pass = self.helper("_format_explanation", msg) + fmt_pass = self.helper('_format_explanation', msg) orig = _get_assertion_exprs(self.source)[assert_.lineno] hook_call_pass = ast.Expr( self.helper( - "_call_assertion_pass", + '_call_assertion_pass', ast.Constant(assert_.lineno), ast.Constant(orig), fmt_pass, - ) + ), ) # If any hooks implement assert_pass hook hook_impl_test = ast.If( - self.helper("_check_if_assertion_pass_impl"), + self.helper('_check_if_assertion_pass_impl'), [*self.expl_stmts, hook_call_pass], [], ) @@ -945,15 +944,15 @@ class AssertionRewriter(ast.NodeVisitor): body = self.expl_stmts self.statements.append(ast.If(negation, body, [])) if assert_.msg: - assertmsg = self.helper("_format_assertmsg", assert_.msg) - explanation = "\n>assert " + explanation + assertmsg = self.helper('_format_assertmsg', assert_.msg) + explanation = '\n>assert ' + explanation else: - assertmsg = ast.Constant("") - explanation = "assert " + explanation + assertmsg = ast.Constant('') + explanation = 'assert ' + explanation template = ast.BinOp(assertmsg, ast.Add(), ast.Constant(explanation)) msg = self.pop_format_context(template) - fmt = self.helper("_format_explanation", msg) - err_name = ast.Name("AssertionError", ast.Load()) + fmt = self.helper('_format_explanation', msg) + err_name = ast.Name('AssertionError', ast.Load()) exc = ast.Call(err_name, [fmt], []) raise_ = ast.Raise(exc, None) @@ -970,32 +969,32 @@ class AssertionRewriter(ast.NodeVisitor): ast.copy_location(node, assert_) return self.statements - def visit_NamedExpr(self, name: ast.NamedExpr) -> Tuple[ast.NamedExpr, str]: + def visit_NamedExpr(self, name: ast.NamedExpr) -> tuple[ast.NamedExpr, str]: # This method handles the 'walrus operator' repr of the target # name if it's a local variable or _should_repr_global_name() # thinks it's acceptable. - locs = ast.Call(self.builtin("locals"), [], []) + locs = ast.Call(self.builtin('locals'), [], []) target_id = name.target.id # type: ignore[attr-defined] inlocs = ast.Compare(ast.Constant(target_id), [ast.In()], [locs]) - dorepr = self.helper("_should_repr_global_name", name) + dorepr = self.helper('_should_repr_global_name', name) test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) expr = ast.IfExp(test, self.display(name), ast.Constant(target_id)) return name, self.explanation_param(expr) - def visit_Name(self, name: ast.Name) -> Tuple[ast.Name, str]: + def visit_Name(self, name: ast.Name) -> tuple[ast.Name, str]: # Display the repr of the name if it's a local variable or # _should_repr_global_name() thinks it's acceptable. - locs = ast.Call(self.builtin("locals"), [], []) + locs = ast.Call(self.builtin('locals'), [], []) inlocs = ast.Compare(ast.Constant(name.id), [ast.In()], [locs]) - dorepr = self.helper("_should_repr_global_name", name) + dorepr = self.helper('_should_repr_global_name', name) test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) expr = ast.IfExp(test, self.display(name), ast.Constant(name.id)) return name, self.explanation_param(expr) - def visit_BoolOp(self, boolop: ast.BoolOp) -> Tuple[ast.Name, str]: + def visit_BoolOp(self, boolop: ast.BoolOp) -> tuple[ast.Name, str]: res_var = self.variable() expl_list = self.assign(ast.List([], ast.Load())) - app = ast.Attribute(expl_list, "append", ast.Load()) + app = ast.Attribute(expl_list, 'append', ast.Load()) is_or = int(isinstance(boolop.op, ast.Or)) body = save = self.statements fail_save = self.expl_stmts @@ -1004,19 +1003,19 @@ class AssertionRewriter(ast.NodeVisitor): # Process each operand, short-circuiting if needed. for i, v in enumerate(boolop.values): if i: - fail_inner: List[ast.stmt] = [] + fail_inner: list[ast.stmt] = [] # cond is set in a prior loop iteration below self.expl_stmts.append(ast.If(cond, fail_inner, [])) # noqa: F821 self.expl_stmts = fail_inner # Check if the left operand is a ast.NamedExpr and the value has already been visited if ( - isinstance(v, ast.Compare) - and isinstance(v.left, ast.NamedExpr) - and v.left.target.id + isinstance(v, ast.Compare) and + isinstance(v.left, ast.NamedExpr) and + v.left.target.id in [ ast_expr.id for ast_expr in boolop.values[:i] - if hasattr(ast_expr, "id") + if hasattr(ast_expr, 'id') ] ): pytest_temp = self.variable() @@ -1032,37 +1031,37 @@ class AssertionRewriter(ast.NodeVisitor): cond: ast.expr = res if is_or: cond = ast.UnaryOp(ast.Not(), cond) - inner: List[ast.stmt] = [] + inner: list[ast.stmt] = [] self.statements.append(ast.If(cond, inner, [])) self.statements = body = inner self.statements = save self.expl_stmts = fail_save - expl_template = self.helper("_format_boolop", expl_list, ast.Constant(is_or)) + expl_template = self.helper('_format_boolop', expl_list, ast.Constant(is_or)) expl = self.pop_format_context(expl_template) return ast.Name(res_var, ast.Load()), self.explanation_param(expl) - def visit_UnaryOp(self, unary: ast.UnaryOp) -> Tuple[ast.Name, str]: + def visit_UnaryOp(self, unary: ast.UnaryOp) -> tuple[ast.Name, str]: pattern = UNARY_MAP[unary.op.__class__] operand_res, operand_expl = self.visit(unary.operand) res = self.assign(ast.UnaryOp(unary.op, operand_res)) return res, pattern % (operand_expl,) - def visit_BinOp(self, binop: ast.BinOp) -> Tuple[ast.Name, str]: + def visit_BinOp(self, binop: ast.BinOp) -> tuple[ast.Name, str]: symbol = BINOP_MAP[binop.op.__class__] left_expr, left_expl = self.visit(binop.left) right_expr, right_expl = self.visit(binop.right) - explanation = f"({left_expl} {symbol} {right_expl})" + explanation = f'({left_expl} {symbol} {right_expl})' res = self.assign(ast.BinOp(left_expr, binop.op, right_expr)) return res, explanation - def visit_Call(self, call: ast.Call) -> Tuple[ast.Name, str]: + def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: new_func, func_expl = self.visit(call.func) arg_expls = [] new_args = [] new_kwargs = [] for arg in call.args: if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get( - self.scope, {} + self.scope, {}, ): arg = self.variables_overwrite[self.scope][arg.id] # type:ignore[assignment] res, expl = self.visit(arg) @@ -1070,51 +1069,51 @@ class AssertionRewriter(ast.NodeVisitor): new_args.append(res) for keyword in call.keywords: if isinstance( - keyword.value, ast.Name + keyword.value, ast.Name, ) and keyword.value.id in self.variables_overwrite.get(self.scope, {}): keyword.value = self.variables_overwrite[self.scope][keyword.value.id] # type:ignore[assignment] res, expl = self.visit(keyword.value) new_kwargs.append(ast.keyword(keyword.arg, res)) if keyword.arg: - arg_expls.append(keyword.arg + "=" + expl) + arg_expls.append(keyword.arg + '=' + expl) else: # **args have `arg` keywords with an .arg of None - arg_expls.append("**" + expl) + arg_expls.append('**' + expl) - expl = "{}({})".format(func_expl, ", ".join(arg_expls)) + expl = '{}({})'.format(func_expl, ', '.join(arg_expls)) new_call = ast.Call(new_func, new_args, new_kwargs) res = self.assign(new_call) res_expl = self.explanation_param(self.display(res)) - outer_expl = f"{res_expl}\n{{{res_expl} = {expl}\n}}" + outer_expl = f'{res_expl}\n{{{res_expl} = {expl}\n}}' return res, outer_expl - def visit_Starred(self, starred: ast.Starred) -> Tuple[ast.Starred, str]: + def visit_Starred(self, starred: ast.Starred) -> tuple[ast.Starred, str]: # A Starred node can appear in a function call. res, expl = self.visit(starred.value) new_starred = ast.Starred(res, starred.ctx) - return new_starred, "*" + expl + return new_starred, '*' + expl - def visit_Attribute(self, attr: ast.Attribute) -> Tuple[ast.Name, str]: + def visit_Attribute(self, attr: ast.Attribute) -> tuple[ast.Name, str]: if not isinstance(attr.ctx, ast.Load): return self.generic_visit(attr) value, value_expl = self.visit(attr.value) res = self.assign(ast.Attribute(value, attr.attr, ast.Load())) res_expl = self.explanation_param(self.display(res)) - pat = "%s\n{%s = %s.%s\n}" + pat = '%s\n{%s = %s.%s\n}' expl = pat % (res_expl, res_expl, value_expl, attr.attr) return res, expl - def visit_Compare(self, comp: ast.Compare) -> Tuple[ast.expr, str]: + def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: self.push_format_context() # We first check if we have overwritten a variable in the previous assert if isinstance( - comp.left, ast.Name + comp.left, ast.Name, ) and comp.left.id in self.variables_overwrite.get(self.scope, {}): comp.left = self.variables_overwrite[self.scope][comp.left.id] # type:ignore[assignment] if isinstance(comp.left, ast.NamedExpr): self.variables_overwrite[self.scope][comp.left.target.id] = comp.left # type:ignore[assignment] left_res, left_expl = self.visit(comp.left) if isinstance(comp.left, (ast.Compare, ast.BoolOp)): - left_expl = f"({left_expl})" + left_expl = f'({left_expl})' res_variables = [self.variable() for i in range(len(comp.ops))] load_names = [ast.Name(v, ast.Load()) for v in res_variables] store_names = [ast.Name(v, ast.Store()) for v in res_variables] @@ -1124,26 +1123,26 @@ class AssertionRewriter(ast.NodeVisitor): results = [left_res] for i, op, next_operand in it: if ( - isinstance(next_operand, ast.NamedExpr) - and isinstance(left_res, ast.Name) - and next_operand.target.id == left_res.id + isinstance(next_operand, ast.NamedExpr) and + isinstance(left_res, ast.Name) and + next_operand.target.id == left_res.id ): next_operand.target.id = self.variable() self.variables_overwrite[self.scope][left_res.id] = next_operand # type:ignore[assignment] next_res, next_expl = self.visit(next_operand) if isinstance(next_operand, (ast.Compare, ast.BoolOp)): - next_expl = f"({next_expl})" + next_expl = f'({next_expl})' results.append(next_res) sym = BINOP_MAP[op.__class__] syms.append(ast.Constant(sym)) - expl = f"{left_expl} {sym} {next_expl}" + expl = f'{left_expl} {sym} {next_expl}' expls.append(ast.Constant(expl)) res_expr = ast.Compare(left_res, [op], [next_res]) self.statements.append(ast.Assign([store_names[i]], res_expr)) left_res, left_expl = next_res, next_expl # Use pytest.assertion.util._reprcompare if that's available. expl_call = self.helper( - "_call_reprcompare", + '_call_reprcompare', ast.Tuple(syms, ast.Load()), ast.Tuple(load_names, ast.Load()), ast.Tuple(expls, ast.Load()), @@ -1190,4 +1189,4 @@ def get_cache_dir(file_path: Path) -> Path: return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1]) else: # classic pycache directory - return file_path.parent / "__pycache__" + return file_path.parent / '__pycache__' diff --git a/.venv/lib/python3.10/site-packages/_pytest/assertion/truncate.py b/.venv/lib/python3.10/site-packages/_pytest/assertion/truncate.py index 4fdfd86..35a67c1 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/assertion/truncate.py +++ b/.venv/lib/python3.10/site-packages/_pytest/assertion/truncate.py @@ -3,6 +3,7 @@ Current default behaviour is to truncate assertion explanations at terminal lines, unless running with an assertions verbosity level of at least 2 or running on CI. """ +from __future__ import annotations from typing import List from typing import Optional @@ -18,8 +19,8 @@ USAGE_MSG = "use '-vv' to show" def truncate_if_required( - explanation: List[str], item: Item, max_length: Optional[int] = None -) -> List[str]: + explanation: list[str], item: Item, max_length: int | None = None, +) -> list[str]: """Truncate this assertion explanation if the given test item is eligible.""" if _should_truncate_item(item): return _truncate_explanation(explanation) @@ -33,10 +34,10 @@ def _should_truncate_item(item: Item) -> bool: def _truncate_explanation( - input_lines: List[str], - max_lines: Optional[int] = None, - max_chars: Optional[int] = None, -) -> List[str]: + input_lines: list[str], + max_lines: int | None = None, + max_chars: int | None = None, +) -> list[str]: """Truncate given list of strings that makes up the assertion explanation. Truncates to either 8 lines, or 640 characters - whichever the input reaches @@ -49,7 +50,7 @@ def _truncate_explanation( max_chars = DEFAULT_MAX_CHARS # Check if truncation required - input_char_count = len("".join(input_lines)) + input_char_count = len(''.join(input_lines)) # The length of the truncation explanation depends on the number of lines # removed but is at least 68 characters: # The real value is @@ -67,17 +68,17 @@ def _truncate_explanation( # The truncation explanation add two lines to the output tolerable_max_lines = max_lines + 2 if ( - len(input_lines) <= tolerable_max_lines - and input_char_count <= tolerable_max_chars + len(input_lines) <= tolerable_max_lines and + input_char_count <= tolerable_max_chars ): return input_lines # Truncate first to max_lines, and then truncate to max_chars if necessary truncated_explanation = input_lines[:max_lines] truncated_char = True # We reevaluate the need to truncate chars following removal of some lines - if len("".join(truncated_explanation)) > tolerable_max_chars: + if len(''.join(truncated_explanation)) > tolerable_max_chars: truncated_explanation = _truncate_by_char_count( - truncated_explanation, max_chars + truncated_explanation, max_chars, ) else: truncated_char = False @@ -85,22 +86,22 @@ def _truncate_explanation( truncated_line_count = len(input_lines) - len(truncated_explanation) if truncated_explanation[-1]: # Add ellipsis and take into account part-truncated final line - truncated_explanation[-1] = truncated_explanation[-1] + "..." + truncated_explanation[-1] = truncated_explanation[-1] + '...' if truncated_char: # It's possible that we did not remove any char from this line truncated_line_count += 1 else: # Add proper ellipsis when we were able to fit a full line exactly - truncated_explanation[-1] = "..." + truncated_explanation[-1] = '...' return [ *truncated_explanation, - "", - f"...Full output truncated ({truncated_line_count} line" + '', + f'...Full output truncated ({truncated_line_count} line' f"{'' if truncated_line_count == 1 else 's'} hidden), {USAGE_MSG}", ] -def _truncate_by_char_count(input_lines: List[str], max_chars: int) -> List[str]: +def _truncate_by_char_count(input_lines: list[str], max_chars: int) -> list[str]: # Find point at which input length exceeds total allowed length iterated_char_count = 0 for iterated_index, input_line in enumerate(input_lines): diff --git a/.venv/lib/python3.10/site-packages/_pytest/assertion/util.py b/.venv/lib/python3.10/site-packages/_pytest/assertion/util.py index ca3df74..09e76e9 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/assertion/util.py +++ b/.venv/lib/python3.10/site-packages/_pytest/assertion/util.py @@ -1,5 +1,7 @@ # mypy: allow-untyped-defs """Utilities for assertion debugging.""" +from __future__ import annotations + import collections.abc import os import pprint @@ -15,8 +17,8 @@ from typing import Protocol from typing import Sequence from unicodedata import normalize -from _pytest import outcomes import _pytest._code +from _pytest import outcomes from _pytest._io.pprint import PrettyPrinter from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited @@ -27,18 +29,18 @@ from _pytest.config import Config # interpretation code and assertion rewriter to detect this plugin was # loaded and in turn call the hooks defined here as part of the # DebugInterpreter. -_reprcompare: Optional[Callable[[str, object, object], Optional[str]]] = None +_reprcompare: Callable[[str, object, object], str | None] | None = None # Works similarly as _reprcompare attribute. Is populated with the hook call # when pytest_runtest_setup is called. -_assertion_pass: Optional[Callable[[int, str, str], None]] = None +_assertion_pass: Callable[[int, str, str], None] | None = None # Config object which is assigned during pytest_runtest_protocol. -_config: Optional[Config] = None +_config: Config | None = None class _HighlightFunc(Protocol): - def __call__(self, source: str, lexer: Literal["diff", "python"] = "python") -> str: + def __call__(self, source: str, lexer: Literal['diff', 'python'] = 'python') -> str: """Apply highlighting to the given source.""" @@ -54,27 +56,27 @@ def format_explanation(explanation: str) -> str: """ lines = _split_explanation(explanation) result = _format_lines(lines) - return "\n".join(result) + return '\n'.join(result) -def _split_explanation(explanation: str) -> List[str]: +def _split_explanation(explanation: str) -> list[str]: r"""Return a list of individual lines in the explanation. This will return a list of lines split on '\n{', '\n}' and '\n~'. Any other newlines will be escaped and appear in the line as the literal '\n' characters. """ - raw_lines = (explanation or "").split("\n") + raw_lines = (explanation or '').split('\n') lines = [raw_lines[0]] for values in raw_lines[1:]: - if values and values[0] in ["{", "}", "~", ">"]: + if values and values[0] in ['{', '}', '~', '>']: lines.append(values) else: - lines[-1] += "\\n" + values + lines[-1] += '\\n' + values return lines -def _format_lines(lines: Sequence[str]) -> List[str]: +def _format_lines(lines: Sequence[str]) -> list[str]: """Format the individual lines. This will replace the '{', '}' and '~' characters of our mini formatting @@ -87,24 +89,24 @@ def _format_lines(lines: Sequence[str]) -> List[str]: stack = [0] stackcnt = [0] for line in lines[1:]: - if line.startswith("{"): + if line.startswith('{'): if stackcnt[-1]: - s = "and " + s = 'and ' else: - s = "where " + s = 'where ' stack.append(len(result)) stackcnt[-1] += 1 stackcnt.append(0) - result.append(" +" + " " * (len(stack) - 1) + s + line[1:]) - elif line.startswith("}"): + result.append(' +' + ' ' * (len(stack) - 1) + s + line[1:]) + elif line.startswith('}'): stack.pop() stackcnt.pop() result[stack[-1]] += line[1:] else: - assert line[0] in ["~", ">"] + assert line[0] in ['~', '>'] stack[-1] += 1 - indent = len(stack) if line.startswith("~") else len(stack) - 1 - result.append(" " * indent + line[1:]) + indent = len(stack) if line.startswith('~') else len(stack) - 1 + result.append(' ' * indent + line[1:]) assert len(stack) == 1 return result @@ -126,15 +128,15 @@ def isset(x: Any) -> bool: def isnamedtuple(obj: Any) -> bool: - return isinstance(obj, tuple) and getattr(obj, "_fields", None) is not None + return isinstance(obj, tuple) and getattr(obj, '_fields', None) is not None def isdatacls(obj: Any) -> bool: - return getattr(obj, "__dataclass_fields__", None) is not None + return getattr(obj, '__dataclass_fields__', None) is not None def isattrs(obj: Any) -> bool: - return getattr(obj, "__attrs_attrs__", None) is not None + return getattr(obj, '__attrs_attrs__', None) is not None def isiterable(obj: Any) -> bool: @@ -156,28 +158,28 @@ def has_default_eq( for dataclasses the default co_filename is , for attrs class, the __eq__ should contain "attrs eq generated" """ # inspired from https://github.com/willmcgugan/rich/blob/07d51ffc1aee6f16bd2e5a25b4e82850fb9ed778/rich/pretty.py#L68 - if hasattr(obj.__eq__, "__code__") and hasattr(obj.__eq__.__code__, "co_filename"): + if hasattr(obj.__eq__, '__code__') and hasattr(obj.__eq__.__code__, 'co_filename'): code_filename = obj.__eq__.__code__.co_filename if isattrs(obj): - return "attrs generated eq" in code_filename + return 'attrs generated eq' in code_filename - return code_filename == "" # data class + return code_filename == '' # data class return True def assertrepr_compare( - config, op: str, left: Any, right: Any, use_ascii: bool = False -) -> Optional[List[str]]: + config, op: str, left: Any, right: Any, use_ascii: bool = False, +) -> list[str] | None: """Return specialised explanations for some operators/operands.""" verbose = config.get_verbosity(Config.VERBOSITY_ASSERTIONS) # Strings which normalize equal are often hard to distinguish when printed; use ascii() to make this easier. # See issue #3246. use_ascii = ( - isinstance(left, str) - and isinstance(right, str) - and normalize("NFD", left) == normalize("NFD", right) + isinstance(left, str) and + isinstance(right, str) and + normalize('NFD', left) == normalize('NFD', right) ) if verbose > 1: @@ -193,29 +195,29 @@ def assertrepr_compare( left_repr = saferepr(left, maxsize=maxsize, use_ascii=use_ascii) right_repr = saferepr(right, maxsize=maxsize, use_ascii=use_ascii) - summary = f"{left_repr} {op} {right_repr}" + summary = f'{left_repr} {op} {right_repr}' highlighter = config.get_terminal_writer()._highlight explanation = None try: - if op == "==": + if op == '==': explanation = _compare_eq_any(left, right, highlighter, verbose) - elif op == "not in": + elif op == 'not in': if istext(left) and istext(right): explanation = _notin_text(left, right, verbose) - elif op == "!=": + elif op == '!=': if isset(left) and isset(right): - explanation = ["Both sets are equal"] - elif op == ">=": + explanation = ['Both sets are equal'] + elif op == '>=': if isset(left) and isset(right): explanation = _compare_gte_set(left, right, highlighter, verbose) - elif op == "<=": + elif op == '<=': if isset(left) and isset(right): explanation = _compare_lte_set(left, right, highlighter, verbose) - elif op == ">": + elif op == '>': if isset(left) and isset(right): explanation = _compare_gt_set(left, right, highlighter, verbose) - elif op == "<": + elif op == '<': if isset(left) and isset(right): explanation = _compare_lt_set(left, right, highlighter, verbose) @@ -223,23 +225,23 @@ def assertrepr_compare( raise except Exception: explanation = [ - "(pytest_assertion plugin: representation of details failed: {}.".format( - _pytest._code.ExceptionInfo.from_current()._getreprcrash() + '(pytest_assertion plugin: representation of details failed: {}.'.format( + _pytest._code.ExceptionInfo.from_current()._getreprcrash(), ), - " Probably an object has a faulty __repr__.)", + ' Probably an object has a faulty __repr__.)', ] if not explanation: return None - if explanation[0] != "": - explanation = ["", *explanation] + if explanation[0] != '': + explanation = ['', *explanation] return [summary, *explanation] def _compare_eq_any( - left: Any, right: Any, highlighter: _HighlightFunc, verbose: int = 0 -) -> List[str]: + left: Any, right: Any, highlighter: _HighlightFunc, verbose: int = 0, +) -> list[str]: explanation = [] if istext(left) and istext(right): explanation = _diff_text(left, right, verbose) @@ -274,7 +276,7 @@ def _compare_eq_any( return explanation -def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]: +def _diff_text(left: str, right: str, verbose: int = 0) -> list[str]: """Return the explanation for the diff between text. Unless --verbose is used this will skip leading and trailing @@ -282,7 +284,7 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]: """ from difflib import ndiff - explanation: List[str] = [] + explanation: list[str] = [] if verbose < 1: i = 0 # just in case left or right has zero length @@ -292,7 +294,7 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]: if i > 42: i -= 10 # Provide some context explanation = [ - "Skipping %s identical leading characters in diff, use -v to show" % i + 'Skipping %s identical leading characters in diff, use -v to show' % i, ] left = left[i:] right = right[i:] @@ -303,8 +305,8 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]: if i > 42: i -= 10 # Provide some context explanation += [ - f"Skipping {i} identical trailing " - "characters in diff, use -v to show" + f'Skipping {i} identical trailing ' + 'characters in diff, use -v to show', ] left = left[:-i] right = right[:-i] @@ -312,11 +314,11 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]: if left.isspace() or right.isspace(): left = repr(str(left)) right = repr(str(right)) - explanation += ["Strings contain only whitespace, escaping them using repr()"] + explanation += ['Strings contain only whitespace, escaping them using repr()'] # "right" is the expected base against which we compare "left", # see https://github.com/pytest-dev/pytest/issues/3333 explanation += [ - line.strip("\n") + line.strip('\n') for line in ndiff(right.splitlines(keepends), left.splitlines(keepends)) ] return explanation @@ -327,26 +329,26 @@ def _compare_eq_iterable( right: Iterable[Any], highligher: _HighlightFunc, verbose: int = 0, -) -> List[str]: +) -> list[str]: if verbose <= 0 and not running_on_ci(): - return ["Use -v to get more diff"] + return ['Use -v to get more diff'] # dynamic import to speedup pytest import difflib left_formatting = PrettyPrinter().pformat(left).splitlines() right_formatting = PrettyPrinter().pformat(right).splitlines() - explanation = ["", "Full diff:"] + explanation = ['', 'Full diff:'] # "right" is the expected base against which we compare "left", # see https://github.com/pytest-dev/pytest/issues/3333 explanation.extend( highligher( - "\n".join( + '\n'.join( line.rstrip() for line in difflib.ndiff(right_formatting, left_formatting) ), - lexer="diff", - ).splitlines() + lexer='diff', + ).splitlines(), ) return explanation @@ -356,9 +358,9 @@ def _compare_eq_sequence( right: Sequence[Any], highlighter: _HighlightFunc, verbose: int = 0, -) -> List[str]: +) -> list[str]: comparing_bytes = isinstance(left, bytes) and isinstance(right, bytes) - explanation: List[str] = [] + explanation: list[str] = [] len_left = len(left) len_right = len(right) for i in range(min(len_left, len_right)): @@ -372,15 +374,15 @@ def _compare_eq_sequence( # 102 # >>> s[0:1] # b'f' - left_value = left[i : i + 1] - right_value = right[i : i + 1] + left_value = left[i: i + 1] + right_value = right[i: i + 1] else: left_value = left[i] right_value = right[i] explanation.append( - f"At index {i} diff:" - f" {highlighter(repr(left_value))} != {highlighter(repr(right_value))}" + f'At index {i} diff:' + f' {highlighter(repr(left_value))} != {highlighter(repr(right_value))}', ) break @@ -393,21 +395,21 @@ def _compare_eq_sequence( len_diff = len_left - len_right if len_diff: if len_diff > 0: - dir_with_more = "Left" + dir_with_more = 'Left' extra = saferepr(left[len_right]) else: len_diff = 0 - len_diff - dir_with_more = "Right" + dir_with_more = 'Right' extra = saferepr(right[len_left]) if len_diff == 1: explanation += [ - f"{dir_with_more} contains one more item: {highlighter(extra)}" + f'{dir_with_more} contains one more item: {highlighter(extra)}', ] else: explanation += [ - "%s contains %d more items, first extra item: %s" - % (dir_with_more, len_diff, highlighter(extra)) + '%s contains %d more items, first extra item: %s' + % (dir_with_more, len_diff, highlighter(extra)), ] return explanation @@ -417,10 +419,10 @@ def _compare_eq_set( right: AbstractSet[Any], highlighter: _HighlightFunc, verbose: int = 0, -) -> List[str]: +) -> list[str]: explanation = [] - explanation.extend(_set_one_sided_diff("left", left, right, highlighter)) - explanation.extend(_set_one_sided_diff("right", right, left, highlighter)) + explanation.extend(_set_one_sided_diff('left', left, right, highlighter)) + explanation.extend(_set_one_sided_diff('right', right, left, highlighter)) return explanation @@ -429,10 +431,10 @@ def _compare_gt_set( right: AbstractSet[Any], highlighter: _HighlightFunc, verbose: int = 0, -) -> List[str]: +) -> list[str]: explanation = _compare_gte_set(left, right, highlighter) if not explanation: - return ["Both sets are equal"] + return ['Both sets are equal'] return explanation @@ -441,10 +443,10 @@ def _compare_lt_set( right: AbstractSet[Any], highlighter: _HighlightFunc, verbose: int = 0, -) -> List[str]: +) -> list[str]: explanation = _compare_lte_set(left, right, highlighter) if not explanation: - return ["Both sets are equal"] + return ['Both sets are equal'] return explanation @@ -453,8 +455,8 @@ def _compare_gte_set( right: AbstractSet[Any], highlighter: _HighlightFunc, verbose: int = 0, -) -> List[str]: - return _set_one_sided_diff("right", right, left, highlighter) +) -> list[str]: + return _set_one_sided_diff('right', right, left, highlighter) def _compare_lte_set( @@ -462,8 +464,8 @@ def _compare_lte_set( right: AbstractSet[Any], highlighter: _HighlightFunc, verbose: int = 0, -) -> List[str]: - return _set_one_sided_diff("left", left, right, highlighter) +) -> list[str]: + return _set_one_sided_diff('left', left, right, highlighter) def _set_one_sided_diff( @@ -471,11 +473,11 @@ def _set_one_sided_diff( set1: AbstractSet[Any], set2: AbstractSet[Any], highlighter: _HighlightFunc, -) -> List[str]: +) -> list[str]: explanation = [] diff = set1 - set2 if diff: - explanation.append(f"Extra items in the {posn} set:") + explanation.append(f'Extra items in the {posn} set:') for item in diff: explanation.append(highlighter(saferepr(item))) return explanation @@ -486,52 +488,52 @@ def _compare_eq_dict( right: Mapping[Any, Any], highlighter: _HighlightFunc, verbose: int = 0, -) -> List[str]: - explanation: List[str] = [] +) -> list[str]: + explanation: list[str] = [] set_left = set(left) set_right = set(right) common = set_left.intersection(set_right) same = {k: left[k] for k in common if left[k] == right[k]} if same and verbose < 2: - explanation += ["Omitting %s identical items, use -vv to show" % len(same)] + explanation += ['Omitting %s identical items, use -vv to show' % len(same)] elif same: - explanation += ["Common items:"] + explanation += ['Common items:'] explanation += highlighter(pprint.pformat(same)).splitlines() diff = {k for k in common if left[k] != right[k]} if diff: - explanation += ["Differing items:"] + explanation += ['Differing items:'] for k in diff: explanation += [ - highlighter(saferepr({k: left[k]})) - + " != " - + highlighter(saferepr({k: right[k]})) + highlighter(saferepr({k: left[k]})) + + ' != ' + + highlighter(saferepr({k: right[k]})), ] extra_left = set_left - set_right len_extra_left = len(extra_left) if len_extra_left: explanation.append( - "Left contains %d more item%s:" - % (len_extra_left, "" if len_extra_left == 1 else "s") + 'Left contains %d more item%s:' + % (len_extra_left, '' if len_extra_left == 1 else 's'), ) explanation.extend( - highlighter(pprint.pformat({k: left[k] for k in extra_left})).splitlines() + highlighter(pprint.pformat({k: left[k] for k in extra_left})).splitlines(), ) extra_right = set_right - set_left len_extra_right = len(extra_right) if len_extra_right: explanation.append( - "Right contains %d more item%s:" - % (len_extra_right, "" if len_extra_right == 1 else "s") + 'Right contains %d more item%s:' + % (len_extra_right, '' if len_extra_right == 1 else 's'), ) explanation.extend( - highlighter(pprint.pformat({k: right[k] for k in extra_right})).splitlines() + highlighter(pprint.pformat({k: right[k] for k in extra_right})).splitlines(), ) return explanation def _compare_eq_cls( - left: Any, right: Any, highlighter: _HighlightFunc, verbose: int -) -> List[str]: + left: Any, right: Any, highlighter: _HighlightFunc, verbose: int, +) -> list[str]: if not has_default_eq(left): return [] if isdatacls(left): @@ -541,13 +543,13 @@ def _compare_eq_cls( fields_to_check = [info.name for info in all_fields if info.compare] elif isattrs(left): all_fields = left.__attrs_attrs__ - fields_to_check = [field.name for field in all_fields if getattr(field, "eq")] + fields_to_check = [field.name for field in all_fields if getattr(field, 'eq')] elif isnamedtuple(left): fields_to_check = left._fields else: assert False - indent = " " + indent = ' ' same = [] diff = [] for field in fields_to_check: @@ -558,46 +560,46 @@ def _compare_eq_cls( explanation = [] if same or diff: - explanation += [""] + explanation += [''] if same and verbose < 2: - explanation.append("Omitting %s identical items, use -vv to show" % len(same)) + explanation.append('Omitting %s identical items, use -vv to show' % len(same)) elif same: - explanation += ["Matching attributes:"] + explanation += ['Matching attributes:'] explanation += highlighter(pprint.pformat(same)).splitlines() if diff: - explanation += ["Differing attributes:"] + explanation += ['Differing attributes:'] explanation += highlighter(pprint.pformat(diff)).splitlines() for field in diff: field_left = getattr(left, field) field_right = getattr(right, field) explanation += [ - "", - f"Drill down into differing attribute {field}:", - f"{indent}{field}: {highlighter(repr(field_left))} != {highlighter(repr(field_right))}", + '', + f'Drill down into differing attribute {field}:', + f'{indent}{field}: {highlighter(repr(field_left))} != {highlighter(repr(field_right))}', ] explanation += [ indent + line for line in _compare_eq_any( - field_left, field_right, highlighter, verbose + field_left, field_right, highlighter, verbose, ) ] return explanation -def _notin_text(term: str, text: str, verbose: int = 0) -> List[str]: +def _notin_text(term: str, text: str, verbose: int = 0) -> list[str]: index = text.find(term) head = text[:index] - tail = text[index + len(term) :] + tail = text[index + len(term):] correct_text = head + tail diff = _diff_text(text, correct_text, verbose) - newdiff = ["%s is contained here:" % saferepr(term, maxsize=42)] + newdiff = ['%s is contained here:' % saferepr(term, maxsize=42)] for line in diff: - if line.startswith("Skipping"): + if line.startswith('Skipping'): continue - if line.startswith("- "): + if line.startswith('- '): continue - if line.startswith("+ "): - newdiff.append(" " + line[2:]) + if line.startswith('+ '): + newdiff.append(' ' + line[2:]) else: newdiff.append(line) return newdiff @@ -605,5 +607,5 @@ def _notin_text(term: str, text: str, verbose: int = 0) -> List[str]: def running_on_ci() -> bool: """Check if we're currently running on a CI system.""" - env_vars = ["CI", "BUILD_NUMBER"] + env_vars = ['CI', 'BUILD_NUMBER'] return any(var in os.environ for var in env_vars) diff --git a/.venv/lib/python3.10/site-packages/_pytest/cacheprovider.py b/.venv/lib/python3.10/site-packages/_pytest/cacheprovider.py index 5ccd216..3d64507 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/cacheprovider.py +++ b/.venv/lib/python3.10/site-packages/_pytest/cacheprovider.py @@ -2,6 +2,8 @@ """Implementation of the cache provider.""" # This plugin was not named "cache" to avoid conflicts with the external # pytest-cache version. +from __future__ import annotations + import dataclasses import json import os @@ -15,9 +17,6 @@ from typing import Optional from typing import Set from typing import Union -from .pathlib import resolve_from_str -from .pathlib import rm_rf -from .reports import CollectReport from _pytest import nodes from _pytest._io import TerminalWriter from _pytest.config import Config @@ -32,6 +31,10 @@ from _pytest.nodes import Directory from _pytest.nodes import File from _pytest.reports import TestReport +from .pathlib import resolve_from_str +from .pathlib import rm_rf +from .reports import CollectReport + README_CONTENT = """\ # pytest cache directory # @@ -61,27 +64,27 @@ class Cache: _config: Config = dataclasses.field(repr=False) # Sub-directory under cache-dir for directories created by `mkdir()`. - _CACHE_PREFIX_DIRS = "d" + _CACHE_PREFIX_DIRS = 'd' # Sub-directory under cache-dir for values created by `set()`. - _CACHE_PREFIX_VALUES = "v" + _CACHE_PREFIX_VALUES = 'v' def __init__( - self, cachedir: Path, config: Config, *, _ispytest: bool = False + self, cachedir: Path, config: Config, *, _ispytest: bool = False, ) -> None: check_ispytest(_ispytest) self._cachedir = cachedir self._config = config @classmethod - def for_config(cls, config: Config, *, _ispytest: bool = False) -> "Cache": + def for_config(cls, config: Config, *, _ispytest: bool = False) -> Cache: """Create the Cache instance for a Config. :meta private: """ check_ispytest(_ispytest) cachedir = cls.cache_dir_from_config(config, _ispytest=True) - if config.getoption("cacheclear") and cachedir.is_dir(): + if config.getoption('cacheclear') and cachedir.is_dir(): cls.clear_cache(cachedir, _ispytest=True) return cls(cachedir, config, _ispytest=True) @@ -104,7 +107,7 @@ class Cache: :meta private: """ check_ispytest(_ispytest) - return resolve_from_str(config.getini("cache_dir"), config.rootpath) + return resolve_from_str(config.getini('cache_dir'), config.rootpath) def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None: """Issue a cache warning. @@ -138,7 +141,7 @@ class Cache: """ path = Path(name) if len(path.parts) > 1: - raise ValueError("name is not allowed to contain path separators") + raise ValueError('name is not allowed to contain path separators') res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path) res.mkdir(exist_ok=True, parents=True) return res @@ -160,7 +163,7 @@ class Cache: """ path = self._getvaluepath(key) try: - with path.open("r", encoding="UTF-8") as f: + with path.open('r', encoding='UTF-8') as f: return json.load(f) except (ValueError, OSError): return default @@ -184,7 +187,7 @@ class Cache: path.parent.mkdir(exist_ok=True, parents=True) except OSError as exc: self.warn( - f"could not create cache path {path}: {exc}", + f'could not create cache path {path}: {exc}', _ispytest=True, ) return @@ -192,10 +195,10 @@ class Cache: self._ensure_supporting_files() data = json.dumps(value, ensure_ascii=False, indent=2) try: - f = path.open("w", encoding="UTF-8") + f = path.open('w', encoding='UTF-8') except OSError as exc: self.warn( - f"cache could not write path {path}: {exc}", + f'cache could not write path {path}: {exc}', _ispytest=True, ) else: @@ -204,25 +207,25 @@ class Cache: def _ensure_supporting_files(self) -> None: """Create supporting files in the cache dir that are not really part of the cache.""" - readme_path = self._cachedir / "README.md" - readme_path.write_text(README_CONTENT, encoding="UTF-8") + readme_path = self._cachedir / 'README.md' + readme_path.write_text(README_CONTENT, encoding='UTF-8') - gitignore_path = self._cachedir.joinpath(".gitignore") - msg = "# Created by pytest automatically.\n*\n" - gitignore_path.write_text(msg, encoding="UTF-8") + gitignore_path = self._cachedir.joinpath('.gitignore') + msg = '# Created by pytest automatically.\n*\n' + gitignore_path.write_text(msg, encoding='UTF-8') - cachedir_tag_path = self._cachedir.joinpath("CACHEDIR.TAG") + cachedir_tag_path = self._cachedir.joinpath('CACHEDIR.TAG') cachedir_tag_path.write_bytes(CACHEDIR_TAG_CONTENT) class LFPluginCollWrapper: - def __init__(self, lfplugin: "LFPlugin") -> None: + def __init__(self, lfplugin: LFPlugin) -> None: self.lfplugin = lfplugin self._collected_at_least_one_failure = False @hookimpl(wrapper=True) def pytest_make_collect_report( - self, collector: nodes.Collector + self, collector: nodes.Collector, ) -> Generator[None, CollectReport, CollectReport]: res = yield if isinstance(collector, (Session, Directory)): @@ -230,7 +233,7 @@ class LFPluginCollWrapper: lf_paths = self.lfplugin._last_failed_paths # Use stable sort to priorize last failed. - def sort_key(node: Union[nodes.Item, nodes.Collector]) -> bool: + def sort_key(node: nodes.Item | nodes.Collector) -> bool: return node.path in lf_paths res.result = sorted( @@ -249,7 +252,7 @@ class LFPluginCollWrapper: if not any(x.nodeid in lastfailed for x in result): return res self.lfplugin.config.pluginmanager.register( - LFPluginCollSkipfiles(self.lfplugin), "lfplugin-collskip" + LFPluginCollSkipfiles(self.lfplugin), 'lfplugin-collskip', ) self._collected_at_least_one_failure = True @@ -257,30 +260,30 @@ class LFPluginCollWrapper: result[:] = [ x for x in result - if x.nodeid in lastfailed + if x.nodeid in lastfailed or # Include any passed arguments (not trivial to filter). - or session.isinitpath(x.path) + session.isinitpath(x.path) or # Keep all sub-collectors. - or isinstance(x, nodes.Collector) + isinstance(x, nodes.Collector) ] return res class LFPluginCollSkipfiles: - def __init__(self, lfplugin: "LFPlugin") -> None: + def __init__(self, lfplugin: LFPlugin) -> None: self.lfplugin = lfplugin @hookimpl def pytest_make_collect_report( - self, collector: nodes.Collector - ) -> Optional[CollectReport]: + self, collector: nodes.Collector, + ) -> CollectReport | None: if isinstance(collector, File): if collector.path not in self.lfplugin._last_failed_paths: self.lfplugin._skipped_files += 1 return CollectReport( - collector.nodeid, "passed", longrepr=None, result=[] + collector.nodeid, 'passed', longrepr=None, result=[], ) return None @@ -290,44 +293,44 @@ class LFPlugin: def __init__(self, config: Config) -> None: self.config = config - active_keys = "lf", "failedfirst" + active_keys = 'lf', 'failedfirst' self.active = any(config.getoption(key) for key in active_keys) assert config.cache - self.lastfailed: Dict[str, bool] = config.cache.get("cache/lastfailed", {}) - self._previously_failed_count: Optional[int] = None - self._report_status: Optional[str] = None + self.lastfailed: dict[str, bool] = config.cache.get('cache/lastfailed', {}) + self._previously_failed_count: int | None = None + self._report_status: str | None = None self._skipped_files = 0 # count skipped files during collection due to --lf - if config.getoption("lf"): + if config.getoption('lf'): self._last_failed_paths = self.get_last_failed_paths() config.pluginmanager.register( - LFPluginCollWrapper(self), "lfplugin-collwrapper" + LFPluginCollWrapper(self), 'lfplugin-collwrapper', ) - def get_last_failed_paths(self) -> Set[Path]: + def get_last_failed_paths(self) -> set[Path]: """Return a set with all Paths of the previously failed nodeids and their parents.""" rootpath = self.config.rootpath result = set() for nodeid in self.lastfailed: - path = rootpath / nodeid.split("::")[0] + path = rootpath / nodeid.split('::')[0] result.add(path) result.update(path.parents) return {x for x in result if x.exists()} - def pytest_report_collectionfinish(self) -> Optional[str]: - if self.active and self.config.getoption("verbose") >= 0: - return "run-last-failure: %s" % self._report_status + def pytest_report_collectionfinish(self) -> str | None: + if self.active and self.config.getoption('verbose') >= 0: + return 'run-last-failure: %s' % self._report_status return None def pytest_runtest_logreport(self, report: TestReport) -> None: - if (report.when == "call" and report.passed) or report.skipped: + if (report.when == 'call' and report.passed) or report.skipped: self.lastfailed.pop(report.nodeid, None) elif report.failed: self.lastfailed[report.nodeid] = True def pytest_collectreport(self, report: CollectReport) -> None: - passed = report.outcome in ("passed", "skipped") + passed = report.outcome in ('passed', 'skipped') if passed: if report.nodeid in self.lastfailed: self.lastfailed.pop(report.nodeid) @@ -337,7 +340,7 @@ class LFPlugin: @hookimpl(wrapper=True, tryfirst=True) def pytest_collection_modifyitems( - self, config: Config, items: List[nodes.Item] + self, config: Config, items: list[nodes.Item], ) -> Generator[None, None, None]: res = yield @@ -357,45 +360,45 @@ class LFPlugin: if not previously_failed: # Running a subset of all tests with recorded failures # only outside of it. - self._report_status = "%d known failures not in selected tests" % ( + self._report_status = '%d known failures not in selected tests' % ( len(self.lastfailed), ) else: - if self.config.getoption("lf"): + if self.config.getoption('lf'): items[:] = previously_failed config.hook.pytest_deselected(items=previously_passed) else: # --failedfirst items[:] = previously_failed + previously_passed - noun = "failure" if self._previously_failed_count == 1 else "failures" - suffix = " first" if self.config.getoption("failedfirst") else "" + noun = 'failure' if self._previously_failed_count == 1 else 'failures' + suffix = ' first' if self.config.getoption('failedfirst') else '' self._report_status = ( - f"rerun previous {self._previously_failed_count} {noun}{suffix}" + f'rerun previous {self._previously_failed_count} {noun}{suffix}' ) if self._skipped_files > 0: - files_noun = "file" if self._skipped_files == 1 else "files" - self._report_status += f" (skipped {self._skipped_files} {files_noun})" + files_noun = 'file' if self._skipped_files == 1 else 'files' + self._report_status += f' (skipped {self._skipped_files} {files_noun})' else: - self._report_status = "no previously failed tests, " - if self.config.getoption("last_failed_no_failures") == "none": - self._report_status += "deselecting all items." + self._report_status = 'no previously failed tests, ' + if self.config.getoption('last_failed_no_failures') == 'none': + self._report_status += 'deselecting all items.' config.hook.pytest_deselected(items=items[:]) items[:] = [] else: - self._report_status += "not deselecting items." + self._report_status += 'not deselecting items.' return res def pytest_sessionfinish(self, session: Session) -> None: config = self.config - if config.getoption("cacheshow") or hasattr(config, "workerinput"): + if config.getoption('cacheshow') or hasattr(config, 'workerinput'): return assert config.cache is not None - saved_lastfailed = config.cache.get("cache/lastfailed", {}) + saved_lastfailed = config.cache.get('cache/lastfailed', {}) if saved_lastfailed != self.lastfailed: - config.cache.set("cache/lastfailed", self.lastfailed) + config.cache.set('cache/lastfailed', self.lastfailed) class NFPlugin: @@ -405,17 +408,17 @@ class NFPlugin: self.config = config self.active = config.option.newfirst assert config.cache is not None - self.cached_nodeids = set(config.cache.get("cache/nodeids", [])) + self.cached_nodeids = set(config.cache.get('cache/nodeids', [])) @hookimpl(wrapper=True, tryfirst=True) def pytest_collection_modifyitems( - self, items: List[nodes.Item] + self, items: list[nodes.Item], ) -> Generator[None, None, None]: res = yield if self.active: - new_items: Dict[str, nodes.Item] = {} - other_items: Dict[str, nodes.Item] = {} + new_items: dict[str, nodes.Item] = {} + other_items: dict[str, nodes.Item] = {} for item in items: if item.nodeid not in self.cached_nodeids: new_items[item.nodeid] = item @@ -423,7 +426,7 @@ class NFPlugin: other_items[item.nodeid] = item items[:] = self._get_increasing_order( - new_items.values() + new_items.values(), ) + self._get_increasing_order(other_items.values()) self.cached_nodeids.update(new_items) else: @@ -431,84 +434,84 @@ class NFPlugin: return res - def _get_increasing_order(self, items: Iterable[nodes.Item]) -> List[nodes.Item]: + def _get_increasing_order(self, items: Iterable[nodes.Item]) -> list[nodes.Item]: return sorted(items, key=lambda item: item.path.stat().st_mtime, reverse=True) # type: ignore[no-any-return] def pytest_sessionfinish(self) -> None: config = self.config - if config.getoption("cacheshow") or hasattr(config, "workerinput"): + if config.getoption('cacheshow') or hasattr(config, 'workerinput'): return - if config.getoption("collectonly"): + if config.getoption('collectonly'): return assert config.cache is not None - config.cache.set("cache/nodeids", sorted(self.cached_nodeids)) + config.cache.set('cache/nodeids', sorted(self.cached_nodeids)) def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") + group = parser.getgroup('general') group.addoption( - "--lf", - "--last-failed", - action="store_true", - dest="lf", - help="Rerun only the tests that failed " - "at the last run (or all if none failed)", + '--lf', + '--last-failed', + action='store_true', + dest='lf', + help='Rerun only the tests that failed ' + 'at the last run (or all if none failed)', ) group.addoption( - "--ff", - "--failed-first", - action="store_true", - dest="failedfirst", - help="Run all tests, but run the last failures first. " - "This may re-order tests and thus lead to " - "repeated fixture setup/teardown.", + '--ff', + '--failed-first', + action='store_true', + dest='failedfirst', + help='Run all tests, but run the last failures first. ' + 'This may re-order tests and thus lead to ' + 'repeated fixture setup/teardown.', ) group.addoption( - "--nf", - "--new-first", - action="store_true", - dest="newfirst", - help="Run tests from new files first, then the rest of the tests " - "sorted by file mtime", + '--nf', + '--new-first', + action='store_true', + dest='newfirst', + help='Run tests from new files first, then the rest of the tests ' + 'sorted by file mtime', ) group.addoption( - "--cache-show", - action="append", - nargs="?", - dest="cacheshow", + '--cache-show', + action='append', + nargs='?', + dest='cacheshow', help=( "Show cache contents, don't perform collection or tests. " "Optional argument: glob (default: '*')." ), ) group.addoption( - "--cache-clear", - action="store_true", - dest="cacheclear", - help="Remove all cache contents at start of test run", + '--cache-clear', + action='store_true', + dest='cacheclear', + help='Remove all cache contents at start of test run', ) - cache_dir_default = ".pytest_cache" - if "TOX_ENV_DIR" in os.environ: - cache_dir_default = os.path.join(os.environ["TOX_ENV_DIR"], cache_dir_default) - parser.addini("cache_dir", default=cache_dir_default, help="Cache directory path") + cache_dir_default = '.pytest_cache' + if 'TOX_ENV_DIR' in os.environ: + cache_dir_default = os.path.join(os.environ['TOX_ENV_DIR'], cache_dir_default) + parser.addini('cache_dir', default=cache_dir_default, help='Cache directory path') group.addoption( - "--lfnf", - "--last-failed-no-failures", - action="store", - dest="last_failed_no_failures", - choices=("all", "none"), - default="all", - help="With ``--lf``, determines whether to execute tests when there " - "are no previously (known) failures or when no " - "cached ``lastfailed`` data was found. " - "``all`` (the default) runs the full test suite again. " - "``none`` just emits a message about no known failures and exits successfully.", + '--lfnf', + '--last-failed-no-failures', + action='store', + dest='last_failed_no_failures', + choices=('all', 'none'), + default='all', + help='With ``--lf``, determines whether to execute tests when there ' + 'are no previously (known) failures or when no ' + 'cached ``lastfailed`` data was found. ' + '``all`` (the default) runs the full test suite again. ' + '``none`` just emits a message about no known failures and exits successfully.', ) -def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: if config.option.cacheshow and not config.option.help: from _pytest.main import wrap_session @@ -519,8 +522,8 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: @hookimpl(tryfirst=True) def pytest_configure(config: Config) -> None: config.cache = Cache.for_config(config, _ispytest=True) - config.pluginmanager.register(LFPlugin(config), "lfplugin") - config.pluginmanager.register(NFPlugin(config), "nfplugin") + config.pluginmanager.register(LFPlugin(config), 'lfplugin') + config.pluginmanager.register(NFPlugin(config), 'nfplugin') @fixture @@ -539,9 +542,9 @@ def cache(request: FixtureRequest) -> Cache: return request.config.cache -def pytest_report_header(config: Config) -> Optional[str]: +def pytest_report_header(config: Config) -> str | None: """Display cachedir with --cache-show and if non-default.""" - if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache": + if config.option.verbose > 0 or config.getini('cache_dir') != '.pytest_cache': assert config.cache is not None cachedir = config.cache._cachedir # TODO: evaluate generating upward relative paths @@ -551,7 +554,7 @@ def pytest_report_header(config: Config) -> Optional[str]: displaypath = cachedir.relative_to(config.rootpath) except ValueError: displaypath = cachedir - return f"cachedir: {displaypath}" + return f'cachedir: {displaypath}' return None @@ -561,37 +564,37 @@ def cacheshow(config: Config, session: Session) -> int: assert config.cache is not None tw = TerminalWriter() - tw.line("cachedir: " + str(config.cache._cachedir)) + tw.line('cachedir: ' + str(config.cache._cachedir)) if not config.cache._cachedir.is_dir(): - tw.line("cache is empty") + tw.line('cache is empty') return 0 glob = config.option.cacheshow[0] if glob is None: - glob = "*" + glob = '*' dummy = object() basedir = config.cache._cachedir vdir = basedir / Cache._CACHE_PREFIX_VALUES - tw.sep("-", "cache values for %r" % glob) + tw.sep('-', 'cache values for %r' % glob) for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()): key = str(valpath.relative_to(vdir)) val = config.cache.get(key, dummy) if val is dummy: - tw.line("%s contains unreadable content, will be ignored" % key) + tw.line('%s contains unreadable content, will be ignored' % key) else: - tw.line("%s contains:" % key) + tw.line('%s contains:' % key) for line in pformat(val).splitlines(): - tw.line(" " + line) + tw.line(' ' + line) ddir = basedir / Cache._CACHE_PREFIX_DIRS if ddir.is_dir(): contents = sorted(ddir.rglob(glob)) - tw.sep("-", "cache directories for %r" % glob) + tw.sep('-', 'cache directories for %r' % glob) for p in contents: # if p.is_dir(): # print("%s/" % p.relative_to(basedir)) if p.is_file(): key = str(p.relative_to(basedir)) - tw.line(f"{key} is a file of length {p.stat().st_size:d}") + tw.line(f'{key} is a file of length {p.stat().st_size:d}') return 0 diff --git a/.venv/lib/python3.10/site-packages/_pytest/capture.py b/.venv/lib/python3.10/site-packages/_pytest/capture.py index dce431c..40d9ab6 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/capture.py +++ b/.venv/lib/python3.10/site-packages/_pytest/capture.py @@ -1,12 +1,14 @@ # mypy: allow-untyped-defs """Per-test stdout/stderr capturing mechanism.""" +from __future__ import annotations + import abc import collections import contextlib import io -from io import UnsupportedOperation import os import sys +from io import UnsupportedOperation from tempfile import TemporaryFile from types import TracebackType from typing import Any @@ -40,25 +42,25 @@ from _pytest.nodes import Item from _pytest.reports import CollectReport -_CaptureMethod = Literal["fd", "sys", "no", "tee-sys"] +_CaptureMethod = Literal['fd', 'sys', 'no', 'tee-sys'] def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") + group = parser.getgroup('general') group._addoption( - "--capture", - action="store", - default="fd", - metavar="method", - choices=["fd", "sys", "no", "tee-sys"], - help="Per-test capturing method: one of fd|sys|no|tee-sys", + '--capture', + action='store', + default='fd', + metavar='method', + choices=['fd', 'sys', 'no', 'tee-sys'], + help='Per-test capturing method: one of fd|sys|no|tee-sys', ) group._addoption( - "-s", - action="store_const", - const="no", - dest="capture", - help="Shortcut for --capture=no", + '-s', + action='store_const', + const='no', + dest='capture', + help='Shortcut for --capture=no', ) @@ -70,7 +72,7 @@ def _colorama_workaround() -> None: first import of colorama while I/O capture is active, colorama will fail in various ways. """ - if sys.platform.startswith("win32"): + if sys.platform.startswith('win32'): try: import colorama # noqa: F401 except ImportError: @@ -101,21 +103,21 @@ def _windowsconsoleio_workaround(stream: TextIO) -> None: See https://github.com/pytest-dev/py/issues/103. """ - if not sys.platform.startswith("win32") or hasattr(sys, "pypy_version_info"): + if not sys.platform.startswith('win32') or hasattr(sys, 'pypy_version_info'): return # Bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666). - if not hasattr(stream, "buffer"): # type: ignore[unreachable] + if not hasattr(stream, 'buffer'): # type: ignore[unreachable] return - buffered = hasattr(stream.buffer, "raw") + buffered = hasattr(stream.buffer, 'raw') raw_stdout = stream.buffer.raw if buffered else stream.buffer # type: ignore[attr-defined] if not isinstance(raw_stdout, io._WindowsConsoleIO): # type: ignore[attr-defined] return def _reopen_stdio(f, mode): - if not buffered and mode[0] == "w": + if not buffered and mode[0] == 'w': buffering = 0 else: buffering = -1 @@ -128,20 +130,20 @@ def _windowsconsoleio_workaround(stream: TextIO) -> None: f.line_buffering, ) - sys.stdin = _reopen_stdio(sys.stdin, "rb") - sys.stdout = _reopen_stdio(sys.stdout, "wb") - sys.stderr = _reopen_stdio(sys.stderr, "wb") + sys.stdin = _reopen_stdio(sys.stdin, 'rb') + sys.stdout = _reopen_stdio(sys.stdout, 'wb') + sys.stderr = _reopen_stdio(sys.stderr, 'wb') @hookimpl(wrapper=True) def pytest_load_initial_conftests(early_config: Config) -> Generator[None, None, None]: ns = early_config.known_args_namespace - if ns.capture == "fd": + if ns.capture == 'fd': _windowsconsoleio_workaround(sys.stdout) _colorama_workaround() pluginmanager = early_config.pluginmanager capman = CaptureManager(ns.capture) - pluginmanager.register(capman, "capturemanager") + pluginmanager.register(capman, 'capturemanager') # Make sure that capturemanager is properly reset at final shutdown. early_config.add_cleanup(capman.stop_global_capturing) @@ -176,16 +178,16 @@ class EncodedFile(io.TextIOWrapper): def mode(self) -> str: # TextIOWrapper doesn't expose a mode, but at least some of our # tests check it. - return self.buffer.mode.replace("b", "") + return self.buffer.mode.replace('b', '') class CaptureIO(io.TextIOWrapper): def __init__(self) -> None: - super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True) + super().__init__(io.BytesIO(), encoding='UTF-8', newline='', write_through=True) def getvalue(self) -> str: assert isinstance(self.buffer, io.BytesIO) - return self.buffer.getvalue().decode("UTF-8") + return self.buffer.getvalue().decode('UTF-8') class TeeCaptureIO(CaptureIO): @@ -205,7 +207,7 @@ class DontReadFromInput(TextIO): def read(self, size: int = -1) -> str: raise OSError( - "pytest: reading from stdin while output is captured! Consider using `-s`." + 'pytest: reading from stdin while output is captured! Consider using `-s`.', ) readline = read @@ -213,19 +215,19 @@ class DontReadFromInput(TextIO): def __next__(self) -> str: return self.readline() - def readlines(self, hint: Optional[int] = -1) -> List[str]: + def readlines(self, hint: int | None = -1) -> list[str]: raise OSError( - "pytest: reading from stdin while output is captured! Consider using `-s`." + 'pytest: reading from stdin while output is captured! Consider using `-s`.', ) def __iter__(self) -> Iterator[str]: return self def fileno(self) -> int: - raise UnsupportedOperation("redirected stdin is pseudofile, has no fileno()") + raise UnsupportedOperation('redirected stdin is pseudofile, has no fileno()') def flush(self) -> None: - raise UnsupportedOperation("redirected stdin is pseudofile, has no flush()") + raise UnsupportedOperation('redirected stdin is pseudofile, has no flush()') def isatty(self) -> bool: return False @@ -237,34 +239,34 @@ class DontReadFromInput(TextIO): return False def seek(self, offset: int, whence: int = 0) -> int: - raise UnsupportedOperation("redirected stdin is pseudofile, has no seek(int)") + raise UnsupportedOperation('redirected stdin is pseudofile, has no seek(int)') def seekable(self) -> bool: return False def tell(self) -> int: - raise UnsupportedOperation("redirected stdin is pseudofile, has no tell()") + raise UnsupportedOperation('redirected stdin is pseudofile, has no tell()') - def truncate(self, size: Optional[int] = None) -> int: - raise UnsupportedOperation("cannot truncate stdin") + def truncate(self, size: int | None = None) -> int: + raise UnsupportedOperation('cannot truncate stdin') def write(self, data: str) -> int: - raise UnsupportedOperation("cannot write to stdin") + raise UnsupportedOperation('cannot write to stdin') def writelines(self, lines: Iterable[str]) -> None: - raise UnsupportedOperation("Cannot write to stdin") + raise UnsupportedOperation('Cannot write to stdin') def writable(self) -> bool: return False - def __enter__(self) -> "DontReadFromInput": + def __enter__(self) -> DontReadFromInput: return self def __exit__( self, - type: Optional[Type[BaseException]], - value: Optional[BaseException], - traceback: Optional[TracebackType], + type: type[BaseException] | None, + value: BaseException | None, + traceback: TracebackType | None, ) -> None: pass @@ -309,11 +311,11 @@ class CaptureBase(abc.ABC, Generic[AnyStr]): raise NotImplementedError() -patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} +patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} class NoCapture(CaptureBase[str]): - EMPTY_BUFFER = "" + EMPTY_BUFFER = '' def __init__(self, fd: int) -> None: pass @@ -331,7 +333,7 @@ class NoCapture(CaptureBase[str]): pass def snap(self) -> str: - return "" + return '' def writeorg(self, data: str) -> None: pass @@ -339,76 +341,76 @@ class NoCapture(CaptureBase[str]): class SysCaptureBase(CaptureBase[AnyStr]): def __init__( - self, fd: int, tmpfile: Optional[TextIO] = None, *, tee: bool = False + self, fd: int, tmpfile: TextIO | None = None, *, tee: bool = False, ) -> None: name = patchsysdict[fd] self._old: TextIO = getattr(sys, name) self.name = name if tmpfile is None: - if name == "stdin": + if name == 'stdin': tmpfile = DontReadFromInput() else: tmpfile = CaptureIO() if not tee else TeeCaptureIO(self._old) self.tmpfile = tmpfile - self._state = "initialized" + self._state = 'initialized' def repr(self, class_name: str) -> str: - return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + return '<{} {} _old={} _state={!r} tmpfile={!r}>'.format( class_name, self.name, - hasattr(self, "_old") and repr(self._old) or "", + hasattr(self, '_old') and repr(self._old) or '', self._state, self.tmpfile, ) def __repr__(self) -> str: - return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + return '<{} {} _old={} _state={!r} tmpfile={!r}>'.format( self.__class__.__name__, self.name, - hasattr(self, "_old") and repr(self._old) or "", + hasattr(self, '_old') and repr(self._old) or '', self._state, self.tmpfile, ) - def _assert_state(self, op: str, states: Tuple[str, ...]) -> None: + def _assert_state(self, op: str, states: tuple[str, ...]) -> None: assert ( self._state in states - ), "cannot {} in state {!r}: expected one of {}".format( - op, self._state, ", ".join(states) + ), 'cannot {} in state {!r}: expected one of {}'.format( + op, self._state, ', '.join(states), ) def start(self) -> None: - self._assert_state("start", ("initialized",)) + self._assert_state('start', ('initialized',)) setattr(sys, self.name, self.tmpfile) - self._state = "started" + self._state = 'started' def done(self) -> None: - self._assert_state("done", ("initialized", "started", "suspended", "done")) - if self._state == "done": + self._assert_state('done', ('initialized', 'started', 'suspended', 'done')) + if self._state == 'done': return setattr(sys, self.name, self._old) del self._old self.tmpfile.close() - self._state = "done" + self._state = 'done' def suspend(self) -> None: - self._assert_state("suspend", ("started", "suspended")) + self._assert_state('suspend', ('started', 'suspended')) setattr(sys, self.name, self._old) - self._state = "suspended" + self._state = 'suspended' def resume(self) -> None: - self._assert_state("resume", ("started", "suspended")) - if self._state == "started": + self._assert_state('resume', ('started', 'suspended')) + if self._state == 'started': return setattr(sys, self.name, self.tmpfile) - self._state = "started" + self._state = 'started' class SysCaptureBinary(SysCaptureBase[bytes]): - EMPTY_BUFFER = b"" + EMPTY_BUFFER = b'' def snap(self) -> bytes: - self._assert_state("snap", ("started", "suspended")) + self._assert_state('snap', ('started', 'suspended')) self.tmpfile.seek(0) res = self.tmpfile.buffer.read() self.tmpfile.seek(0) @@ -416,17 +418,17 @@ class SysCaptureBinary(SysCaptureBase[bytes]): return res def writeorg(self, data: bytes) -> None: - self._assert_state("writeorg", ("started", "suspended")) + self._assert_state('writeorg', ('started', 'suspended')) self._old.flush() self._old.buffer.write(data) self._old.buffer.flush() class SysCapture(SysCaptureBase[str]): - EMPTY_BUFFER = "" + EMPTY_BUFFER = '' def snap(self) -> str: - self._assert_state("snap", ("started", "suspended")) + self._assert_state('snap', ('started', 'suspended')) assert isinstance(self.tmpfile, CaptureIO) res = self.tmpfile.getvalue() self.tmpfile.seek(0) @@ -434,7 +436,7 @@ class SysCapture(SysCaptureBase[str]): return res def writeorg(self, data: str) -> None: - self._assert_state("writeorg", ("started", "suspended")) + self._assert_state('writeorg', ('started', 'suspended')) self._old.write(data) self._old.flush() @@ -457,21 +459,21 @@ class FDCaptureBase(CaptureBase[AnyStr]): # Further complications are the need to support suspend() and the # possibility of FD reuse (e.g. the tmpfile getting the very same # target FD). The following approach is robust, I believe. - self.targetfd_invalid: Optional[int] = os.open(os.devnull, os.O_RDWR) + self.targetfd_invalid: int | None = os.open(os.devnull, os.O_RDWR) os.dup2(self.targetfd_invalid, targetfd) else: self.targetfd_invalid = None self.targetfd_save = os.dup(targetfd) if targetfd == 0: - self.tmpfile = open(os.devnull, encoding="utf-8") + self.tmpfile = open(os.devnull, encoding='utf-8') self.syscapture: CaptureBase[str] = SysCapture(targetfd) else: self.tmpfile = EncodedFile( TemporaryFile(buffering=0), - encoding="utf-8", - errors="replace", - newline="", + encoding='utf-8', + errors='replace', + newline='', write_through=True, ) if targetfd in patchsysdict: @@ -479,10 +481,10 @@ class FDCaptureBase(CaptureBase[AnyStr]): else: self.syscapture = NoCapture(targetfd) - self._state = "initialized" + self._state = 'initialized' def __repr__(self) -> str: - return "<{} {} oldfd={} _state={!r} tmpfile={!r}>".format( + return '<{} {} oldfd={} _state={!r} tmpfile={!r}>'.format( self.__class__.__name__, self.targetfd, self.targetfd_save, @@ -490,25 +492,25 @@ class FDCaptureBase(CaptureBase[AnyStr]): self.tmpfile, ) - def _assert_state(self, op: str, states: Tuple[str, ...]) -> None: + def _assert_state(self, op: str, states: tuple[str, ...]) -> None: assert ( self._state in states - ), "cannot {} in state {!r}: expected one of {}".format( - op, self._state, ", ".join(states) + ), 'cannot {} in state {!r}: expected one of {}'.format( + op, self._state, ', '.join(states), ) def start(self) -> None: """Start capturing on targetfd using memorized tmpfile.""" - self._assert_state("start", ("initialized",)) + self._assert_state('start', ('initialized',)) os.dup2(self.tmpfile.fileno(), self.targetfd) self.syscapture.start() - self._state = "started" + self._state = 'started' def done(self) -> None: """Stop capturing, restore streams, return original capture file, seeked to position zero.""" - self._assert_state("done", ("initialized", "started", "suspended", "done")) - if self._state == "done": + self._assert_state('done', ('initialized', 'started', 'suspended', 'done')) + if self._state == 'done': return os.dup2(self.targetfd_save, self.targetfd) os.close(self.targetfd_save) @@ -518,23 +520,23 @@ class FDCaptureBase(CaptureBase[AnyStr]): os.close(self.targetfd_invalid) self.syscapture.done() self.tmpfile.close() - self._state = "done" + self._state = 'done' def suspend(self) -> None: - self._assert_state("suspend", ("started", "suspended")) - if self._state == "suspended": + self._assert_state('suspend', ('started', 'suspended')) + if self._state == 'suspended': return self.syscapture.suspend() os.dup2(self.targetfd_save, self.targetfd) - self._state = "suspended" + self._state = 'suspended' def resume(self) -> None: - self._assert_state("resume", ("started", "suspended")) - if self._state == "started": + self._assert_state('resume', ('started', 'suspended')) + if self._state == 'started': return self.syscapture.resume() os.dup2(self.tmpfile.fileno(), self.targetfd) - self._state = "started" + self._state = 'started' class FDCaptureBinary(FDCaptureBase[bytes]): @@ -543,10 +545,10 @@ class FDCaptureBinary(FDCaptureBase[bytes]): snap() produces `bytes`. """ - EMPTY_BUFFER = b"" + EMPTY_BUFFER = b'' def snap(self) -> bytes: - self._assert_state("snap", ("started", "suspended")) + self._assert_state('snap', ('started', 'suspended')) self.tmpfile.seek(0) res = self.tmpfile.buffer.read() self.tmpfile.seek(0) @@ -555,7 +557,7 @@ class FDCaptureBinary(FDCaptureBase[bytes]): def writeorg(self, data: bytes) -> None: """Write to original file descriptor.""" - self._assert_state("writeorg", ("started", "suspended")) + self._assert_state('writeorg', ('started', 'suspended')) os.write(self.targetfd_save, data) @@ -565,10 +567,10 @@ class FDCapture(FDCaptureBase[str]): snap() produces text. """ - EMPTY_BUFFER = "" + EMPTY_BUFFER = '' def snap(self) -> str: - self._assert_state("snap", ("started", "suspended")) + self._assert_state('snap', ('started', 'suspended')) self.tmpfile.seek(0) res = self.tmpfile.read() self.tmpfile.seek(0) @@ -577,9 +579,9 @@ class FDCapture(FDCaptureBase[str]): def writeorg(self, data: str) -> None: """Write to original file descriptor.""" - self._assert_state("writeorg", ("started", "suspended")) + self._assert_state('writeorg', ('started', 'suspended')) # XXX use encoding of original stream - os.write(self.targetfd_save, data.encode("utf-8")) + os.write(self.targetfd_save, data.encode('utf-8')) # MultiCapture @@ -598,7 +600,7 @@ if sys.version_info >= (3, 11) or TYPE_CHECKING: else: class CaptureResult( - collections.namedtuple("CaptureResult", ["out", "err"]), # noqa: PYI024 + collections.namedtuple('CaptureResult', ['out', 'err']), # noqa: PYI024 Generic[AnyStr], ): """The result of :method:`caplog.readouterr() `.""" @@ -612,16 +614,16 @@ class MultiCapture(Generic[AnyStr]): def __init__( self, - in_: Optional[CaptureBase[AnyStr]], - out: Optional[CaptureBase[AnyStr]], - err: Optional[CaptureBase[AnyStr]], + in_: CaptureBase[AnyStr] | None, + out: CaptureBase[AnyStr] | None, + err: CaptureBase[AnyStr] | None, ) -> None: - self.in_: Optional[CaptureBase[AnyStr]] = in_ - self.out: Optional[CaptureBase[AnyStr]] = out - self.err: Optional[CaptureBase[AnyStr]] = err + self.in_: CaptureBase[AnyStr] | None = in_ + self.out: CaptureBase[AnyStr] | None = out + self.err: CaptureBase[AnyStr] | None = err def __repr__(self) -> str: - return "".format( + return ''.format( self.out, self.err, self.in_, @@ -630,7 +632,7 @@ class MultiCapture(Generic[AnyStr]): ) def start_capturing(self) -> None: - self._state = "started" + self._state = 'started' if self.in_: self.in_.start() if self.out: @@ -638,7 +640,7 @@ class MultiCapture(Generic[AnyStr]): if self.err: self.err.start() - def pop_outerr_to_orig(self) -> Tuple[AnyStr, AnyStr]: + def pop_outerr_to_orig(self) -> tuple[AnyStr, AnyStr]: """Pop current snapshot out/err capture and flush to orig streams.""" out, err = self.readouterr() if out: @@ -650,7 +652,7 @@ class MultiCapture(Generic[AnyStr]): return out, err def suspend_capturing(self, in_: bool = False) -> None: - self._state = "suspended" + self._state = 'suspended' if self.out: self.out.suspend() if self.err: @@ -660,7 +662,7 @@ class MultiCapture(Generic[AnyStr]): self._in_suspended = True def resume_capturing(self) -> None: - self._state = "started" + self._state = 'started' if self.out: self.out.resume() if self.err: @@ -672,9 +674,9 @@ class MultiCapture(Generic[AnyStr]): def stop_capturing(self) -> None: """Stop capturing and reset capturing streams.""" - if self._state == "stopped": - raise ValueError("was already stopped") - self._state = "stopped" + if self._state == 'stopped': + raise ValueError('was already stopped') + self._state = 'stopped' if self.out: self.out.done() if self.err: @@ -684,27 +686,27 @@ class MultiCapture(Generic[AnyStr]): def is_started(self) -> bool: """Whether actively capturing -- not suspended or stopped.""" - return self._state == "started" + return self._state == 'started' def readouterr(self) -> CaptureResult[AnyStr]: - out = self.out.snap() if self.out else "" - err = self.err.snap() if self.err else "" + out = self.out.snap() if self.out else '' + err = self.err.snap() if self.err else '' # TODO: This type error is real, need to fix. return CaptureResult(out, err) # type: ignore[arg-type] def _get_multicapture(method: _CaptureMethod) -> MultiCapture[str]: - if method == "fd": + if method == 'fd': return MultiCapture(in_=FDCapture(0), out=FDCapture(1), err=FDCapture(2)) - elif method == "sys": + elif method == 'sys': return MultiCapture(in_=SysCapture(0), out=SysCapture(1), err=SysCapture(2)) - elif method == "no": + elif method == 'no': return MultiCapture(in_=None, out=None, err=None) - elif method == "tee-sys": + elif method == 'tee-sys': return MultiCapture( - in_=None, out=SysCapture(1, tee=True), err=SysCapture(2, tee=True) + in_=None, out=SysCapture(1, tee=True), err=SysCapture(2, tee=True), ) - raise ValueError(f"unknown capturing method: {method!r}") + raise ValueError(f'unknown capturing method: {method!r}') # CaptureManager and CaptureFixture @@ -731,25 +733,25 @@ class CaptureManager: def __init__(self, method: _CaptureMethod) -> None: self._method: Final = method - self._global_capturing: Optional[MultiCapture[str]] = None - self._capture_fixture: Optional[CaptureFixture[Any]] = None + self._global_capturing: MultiCapture[str] | None = None + self._capture_fixture: CaptureFixture[Any] | None = None def __repr__(self) -> str: - return "".format( - self._method, self._global_capturing, self._capture_fixture + return ''.format( + self._method, self._global_capturing, self._capture_fixture, ) - def is_capturing(self) -> Union[str, bool]: + def is_capturing(self) -> str | bool: if self.is_globally_capturing(): - return "global" + return 'global' if self._capture_fixture: - return "fixture %s" % self._capture_fixture.request.fixturename + return 'fixture %s' % self._capture_fixture.request.fixturename return False # Global capturing control def is_globally_capturing(self) -> bool: - return self._method != "no" + return self._method != 'no' def start_global_capturing(self) -> None: assert self._global_capturing is None @@ -787,12 +789,12 @@ class CaptureManager: # Fixture Control - def set_fixture(self, capture_fixture: "CaptureFixture[Any]") -> None: + def set_fixture(self, capture_fixture: CaptureFixture[Any]) -> None: if self._capture_fixture: current_fixture = self._capture_fixture.request.fixturename requested_fixture = capture_fixture.request.fixturename capture_fixture.request.raiseerror( - f"cannot use {requested_fixture} and {current_fixture} at the same time" + f'cannot use {requested_fixture} and {current_fixture} at the same time', ) self._capture_fixture = capture_fixture @@ -848,14 +850,14 @@ class CaptureManager: self.suspend_global_capture(in_=False) out, err = self.read_global_capture() - item.add_report_section(when, "stdout", out) - item.add_report_section(when, "stderr", err) + item.add_report_section(when, 'stdout', out) + item.add_report_section(when, 'stderr', err) # Hooks @hookimpl(wrapper=True) def pytest_make_collect_report( - self, collector: Collector + self, collector: Collector, ) -> Generator[None, CollectReport, CollectReport]: if isinstance(collector, File): self.resume_global_capture() @@ -865,26 +867,26 @@ class CaptureManager: self.suspend_global_capture() out, err = self.read_global_capture() if out: - rep.sections.append(("Captured stdout", out)) + rep.sections.append(('Captured stdout', out)) if err: - rep.sections.append(("Captured stderr", err)) + rep.sections.append(('Captured stderr', err)) else: rep = yield return rep @hookimpl(wrapper=True) def pytest_runtest_setup(self, item: Item) -> Generator[None, None, None]: - with self.item_capture("setup", item): + with self.item_capture('setup', item): return (yield) @hookimpl(wrapper=True) def pytest_runtest_call(self, item: Item) -> Generator[None, None, None]: - with self.item_capture("call", item): + with self.item_capture('call', item): return (yield) @hookimpl(wrapper=True) def pytest_runtest_teardown(self, item: Item) -> Generator[None, None, None]: - with self.item_capture("teardown", item): + with self.item_capture('teardown', item): return (yield) @hookimpl(tryfirst=True) @@ -902,15 +904,15 @@ class CaptureFixture(Generic[AnyStr]): def __init__( self, - captureclass: Type[CaptureBase[AnyStr]], + captureclass: type[CaptureBase[AnyStr]], request: SubRequest, *, _ispytest: bool = False, ) -> None: check_ispytest(_ispytest) - self.captureclass: Type[CaptureBase[AnyStr]] = captureclass + self.captureclass: type[CaptureBase[AnyStr]] = captureclass self.request = request - self._capture: Optional[MultiCapture[AnyStr]] = None + self._capture: MultiCapture[AnyStr] | None = None self._captured_out: AnyStr = self.captureclass.EMPTY_BUFFER self._captured_err: AnyStr = self.captureclass.EMPTY_BUFFER @@ -968,7 +970,7 @@ class CaptureFixture(Generic[AnyStr]): def disabled(self) -> Generator[None, None, None]: """Temporarily disable capturing while inside the ``with`` block.""" capmanager: CaptureManager = self.request.config.pluginmanager.getplugin( - "capturemanager" + 'capturemanager', ) with capmanager.global_and_fixture_disabled(): yield @@ -995,7 +997,7 @@ def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]: captured = capsys.readouterr() assert captured.out == "hello\n" """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capman: CaptureManager = request.config.pluginmanager.getplugin('capturemanager') capture_fixture = CaptureFixture(SysCapture, request, _ispytest=True) capman.set_fixture(capture_fixture) capture_fixture._start() @@ -1022,7 +1024,7 @@ def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, captured = capsysbinary.readouterr() assert captured.out == b"hello\n" """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capman: CaptureManager = request.config.pluginmanager.getplugin('capturemanager') capture_fixture = CaptureFixture(SysCaptureBinary, request, _ispytest=True) capman.set_fixture(capture_fixture) capture_fixture._start() @@ -1049,7 +1051,7 @@ def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]: captured = capfd.readouterr() assert captured.out == "hello\n" """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capman: CaptureManager = request.config.pluginmanager.getplugin('capturemanager') capture_fixture = CaptureFixture(FDCapture, request, _ispytest=True) capman.set_fixture(capture_fixture) capture_fixture._start() @@ -1077,7 +1079,7 @@ def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, N assert captured.out == b"hello\n" """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capman: CaptureManager = request.config.pluginmanager.getplugin('capturemanager') capture_fixture = CaptureFixture(FDCaptureBinary, request, _ispytest=True) capman.set_fixture(capture_fixture) capture_fixture._start() diff --git a/.venv/lib/python3.10/site-packages/_pytest/compat.py b/.venv/lib/python3.10/site-packages/_pytest/compat.py index 121b1f9..e68b1a8 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/compat.py +++ b/.venv/lib/python3.10/site-packages/_pytest/compat.py @@ -1,17 +1,16 @@ # mypy: allow-untyped-defs """Python version compatibility code.""" - from __future__ import annotations import dataclasses import enum import functools import inspect +import os +import sys from inspect import Parameter from inspect import signature -import os from pathlib import Path -import sys from typing import Any from typing import Callable from typing import Final @@ -57,7 +56,7 @@ def iscoroutinefunction(func: object) -> bool: importing asyncio directly, which in turns also initializes the "logging" module as a side-effect (see issue #8). """ - return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False) + return inspect.iscoroutinefunction(func) or getattr(func, '_is_coroutine', False) def is_async_function(func: object) -> bool: @@ -76,33 +75,33 @@ def getlocation(function, curdir: str | os.PathLike[str] | None = None) -> str: except ValueError: pass else: - return "%s:%d" % (relfn, lineno + 1) - return "%s:%d" % (fn, lineno + 1) + return '%s:%d' % (relfn, lineno + 1) + return '%s:%d' % (fn, lineno + 1) def num_mock_patch_args(function) -> int: """Return number of arguments used up by mock arguments (if any).""" - patchings = getattr(function, "patchings", None) + patchings = getattr(function, 'patchings', None) if not patchings: return 0 - mock_sentinel = getattr(sys.modules.get("mock"), "DEFAULT", object()) - ut_mock_sentinel = getattr(sys.modules.get("unittest.mock"), "DEFAULT", object()) + mock_sentinel = getattr(sys.modules.get('mock'), 'DEFAULT', object()) + ut_mock_sentinel = getattr(sys.modules.get('unittest.mock'), 'DEFAULT', object()) return len( [ p for p in patchings - if not p.attribute_name - and (p.new is mock_sentinel or p.new is ut_mock_sentinel) - ] + if not p.attribute_name and + (p.new is mock_sentinel or p.new is ut_mock_sentinel) + ], ) def getfuncargnames( function: Callable[..., object], *, - name: str = "", + name: str = '', is_method: bool = False, cls: type | None = None, ) -> tuple[str, ...]: @@ -135,7 +134,7 @@ def getfuncargnames( from _pytest.outcomes import fail fail( - f"Could not determine arguments of {function!r}: {e}", + f'Could not determine arguments of {function!r}: {e}', pytrace=False, ) @@ -143,10 +142,10 @@ def getfuncargnames( p.name for p in parameters.values() if ( - p.kind is Parameter.POSITIONAL_OR_KEYWORD - or p.kind is Parameter.KEYWORD_ONLY - ) - and p.default is Parameter.empty + p.kind is Parameter.POSITIONAL_OR_KEYWORD or + p.kind is Parameter.KEYWORD_ONLY + ) and + p.default is Parameter.empty ) if not name: name = function.__name__ @@ -157,15 +156,15 @@ def getfuncargnames( if is_method or ( # Not using `getattr` because we don't want to resolve the staticmethod. # Not using `cls.__dict__` because we want to check the entire MRO. - cls - and not isinstance( - inspect.getattr_static(cls, name, default=None), staticmethod + cls and + not isinstance( + inspect.getattr_static(cls, name, default=None), staticmethod, ) ): arg_names = arg_names[1:] # Remove any names that will be replaced with mocks. - if hasattr(function, "__wrapped__"): - arg_names = arg_names[num_mock_patch_args(function) :] + if hasattr(function, '__wrapped__'): + arg_names = arg_names[num_mock_patch_args(function):] return arg_names @@ -176,16 +175,16 @@ def get_default_arg_names(function: Callable[..., Any]) -> tuple[str, ...]: return tuple( p.name for p in signature(function).parameters.values() - if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) - and p.default is not Parameter.empty + if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) and + p.default is not Parameter.empty ) _non_printable_ascii_translate_table = { - i: f"\\x{i:02x}" for i in range(128) if i not in range(32, 127) + i: f'\\x{i:02x}' for i in range(128) if i not in range(32, 127) } _non_printable_ascii_translate_table.update( - {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"} + {ord('\t'): '\\t', ord('\r'): '\\r', ord('\n'): '\\n'}, ) @@ -206,9 +205,9 @@ def ascii_escaped(val: bytes | str) -> str: a UTF-8 string. """ if isinstance(val, bytes): - ret = val.decode("ascii", "backslashreplace") + ret = val.decode('ascii', 'backslashreplace') else: - ret = val.encode("unicode_escape").decode("ascii") + ret = val.encode('unicode_escape').decode('ascii') return ret.translate(_non_printable_ascii_translate_table) @@ -232,11 +231,11 @@ def get_real_func(obj): # __pytest_wrapped__ is set by @pytest.fixture when wrapping the fixture function # to trigger a warning if it gets called directly instead of by pytest: we don't # want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774) - new_obj = getattr(obj, "__pytest_wrapped__", None) + new_obj = getattr(obj, '__pytest_wrapped__', None) if isinstance(new_obj, _PytestWrapper): obj = new_obj.obj break - new_obj = getattr(obj, "__wrapped__", None) + new_obj = getattr(obj, '__wrapped__', None) if new_obj is None: break obj = new_obj @@ -244,7 +243,7 @@ def get_real_func(obj): from _pytest._io.saferepr import saferepr raise ValueError( - f"could not find real function of {saferepr(start_obj)}\nstopped at {saferepr(obj)}" + f'could not find real function of {saferepr(start_obj)}\nstopped at {saferepr(obj)}', ) if isinstance(obj, functools.partial): obj = obj.func @@ -256,11 +255,11 @@ def get_real_method(obj, holder): ``obj``, while at the same time returning a bound method to ``holder`` if the original object was a bound method.""" try: - is_method = hasattr(obj, "__func__") + is_method = hasattr(obj, '__func__') obj = get_real_func(obj) except Exception: # pragma: no cover return obj - if is_method and hasattr(obj, "__get__") and callable(obj.__get__): + if is_method and hasattr(obj, '__get__') and callable(obj.__get__): obj = obj.__get__(holder) return obj @@ -306,7 +305,7 @@ def get_user_id() -> int | None: # mypy follows the version and platform checking expectation of PEP 484: # https://mypy.readthedocs.io/en/stable/common_issues.html?highlight=platform#python-version-and-system-platform-checks # Containment checks are too complex for mypy v1.5.0 and cause failure. - if sys.platform == "win32" or sys.platform == "emscripten": # noqa: PLR1714 + if sys.platform == 'win32' or sys.platform == 'emscripten': # noqa: PLR1714 # win32 does not have a getuid() function. # Emscripten has a return 0 stub. return None @@ -350,4 +349,4 @@ def get_user_id() -> int | None: # # This also work for Enums (if you use `is` to compare) and Literals. def assert_never(value: NoReturn) -> NoReturn: - assert False, f"Unhandled value: {value} ({type(value).__name__})" + assert False, f'Unhandled value: {value} ({type(value).__name__})' diff --git a/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py b/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py index bf2cfc3..058b09d 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py +++ b/.venv/lib/python3.10/site-packages/_pytest/config/__init__.py @@ -1,21 +1,24 @@ # mypy: allow-untyped-defs """Command line options, ini-file and conftest.py processing.""" +from __future__ import annotations + import argparse import collections.abc import copy import dataclasses import enum -from functools import lru_cache import glob import importlib.metadata import inspect import os -from pathlib import Path import re import shlex import sys -from textwrap import dedent import types +import warnings +from functools import lru_cache +from pathlib import Path +from textwrap import dedent from types import FunctionType from typing import Any from typing import Callable @@ -36,25 +39,14 @@ from typing import Tuple from typing import Type from typing import TYPE_CHECKING from typing import Union -import warnings -import pluggy -from pluggy import HookimplMarker -from pluggy import HookimplOpts -from pluggy import HookspecMarker -from pluggy import HookspecOpts -from pluggy import PluginManager - -from .compat import PathAwareHookProxy -from .exceptions import PrintHelp as PrintHelp -from .exceptions import UsageError as UsageError -from .findpaths import determine_setup import _pytest._code +import _pytest.deprecated +import _pytest.hookspec +import pluggy from _pytest._code import ExceptionInfo from _pytest._code import filter_traceback from _pytest._io import TerminalWriter -import _pytest.deprecated -import _pytest.hookspec from _pytest.outcomes import fail from _pytest.outcomes import Skipped from _pytest.pathlib import absolutepath @@ -66,6 +58,16 @@ from _pytest.pathlib import safe_exists from _pytest.stash import Stash from _pytest.warning_types import PytestConfigWarning from _pytest.warning_types import warn_explicit_for +from pluggy import HookimplMarker +from pluggy import HookimplOpts +from pluggy import HookspecMarker +from pluggy import HookspecOpts +from pluggy import PluginManager + +from .compat import PathAwareHookProxy +from .exceptions import PrintHelp as PrintHelp +from .exceptions import UsageError as UsageError +from .findpaths import determine_setup if TYPE_CHECKING: @@ -85,8 +87,8 @@ Ideally this type would be provided by pluggy itself. """ -hookimpl = HookimplMarker("pytest") -hookspec = HookspecMarker("pytest") +hookimpl = HookimplMarker('pytest') +hookspec = HookspecMarker('pytest') @final @@ -123,7 +125,7 @@ class ConftestImportFailure(Exception): self.cause = cause def __str__(self) -> str: - return f"{type(self.cause).__name__}: {self.cause} (from {self.path})" + return f'{type(self.cause).__name__}: {self.cause} (from {self.path})' def filter_traceback_for_conftest_import_failure( @@ -134,13 +136,13 @@ def filter_traceback_for_conftest_import_failure( Make a special case for importlib because we use it to import test modules and conftest files in _pytest.pathlib.import_path. """ - return filter_traceback(entry) and "importlib" not in str(entry.path).split(os.sep) + return filter_traceback(entry) and 'importlib' not in str(entry.path).split(os.sep) def main( - args: Optional[Union[List[str], "os.PathLike[str]"]] = None, - plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, -) -> Union[int, ExitCode]: + args: list[str] | os.PathLike[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> int | ExitCode: """Perform an in-process test run. :param args: @@ -158,10 +160,10 @@ def main( tw = TerminalWriter(sys.stderr) tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) exc_info.traceback = exc_info.traceback.filter( - filter_traceback_for_conftest_import_failure + filter_traceback_for_conftest_import_failure, ) exc_repr = ( - exc_info.getrepr(style="short", chain=False) + exc_info.getrepr(style='short', chain=False) if exc_info.traceback else exc_info.exconly() ) @@ -171,8 +173,8 @@ def main( return ExitCode.USAGE_ERROR else: try: - ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main( - config=config + ret: ExitCode | int = config.hook.pytest_cmdline_main( + config=config, ) try: return ExitCode(ret) @@ -183,7 +185,7 @@ def main( except UsageError as e: tw = TerminalWriter(sys.stderr) for msg in e.args: - tw.line(f"ERROR: {msg}\n", red=True) + tw.line(f'ERROR: {msg}\n', red=True) return ExitCode.USAGE_ERROR @@ -216,7 +218,7 @@ def filename_arg(path: str, optname: str) -> str: :optname: Name of the option. """ if os.path.isdir(path): - raise UsageError(f"{optname} must be a filename, given: {path}") + raise UsageError(f'{optname} must be a filename, given: {path}') return path @@ -227,58 +229,58 @@ def directory_arg(path: str, optname: str) -> str: :optname: Name of the option. """ if not os.path.isdir(path): - raise UsageError(f"{optname} must be a directory, given: {path}") + raise UsageError(f'{optname} must be a directory, given: {path}') return path # Plugins that cannot be disabled via "-p no:X" currently. essential_plugins = ( - "mark", - "main", - "runner", - "fixtures", - "helpconfig", # Provides -p. + 'mark', + 'main', + 'runner', + 'fixtures', + 'helpconfig', # Provides -p. ) default_plugins = ( *essential_plugins, - "python", - "terminal", - "debugging", - "unittest", - "capture", - "skipping", - "legacypath", - "tmpdir", - "monkeypatch", - "recwarn", - "pastebin", - "assertion", - "junitxml", - "doctest", - "cacheprovider", - "freeze_support", - "setuponly", - "setupplan", - "stepwise", - "warnings", - "logging", - "reports", - "python_path", - "unraisableexception", - "threadexception", - "faulthandler", + 'python', + 'terminal', + 'debugging', + 'unittest', + 'capture', + 'skipping', + 'legacypath', + 'tmpdir', + 'monkeypatch', + 'recwarn', + 'pastebin', + 'assertion', + 'junitxml', + 'doctest', + 'cacheprovider', + 'freeze_support', + 'setuponly', + 'setupplan', + 'stepwise', + 'warnings', + 'logging', + 'reports', + 'python_path', + 'unraisableexception', + 'threadexception', + 'faulthandler', ) builtin_plugins = set(default_plugins) -builtin_plugins.add("pytester") -builtin_plugins.add("pytester_assertions") +builtin_plugins.add('pytester') +builtin_plugins.add('pytester_assertions') def get_config( - args: Optional[List[str]] = None, - plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, -) -> "Config": + args: list[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> Config: # subsequent calls to main will create a fresh instance pluginmanager = PytestPluginManager() config = Config( @@ -300,7 +302,7 @@ def get_config( return config -def get_plugin_manager() -> "PytestPluginManager": +def get_plugin_manager() -> PytestPluginManager: """Obtain a new instance of the :py:class:`pytest.PytestPluginManager`, with default plugins already loaded. @@ -312,16 +314,16 @@ def get_plugin_manager() -> "PytestPluginManager": def _prepareconfig( - args: Optional[Union[List[str], "os.PathLike[str]"]] = None, - plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, -) -> "Config": + args: list[str] | os.PathLike[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> Config: if args is None: args = sys.argv[1:] elif isinstance(args, os.PathLike): args = [os.fspath(args)] elif not isinstance(args, list): msg = ( # type:ignore[unreachable] - "`args` parameter expected to be a list of strings, got: {!r} (type: {})" + '`args` parameter expected to be a list of strings, got: {!r} (type: {})' ) raise TypeError(msg.format(args, type(args))) @@ -335,7 +337,7 @@ def _prepareconfig( else: pluginmanager.register(plugin) config = pluginmanager.hook.pytest_cmdline_parse( - pluginmanager=pluginmanager, args=args + pluginmanager=pluginmanager, args=args, ) return config except BaseException: @@ -354,26 +356,26 @@ def _get_directory(path: Path) -> Path: def _get_legacy_hook_marks( method: Any, hook_type: str, - opt_names: Tuple[str, ...], -) -> Dict[str, bool]: + opt_names: tuple[str, ...], +) -> dict[str, bool]: if TYPE_CHECKING: # abuse typeguard from importlib to avoid massive method type union thats lacking a alias assert inspect.isroutine(method) - known_marks: Set[str] = {m.name for m in getattr(method, "pytestmark", [])} - must_warn: List[str] = [] - opts: Dict[str, bool] = {} + known_marks: set[str] = {m.name for m in getattr(method, 'pytestmark', [])} + must_warn: list[str] = [] + opts: dict[str, bool] = {} for opt_name in opt_names: opt_attr = getattr(method, opt_name, AttributeError) if opt_attr is not AttributeError: - must_warn.append(f"{opt_name}={opt_attr}") + must_warn.append(f'{opt_name}={opt_attr}') opts[opt_name] = True elif opt_name in known_marks: - must_warn.append(f"{opt_name}=True") + must_warn.append(f'{opt_name}=True') opts[opt_name] = True else: opts[opt_name] = False if must_warn: - hook_opts = ", ".join(must_warn) + hook_opts = ', '.join(must_warn) message = _pytest.deprecated.HOOK_LEGACY_MARKING.format( type=hook_type, fullname=method.__qualname__, @@ -396,17 +398,17 @@ class PytestPluginManager(PluginManager): def __init__(self) -> None: import _pytest.assertion - super().__init__("pytest") + super().__init__('pytest') # -- State related to local conftest plugins. # All loaded conftest modules. - self._conftest_plugins: Set[types.ModuleType] = set() + self._conftest_plugins: set[types.ModuleType] = set() # All conftest modules applicable for a directory. # This includes the directory's own conftest modules as well # as those of its parent directories. - self._dirpath2confmods: Dict[Path, List[types.ModuleType]] = {} + self._dirpath2confmods: dict[Path, list[types.ModuleType]] = {} # Cutoff directory above which conftests are no longer discovered. - self._confcutdir: Optional[Path] = None + self._confcutdir: Path | None = None # If set, conftest loading is skipped. self._noconftest = False @@ -420,13 +422,13 @@ class PytestPluginManager(PluginManager): # previously we would issue a warning when a plugin was skipped, but # since we refactored warnings as first citizens of Config, they are # just stored here to be used later. - self.skipped_plugins: List[Tuple[str, str]] = [] + self.skipped_plugins: list[tuple[str, str]] = [] self.add_hookspecs(_pytest.hookspec) self.register(self) - if os.environ.get("PYTEST_DEBUG"): + if os.environ.get('PYTEST_DEBUG'): err: IO[str] = sys.stderr - encoding: str = getattr(err, "encoding", "utf8") + encoding: str = getattr(err, 'encoding', 'utf8') try: err = open( os.dup(err.fileno()), @@ -445,16 +447,16 @@ class PytestPluginManager(PluginManager): self._configured = False def parse_hookimpl_opts( - self, plugin: _PluggyPlugin, name: str - ) -> Optional[HookimplOpts]: + self, plugin: _PluggyPlugin, name: str, + ) -> HookimplOpts | None: """:meta private:""" # pytest hooks are always prefixed with "pytest_", # so we avoid accessing possibly non-readable attributes # (see issue #1073). - if not name.startswith("pytest_"): + if not name.startswith('pytest_'): return None # Ignore names which can not be hooks. - if name == "pytest_plugins": + if name == 'pytest_plugins': return None opts = super().parse_hookimpl_opts(plugin, name) @@ -467,33 +469,33 @@ class PytestPluginManager(PluginManager): return None # Collect unmarked hooks as long as they have the `pytest_' prefix. return _get_legacy_hook_marks( # type: ignore[return-value] - method, "impl", ("tryfirst", "trylast", "optionalhook", "hookwrapper") + method, 'impl', ('tryfirst', 'trylast', 'optionalhook', 'hookwrapper'), ) - def parse_hookspec_opts(self, module_or_class, name: str) -> Optional[HookspecOpts]: + def parse_hookspec_opts(self, module_or_class, name: str) -> HookspecOpts | None: """:meta private:""" opts = super().parse_hookspec_opts(module_or_class, name) if opts is None: method = getattr(module_or_class, name) - if name.startswith("pytest_"): + if name.startswith('pytest_'): opts = _get_legacy_hook_marks( # type: ignore[assignment] method, - "spec", - ("firstresult", "historic"), + 'spec', + ('firstresult', 'historic'), ) return opts def register( - self, plugin: _PluggyPlugin, name: Optional[str] = None - ) -> Optional[str]: + self, plugin: _PluggyPlugin, name: str | None = None, + ) -> str | None: if name in _pytest.deprecated.DEPRECATED_EXTERNAL_PLUGINS: warnings.warn( PytestConfigWarning( - "{} plugin has been merged into the core, " - "please remove it from your requirements.".format( - name.replace("_", "-") - ) - ) + '{} plugin has been merged into the core, ' + 'please remove it from your requirements.'.format( + name.replace('_', '-'), + ), + ), ) return None plugin_name = super().register(plugin, name) @@ -503,7 +505,7 @@ class PytestPluginManager(PluginManager): plugin=plugin, plugin_name=plugin_name, manager=self, - ) + ), ) if isinstance(plugin, types.ModuleType): @@ -512,28 +514,28 @@ class PytestPluginManager(PluginManager): def getplugin(self, name: str): # Support deprecated naming because plugins (xdist e.g.) use it. - plugin: Optional[_PluggyPlugin] = self.get_plugin(name) + plugin: _PluggyPlugin | None = self.get_plugin(name) return plugin def hasplugin(self, name: str) -> bool: """Return whether a plugin with the given name is registered.""" return bool(self.get_plugin(name)) - def pytest_configure(self, config: "Config") -> None: + def pytest_configure(self, config: Config) -> None: """:meta private:""" # XXX now that the pluginmanager exposes hookimpl(tryfirst...) # we should remove tryfirst/trylast as markers. config.addinivalue_line( - "markers", - "tryfirst: mark a hook implementation function such that the " - "plugin machinery will try to call it first/as early as possible. " - "DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.", + 'markers', + 'tryfirst: mark a hook implementation function such that the ' + 'plugin machinery will try to call it first/as early as possible. ' + 'DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.', ) config.addinivalue_line( - "markers", - "trylast: mark a hook implementation function such that the " - "plugin machinery will try to call it last/as late as possible. " - "DEPRECATED, use @pytest.hookimpl(trylast=True) instead.", + 'markers', + 'trylast: mark a hook implementation function such that the ' + 'plugin machinery will try to call it last/as late as possible. ' + 'DEPRECATED, use @pytest.hookimpl(trylast=True) instead.', ) self._configured = True @@ -542,13 +544,13 @@ class PytestPluginManager(PluginManager): # def _set_initial_conftests( self, - args: Sequence[Union[str, Path]], + args: Sequence[str | Path], pyargs: bool, noconftest: bool, rootpath: Path, - confcutdir: Optional[Path], + confcutdir: Path | None, invocation_dir: Path, - importmode: Union[ImportMode, str], + importmode: ImportMode | str, *, consider_namespace_packages: bool, ) -> None: @@ -568,7 +570,7 @@ class PytestPluginManager(PluginManager): for intitial_path in args: path = str(intitial_path) # remove node-id syntax - i = path.find("::") + i = path.find('::') if i != -1: path = path[:i] anchor = absolutepath(invocation_dir / path) @@ -609,7 +611,7 @@ class PytestPluginManager(PluginManager): def _try_load_conftest( self, anchor: Path, - importmode: Union[str, ImportMode], + importmode: str | ImportMode, rootpath: Path, *, consider_namespace_packages: bool, @@ -622,7 +624,7 @@ class PytestPluginManager(PluginManager): ) # let's also consider test* subdirs if anchor.is_dir(): - for x in anchor.glob("test*"): + for x in anchor.glob('test*'): if x.is_dir(): self._loadconftestmodules( x, @@ -634,7 +636,7 @@ class PytestPluginManager(PluginManager): def _loadconftestmodules( self, path: Path, - importmode: Union[str, ImportMode], + importmode: str | ImportMode, rootpath: Path, *, consider_namespace_packages: bool, @@ -652,7 +654,7 @@ class PytestPluginManager(PluginManager): clist = [] for parent in reversed((directory, *directory.parents)): if self._is_in_confcutdir(parent): - conftestpath = parent / "conftest.py" + conftestpath = parent / 'conftest.py' if conftestpath.is_file(): mod = self._importconftest( conftestpath, @@ -671,7 +673,7 @@ class PytestPluginManager(PluginManager): self, name: str, path: Path, - ) -> Tuple[types.ModuleType, Any]: + ) -> tuple[types.ModuleType, Any]: modules = self._getconftestmodules(path) for mod in reversed(modules): try: @@ -683,7 +685,7 @@ class PytestPluginManager(PluginManager): def _importconftest( self, conftestpath: Path, - importmode: Union[str, ImportMode], + importmode: str | ImportMode, rootpath: Path, *, consider_namespace_packages: bool, @@ -724,12 +726,12 @@ class PytestPluginManager(PluginManager): if dirpath in path.parents or path == dirpath: if mod in mods: raise AssertionError( - f"While trying to load conftest path {conftestpath!s}, " - f"found that the module {mod} is already loaded with path {mod.__file__}. " - "This is not supposed to happen. Please report this issue to pytest." + f'While trying to load conftest path {conftestpath!s}, ' + f'found that the module {mod} is already loaded with path {mod.__file__}. ' + 'This is not supposed to happen. Please report this issue to pytest.', ) mods.append(mod) - self.trace(f"loading conftestmodule {mod!r}") + self.trace(f'loading conftestmodule {mod!r}') self.consider_conftest(mod, registration_name=conftestpath_plugin_name) return mod @@ -739,18 +741,18 @@ class PytestPluginManager(PluginManager): conftestpath: Path, ) -> None: if ( - hasattr(mod, "pytest_plugins") - and self._configured - and not self._using_pyargs + hasattr(mod, 'pytest_plugins') and + self._configured and + not self._using_pyargs ): msg = ( "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" - "It affects the entire test suite instead of just below the conftest as expected.\n" - " {}\n" - "Please move it to a top level conftest file at the rootdir:\n" - " {}\n" - "For more information, visit:\n" - " https://docs.pytest.org/en/stable/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" + 'It affects the entire test suite instead of just below the conftest as expected.\n' + ' {}\n' + 'Please move it to a top level conftest file at the rootdir:\n' + ' {}\n' + 'For more information, visit:\n' + ' https://docs.pytest.org/en/stable/deprecations.html#pytest-plugins-in-non-top-level-conftest-files' ) fail(msg.format(conftestpath, self._confcutdir), pytrace=False) @@ -760,7 +762,7 @@ class PytestPluginManager(PluginManager): # def consider_preparse( - self, args: Sequence[str], *, exclude_only: bool = False + self, args: Sequence[str], *, exclude_only: bool = False, ) -> None: """:meta private:""" i = 0 @@ -769,60 +771,60 @@ class PytestPluginManager(PluginManager): opt = args[i] i += 1 if isinstance(opt, str): - if opt == "-p": + if opt == '-p': try: parg = args[i] except IndexError: return i += 1 - elif opt.startswith("-p"): + elif opt.startswith('-p'): parg = opt[2:] else: continue parg = parg.strip() - if exclude_only and not parg.startswith("no:"): + if exclude_only and not parg.startswith('no:'): continue self.consider_pluginarg(parg) def consider_pluginarg(self, arg: str) -> None: """:meta private:""" - if arg.startswith("no:"): + if arg.startswith('no:'): name = arg[3:] if name in essential_plugins: - raise UsageError("plugin %s cannot be disabled" % name) + raise UsageError('plugin %s cannot be disabled' % name) # PR #4304: remove stepwise if cacheprovider is blocked. - if name == "cacheprovider": - self.set_blocked("stepwise") - self.set_blocked("pytest_stepwise") + if name == 'cacheprovider': + self.set_blocked('stepwise') + self.set_blocked('pytest_stepwise') self.set_blocked(name) - if not name.startswith("pytest_"): - self.set_blocked("pytest_" + name) + if not name.startswith('pytest_'): + self.set_blocked('pytest_' + name) else: name = arg # Unblock the plugin. self.unblock(name) - if not name.startswith("pytest_"): - self.unblock("pytest_" + name) + if not name.startswith('pytest_'): + self.unblock('pytest_' + name) self.import_plugin(arg, consider_entry_points=True) def consider_conftest( - self, conftestmodule: types.ModuleType, registration_name: str + self, conftestmodule: types.ModuleType, registration_name: str, ) -> None: """:meta private:""" self.register(conftestmodule, name=registration_name) def consider_env(self) -> None: """:meta private:""" - self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) + self._import_plugin_specs(os.environ.get('PYTEST_PLUGINS')) def consider_module(self, mod: types.ModuleType) -> None: """:meta private:""" - self._import_plugin_specs(getattr(mod, "pytest_plugins", [])) + self._import_plugin_specs(getattr(mod, 'pytest_plugins', [])) def _import_plugin_specs( - self, spec: Union[None, types.ModuleType, str, Sequence[str]] + self, spec: None | types.ModuleType | str | Sequence[str], ) -> None: plugins = _get_plugin_specs_as_list(spec) for import_spec in plugins: @@ -839,16 +841,16 @@ class PytestPluginManager(PluginManager): # basename for historic purposes but must be imported with the # _pytest prefix. assert isinstance(modname, str), ( - "module name as text required, got %r" % modname + 'module name as text required, got %r' % modname ) if self.is_blocked(modname) or self.get_plugin(modname) is not None: return - importspec = "_pytest." + modname if modname in builtin_plugins else modname + importspec = '_pytest.' + modname if modname in builtin_plugins else modname self.rewrite_hook.mark_rewrite(importspec) if consider_entry_points: - loaded = self.load_setuptools_entrypoints("pytest11", name=modname) + loaded = self.load_setuptools_entrypoints('pytest11', name=modname) if loaded: return @@ -856,19 +858,19 @@ class PytestPluginManager(PluginManager): __import__(importspec) except ImportError as e: raise ImportError( - f'Error importing plugin "{modname}": {e.args[0]}' + f'Error importing plugin "{modname}": {e.args[0]}', ).with_traceback(e.__traceback__) from e except Skipped as e: - self.skipped_plugins.append((modname, e.msg or "")) + self.skipped_plugins.append((modname, e.msg or '')) else: mod = sys.modules[importspec] self.register(mod, modname) def _get_plugin_specs_as_list( - specs: Union[None, types.ModuleType, str, Sequence[str]], -) -> List[str]: + specs: None | types.ModuleType | str | Sequence[str], +) -> list[str]: """Parse a plugins specification into a list of plugin names.""" # None means empty. if specs is None: @@ -878,19 +880,19 @@ def _get_plugin_specs_as_list( return [] # Comma-separated list. if isinstance(specs, str): - return specs.split(",") if specs else [] + return specs.split(',') if specs else [] # Direct specification. if isinstance(specs, collections.abc.Sequence): return list(specs) raise UsageError( "Plugins may be specified as a sequence or a ','-separated string of plugin names. Got: %r" - % specs + % specs, ) class Notset: def __repr__(self): - return "" + return '' notset = Notset() @@ -931,13 +933,13 @@ def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]: package_files = list(package_files) seen_some = False for fn in package_files: - is_simple_module = "/" not in fn and fn.endswith(".py") - is_package = fn.count("/") == 1 and fn.endswith("__init__.py") + is_simple_module = '/' not in fn and fn.endswith('.py') + is_package = fn.count('/') == 1 and fn.endswith('__init__.py') if is_simple_module: module_name, _ = os.path.splitext(fn) # we ignore "setup.py" at the root of the distribution # as well as editable installation finder modules made by setuptools - if module_name != "setup" and not module_name.startswith("__editable__"): + if module_name != 'setup' and not module_name.startswith('__editable__'): seen_some = True yield module_name elif is_package: @@ -953,8 +955,8 @@ def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]: # are rarer. new_package_files = [] for fn in package_files: - parts = fn.split("/") - new_fn = "/".join(parts[1:]) + parts = fn.split('/') + new_fn = '/'.join(parts[1:]) if new_fn: new_package_files.append(new_fn) if new_package_files: @@ -990,9 +992,9 @@ class Config: Plugins accessing ``InvocationParams`` must be aware of that. """ - args: Tuple[str, ...] + args: tuple[str, ...] """The command-line arguments as passed to :func:`pytest.main`.""" - plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] + plugins: Sequence[str | _PluggyPlugin] | None """Extra plugins, might be `None`.""" dir: Path """The directory from which :func:`pytest.main` was invoked.""" @@ -1001,12 +1003,12 @@ class Config: self, *, args: Iterable[str], - plugins: Optional[Sequence[Union[str, _PluggyPlugin]]], + plugins: Sequence[str | _PluggyPlugin] | None, dir: Path, ) -> None: - object.__setattr__(self, "args", tuple(args)) - object.__setattr__(self, "plugins", plugins) - object.__setattr__(self, "dir", dir) + object.__setattr__(self, 'args', tuple(args)) + object.__setattr__(self, 'plugins', plugins) + object.__setattr__(self, 'dir', dir) class ArgsSource(enum.Enum): """Indicates the source of the test arguments. @@ -1026,14 +1028,14 @@ class Config: self, pluginmanager: PytestPluginManager, *, - invocation_params: Optional[InvocationParams] = None, + invocation_params: InvocationParams | None = None, ) -> None: from .argparsing import FILE_OR_DIR from .argparsing import Parser if invocation_params is None: invocation_params = self.InvocationParams( - args=(), plugins=None, dir=Path.cwd() + args=(), plugins=None, dir=Path.cwd(), ) self.option = argparse.Namespace() @@ -1050,7 +1052,7 @@ class Config: _a = FILE_OR_DIR self._parser = Parser( - usage=f"%(prog)s [options] [{_a}] [{_a}] [...]", + usage=f'%(prog)s [options] [{_a}] [{_a}] [...]', processopt=self._processopt, _ispytest=True, ) @@ -1069,24 +1071,24 @@ class Config: # Deprecated alias. Was never public. Can be removed in a few releases. self._store = self.stash - self.trace = self.pluginmanager.trace.root.get("config") + self.trace = self.pluginmanager.trace.root.get('config') self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment] - self._inicache: Dict[str, Any] = {} + self._inicache: dict[str, Any] = {} self._override_ini: Sequence[str] = () - self._opt2dest: Dict[str, str] = {} - self._cleanup: List[Callable[[], None]] = [] - self.pluginmanager.register(self, "pytestconfig") + self._opt2dest: dict[str, str] = {} + self._cleanup: list[Callable[[], None]] = [] + self.pluginmanager.register(self, 'pytestconfig') self._configured = False self.hook.pytest_addoption.call_historic( - kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager) + kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager), ) self.args_source = Config.ArgsSource.ARGS - self.args: List[str] = [] + self.args: list[str] = [] if TYPE_CHECKING: from _pytest.cacheprovider import Cache - self.cache: Optional[Cache] = None + self.cache: Cache | None = None @property def rootpath(self) -> Path: @@ -1099,7 +1101,7 @@ class Config: return self._rootpath @property - def inipath(self) -> Optional[Path]: + def inipath(self) -> Path | None: """The path to the :ref:`configfile `. :type: Optional[pathlib.Path] @@ -1117,7 +1119,7 @@ class Config: assert not self._configured self._configured = True with warnings.catch_warnings(): - warnings.simplefilter("default") + warnings.simplefilter('default') self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) def _ensure_unconfigure(self) -> None: @@ -1130,31 +1132,31 @@ class Config: fin() def get_terminal_writer(self) -> TerminalWriter: - terminalreporter: Optional[TerminalReporter] = self.pluginmanager.get_plugin( - "terminalreporter" + terminalreporter: TerminalReporter | None = self.pluginmanager.get_plugin( + 'terminalreporter', ) assert terminalreporter is not None return terminalreporter._tw def pytest_cmdline_parse( - self, pluginmanager: PytestPluginManager, args: List[str] - ) -> "Config": + self, pluginmanager: PytestPluginManager, args: list[str], + ) -> Config: try: self.parse(args) except UsageError: # Handle --version and --help here in a minimal fashion. # This gets done via helpconfig normally, but its # pytest_cmdline_main is not called in case of errors. - if getattr(self.option, "version", False) or "--version" in args: + if getattr(self.option, 'version', False) or '--version' in args: from _pytest.helpconfig import showversion showversion(self) elif ( - getattr(self.option, "help", False) or "--help" in args or "-h" in args + getattr(self.option, 'help', False) or '--help' in args or '-h' in args ): self._parser._getparser().print_help() sys.stdout.write( - "\nNOTE: displaying only minimal help due to UsageError.\n\n" + '\nNOTE: displaying only minimal help due to UsageError.\n\n', ) raise @@ -1164,19 +1166,19 @@ class Config: def notify_exception( self, excinfo: ExceptionInfo[BaseException], - option: Optional[argparse.Namespace] = None, + option: argparse.Namespace | None = None, ) -> None: - if option and getattr(option, "fulltrace", False): - style: _TracebackStyle = "long" + if option and getattr(option, 'fulltrace', False): + style: _TracebackStyle = 'long' else: - style = "native" + style = 'native' excrepr = excinfo.getrepr( - funcargs=True, showlocals=getattr(option, "showlocals", False), style=style + funcargs=True, showlocals=getattr(option, 'showlocals', False), style=style, ) res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) if not any(res): - for line in str(excrepr).split("\n"): - sys.stderr.write("INTERNALERROR> %s\n" % line) + for line in str(excrepr).split('\n'): + sys.stderr.write('INTERNALERROR> %s\n' % line) sys.stderr.flush() def cwd_relative_nodeid(self, nodeid: str) -> str: @@ -1187,7 +1189,7 @@ class Config: return nodeid @classmethod - def fromdictargs(cls, option_dict, args) -> "Config": + def fromdictargs(cls, option_dict, args) -> Config: """Constructor usable for subprocesses.""" config = get_config(args) config.option.__dict__.update(option_dict) @@ -1196,16 +1198,16 @@ class Config: config.pluginmanager.consider_pluginarg(x) return config - def _processopt(self, opt: "Argument") -> None: + def _processopt(self, opt: Argument) -> None: for name in opt._short_opts + opt._long_opts: self._opt2dest[name] = opt.dest - if hasattr(opt, "default"): + if hasattr(opt, 'default'): if not hasattr(self.option, opt.dest): setattr(self.option, opt.dest, opt.default) @hookimpl(trylast=True) - def pytest_load_initial_conftests(self, early_config: "Config") -> None: + def pytest_load_initial_conftests(self, early_config: Config) -> None: # We haven't fully parsed the command line arguments yet, so # early_config.args it not set yet. But we need it for # discovering the initial conftests. So "pre-run" the logic here. @@ -1213,7 +1215,7 @@ class Config: args, args_source = early_config._decide_args( args=early_config.known_args_namespace.file_or_dir, pyargs=early_config.known_args_namespace.pyargs, - testpaths=early_config.getini("testpaths"), + testpaths=early_config.getini('testpaths'), invocation_dir=early_config.invocation_params.dir, rootpath=early_config.rootpath, warn=False, @@ -1227,13 +1229,13 @@ class Config: invocation_dir=early_config.invocation_params.dir, importmode=early_config.known_args_namespace.importmode, consider_namespace_packages=early_config.getini( - "consider_namespace_packages" + 'consider_namespace_packages', ), ) def _initini(self, args: Sequence[str]) -> None: ns, unknown_args = self._parser.parse_known_and_unknown_args( - args, namespace=copy.copy(self.option) + args, namespace=copy.copy(self.option), ) rootpath, inipath, inicfg = determine_setup( inifile=ns.inifilename, @@ -1244,14 +1246,14 @@ class Config: self._rootpath = rootpath self._inipath = inipath self.inicfg = inicfg - self._parser.extra_info["rootdir"] = str(self.rootpath) - self._parser.extra_info["inifile"] = str(self.inipath) - self._parser.addini("addopts", "Extra command line options", "args") - self._parser.addini("minversion", "Minimally required pytest version") + self._parser.extra_info['rootdir'] = str(self.rootpath) + self._parser.extra_info['inifile'] = str(self.inipath) + self._parser.addini('addopts', 'Extra command line options', 'args') + self._parser.addini('minversion', 'Minimally required pytest version') self._parser.addini( - "required_plugins", - "Plugins that must be present for pytest to run", - type="args", + 'required_plugins', + 'Plugins that must be present for pytest to run', + type='args', default=[], ) self._override_ini = ns.override_ini or () @@ -1264,14 +1266,14 @@ class Config: by the importhook. """ ns, unknown_args = self._parser.parse_known_and_unknown_args(args) - mode = getattr(ns, "assertmode", "plain") - if mode == "rewrite": + mode = getattr(ns, 'assertmode', 'plain') + if mode == 'rewrite': import _pytest.assertion try: hook = _pytest.assertion.install_importhook(self) except SystemError: - mode = "plain" + mode = 'plain' else: self._mark_plugins_for_rewrite(hook) self._warn_about_missing_assertion(mode) @@ -1282,26 +1284,26 @@ class Config: all pytest plugins.""" self.pluginmanager.rewrite_hook = hook - if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): + if os.environ.get('PYTEST_DISABLE_PLUGIN_AUTOLOAD'): # We don't autoload from setuptools entry points, no need to continue. return package_files = ( str(file) for dist in importlib.metadata.distributions() - if any(ep.group == "pytest11" for ep in dist.entry_points) + if any(ep.group == 'pytest11' for ep in dist.entry_points) for file in dist.files or [] ) for name in _iter_rewritable_modules(package_files): hook.mark_rewrite(name) - def _validate_args(self, args: List[str], via: str) -> List[str]: + def _validate_args(self, args: list[str], via: str) -> list[str]: """Validate known args.""" self._parser._config_source_hint = via # type: ignore try: self._parser.parse_known_and_unknown_args( - args, namespace=copy.copy(self.option) + args, namespace=copy.copy(self.option), ) finally: del self._parser._config_source_hint # type: ignore @@ -1311,13 +1313,13 @@ class Config: def _decide_args( self, *, - args: List[str], + args: list[str], pyargs: bool, - testpaths: List[str], + testpaths: list[str], invocation_dir: Path, rootpath: Path, warn: bool, - ) -> Tuple[List[str], ArgsSource]: + ) -> tuple[list[str], ArgsSource]: """Decide the args (initial paths/nodeids) to use given the relevant inputs. :param warn: Whether can issue warnings. @@ -1339,12 +1341,12 @@ class Config: if testpaths and not result: if warn: warning_text = ( - "No files were found in testpaths; " - "consider removing or adjusting your testpaths configuration. " - "Searching recursively from the current directory instead." + 'No files were found in testpaths; ' + 'consider removing or adjusting your testpaths configuration. ' + 'Searching recursively from the current directory instead.' ) self.issue_config_time_warning( - PytestConfigWarning(warning_text), stacklevel=3 + PytestConfigWarning(warning_text), stacklevel=3, ) else: result = [] @@ -1353,34 +1355,34 @@ class Config: result = [str(invocation_dir)] return result, source - def _preparse(self, args: List[str], addopts: bool = True) -> None: + def _preparse(self, args: list[str], addopts: bool = True) -> None: if addopts: - env_addopts = os.environ.get("PYTEST_ADDOPTS", "") + env_addopts = os.environ.get('PYTEST_ADDOPTS', '') if len(env_addopts): args[:] = ( - self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") - + args + self._validate_args(shlex.split(env_addopts), 'via PYTEST_ADDOPTS') + + args ) self._initini(args) if addopts: args[:] = ( - self._validate_args(self.getini("addopts"), "via addopts config") + args + self._validate_args(self.getini('addopts'), 'via addopts config') + args ) self.known_args_namespace = self._parser.parse_known_args( - args, namespace=copy.copy(self.option) + args, namespace=copy.copy(self.option), ) self._checkversion() self._consider_importhook(args) self.pluginmanager.consider_preparse(args, exclude_only=False) - if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): + if not os.environ.get('PYTEST_DISABLE_PLUGIN_AUTOLOAD'): # Don't autoload from setuptools entry point. Only explicitly specified # plugins are going to be loaded. - self.pluginmanager.load_setuptools_entrypoints("pytest11") + self.pluginmanager.load_setuptools_entrypoints('pytest11') self.pluginmanager.consider_env() self.known_args_namespace = self._parser.parse_known_args( - args, namespace=copy.copy(self.known_args_namespace) + args, namespace=copy.copy(self.known_args_namespace), ) self._validate_plugins() @@ -1394,14 +1396,14 @@ class Config: self.known_args_namespace.confcutdir = confcutdir try: self.hook.pytest_load_initial_conftests( - early_config=self, args=args, parser=self._parser + early_config=self, args=args, parser=self._parser, ) except ConftestImportFailure as e: if self.known_args_namespace.help or self.known_args_namespace.version: # we don't want to prevent --help/--version to work # so just let is pass and print a warning at the end self.issue_config_time_warning( - PytestConfigWarning(f"could not load initial conftests: {e.path}"), + PytestConfigWarning(f'could not load initial conftests: {e.path}'), stacklevel=2, ) else: @@ -1419,27 +1421,27 @@ class Config: def _checkversion(self) -> None: import pytest - minver = self.inicfg.get("minversion", None) + minver = self.inicfg.get('minversion', None) if minver: # Imported lazily to improve start-up time. from packaging.version import Version if not isinstance(minver, str): raise pytest.UsageError( - "%s: 'minversion' must be a single value" % self.inipath + "%s: 'minversion' must be a single value" % self.inipath, ) if Version(minver) > Version(pytest.__version__): raise pytest.UsageError( - f"{self.inipath}: 'minversion' requires pytest-{minver}, actual pytest-{pytest.__version__}'" + f"{self.inipath}: 'minversion' requires pytest-{minver}, actual pytest-{pytest.__version__}'", ) def _validate_config_options(self) -> None: for key in sorted(self._get_unknown_ini_keys()): - self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + self._warn_or_fail_if_strict(f'Unknown config option: {key}\n') def _validate_plugins(self) -> None: - required_plugins = sorted(self.getini("required_plugins")) + required_plugins = sorted(self.getini('required_plugins')) if not required_plugins: return @@ -1462,13 +1464,13 @@ class Config: if req.name not in plugin_dist_info: missing_plugins.append(required_plugin) elif not req.specifier.contains( - Version(plugin_dist_info[req.name]), prereleases=True + Version(plugin_dist_info[req.name]), prereleases=True, ): missing_plugins.append(required_plugin) if missing_plugins: raise UsageError( - "Missing required plugins: {}".format(", ".join(missing_plugins)), + 'Missing required plugins: {}'.format(', '.join(missing_plugins)), ) def _warn_or_fail_if_strict(self, message: str) -> None: @@ -1477,28 +1479,28 @@ class Config: self.issue_config_time_warning(PytestConfigWarning(message), stacklevel=3) - def _get_unknown_ini_keys(self) -> List[str]: + def _get_unknown_ini_keys(self) -> list[str]: parser_inicfg = self._parser._inidict return [name for name in self.inicfg if name not in parser_inicfg] - def parse(self, args: List[str], addopts: bool = True) -> None: + def parse(self, args: list[str], addopts: bool = True) -> None: # Parse given cmdline arguments into this config object. assert ( self.args == [] - ), "can only parse cmdline args at most once per Config object" + ), 'can only parse cmdline args at most once per Config object' self.hook.pytest_addhooks.call_historic( - kwargs=dict(pluginmanager=self.pluginmanager) + kwargs=dict(pluginmanager=self.pluginmanager), ) self._preparse(args, addopts=addopts) self._parser.after_preparse = True # type: ignore try: args = self._parser.parse_setoption( - args, self.option, namespace=self.option + args, self.option, namespace=self.option, ) self.args, self.args_source = self._decide_args( args=args, pyargs=self.known_args_namespace.pyargs, - testpaths=self.getini("testpaths"), + testpaths=self.getini('testpaths'), invocation_dir=self.invocation_params.dir, rootpath=self.rootpath, warn=True, @@ -1518,14 +1520,14 @@ class Config: :param warning: The warning instance. :param stacklevel: stacklevel forwarded to warnings.warn. """ - if self.pluginmanager.is_blocked("warnings"): + if self.pluginmanager.is_blocked('warnings'): return cmdline_filters = self.known_args_namespace.pythonwarnings or [] - config_filters = self.getini("filterwarnings") + config_filters = self.getini('filterwarnings') with warnings.catch_warnings(record=True) as records: - warnings.simplefilter("always", type(warning)) + warnings.simplefilter('always', type(warning)) apply_warning_filters(config_filters, cmdline_filters) warnings.warn(warning, stacklevel=stacklevel) @@ -1535,10 +1537,10 @@ class Config: self.hook.pytest_warning_recorded.call_historic( kwargs=dict( warning_message=records[0], - when="config", - nodeid="", + when='config', + nodeid='', location=location, - ) + ), ) def addinivalue_line(self, name: str, line: str) -> None: @@ -1585,15 +1587,15 @@ class Config: # Meant for easy monkeypatching by legacypath plugin. # Can be inlined back (with no cover removed) once legacypath is gone. - def _getini_unknown_type(self, name: str, type: str, value: Union[str, List[str]]): - msg = f"unknown configuration type: {type}" + def _getini_unknown_type(self, name: str, type: str, value: str | list[str]): + msg = f'unknown configuration type: {type}' raise ValueError(msg, value) # pragma: no cover def _getini(self, name: str): try: description, type, default = self._parser._inidict[name] except KeyError as e: - raise ValueError(f"unknown configuration value: {name!r}") from e + raise ValueError(f'unknown configuration value: {name!r}') from e override_value = self._get_override_ini_value(name) if override_value is None: try: @@ -1617,7 +1619,7 @@ class Config: # a_line_list = ["tests", "acceptance"] # in this case, we already have a list ready to use. # - if type == "paths": + if type == 'paths': dp = ( self.inipath.parent if self.inipath is not None @@ -1625,50 +1627,50 @@ class Config: ) input_values = shlex.split(value) if isinstance(value, str) else value return [dp / x for x in input_values] - elif type == "args": + elif type == 'args': return shlex.split(value) if isinstance(value, str) else value - elif type == "linelist": + elif type == 'linelist': if isinstance(value, str): - return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] + return [t for t in map(lambda x: x.strip(), value.split('\n')) if t] else: return value - elif type == "bool": + elif type == 'bool': return _strtobool(str(value).strip()) - elif type == "string": + elif type == 'string': return value elif type is None: return value else: return self._getini_unknown_type(name, type, value) - def _getconftest_pathlist(self, name: str, path: Path) -> Optional[List[Path]]: + def _getconftest_pathlist(self, name: str, path: Path) -> list[Path] | None: try: mod, relroots = self.pluginmanager._rget_with_confmod(name, path) except KeyError: return None assert mod.__file__ is not None modpath = Path(mod.__file__).parent - values: List[Path] = [] + values: list[Path] = [] for relroot in relroots: if isinstance(relroot, os.PathLike): relroot = Path(relroot) else: - relroot = relroot.replace("/", os.sep) + relroot = relroot.replace('/', os.sep) relroot = absolutepath(modpath / relroot) values.append(relroot) return values - def _get_override_ini_value(self, name: str) -> Optional[str]: + def _get_override_ini_value(self, name: str) -> str | None: value = None # override_ini is a list of "ini=value" options. # Always use the last item if multiple values are set for same ini-name, # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2. for ini_config in self._override_ini: try: - key, user_ini_value = ini_config.split("=", 1) + key, user_ini_value = ini_config.split('=', 1) except ValueError as e: raise UsageError( - f"-o/--override-ini expects option=value style (got: {ini_config!r})." + f'-o/--override-ini expects option=value style (got: {ini_config!r}).', ) from e else: if key == name: @@ -1696,8 +1698,8 @@ class Config: if skip: import pytest - pytest.skip(f"no {name!r} option found") - raise ValueError(f"no option named {name!r}") from e + pytest.skip(f'no {name!r} option found') + raise ValueError(f'no option named {name!r}') from e def getvalue(self, name: str, path=None): """Deprecated, use getoption() instead.""" @@ -1708,12 +1710,12 @@ class Config: return self.getoption(name, skip=True) #: Verbosity type for failed assertions (see :confval:`verbosity_assertions`). - VERBOSITY_ASSERTIONS: Final = "assertions" + VERBOSITY_ASSERTIONS: Final = 'assertions' #: Verbosity type for test case execution (see :confval:`verbosity_test_cases`). - VERBOSITY_TEST_CASES: Final = "test_cases" - _VERBOSITY_INI_DEFAULT: Final = "auto" + VERBOSITY_TEST_CASES: Final = 'test_cases' + _VERBOSITY_INI_DEFAULT: Final = 'auto' - def get_verbosity(self, verbosity_type: Optional[str] = None) -> int: + def get_verbosity(self, verbosity_type: str | None = None) -> int: r"""Retrieve the verbosity level for a fine-grained verbosity type. :param verbosity_type: Verbosity type to get level for. If a level is @@ -1760,10 +1762,10 @@ class Config: @staticmethod def _verbosity_ini_name(verbosity_type: str) -> str: - return f"verbosity_{verbosity_type}" + return f'verbosity_{verbosity_type}' @staticmethod - def _add_verbosity_ini(parser: "Parser", verbosity_type: str, help: str) -> None: + def _add_verbosity_ini(parser: Parser, verbosity_type: str, help: str) -> None: """Add a output verbosity configuration option for the given output type. :param parser: Parser for command line arguments and ini-file values. @@ -1776,25 +1778,25 @@ class Config: parser.addini( Config._verbosity_ini_name(verbosity_type), help=help, - type="string", + type='string', default=Config._VERBOSITY_INI_DEFAULT, ) def _warn_about_missing_assertion(self, mode: str) -> None: if not _assertion_supported(): - if mode == "plain": + if mode == 'plain': warning_text = ( - "ASSERTIONS ARE NOT EXECUTED" - " and FAILING TESTS WILL PASS. Are you" - " using python -O?" + 'ASSERTIONS ARE NOT EXECUTED' + ' and FAILING TESTS WILL PASS. Are you' + ' using python -O?' ) else: warning_text = ( - "assertions not in test modules or" - " plugins will be ignored" - " because assert statements are not executed " - "by the underlying Python interpreter " - "(are you using python -O?)\n" + 'assertions not in test modules or' + ' plugins will be ignored' + ' because assert statements are not executed ' + 'by the underlying Python interpreter ' + '(are you using python -O?)\n' ) self.issue_config_time_warning( PytestConfigWarning(warning_text), @@ -1804,7 +1806,7 @@ class Config: def _warn_about_skipped_plugins(self) -> None: for module_name, msg in self.pluginmanager.skipped_plugins: self.issue_config_time_warning( - PytestConfigWarning(f"skipped plugin {module_name!r}: {msg}"), + PytestConfigWarning(f'skipped plugin {module_name!r}: {msg}'), stacklevel=2, ) @@ -1819,7 +1821,7 @@ def _assertion_supported() -> bool: def create_terminal_writer( - config: Config, file: Optional[TextIO] = None + config: Config, file: TextIO | None = None, ) -> TerminalWriter: """Create a TerminalWriter instance configured according to the options in the config object. @@ -1829,14 +1831,14 @@ def create_terminal_writer( """ tw = TerminalWriter(file=file) - if config.option.color == "yes": + if config.option.color == 'yes': tw.hasmarkup = True - elif config.option.color == "no": + elif config.option.color == 'no': tw.hasmarkup = False - if config.option.code_highlight == "yes": + if config.option.code_highlight == 'yes': tw.code_highlight = True - elif config.option.code_highlight == "no": + elif config.option.code_highlight == 'no': tw.code_highlight = False return tw @@ -1852,18 +1854,18 @@ def _strtobool(val: str) -> bool: .. note:: Copied from distutils.util. """ val = val.lower() - if val in ("y", "yes", "t", "true", "on", "1"): + if val in ('y', 'yes', 't', 'true', 'on', '1'): return True - elif val in ("n", "no", "f", "false", "off", "0"): + elif val in ('n', 'no', 'f', 'false', 'off', '0'): return False else: - raise ValueError(f"invalid truth value {val!r}") + raise ValueError(f'invalid truth value {val!r}') @lru_cache(maxsize=50) def parse_warning_filter( - arg: str, *, escape: bool -) -> Tuple["warnings._ActionKind", str, Type[Warning], str, int]: + arg: str, *, escape: bool, +) -> tuple[warnings._ActionKind, str, type[Warning], str, int]: """Parse a warnings filter string. This is copied from warnings._setoption with the following changes: @@ -1882,13 +1884,13 @@ def parse_warning_filter( This error occurred: {{error}} - """ + """, ) - parts = arg.split(":") + parts = arg.split(':') if len(parts) > 5: doc_url = ( - "https://docs.python.org/3/library/warnings.html#describing-warning-filters" + 'https://docs.python.org/3/library/warnings.html#describing-warning-filters' ) error = dedent( f"""\ @@ -1897,42 +1899,42 @@ def parse_warning_filter( action:message:category:module:line For more information please consult: {doc_url} - """ + """, ) raise UsageError(error_template.format(error=error)) while len(parts) < 5: - parts.append("") + parts.append('') action_, message, category_, module, lineno_ = (s.strip() for s in parts) try: - action: "warnings._ActionKind" = warnings._getaction(action_) # type: ignore[attr-defined] + action: warnings._ActionKind = warnings._getaction(action_) # type: ignore[attr-defined] except warnings._OptionError as e: raise UsageError(error_template.format(error=str(e))) from None try: - category: Type[Warning] = _resolve_warning_category(category_) + category: type[Warning] = _resolve_warning_category(category_) except Exception: exc_info = ExceptionInfo.from_current() - exception_text = exc_info.getrepr(style="native") + exception_text = exc_info.getrepr(style='native') raise UsageError(error_template.format(error=exception_text)) from None if message and escape: message = re.escape(message) if module and escape: - module = re.escape(module) + r"\Z" + module = re.escape(module) + r'\Z' if lineno_: try: lineno = int(lineno_) if lineno < 0: - raise ValueError("number is negative") + raise ValueError('number is negative') except ValueError as e: raise UsageError( - error_template.format(error=f"invalid lineno {lineno_!r}: {e}") + error_template.format(error=f'invalid lineno {lineno_!r}: {e}'), ) from None else: lineno = 0 return action, message, category, module, lineno -def _resolve_warning_category(category: str) -> Type[Warning]: +def _resolve_warning_category(category: str) -> type[Warning]: """ Copied from warnings._getcategory, but changed so it lets exceptions (specially ImportErrors) propagate so we can get access to their tracebacks (#9218). @@ -1941,21 +1943,21 @@ def _resolve_warning_category(category: str) -> Type[Warning]: if not category: return Warning - if "." not in category: + if '.' not in category: import builtins as m klass = category else: - module, _, klass = category.rpartition(".") + module, _, klass = category.rpartition('.') m = __import__(module, None, None, [klass]) cat = getattr(m, klass) if not issubclass(cat, Warning): - raise UsageError(f"{cat} is not a Warning subclass") + raise UsageError(f'{cat} is not a Warning subclass') return cast(Type[Warning], cat) def apply_warning_filters( - config_filters: Iterable[str], cmdline_filters: Iterable[str] + config_filters: Iterable[str], cmdline_filters: Iterable[str], ) -> None: """Applies pytest-configured filters to the warnings module""" # Filters should have this precedence: cmdline options, config. diff --git a/.venv/lib/python3.10/site-packages/_pytest/config/argparsing.py b/.venv/lib/python3.10/site-packages/_pytest/config/argparsing.py index d98f1ae..e83f209 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/config/argparsing.py +++ b/.venv/lib/python3.10/site-packages/_pytest/config/argparsing.py @@ -1,8 +1,10 @@ # mypy: allow-untyped-defs +from __future__ import annotations + import argparse -from gettext import gettext import os import sys +from gettext import gettext from typing import Any from typing import Callable from typing import cast @@ -22,12 +24,12 @@ from _pytest.config.exceptions import UsageError from _pytest.deprecated import check_ispytest -FILE_OR_DIR = "file_or_dir" +FILE_OR_DIR = 'file_or_dir' class NotSet: def __repr__(self) -> str: - return "" + return '' NOT_SET = NotSet() @@ -41,32 +43,32 @@ class Parser: there's an error processing the command line arguments. """ - prog: Optional[str] = None + prog: str | None = None def __init__( self, - usage: Optional[str] = None, - processopt: Optional[Callable[["Argument"], None]] = None, + usage: str | None = None, + processopt: Callable[[Argument], None] | None = None, *, _ispytest: bool = False, ) -> None: check_ispytest(_ispytest) - self._anonymous = OptionGroup("Custom options", parser=self, _ispytest=True) - self._groups: List[OptionGroup] = [] + self._anonymous = OptionGroup('Custom options', parser=self, _ispytest=True) + self._groups: list[OptionGroup] = [] self._processopt = processopt self._usage = usage - self._inidict: Dict[str, Tuple[str, Optional[str], Any]] = {} - self._ininames: List[str] = [] - self.extra_info: Dict[str, Any] = {} + self._inidict: dict[str, tuple[str, str | None, Any]] = {} + self._ininames: list[str] = [] + self.extra_info: dict[str, Any] = {} - def processoption(self, option: "Argument") -> None: + def processoption(self, option: Argument) -> None: if self._processopt: if option.dest: self._processopt(option) def getgroup( - self, name: str, description: str = "", after: Optional[str] = None - ) -> "OptionGroup": + self, name: str, description: str = '', after: str | None = None, + ) -> OptionGroup: """Get (or create) a named option Group. :param name: Name of the option group. @@ -108,8 +110,8 @@ class Parser: def parse( self, - args: Sequence[Union[str, "os.PathLike[str]"]], - namespace: Optional[argparse.Namespace] = None, + args: Sequence[str | os.PathLike[str]], + namespace: argparse.Namespace | None = None, ) -> argparse.Namespace: from _pytest._argcomplete import try_argcomplete @@ -118,7 +120,7 @@ class Parser: strargs = [os.fspath(x) for x in args] return self.optparser.parse_args(strargs, namespace=namespace) - def _getparser(self) -> "MyOptionParser": + def _getparser(self) -> MyOptionParser: from _pytest._argcomplete import filescompleter optparser = MyOptionParser(self, self.extra_info, prog=self.prog) @@ -131,7 +133,7 @@ class Parser: n = option.names() a = option.attrs() arggroup.add_argument(*n, **a) - file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*") + file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs='*') # bash like autocompletion for dirs (appending '/') # Type ignored because typeshed doesn't know about argcomplete. file_or_dir_arg.completer = filescompleter # type: ignore @@ -139,10 +141,10 @@ class Parser: def parse_setoption( self, - args: Sequence[Union[str, "os.PathLike[str]"]], + args: Sequence[str | os.PathLike[str]], option: argparse.Namespace, - namespace: Optional[argparse.Namespace] = None, - ) -> List[str]: + namespace: argparse.Namespace | None = None, + ) -> list[str]: parsedoption = self.parse(args, namespace=namespace) for name, value in parsedoption.__dict__.items(): setattr(option, name, value) @@ -150,8 +152,8 @@ class Parser: def parse_known_args( self, - args: Sequence[Union[str, "os.PathLike[str]"]], - namespace: Optional[argparse.Namespace] = None, + args: Sequence[str | os.PathLike[str]], + namespace: argparse.Namespace | None = None, ) -> argparse.Namespace: """Parse the known arguments at this point. @@ -161,9 +163,9 @@ class Parser: def parse_known_and_unknown_args( self, - args: Sequence[Union[str, "os.PathLike[str]"]], - namespace: Optional[argparse.Namespace] = None, - ) -> Tuple[argparse.Namespace, List[str]]: + args: Sequence[str | os.PathLike[str]], + namespace: argparse.Namespace | None = None, + ) -> tuple[argparse.Namespace, list[str]]: """Parse the known arguments at this point, and also return the remaining unknown arguments. @@ -179,9 +181,9 @@ class Parser: self, name: str, help: str, - type: Optional[ - Literal["string", "paths", "pathlist", "args", "linelist", "bool"] - ] = None, + type: None | ( + Literal['string', 'paths', 'pathlist', 'args', 'linelist', 'bool'] + ) = None, default: Any = NOT_SET, ) -> None: """Register an ini-file option. @@ -215,7 +217,7 @@ class Parser: The value of ini-variables can be retrieved via a call to :py:func:`config.getini(name) `. """ - assert type in (None, "string", "paths", "pathlist", "args", "linelist", "bool") + assert type in (None, 'string', 'paths', 'pathlist', 'args', 'linelist', 'bool') if default is NOT_SET: default = get_ini_default_for_type(type) @@ -224,33 +226,33 @@ class Parser: def get_ini_default_for_type( - type: Optional[Literal["string", "paths", "pathlist", "args", "linelist", "bool"]], + type: Literal['string', 'paths', 'pathlist', 'args', 'linelist', 'bool'] | None, ) -> Any: """ Used by addini to get the default value for a given ini-option type, when default is not supplied. """ if type is None: - return "" - elif type in ("paths", "pathlist", "args", "linelist"): + return '' + elif type in ('paths', 'pathlist', 'args', 'linelist'): return [] - elif type == "bool": + elif type == 'bool': return False else: - return "" + return '' class ArgumentError(Exception): """Raised if an Argument instance is created with invalid or inconsistent arguments.""" - def __init__(self, msg: str, option: Union["Argument", str]) -> None: + def __init__(self, msg: str, option: Argument | str) -> None: self.msg = msg self.option_id = str(option) def __str__(self) -> str: if self.option_id: - return f"option {self.option_id}: {self.msg}" + return f'option {self.option_id}: {self.msg}' else: return self.msg @@ -267,36 +269,36 @@ class Argument: def __init__(self, *names: str, **attrs: Any) -> None: """Store params in private vars for use in add_argument.""" self._attrs = attrs - self._short_opts: List[str] = [] - self._long_opts: List[str] = [] + self._short_opts: list[str] = [] + self._long_opts: list[str] = [] try: - self.type = attrs["type"] + self.type = attrs['type'] except KeyError: pass try: # Attribute existence is tested in Config._processopt. - self.default = attrs["default"] + self.default = attrs['default'] except KeyError: pass self._set_opt_strings(names) - dest: Optional[str] = attrs.get("dest") + dest: str | None = attrs.get('dest') if dest: self.dest = dest elif self._long_opts: - self.dest = self._long_opts[0][2:].replace("-", "_") + self.dest = self._long_opts[0][2:].replace('-', '_') else: try: self.dest = self._short_opts[0][1:] except IndexError as e: - self.dest = "???" # Needed for the error repr. - raise ArgumentError("need a long or short option", self) from e + self.dest = '???' # Needed for the error repr. + raise ArgumentError('need a long or short option', self) from e - def names(self) -> List[str]: + def names(self) -> list[str]: return self._short_opts + self._long_opts def attrs(self) -> Mapping[str, Any]: # Update any attributes set by processopt. - attrs = "default dest help".split() + attrs = 'default dest help'.split() attrs.append(self.dest) for attr in attrs: try: @@ -313,39 +315,39 @@ class Argument: for opt in opts: if len(opt) < 2: raise ArgumentError( - "invalid option string %r: " - "must be at least two characters long" % opt, + 'invalid option string %r: ' + 'must be at least two characters long' % opt, self, ) elif len(opt) == 2: - if not (opt[0] == "-" and opt[1] != "-"): + if not (opt[0] == '-' and opt[1] != '-'): raise ArgumentError( - "invalid short option string %r: " - "must be of the form -x, (x any non-dash char)" % opt, + 'invalid short option string %r: ' + 'must be of the form -x, (x any non-dash char)' % opt, self, ) self._short_opts.append(opt) else: - if not (opt[0:2] == "--" and opt[2] != "-"): + if not (opt[0:2] == '--' and opt[2] != '-'): raise ArgumentError( - "invalid long option string %r: " - "must start with --, followed by non-dash" % opt, + 'invalid long option string %r: ' + 'must start with --, followed by non-dash' % opt, self, ) self._long_opts.append(opt) def __repr__(self) -> str: - args: List[str] = [] + args: list[str] = [] if self._short_opts: - args += ["_short_opts: " + repr(self._short_opts)] + args += ['_short_opts: ' + repr(self._short_opts)] if self._long_opts: - args += ["_long_opts: " + repr(self._long_opts)] - args += ["dest: " + repr(self.dest)] - if hasattr(self, "type"): - args += ["type: " + repr(self.type)] - if hasattr(self, "default"): - args += ["default: " + repr(self.default)] - return "Argument({})".format(", ".join(args)) + args += ['_long_opts: ' + repr(self._long_opts)] + args += ['dest: ' + repr(self.dest)] + if hasattr(self, 'type'): + args += ['type: ' + repr(self.type)] + if hasattr(self, 'default'): + args += ['default: ' + repr(self.default)] + return 'Argument({})'.format(', '.join(args)) class OptionGroup: @@ -354,15 +356,15 @@ class OptionGroup: def __init__( self, name: str, - description: str = "", - parser: Optional[Parser] = None, + description: str = '', + parser: Parser | None = None, *, _ispytest: bool = False, ) -> None: check_ispytest(_ispytest) self.name = name self.description = description - self.options: List[Argument] = [] + self.options: list[Argument] = [] self.parser = parser def addoption(self, *opts: str, **attrs: Any) -> None: @@ -383,7 +385,7 @@ class OptionGroup: name for opt in self.options for name in opt.names() ) if conflict: - raise ValueError("option names %s already added" % conflict) + raise ValueError('option names %s already added' % conflict) option = Argument(*opts, **attrs) self._addoption_instance(option, shortupper=False) @@ -391,11 +393,11 @@ class OptionGroup: option = Argument(*opts, **attrs) self._addoption_instance(option, shortupper=True) - def _addoption_instance(self, option: "Argument", shortupper: bool = False) -> None: + def _addoption_instance(self, option: Argument, shortupper: bool = False) -> None: if not shortupper: for opt in option._short_opts: - if opt[0] == "-" and opt[1].islower(): - raise ValueError("lowercase shortoptions reserved") + if opt[0] == '-' and opt[1].islower(): + raise ValueError('lowercase shortoptions reserved') if self.parser: self.parser.processoption(option) self.options.append(option) @@ -405,8 +407,8 @@ class MyOptionParser(argparse.ArgumentParser): def __init__( self, parser: Parser, - extra_info: Optional[Dict[str, Any]] = None, - prog: Optional[str] = None, + extra_info: dict[str, Any] | None = None, + prog: str | None = None, ) -> None: self._parser = parser super().__init__( @@ -422,29 +424,29 @@ class MyOptionParser(argparse.ArgumentParser): def error(self, message: str) -> NoReturn: """Transform argparse error message into UsageError.""" - msg = f"{self.prog}: error: {message}" + msg = f'{self.prog}: error: {message}' - if hasattr(self._parser, "_config_source_hint"): + if hasattr(self._parser, '_config_source_hint'): # Type ignored because the attribute is set dynamically. - msg = f"{msg} ({self._parser._config_source_hint})" # type: ignore + msg = f'{msg} ({self._parser._config_source_hint})' # type: ignore raise UsageError(self.format_usage() + msg) # Type ignored because typeshed has a very complex type in the superclass. def parse_args( # type: ignore self, - args: Optional[Sequence[str]] = None, - namespace: Optional[argparse.Namespace] = None, + args: Sequence[str] | None = None, + namespace: argparse.Namespace | None = None, ) -> argparse.Namespace: """Allow splitting of positional arguments.""" parsed, unrecognized = self.parse_known_args(args, namespace) if unrecognized: for arg in unrecognized: - if arg and arg[0] == "-": - lines = ["unrecognized arguments: %s" % (" ".join(unrecognized))] + if arg and arg[0] == '-': + lines = ['unrecognized arguments: %s' % (' '.join(unrecognized))] for k, v in sorted(self.extra_info.items()): - lines.append(f" {k}: {v}") - self.error("\n".join(lines)) + lines.append(f' {k}: {v}') + self.error('\n'.join(lines)) getattr(parsed, FILE_OR_DIR).extend(unrecognized) return parsed @@ -452,8 +454,8 @@ class MyOptionParser(argparse.ArgumentParser): # Backport of https://github.com/python/cpython/pull/14316 so we can # disable long --argument abbreviations without breaking short flags. def _parse_optional( - self, arg_string: str - ) -> Optional[Tuple[Optional[argparse.Action], str, Optional[str]]]: + self, arg_string: str, + ) -> tuple[argparse.Action | None, str, str | None] | None: if not arg_string: return None if arg_string[0] not in self.prefix_chars: @@ -463,26 +465,26 @@ class MyOptionParser(argparse.ArgumentParser): return action, arg_string, None if len(arg_string) == 1: return None - if "=" in arg_string: - option_string, explicit_arg = arg_string.split("=", 1) + if '=' in arg_string: + option_string, explicit_arg = arg_string.split('=', 1) if option_string in self._option_string_actions: action = self._option_string_actions[option_string] return action, option_string, explicit_arg - if self.allow_abbrev or not arg_string.startswith("--"): + if self.allow_abbrev or not arg_string.startswith('--'): option_tuples = self._get_option_tuples(arg_string) if len(option_tuples) > 1: msg = gettext( - "ambiguous option: %(option)s could match %(matches)s" + 'ambiguous option: %(option)s could match %(matches)s', ) - options = ", ".join(option for _, option, _ in option_tuples) - self.error(msg % {"option": arg_string, "matches": options}) + options = ', '.join(option for _, option, _ in option_tuples) + self.error(msg % {'option': arg_string, 'matches': options}) elif len(option_tuples) == 1: (option_tuple,) = option_tuples return option_tuple if self._negative_number_matcher.match(arg_string): if not self._has_negative_number_optionals: return None - if " " in arg_string: + if ' ' in arg_string: return None return None, arg_string, None @@ -497,45 +499,45 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter): def __init__(self, *args: Any, **kwargs: Any) -> None: # Use more accurate terminal width. - if "width" not in kwargs: - kwargs["width"] = _pytest._io.get_terminal_width() + if 'width' not in kwargs: + kwargs['width'] = _pytest._io.get_terminal_width() super().__init__(*args, **kwargs) def _format_action_invocation(self, action: argparse.Action) -> str: orgstr = super()._format_action_invocation(action) - if orgstr and orgstr[0] != "-": # only optional arguments + if orgstr and orgstr[0] != '-': # only optional arguments return orgstr - res: Optional[str] = getattr(action, "_formatted_action_invocation", None) + res: str | None = getattr(action, '_formatted_action_invocation', None) if res: return res - options = orgstr.split(", ") + options = orgstr.split(', ') if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2): # a shortcut for '-h, --help' or '--abc', '-a' action._formatted_action_invocation = orgstr # type: ignore return orgstr return_list = [] - short_long: Dict[str, str] = {} + short_long: dict[str, str] = {} for option in options: - if len(option) == 2 or option[2] == " ": + if len(option) == 2 or option[2] == ' ': continue - if not option.startswith("--"): + if not option.startswith('--'): raise ArgumentError( - 'long optional argument without "--": [%s]' % (option), option + 'long optional argument without "--": [%s]' % (option), option, ) xxoption = option[2:] - shortened = xxoption.replace("-", "") + shortened = xxoption.replace('-', '') if shortened not in short_long or len(short_long[shortened]) < len( - xxoption + xxoption, ): short_long[shortened] = xxoption # now short_long has been filled out to the longest with dashes # **and** we keep the right option ordering from add_argument for option in options: - if len(option) == 2 or option[2] == " ": + if len(option) == 2 or option[2] == ' ': return_list.append(option) - if option[2:] == short_long.get(option.replace("-", "")): - return_list.append(option.replace(" ", "=", 1)) - formatted_action_invocation = ", ".join(return_list) + if option[2:] == short_long.get(option.replace('-', '')): + return_list.append(option.replace(' ', '=', 1)) + formatted_action_invocation = ', '.join(return_list) action._formatted_action_invocation = formatted_action_invocation # type: ignore return formatted_action_invocation diff --git a/.venv/lib/python3.10/site-packages/_pytest/config/compat.py b/.venv/lib/python3.10/site-packages/_pytest/config/compat.py index 2856d85..70f5692 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/config/compat.py +++ b/.venv/lib/python3.10/site-packages/_pytest/config/compat.py @@ -1,10 +1,10 @@ from __future__ import annotations import functools +import warnings from pathlib import Path from typing import Any from typing import Mapping -import warnings import pluggy @@ -15,19 +15,19 @@ from ..deprecated import HOOK_LEGACY_PATH_ARG # hookname: (Path, LEGACY_PATH) imply_paths_hooks: Mapping[str, tuple[str, str]] = { - "pytest_ignore_collect": ("collection_path", "path"), - "pytest_collect_file": ("file_path", "path"), - "pytest_pycollect_makemodule": ("module_path", "path"), - "pytest_report_header": ("start_path", "startdir"), - "pytest_report_collectionfinish": ("start_path", "startdir"), + 'pytest_ignore_collect': ('collection_path', 'path'), + 'pytest_collect_file': ('file_path', 'path'), + 'pytest_pycollect_makemodule': ('module_path', 'path'), + 'pytest_report_header': ('start_path', 'startdir'), + 'pytest_report_collectionfinish': ('start_path', 'startdir'), } def _check_path(path: Path, fspath: LEGACY_PATH) -> None: if Path(fspath) != path: raise ValueError( - f"Path({fspath!r}) != {path!r}\n" - "if both path and fspath are given they need to be equal" + f'Path({fspath!r}) != {path!r}\n' + 'if both path and fspath are given they need to be equal', ) @@ -61,7 +61,7 @@ class PathAwareHookProxy: if fspath_value is not None: warnings.warn( HOOK_LEGACY_PATH_ARG.format( - pylib_path_arg=fspath_var, pathlib_path_arg=path_var + pylib_path_arg=fspath_var, pathlib_path_arg=path_var, ), stacklevel=2, ) diff --git a/.venv/lib/python3.10/site-packages/_pytest/config/exceptions.py b/.venv/lib/python3.10/site-packages/_pytest/config/exceptions.py index 4031ea7..90108ec 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/config/exceptions.py +++ b/.venv/lib/python3.10/site-packages/_pytest/config/exceptions.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import final diff --git a/.venv/lib/python3.10/site-packages/_pytest/config/findpaths.py b/.venv/lib/python3.10/site-packages/_pytest/config/findpaths.py index 9909376..9a00b82 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/config/findpaths.py +++ b/.venv/lib/python3.10/site-packages/_pytest/config/findpaths.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import os -from pathlib import Path import sys +from pathlib import Path from typing import Dict from typing import Iterable from typing import List @@ -10,13 +12,13 @@ from typing import Tuple from typing import Union import iniconfig - -from .exceptions import UsageError from _pytest.outcomes import fail from _pytest.pathlib import absolutepath from _pytest.pathlib import commonpath from _pytest.pathlib import safe_exists +from .exceptions import UsageError + def _parse_ini_config(path: Path) -> iniconfig.IniConfig: """Parse the given generic '.ini' file using legacy IniConfig parser, returning @@ -32,52 +34,52 @@ def _parse_ini_config(path: Path) -> iniconfig.IniConfig: def load_config_dict_from_file( filepath: Path, -) -> Optional[Dict[str, Union[str, List[str]]]]: +) -> dict[str, str | list[str]] | None: """Load pytest configuration from the given file path, if supported. Return None if the file does not contain valid pytest configuration. """ # Configuration from ini files are obtained from the [pytest] section, if present. - if filepath.suffix == ".ini": + if filepath.suffix == '.ini': iniconfig = _parse_ini_config(filepath) - if "pytest" in iniconfig: - return dict(iniconfig["pytest"].items()) + if 'pytest' in iniconfig: + return dict(iniconfig['pytest'].items()) else: # "pytest.ini" files are always the source of configuration, even if empty. - if filepath.name == "pytest.ini": + if filepath.name == 'pytest.ini': return {} # '.cfg' files are considered if they contain a "[tool:pytest]" section. - elif filepath.suffix == ".cfg": + elif filepath.suffix == '.cfg': iniconfig = _parse_ini_config(filepath) - if "tool:pytest" in iniconfig.sections: - return dict(iniconfig["tool:pytest"].items()) - elif "pytest" in iniconfig.sections: + if 'tool:pytest' in iniconfig.sections: + return dict(iniconfig['tool:pytest'].items()) + elif 'pytest' in iniconfig.sections: # If a setup.cfg contains a "[pytest]" section, we raise a failure to indicate users that # plain "[pytest]" sections in setup.cfg files is no longer supported (#3086). - fail(CFG_PYTEST_SECTION.format(filename="setup.cfg"), pytrace=False) + fail(CFG_PYTEST_SECTION.format(filename='setup.cfg'), pytrace=False) # '.toml' files are considered if they contain a [tool.pytest.ini_options] table. - elif filepath.suffix == ".toml": + elif filepath.suffix == '.toml': if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib - toml_text = filepath.read_text(encoding="utf-8") + toml_text = filepath.read_text(encoding='utf-8') try: config = tomllib.loads(toml_text) except tomllib.TOMLDecodeError as exc: - raise UsageError(f"{filepath}: {exc}") from exc + raise UsageError(f'{filepath}: {exc}') from exc - result = config.get("tool", {}).get("pytest", {}).get("ini_options", None) + result = config.get('tool', {}).get('pytest', {}).get('ini_options', None) if result is not None: # TOML supports richer data types than ini files (strings, arrays, floats, ints, etc), # however we need to convert all scalar values to str for compatibility with the rest # of the configuration system, which expects strings only. - def make_scalar(v: object) -> Union[str, List[str]]: + def make_scalar(v: object) -> str | list[str]: return v if isinstance(v, list) else str(v) return {k: make_scalar(v) for k, v in result.items()} @@ -88,27 +90,27 @@ def load_config_dict_from_file( def locate_config( invocation_dir: Path, args: Iterable[Path], -) -> Tuple[Optional[Path], Optional[Path], Dict[str, Union[str, List[str]]]]: +) -> tuple[Path | None, Path | None, dict[str, str | list[str]]]: """Search in the list of arguments for a valid ini-file for pytest, and return a tuple of (rootdir, inifile, cfg-dict).""" config_names = [ - "pytest.ini", - ".pytest.ini", - "pyproject.toml", - "tox.ini", - "setup.cfg", + 'pytest.ini', + '.pytest.ini', + 'pyproject.toml', + 'tox.ini', + 'setup.cfg', ] - args = [x for x in args if not str(x).startswith("-")] + args = [x for x in args if not str(x).startswith('-')] if not args: args = [invocation_dir] - found_pyproject_toml: Optional[Path] = None + found_pyproject_toml: Path | None = None for arg in args: argpath = absolutepath(arg) for base in (argpath, *argpath.parents): for config_name in config_names: p = base / config_name if p.is_file(): - if p.name == "pyproject.toml" and found_pyproject_toml is None: + if p.name == 'pyproject.toml' and found_pyproject_toml is None: found_pyproject_toml = p ini_config = load_config_dict_from_file(p) if ini_config is not None: @@ -122,7 +124,7 @@ def get_common_ancestor( invocation_dir: Path, paths: Iterable[Path], ) -> Path: - common_ancestor: Optional[Path] = None + common_ancestor: Path | None = None for path in paths: if not path.exists(): continue @@ -144,12 +146,12 @@ def get_common_ancestor( return common_ancestor -def get_dirs_from_args(args: Iterable[str]) -> List[Path]: +def get_dirs_from_args(args: Iterable[str]) -> list[Path]: def is_option(x: str) -> bool: - return x.startswith("-") + return x.startswith('-') def get_file_part_from_node_id(x: str) -> str: - return x.split("::")[0] + return x.split('::')[0] def get_dir_from_path(path: Path) -> Path: if path.is_dir(): @@ -166,16 +168,16 @@ def get_dirs_from_args(args: Iterable[str]) -> List[Path]: return [get_dir_from_path(path) for path in possible_paths if safe_exists(path)] -CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." +CFG_PYTEST_SECTION = '[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead.' def determine_setup( *, - inifile: Optional[str], + inifile: str | None, args: Sequence[str], - rootdir_cmd_arg: Optional[str], + rootdir_cmd_arg: str | None, invocation_dir: Path, -) -> Tuple[Path, Optional[Path], Dict[str, Union[str, List[str]]]]: +) -> tuple[Path, Path | None, dict[str, str | list[str]]]: """Determine the rootdir, inifile and ini configuration values from the command line arguments. @@ -192,7 +194,7 @@ def determine_setup( dirs = get_dirs_from_args(args) if inifile: inipath_ = absolutepath(inifile) - inipath: Optional[Path] = inipath_ + inipath: Path | None = inipath_ inicfg = load_config_dict_from_file(inipath_) or {} if rootdir_cmd_arg is None: rootdir = inipath_.parent @@ -201,7 +203,7 @@ def determine_setup( rootdir, inipath, inicfg = locate_config(invocation_dir, [ancestor]) if rootdir is None and rootdir_cmd_arg is None: for possible_rootdir in (ancestor, *ancestor.parents): - if (possible_rootdir / "setup.py").is_file(): + if (possible_rootdir / 'setup.py').is_file(): rootdir = possible_rootdir break else: @@ -209,7 +211,7 @@ def determine_setup( rootdir, inipath, inicfg = locate_config(invocation_dir, dirs) if rootdir is None: rootdir = get_common_ancestor( - invocation_dir, [invocation_dir, ancestor] + invocation_dir, [invocation_dir, ancestor], ) if is_fs_root(rootdir): rootdir = ancestor @@ -217,7 +219,7 @@ def determine_setup( rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg)) if not rootdir.is_dir(): raise UsageError( - f"Directory '{rootdir}' not found. Check your '--rootdir' option." + f"Directory '{rootdir}' not found. Check your '--rootdir' option.", ) assert rootdir is not None return rootdir, inipath, inicfg or {} diff --git a/.venv/lib/python3.10/site-packages/_pytest/debugging.py b/.venv/lib/python3.10/site-packages/_pytest/debugging.py index cb157cd..7ac0a12 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/debugging.py +++ b/.venv/lib/python3.10/site-packages/_pytest/debugging.py @@ -1,9 +1,12 @@ # mypy: allow-untyped-defs """Interactive debugging with PDB, the Python Debugger.""" +from __future__ import annotations + import argparse import functools import sys import types +import unittest from typing import Any from typing import Callable from typing import Generator @@ -13,7 +16,6 @@ from typing import Tuple from typing import Type from typing import TYPE_CHECKING from typing import Union -import unittest from _pytest import outcomes from _pytest._code import ExceptionInfo @@ -32,51 +34,51 @@ if TYPE_CHECKING: from _pytest.runner import CallInfo -def _validate_usepdb_cls(value: str) -> Tuple[str, str]: +def _validate_usepdb_cls(value: str) -> tuple[str, str]: """Validate syntax of --pdbcls option.""" try: - modname, classname = value.split(":") + modname, classname = value.split(':') except ValueError as e: raise argparse.ArgumentTypeError( - f"{value!r} is not in the format 'modname:classname'" + f"{value!r} is not in the format 'modname:classname'", ) from e return (modname, classname) def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") + group = parser.getgroup('general') group._addoption( - "--pdb", - dest="usepdb", - action="store_true", - help="Start the interactive Python debugger on errors or KeyboardInterrupt", + '--pdb', + dest='usepdb', + action='store_true', + help='Start the interactive Python debugger on errors or KeyboardInterrupt', ) group._addoption( - "--pdbcls", - dest="usepdb_cls", - metavar="modulename:classname", + '--pdbcls', + dest='usepdb_cls', + metavar='modulename:classname', type=_validate_usepdb_cls, - help="Specify a custom interactive Python debugger for use with --pdb." - "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", + help='Specify a custom interactive Python debugger for use with --pdb.' + 'For example: --pdbcls=IPython.terminal.debugger:TerminalPdb', ) group._addoption( - "--trace", - dest="trace", - action="store_true", - help="Immediately break when running each test", + '--trace', + dest='trace', + action='store_true', + help='Immediately break when running each test', ) def pytest_configure(config: Config) -> None: import pdb - if config.getvalue("trace"): - config.pluginmanager.register(PdbTrace(), "pdbtrace") - if config.getvalue("usepdb"): - config.pluginmanager.register(PdbInvoke(), "pdbinvoke") + if config.getvalue('trace'): + config.pluginmanager.register(PdbTrace(), 'pdbtrace') + if config.getvalue('usepdb'): + config.pluginmanager.register(PdbInvoke(), 'pdbinvoke') pytestPDB._saved.append( - (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config) + (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config), ) pdb.set_trace = pytestPDB.set_trace pytestPDB._pluginmanager = config.pluginmanager @@ -97,29 +99,29 @@ def pytest_configure(config: Config) -> None: class pytestPDB: """Pseudo PDB that defers to the real pdb.""" - _pluginmanager: Optional[PytestPluginManager] = None - _config: Optional[Config] = None - _saved: List[ - Tuple[Callable[..., None], Optional[PytestPluginManager], Optional[Config]] + _pluginmanager: PytestPluginManager | None = None + _config: Config | None = None + _saved: list[ + tuple[Callable[..., None], PytestPluginManager | None, Config | None] ] = [] _recursive_debug = 0 - _wrapped_pdb_cls: Optional[Tuple[Type[Any], Type[Any]]] = None + _wrapped_pdb_cls: tuple[type[Any], type[Any]] | None = None @classmethod - def _is_capturing(cls, capman: Optional["CaptureManager"]) -> Union[str, bool]: + def _is_capturing(cls, capman: CaptureManager | None) -> str | bool: if capman: return capman.is_capturing() return False @classmethod - def _import_pdb_cls(cls, capman: Optional["CaptureManager"]): + def _import_pdb_cls(cls, capman: CaptureManager | None): if not cls._config: import pdb # Happens when using pytest.set_trace outside of a test. return pdb.Pdb - usepdb_cls = cls._config.getvalue("usepdb_cls") + usepdb_cls = cls._config.getvalue('usepdb_cls') if cls._wrapped_pdb_cls and cls._wrapped_pdb_cls[0] == usepdb_cls: return cls._wrapped_pdb_cls[1] @@ -132,14 +134,14 @@ class pytestPDB: mod = sys.modules[modname] # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp). - parts = classname.split(".") + parts = classname.split('.') pdb_cls = getattr(mod, parts[0]) for part in parts[1:]: pdb_cls = getattr(pdb_cls, part) except Exception as exc: - value = ":".join((modname, classname)) + value = ':'.join((modname, classname)) raise UsageError( - f"--pdbcls: could not import {value!r}: {exc}" + f'--pdbcls: could not import {value!r}: {exc}', ) from exc else: import pdb @@ -151,7 +153,7 @@ class pytestPDB: return wrapped_cls @classmethod - def _get_pdb_wrapper_class(cls, pdb_cls, capman: Optional["CaptureManager"]): + def _get_pdb_wrapper_class(cls, pdb_cls, capman: CaptureManager | None): import _pytest.config # Type ignored because mypy doesn't support "dynamic" @@ -176,18 +178,18 @@ class pytestPDB: capman = self._pytest_capman capturing = pytestPDB._is_capturing(capman) if capturing: - if capturing == "global": - tw.sep(">", "PDB continue (IO-capturing resumed)") + if capturing == 'global': + tw.sep('>', 'PDB continue (IO-capturing resumed)') else: tw.sep( - ">", - "PDB continue (IO-capturing resumed for %s)" + '>', + 'PDB continue (IO-capturing resumed for %s)' % capturing, ) assert capman is not None capman.resume() else: - tw.sep(">", "PDB continue") + tw.sep('>', 'PDB continue') assert cls._pluginmanager is not None cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self) self._continued = True @@ -205,7 +207,7 @@ class pytestPDB: ret = super().do_quit(arg) if cls._recursive_debug == 0: - outcomes.exit("Quitting debugger") + outcomes.exit('Quitting debugger') return ret @@ -231,7 +233,7 @@ class pytestPDB: if f is None: # Find last non-hidden frame. i = max(0, len(stack) - 1) - while i and stack[i][0].f_locals.get("__tracebackhide__", False): + while i and stack[i][0].f_locals.get('__tracebackhide__', False): i -= 1 return stack, i @@ -243,9 +245,9 @@ class pytestPDB: import _pytest.config if cls._pluginmanager is None: - capman: Optional[CaptureManager] = None + capman: CaptureManager | None = None else: - capman = cls._pluginmanager.getplugin("capturemanager") + capman = cls._pluginmanager.getplugin('capturemanager') if capman: capman.suspend(in_=True) @@ -255,20 +257,20 @@ class pytestPDB: if cls._recursive_debug == 0: # Handle header similar to pdb.set_trace in py37+. - header = kwargs.pop("header", None) + header = kwargs.pop('header', None) if header is not None: - tw.sep(">", header) + tw.sep('>', header) else: capturing = cls._is_capturing(capman) - if capturing == "global": - tw.sep(">", f"PDB {method} (IO-capturing turned off)") + if capturing == 'global': + tw.sep('>', f'PDB {method} (IO-capturing turned off)') elif capturing: tw.sep( - ">", - f"PDB {method} (IO-capturing turned off for {capturing})", + '>', + f'PDB {method} (IO-capturing turned off for {capturing})', ) else: - tw.sep(">", f"PDB {method}") + tw.sep('>', f'PDB {method}') _pdb = cls._import_pdb_cls(capman)(**kwargs) @@ -280,15 +282,15 @@ class pytestPDB: def set_trace(cls, *args, **kwargs) -> None: """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" frame = sys._getframe().f_back - _pdb = cls._init_pdb("set_trace", *args, **kwargs) + _pdb = cls._init_pdb('set_trace', *args, **kwargs) _pdb.set_trace(frame) class PdbInvoke: def pytest_exception_interact( - self, node: Node, call: "CallInfo[Any]", report: BaseReport + self, node: Node, call: CallInfo[Any], report: BaseReport, ) -> None: - capman = node.config.pluginmanager.getplugin("capturemanager") + capman = node.config.pluginmanager.getplugin('capturemanager') if capman: capman.suspend_global_capture(in_=True) out, err = capman.read_global_capture() @@ -316,7 +318,7 @@ def wrap_pytest_function_for_tracing(pyfuncitem): wrapper which actually enters pdb before calling the python function itself, effectively leaving the user in the pdb prompt in the first statement of the function.""" - _pdb = pytestPDB._init_pdb("runcall") + _pdb = pytestPDB._init_pdb('runcall') testfunction = pyfuncitem.obj # we can't just return `partial(pdb.runcall, testfunction)` because (on @@ -333,35 +335,35 @@ def wrap_pytest_function_for_tracing(pyfuncitem): def maybe_wrap_pytest_function_for_tracing(pyfuncitem): """Wrap the given pytestfunct item for tracing support if --trace was given in the command line.""" - if pyfuncitem.config.getvalue("trace"): + if pyfuncitem.config.getvalue('trace'): wrap_pytest_function_for_tracing(pyfuncitem) def _enter_pdb( - node: Node, excinfo: ExceptionInfo[BaseException], rep: BaseReport + node: Node, excinfo: ExceptionInfo[BaseException], rep: BaseReport, ) -> BaseReport: # XXX we re-use the TerminalReporter's terminalwriter # because this seems to avoid some encoding related troubles # for not completely clear reasons. - tw = node.config.pluginmanager.getplugin("terminalreporter")._tw + tw = node.config.pluginmanager.getplugin('terminalreporter')._tw tw.line() showcapture = node.config.option.showcapture for sectionname, content in ( - ("stdout", rep.capstdout), - ("stderr", rep.capstderr), - ("log", rep.caplog), + ('stdout', rep.capstdout), + ('stderr', rep.capstderr), + ('log', rep.caplog), ): - if showcapture in (sectionname, "all") and content: - tw.sep(">", "captured " + sectionname) - if content[-1:] == "\n": + if showcapture in (sectionname, 'all') and content: + tw.sep('>', 'captured ' + sectionname) + if content[-1:] == '\n': content = content[:-1] tw.line(content) - tw.sep(">", "traceback") + tw.sep('>', 'traceback') rep.toterminal(tw) - tw.sep(">", "entering PDB") + tw.sep('>', 'entering PDB') tb = _postmortem_traceback(excinfo) rep._pdbshown = True # type: ignore[attr-defined] post_mortem(tb) @@ -386,8 +388,8 @@ def _postmortem_traceback(excinfo: ExceptionInfo[BaseException]) -> types.Traceb def post_mortem(t: types.TracebackType) -> None: - p = pytestPDB._init_pdb("post_mortem") + p = pytestPDB._init_pdb('post_mortem') p.reset() p.interaction(None, t) if p.quitting: - outcomes.exit("Quitting debugger") + outcomes.exit('Quitting debugger') diff --git a/.venv/lib/python3.10/site-packages/_pytest/deprecated.py b/.venv/lib/python3.10/site-packages/_pytest/deprecated.py index 10811d1..77c0d1d 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/deprecated.py +++ b/.venv/lib/python3.10/site-packages/_pytest/deprecated.py @@ -8,6 +8,7 @@ All constants defined in this module should be either instances of :class:`PytestWarning`, or :class:`UnformattedWarning` in case of warnings which need to format their messages. """ +from __future__ import annotations from warnings import warn @@ -19,50 +20,50 @@ from _pytest.warning_types import UnformattedWarning # set of plugins which have been integrated into the core; we use this list to ignore # them during registration to avoid conflicts DEPRECATED_EXTERNAL_PLUGINS = { - "pytest_catchlog", - "pytest_capturelog", - "pytest_faulthandler", + 'pytest_catchlog', + 'pytest_capturelog', + 'pytest_faulthandler', } # This can be* removed pytest 8, but it's harmless and common, so no rush to remove. # * If you're in the future: "could have been". YIELD_FIXTURE = PytestDeprecationWarning( - "@pytest.yield_fixture is deprecated.\n" - "Use @pytest.fixture instead; they are the same." + '@pytest.yield_fixture is deprecated.\n' + 'Use @pytest.fixture instead; they are the same.', ) # This deprecation is never really meant to be removed. -PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.") +PRIVATE = PytestDeprecationWarning('A private pytest class or function was used.') HOOK_LEGACY_PATH_ARG = UnformattedWarning( PytestRemovedIn9Warning, - "The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n" - "see https://docs.pytest.org/en/latest/deprecations.html" - "#py-path-local-arguments-for-hooks-replaced-with-pathlib-path", + 'The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n' + 'see https://docs.pytest.org/en/latest/deprecations.html' + '#py-path-local-arguments-for-hooks-replaced-with-pathlib-path', ) NODE_CTOR_FSPATH_ARG = UnformattedWarning( PytestRemovedIn9Warning, - "The (fspath: py.path.local) argument to {node_type_name} is deprecated. " - "Please use the (path: pathlib.Path) argument instead.\n" - "See https://docs.pytest.org/en/latest/deprecations.html" - "#fspath-argument-for-node-constructors-replaced-with-pathlib-path", + 'The (fspath: py.path.local) argument to {node_type_name} is deprecated. ' + 'Please use the (path: pathlib.Path) argument instead.\n' + 'See https://docs.pytest.org/en/latest/deprecations.html' + '#fspath-argument-for-node-constructors-replaced-with-pathlib-path', ) HOOK_LEGACY_MARKING = UnformattedWarning( PytestDeprecationWarning, - "The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n" - "Please use the pytest.hook{type}({hook_opts}) decorator instead\n" - " to configure the hooks.\n" - " See https://docs.pytest.org/en/latest/deprecations.html" - "#configuring-hook-specs-impls-using-markers", + 'The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n' + 'Please use the pytest.hook{type}({hook_opts}) decorator instead\n' + ' to configure the hooks.\n' + ' See https://docs.pytest.org/en/latest/deprecations.html' + '#configuring-hook-specs-impls-using-markers', ) MARKED_FIXTURE = PytestRemovedIn9Warning( - "Marks applied to fixtures have no effect\n" - "See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function" + 'Marks applied to fixtures have no effect\n' + 'See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function', ) # You want to make some `__init__` or function "private". diff --git a/.venv/lib/python3.10/site-packages/_pytest/doctest.py b/.venv/lib/python3.10/site-packages/_pytest/doctest.py index ced3b82..c2b0b06 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/doctest.py +++ b/.venv/lib/python3.10/site-packages/_pytest/doctest.py @@ -1,15 +1,18 @@ # mypy: allow-untyped-defs """Discover and run doctests in modules and test files.""" +from __future__ import annotations + import bdb -from contextlib import contextmanager import functools import inspect import os -from pathlib import Path import platform import sys import traceback import types +import warnings +from contextlib import contextmanager +from pathlib import Path from typing import Any from typing import Callable from typing import Dict @@ -23,7 +26,6 @@ from typing import Tuple from typing import Type from typing import TYPE_CHECKING from typing import Union -import warnings from _pytest import outcomes from _pytest._code.code import ExceptionInfo @@ -49,11 +51,11 @@ if TYPE_CHECKING: import doctest from typing import Self -DOCTEST_REPORT_CHOICE_NONE = "none" -DOCTEST_REPORT_CHOICE_CDIFF = "cdiff" -DOCTEST_REPORT_CHOICE_NDIFF = "ndiff" -DOCTEST_REPORT_CHOICE_UDIFF = "udiff" -DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = "only_first_failure" +DOCTEST_REPORT_CHOICE_NONE = 'none' +DOCTEST_REPORT_CHOICE_CDIFF = 'cdiff' +DOCTEST_REPORT_CHOICE_NDIFF = 'ndiff' +DOCTEST_REPORT_CHOICE_UDIFF = 'udiff' +DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = 'only_first_failure' DOCTEST_REPORT_CHOICES = ( DOCTEST_REPORT_CHOICE_NONE, @@ -66,56 +68,56 @@ DOCTEST_REPORT_CHOICES = ( # Lazy definition of runner class RUNNER_CLASS = None # Lazy definition of output checker class -CHECKER_CLASS: Optional[Type["doctest.OutputChecker"]] = None +CHECKER_CLASS: type[doctest.OutputChecker] | None = None def pytest_addoption(parser: Parser) -> None: parser.addini( - "doctest_optionflags", - "Option flags for doctests", - type="args", - default=["ELLIPSIS"], + 'doctest_optionflags', + 'Option flags for doctests', + type='args', + default=['ELLIPSIS'], ) parser.addini( - "doctest_encoding", "Encoding used for doctest files", default="utf-8" + 'doctest_encoding', 'Encoding used for doctest files', default='utf-8', ) - group = parser.getgroup("collect") + group = parser.getgroup('collect') group.addoption( - "--doctest-modules", - action="store_true", + '--doctest-modules', + action='store_true', default=False, - help="Run doctests in all .py modules", - dest="doctestmodules", + help='Run doctests in all .py modules', + dest='doctestmodules', ) group.addoption( - "--doctest-report", + '--doctest-report', type=str.lower, - default="udiff", - help="Choose another output format for diffs on doctest failure", + default='udiff', + help='Choose another output format for diffs on doctest failure', choices=DOCTEST_REPORT_CHOICES, - dest="doctestreport", + dest='doctestreport', ) group.addoption( - "--doctest-glob", - action="append", + '--doctest-glob', + action='append', default=[], - metavar="pat", - help="Doctests file matching pattern, default: test*.txt", - dest="doctestglob", + metavar='pat', + help='Doctests file matching pattern, default: test*.txt', + dest='doctestglob', ) group.addoption( - "--doctest-ignore-import-errors", - action="store_true", + '--doctest-ignore-import-errors', + action='store_true', default=False, - help="Ignore doctest collection errors", - dest="doctest_ignore_import_errors", + help='Ignore doctest collection errors', + dest='doctest_ignore_import_errors', ) group.addoption( - "--doctest-continue-on-failure", - action="store_true", + '--doctest-continue-on-failure', + action='store_true', default=False, - help="For a given doctest, continue to run after the first failure", - dest="doctest_continue_on_failure", + help='For a given doctest, continue to run after the first failure', + dest='doctest_continue_on_failure', ) @@ -128,11 +130,11 @@ def pytest_unconfigure() -> None: def pytest_collect_file( file_path: Path, parent: Collector, -) -> Optional[Union["DoctestModule", "DoctestTextfile"]]: +) -> DoctestModule | DoctestTextfile | None: config = parent.config - if file_path.suffix == ".py": + if file_path.suffix == '.py': if config.option.doctestmodules and not any( - (_is_setup_py(file_path), _is_main_py(file_path)) + (_is_setup_py(file_path), _is_main_py(file_path)), ): return DoctestModule.from_parent(parent, path=file_path) elif _is_doctest(config, file_path, parent): @@ -141,26 +143,26 @@ def pytest_collect_file( def _is_setup_py(path: Path) -> bool: - if path.name != "setup.py": + if path.name != 'setup.py': return False contents = path.read_bytes() - return b"setuptools" in contents or b"distutils" in contents + return b'setuptools' in contents or b'distutils' in contents def _is_doctest(config: Config, path: Path, parent: Collector) -> bool: - if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path): + if path.suffix in ('.txt', '.rst') and parent.session.isinitpath(path): return True - globs = config.getoption("doctestglob") or ["test*.txt"] + globs = config.getoption('doctestglob') or ['test*.txt'] return any(fnmatch_ex(glob, path) for glob in globs) def _is_main_py(path: Path) -> bool: - return path.name == "__main__.py" + return path.name == '__main__.py' class ReprFailDoctest(TerminalRepr): def __init__( - self, reprlocation_lines: Sequence[Tuple[ReprFileLocation, Sequence[str]]] + self, reprlocation_lines: Sequence[tuple[ReprFileLocation, Sequence[str]]], ) -> None: self.reprlocation_lines = reprlocation_lines @@ -172,12 +174,12 @@ class ReprFailDoctest(TerminalRepr): class MultipleDoctestFailures(Exception): - def __init__(self, failures: Sequence["doctest.DocTestFailure"]) -> None: + def __init__(self, failures: Sequence[doctest.DocTestFailure]) -> None: super().__init__() self.failures = failures -def _init_runner_class() -> Type["doctest.DocTestRunner"]: +def _init_runner_class() -> type[doctest.DocTestRunner]: import doctest class PytestDoctestRunner(doctest.DebugRunner): @@ -189,8 +191,8 @@ def _init_runner_class() -> Type["doctest.DocTestRunner"]: def __init__( self, - checker: Optional["doctest.OutputChecker"] = None, - verbose: Optional[bool] = None, + checker: doctest.OutputChecker | None = None, + verbose: bool | None = None, optionflags: int = 0, continue_on_failure: bool = True, ) -> None: @@ -200,8 +202,8 @@ def _init_runner_class() -> Type["doctest.DocTestRunner"]: def report_failure( self, out, - test: "doctest.DocTest", - example: "doctest.Example", + test: doctest.DocTest, + example: doctest.Example, got: str, ) -> None: failure = doctest.DocTestFailure(test, example, got) @@ -213,14 +215,14 @@ def _init_runner_class() -> Type["doctest.DocTestRunner"]: def report_unexpected_exception( self, out, - test: "doctest.DocTest", - example: "doctest.Example", - exc_info: Tuple[Type[BaseException], BaseException, types.TracebackType], + test: doctest.DocTest, + example: doctest.Example, + exc_info: tuple[type[BaseException], BaseException, types.TracebackType], ) -> None: if isinstance(exc_info[1], OutcomeException): raise exc_info[1] if isinstance(exc_info[1], bdb.BdbQuit): - outcomes.exit("Quitting debugger") + outcomes.exit('Quitting debugger') failure = doctest.UnexpectedException(test, example, exc_info) if self.continue_on_failure: out.append(failure) @@ -231,11 +233,11 @@ def _init_runner_class() -> Type["doctest.DocTestRunner"]: def _get_runner( - checker: Optional["doctest.OutputChecker"] = None, - verbose: Optional[bool] = None, + checker: doctest.OutputChecker | None = None, + verbose: bool | None = None, optionflags: int = 0, continue_on_failure: bool = True, -) -> "doctest.DocTestRunner": +) -> doctest.DocTestRunner: # We need this in order to do a lazy import on doctest global RUNNER_CLASS if RUNNER_CLASS is None: @@ -254,9 +256,9 @@ class DoctestItem(Item): def __init__( self, name: str, - parent: "Union[DoctestTextfile, DoctestModule]", - runner: "doctest.DocTestRunner", - dtest: "doctest.DocTest", + parent: Union[DoctestTextfile, DoctestModule], + runner: doctest.DocTestRunner, + dtest: doctest.DocTest, ) -> None: super().__init__(name, parent) self.runner = runner @@ -273,31 +275,31 @@ class DoctestItem(Item): @classmethod def from_parent( # type: ignore[override] cls, - parent: "Union[DoctestTextfile, DoctestModule]", + parent: Union[DoctestTextfile, DoctestModule], *, name: str, - runner: "doctest.DocTestRunner", - dtest: "doctest.DocTest", - ) -> "Self": + runner: doctest.DocTestRunner, + dtest: doctest.DocTest, + ) -> Self: # incompatible signature due to imposed limits on subclass """The public named constructor.""" return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) def _initrequest(self) -> None: - self.funcargs: Dict[str, object] = {} + self.funcargs: dict[str, object] = {} self._request = TopRequest(self, _ispytest=True) # type: ignore[arg-type] def setup(self) -> None: self._request._fillfixtures() globs = dict(getfixture=self._request.getfixturevalue) - for name, value in self._request.getfixturevalue("doctest_namespace").items(): + for name, value in self._request.getfixturevalue('doctest_namespace').items(): globs[name] = value self.dtest.globs.update(globs) def runtest(self) -> None: _check_all_skipped(self.dtest) self._disable_output_capturing_for_darwin() - failures: List["doctest.DocTestFailure"] = [] + failures: list[doctest.DocTestFailure] = [] # Type ignored because we change the type of `out` from what # doctest expects. self.runner.run(self.dtest, out=failures) # type: ignore[arg-type] @@ -306,9 +308,9 @@ class DoctestItem(Item): def _disable_output_capturing_for_darwin(self) -> None: """Disable output capturing. Otherwise, stdout is lost to doctest (#985).""" - if platform.system() != "Darwin": + if platform.system() != 'Darwin': return - capman = self.config.pluginmanager.getplugin("capturemanager") + capman = self.config.pluginmanager.getplugin('capturemanager') if capman: capman.suspend_global_capture(in_=True) out, err = capman.read_global_capture() @@ -319,14 +321,14 @@ class DoctestItem(Item): def repr_failure( # type: ignore[override] self, excinfo: ExceptionInfo[BaseException], - ) -> Union[str, TerminalRepr]: + ) -> str | TerminalRepr: import doctest - failures: Optional[ - Sequence[Union[doctest.DocTestFailure, doctest.UnexpectedException]] - ] = None + failures: None | ( + Sequence[doctest.DocTestFailure | doctest.UnexpectedException] + ) = None if isinstance( - excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException) + excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException), ): failures = [excinfo.value] elif isinstance(excinfo.value, MultipleDoctestFailures): @@ -348,43 +350,43 @@ class DoctestItem(Item): # TODO: ReprFileLocation doesn't expect a None lineno. reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] checker = _get_checker() - report_choice = _get_report_choice(self.config.getoption("doctestreport")) + report_choice = _get_report_choice(self.config.getoption('doctestreport')) if lineno is not None: assert failure.test.docstring is not None lines = failure.test.docstring.splitlines(False) # add line numbers to the left of the error message assert test.lineno is not None lines = [ - "%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines) + '%03d %s' % (i + test.lineno + 1, x) for (i, x) in enumerate(lines) ] # trim docstring error lines to 10 - lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] + lines = lines[max(example.lineno - 9, 0): example.lineno + 1] else: lines = [ - "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" + 'EXAMPLE LOCATION UNKNOWN, not showing all tests of that example', ] - indent = ">>>" + indent = '>>>' for line in example.source.splitlines(): - lines.append(f"??? {indent} {line}") - indent = "..." + lines.append(f'??? {indent} {line}') + indent = '...' if isinstance(failure, doctest.DocTestFailure): lines += checker.output_difference( - example, failure.got, report_choice - ).split("\n") + example, failure.got, report_choice, + ).split('\n') else: inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) - lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] + lines += ['UNEXPECTED EXCEPTION: %s' % repr(inner_excinfo.value)] lines += [ - x.strip("\n") for x in traceback.format_exception(*failure.exc_info) + x.strip('\n') for x in traceback.format_exception(*failure.exc_info) ] reprlocation_lines.append((reprlocation, lines)) return ReprFailDoctest(reprlocation_lines) - def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: - return self.path, self.dtest.lineno, "[doctest] %s" % self.name + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + return self.path, self.dtest.lineno, '[doctest] %s' % self.name -def _get_flag_lookup() -> Dict[str, int]: +def _get_flag_lookup() -> dict[str, int]: import doctest return dict( @@ -401,7 +403,7 @@ def _get_flag_lookup() -> Dict[str, int]: def get_optionflags(config: Config) -> int: - optionflags_str = config.getini("doctest_optionflags") + optionflags_str = config.getini('doctest_optionflags') flag_lookup_table = _get_flag_lookup() flag_acc = 0 for flag in optionflags_str: @@ -410,11 +412,11 @@ def get_optionflags(config: Config) -> int: def _get_continue_on_failure(config: Config) -> bool: - continue_on_failure: bool = config.getvalue("doctest_continue_on_failure") + continue_on_failure: bool = config.getvalue('doctest_continue_on_failure') if continue_on_failure: # We need to turn off this if we use pdb since we should stop at # the first failure. - if config.getvalue("usepdb"): + if config.getvalue('usepdb'): continue_on_failure = False return continue_on_failure @@ -427,11 +429,11 @@ class DoctestTextfile(Module): # Inspired by doctest.testfile; ideally we would use it directly, # but it doesn't support passing a custom checker. - encoding = self.config.getini("doctest_encoding") + encoding = self.config.getini('doctest_encoding') text = self.path.read_text(encoding) filename = str(self.path) name = self.path.name - globs = {"__name__": "__main__"} + globs = {'__name__': '__main__'} optionflags = get_optionflags(self.config) @@ -446,25 +448,25 @@ class DoctestTextfile(Module): test = parser.get_doctest(text, globs, name, filename, 0) if test.examples: yield DoctestItem.from_parent( - self, name=test.name, runner=runner, dtest=test + self, name=test.name, runner=runner, dtest=test, ) -def _check_all_skipped(test: "doctest.DocTest") -> None: +def _check_all_skipped(test: doctest.DocTest) -> None: """Raise pytest.skip() if all examples in the given DocTest have the SKIP option set.""" import doctest all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples) if all_skipped: - skip("all tests skipped by +SKIP option") + skip('all tests skipped by +SKIP option') def _is_mocked(obj: object) -> bool: """Return if an object is possibly a mock object by checking the existence of a highly improbable attribute.""" return ( - safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None) + safe_getattr(obj, 'pytest_mock_example_attribute_that_shouldnt_exist', None) is not None ) @@ -476,7 +478,7 @@ def _patch_unwrap_mock_aware() -> Generator[None, None, None]: real_unwrap = inspect.unwrap def _mock_aware_unwrap( - func: Callable[..., Any], *, stop: Optional[Callable[[Any], Any]] = None + func: Callable[..., Any], *, stop: Callable[[Any], Any] | None = None, ) -> Any: try: if stop is None or stop is _is_mocked: @@ -485,9 +487,9 @@ def _patch_unwrap_mock_aware() -> Generator[None, None, None]: return real_unwrap(func, stop=lambda obj: _is_mocked(obj) or _stop(func)) except Exception as e: warnings.warn( - f"Got {e!r} when unwrapping {func!r}. This is usually caused " + f'Got {e!r} when unwrapping {func!r}. This is usually caused ' "by a violation of Python's object protocol; see e.g. " - "https://github.com/pytest-dev/pytest/issues/5080", + 'https://github.com/pytest-dev/pytest/issues/5080', PytestWarning, ) raise @@ -518,9 +520,9 @@ class DoctestModule(Module): line number is returned. This will be reported upstream. #8796 """ if isinstance(obj, property): - obj = getattr(obj, "fget", obj) + obj = getattr(obj, 'fget', obj) - if hasattr(obj, "__wrapped__"): + if hasattr(obj, '__wrapped__'): # Get the main obj in case of it being wrapped obj = inspect.unwrap(obj) @@ -531,14 +533,14 @@ class DoctestModule(Module): ) def _find( - self, tests, obj, name, module, source_lines, globs, seen + self, tests, obj, name, module, source_lines, globs, seen, ) -> None: if _is_mocked(obj): return with _patch_unwrap_mock_aware(): # Type ignored because this is a private function. super()._find( # type:ignore[misc] - tests, obj, name, module, source_lines, globs, seen + tests, obj, name, module, source_lines, globs, seen, ) if sys.version_info < (3, 13): @@ -561,8 +563,8 @@ class DoctestModule(Module): try: module = self.obj except Collector.CollectError: - if self.config.getvalue("doctest_ignore_import_errors"): - skip("unable to import module %r" % self.path) + if self.config.getvalue('doctest_ignore_import_errors'): + skip('unable to import module %r' % self.path) else: raise @@ -583,11 +585,11 @@ class DoctestModule(Module): for test in finder.find(module, module.__name__): if test.examples: # skip empty doctests yield DoctestItem.from_parent( - self, name=test.name, runner=runner, dtest=test + self, name=test.name, runner=runner, dtest=test, ) -def _init_checker_class() -> Type["doctest.OutputChecker"]: +def _init_checker_class() -> type[doctest.OutputChecker]: import doctest import re @@ -633,7 +635,7 @@ def _init_checker_class() -> Type["doctest.OutputChecker"]: return False def remove_prefixes(regex: Pattern[str], txt: str) -> str: - return re.sub(regex, r"\1\2", txt) + return re.sub(regex, r'\1\2', txt) if allow_unicode: want = remove_prefixes(self._unicode_literal_re, want) @@ -655,10 +657,10 @@ def _init_checker_class() -> Type["doctest.OutputChecker"]: return got offset = 0 for w, g in zip(wants, gots): - fraction: Optional[str] = w.group("fraction") - exponent: Optional[str] = w.group("exponent1") + fraction: str | None = w.group('fraction') + exponent: str | None = w.group('exponent1') if exponent is None: - exponent = w.group("exponent2") + exponent = w.group('exponent2') precision = 0 if fraction is None else len(fraction) if exponent is not None: precision -= int(exponent) @@ -667,7 +669,7 @@ def _init_checker_class() -> Type["doctest.OutputChecker"]: # got with the text we want, so that it will match when we # check the string literally. got = ( - got[: g.start() + offset] + w.group() + got[g.end() + offset :] + got[: g.start() + offset] + w.group() + got[g.end() + offset:] ) offset += w.end() - w.start() - (g.end() - g.start()) return got @@ -675,7 +677,7 @@ def _init_checker_class() -> Type["doctest.OutputChecker"]: return LiteralsOutputChecker -def _get_checker() -> "doctest.OutputChecker": +def _get_checker() -> doctest.OutputChecker: """Return a doctest.OutputChecker subclass that supports some additional options: @@ -699,21 +701,21 @@ def _get_allow_unicode_flag() -> int: """Register and return the ALLOW_UNICODE flag.""" import doctest - return doctest.register_optionflag("ALLOW_UNICODE") + return doctest.register_optionflag('ALLOW_UNICODE') def _get_allow_bytes_flag() -> int: """Register and return the ALLOW_BYTES flag.""" import doctest - return doctest.register_optionflag("ALLOW_BYTES") + return doctest.register_optionflag('ALLOW_BYTES') def _get_number_flag() -> int: """Register and return the NUMBER flag.""" import doctest - return doctest.register_optionflag("NUMBER") + return doctest.register_optionflag('NUMBER') def _get_report_choice(key: str) -> int: @@ -733,8 +735,8 @@ def _get_report_choice(key: str) -> int: }[key] -@fixture(scope="session") -def doctest_namespace() -> Dict[str, Any]: +@fixture(scope='session') +def doctest_namespace() -> dict[str, Any]: """Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests. diff --git a/.venv/lib/python3.10/site-packages/_pytest/faulthandler.py b/.venv/lib/python3.10/site-packages/_pytest/faulthandler.py index 083bcb8..1a7576f 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/faulthandler.py +++ b/.venv/lib/python3.10/site-packages/_pytest/faulthandler.py @@ -1,12 +1,14 @@ +from __future__ import annotations + import os import sys from typing import Generator +import pytest from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.nodes import Item from _pytest.stash import StashKey -import pytest fault_handler_original_stderr_fd_key = StashKey[int]() @@ -15,10 +17,10 @@ fault_handler_stderr_fd_key = StashKey[int]() def pytest_addoption(parser: Parser) -> None: help = ( - "Dump the traceback of all threads if a test takes " - "more than TIMEOUT seconds to finish" + 'Dump the traceback of all threads if a test takes ' + 'more than TIMEOUT seconds to finish' ) - parser.addini("faulthandler_timeout", help, default=0.0) + parser.addini('faulthandler_timeout', help, default=0.0) def pytest_configure(config: Config) -> None: @@ -66,7 +68,7 @@ def get_stderr_fileno() -> int: def get_timeout_config_value(config: Config) -> float: - return float(config.getini("faulthandler_timeout") or 0.0) + return float(config.getini('faulthandler_timeout') or 0.0) @pytest.hookimpl(wrapper=True, trylast=True) diff --git a/.venv/lib/python3.10/site-packages/_pytest/fixtures.py b/.venv/lib/python3.10/site-packages/_pytest/fixtures.py index 1ee7e84..f8ada2d 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/fixtures.py +++ b/.venv/lib/python3.10/site-packages/_pytest/fixtures.py @@ -1,13 +1,16 @@ # mypy: allow-untyped-defs +from __future__ import annotations + import abc -from collections import defaultdict -from collections import deque import dataclasses import functools import inspect import os -from pathlib import Path import sys +import warnings +from collections import defaultdict +from collections import deque +from pathlib import Path from typing import AbstractSet from typing import Any from typing import Callable @@ -30,7 +33,6 @@ from typing import Tuple from typing import TYPE_CHECKING from typing import TypeVar from typing import Union -import warnings import _pytest from _pytest import nodes @@ -82,12 +84,12 @@ if TYPE_CHECKING: # The value of the fixture -- return/yield of the fixture function (type variable). -FixtureValue = TypeVar("FixtureValue") +FixtureValue = TypeVar('FixtureValue') # The type of the fixture function (type variable). -FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object]) +FixtureFunction = TypeVar('FixtureFunction', bound=Callable[..., object]) # The type of a fixture function (type alias generic in fixture value). _FixtureFunc = Union[ - Callable[..., FixtureValue], Callable[..., Generator[FixtureValue, None, None]] + Callable[..., FixtureValue], Callable[..., Generator[FixtureValue, None, None]], ] # The type of FixtureDef.cached_result (type alias generic in fixture value). _FixtureCachedResult = Union[ @@ -110,18 +112,18 @@ _FixtureCachedResult = Union[ @dataclasses.dataclass(frozen=True) class PseudoFixtureDef(Generic[FixtureValue]): - cached_result: "_FixtureCachedResult[FixtureValue]" + cached_result: _FixtureCachedResult[FixtureValue] _scope: Scope -def pytest_sessionstart(session: "Session") -> None: +def pytest_sessionstart(session: Session) -> None: session._fixturemanager = FixtureManager(session) def get_scope_package( node: nodes.Item, - fixturedef: "FixtureDef[object]", -) -> Optional[nodes.Node]: + fixturedef: FixtureDef[object], +) -> nodes.Node | None: from _pytest.python import Package for parent in node.iter_parents(): @@ -130,7 +132,7 @@ def get_scope_package( return node.session -def get_scope_node(node: nodes.Node, scope: Scope) -> Optional[nodes.Node]: +def get_scope_node(node: nodes.Node, scope: Scope) -> nodes.Node | None: import _pytest.python if scope is Scope.Function: @@ -149,12 +151,12 @@ def get_scope_node(node: nodes.Node, scope: Scope) -> Optional[nodes.Node]: assert_never(scope) -def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]: +def getfixturemarker(obj: object) -> FixtureFunctionMarker | None: """Return fixturemarker or None if it doesn't exist or raised exceptions.""" return cast( Optional[FixtureFunctionMarker], - safe_getattr(obj, "_pytestfixturefunction", None), + safe_getattr(obj, '_pytestfixturefunction', None), ) @@ -162,12 +164,12 @@ def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]: class FixtureArgKey: argname: str param_index: int - scoped_item_path: Optional[Path] - item_cls: Optional[type] + scoped_item_path: Path | None + item_cls: type | None def get_parametrized_fixture_keys( - item: nodes.Item, scope: Scope + item: nodes.Item, scope: Scope, ) -> Iterator[FixtureArgKey]: """Return list of keys for all parametrized arguments which match the specified scope.""" @@ -203,9 +205,9 @@ def get_parametrized_fixture_keys( # setups and teardowns. -def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]: - argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[FixtureArgKey, None]]] = {} - items_by_argkey: Dict[Scope, Dict[FixtureArgKey, Deque[nodes.Item]]] = {} +def reorder_items(items: Sequence[nodes.Item]) -> list[nodes.Item]: + argkeys_cache: dict[Scope, dict[nodes.Item, dict[FixtureArgKey, None]]] = {} + items_by_argkey: dict[Scope, dict[FixtureArgKey, Deque[nodes.Item]]] = {} for scope in HIGH_SCOPES: scoped_argkeys_cache = argkeys_cache[scope] = {} scoped_items_by_argkey = items_by_argkey[scope] = defaultdict(deque) @@ -217,14 +219,14 @@ def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]: scoped_items_by_argkey[key].append(item) items_dict = dict.fromkeys(items, None) return list( - reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, Scope.Session) + reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, Scope.Session), ) def fix_cache_order( item: nodes.Item, - argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[FixtureArgKey, None]]], - items_by_argkey: Dict[Scope, Dict[FixtureArgKey, "Deque[nodes.Item]"]], + argkeys_cache: dict[Scope, dict[nodes.Item, dict[FixtureArgKey, None]]], + items_by_argkey: dict[Scope, dict[FixtureArgKey, Deque[nodes.Item]]], ) -> None: for scope in HIGH_SCOPES: for key in argkeys_cache[scope].get(item, []): @@ -232,27 +234,27 @@ def fix_cache_order( def reorder_items_atscope( - items: Dict[nodes.Item, None], - argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[FixtureArgKey, None]]], - items_by_argkey: Dict[Scope, Dict[FixtureArgKey, "Deque[nodes.Item]"]], + items: dict[nodes.Item, None], + argkeys_cache: dict[Scope, dict[nodes.Item, dict[FixtureArgKey, None]]], + items_by_argkey: dict[Scope, dict[FixtureArgKey, Deque[nodes.Item]]], scope: Scope, -) -> Dict[nodes.Item, None]: +) -> dict[nodes.Item, None]: if scope is Scope.Function or len(items) < 3: return items - ignore: Set[Optional[FixtureArgKey]] = set() + ignore: set[FixtureArgKey | None] = set() items_deque = deque(items) - items_done: Dict[nodes.Item, None] = {} + items_done: dict[nodes.Item, None] = {} scoped_items_by_argkey = items_by_argkey[scope] scoped_argkeys_cache = argkeys_cache[scope] while items_deque: - no_argkey_group: Dict[nodes.Item, None] = {} + no_argkey_group: dict[nodes.Item, None] = {} slicing_argkey = None while items_deque: item = items_deque.popleft() if item in items_done or item in no_argkey_group: continue argkeys = dict.fromkeys( - (k for k in scoped_argkeys_cache.get(item, []) if k not in ignore), None + (k for k in scoped_argkeys_cache.get(item, []) if k not in ignore), None, ) if not argkeys: no_argkey_group[item] = None @@ -269,7 +271,7 @@ def reorder_items_atscope( break if no_argkey_group: no_argkey_group = reorder_items_atscope( - no_argkey_group, argkeys_cache, items_by_argkey, scope.next_lower() + no_argkey_group, argkeys_cache, items_by_argkey, scope.next_lower(), ) for item in no_argkey_group: items_done[item] = None @@ -291,22 +293,22 @@ class FuncFixtureInfo: these are not reflected here. """ - __slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs") + __slots__ = ('argnames', 'initialnames', 'names_closure', 'name2fixturedefs') # Fixture names that the item requests directly by function parameters. - argnames: Tuple[str, ...] + argnames: tuple[str, ...] # Fixture names that the item immediately requires. These include # argnames + fixture names specified via usefixtures and via autouse=True in # fixture definitions. - initialnames: Tuple[str, ...] + initialnames: tuple[str, ...] # The transitive closure of the fixture names that the item requires. # Note: can't include dynamic dependencies (`request.getfixturevalue` calls). - names_closure: List[str] + names_closure: list[str] # A map from a fixture name in the transitive closure to the FixtureDefs # matching the name which are applicable to this function. # There may be multiple overriding fixtures with the same name. The # sequence is ordered from furthest to closes to the function. - name2fixturedefs: Dict[str, Sequence["FixtureDef[Any]"]] + name2fixturedefs: dict[str, Sequence[FixtureDef[Any]]] def prune_dependency_tree(self) -> None: """Recompute names_closure from initialnames and name2fixturedefs. @@ -319,7 +321,7 @@ class FuncFixtureInfo: tree. In this way the dependency tree can get pruned, and the closure of argnames may get reduced. """ - closure: Set[str] = set() + closure: set[str] = set() working_set = set(self.initialnames) while working_set: argname = working_set.pop() @@ -345,10 +347,10 @@ class FixtureRequest(abc.ABC): def __init__( self, - pyfuncitem: "Function", - fixturename: Optional[str], - arg2fixturedefs: Dict[str, Sequence["FixtureDef[Any]"]], - fixture_defs: Dict[str, "FixtureDef[Any]"], + pyfuncitem: Function, + fixturename: str | None, + arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]], + fixture_defs: dict[str, FixtureDef[Any]], *, _ispytest: bool = False, ) -> None: @@ -375,7 +377,7 @@ class FixtureRequest(abc.ABC): self.param: Any @property - def _fixturemanager(self) -> "FixtureManager": + def _fixturemanager(self) -> FixtureManager: return self._pyfuncitem.session._fixturemanager @property @@ -389,7 +391,7 @@ class FixtureRequest(abc.ABC): return self._scope.value @property - def fixturenames(self) -> List[str]: + def fixturenames(self) -> list[str]: """Names of all active fixtures in this request.""" result = list(self._pyfuncitem.fixturenames) result.extend(set(self._fixture_defs).difference(result)) @@ -401,7 +403,7 @@ class FixtureRequest(abc.ABC): """Underlying collection node (depends on current request scope).""" raise NotImplementedError() - def _getnextfixturedef(self, argname: str) -> "FixtureDef[Any]": + def _getnextfixturedef(self, argname: str) -> FixtureDef[Any]: fixturedefs = self._arg2fixturedefs.get(argname, None) if fixturedefs is None: # We arrive here because of a dynamic call to @@ -444,17 +446,17 @@ class FixtureRequest(abc.ABC): @property def function(self): """Test function object if the request has a per-function scope.""" - if self.scope != "function": + if self.scope != 'function': raise AttributeError( - f"function not available in {self.scope}-scoped context" + f'function not available in {self.scope}-scoped context', ) return self._pyfuncitem.obj @property def cls(self): """Class (can be None) where the test function was collected.""" - if self.scope not in ("class", "function"): - raise AttributeError(f"cls not available in {self.scope}-scoped context") + if self.scope not in ('class', 'function'): + raise AttributeError(f'cls not available in {self.scope}-scoped context') clscol = self._pyfuncitem.getparent(_pytest.python.Class) if clscol: return clscol.obj @@ -466,14 +468,14 @@ class FixtureRequest(abc.ABC): try: return self._pyfuncitem._testcase # type: ignore[attr-defined] except AttributeError: - function = getattr(self, "function", None) - return getattr(function, "__self__", None) + function = getattr(self, 'function', None) + return getattr(function, '__self__', None) @property def module(self): """Python module object where the test function was collected.""" - if self.scope not in ("function", "class", "module"): - raise AttributeError(f"module not available in {self.scope}-scoped context") + if self.scope not in ('function', 'class', 'module'): + raise AttributeError(f'module not available in {self.scope}-scoped context') mod = self._pyfuncitem.getparent(_pytest.python.Module) assert mod is not None return mod.obj @@ -481,8 +483,8 @@ class FixtureRequest(abc.ABC): @property def path(self) -> Path: """Path where the test function was collected.""" - if self.scope not in ("function", "class", "module", "package"): - raise AttributeError(f"path not available in {self.scope}-scoped context") + if self.scope not in ('function', 'class', 'module', 'package'): + raise AttributeError(f'path not available in {self.scope}-scoped context') return self._pyfuncitem.path @property @@ -492,7 +494,7 @@ class FixtureRequest(abc.ABC): return node.keywords @property - def session(self) -> "Session": + def session(self) -> Session: """Pytest session object.""" return self._pyfuncitem.session # type: ignore[no-any-return] @@ -502,7 +504,7 @@ class FixtureRequest(abc.ABC): the last test within the requesting test context finished execution.""" raise NotImplementedError() - def applymarker(self, marker: Union[str, MarkDecorator]) -> None: + def applymarker(self, marker: str | MarkDecorator) -> None: """Apply a marker to a single test function invocation. This method is useful if you don't want to have a keyword/marker @@ -513,7 +515,7 @@ class FixtureRequest(abc.ABC): """ self.node.add_marker(marker) - def raiseerror(self, msg: Optional[str]) -> NoReturn: + def raiseerror(self, msg: str | None) -> NoReturn: """Raise a FixtureLookupError exception. :param msg: @@ -541,11 +543,11 @@ class FixtureRequest(abc.ABC): fixturedef = self._get_active_fixturedef(argname) assert fixturedef.cached_result is not None, ( f'The fixture value for "{argname}" is not available. ' - "This can happen when the fixture has already been torn down." + 'This can happen when the fixture has already been torn down.' ) return fixturedef.cached_result[0] - def _iter_chain(self) -> Iterator["SubRequest"]: + def _iter_chain(self) -> Iterator[SubRequest]: """Yield all SubRequests in the chain, from self up. Note: does *not* yield the TopRequest. @@ -556,14 +558,14 @@ class FixtureRequest(abc.ABC): current = current._parent_request def _get_active_fixturedef( - self, argname: str - ) -> Union["FixtureDef[object]", PseudoFixtureDef[object]]: + self, argname: str, + ) -> FixtureDef[object] | PseudoFixtureDef[object]: fixturedef = self._fixture_defs.get(argname) if fixturedef is None: try: fixturedef = self._getnextfixturedef(argname) except FixtureLookupError: - if argname == "request": + if argname == 'request': cached_result = (self, [0], None) return PseudoFixtureDef(cached_result, Scope.Function) raise @@ -571,12 +573,12 @@ class FixtureRequest(abc.ABC): self._fixture_defs[argname] = fixturedef return fixturedef - def _get_fixturestack(self) -> List["FixtureDef[Any]"]: + def _get_fixturestack(self) -> list[FixtureDef[Any]]: values = [request._fixturedef for request in self._iter_chain()] values.reverse() return values - def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: + def _compute_fixture_value(self, fixturedef: FixtureDef[object]) -> None: """Create a SubRequest based on "self" and call the execute method of the given FixtureDef object. @@ -603,12 +605,12 @@ class FixtureRequest(abc.ABC): scope = fixturedef._scope has_params = fixturedef.params is not None - fixtures_not_supported = getattr(funcitem, "nofuncargs", False) + fixtures_not_supported = getattr(funcitem, 'nofuncargs', False) if has_params and fixtures_not_supported: msg = ( - f"{funcitem.name} does not support fixtures, maybe unittest.TestCase subclass?\n" - f"Node id: {funcitem.nodeid}\n" - f"Function type: {type(funcitem).__name__}" + f'{funcitem.name} does not support fixtures, maybe unittest.TestCase subclass?\n' + f'Node id: {funcitem.nodeid}\n' + f'Function type: {type(funcitem).__name__}' ) fail(msg, pytrace=False) if has_params: @@ -618,15 +620,15 @@ class FixtureRequest(abc.ABC): source_lineno = frameinfo.lineno try: source_path_str = str( - source_path.relative_to(funcitem.config.rootpath) + source_path.relative_to(funcitem.config.rootpath), ) except ValueError: source_path_str = str(source_path) msg = ( - "The requested fixture has no parameter defined for test:\n" - " {}\n\n" + 'The requested fixture has no parameter defined for test:\n' + ' {}\n\n' "Requested fixture '{}' defined in:\n{}" - "\n\nRequested here:\n{}:{}".format( + '\n\nRequested here:\n{}:{}'.format( funcitem.nodeid, fixturedef.argname, getlocation(fixturedef.func, funcitem.config.rootpath), @@ -637,7 +639,7 @@ class FixtureRequest(abc.ABC): fail(msg, pytrace=False) subrequest = SubRequest( - self, scope, param, param_index, fixturedef, _ispytest=True + self, scope, param, param_index, fixturedef, _ispytest=True, ) # Check if a higher-level scoped fixture accesses a lower level one. @@ -649,7 +651,7 @@ class FixtureRequest(abc.ABC): self._schedule_finalizers(fixturedef, subrequest) def _schedule_finalizers( - self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" + self, fixturedef: FixtureDef[object], subrequest: SubRequest, ) -> None: # If fixture function failed it might have registered finalizers. finalizer = functools.partial(fixturedef.finish, request=subrequest) @@ -660,7 +662,7 @@ class FixtureRequest(abc.ABC): class TopRequest(FixtureRequest): """The type of the ``request`` fixture in a test function.""" - def __init__(self, pyfuncitem: "Function", *, _ispytest: bool = False) -> None: + def __init__(self, pyfuncitem: Function, *, _ispytest: bool = False) -> None: super().__init__( fixturename=None, pyfuncitem=pyfuncitem, @@ -678,7 +680,7 @@ class TopRequest(FixtureRequest): return self._pyfuncitem def __repr__(self) -> str: - return "" % (self.node) + return '' % (self.node) def _fillfixtures(self) -> None: item = self._pyfuncitem @@ -701,7 +703,7 @@ class SubRequest(FixtureRequest): scope: Scope, param: Any, param_index: int, - fixturedef: "FixtureDef[object]", + fixturedef: FixtureDef[object], *, _ispytest: bool = False, ) -> None: @@ -720,7 +722,7 @@ class SubRequest(FixtureRequest): self.param_index: Final = param_index def __repr__(self) -> str: - return f"" + return f'' @property def _scope(self) -> Scope: @@ -731,7 +733,7 @@ class SubRequest(FixtureRequest): scope = self._scope if scope is Scope.Function: # This might also be a non-function Item despite its attribute name. - node: Optional[nodes.Node] = self._pyfuncitem + node: nodes.Node | None = self._pyfuncitem elif scope is Scope.Package: node = get_scope_package(self._pyfuncitem, self._fixturedef) else: @@ -748,19 +750,19 @@ class SubRequest(FixtureRequest): invoking_scope: Scope, requested_scope: Scope, ) -> None: - if argname == "request": + if argname == 'request': return if invoking_scope > requested_scope: # Try to report something helpful. - text = "\n".join(self._factorytraceback()) + text = '\n'.join(self._factorytraceback()) fail( - f"ScopeMismatch: You tried to access the {requested_scope.value} scoped " - f"fixture {argname} with a {invoking_scope.value} scoped request object, " - f"involved factories:\n{text}", + f'ScopeMismatch: You tried to access the {requested_scope.value} scoped ' + f'fixture {argname} with a {invoking_scope.value} scoped request object, ' + f'involved factories:\n{text}', pytrace=False, ) - def _factorytraceback(self) -> List[str]: + def _factorytraceback(self) -> list[str]: lines = [] for fixturedef in self._get_fixturestack(): factory = fixturedef.func @@ -771,8 +773,8 @@ class SubRequest(FixtureRequest): else: p = fs lines.append( - "%s:%d: def %s%s" - % (p, lineno + 1, factory.__name__, inspect.signature(factory)) + '%s:%d: def %s%s' + % (p, lineno + 1, factory.__name__, inspect.signature(factory)), ) return lines @@ -780,17 +782,17 @@ class SubRequest(FixtureRequest): self._fixturedef.addfinalizer(finalizer) def _schedule_finalizers( - self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" + self, fixturedef: FixtureDef[object], subrequest: SubRequest, ) -> None: # If the executing fixturedef was not explicitly requested in the argument list (via # getfixturevalue inside the fixture call) then ensure this fixture def will be finished # first. if ( - fixturedef.argname not in self._fixture_defs - and fixturedef.argname not in self._pyfuncitem.fixturenames + fixturedef.argname not in self._fixture_defs and + fixturedef.argname not in self._pyfuncitem.fixturenames ): fixturedef.addfinalizer( - functools.partial(self._fixturedef.finish, request=self) + functools.partial(self._fixturedef.finish, request=self), ) super()._schedule_finalizers(fixturedef, subrequest) @@ -800,15 +802,15 @@ class FixtureLookupError(LookupError): """Could not return a requested fixture (missing or invalid).""" def __init__( - self, argname: Optional[str], request: FixtureRequest, msg: Optional[str] = None + self, argname: str | None, request: FixtureRequest, msg: str | None = None, ) -> None: self.argname = argname self.request = request self.fixturestack = request._get_fixturestack() self.msg = msg - def formatrepr(self) -> "FixtureLookupErrorRepr": - tblines: List[str] = [] + def formatrepr(self) -> FixtureLookupErrorRepr: + tblines: list[str] = [] addline = tblines.append stack = [self.request._pyfuncitem.obj] stack.extend(map(lambda x: x.func, self.fixturestack)) @@ -822,14 +824,14 @@ class FixtureLookupError(LookupError): try: lines, _ = inspect.getsourcelines(get_real_func(function)) except (OSError, IndexError, TypeError): - error_msg = "file %s, line %s: source code not available" + error_msg = 'file %s, line %s: source code not available' addline(error_msg % (fspath, lineno + 1)) else: - addline(f"file {fspath}, line {lineno + 1}") + addline(f'file {fspath}, line {lineno + 1}') for i, line in enumerate(lines): line = line.rstrip() - addline(" " + line) - if line.lstrip().startswith("def"): + addline(' ' + line) + if line.lstrip().startswith('def'): break if msg is None: @@ -847,7 +849,7 @@ class FixtureLookupError(LookupError): ) else: msg = f"fixture '{self.argname}' not found" - msg += "\n available fixtures: {}".format(", ".join(sorted(available))) + msg += '\n available fixtures: {}'.format(', '.join(sorted(available))) msg += "\n use 'pytest --fixtures [testpath]' for help on them." return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname) @@ -856,11 +858,11 @@ class FixtureLookupError(LookupError): class FixtureLookupErrorRepr(TerminalRepr): def __init__( self, - filename: Union[str, "os.PathLike[str]"], + filename: str | os.PathLike[str], firstlineno: int, tblines: Sequence[str], errorstring: str, - argname: Optional[str], + argname: str | None, ) -> None: self.tblines = tblines self.errorstring = errorstring @@ -872,40 +874,40 @@ class FixtureLookupErrorRepr(TerminalRepr): # tw.line("FixtureLookupError: %s" %(self.argname), red=True) for tbline in self.tblines: tw.line(tbline.rstrip()) - lines = self.errorstring.split("\n") + lines = self.errorstring.split('\n') if lines: tw.line( - f"{FormattedExcinfo.fail_marker} {lines[0].strip()}", + f'{FormattedExcinfo.fail_marker} {lines[0].strip()}', red=True, ) for line in lines[1:]: tw.line( - f"{FormattedExcinfo.flow_marker} {line.strip()}", + f'{FormattedExcinfo.flow_marker} {line.strip()}', red=True, ) tw.line() - tw.line("%s:%d" % (os.fspath(self.filename), self.firstlineno + 1)) + tw.line('%s:%d' % (os.fspath(self.filename), self.firstlineno + 1)) def fail_fixturefunc(fixturefunc, msg: str) -> NoReturn: fs, lineno = getfslineno(fixturefunc) - location = f"{fs}:{lineno + 1}" + location = f'{fs}:{lineno + 1}' source = _pytest._code.Source(fixturefunc) - fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False) + fail(msg + ':\n\n' + str(source.indent()) + '\n' + location, pytrace=False) def call_fixture_func( - fixturefunc: "_FixtureFunc[FixtureValue]", request: FixtureRequest, kwargs + fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs, ) -> FixtureValue: if is_generator(fixturefunc): fixturefunc = cast( - Callable[..., Generator[FixtureValue, None, None]], fixturefunc + Callable[..., Generator[FixtureValue, None, None]], fixturefunc, ) generator = fixturefunc(**kwargs) try: fixture_result = next(generator) except StopIteration: - raise ValueError(f"{request.fixturename} did not yield a value") from None + raise ValueError(f'{request.fixturename} did not yield a value') from None finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator) request.addfinalizer(finalizer) else: @@ -938,12 +940,12 @@ def _eval_scope_callable( except Exception as e: raise TypeError( f"Error evaluating {scope_callable} while defining fixture '{fixture_name}'.\n" - "Expected a function with the signature (*, fixture_name, config)" + 'Expected a function with the signature (*, fixture_name, config)', ) from e if not isinstance(result, str): fail( f"Expected {scope_callable} to return a 'str' while defining fixture '{fixture_name}', but it returned:\n" - f"{result!r}", + f'{result!r}', pytrace=False, ) return result @@ -960,15 +962,15 @@ class FixtureDef(Generic[FixtureValue]): def __init__( self, config: Config, - baseid: Optional[str], + baseid: str | None, argname: str, - func: "_FixtureFunc[FixtureValue]", - scope: Union[Scope, _ScopeName, Callable[[str, Config], _ScopeName], None], - params: Optional[Sequence[object]], + func: _FixtureFunc[FixtureValue], + scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] | None, + params: Sequence[object] | None, unittest: bool = False, - ids: Optional[ - Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]] - ] = None, + ids: None | ( + tuple[object | None, ...] | Callable[[Any], object | None] + ) = None, *, _ispytest: bool = False, ) -> None: @@ -986,7 +988,7 @@ class FixtureDef(Generic[FixtureValue]): # directory path relative to the rootdir. # # For other plugins, the baseid is the empty string (always matches). - self.baseid: Final = baseid or "" + self.baseid: Final = baseid or '' # Whether the fixture was found from a node or a conftest in the # collection tree. Will be false for fixtures defined in non-conftest # plugins. @@ -1001,7 +1003,7 @@ class FixtureDef(Generic[FixtureValue]): scope = _eval_scope_callable(scope, argname, config) if isinstance(scope, str): scope = Scope.from_user( - scope, descr=f"Fixture '{func.__name__}'", where=baseid + scope, descr=f"Fixture '{func.__name__}'", where=baseid, ) self._scope: Final = scope # If the fixture is directly parametrized, the parameter values. @@ -1016,8 +1018,8 @@ class FixtureDef(Generic[FixtureValue]): self.unittest: Final = unittest # If the fixture was executed, the current value of the fixture. # Can change if the fixture is executed with different parameters. - self.cached_result: Optional[_FixtureCachedResult[FixtureValue]] = None - self._finalizers: Final[List[Callable[[], object]]] = [] + self.cached_result: _FixtureCachedResult[FixtureValue] | None = None + self._finalizers: Final[list[Callable[[], object]]] = [] @property def scope(self) -> _ScopeName: @@ -1028,7 +1030,7 @@ class FixtureDef(Generic[FixtureValue]): self._finalizers.append(finalizer) def finish(self, request: SubRequest) -> None: - exceptions: List[BaseException] = [] + exceptions: list[BaseException] = [] while self._finalizers: fin = self._finalizers.pop() try: @@ -1053,7 +1055,7 @@ class FixtureDef(Generic[FixtureValue]): # with their finalization. for argname in self.argnames: fixturedef = request._get_active_fixturedef(argname) - if argname != "request": + if argname != 'request': # PseudoFixtureDef is only for "request". assert isinstance(fixturedef, FixtureDef) fixturedef.addfinalizer(functools.partial(self.finish, request=request)) @@ -1080,15 +1082,15 @@ class FixtureDef(Generic[FixtureValue]): return result def cache_key(self, request: SubRequest) -> object: - return request.param_index if not hasattr(request, "param") else request.param + return request.param_index if not hasattr(request, 'param') else request.param def __repr__(self) -> str: - return f"" + return f'' def resolve_fixture_function( - fixturedef: FixtureDef[FixtureValue], request: FixtureRequest -) -> "_FixtureFunc[FixtureValue]": + fixturedef: FixtureDef[FixtureValue], request: FixtureRequest, +) -> _FixtureFunc[FixtureValue]: """Get the actual callable that can be called to obtain the fixture value, dealing with unittest-specific instances and bound methods.""" fixturefunc = fixturedef.func @@ -1103,7 +1105,7 @@ def resolve_fixture_function( if request.instance is not None: # Handle the case where fixture is defined not in a test class, but some other class # (for example a plugin class with a fixture), see #2270. - if hasattr(fixturefunc, "__self__") and not isinstance( + if hasattr(fixturefunc, '__self__') and not isinstance( request.instance, fixturefunc.__self__.__class__, # type: ignore[union-attr] ): @@ -1115,7 +1117,7 @@ def resolve_fixture_function( def pytest_fixture_setup( - fixturedef: FixtureDef[FixtureValue], request: SubRequest + fixturedef: FixtureDef[FixtureValue], request: SubRequest, ) -> FixtureValue: """Execution of fixture setup.""" kwargs = {} @@ -1144,15 +1146,15 @@ def pytest_fixture_setup( def wrap_function_to_error_out_if_called_directly( function: FixtureFunction, - fixture_marker: "FixtureFunctionMarker", + fixture_marker: FixtureFunctionMarker, ) -> FixtureFunction: """Wrap the given fixture function so we can raise an error about it being called directly, instead of used as an argument in a test function.""" message = ( 'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n' - "but are created automatically when test functions request them as parameters.\n" - "See https://docs.pytest.org/en/stable/explanation/fixtures.html for more information about fixtures, and\n" - "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly about how to update your code." + 'but are created automatically when test functions request them as parameters.\n' + 'See https://docs.pytest.org/en/stable/explanation/fixtures.html for more information about fixtures, and\n' + 'https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly about how to update your code.' ).format(name=fixture_marker.name or function.__name__) @functools.wraps(function) @@ -1169,13 +1171,13 @@ def wrap_function_to_error_out_if_called_directly( @final @dataclasses.dataclass(frozen=True) class FixtureFunctionMarker: - scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" - params: Optional[Tuple[object, ...]] + scope: Union[_ScopeName, Callable[[str, Config], _ScopeName]] + params: tuple[object, ...] | None autouse: bool = False - ids: Optional[ - Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]] - ] = None - name: Optional[str] = None + ids: None | ( + tuple[object | None, ...] | Callable[[Any], object | None] + ) = None + name: str | None = None _ispytest: dataclasses.InitVar[bool] = False @@ -1184,20 +1186,20 @@ class FixtureFunctionMarker: def __call__(self, function: FixtureFunction) -> FixtureFunction: if inspect.isclass(function): - raise ValueError("class fixtures not supported (maybe in the future)") + raise ValueError('class fixtures not supported (maybe in the future)') - if getattr(function, "_pytestfixturefunction", False): + if getattr(function, '_pytestfixturefunction', False): raise ValueError( - f"@pytest.fixture is being applied more than once to the same function {function.__name__!r}" + f'@pytest.fixture is being applied more than once to the same function {function.__name__!r}', ) - if hasattr(function, "pytestmark"): + if hasattr(function, 'pytestmark'): warnings.warn(MARKED_FIXTURE, stacklevel=2) function = wrap_function_to_error_out_if_called_directly(function, self) name = self.name or function.__name__ - if name == "request": + if name == 'request': location = getlocation(function) fail( f"'request' is a reserved word for fixtures, use another name:\n {location}", @@ -1213,13 +1215,13 @@ class FixtureFunctionMarker: def fixture( fixture_function: FixtureFunction, *, - scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ..., - params: Optional[Iterable[object]] = ..., + scope: Union[_ScopeName, Callable[[str, Config], _ScopeName]] = ..., + params: Iterable[object] | None = ..., autouse: bool = ..., - ids: Optional[ - Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] - ] = ..., - name: Optional[str] = ..., + ids: None | ( + Sequence[object | None] | Callable[[Any], object | None] + ) = ..., + name: str | None = ..., ) -> FixtureFunction: ... @@ -1228,28 +1230,28 @@ def fixture( def fixture( fixture_function: None = ..., *, - scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ..., - params: Optional[Iterable[object]] = ..., + scope: Union[_ScopeName, Callable[[str, Config], _ScopeName]] = ..., + params: Iterable[object] | None = ..., autouse: bool = ..., - ids: Optional[ - Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] - ] = ..., - name: Optional[str] = None, + ids: None | ( + Sequence[object | None] | Callable[[Any], object | None] + ) = ..., + name: str | None = None, ) -> FixtureFunctionMarker: ... def fixture( - fixture_function: Optional[FixtureFunction] = None, + fixture_function: FixtureFunction | None = None, *, - scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = "function", - params: Optional[Iterable[object]] = None, + scope: Union[_ScopeName, Callable[[str, Config], _ScopeName]] = 'function', + params: Iterable[object] | None = None, autouse: bool = False, - ids: Optional[ - Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] - ] = None, - name: Optional[str] = None, -) -> Union[FixtureFunctionMarker, FixtureFunction]: + ids: None | ( + Sequence[object | None] | Callable[[Any], object | None] + ) = None, + name: str | None = None, +) -> FixtureFunctionMarker | FixtureFunction: """Decorator to mark a fixture factory function. This decorator can be used, with or without parameters, to define a @@ -1319,7 +1321,7 @@ def fixture( def yield_fixture( fixture_function=None, *args, - scope="function", + scope='function', params=None, autouse=False, ids=None, @@ -1342,7 +1344,7 @@ def yield_fixture( ) -@fixture(scope="session") +@fixture(scope='session') def pytestconfig(request: FixtureRequest) -> Config: """Session-scoped fixture that returns the session's :class:`pytest.Config` object. @@ -1359,14 +1361,14 @@ def pytestconfig(request: FixtureRequest) -> Config: def pytest_addoption(parser: Parser) -> None: parser.addini( - "usefixtures", - type="args", + 'usefixtures', + type='args', default=[], - help="List of default fixtures to be used with this project", + help='List of default fixtures to be used with this project', ) -def _get_direct_parametrize_args(node: nodes.Node) -> Set[str]: +def _get_direct_parametrize_args(node: nodes.Node) -> set[str]: """Return all direct parametrization arguments of a node, so we don't mistake them for fixtures. @@ -1375,17 +1377,17 @@ def _get_direct_parametrize_args(node: nodes.Node) -> Set[str]: These things are done later as well when dealing with parametrization so this could be improved. """ - parametrize_argnames: Set[str] = set() - for marker in node.iter_markers(name="parametrize"): - if not marker.kwargs.get("indirect", False): + parametrize_argnames: set[str] = set() + for marker in node.iter_markers(name='parametrize'): + if not marker.kwargs.get('indirect', False): p_argnames, _ = ParameterSet._parse_parametrize_args( - *marker.args, **marker.kwargs + *marker.args, **marker.kwargs, ) parametrize_argnames.update(p_argnames) return parametrize_argnames -def deduplicate_names(*seqs: Iterable[str]) -> Tuple[str, ...]: +def deduplicate_names(*seqs: Iterable[str]) -> tuple[str, ...]: """De-duplicate the sequence of names while keeping the original order.""" # Ideally we would use a set, but it does not preserve insertion order. return tuple(dict.fromkeys(name for seq in seqs for name in seq)) @@ -1422,26 +1424,26 @@ class FixtureManager: by a lookup of their FuncFixtureInfo. """ - def __init__(self, session: "Session") -> None: + def __init__(self, session: Session) -> None: self.session = session self.config: Config = session.config # Maps a fixture name (argname) to all of the FixtureDefs in the test # suite/plugins defined with this name. Populated by parsefactories(). # TODO: The order of the FixtureDefs list of each arg is significant, # explain. - self._arg2fixturedefs: Final[Dict[str, List[FixtureDef[Any]]]] = {} - self._holderobjseen: Final[Set[object]] = set() + self._arg2fixturedefs: Final[dict[str, list[FixtureDef[Any]]]] = {} + self._holderobjseen: Final[set[object]] = set() # A mapping from a nodeid to a list of autouse fixtures it defines. - self._nodeid_autousenames: Final[Dict[str, List[str]]] = { - "": self.config.getini("usefixtures"), + self._nodeid_autousenames: Final[dict[str, list[str]]] = { + '': self.config.getini('usefixtures'), } - session.config.pluginmanager.register(self, "funcmanage") + session.config.pluginmanager.register(self, 'funcmanage') def getfixtureinfo( self, node: nodes.Item, - func: Optional[Callable[..., object]], - cls: Optional[type], + func: Callable[..., object] | None, + cls: type | None, ) -> FuncFixtureInfo: """Calculate the :class:`FuncFixtureInfo` for an item. @@ -1455,7 +1457,7 @@ class FixtureManager: :param cls: If the function is a method, the method's class. """ - if func is not None and not getattr(node, "nofuncargs", False): + if func is not None and not getattr(node, 'nofuncargs', False): argnames = getfuncargnames(func, name=node.name, cls=cls) else: argnames = () @@ -1478,7 +1480,7 @@ class FixtureManager: # conftest's directory. This is unlike fixtures in non-conftest plugins # which have global visibility. So for conftests, construct the base # nodeid from the plugin name (which is the conftest path). - if plugin_name and plugin_name.endswith("conftest.py"): + if plugin_name and plugin_name.endswith('conftest.py'): # Note: we explicitly do *not* use `plugin.__file__` here -- The # difference is that plugin_name has the correct capitalization on # case-insensitive systems (Windows) and other normalization issues @@ -1487,9 +1489,9 @@ class FixtureManager: try: nodeid = str(conftestpath.parent.relative_to(self.config.rootpath)) except ValueError: - nodeid = "" - if nodeid == ".": - nodeid = "" + nodeid = '' + if nodeid == '.': + nodeid = '' if os.sep != nodes.SEP: nodeid = nodeid.replace(os.sep, nodes.SEP) else: @@ -1506,15 +1508,15 @@ class FixtureManager: def _getusefixturesnames(self, node: nodes.Item) -> Iterator[str]: """Return the names of usefixtures fixtures applicable to node.""" - for mark in node.iter_markers(name="usefixtures"): + for mark in node.iter_markers(name='usefixtures'): yield from mark.args def getfixtureclosure( self, parentnode: nodes.Node, - initialnames: Tuple[str, ...], + initialnames: tuple[str, ...], ignore_args: AbstractSet[str], - ) -> Tuple[List[str], Dict[str, Sequence[FixtureDef[Any]]]]: + ) -> tuple[list[str], dict[str, Sequence[FixtureDef[Any]]]]: # Collect the closure of all fixtures, starting with the given # fixturenames as the initial set. As we have to visit all # factory definitions anyway, we also return an arg2fixturedefs @@ -1524,7 +1526,7 @@ class FixtureManager: fixturenames_closure = list(initialnames) - arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {} + arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]] = {} lastlen = -1 while lastlen != len(fixturenames_closure): lastlen = len(fixturenames_closure) @@ -1551,7 +1553,7 @@ class FixtureManager: fixturenames_closure.sort(key=sort_by_scope, reverse=True) return fixturenames_closure, arg2fixturedefs - def pytest_generate_tests(self, metafunc: "Metafunc") -> None: + def pytest_generate_tests(self, metafunc: Metafunc) -> None: """Generate new tests based on parametrized fixtures used by the given metafunc""" def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]: @@ -1570,7 +1572,7 @@ class FixtureManager: # precedence. if any( argname in get_parametrize_mark_argnames(mark) - for mark in metafunc.definition.iter_markers("parametrize") + for mark in metafunc.definition.iter_markers('parametrize') ): continue @@ -1596,7 +1598,7 @@ class FixtureManager: # Try next super fixture, if any. - def pytest_collection_modifyitems(self, items: List[nodes.Item]) -> None: + def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> None: # Separate parametrized setups. items[:] = reorder_items(items) @@ -1604,15 +1606,15 @@ class FixtureManager: self, *, name: str, - func: "_FixtureFunc[object]", - nodeid: Optional[str], - scope: Union[ - Scope, _ScopeName, Callable[[str, Config], _ScopeName], None - ] = "function", - params: Optional[Sequence[object]] = None, - ids: Optional[ - Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]] - ] = None, + func: _FixtureFunc[object], + nodeid: str | None, + scope: ( + Scope | _ScopeName | Callable[[str, Config], _ScopeName] | None + ) = 'function', + params: Sequence[object] | None = None, + ids: None | ( + tuple[object | None, ...] | Callable[[Any], object | None] + ) = None, autouse: bool = False, unittest: bool = False, ) -> None: @@ -1661,7 +1663,7 @@ class FixtureManager: i = len([f for f in faclist if not f.has_location]) faclist.insert(i, fixture_def) if autouse: - self._nodeid_autousenames.setdefault(nodeid or "", []).append(name) + self._nodeid_autousenames.setdefault(nodeid or '', []).append(name) @overload def parsefactories( @@ -1676,7 +1678,7 @@ class FixtureManager: def parsefactories( self, node_or_obj: object, - nodeid: Optional[str], + nodeid: str | None, *, unittest: bool = ..., ) -> None: @@ -1684,8 +1686,8 @@ class FixtureManager: def parsefactories( self, - node_or_obj: Union[nodes.Node, object], - nodeid: Union[str, NotSetType, None] = NOTSET, + node_or_obj: nodes.Node | object, + nodeid: str | NotSetType | None = NOTSET, *, unittest: bool = False, ) -> None: @@ -1745,8 +1747,8 @@ class FixtureManager: ) def getfixturedefs( - self, argname: str, node: nodes.Node - ) -> Optional[Sequence[FixtureDef[Any]]]: + self, argname: str, node: nodes.Node, + ) -> Sequence[FixtureDef[Any]] | None: """Get FixtureDefs for a fixture name which are applicable to a given node. @@ -1765,7 +1767,7 @@ class FixtureManager: return tuple(self._matchfactories(fixturedefs, node)) def _matchfactories( - self, fixturedefs: Iterable[FixtureDef[Any]], node: nodes.Node + self, fixturedefs: Iterable[FixtureDef[Any]], node: nodes.Node, ) -> Iterator[FixtureDef[Any]]: parentnodeids = {n.nodeid for n in node.iter_parents()} for fixturedef in fixturedefs: diff --git a/.venv/lib/python3.10/site-packages/_pytest/freeze_support.py b/.venv/lib/python3.10/site-packages/_pytest/freeze_support.py index d028058..706a982 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/freeze_support.py +++ b/.venv/lib/python3.10/site-packages/_pytest/freeze_support.py @@ -1,5 +1,6 @@ """Provides a function to report all internal modules for using freezing tools.""" +from __future__ import annotations import types from typing import Iterator @@ -7,7 +8,7 @@ from typing import List from typing import Union -def freeze_includes() -> List[str]: +def freeze_includes() -> list[str]: """Return a list of module names used by pytest that should be included by cx_freeze.""" import _pytest @@ -17,8 +18,8 @@ def freeze_includes() -> List[str]: def _iter_all_modules( - package: Union[str, types.ModuleType], - prefix: str = "", + package: str | types.ModuleType, + prefix: str = '', ) -> Iterator[str]: """Iterate over the names of all modules that can be found in the given package, recursively. @@ -36,10 +37,10 @@ def _iter_all_modules( # Type ignored because typeshed doesn't define ModuleType.__path__ # (only defined on packages). package_path = package.__path__ # type: ignore[attr-defined] - path, prefix = package_path[0], package.__name__ + "." + path, prefix = package_path[0], package.__name__ + '.' for _, name, is_package in pkgutil.iter_modules([path]): if is_package: - for m in _iter_all_modules(os.path.join(path, name), prefix=name + "."): + for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'): yield prefix + m else: yield prefix + name diff --git a/.venv/lib/python3.10/site-packages/_pytest/helpconfig.py b/.venv/lib/python3.10/site-packages/_pytest/helpconfig.py index aa8bf65..e5cc69c 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/helpconfig.py +++ b/.venv/lib/python3.10/site-packages/_pytest/helpconfig.py @@ -1,19 +1,21 @@ # mypy: allow-untyped-defs """Version info, help messages, tracing configuration.""" -from argparse import Action +from __future__ import annotations + import os import sys +from argparse import Action from typing import Generator from typing import List from typing import Optional from typing import Union +import pytest from _pytest.config import Config from _pytest.config import ExitCode from _pytest.config import PrintHelp from _pytest.config.argparsing import Parser from _pytest.terminal import TerminalReporter -import pytest class HelpAction(Action): @@ -40,63 +42,63 @@ class HelpAction(Action): setattr(namespace, self.dest, self.const) # We should only skip the rest of the parsing after preparse is done. - if getattr(parser._parser, "after_preparse", False): + if getattr(parser._parser, 'after_preparse', False): raise PrintHelp def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("debugconfig") + group = parser.getgroup('debugconfig') group.addoption( - "--version", - "-V", - action="count", + '--version', + '-V', + action='count', default=0, - dest="version", - help="Display pytest version and information about plugins. " - "When given twice, also display information about plugins.", + dest='version', + help='Display pytest version and information about plugins. ' + 'When given twice, also display information about plugins.', ) group._addoption( - "-h", - "--help", + '-h', + '--help', action=HelpAction, - dest="help", - help="Show help message and configuration info", + dest='help', + help='Show help message and configuration info', ) group._addoption( - "-p", - action="append", - dest="plugins", + '-p', + action='append', + dest='plugins', default=[], - metavar="name", - help="Early-load given plugin module name or entry point (multi-allowed). " - "To avoid loading of plugins, use the `no:` prefix, e.g. " - "`no:doctest`.", + metavar='name', + help='Early-load given plugin module name or entry point (multi-allowed). ' + 'To avoid loading of plugins, use the `no:` prefix, e.g. ' + '`no:doctest`.', ) group.addoption( - "--traceconfig", - "--trace-config", - action="store_true", + '--traceconfig', + '--trace-config', + action='store_true', default=False, - help="Trace considerations of conftest.py files", + help='Trace considerations of conftest.py files', ) group.addoption( - "--debug", - action="store", - nargs="?", - const="pytestdebug.log", - dest="debug", - metavar="DEBUG_FILE_NAME", - help="Store internal tracing debug information in this log file. " + '--debug', + action='store', + nargs='?', + const='pytestdebug.log', + dest='debug', + metavar='DEBUG_FILE_NAME', + help='Store internal tracing debug information in this log file. ' "This file is opened with 'w' and truncated as a result, care advised. " - "Default: pytestdebug.log.", + 'Default: pytestdebug.log.', ) group._addoption( - "-o", - "--override-ini", - dest="override_ini", - action="append", + '-o', + '--override-ini', + dest='override_ini', + action='append', help='Override ini option with "option=value" style, ' - "e.g. `-o xfail_strict=True -o cache_dir=cache`.", + 'e.g. `-o xfail_strict=True -o cache_dir=cache`.', ) @@ -107,24 +109,24 @@ def pytest_cmdline_parse() -> Generator[None, Config, Config]: if config.option.debug: # --debug | --debug was provided. path = config.option.debug - debugfile = open(path, "w", encoding="utf-8") + debugfile = open(path, 'w', encoding='utf-8') debugfile.write( - "versions pytest-{}, " - "python-{}\ninvocation_dir={}\ncwd={}\nargs={}\n\n".format( + 'versions pytest-{}, ' + 'python-{}\ninvocation_dir={}\ncwd={}\nargs={}\n\n'.format( pytest.__version__, - ".".join(map(str, sys.version_info)), + '.'.join(map(str, sys.version_info)), config.invocation_params.dir, os.getcwd(), config.invocation_params.args, - ) + ), ) config.trace.root.setwriter(debugfile.write) undo_tracing = config.pluginmanager.enable_tracing() - sys.stderr.write("writing pytest debug information to %s\n" % path) + sys.stderr.write('writing pytest debug information to %s\n' % path) def unset_tracing() -> None: debugfile.close() - sys.stderr.write("wrote pytest debug information to %s\n" % debugfile.name) + sys.stderr.write('wrote pytest debug information to %s\n' % debugfile.name) config.trace.root.setwriter(None) undo_tracing() @@ -136,17 +138,17 @@ def pytest_cmdline_parse() -> Generator[None, Config, Config]: def showversion(config: Config) -> None: if config.option.version > 1: sys.stdout.write( - f"This is pytest version {pytest.__version__}, imported from {pytest.__file__}\n" + f'This is pytest version {pytest.__version__}, imported from {pytest.__file__}\n', ) plugininfo = getpluginversioninfo(config) if plugininfo: for line in plugininfo: - sys.stdout.write(line + "\n") + sys.stdout.write(line + '\n') else: - sys.stdout.write(f"pytest {pytest.__version__}\n") + sys.stdout.write(f'pytest {pytest.__version__}\n') -def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: if config.option.version > 0: showversion(config) return 0 @@ -161,30 +163,30 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: def showhelp(config: Config) -> None: import textwrap - reporter: Optional[TerminalReporter] = config.pluginmanager.get_plugin( - "terminalreporter" + reporter: TerminalReporter | None = config.pluginmanager.get_plugin( + 'terminalreporter', ) assert reporter is not None tw = reporter._tw tw.write(config._parser.optparser.format_help()) tw.line() tw.line( - "[pytest] ini-options in the first " - "pytest.ini|tox.ini|setup.cfg|pyproject.toml file found:" + '[pytest] ini-options in the first ' + 'pytest.ini|tox.ini|setup.cfg|pyproject.toml file found:', ) tw.line() columns = tw.fullwidth # costly call indent_len = 24 # based on argparse's max_help_position=24 - indent = " " * indent_len + indent = ' ' * indent_len for name in config._parser._ininames: help, type, default = config._parser._inidict[name] if type is None: - type = "string" + type = 'string' if help is None: - raise TypeError(f"help argument cannot be None for {name}") - spec = f"{name} ({type}):" - tw.write(" %s" % spec) + raise TypeError(f'help argument cannot be None for {name}') + spec = f'{name} ({type}):' + tw.write(' %s' % spec) spec_len = len(spec) if spec_len > (indent_len - 3): # Display help starting at a new line. @@ -201,7 +203,7 @@ def showhelp(config: Config) -> None: tw.line(line) else: # Display help starting after the spec, following lines indented. - tw.write(" " * (indent_len - spec_len - 2)) + tw.write(' ' * (indent_len - spec_len - 2)) wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False) if wrapped: @@ -210,62 +212,62 @@ def showhelp(config: Config) -> None: tw.line(indent + line) tw.line() - tw.line("Environment variables:") + tw.line('Environment variables:') vars = [ - ("PYTEST_ADDOPTS", "Extra command line options"), - ("PYTEST_PLUGINS", "Comma-separated plugins to load during startup"), - ("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "Set to disable plugin auto-loading"), - ("PYTEST_DEBUG", "Set to enable debug tracing of pytest's internals"), + ('PYTEST_ADDOPTS', 'Extra command line options'), + ('PYTEST_PLUGINS', 'Comma-separated plugins to load during startup'), + ('PYTEST_DISABLE_PLUGIN_AUTOLOAD', 'Set to disable plugin auto-loading'), + ('PYTEST_DEBUG', "Set to enable debug tracing of pytest's internals"), ] for name, help in vars: - tw.line(f" {name:<24} {help}") + tw.line(f' {name:<24} {help}') tw.line() tw.line() - tw.line("to see available markers type: pytest --markers") - tw.line("to see available fixtures type: pytest --fixtures") + tw.line('to see available markers type: pytest --markers') + tw.line('to see available fixtures type: pytest --fixtures') tw.line( - "(shown according to specified file_or_dir or current dir " + '(shown according to specified file_or_dir or current dir ' "if not specified; fixtures with leading '_' are only shown " - "with the '-v' option" + "with the '-v' option", ) - for warningreport in reporter.stats.get("warnings", []): - tw.line("warning : " + warningreport.message, red=True) + for warningreport in reporter.stats.get('warnings', []): + tw.line('warning : ' + warningreport.message, red=True) return -conftest_options = [("pytest_plugins", "list of plugin names to load")] +conftest_options = [('pytest_plugins', 'list of plugin names to load')] -def getpluginversioninfo(config: Config) -> List[str]: +def getpluginversioninfo(config: Config) -> list[str]: lines = [] plugininfo = config.pluginmanager.list_plugin_distinfo() if plugininfo: - lines.append("setuptools registered plugins:") + lines.append('setuptools registered plugins:') for plugin, dist in plugininfo: - loc = getattr(plugin, "__file__", repr(plugin)) - content = f"{dist.project_name}-{dist.version} at {loc}" - lines.append(" " + content) + loc = getattr(plugin, '__file__', repr(plugin)) + content = f'{dist.project_name}-{dist.version} at {loc}' + lines.append(' ' + content) return lines -def pytest_report_header(config: Config) -> List[str]: +def pytest_report_header(config: Config) -> list[str]: lines = [] if config.option.debug or config.option.traceconfig: - lines.append(f"using: pytest-{pytest.__version__}") + lines.append(f'using: pytest-{pytest.__version__}') verinfo = getpluginversioninfo(config) if verinfo: lines.extend(verinfo) if config.option.traceconfig: - lines.append("active plugins:") + lines.append('active plugins:') items = config.pluginmanager.list_name_plugin() for name, plugin in items: - if hasattr(plugin, "__file__"): + if hasattr(plugin, '__file__'): r = plugin.__file__ else: r = repr(plugin) - lines.append(f" {name:<20}: {r}") + lines.append(f' {name:<20}: {r}') return lines diff --git a/.venv/lib/python3.10/site-packages/_pytest/hookspec.py b/.venv/lib/python3.10/site-packages/_pytest/hookspec.py index 4bee76f..e46f442 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/hookspec.py +++ b/.venv/lib/python3.10/site-packages/_pytest/hookspec.py @@ -1,6 +1,8 @@ # mypy: allow-untyped-defs """Hook specifications for pytest plugins which are invoked by pytest itself and by builtin plugins.""" +from __future__ import annotations + from pathlib import Path from typing import Any from typing import Dict @@ -45,7 +47,7 @@ if TYPE_CHECKING: from _pytest.terminal import TestShortLogReport -hookspec = HookspecMarker("pytest") +hookspec = HookspecMarker('pytest') # ------------------------------------------------------------------------- # Initialization hooks called for every plugin @@ -53,7 +55,7 @@ hookspec = HookspecMarker("pytest") @hookspec(historic=True) -def pytest_addhooks(pluginmanager: "PytestPluginManager") -> None: +def pytest_addhooks(pluginmanager: PytestPluginManager) -> None: """Called at plugin registration time to allow adding new hooks via a call to :func:`pluginmanager.add_hookspecs(module_or_class, prefix) `. @@ -72,9 +74,9 @@ def pytest_addhooks(pluginmanager: "PytestPluginManager") -> None: @hookspec(historic=True) def pytest_plugin_registered( - plugin: "_PluggyPlugin", + plugin: _PluggyPlugin, plugin_name: str, - manager: "PytestPluginManager", + manager: PytestPluginManager, ) -> None: """A new pytest plugin got registered. @@ -96,7 +98,7 @@ def pytest_plugin_registered( @hookspec(historic=True) -def pytest_addoption(parser: "Parser", pluginmanager: "PytestPluginManager") -> None: +def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None: """Register argparse-style options and ini-style config values, called once at the beginning of a test run. @@ -137,7 +139,7 @@ def pytest_addoption(parser: "Parser", pluginmanager: "PytestPluginManager") -> @hookspec(historic=True) -def pytest_configure(config: "Config") -> None: +def pytest_configure(config: Config) -> None: """Allow plugins and conftest files to perform initial configuration. .. note:: @@ -162,8 +164,8 @@ def pytest_configure(config: "Config") -> None: @hookspec(firstresult=True) def pytest_cmdline_parse( - pluginmanager: "PytestPluginManager", args: List[str] -) -> Optional["Config"]: + pluginmanager: PytestPluginManager, args: list[str], +) -> Config | None: """Return an initialized :class:`~pytest.Config`, parsing the specified args. Stops at first non-None result, see :ref:`firstresult`. @@ -185,7 +187,7 @@ def pytest_cmdline_parse( def pytest_load_initial_conftests( - early_config: "Config", parser: "Parser", args: List[str] + early_config: Config, parser: Parser, args: list[str], ) -> None: """Called to implement the loading of :ref:`initial conftest files ` ahead of command line option parsing. @@ -202,7 +204,7 @@ def pytest_load_initial_conftests( @hookspec(firstresult=True) -def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]: +def pytest_cmdline_main(config: Config) -> ExitCode | int | None: """Called for performing the main command line action. The default implementation will invoke the configure hooks and @@ -226,7 +228,7 @@ def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]: @hookspec(firstresult=True) -def pytest_collection(session: "Session") -> Optional[object]: +def pytest_collection(session: Session) -> object | None: """Perform the collection phase for the given session. Stops at first non-None result, see :ref:`firstresult`. @@ -268,7 +270,7 @@ def pytest_collection(session: "Session") -> Optional[object]: def pytest_collection_modifyitems( - session: "Session", config: "Config", items: List["Item"] + session: Session, config: Config, items: list[Item], ) -> None: """Called after collection has been performed. May filter or re-order the items in-place. @@ -284,7 +286,7 @@ def pytest_collection_modifyitems( """ -def pytest_collection_finish(session: "Session") -> None: +def pytest_collection_finish(session: Session) -> None: """Called after collection has been performed and modified. :param session: The pytest session object. @@ -298,8 +300,8 @@ def pytest_collection_finish(session: "Session") -> None: @hookspec(firstresult=True) def pytest_ignore_collect( - collection_path: Path, path: "LEGACY_PATH", config: "Config" -) -> Optional[bool]: + collection_path: Path, path: LEGACY_PATH, config: Config, +) -> bool | None: """Return True to prevent considering this path for collection. This hook is consulted for all files and directories prior to calling @@ -327,7 +329,7 @@ def pytest_ignore_collect( @hookspec(firstresult=True) -def pytest_collect_directory(path: Path, parent: "Collector") -> "Optional[Collector]": +def pytest_collect_directory(path: Path, parent: Collector) -> Optional[Collector]: """Create a :class:`~pytest.Collector` for the given directory, or None if not relevant. @@ -356,8 +358,8 @@ def pytest_collect_directory(path: Path, parent: "Collector") -> "Optional[Colle def pytest_collect_file( - file_path: Path, path: "LEGACY_PATH", parent: "Collector" -) -> "Optional[Collector]": + file_path: Path, path: LEGACY_PATH, parent: Collector, +) -> Optional[Collector]: """Create a :class:`~pytest.Collector` for the given path, or None if not relevant. For best results, the returned collector should be a subclass of @@ -384,7 +386,7 @@ def pytest_collect_file( # logging hooks for collection -def pytest_collectstart(collector: "Collector") -> None: +def pytest_collectstart(collector: Collector) -> None: """Collector starts collecting. :param collector: @@ -399,7 +401,7 @@ def pytest_collectstart(collector: "Collector") -> None: """ -def pytest_itemcollected(item: "Item") -> None: +def pytest_itemcollected(item: Item) -> None: """We just collected a test item. :param item: @@ -413,7 +415,7 @@ def pytest_itemcollected(item: "Item") -> None: """ -def pytest_collectreport(report: "CollectReport") -> None: +def pytest_collectreport(report: CollectReport) -> None: """Collector finished collecting. :param report: @@ -428,7 +430,7 @@ def pytest_collectreport(report: "CollectReport") -> None: """ -def pytest_deselected(items: Sequence["Item"]) -> None: +def pytest_deselected(items: Sequence[Item]) -> None: """Called for deselected test items, e.g. by keyword. May be called multiple times. @@ -444,7 +446,7 @@ def pytest_deselected(items: Sequence["Item"]) -> None: @hookspec(firstresult=True) -def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectReport]": +def pytest_make_collect_report(collector: Collector) -> Optional[CollectReport]: """Perform :func:`collector.collect() ` and return a :class:`~pytest.CollectReport`. @@ -469,8 +471,8 @@ def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectRepor @hookspec(firstresult=True) def pytest_pycollect_makemodule( - module_path: Path, path: "LEGACY_PATH", parent -) -> Optional["Module"]: + module_path: Path, path: LEGACY_PATH, parent, +) -> Module | None: """Return a :class:`pytest.Module` collector or None for the given path. This hook will be called for each matching test module path. @@ -499,8 +501,8 @@ def pytest_pycollect_makemodule( @hookspec(firstresult=True) def pytest_pycollect_makeitem( - collector: Union["Module", "Class"], name: str, obj: object -) -> Union[None, "Item", "Collector", List[Union["Item", "Collector"]]]: + collector: Module | Class, name: str, obj: object, +) -> None | Item | Collector | list[Item | Collector]: """Return a custom item/collector for a Python object in a module, or None. Stops at first non-None result, see :ref:`firstresult`. @@ -524,7 +526,7 @@ def pytest_pycollect_makeitem( @hookspec(firstresult=True) -def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: +def pytest_pyfunc_call(pyfuncitem: Function) -> object | None: """Call underlying test function. Stops at first non-None result, see :ref:`firstresult`. @@ -541,7 +543,7 @@ def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: """ -def pytest_generate_tests(metafunc: "Metafunc") -> None: +def pytest_generate_tests(metafunc: Metafunc) -> None: """Generate (multiple) parametrized calls to a test function. :param metafunc: @@ -558,8 +560,8 @@ def pytest_generate_tests(metafunc: "Metafunc") -> None: @hookspec(firstresult=True) def pytest_make_parametrize_id( - config: "Config", val: object, argname: str -) -> Optional[str]: + config: Config, val: object, argname: str, +) -> str | None: """Return a user-friendly string representation of the given ``val`` that will be used by @pytest.mark.parametrize calls, or None if the hook doesn't know about ``val``. @@ -585,7 +587,7 @@ def pytest_make_parametrize_id( @hookspec(firstresult=True) -def pytest_runtestloop(session: "Session") -> Optional[object]: +def pytest_runtestloop(session: Session) -> object | None: """Perform the main runtest loop (after collection finished). The default hook implementation performs the runtest protocol for all items @@ -612,8 +614,8 @@ def pytest_runtestloop(session: "Session") -> Optional[object]: @hookspec(firstresult=True) def pytest_runtest_protocol( - item: "Item", nextitem: "Optional[Item]" -) -> Optional[object]: + item: Item, nextitem: Optional[Item], +) -> object | None: """Perform the runtest protocol for a single test item. The default runtest protocol is this (see individual hooks for full details): @@ -654,7 +656,7 @@ def pytest_runtest_protocol( def pytest_runtest_logstart( - nodeid: str, location: Tuple[str, Optional[int], str] + nodeid: str, location: tuple[str, int | None, str], ) -> None: """Called at the start of running the runtest protocol for a single item. @@ -674,7 +676,7 @@ def pytest_runtest_logstart( def pytest_runtest_logfinish( - nodeid: str, location: Tuple[str, Optional[int], str] + nodeid: str, location: tuple[str, int | None, str], ) -> None: """Called at the end of running the runtest protocol for a single item. @@ -693,7 +695,7 @@ def pytest_runtest_logfinish( """ -def pytest_runtest_setup(item: "Item") -> None: +def pytest_runtest_setup(item: Item) -> None: """Called to perform the setup phase for a test item. The default implementation runs ``setup()`` on ``item`` and all of its @@ -712,7 +714,7 @@ def pytest_runtest_setup(item: "Item") -> None: """ -def pytest_runtest_call(item: "Item") -> None: +def pytest_runtest_call(item: Item) -> None: """Called to run the test for test item (the call phase). The default implementation calls ``item.runtest()``. @@ -728,7 +730,7 @@ def pytest_runtest_call(item: "Item") -> None: """ -def pytest_runtest_teardown(item: "Item", nextitem: Optional["Item"]) -> None: +def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: """Called to perform the teardown phase for a test item. The default implementation runs the finalizers and calls ``teardown()`` @@ -754,8 +756,8 @@ def pytest_runtest_teardown(item: "Item", nextitem: Optional["Item"]) -> None: @hookspec(firstresult=True) def pytest_runtest_makereport( - item: "Item", call: "CallInfo[None]" -) -> Optional["TestReport"]: + item: Item, call: CallInfo[None], +) -> TestReport | None: """Called to create a :class:`~pytest.TestReport` for each of the setup, call and teardown runtest phases of a test item. @@ -774,7 +776,7 @@ def pytest_runtest_makereport( """ -def pytest_runtest_logreport(report: "TestReport") -> None: +def pytest_runtest_logreport(report: TestReport) -> None: """Process the :class:`~pytest.TestReport` produced for each of the setup, call and teardown runtest phases of an item. @@ -790,9 +792,9 @@ def pytest_runtest_logreport(report: "TestReport") -> None: @hookspec(firstresult=True) def pytest_report_to_serializable( - config: "Config", - report: Union["CollectReport", "TestReport"], -) -> Optional[Dict[str, Any]]: + config: Config, + report: CollectReport | TestReport, +) -> dict[str, Any] | None: """Serialize the given report object into a data structure suitable for sending over the wire, e.g. converted to JSON. @@ -809,9 +811,9 @@ def pytest_report_to_serializable( @hookspec(firstresult=True) def pytest_report_from_serializable( - config: "Config", - data: Dict[str, Any], -) -> Optional[Union["CollectReport", "TestReport"]]: + config: Config, + data: dict[str, Any], +) -> CollectReport | TestReport | None: """Restore a report object previously serialized with :hook:`pytest_report_to_serializable`. @@ -832,8 +834,8 @@ def pytest_report_from_serializable( @hookspec(firstresult=True) def pytest_fixture_setup( - fixturedef: "FixtureDef[Any]", request: "SubRequest" -) -> Optional[object]: + fixturedef: FixtureDef[Any], request: SubRequest, +) -> object | None: """Perform fixture setup execution. :param fixturdef: @@ -860,7 +862,7 @@ def pytest_fixture_setup( def pytest_fixture_post_finalizer( - fixturedef: "FixtureDef[Any]", request: "SubRequest" + fixturedef: FixtureDef[Any], request: SubRequest, ) -> None: """Called after fixture teardown, but before the cache is cleared, so the fixture result ``fixturedef.cached_result`` is still available (not @@ -885,7 +887,7 @@ def pytest_fixture_post_finalizer( # ------------------------------------------------------------------------- -def pytest_sessionstart(session: "Session") -> None: +def pytest_sessionstart(session: Session) -> None: """Called after the ``Session`` object has been created and before performing collection and entering the run test loop. @@ -899,8 +901,8 @@ def pytest_sessionstart(session: "Session") -> None: def pytest_sessionfinish( - session: "Session", - exitstatus: Union[int, "ExitCode"], + session: Session, + exitstatus: int | ExitCode, ) -> None: """Called after whole test run finished, right before returning the exit status to the system. @@ -914,7 +916,7 @@ def pytest_sessionfinish( """ -def pytest_unconfigure(config: "Config") -> None: +def pytest_unconfigure(config: Config) -> None: """Called before test process is exited. :param config: The pytest config object. @@ -932,8 +934,8 @@ def pytest_unconfigure(config: "Config") -> None: def pytest_assertrepr_compare( - config: "Config", op: str, left: object, right: object -) -> Optional[List[str]]: + config: Config, op: str, left: object, right: object, +) -> list[str] | None: """Return explanation for comparisons in failing assert expressions. Return None for no custom explanation, otherwise return a list @@ -954,7 +956,7 @@ def pytest_assertrepr_compare( """ -def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> None: +def pytest_assertion_pass(item: Item, lineno: int, orig: str, expl: str) -> None: """Called whenever an assertion passes. .. versionadded:: 5.0 @@ -994,8 +996,8 @@ def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> No def pytest_report_header( # type:ignore[empty-body] - config: "Config", start_path: Path, startdir: "LEGACY_PATH" -) -> Union[str, List[str]]: + config: Config, start_path: Path, startdir: LEGACY_PATH, +) -> str | list[str]: """Return a string or list of strings to be displayed as header info for terminal reporting. :param config: The pytest config object. @@ -1022,11 +1024,11 @@ def pytest_report_header( # type:ignore[empty-body] def pytest_report_collectionfinish( # type:ignore[empty-body] - config: "Config", + config: Config, start_path: Path, - startdir: "LEGACY_PATH", - items: Sequence["Item"], -) -> Union[str, List[str]]: + startdir: LEGACY_PATH, + items: Sequence[Item], +) -> str | list[str]: """Return a string or list of strings to be displayed after collection has finished successfully. @@ -1060,8 +1062,8 @@ def pytest_report_collectionfinish( # type:ignore[empty-body] @hookspec(firstresult=True) def pytest_report_teststatus( # type:ignore[empty-body] - report: Union["CollectReport", "TestReport"], config: "Config" -) -> "TestShortLogReport | Tuple[str, str, Union[str, Tuple[str, Mapping[str, bool]]]]": + report: CollectReport | TestReport, config: Config, +) -> TestShortLogReport | Tuple[str, str, Union[str, Tuple[str, Mapping[str, bool]]]]: """Return result-category, shortletter and verbose word for status reporting. @@ -1092,9 +1094,9 @@ def pytest_report_teststatus( # type:ignore[empty-body] def pytest_terminal_summary( - terminalreporter: "TerminalReporter", - exitstatus: "ExitCode", - config: "Config", + terminalreporter: TerminalReporter, + exitstatus: ExitCode, + config: Config, ) -> None: """Add a section to terminal summary reporting. @@ -1114,10 +1116,10 @@ def pytest_terminal_summary( @hookspec(historic=True) def pytest_warning_recorded( - warning_message: "warnings.WarningMessage", - when: "Literal['config', 'collect', 'runtest']", + warning_message: warnings.WarningMessage, + when: Literal['config', 'collect', 'runtest'], nodeid: str, - location: Optional[Tuple[str, int, str]], + location: tuple[str, int, str] | None, ) -> None: """Process a warning captured by the internal pytest warnings plugin. @@ -1158,8 +1160,8 @@ def pytest_warning_recorded( def pytest_markeval_namespace( # type:ignore[empty-body] - config: "Config", -) -> Dict[str, Any]: + config: Config, +) -> dict[str, Any]: """Called when constructing the globals dictionary used for evaluating string conditions in xfail/skipif markers. @@ -1187,9 +1189,9 @@ def pytest_markeval_namespace( # type:ignore[empty-body] def pytest_internalerror( - excrepr: "ExceptionRepr", - excinfo: "ExceptionInfo[BaseException]", -) -> Optional[bool]: + excrepr: ExceptionRepr, + excinfo: ExceptionInfo[BaseException], +) -> bool | None: """Called for internal errors. Return True to suppress the fallback handling of printing an @@ -1206,7 +1208,7 @@ def pytest_internalerror( def pytest_keyboard_interrupt( - excinfo: "ExceptionInfo[Union[KeyboardInterrupt, Exit]]", + excinfo: ExceptionInfo[Union[KeyboardInterrupt, Exit]], ) -> None: """Called for keyboard interrupt. @@ -1220,9 +1222,9 @@ def pytest_keyboard_interrupt( def pytest_exception_interact( - node: Union["Item", "Collector"], - call: "CallInfo[Any]", - report: Union["CollectReport", "TestReport"], + node: Item | Collector, + call: CallInfo[Any], + report: CollectReport | TestReport, ) -> None: """Called when an exception was raised which can potentially be interactively handled. @@ -1251,7 +1253,7 @@ def pytest_exception_interact( """ -def pytest_enter_pdb(config: "Config", pdb: "pdb.Pdb") -> None: +def pytest_enter_pdb(config: Config, pdb: pdb.Pdb) -> None: """Called upon pdb.set_trace(). Can be used by plugins to take special action just before the python @@ -1267,7 +1269,7 @@ def pytest_enter_pdb(config: "Config", pdb: "pdb.Pdb") -> None: """ -def pytest_leave_pdb(config: "Config", pdb: "pdb.Pdb") -> None: +def pytest_leave_pdb(config: Config, pdb: pdb.Pdb) -> None: """Called when leaving pdb (e.g. with continue after pdb.set_trace()). Can be used by plugins to take special action just after the python diff --git a/.venv/lib/python3.10/site-packages/_pytest/junitxml.py b/.venv/lib/python3.10/site-packages/_pytest/junitxml.py index 4ca356f..09ade64 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/junitxml.py +++ b/.venv/lib/python3.10/site-packages/_pytest/junitxml.py @@ -7,11 +7,14 @@ Based on initial code from Ross Lawley. Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd """ -from datetime import datetime +from __future__ import annotations + import functools import os import platform import re +import xml.etree.ElementTree as ET +from datetime import datetime from typing import Callable from typing import Dict from typing import List @@ -19,8 +22,8 @@ from typing import Match from typing import Optional from typing import Tuple from typing import Union -import xml.etree.ElementTree as ET +import pytest from _pytest import nodes from _pytest import timing from _pytest._code.code import ExceptionRepr @@ -32,10 +35,9 @@ from _pytest.fixtures import FixtureRequest from _pytest.reports import TestReport from _pytest.stash import StashKey from _pytest.terminal import TerminalReporter -import pytest -xml_key = StashKey["LogXML"]() +xml_key = StashKey['LogXML']() def bin_xml_escape(arg: object) -> str: @@ -52,15 +54,15 @@ def bin_xml_escape(arg: object) -> str: def repl(matchobj: Match[str]) -> str: i = ord(matchobj.group()) if i <= 0xFF: - return "#x%02X" % i + return '#x%02X' % i else: - return "#x%04X" % i + return '#x%04X' % i # The spec range of valid chars is: # Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] # For an unknown(?) reason, we disallow #x7F (DEL) as well. illegal_xml_re = ( - "[^\u0009\u000A\u000D\u0020-\u007E\u0080-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]" + '[^\u0009\u000A\u000D\u0020-\u007E\u0080-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]' ) return re.sub(illegal_xml_re, repl, str(arg)) @@ -76,27 +78,27 @@ def merge_family(left, right) -> None: families = {} -families["_base"] = {"testcase": ["classname", "name"]} -families["_base_legacy"] = {"testcase": ["file", "line", "url"]} +families['_base'] = {'testcase': ['classname', 'name']} +families['_base_legacy'] = {'testcase': ['file', 'line', 'url']} # xUnit 1.x inherits legacy attributes. -families["xunit1"] = families["_base"].copy() -merge_family(families["xunit1"], families["_base_legacy"]) +families['xunit1'] = families['_base'].copy() +merge_family(families['xunit1'], families['_base_legacy']) # xUnit 2.x uses strict base attributes. -families["xunit2"] = families["_base"] +families['xunit2'] = families['_base'] class _NodeReporter: - def __init__(self, nodeid: Union[str, TestReport], xml: "LogXML") -> None: + def __init__(self, nodeid: str | TestReport, xml: LogXML) -> None: self.id = nodeid self.xml = xml self.add_stats = self.xml.add_stats self.family = self.xml.family self.duration = 0.0 - self.properties: List[Tuple[str, str]] = [] - self.nodes: List[ET.Element] = [] - self.attrs: Dict[str, str] = {} + self.properties: list[tuple[str, str]] = [] + self.nodes: list[ET.Element] = [] + self.attrs: dict[str, str] = {} def append(self, node: ET.Element) -> None: self.xml.add_stats(node.tag) @@ -108,12 +110,12 @@ class _NodeReporter: def add_attribute(self, name: str, value: object) -> None: self.attrs[str(name)] = bin_xml_escape(value) - def make_properties_node(self) -> Optional[ET.Element]: + def make_properties_node(self) -> ET.Element | None: """Return a Junit node containing custom properties, if any.""" if self.properties: - properties = ET.Element("properties") + properties = ET.Element('properties') for name, value in self.properties: - properties.append(ET.Element("property", name=name, value=value)) + properties.append(ET.Element('property', name=name, value=value)) return properties return None @@ -123,39 +125,39 @@ class _NodeReporter: classnames = names[:-1] if self.xml.prefix: classnames.insert(0, self.xml.prefix) - attrs: Dict[str, str] = { - "classname": ".".join(classnames), - "name": bin_xml_escape(names[-1]), - "file": testreport.location[0], + attrs: dict[str, str] = { + 'classname': '.'.join(classnames), + 'name': bin_xml_escape(names[-1]), + 'file': testreport.location[0], } if testreport.location[1] is not None: - attrs["line"] = str(testreport.location[1]) - if hasattr(testreport, "url"): - attrs["url"] = testreport.url + attrs['line'] = str(testreport.location[1]) + if hasattr(testreport, 'url'): + attrs['url'] = testreport.url self.attrs = attrs self.attrs.update(existing_attrs) # Restore any user-defined attributes. # Preserve legacy testcase behavior. - if self.family == "xunit1": + if self.family == 'xunit1': return # Filter out attributes not permitted by this test family. # Including custom attributes because they are not valid here. temp_attrs = {} for key in self.attrs.keys(): - if key in families[self.family]["testcase"]: + if key in families[self.family]['testcase']: temp_attrs[key] = self.attrs[key] self.attrs = temp_attrs def to_xml(self) -> ET.Element: - testcase = ET.Element("testcase", self.attrs, time="%.3f" % self.duration) + testcase = ET.Element('testcase', self.attrs, time='%.3f' % self.duration) properties = self.make_properties_node() if properties is not None: testcase.append(properties) testcase.extend(self.nodes) return testcase - def _add_simple(self, tag: str, message: str, data: Optional[str] = None) -> None: + def _add_simple(self, tag: str, message: str, data: str | None = None) -> None: node = ET.Element(tag, message=message) node.text = bin_xml_escape(data) self.append(node) @@ -167,24 +169,24 @@ class _NodeReporter: content_out = report.capstdout content_log = report.caplog content_err = report.capstderr - if self.xml.logging == "no": + if self.xml.logging == 'no': return - content_all = "" - if self.xml.logging in ["log", "all"]: - content_all = self._prepare_content(content_log, " Captured Log ") - if self.xml.logging in ["system-out", "out-err", "all"]: - content_all += self._prepare_content(content_out, " Captured Out ") - self._write_content(report, content_all, "system-out") - content_all = "" - if self.xml.logging in ["system-err", "out-err", "all"]: - content_all += self._prepare_content(content_err, " Captured Err ") - self._write_content(report, content_all, "system-err") - content_all = "" + content_all = '' + if self.xml.logging in ['log', 'all']: + content_all = self._prepare_content(content_log, ' Captured Log ') + if self.xml.logging in ['system-out', 'out-err', 'all']: + content_all += self._prepare_content(content_out, ' Captured Out ') + self._write_content(report, content_all, 'system-out') + content_all = '' + if self.xml.logging in ['system-err', 'out-err', 'all']: + content_all += self._prepare_content(content_err, ' Captured Err ') + self._write_content(report, content_all, 'system-err') + content_all = '' if content_all: - self._write_content(report, content_all, "system-out") + self._write_content(report, content_all, 'system-out') def _prepare_content(self, content: str, header: str) -> str: - return "\n".join([header.center(80, "-"), content, ""]) + return '\n'.join([header.center(80, '-'), content, '']) def _write_content(self, report: TestReport, content: str, jheader: str) -> None: tag = ET.Element(jheader) @@ -192,65 +194,65 @@ class _NodeReporter: self.append(tag) def append_pass(self, report: TestReport) -> None: - self.add_stats("passed") + self.add_stats('passed') def append_failure(self, report: TestReport) -> None: # msg = str(report.longrepr.reprtraceback.extraline) - if hasattr(report, "wasxfail"): - self._add_simple("skipped", "xfail-marked test passes unexpectedly") + if hasattr(report, 'wasxfail'): + self._add_simple('skipped', 'xfail-marked test passes unexpectedly') else: assert report.longrepr is not None - reprcrash: Optional[ReprFileLocation] = getattr( - report.longrepr, "reprcrash", None + reprcrash: ReprFileLocation | None = getattr( + report.longrepr, 'reprcrash', None, ) if reprcrash is not None: message = reprcrash.message else: message = str(report.longrepr) message = bin_xml_escape(message) - self._add_simple("failure", message, str(report.longrepr)) + self._add_simple('failure', message, str(report.longrepr)) def append_collect_error(self, report: TestReport) -> None: # msg = str(report.longrepr.reprtraceback.extraline) assert report.longrepr is not None - self._add_simple("error", "collection failure", str(report.longrepr)) + self._add_simple('error', 'collection failure', str(report.longrepr)) def append_collect_skipped(self, report: TestReport) -> None: - self._add_simple("skipped", "collection skipped", str(report.longrepr)) + self._add_simple('skipped', 'collection skipped', str(report.longrepr)) def append_error(self, report: TestReport) -> None: assert report.longrepr is not None - reprcrash: Optional[ReprFileLocation] = getattr( - report.longrepr, "reprcrash", None + reprcrash: ReprFileLocation | None = getattr( + report.longrepr, 'reprcrash', None, ) if reprcrash is not None: reason = reprcrash.message else: reason = str(report.longrepr) - if report.when == "teardown": + if report.when == 'teardown': msg = f'failed on teardown with "{reason}"' else: msg = f'failed on setup with "{reason}"' - self._add_simple("error", bin_xml_escape(msg), str(report.longrepr)) + self._add_simple('error', bin_xml_escape(msg), str(report.longrepr)) def append_skipped(self, report: TestReport) -> None: - if hasattr(report, "wasxfail"): + if hasattr(report, 'wasxfail'): xfailreason = report.wasxfail - if xfailreason.startswith("reason: "): + if xfailreason.startswith('reason: '): xfailreason = xfailreason[8:] xfailreason = bin_xml_escape(xfailreason) - skipped = ET.Element("skipped", type="pytest.xfail", message=xfailreason) + skipped = ET.Element('skipped', type='pytest.xfail', message=xfailreason) self.append(skipped) else: assert isinstance(report.longrepr, tuple) filename, lineno, skipreason = report.longrepr - if skipreason.startswith("Skipped: "): + if skipreason.startswith('Skipped: '): skipreason = skipreason[9:] - details = f"{filename}:{lineno}: {skipreason}" + details = f'{filename}:{lineno}: {skipreason}' skipped = ET.Element( - "skipped", type="pytest.skip", message=bin_xml_escape(skipreason) + 'skipped', type='pytest.skip', message=bin_xml_escape(skipreason), ) skipped.text = bin_xml_escape(details) self.append(skipped) @@ -265,17 +267,17 @@ class _NodeReporter: def _warn_incompatibility_with_xunit2( - request: FixtureRequest, fixture_name: str + request: FixtureRequest, fixture_name: str, ) -> None: """Emit a PytestWarning about the given fixture being incompatible with newer xunit revisions.""" from _pytest.warning_types import PytestWarning xml = request.config.stash.get(xml_key, None) - if xml is not None and xml.family not in ("xunit1", "legacy"): + if xml is not None and xml.family not in ('xunit1', 'legacy'): request.node.warn( PytestWarning( - f"{fixture_name} is incompatible with junit_family '{xml.family}' (use 'legacy' or 'xunit1')" - ) + f"{fixture_name} is incompatible with junit_family '{xml.family}' (use 'legacy' or 'xunit1')", + ), ) @@ -294,7 +296,7 @@ def record_property(request: FixtureRequest) -> Callable[[str, object], None]: def test_function(record_property): record_property("example_key", 1) """ - _warn_incompatibility_with_xunit2(request, "record_property") + _warn_incompatibility_with_xunit2(request, 'record_property') def append_property(name: str, value: object) -> None: request.node.user_properties.append((name, value)) @@ -312,10 +314,10 @@ def record_xml_attribute(request: FixtureRequest) -> Callable[[str, object], Non from _pytest.warning_types import PytestExperimentalApiWarning request.node.warn( - PytestExperimentalApiWarning("record_xml_attribute is an experimental feature") + PytestExperimentalApiWarning('record_xml_attribute is an experimental feature'), ) - _warn_incompatibility_with_xunit2(request, "record_xml_attribute") + _warn_incompatibility_with_xunit2(request, 'record_xml_attribute') # Declare noop def add_attr_noop(name: str, value: object) -> None: @@ -336,11 +338,11 @@ def _check_record_param_type(param: str, v: str) -> None: type.""" __tracebackhide__ = True if not isinstance(v, str): - msg = "{param} parameter needs to be a string, but {g} given" # type: ignore[unreachable] + msg = '{param} parameter needs to be a string, but {g} given' # type: ignore[unreachable] raise TypeError(msg.format(param=param, g=type(v).__name__)) -@pytest.fixture(scope="session") +@pytest.fixture(scope='session') def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object], None]: """Record a new ```` tag as child of the root ````. @@ -371,7 +373,7 @@ def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object] def record_func(name: str, value: object) -> None: """No-op function in case --junit-xml was not passed in the command-line.""" __tracebackhide__ = True - _check_record_param_type("name", name) + _check_record_param_type('name', name) xml = request.config.stash.get(xml_key, None) if xml is not None: @@ -380,65 +382,65 @@ def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object] def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("terminal reporting") + group = parser.getgroup('terminal reporting') group.addoption( - "--junitxml", - "--junit-xml", - action="store", - dest="xmlpath", - metavar="path", - type=functools.partial(filename_arg, optname="--junitxml"), + '--junitxml', + '--junit-xml', + action='store', + dest='xmlpath', + metavar='path', + type=functools.partial(filename_arg, optname='--junitxml'), default=None, - help="Create junit-xml style report file at given path", + help='Create junit-xml style report file at given path', ) group.addoption( - "--junitprefix", - "--junit-prefix", - action="store", - metavar="str", + '--junitprefix', + '--junit-prefix', + action='store', + metavar='str', default=None, - help="Prepend prefix to classnames in junit-xml output", + help='Prepend prefix to classnames in junit-xml output', ) parser.addini( - "junit_suite_name", "Test suite name for JUnit report", default="pytest" + 'junit_suite_name', 'Test suite name for JUnit report', default='pytest', ) parser.addini( - "junit_logging", - "Write captured log messages to JUnit report: " - "one of no|log|system-out|system-err|out-err|all", - default="no", + 'junit_logging', + 'Write captured log messages to JUnit report: ' + 'one of no|log|system-out|system-err|out-err|all', + default='no', ) parser.addini( - "junit_log_passing_tests", - "Capture log information for passing tests to JUnit report: ", - type="bool", + 'junit_log_passing_tests', + 'Capture log information for passing tests to JUnit report: ', + type='bool', default=True, ) parser.addini( - "junit_duration_report", - "Duration time to report: one of total|call", - default="total", + 'junit_duration_report', + 'Duration time to report: one of total|call', + default='total', ) # choices=['total', 'call']) parser.addini( - "junit_family", - "Emit XML for schema: one of legacy|xunit1|xunit2", - default="xunit2", + 'junit_family', + 'Emit XML for schema: one of legacy|xunit1|xunit2', + default='xunit2', ) def pytest_configure(config: Config) -> None: xmlpath = config.option.xmlpath # Prevent opening xmllog on worker nodes (xdist). - if xmlpath and not hasattr(config, "workerinput"): - junit_family = config.getini("junit_family") + if xmlpath and not hasattr(config, 'workerinput'): + junit_family = config.getini('junit_family') config.stash[xml_key] = LogXML( xmlpath, config.option.junitprefix, - config.getini("junit_suite_name"), - config.getini("junit_logging"), - config.getini("junit_duration_report"), + config.getini('junit_suite_name'), + config.getini('junit_logging'), + config.getini('junit_duration_report'), junit_family, - config.getini("junit_log_passing_tests"), + config.getini('junit_log_passing_tests'), ) config.pluginmanager.register(config.stash[xml_key]) @@ -450,12 +452,12 @@ def pytest_unconfigure(config: Config) -> None: config.pluginmanager.unregister(xml) -def mangle_test_address(address: str) -> List[str]: - path, possible_open_bracket, params = address.partition("[") - names = path.split("::") +def mangle_test_address(address: str) -> list[str]: + path, possible_open_bracket, params = address.partition('[') + names = path.split('::') # Convert file path to dotted path. - names[0] = names[0].replace(nodes.SEP, ".") - names[0] = re.sub(r"\.py$", "", names[0]) + names[0] = names[0].replace(nodes.SEP, '.') + names[0] = re.sub(r'\.py$', '', names[0]) # Put any params back. names[-1] += possible_open_bracket + params return names @@ -465,11 +467,11 @@ class LogXML: def __init__( self, logfile, - prefix: Optional[str], - suite_name: str = "pytest", - logging: str = "no", - report_duration: str = "total", - family="xunit1", + prefix: str | None, + suite_name: str = 'pytest', + logging: str = 'no', + report_duration: str = 'total', + family='xunit1', log_passing_tests: bool = True, ) -> None: logfile = os.path.expanduser(os.path.expandvars(logfile)) @@ -480,27 +482,27 @@ class LogXML: self.log_passing_tests = log_passing_tests self.report_duration = report_duration self.family = family - self.stats: Dict[str, int] = dict.fromkeys( - ["error", "passed", "failure", "skipped"], 0 + self.stats: dict[str, int] = dict.fromkeys( + ['error', 'passed', 'failure', 'skipped'], 0, ) - self.node_reporters: Dict[ - Tuple[Union[str, TestReport], object], _NodeReporter + self.node_reporters: dict[ + tuple[str | TestReport, object], _NodeReporter, ] = {} - self.node_reporters_ordered: List[_NodeReporter] = [] - self.global_properties: List[Tuple[str, str]] = [] + self.node_reporters_ordered: list[_NodeReporter] = [] + self.global_properties: list[tuple[str, str]] = [] # List of reports that failed on call but teardown is pending. - self.open_reports: List[TestReport] = [] + self.open_reports: list[TestReport] = [] self.cnt_double_fail_tests = 0 # Replaces convenience family with real family. - if self.family == "legacy": - self.family = "xunit1" + if self.family == 'legacy': + self.family = 'xunit1' def finalize(self, report: TestReport) -> None: - nodeid = getattr(report, "nodeid", report) + nodeid = getattr(report, 'nodeid', report) # Local hack to handle xdist report order. - workernode = getattr(report, "node", None) + workernode = getattr(report, 'node', None) reporter = self.node_reporters.pop((nodeid, workernode)) for propname, propvalue in report.user_properties: @@ -509,10 +511,10 @@ class LogXML: if reporter is not None: reporter.finalize() - def node_reporter(self, report: Union[TestReport, str]) -> _NodeReporter: - nodeid: Union[str, TestReport] = getattr(report, "nodeid", report) + def node_reporter(self, report: TestReport | str) -> _NodeReporter: + nodeid: str | TestReport = getattr(report, 'nodeid', report) # Local hack to handle xdist report order. - workernode = getattr(report, "node", None) + workernode = getattr(report, 'node', None) key = nodeid, workernode @@ -561,22 +563,22 @@ class LogXML: """ close_report = None if report.passed: - if report.when == "call": # ignore setup/teardown + if report.when == 'call': # ignore setup/teardown reporter = self._opentestcase(report) reporter.append_pass(report) elif report.failed: - if report.when == "teardown": + if report.when == 'teardown': # The following vars are needed when xdist plugin is used. - report_wid = getattr(report, "worker_id", None) - report_ii = getattr(report, "item_index", None) + report_wid = getattr(report, 'worker_id', None) + report_ii = getattr(report, 'item_index', None) close_report = next( ( rep for rep in self.open_reports if ( - rep.nodeid == report.nodeid - and getattr(rep, "item_index", None) == report_ii - and getattr(rep, "worker_id", None) == report_wid + rep.nodeid == report.nodeid and + getattr(rep, 'item_index', None) == report_ii and + getattr(rep, 'worker_id', None) == report_wid ) ), None, @@ -588,7 +590,7 @@ class LogXML: self.finalize(close_report) self.cnt_double_fail_tests += 1 reporter = self._opentestcase(report) - if report.when == "call": + if report.when == 'call': reporter.append_failure(report) self.open_reports.append(report) if not self.log_passing_tests: @@ -599,21 +601,21 @@ class LogXML: reporter = self._opentestcase(report) reporter.append_skipped(report) self.update_testcase_duration(report) - if report.when == "teardown": + if report.when == 'teardown': reporter = self._opentestcase(report) reporter.write_captured_output(report) self.finalize(report) - report_wid = getattr(report, "worker_id", None) - report_ii = getattr(report, "item_index", None) + report_wid = getattr(report, 'worker_id', None) + report_ii = getattr(report, 'item_index', None) close_report = next( ( rep for rep in self.open_reports if ( - rep.nodeid == report.nodeid - and getattr(rep, "item_index", None) == report_ii - and getattr(rep, "worker_id", None) == report_wid + rep.nodeid == report.nodeid and + getattr(rep, 'item_index', None) == report_ii and + getattr(rep, 'worker_id', None) == report_wid ) ), None, @@ -624,9 +626,9 @@ class LogXML: def update_testcase_duration(self, report: TestReport) -> None: """Accumulate total duration for nodeid from given report and update the Junit.testcase with the new total if already created.""" - if self.report_duration in {"total", report.when}: + if self.report_duration in {'total', report.when}: reporter = self.node_reporter(report) - reporter.duration += getattr(report, "duration", 0.0) + reporter.duration += getattr(report, 'duration', 0.0) def pytest_collectreport(self, report: TestReport) -> None: if not report.passed: @@ -637,9 +639,9 @@ class LogXML: reporter.append_collect_skipped(report) def pytest_internalerror(self, excrepr: ExceptionRepr) -> None: - reporter = self.node_reporter("internal") - reporter.attrs.update(classname="pytest", name="internal") - reporter._add_simple("error", "internal error", str(excrepr)) + reporter = self.node_reporter('internal') + reporter.attrs.update(classname='pytest', name='internal') + reporter._add_simple('error', 'internal error', str(excrepr)) def pytest_sessionstart(self) -> None: self.suite_start_time = timing.time() @@ -649,27 +651,27 @@ class LogXML: # exist_ok avoids filesystem race conditions between checking path existence and requesting creation os.makedirs(dirname, exist_ok=True) - with open(self.logfile, "w", encoding="utf-8") as logfile: + with open(self.logfile, 'w', encoding='utf-8') as logfile: suite_stop_time = timing.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = ( - self.stats["passed"] - + self.stats["failure"] - + self.stats["skipped"] - + self.stats["error"] - - self.cnt_double_fail_tests + self.stats['passed'] + + self.stats['failure'] + + self.stats['skipped'] + + self.stats['error'] - + self.cnt_double_fail_tests ) logfile.write('') suite_node = ET.Element( - "testsuite", + 'testsuite', name=self.suite_name, - errors=str(self.stats["error"]), - failures=str(self.stats["failure"]), - skipped=str(self.stats["skipped"]), + errors=str(self.stats['error']), + failures=str(self.stats['failure']), + skipped=str(self.stats['skipped']), tests=str(numtests), - time="%.3f" % suite_time_delta, + time='%.3f' % suite_time_delta, timestamp=datetime.fromtimestamp(self.suite_start_time).isoformat(), hostname=platform.node(), ) @@ -678,23 +680,23 @@ class LogXML: suite_node.append(global_properties) for node_reporter in self.node_reporters_ordered: suite_node.append(node_reporter.to_xml()) - testsuites = ET.Element("testsuites") + testsuites = ET.Element('testsuites') testsuites.append(suite_node) - logfile.write(ET.tostring(testsuites, encoding="unicode")) + logfile.write(ET.tostring(testsuites, encoding='unicode')) def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None: - terminalreporter.write_sep("-", f"generated xml file: {self.logfile}") + terminalreporter.write_sep('-', f'generated xml file: {self.logfile}') def add_global_property(self, name: str, value: object) -> None: __tracebackhide__ = True - _check_record_param_type("name", name) + _check_record_param_type('name', name) self.global_properties.append((name, bin_xml_escape(value))) - def _get_global_properties_node(self) -> Optional[ET.Element]: + def _get_global_properties_node(self) -> ET.Element | None: """Return a Junit node containing custom properties, if any.""" if self.global_properties: - properties = ET.Element("properties") + properties = ET.Element('properties') for name, value in self.global_properties: - properties.append(ET.Element("property", name=name, value=value)) + properties.append(ET.Element('property', name=name, value=value)) return properties return None diff --git a/.venv/lib/python3.10/site-packages/_pytest/legacypath.py b/.venv/lib/python3.10/site-packages/_pytest/legacypath.py index b28c897..bd8e1ed 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/legacypath.py +++ b/.venv/lib/python3.10/site-packages/_pytest/legacypath.py @@ -1,10 +1,11 @@ # mypy: allow-untyped-defs """Add backward compatibility support for the legacy py path type.""" +from __future__ import annotations import dataclasses -from pathlib import Path import shlex import subprocess +from pathlib import Path from typing import Final from typing import final from typing import List @@ -12,8 +13,6 @@ from typing import Optional from typing import TYPE_CHECKING from typing import Union -from iniconfig import SectionWrapper - from _pytest.cacheprovider import Cache from _pytest.compat import LEGACY_PATH from _pytest.compat import legacy_path @@ -33,6 +32,7 @@ from _pytest.pytester import Pytester from _pytest.pytester import RunResult from _pytest.terminal import TerminalReporter from _pytest.tmpdir import TempPathFactory +from iniconfig import SectionWrapper if TYPE_CHECKING: @@ -50,8 +50,8 @@ class Testdir: __test__ = False - CLOSE_STDIN: "Final" = Pytester.CLOSE_STDIN - TimeoutExpired: "Final" = Pytester.TimeoutExpired + CLOSE_STDIN: Final = Pytester.CLOSE_STDIN + TimeoutExpired: Final = Pytester.TimeoutExpired def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: check_ispytest(_ispytest) @@ -95,14 +95,14 @@ class Testdir: def makefile(self, ext, *args, **kwargs) -> LEGACY_PATH: """See :meth:`Pytester.makefile`.""" - if ext and not ext.startswith("."): + if ext and not ext.startswith('.'): # pytester.makefile is going to throw a ValueError in a way that # testdir.makefile did not, because # pathlib.Path is stricter suffixes than py.path # This ext arguments is likely user error, but since testdir has # allowed this, we will prepend "." as a workaround to avoid breaking # testdir usage that worked before - ext = "." + ext + ext = '.' + ext return legacy_path(self._pytester.makefile(ext, *args, **kwargs)) def makeconftest(self, source) -> LEGACY_PATH: @@ -145,7 +145,7 @@ class Testdir: """See :meth:`Pytester.copy_example`.""" return legacy_path(self._pytester.copy_example(name)) - def getnode(self, config: Config, arg) -> Optional[Union[Item, Collector]]: + def getnode(self, config: Config, arg) -> Item | Collector | None: """See :meth:`Pytester.getnode`.""" return self._pytester.getnode(config, arg) @@ -153,7 +153,7 @@ class Testdir: """See :meth:`Pytester.getpathnode`.""" return self._pytester.getpathnode(path) - def genitems(self, colitems: List[Union[Item, Collector]]) -> List[Item]: + def genitems(self, colitems: list[Item | Collector]) -> list[Item]: """See :meth:`Pytester.genitems`.""" return self._pytester.genitems(colitems) @@ -172,7 +172,7 @@ class Testdir: def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False): """See :meth:`Pytester.inline_run`.""" return self._pytester.inline_run( - *args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc + *args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc, ) def runpytest_inprocess(self, *args, **kwargs) -> RunResult: @@ -191,7 +191,7 @@ class Testdir: """See :meth:`Pytester.parseconfigure`.""" return self._pytester.parseconfigure(*args) - def getitem(self, source, funcname="test_func"): + def getitem(self, source, funcname='test_func'): """See :meth:`Pytester.getitem`.""" return self._pytester.getitem(source, funcname) @@ -202,12 +202,12 @@ class Testdir: def getmodulecol(self, source, configargs=(), withinit=False): """See :meth:`Pytester.getmodulecol`.""" return self._pytester.getmodulecol( - source, configargs=configargs, withinit=withinit + source, configargs=configargs, withinit=withinit, ) def collect_by_name( - self, modcol: Collector, name: str - ) -> Optional[Union[Item, Collector]]: + self, modcol: Collector, name: str, + ) -> Item | Collector | None: """See :meth:`Pytester.collect_by_name`.""" return self._pytester.collect_by_name(modcol, name) @@ -239,17 +239,17 @@ class Testdir: return self._pytester.runpytest_subprocess(*args, timeout=timeout) def spawn_pytest( - self, string: str, expect_timeout: float = 10.0 - ) -> "pexpect.spawn": + self, string: str, expect_timeout: float = 10.0, + ) -> pexpect.spawn: """See :meth:`Pytester.spawn_pytest`.""" return self._pytester.spawn_pytest(string, expect_timeout=expect_timeout) - def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn: """See :meth:`Pytester.spawn`.""" return self._pytester.spawn(cmd, expect_timeout=expect_timeout) def __repr__(self) -> str: - return f"" + return f'' def __str__(self) -> str: return str(self.tmpdir) @@ -284,7 +284,7 @@ class TempdirFactory: _tmppath_factory: TempPathFactory def __init__( - self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False + self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False, ) -> None: check_ispytest(_ispytest) self._tmppath_factory = tmppath_factory @@ -300,7 +300,7 @@ class TempdirFactory: class LegacyTmpdirPlugin: @staticmethod - @fixture(scope="session") + @fixture(scope='session') def tmpdir_factory(request: FixtureRequest) -> TempdirFactory: """Return a :class:`pytest.TempdirFactory` instance for the test session.""" # Set dynamically by pytest_configure(). @@ -374,7 +374,7 @@ def Config_rootdir(self: Config) -> LEGACY_PATH: return legacy_path(str(self.rootpath)) -def Config_inifile(self: Config) -> Optional[LEGACY_PATH]: +def Config_inifile(self: Config) -> LEGACY_PATH | None: """The path to the :ref:`configfile `. Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`. @@ -395,16 +395,16 @@ def Session_stardir(self: Session) -> LEGACY_PATH: def Config__getini_unknown_type( - self, name: str, type: str, value: Union[str, List[str]] + self, name: str, type: str, value: str | list[str], ): - if type == "pathlist": + if type == 'pathlist': # TODO: This assert is probably not valid in all cases. assert self.inipath is not None dp = self.inipath.parent input_values = shlex.split(value) if isinstance(value, str) else value return [legacy_path(str(dp / x)) for x in input_values] else: - raise ValueError(f"unknown configuration type: {type}", value) + raise ValueError(f'unknown configuration type: {type}', value) def Node_fspath(self: Node) -> LEGACY_PATH: @@ -423,35 +423,35 @@ def pytest_load_initial_conftests(early_config: Config) -> None: early_config.add_cleanup(mp.undo) # Add Cache.makedir(). - mp.setattr(Cache, "makedir", Cache_makedir, raising=False) + mp.setattr(Cache, 'makedir', Cache_makedir, raising=False) # Add FixtureRequest.fspath property. - mp.setattr(FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False) + mp.setattr(FixtureRequest, 'fspath', property(FixtureRequest_fspath), raising=False) # Add TerminalReporter.startdir property. mp.setattr( - TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False + TerminalReporter, 'startdir', property(TerminalReporter_startdir), raising=False, ) # Add Config.{invocation_dir,rootdir,inifile} properties. - mp.setattr(Config, "invocation_dir", property(Config_invocation_dir), raising=False) - mp.setattr(Config, "rootdir", property(Config_rootdir), raising=False) - mp.setattr(Config, "inifile", property(Config_inifile), raising=False) + mp.setattr(Config, 'invocation_dir', property(Config_invocation_dir), raising=False) + mp.setattr(Config, 'rootdir', property(Config_rootdir), raising=False) + mp.setattr(Config, 'inifile', property(Config_inifile), raising=False) # Add Session.startdir property. - mp.setattr(Session, "startdir", property(Session_stardir), raising=False) + mp.setattr(Session, 'startdir', property(Session_stardir), raising=False) # Add pathlist configuration type. - mp.setattr(Config, "_getini_unknown_type", Config__getini_unknown_type) + mp.setattr(Config, '_getini_unknown_type', Config__getini_unknown_type) # Add Node.fspath property. - mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False) + mp.setattr(Node, 'fspath', property(Node_fspath, Node_fspath_set), raising=False) @hookimpl def pytest_configure(config: Config) -> None: """Installs the LegacyTmpdirPlugin if the ``tmpdir`` plugin is also installed.""" - if config.pluginmanager.has_plugin("tmpdir"): + if config.pluginmanager.has_plugin('tmpdir'): mp = MonkeyPatch() config.add_cleanup(mp.undo) # Create TmpdirFactory and attach it to the config object. @@ -466,15 +466,15 @@ def pytest_configure(config: Config) -> None: pass else: _tmpdirhandler = TempdirFactory(tmp_path_factory, _ispytest=True) - mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False) + mp.setattr(config, '_tmpdirhandler', _tmpdirhandler, raising=False) - config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir") + config.pluginmanager.register(LegacyTmpdirPlugin, 'legacypath-tmpdir') @hookimpl def pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None: # pytester is not loaded by default and is commonly loaded from a conftest, # so checking for it in `pytest_configure` is not enough. - is_pytester = plugin is manager.get_plugin("pytester") + is_pytester = plugin is manager.get_plugin('pytester') if is_pytester and not manager.is_registered(LegacyTestdirPlugin): - manager.register(LegacyTestdirPlugin, "legacypath-pytester") + manager.register(LegacyTestdirPlugin, 'legacypath-pytester') diff --git a/.venv/lib/python3.10/site-packages/_pytest/logging.py b/.venv/lib/python3.10/site-packages/_pytest/logging.py index e9a3234..76c0c85 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/logging.py +++ b/.venv/lib/python3.10/site-packages/_pytest/logging.py @@ -1,17 +1,19 @@ # mypy: allow-untyped-defs """Access and control log capturing.""" +from __future__ import annotations + +import io +import logging +import os +import re from contextlib import contextmanager from contextlib import nullcontext from datetime import datetime from datetime import timedelta from datetime import timezone -import io from io import StringIO -import logging from logging import LogRecord -import os from pathlib import Path -import re from types import TracebackType from typing import AbstractSet from typing import Dict @@ -50,15 +52,15 @@ if TYPE_CHECKING: else: logging_StreamHandler = logging.StreamHandler -DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" -DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" -_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") -caplog_handler_key = StashKey["LogCaptureHandler"]() +DEFAULT_LOG_FORMAT = '%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s' +DEFAULT_LOG_DATE_FORMAT = '%H:%M:%S' +_ANSI_ESCAPE_SEQ = re.compile(r'\x1b\[[\d;]+m') +caplog_handler_key = StashKey['LogCaptureHandler']() caplog_records_key = StashKey[Dict[str, List[logging.LogRecord]]]() def _remove_ansi_escape_sequences(text: str) -> str: - return _ANSI_ESCAPE_SEQ.sub("", text) + return _ANSI_ESCAPE_SEQ.sub('', text) class DatetimeFormatter(logging.Formatter): @@ -67,8 +69,8 @@ class DatetimeFormatter(logging.Formatter): :func:`time.strftime` in case of microseconds in format string. """ - def formatTime(self, record: LogRecord, datefmt: Optional[str] = None) -> str: - if datefmt and "%f" in datefmt: + def formatTime(self, record: LogRecord, datefmt: str | None = None) -> str: + if datefmt and '%f' in datefmt: ct = self.converter(record.created) tz = timezone(timedelta(seconds=ct.tm_gmtoff), ct.tm_zone) # Construct `datetime.datetime` object from `struct_time` @@ -85,21 +87,21 @@ class ColoredLevelFormatter(DatetimeFormatter): log format passed to __init__.""" LOGLEVEL_COLOROPTS: Mapping[int, AbstractSet[str]] = { - logging.CRITICAL: {"red"}, - logging.ERROR: {"red", "bold"}, - logging.WARNING: {"yellow"}, - logging.WARN: {"yellow"}, - logging.INFO: {"green"}, - logging.DEBUG: {"purple"}, + logging.CRITICAL: {'red'}, + logging.ERROR: {'red', 'bold'}, + logging.WARNING: {'yellow'}, + logging.WARN: {'yellow'}, + logging.INFO: {'green'}, + logging.DEBUG: {'purple'}, logging.NOTSET: set(), } - LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*(?:\.\d+)?s)") + LEVELNAME_FMT_REGEX = re.compile(r'%\(levelname\)([+-.]?\d*(?:\.\d+)?s)') def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self._terminalwriter = terminalwriter self._original_fmt = self._style._fmt - self._level_to_fmt_mapping: Dict[int, str] = {} + self._level_to_fmt_mapping: dict[int, str] = {} for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): self.add_color_level(level, *color_opts) @@ -123,15 +125,15 @@ class ColoredLevelFormatter(DatetimeFormatter): return levelname_fmt = levelname_fmt_match.group() - formatted_levelname = levelname_fmt % {"levelname": logging.getLevelName(level)} + formatted_levelname = levelname_fmt % {'levelname': logging.getLevelName(level)} # add ANSI escape sequences around the formatted levelname color_kwargs = {name: True for name in color_opts} colorized_formatted_levelname = self._terminalwriter.markup( - formatted_levelname, **color_kwargs + formatted_levelname, **color_kwargs, ) self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub( - colorized_formatted_levelname, self._fmt + colorized_formatted_levelname, self._fmt, ) def format(self, record: logging.LogRecord) -> str: @@ -147,12 +149,12 @@ class PercentStyleMultiline(logging.PercentStyle): formats the message as if each line were logged separately. """ - def __init__(self, fmt: str, auto_indent: Union[int, str, bool, None]) -> None: + def __init__(self, fmt: str, auto_indent: int | str | bool | None) -> None: super().__init__(fmt) self._auto_indent = self._get_auto_indent(auto_indent) @staticmethod - def _get_auto_indent(auto_indent_option: Union[int, str, bool, None]) -> int: + def _get_auto_indent(auto_indent_option: int | str | bool | None) -> int: """Determine the current auto indentation setting. Specify auto indent behavior (on/off/fixed) by passing in @@ -206,8 +208,8 @@ class PercentStyleMultiline(logging.PercentStyle): return 0 def format(self, record: logging.LogRecord) -> str: - if "\n" in record.message: - if hasattr(record, "auto_indent"): + if '\n' in record.message: + if hasattr(record, 'auto_indent'): # Passed in from the "extra={}" kwarg on the call to logging.log(). auto_indent = self._get_auto_indent(record.auto_indent) # type: ignore[attr-defined] else: @@ -215,17 +217,17 @@ class PercentStyleMultiline(logging.PercentStyle): if auto_indent: lines = record.message.splitlines() - formatted = self._fmt % {**record.__dict__, "message": lines[0]} + formatted = self._fmt % {**record.__dict__, 'message': lines[0]} if auto_indent < 0: indentation = _remove_ansi_escape_sequences(formatted).find( - lines[0] + lines[0], ) else: # Optimizes logging by allowing a fixed indentation. indentation = auto_indent lines[0] = formatted - return ("\n" + " " * indentation).join(lines) + return ('\n' + ' ' * indentation).join(lines) return self._fmt % record.__dict__ @@ -240,114 +242,114 @@ def get_option_ini(config: Config, *names: str): def pytest_addoption(parser: Parser) -> None: """Add options to control log capturing.""" - group = parser.getgroup("logging") + group = parser.getgroup('logging') def add_option_ini(option, dest, default=None, type=None, **kwargs): parser.addini( - dest, default=default, type=type, help="Default value for " + option + dest, default=default, type=type, help='Default value for ' + option, ) group.addoption(option, dest=dest, **kwargs) add_option_ini( - "--log-level", - dest="log_level", + '--log-level', + dest='log_level', default=None, - metavar="LEVEL", + metavar='LEVEL', help=( - "Level of messages to catch/display." + 'Level of messages to catch/display.' " Not set by default, so it depends on the root/parent log handler's" ' effective level, where it is "WARNING" by default.' ), ) add_option_ini( - "--log-format", - dest="log_format", + '--log-format', + dest='log_format', default=DEFAULT_LOG_FORMAT, - help="Log format used by the logging module", + help='Log format used by the logging module', ) add_option_ini( - "--log-date-format", - dest="log_date_format", + '--log-date-format', + dest='log_date_format', default=DEFAULT_LOG_DATE_FORMAT, - help="Log date format used by the logging module", + help='Log date format used by the logging module', ) parser.addini( - "log_cli", + 'log_cli', default=False, - type="bool", + type='bool', help='Enable log display during test run (also known as "live logging")', ) add_option_ini( - "--log-cli-level", dest="log_cli_level", default=None, help="CLI logging level" + '--log-cli-level', dest='log_cli_level', default=None, help='CLI logging level', ) add_option_ini( - "--log-cli-format", - dest="log_cli_format", + '--log-cli-format', + dest='log_cli_format', default=None, - help="Log format used by the logging module", + help='Log format used by the logging module', ) add_option_ini( - "--log-cli-date-format", - dest="log_cli_date_format", + '--log-cli-date-format', + dest='log_cli_date_format', default=None, - help="Log date format used by the logging module", + help='Log date format used by the logging module', ) add_option_ini( - "--log-file", - dest="log_file", + '--log-file', + dest='log_file', default=None, - help="Path to a file when logging will be written to", + help='Path to a file when logging will be written to', ) add_option_ini( - "--log-file-mode", - dest="log_file_mode", - default="w", - choices=["w", "a"], - help="Log file open mode", + '--log-file-mode', + dest='log_file_mode', + default='w', + choices=['w', 'a'], + help='Log file open mode', ) add_option_ini( - "--log-file-level", - dest="log_file_level", + '--log-file-level', + dest='log_file_level', default=None, - help="Log file logging level", + help='Log file logging level', ) add_option_ini( - "--log-file-format", - dest="log_file_format", + '--log-file-format', + dest='log_file_format', default=None, - help="Log format used by the logging module", + help='Log format used by the logging module', ) add_option_ini( - "--log-file-date-format", - dest="log_file_date_format", + '--log-file-date-format', + dest='log_file_date_format', default=None, - help="Log date format used by the logging module", + help='Log date format used by the logging module', ) add_option_ini( - "--log-auto-indent", - dest="log_auto_indent", + '--log-auto-indent', + dest='log_auto_indent', default=None, - help="Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.", + help='Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.', ) group.addoption( - "--log-disable", - action="append", + '--log-disable', + action='append', default=[], - dest="logger_disable", - help="Disable a logger by name. Can be passed multiple times.", + dest='logger_disable', + help='Disable a logger by name. Can be passed multiple times.', ) -_HandlerType = TypeVar("_HandlerType", bound=logging.Handler) +_HandlerType = TypeVar('_HandlerType', bound=logging.Handler) # Not using @contextmanager for performance reasons. class catching_logs(Generic[_HandlerType]): """Context manager that prepares the whole logging machinery properly.""" - __slots__ = ("handler", "level", "orig_level") + __slots__ = ('handler', 'level', 'orig_level') - def __init__(self, handler: _HandlerType, level: Optional[int] = None) -> None: + def __init__(self, handler: _HandlerType, level: int | None = None) -> None: self.handler = handler self.level = level @@ -363,9 +365,9 @@ class catching_logs(Generic[_HandlerType]): def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: root_logger = logging.getLogger() if self.level is not None: @@ -379,7 +381,7 @@ class LogCaptureHandler(logging_StreamHandler): def __init__(self) -> None: """Create a new log handler.""" super().__init__(StringIO()) - self.records: List[logging.LogRecord] = [] + self.records: list[logging.LogRecord] = [] def emit(self, record: logging.LogRecord) -> None: """Keep the log records in a list in addition to the log text.""" @@ -410,10 +412,10 @@ class LogCaptureFixture: def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None: check_ispytest(_ispytest) self._item = item - self._initial_handler_level: Optional[int] = None + self._initial_handler_level: int | None = None # Dict of log name -> log level. - self._initial_logger_levels: Dict[Optional[str], int] = {} - self._initial_disabled_logging_level: Optional[int] = None + self._initial_logger_levels: dict[str | None, int] = {} + self._initial_disabled_logging_level: int | None = None def _finalize(self) -> None: """Finalize the fixture. @@ -437,8 +439,8 @@ class LogCaptureFixture: return self._item.stash[caplog_handler_key] def get_records( - self, when: Literal["setup", "call", "teardown"] - ) -> List[logging.LogRecord]: + self, when: Literal['setup', 'call', 'teardown'], + ) -> list[logging.LogRecord]: """Get the logging records for one of the possible test phases. :param when: @@ -457,12 +459,12 @@ class LogCaptureFixture: return _remove_ansi_escape_sequences(self.handler.stream.getvalue()) @property - def records(self) -> List[logging.LogRecord]: + def records(self) -> list[logging.LogRecord]: """The list of log records.""" return self.handler.records @property - def record_tuples(self) -> List[Tuple[str, int, str]]: + def record_tuples(self) -> list[tuple[str, int, str]]: """A list of a stripped down version of log records intended for use in assertion comparison. @@ -473,7 +475,7 @@ class LogCaptureFixture: return [(r.name, r.levelno, r.getMessage()) for r in self.records] @property - def messages(self) -> List[str]: + def messages(self) -> list[str]: """A list of format-interpolated log messages. Unlike 'records', which contains the format string and parameters for @@ -496,7 +498,7 @@ class LogCaptureFixture: self.handler.clear() def _force_enable_logging( - self, level: Union[int, str], logger_obj: logging.Logger + self, level: int | str, logger_obj: logging.Logger, ) -> int: """Enable the desired logging level if the global level was disabled via ``logging.disabled``. @@ -529,7 +531,7 @@ class LogCaptureFixture: return original_disable_level - def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: + def set_level(self, level: int | str, logger: str | None = None) -> None: """Set the threshold level of a logger for the duration of a test. Logging messages which are less severe than this level will not be captured. @@ -556,7 +558,7 @@ class LogCaptureFixture: @contextmanager def at_level( - self, level: Union[int, str], logger: Optional[str] = None + self, level: int | str, logger: str | None = None, ) -> Generator[None, None, None]: """Context manager that sets the level for capturing of logs. After the end of the 'with' statement the level is restored to its original @@ -614,7 +616,7 @@ def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture, None, None]: result._finalize() -def get_log_level_for_setting(config: Config, *setting_names: str) -> Optional[int]: +def get_log_level_for_setting(config: Config, *setting_names: str) -> int | None: for setting_name in setting_names: log_level = config.getoption(setting_name) if log_level is None: @@ -633,14 +635,14 @@ def get_log_level_for_setting(config: Config, *setting_names: str) -> Optional[i raise UsageError( f"'{log_level}' is not recognized as a logging level name for " f"'{setting_name}'. Please consider passing the " - "logging level num instead." + 'logging level num instead.', ) from e # run after terminalreporter/capturemanager are configured @hookimpl(trylast=True) def pytest_configure(config: Config) -> None: - config.pluginmanager.register(LoggingPlugin(config), "logging-plugin") + config.pluginmanager.register(LoggingPlugin(config), 'logging-plugin') class LoggingPlugin: @@ -656,11 +658,11 @@ class LoggingPlugin: # Report logging. self.formatter = self._create_formatter( - get_option_ini(config, "log_format"), - get_option_ini(config, "log_date_format"), - get_option_ini(config, "log_auto_indent"), + get_option_ini(config, 'log_format'), + get_option_ini(config, 'log_date_format'), + get_option_ini(config, 'log_auto_indent'), ) - self.log_level = get_log_level_for_setting(config, "log_level") + self.log_level = get_log_level_for_setting(config, 'log_level') self.caplog_handler = LogCaptureHandler() self.caplog_handler.setFormatter(self.formatter) self.report_handler = LogCaptureHandler() @@ -668,52 +670,52 @@ class LoggingPlugin: # File logging. self.log_file_level = get_log_level_for_setting( - config, "log_file_level", "log_level" + config, 'log_file_level', 'log_level', ) - log_file = get_option_ini(config, "log_file") or os.devnull + log_file = get_option_ini(config, 'log_file') or os.devnull if log_file != os.devnull: directory = os.path.dirname(os.path.abspath(log_file)) if not os.path.isdir(directory): os.makedirs(directory) - self.log_file_mode = get_option_ini(config, "log_file_mode") or "w" + self.log_file_mode = get_option_ini(config, 'log_file_mode') or 'w' self.log_file_handler = _FileHandler( - log_file, mode=self.log_file_mode, encoding="UTF-8" + log_file, mode=self.log_file_mode, encoding='UTF-8', ) - log_file_format = get_option_ini(config, "log_file_format", "log_format") + log_file_format = get_option_ini(config, 'log_file_format', 'log_format') log_file_date_format = get_option_ini( - config, "log_file_date_format", "log_date_format" + config, 'log_file_date_format', 'log_date_format', ) log_file_formatter = DatetimeFormatter( - log_file_format, datefmt=log_file_date_format + log_file_format, datefmt=log_file_date_format, ) self.log_file_handler.setFormatter(log_file_formatter) # CLI/live logging. self.log_cli_level = get_log_level_for_setting( - config, "log_cli_level", "log_level" + config, 'log_cli_level', 'log_level', ) if self._log_cli_enabled(): - terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") + terminal_reporter = config.pluginmanager.get_plugin('terminalreporter') # Guaranteed by `_log_cli_enabled()`. assert terminal_reporter is not None - capture_manager = config.pluginmanager.get_plugin("capturemanager") + capture_manager = config.pluginmanager.get_plugin('capturemanager') # if capturemanager plugin is disabled, live logging still works. - self.log_cli_handler: Union[ - _LiveLoggingStreamHandler, _LiveLoggingNullHandler - ] = _LiveLoggingStreamHandler(terminal_reporter, capture_manager) + self.log_cli_handler: ( + _LiveLoggingStreamHandler | _LiveLoggingNullHandler + ) = _LiveLoggingStreamHandler(terminal_reporter, capture_manager) else: self.log_cli_handler = _LiveLoggingNullHandler() log_cli_formatter = self._create_formatter( - get_option_ini(config, "log_cli_format", "log_format"), - get_option_ini(config, "log_cli_date_format", "log_date_format"), - get_option_ini(config, "log_auto_indent"), + get_option_ini(config, 'log_cli_format', 'log_format'), + get_option_ini(config, 'log_cli_date_format', 'log_date_format'), + get_option_ini(config, 'log_auto_indent'), ) self.log_cli_handler.setFormatter(log_cli_formatter) self._disable_loggers(loggers_to_disable=config.option.logger_disable) - def _disable_loggers(self, loggers_to_disable: List[str]) -> None: + def _disable_loggers(self, loggers_to_disable: list[str]) -> None: if not loggers_to_disable: return @@ -723,18 +725,18 @@ class LoggingPlugin: def _create_formatter(self, log_format, log_date_format, auto_indent): # Color option doesn't exist if terminal plugin is disabled. - color = getattr(self._config.option, "color", "no") - if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( - log_format + color = getattr(self._config.option, 'color', 'no') + if color != 'no' and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( + log_format, ): formatter: logging.Formatter = ColoredLevelFormatter( - create_terminal_writer(self._config), log_format, log_date_format + create_terminal_writer(self._config), log_format, log_date_format, ) else: formatter = DatetimeFormatter(log_format, log_date_format) formatter._style = PercentStyleMultiline( - formatter._style._fmt, auto_indent=auto_indent + formatter._style._fmt, auto_indent=auto_indent, ) return formatter @@ -756,7 +758,7 @@ class LoggingPlugin: fpath.parent.mkdir(exist_ok=True, parents=True) # https://github.com/python/mypy/issues/11193 - stream: io.TextIOWrapper = fpath.open(mode=self.log_file_mode, encoding="UTF-8") # type: ignore[assignment] + stream: io.TextIOWrapper = fpath.open(mode=self.log_file_mode, encoding='UTF-8') # type: ignore[assignment] old_stream = self.log_file_handler.setStream(stream) if old_stream: old_stream.close() @@ -764,12 +766,12 @@ class LoggingPlugin: def _log_cli_enabled(self) -> bool: """Return whether live logging is enabled.""" enabled = self._config.getoption( - "--log-cli-level" - ) is not None or self._config.getini("log_cli") + '--log-cli-level', + ) is not None or self._config.getini('log_cli') if not enabled: return False - terminal_reporter = self._config.pluginmanager.get_plugin("terminalreporter") + terminal_reporter = self._config.pluginmanager.get_plugin('terminalreporter') if terminal_reporter is None: # terminal reporter is disabled e.g. by pytest-xdist. return False @@ -778,7 +780,7 @@ class LoggingPlugin: @hookimpl(wrapper=True, tryfirst=True) def pytest_sessionstart(self) -> Generator[None, None, None]: - self.log_cli_handler.set_when("sessionstart") + self.log_cli_handler.set_when('sessionstart') with catching_logs(self.log_cli_handler, level=self.log_cli_level): with catching_logs(self.log_file_handler, level=self.log_file_level): @@ -786,7 +788,7 @@ class LoggingPlugin: @hookimpl(wrapper=True, tryfirst=True) def pytest_collection(self) -> Generator[None, None, None]: - self.log_cli_handler.set_when("collection") + self.log_cli_handler.set_when('collection') with catching_logs(self.log_cli_handler, level=self.log_cli_level): with catching_logs(self.log_file_handler, level=self.log_file_level): @@ -797,7 +799,7 @@ class LoggingPlugin: if session.config.option.collectonly: return (yield) - if self._log_cli_enabled() and self._config.getoption("verbose") < 1: + if self._log_cli_enabled() and self._config.getoption('verbose') < 1: # The verbose flag is needed to avoid messy test progress output. self._config.option.verbose = 1 @@ -808,11 +810,11 @@ class LoggingPlugin: @hookimpl def pytest_runtest_logstart(self) -> None: self.log_cli_handler.reset() - self.log_cli_handler.set_when("start") + self.log_cli_handler.set_when('start') @hookimpl def pytest_runtest_logreport(self) -> None: - self.log_cli_handler.set_when("logreport") + self.log_cli_handler.set_when('logreport') def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, None]: """Implement the internals of the pytest_runtest_xxx() hooks.""" @@ -832,39 +834,39 @@ class LoggingPlugin: yield finally: log = report_handler.stream.getvalue().strip() - item.add_report_section(when, "log", log) + item.add_report_section(when, 'log', log) @hookimpl(wrapper=True) def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]: - self.log_cli_handler.set_when("setup") + self.log_cli_handler.set_when('setup') - empty: Dict[str, List[logging.LogRecord]] = {} + empty: dict[str, list[logging.LogRecord]] = {} item.stash[caplog_records_key] = empty - yield from self._runtest_for(item, "setup") + yield from self._runtest_for(item, 'setup') @hookimpl(wrapper=True) def pytest_runtest_call(self, item: nodes.Item) -> Generator[None, None, None]: - self.log_cli_handler.set_when("call") + self.log_cli_handler.set_when('call') - yield from self._runtest_for(item, "call") + yield from self._runtest_for(item, 'call') @hookimpl(wrapper=True) def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None, None, None]: - self.log_cli_handler.set_when("teardown") + self.log_cli_handler.set_when('teardown') try: - yield from self._runtest_for(item, "teardown") + yield from self._runtest_for(item, 'teardown') finally: del item.stash[caplog_records_key] del item.stash[caplog_handler_key] @hookimpl def pytest_runtest_logfinish(self) -> None: - self.log_cli_handler.set_when("finish") + self.log_cli_handler.set_when('finish') @hookimpl(wrapper=True, tryfirst=True) def pytest_sessionfinish(self) -> Generator[None, None, None]: - self.log_cli_handler.set_when("sessionfinish") + self.log_cli_handler.set_when('sessionfinish') with catching_logs(self.log_cli_handler, level=self.log_cli_level): with catching_logs(self.log_file_handler, level=self.log_file_level): @@ -901,7 +903,7 @@ class _LiveLoggingStreamHandler(logging_StreamHandler): def __init__( self, terminal_reporter: TerminalReporter, - capture_manager: Optional[CaptureManager], + capture_manager: CaptureManager | None, ) -> None: super().__init__(stream=terminal_reporter) # type: ignore[arg-type] self.capture_manager = capture_manager @@ -913,11 +915,11 @@ class _LiveLoggingStreamHandler(logging_StreamHandler): """Reset the handler; should be called before the start of each test.""" self._first_record_emitted = False - def set_when(self, when: Optional[str]) -> None: + def set_when(self, when: str | None) -> None: """Prepare for the given test phase (setup/call/teardown).""" self._when = when self._section_name_shown = False - if when == "start": + if when == 'start': self._test_outcome_written = False def emit(self, record: logging.LogRecord) -> None: @@ -928,14 +930,14 @@ class _LiveLoggingStreamHandler(logging_StreamHandler): ) with ctx_manager: if not self._first_record_emitted: - self.stream.write("\n") + self.stream.write('\n') self._first_record_emitted = True - elif self._when in ("teardown", "finish"): + elif self._when in ('teardown', 'finish'): if not self._test_outcome_written: self._test_outcome_written = True - self.stream.write("\n") + self.stream.write('\n') if not self._section_name_shown and self._when: - self.stream.section("live log " + self._when, sep="-", bold=True) + self.stream.section('live log ' + self._when, sep='-', bold=True) self._section_name_shown = True super().emit(record) diff --git a/.venv/lib/python3.10/site-packages/_pytest/main.py b/.venv/lib/python3.10/site-packages/_pytest/main.py index 3b9ac93..d0ed82e 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/main.py +++ b/.venv/lib/python3.10/site-packages/_pytest/main.py @@ -1,14 +1,15 @@ """Core implementation of the testing process: init, session, runtest loop.""" +from __future__ import annotations import argparse import dataclasses import fnmatch import functools -import importlib import importlib.util import os -from pathlib import Path import sys +import warnings +from pathlib import Path from typing import AbstractSet from typing import Callable from typing import Dict @@ -24,12 +25,10 @@ from typing import Sequence from typing import Tuple from typing import TYPE_CHECKING from typing import Union -import warnings -import pluggy - -from _pytest import nodes import _pytest._code +import pluggy +from _pytest import nodes from _pytest.config import Config from _pytest.config import directory_arg from _pytest.config import ExitCode @@ -58,195 +57,195 @@ if TYPE_CHECKING: def pytest_addoption(parser: Parser) -> None: parser.addini( - "norecursedirs", - "Directory patterns to avoid for recursion", - type="args", + 'norecursedirs', + 'Directory patterns to avoid for recursion', + type='args', default=[ - "*.egg", - ".*", - "_darcs", - "build", - "CVS", - "dist", - "node_modules", - "venv", - "{arch}", + '*.egg', + '.*', + '_darcs', + 'build', + 'CVS', + 'dist', + 'node_modules', + 'venv', + '{arch}', ], ) parser.addini( - "testpaths", - "Directories to search for tests when no files or directories are given on the " - "command line", - type="args", + 'testpaths', + 'Directories to search for tests when no files or directories are given on the ' + 'command line', + type='args', default=[], ) - group = parser.getgroup("general", "Running and selection options") + group = parser.getgroup('general', 'Running and selection options') group._addoption( - "-x", - "--exitfirst", - action="store_const", - dest="maxfail", + '-x', + '--exitfirst', + action='store_const', + dest='maxfail', const=1, - help="Exit instantly on first error or failed test", + help='Exit instantly on first error or failed test', ) - group = parser.getgroup("pytest-warnings") + group = parser.getgroup('pytest-warnings') group.addoption( - "-W", - "--pythonwarnings", - action="append", - help="Set which warnings to report, see -W option of Python itself", + '-W', + '--pythonwarnings', + action='append', + help='Set which warnings to report, see -W option of Python itself', ) parser.addini( - "filterwarnings", - type="linelist", - help="Each line specifies a pattern for " - "warnings.filterwarnings. " - "Processed after -W/--pythonwarnings.", + 'filterwarnings', + type='linelist', + help='Each line specifies a pattern for ' + 'warnings.filterwarnings. ' + 'Processed after -W/--pythonwarnings.', ) group._addoption( - "--maxfail", - metavar="num", - action="store", + '--maxfail', + metavar='num', + action='store', type=int, - dest="maxfail", + dest='maxfail', default=0, - help="Exit after first num failures or errors", + help='Exit after first num failures or errors', ) group._addoption( - "--strict-config", - action="store_true", - help="Any warnings encountered while parsing the `pytest` section of the " - "configuration file raise errors", + '--strict-config', + action='store_true', + help='Any warnings encountered while parsing the `pytest` section of the ' + 'configuration file raise errors', ) group._addoption( - "--strict-markers", - action="store_true", - help="Markers not registered in the `markers` section of the configuration " - "file raise errors", + '--strict-markers', + action='store_true', + help='Markers not registered in the `markers` section of the configuration ' + 'file raise errors', ) group._addoption( - "--strict", - action="store_true", - help="(Deprecated) alias to --strict-markers", + '--strict', + action='store_true', + help='(Deprecated) alias to --strict-markers', ) group._addoption( - "-c", - "--config-file", - metavar="FILE", + '-c', + '--config-file', + metavar='FILE', type=str, - dest="inifilename", - help="Load configuration from `FILE` instead of trying to locate one of the " - "implicit configuration files.", + dest='inifilename', + help='Load configuration from `FILE` instead of trying to locate one of the ' + 'implicit configuration files.', ) group._addoption( - "--continue-on-collection-errors", - action="store_true", + '--continue-on-collection-errors', + action='store_true', default=False, - dest="continue_on_collection_errors", - help="Force test execution even if collection errors occur", + dest='continue_on_collection_errors', + help='Force test execution even if collection errors occur', ) group._addoption( - "--rootdir", - action="store", - dest="rootdir", + '--rootdir', + action='store', + dest='rootdir', help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', " "'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: " "'$HOME/root_dir'.", ) - group = parser.getgroup("collect", "collection") + group = parser.getgroup('collect', 'collection') group.addoption( - "--collectonly", - "--collect-only", - "--co", - action="store_true", + '--collectonly', + '--collect-only', + '--co', + action='store_true', help="Only collect tests, don't execute them", ) group.addoption( - "--pyargs", - action="store_true", - help="Try to interpret all arguments as Python packages", + '--pyargs', + action='store_true', + help='Try to interpret all arguments as Python packages', ) group.addoption( - "--ignore", - action="append", - metavar="path", - help="Ignore path during collection (multi-allowed)", + '--ignore', + action='append', + metavar='path', + help='Ignore path during collection (multi-allowed)', ) group.addoption( - "--ignore-glob", - action="append", - metavar="path", - help="Ignore path pattern during collection (multi-allowed)", + '--ignore-glob', + action='append', + metavar='path', + help='Ignore path pattern during collection (multi-allowed)', ) group.addoption( - "--deselect", - action="append", - metavar="nodeid_prefix", - help="Deselect item (via node id prefix) during collection (multi-allowed)", + '--deselect', + action='append', + metavar='nodeid_prefix', + help='Deselect item (via node id prefix) during collection (multi-allowed)', ) group.addoption( - "--confcutdir", - dest="confcutdir", + '--confcutdir', + dest='confcutdir', default=None, - metavar="dir", - type=functools.partial(directory_arg, optname="--confcutdir"), + metavar='dir', + type=functools.partial(directory_arg, optname='--confcutdir'), help="Only load conftest.py's relative to specified dir", ) group.addoption( - "--noconftest", - action="store_true", - dest="noconftest", + '--noconftest', + action='store_true', + dest='noconftest', default=False, help="Don't load any conftest.py files", ) group.addoption( - "--keepduplicates", - "--keep-duplicates", - action="store_true", - dest="keepduplicates", + '--keepduplicates', + '--keep-duplicates', + action='store_true', + dest='keepduplicates', default=False, - help="Keep duplicate tests", + help='Keep duplicate tests', ) group.addoption( - "--collect-in-virtualenv", - action="store_true", - dest="collect_in_virtualenv", + '--collect-in-virtualenv', + action='store_true', + dest='collect_in_virtualenv', default=False, help="Don't ignore tests in a local virtualenv directory", ) group.addoption( - "--import-mode", - default="prepend", - choices=["prepend", "append", "importlib"], - dest="importmode", - help="Prepend/append to sys.path when importing test modules and conftest " - "files. Default: prepend.", + '--import-mode', + default='prepend', + choices=['prepend', 'append', 'importlib'], + dest='importmode', + help='Prepend/append to sys.path when importing test modules and conftest ' + 'files. Default: prepend.', ) parser.addini( - "consider_namespace_packages", - type="bool", + 'consider_namespace_packages', + type='bool', default=False, - help="Consider namespace packages when resolving module names during import", + help='Consider namespace packages when resolving module names during import', ) - group = parser.getgroup("debugconfig", "test session debugging and configuration") + group = parser.getgroup('debugconfig', 'test session debugging and configuration') group.addoption( - "--basetemp", - dest="basetemp", + '--basetemp', + dest='basetemp', default=None, type=validate_basetemp, - metavar="dir", + metavar='dir', help=( - "Base temporary directory for this test run. " - "(Warning: this directory is removed if it exists.)" + 'Base temporary directory for this test run. ' + '(Warning: this directory is removed if it exists.)' ), ) def validate_basetemp(path: str) -> str: # GH 7119 - msg = "basetemp must not be empty, the current working directory or any parent directory of it" + msg = 'basetemp must not be empty, the current working directory or any parent directory of it' # empty path if not path: @@ -270,8 +269,8 @@ def validate_basetemp(path: str) -> str: def wrap_session( - config: Config, doit: Callable[[Config, "Session"], Optional[Union[int, ExitCode]]] -) -> Union[int, ExitCode]: + config: Config, doit: Callable[[Config, Session], int | ExitCode | None], +) -> int | ExitCode: """Skeleton command line program.""" session = Session.from_config(config) session.exitstatus = ExitCode.OK @@ -290,12 +289,12 @@ def wrap_session( session.exitstatus = ExitCode.TESTS_FAILED except (KeyboardInterrupt, exit.Exception): excinfo = _pytest._code.ExceptionInfo.from_current() - exitstatus: Union[int, ExitCode] = ExitCode.INTERRUPTED + exitstatus: int | ExitCode = ExitCode.INTERRUPTED if isinstance(excinfo.value, exit.Exception): if excinfo.value.returncode is not None: exitstatus = excinfo.value.returncode if initstate < 2: - sys.stderr.write(f"{excinfo.typename}: {excinfo.value.msg}\n") + sys.stderr.write(f'{excinfo.typename}: {excinfo.value.msg}\n') config.hook.pytest_keyboard_interrupt(excinfo=excinfo) session.exitstatus = exitstatus except BaseException: @@ -306,10 +305,10 @@ def wrap_session( except exit.Exception as exc: if exc.returncode is not None: session.exitstatus = exc.returncode - sys.stderr.write(f"{type(exc).__name__}: {exc}\n") + sys.stderr.write(f'{type(exc).__name__}: {exc}\n') else: if isinstance(excinfo.value, SystemExit): - sys.stderr.write("mainloop: caught unexpected SystemExit!\n") + sys.stderr.write('mainloop: caught unexpected SystemExit!\n') finally: # Explicitly break reference cycle. @@ -318,21 +317,21 @@ def wrap_session( if initstate >= 2: try: config.hook.pytest_sessionfinish( - session=session, exitstatus=session.exitstatus + session=session, exitstatus=session.exitstatus, ) except exit.Exception as exc: if exc.returncode is not None: session.exitstatus = exc.returncode - sys.stderr.write(f"{type(exc).__name__}: {exc}\n") + sys.stderr.write(f'{type(exc).__name__}: {exc}\n') config._ensure_unconfigure() return session.exitstatus -def pytest_cmdline_main(config: Config) -> Union[int, ExitCode]: +def pytest_cmdline_main(config: Config) -> int | ExitCode: return wrap_session(config, _main) -def _main(config: Config, session: "Session") -> Optional[Union[int, ExitCode]]: +def _main(config: Config, session: Session) -> int | ExitCode | None: """Default command line protocol for initialization, session, running tests and reporting.""" config.hook.pytest_collection(session=session) @@ -345,15 +344,15 @@ def _main(config: Config, session: "Session") -> Optional[Union[int, ExitCode]]: return None -def pytest_collection(session: "Session") -> None: +def pytest_collection(session: Session) -> None: session.perform_collect() -def pytest_runtestloop(session: "Session") -> bool: +def pytest_runtestloop(session: Session) -> bool: if session.testsfailed and not session.config.option.continue_on_collection_errors: raise session.Interrupted( - "%d error%s during collection" - % (session.testsfailed, "s" if session.testsfailed != 1 else "") + '%d error%s during collection' + % (session.testsfailed, 's' if session.testsfailed != 1 else ''), ) if session.config.option.collectonly: @@ -372,32 +371,32 @@ def pytest_runtestloop(session: "Session") -> bool: def _in_venv(path: Path) -> bool: """Attempt to detect if ``path`` is the root of a Virtual Environment by checking for the existence of the appropriate activate script.""" - bindir = path.joinpath("Scripts" if sys.platform.startswith("win") else "bin") + bindir = path.joinpath('Scripts' if sys.platform.startswith('win') else 'bin') try: if not bindir.is_dir(): return False except OSError: return False activates = ( - "activate", - "activate.csh", - "activate.fish", - "Activate", - "Activate.bat", - "Activate.ps1", + 'activate', + 'activate.csh', + 'activate.fish', + 'Activate', + 'Activate.bat', + 'Activate.ps1', ) return any(fname.name in activates for fname in bindir.iterdir()) -def pytest_ignore_collect(collection_path: Path, config: Config) -> Optional[bool]: - if collection_path.name == "__pycache__": +def pytest_ignore_collect(collection_path: Path, config: Config) -> bool | None: + if collection_path.name == '__pycache__': return True ignore_paths = config._getconftest_pathlist( - "collect_ignore", path=collection_path.parent + 'collect_ignore', path=collection_path.parent, ) ignore_paths = ignore_paths or [] - excludeopt = config.getoption("ignore") + excludeopt = config.getoption('ignore') if excludeopt: ignore_paths.extend(absolutepath(x) for x in excludeopt) @@ -405,22 +404,22 @@ def pytest_ignore_collect(collection_path: Path, config: Config) -> Optional[boo return True ignore_globs = config._getconftest_pathlist( - "collect_ignore_glob", path=collection_path.parent + 'collect_ignore_glob', path=collection_path.parent, ) ignore_globs = ignore_globs or [] - excludeglobopt = config.getoption("ignore_glob") + excludeglobopt = config.getoption('ignore_glob') if excludeglobopt: ignore_globs.extend(absolutepath(x) for x in excludeglobopt) if any(fnmatch.fnmatch(str(collection_path), str(glob)) for glob in ignore_globs): return True - allow_in_venv = config.getoption("collect_in_virtualenv") + allow_in_venv = config.getoption('collect_in_virtualenv') if not allow_in_venv and _in_venv(collection_path): return True if collection_path.is_dir(): - norecursepatterns = config.getini("norecursedirs") + norecursepatterns = config.getini('norecursedirs') if any(fnmatch_ex(pat, collection_path) for pat in norecursepatterns): return True @@ -428,13 +427,13 @@ def pytest_ignore_collect(collection_path: Path, config: Config) -> Optional[boo def pytest_collect_directory( - path: Path, parent: nodes.Collector -) -> Optional[nodes.Collector]: + path: Path, parent: nodes.Collector, +) -> nodes.Collector | None: return Dir.from_parent(parent, path=path) -def pytest_collection_modifyitems(items: List[nodes.Item], config: Config) -> None: - deselect_prefixes = tuple(config.getoption("deselect") or []) +def pytest_collection_modifyitems(items: list[nodes.Item], config: Config) -> None: + deselect_prefixes = tuple(config.getoption('deselect') or []) if not deselect_prefixes: return @@ -469,7 +468,7 @@ class FSHookProxy: class Interrupted(KeyboardInterrupt): """Signals that the test run was interrupted.""" - __module__ = "builtins" # For py3. + __module__ = 'builtins' # For py3. class Failed(Exception): @@ -478,7 +477,7 @@ class Failed(Exception): @dataclasses.dataclass class _bestrelpath_cache(Dict[Path, str]): - __slots__ = ("path",) + __slots__ = ('path',) path: Path @@ -507,7 +506,7 @@ class Dir(nodes.Directory): parent: nodes.Collector, *, path: Path, - ) -> "Self": + ) -> Self: """The public constructor. :param parent: The parent collector of this Dir. @@ -515,9 +514,9 @@ class Dir(nodes.Directory): """ return super().from_parent(parent=parent, path=path) - def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: config = self.config - col: Optional[nodes.Collector] + col: nodes.Collector | None cols: Sequence[nodes.Collector] ihook = self.ihook for direntry in scandir(self.path): @@ -552,60 +551,60 @@ class Session(nodes.Collector): _setupstate: SetupState # Set on the session by fixtures.pytest_sessionstart. _fixturemanager: FixtureManager - exitstatus: Union[int, ExitCode] + exitstatus: int | ExitCode def __init__(self, config: Config) -> None: super().__init__( - name="", + name='', path=config.rootpath, fspath=None, parent=None, config=config, session=self, - nodeid="", + nodeid='', ) self.testsfailed = 0 self.testscollected = 0 - self._shouldstop: Union[bool, str] = False - self._shouldfail: Union[bool, str] = False - self.trace = config.trace.root.get("collection") - self._initialpaths: FrozenSet[Path] = frozenset() - self._initialpaths_with_parents: FrozenSet[Path] = frozenset() - self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = [] - self._initial_parts: List[CollectionArgument] = [] - self._collection_cache: Dict[nodes.Collector, CollectReport] = {} - self.items: List[nodes.Item] = [] + self._shouldstop: bool | str = False + self._shouldfail: bool | str = False + self.trace = config.trace.root.get('collection') + self._initialpaths: frozenset[Path] = frozenset() + self._initialpaths_with_parents: frozenset[Path] = frozenset() + self._notfound: list[tuple[str, Sequence[nodes.Collector]]] = [] + self._initial_parts: list[CollectionArgument] = [] + self._collection_cache: dict[nodes.Collector, CollectReport] = {} + self.items: list[nodes.Item] = [] - self._bestrelpathcache: Dict[Path, str] = _bestrelpath_cache(config.rootpath) + self._bestrelpathcache: dict[Path, str] = _bestrelpath_cache(config.rootpath) - self.config.pluginmanager.register(self, name="session") + self.config.pluginmanager.register(self, name='session') @classmethod - def from_config(cls, config: Config) -> "Session": + def from_config(cls, config: Config) -> Session: session: Session = cls._create(config=config) return session def __repr__(self) -> str: - return "<%s %s exitstatus=%r testsfailed=%d testscollected=%d>" % ( + return '<%s %s exitstatus=%r testsfailed=%d testscollected=%d>' % ( self.__class__.__name__, self.name, - getattr(self, "exitstatus", ""), + getattr(self, 'exitstatus', ''), self.testsfailed, self.testscollected, ) @property - def shouldstop(self) -> Union[bool, str]: + def shouldstop(self) -> bool | str: return self._shouldstop @shouldstop.setter - def shouldstop(self, value: Union[bool, str]) -> None: + def shouldstop(self, value: bool | str) -> None: # The runner checks shouldfail and assumes that if it is set we are # definitely stopping, so prevent unsetting it. if value is False and self._shouldstop: warnings.warn( PytestWarning( - "session.shouldstop cannot be unset after it has been set; ignoring." + 'session.shouldstop cannot be unset after it has been set; ignoring.', ), stacklevel=2, ) @@ -613,17 +612,17 @@ class Session(nodes.Collector): self._shouldstop = value @property - def shouldfail(self) -> Union[bool, str]: + def shouldfail(self) -> bool | str: return self._shouldfail @shouldfail.setter - def shouldfail(self, value: Union[bool, str]) -> None: + def shouldfail(self, value: bool | str) -> None: # The runner checks shouldfail and assumes that if it is set we are # definitely stopping, so prevent unsetting it. if value is False and self._shouldfail: warnings.warn( PytestWarning( - "session.shouldfail cannot be unset after it has been set; ignoring." + 'session.shouldfail cannot be unset after it has been set; ignoring.', ), stacklevel=2, ) @@ -651,19 +650,19 @@ class Session(nodes.Collector): @hookimpl(tryfirst=True) def pytest_runtest_logreport( - self, report: Union[TestReport, CollectReport] + self, report: TestReport | CollectReport, ) -> None: - if report.failed and not hasattr(report, "wasxfail"): + if report.failed and not hasattr(report, 'wasxfail'): self.testsfailed += 1 - maxfail = self.config.getvalue("maxfail") + maxfail = self.config.getvalue('maxfail') if maxfail and self.testsfailed >= maxfail: - self.shouldfail = "stopping after %d failures" % (self.testsfailed) + self.shouldfail = 'stopping after %d failures' % (self.testsfailed) pytest_collectreport = pytest_runtest_logreport def isinitpath( self, - path: Union[str, "os.PathLike[str]"], + path: str | os.PathLike[str], *, with_parents: bool = False, ) -> bool: @@ -685,7 +684,7 @@ class Session(nodes.Collector): else: return path_ in self._initialpaths - def gethookproxy(self, fspath: "os.PathLike[str]") -> pluggy.HookRelay: + def gethookproxy(self, fspath: os.PathLike[str]) -> pluggy.HookRelay: # Optimization: Path(Path(...)) is much slower than isinstance. path = fspath if isinstance(fspath, Path) else Path(fspath) pm = self.config.pluginmanager @@ -705,7 +704,7 @@ class Session(nodes.Collector): def _collect_path( self, path: Path, - path_cache: Dict[Path, Sequence[nodes.Collector]], + path_cache: dict[Path, Sequence[nodes.Collector]], ) -> Sequence[nodes.Collector]: """Create a Collector for the given path. @@ -717,8 +716,8 @@ class Session(nodes.Collector): if path.is_dir(): ihook = self.gethookproxy(path.parent) - col: Optional[nodes.Collector] = ihook.pytest_collect_directory( - path=path, parent=self + col: nodes.Collector | None = ihook.pytest_collect_directory( + path=path, parent=self, ) cols: Sequence[nodes.Collector] = (col,) if col is not None else () @@ -735,19 +734,19 @@ class Session(nodes.Collector): @overload def perform_collect( - self, args: Optional[Sequence[str]] = ..., genitems: "Literal[True]" = ... + self, args: Sequence[str] | None = ..., genitems: Literal[True] = ..., ) -> Sequence[nodes.Item]: ... @overload def perform_collect( - self, args: Optional[Sequence[str]] = ..., genitems: bool = ... - ) -> Sequence[Union[nodes.Item, nodes.Collector]]: + self, args: Sequence[str] | None = ..., genitems: bool = ..., + ) -> Sequence[nodes.Item | nodes.Collector]: ... def perform_collect( - self, args: Optional[Sequence[str]] = None, genitems: bool = True - ) -> Sequence[Union[nodes.Item, nodes.Collector]]: + self, args: Sequence[str] | None = None, genitems: bool = True, + ) -> Sequence[nodes.Item | nodes.Collector]: """Perform the collection phase for this session. This is called by the default :hook:`pytest_collection` hook @@ -764,7 +763,7 @@ class Session(nodes.Collector): if args is None: args = self.config.args - self.trace("perform_collect", self, args) + self.trace('perform_collect', self, args) self.trace.root.indent += 1 hook = self.config.hook @@ -773,10 +772,10 @@ class Session(nodes.Collector): self._initial_parts = [] self._collection_cache = {} self.items = [] - items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items + items: Sequence[nodes.Item | nodes.Collector] = self.items try: - initialpaths: List[Path] = [] - initialpaths_with_parents: List[Path] = [] + initialpaths: list[Path] = [] + initialpaths_with_parents: list[Path] = [] for arg in args: collection_argument = resolve_collection_argument( self.config.invocation_params.dir, @@ -798,10 +797,10 @@ class Session(nodes.Collector): for arg, collectors in self._notfound: if collectors: errors.append( - f"not found: {arg}\n(no match in any of {collectors!r})" + f'not found: {arg}\n(no match in any of {collectors!r})', ) else: - errors.append(f"found no collectors for {arg}") + errors.append(f'found no collectors for {arg}') raise UsageError(*errors) @@ -814,7 +813,7 @@ class Session(nodes.Collector): self.config.pluginmanager.check_pending() hook.pytest_collection_modifyitems( - session=self, config=self.config, items=items + session=self, config=self.config, items=items, ) finally: self._notfound = [] @@ -831,7 +830,7 @@ class Session(nodes.Collector): self, node: nodes.Collector, handle_dupes: bool = True, - ) -> Tuple[CollectReport, bool]: + ) -> tuple[CollectReport, bool]: if node in self._collection_cache and handle_dupes: rep = self._collection_cache[node] return rep, True @@ -840,16 +839,16 @@ class Session(nodes.Collector): self._collection_cache[node] = rep return rep, False - def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: + def collect(self) -> Iterator[nodes.Item | nodes.Collector]: # This is a cache for the root directories of the initial paths. # We can't use collection_cache for Session because of its special # role as the bootstrapping collector. - path_cache: Dict[Path, Sequence[nodes.Collector]] = {} + path_cache: dict[Path, Sequence[nodes.Collector]] = {} pm = self.config.pluginmanager for collection_argument in self._initial_parts: - self.trace("processing argument", collection_argument) + self.trace('processing argument', collection_argument) self.trace.root.indent += 1 argpath = collection_argument.path @@ -858,7 +857,7 @@ class Session(nodes.Collector): # resolve_collection_argument() ensures this. if argpath.is_dir(): - assert not names, f"invalid arg {(argpath, names)!r}" + assert not names, f'invalid arg {(argpath, names)!r}' paths = [argpath] # Add relevant parents of the path, from the root, e.g. @@ -872,7 +871,7 @@ class Session(nodes.Collector): else: # For --pyargs arguments, only consider paths matching the module # name. Paths beyond the package hierarchy are not included. - module_name_parts = module_name.split(".") + module_name_parts = module_name.split('.') for i, path in enumerate(argpath.parents, 2): if i > len(module_name_parts) or path.stem != module_name_parts[-i]: break @@ -882,8 +881,8 @@ class Session(nodes.Collector): # and discarding all nodes which don't match the level's part. any_matched_in_initial_part = False notfound_collectors = [] - work: List[ - Tuple[Union[nodes.Collector, nodes.Item], List[Union[Path, str]]] + work: list[ + tuple[nodes.Collector | nodes.Item, list[Path | str]] ] = [(self, [*paths, *names])] while work: matchnode, matchparts = work.pop() @@ -901,7 +900,7 @@ class Session(nodes.Collector): # Collect this level of matching. # Collecting Session (self) is done directly to avoid endless # recursion to this function. - subnodes: Sequence[Union[nodes.Collector, nodes.Item]] + subnodes: Sequence[nodes.Collector | nodes.Item] if isinstance(matchnode, Session): assert isinstance(matchparts[0], Path) subnodes = matchnode._collect_path(matchparts[0], path_cache) @@ -909,9 +908,9 @@ class Session(nodes.Collector): # For backward compat, files given directly multiple # times on the command line should not be deduplicated. handle_dupes = not ( - len(matchparts) == 1 - and isinstance(matchparts[0], Path) - and matchparts[0].is_file() + len(matchparts) == 1 and + isinstance(matchparts[0], Path) and + matchparts[0].is_file() ) rep, duplicate = self._collect_one_node(matchnode, handle_dupes) if not duplicate and not rep.passed: @@ -929,15 +928,15 @@ class Session(nodes.Collector): # Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`. if isinstance(matchparts[0], Path): is_match = node.path == matchparts[0] - if sys.platform == "win32" and not is_match: + if sys.platform == 'win32' and not is_match: # In case the file paths do not match, fallback to samefile() to # account for short-paths on Windows (#11895). same_file = os.path.samefile(node.path, matchparts[0]) # We don't want to match links to the current node, # otherwise we would match the same file more than once (#12039). is_match = same_file and ( - os.path.islink(node.path) - == os.path.islink(matchparts[0]) + os.path.islink(node.path) == + os.path.islink(matchparts[0]) ) # Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`. @@ -945,8 +944,8 @@ class Session(nodes.Collector): # TODO: Remove parametrized workaround once collection structure contains # parametrization. is_match = ( - node.name == matchparts[0] - or node.name.split("[")[0] == matchparts[0] + node.name == matchparts[0] or + node.name.split('[')[0] == matchparts[0] ) if is_match: work.append((node, matchparts[1:])) @@ -956,21 +955,21 @@ class Session(nodes.Collector): notfound_collectors.append(matchnode) if not any_matched_in_initial_part: - report_arg = "::".join((str(argpath), *names)) + report_arg = '::'.join((str(argpath), *names)) self._notfound.append((report_arg, notfound_collectors)) self.trace.root.indent -= 1 def genitems( - self, node: Union[nodes.Item, nodes.Collector] + self, node: nodes.Item | nodes.Collector, ) -> Iterator[nodes.Item]: - self.trace("genitems", node) + self.trace('genitems', node) if isinstance(node, nodes.Item): node.ihook.pytest_itemcollected(item=node) yield node else: assert isinstance(node, nodes.Collector) - keepduplicates = self.config.getoption("keepduplicates") + keepduplicates = self.config.getoption('keepduplicates') # For backward compat, dedup only applies to files. handle_dupes = not (keepduplicates and isinstance(node, nodes.File)) rep, duplicate = self._collect_one_node(node, handle_dupes) @@ -983,7 +982,7 @@ class Session(nodes.Collector): node.ihook.pytest_collectreport(report=rep) -def search_pypath(module_name: str) -> Optional[str]: +def search_pypath(module_name: str) -> str | None: """Search sys.path for the given a dotted module name, and return its file system path if found.""" try: @@ -993,7 +992,7 @@ def search_pypath(module_name: str) -> Optional[str]: # ValueError: not a module name except (AttributeError, ImportError, ValueError): return None - if spec is None or spec.origin is None or spec.origin == "namespace": + if spec is None or spec.origin is None or spec.origin == 'namespace': return None elif spec.submodule_search_locations: return os.path.dirname(spec.origin) @@ -1007,11 +1006,11 @@ class CollectionArgument: path: Path parts: Sequence[str] - module_name: Optional[str] + module_name: str | None def resolve_collection_argument( - invocation_path: Path, arg: str, *, as_pypath: bool = False + invocation_path: Path, arg: str, *, as_pypath: bool = False, ) -> CollectionArgument: """Parse path arguments optionally containing selection parts and return (fspath, names). @@ -1045,10 +1044,10 @@ def resolve_collection_argument( If the path doesn't exist, raise UsageError. If the path is a directory and selection parts are present, raise UsageError. """ - base, squacket, rest = str(arg).partition("[") - strpath, *parts = base.split("::") + base, squacket, rest = str(arg).partition('[') + strpath, *parts = base.split('::') if parts: - parts[-1] = f"{parts[-1]}{squacket}{rest}" + parts[-1] = f'{parts[-1]}{squacket}{rest}' module_name = None if as_pypath: pyarg_strpath = search_pypath(strpath) @@ -1059,16 +1058,16 @@ def resolve_collection_argument( fspath = absolutepath(fspath) if not safe_exists(fspath): msg = ( - "module or package not found: {arg} (missing __init__.py?)" + 'module or package not found: {arg} (missing __init__.py?)' if as_pypath - else "file or directory not found: {arg}" + else 'file or directory not found: {arg}' ) raise UsageError(msg.format(arg=arg)) if parts and fspath.is_dir(): msg = ( - "package argument cannot contain :: selection parts: {arg}" + 'package argument cannot contain :: selection parts: {arg}' if as_pypath - else "directory argument cannot contain :: selection parts: {arg}" + else 'directory argument cannot contain :: selection parts: {arg}' ) raise UsageError(msg.format(arg=arg)) return CollectionArgument( diff --git a/.venv/lib/python3.10/site-packages/_pytest/mark/__init__.py b/.venv/lib/python3.10/site-packages/_pytest/mark/__init__.py index 77dabd9..5f92386 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/mark/__init__.py +++ b/.venv/lib/python3.10/site-packages/_pytest/mark/__init__.py @@ -1,4 +1,5 @@ """Generic mechanism for marking and selecting python functions.""" +from __future__ import annotations import dataclasses from typing import AbstractSet @@ -8,6 +9,13 @@ from typing import Optional from typing import TYPE_CHECKING from typing import Union +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import UsageError +from _pytest.config.argparsing import Parser +from _pytest.stash import StashKey + from .expression import Expression from .expression import ParseError from .structures import EMPTY_PARAMETERSET_OPTION @@ -17,12 +25,6 @@ from .structures import MARK_GEN from .structures import MarkDecorator from .structures import MarkGenerator from .structures import ParameterSet -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config import hookimpl -from _pytest.config import UsageError -from _pytest.config.argparsing import Parser -from _pytest.stash import StashKey if TYPE_CHECKING: @@ -30,12 +32,12 @@ if TYPE_CHECKING: __all__ = [ - "MARK_GEN", - "Mark", - "MarkDecorator", - "MarkGenerator", - "ParameterSet", - "get_empty_parameterset_mark", + 'MARK_GEN', + 'Mark', + 'MarkDecorator', + 'MarkGenerator', + 'ParameterSet', + 'get_empty_parameterset_mark', ] @@ -44,8 +46,8 @@ old_mark_config_key = StashKey[Optional[Config]]() def param( *values: object, - marks: Union[MarkDecorator, Collection[Union[MarkDecorator, Mark]]] = (), - id: Optional[str] = None, + marks: MarkDecorator | Collection[MarkDecorator | Mark] = (), + id: str | None = None, ) -> ParameterSet: """Specify a parameter in `pytest.mark.parametrize`_ calls or :ref:`parametrized fixtures `. @@ -70,59 +72,59 @@ def param( def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") + group = parser.getgroup('general') group._addoption( - "-k", - action="store", - dest="keyword", - default="", - metavar="EXPRESSION", - help="Only run tests which match the given substring expression. " - "An expression is a Python evaluatable expression " - "where all names are substring-matched against test names " + '-k', + action='store', + dest='keyword', + default='', + metavar='EXPRESSION', + help='Only run tests which match the given substring expression. ' + 'An expression is a Python evaluatable expression ' + 'where all names are substring-matched against test names ' "and their parent classes. Example: -k 'test_method or test_" "other' matches all test functions and classes whose name " "contains 'test_method' or 'test_other', while -k 'not test_method' " "matches those that don't contain 'test_method' in their names. " "-k 'not test_method and not test_other' will eliminate the matches. " - "Additionally keywords are matched to classes and functions " + 'Additionally keywords are matched to classes and functions ' "containing extra names in their 'extra_keyword_matches' set, " - "as well as functions which have names assigned directly to them. " - "The matching is case-insensitive.", + 'as well as functions which have names assigned directly to them. ' + 'The matching is case-insensitive.', ) group._addoption( - "-m", - action="store", - dest="markexpr", - default="", - metavar="MARKEXPR", - help="Only run tests matching given mark expression. " + '-m', + action='store', + dest='markexpr', + default='', + metavar='MARKEXPR', + help='Only run tests matching given mark expression. ' "For example: -m 'mark1 and not mark2'.", ) group.addoption( - "--markers", - action="store_true", - help="show markers (builtin, plugin and per-project ones).", + '--markers', + action='store_true', + help='show markers (builtin, plugin and per-project ones).', ) - parser.addini("markers", "Register new markers for test functions", "linelist") - parser.addini(EMPTY_PARAMETERSET_OPTION, "Default marker for empty parametersets") + parser.addini('markers', 'Register new markers for test functions', 'linelist') + parser.addini(EMPTY_PARAMETERSET_OPTION, 'Default marker for empty parametersets') @hookimpl(tryfirst=True) -def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: import _pytest.config if config.option.markers: config._do_configure() tw = _pytest.config.create_terminal_writer(config) - for line in config.getini("markers"): - parts = line.split(":", 1) + for line in config.getini('markers'): + parts = line.split(':', 1) name = parts[0] - rest = parts[1] if len(parts) == 2 else "" - tw.write("@pytest.mark.%s:" % name, bold=True) + rest = parts[1] if len(parts) == 2 else '' + tw.write('@pytest.mark.%s:' % name, bold=True) tw.line(rest) tw.line() config._ensure_unconfigure() @@ -146,12 +148,12 @@ class KeywordMatcher: any item, as well as names directly assigned to test functions. """ - __slots__ = ("_names",) + __slots__ = ('_names',) _names: AbstractSet[str] @classmethod - def from_item(cls, item: "Item") -> "KeywordMatcher": + def from_item(cls, item: Item) -> KeywordMatcher: mapped_names = set() # Add the names of the current item and any parent items, @@ -163,7 +165,7 @@ class KeywordMatcher: if isinstance(node, pytest.Session): continue if isinstance(node, pytest.Directory) and isinstance( - node.parent, pytest.Session + node.parent, pytest.Session, ): continue mapped_names.add(node.name) @@ -172,7 +174,7 @@ class KeywordMatcher: mapped_names.update(item.listextrakeywords()) # Add the names attached to the current function through direct assignment. - function_obj = getattr(item, "function", None) + function_obj = getattr(item, 'function', None) if function_obj: mapped_names.update(function_obj.__dict__) @@ -191,7 +193,7 @@ class KeywordMatcher: return False -def deselect_by_keyword(items: "List[Item]", config: Config) -> None: +def deselect_by_keyword(items: List[Item], config: Config) -> None: keywordexpr = config.option.keyword.lstrip() if not keywordexpr: return @@ -218,12 +220,12 @@ class MarkMatcher: Tries to match on any marker names, attached to the given colitem. """ - __slots__ = ("own_mark_names",) + __slots__ = ('own_mark_names',) own_mark_names: AbstractSet[str] @classmethod - def from_item(cls, item: "Item") -> "MarkMatcher": + def from_item(cls, item: Item) -> MarkMatcher: mark_names = {mark.name for mark in item.iter_markers()} return cls(mark_names) @@ -231,14 +233,14 @@ class MarkMatcher: return name in self.own_mark_names -def deselect_by_mark(items: "List[Item]", config: Config) -> None: +def deselect_by_mark(items: List[Item], config: Config) -> None: matchexpr = config.option.markexpr if not matchexpr: return expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'") - remaining: List[Item] = [] - deselected: List[Item] = [] + remaining: list[Item] = [] + deselected: list[Item] = [] for item in items: if expr.evaluate(MarkMatcher.from_item(item)): remaining.append(item) @@ -253,10 +255,10 @@ def _parse_expression(expr: str, exc_message: str) -> Expression: try: return Expression.compile(expr) except ParseError as e: - raise UsageError(f"{exc_message}: {expr}: {e}") from None + raise UsageError(f'{exc_message}: {expr}: {e}') from None -def pytest_collection_modifyitems(items: "List[Item]", config: Config) -> None: +def pytest_collection_modifyitems(items: List[Item], config: Config) -> None: deselect_by_keyword(items, config) deselect_by_mark(items, config) @@ -267,10 +269,10 @@ def pytest_configure(config: Config) -> None: empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION) - if empty_parameterset not in ("skip", "xfail", "fail_at_collect", None, ""): + if empty_parameterset not in ('skip', 'xfail', 'fail_at_collect', None, ''): raise UsageError( - f"{EMPTY_PARAMETERSET_OPTION!s} must be one of skip, xfail or fail_at_collect" - f" but it is {empty_parameterset!r}" + f'{EMPTY_PARAMETERSET_OPTION!s} must be one of skip, xfail or fail_at_collect' + f' but it is {empty_parameterset!r}', ) diff --git a/.venv/lib/python3.10/site-packages/_pytest/mark/expression.py b/.venv/lib/python3.10/site-packages/_pytest/mark/expression.py index 78b7fda..f3a3dc0 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/mark/expression.py +++ b/.venv/lib/python3.10/site-packages/_pytest/mark/expression.py @@ -14,6 +14,7 @@ The semantics are: - ident evaluates to True of False according to a provided matcher function. - or/and/not evaluate according to the usual boolean semantics. """ +from __future__ import annotations import ast import dataclasses @@ -29,24 +30,24 @@ from typing import Sequence __all__ = [ - "Expression", - "ParseError", + 'Expression', + 'ParseError', ] class TokenType(enum.Enum): - LPAREN = "left parenthesis" - RPAREN = "right parenthesis" - OR = "or" - AND = "and" - NOT = "not" - IDENT = "identifier" - EOF = "end of input" + LPAREN = 'left parenthesis' + RPAREN = 'right parenthesis' + OR = 'or' + AND = 'and' + NOT = 'not' + IDENT = 'identifier' + EOF = 'end of input' @dataclasses.dataclass(frozen=True) class Token: - __slots__ = ("type", "value", "pos") + __slots__ = ('type', 'value', 'pos') type: TokenType value: str pos: int @@ -64,11 +65,11 @@ class ParseError(Exception): self.message = message def __str__(self) -> str: - return f"at column {self.column}: {self.message}" + return f'at column {self.column}: {self.message}' class Scanner: - __slots__ = ("tokens", "current") + __slots__ = ('tokens', 'current') def __init__(self, input: str) -> None: self.tokens = self.lex(input) @@ -77,23 +78,23 @@ class Scanner: def lex(self, input: str) -> Iterator[Token]: pos = 0 while pos < len(input): - if input[pos] in (" ", "\t"): + if input[pos] in (' ', '\t'): pos += 1 - elif input[pos] == "(": - yield Token(TokenType.LPAREN, "(", pos) + elif input[pos] == '(': + yield Token(TokenType.LPAREN, '(', pos) pos += 1 - elif input[pos] == ")": - yield Token(TokenType.RPAREN, ")", pos) + elif input[pos] == ')': + yield Token(TokenType.RPAREN, ')', pos) pos += 1 else: - match = re.match(r"(:?\w|:|\+|-|\.|\[|\]|\\|/)+", input[pos:]) + match = re.match(r'(:?\w|:|\+|-|\.|\[|\]|\\|/)+', input[pos:]) if match: value = match.group(0) - if value == "or": + if value == 'or': yield Token(TokenType.OR, value, pos) - elif value == "and": + elif value == 'and': yield Token(TokenType.AND, value, pos) - elif value == "not": + elif value == 'not': yield Token(TokenType.NOT, value, pos) else: yield Token(TokenType.IDENT, value, pos) @@ -103,9 +104,9 @@ class Scanner: pos + 1, f'unexpected character "{input[pos]}"', ) - yield Token(TokenType.EOF, "", pos) + yield Token(TokenType.EOF, '', pos) - def accept(self, type: TokenType, *, reject: bool = False) -> Optional[Token]: + def accept(self, type: TokenType, *, reject: bool = False) -> Token | None: if self.current.type is type: token = self.current if token.type is not TokenType.EOF: @@ -118,8 +119,8 @@ class Scanner: def reject(self, expected: Sequence[TokenType]) -> NoReturn: raise ParseError( self.current.pos + 1, - "expected {}; got {}".format( - " OR ".join(type.value for type in expected), + 'expected {}; got {}'.format( + ' OR '.join(type.value for type in expected), self.current.type.value, ), ) @@ -128,7 +129,7 @@ class Scanner: # True, False and None are legal match expression identifiers, # but illegal as Python identifiers. To fix this, this prefix # is added to identifiers in the conversion to Python AST. -IDENT_PREFIX = "$" +IDENT_PREFIX = '$' def expression(s: Scanner) -> ast.Expression: @@ -176,7 +177,7 @@ class MatcherAdapter(Mapping[str, bool]): self.matcher = matcher def __getitem__(self, key: str) -> bool: - return self.matcher(key[len(IDENT_PREFIX) :]) + return self.matcher(key[len(IDENT_PREFIX):]) def __iter__(self) -> Iterator[str]: raise NotImplementedError() @@ -191,13 +192,13 @@ class Expression: The expression can be evaluated against different matchers. """ - __slots__ = ("code",) + __slots__ = ('code',) def __init__(self, code: types.CodeType) -> None: self.code = code @classmethod - def compile(self, input: str) -> "Expression": + def compile(self, input: str) -> Expression: """Compile a match expression. :param input: The input expression - one line. @@ -205,8 +206,8 @@ class Expression: astexpr = expression(Scanner(input)) code: types.CodeType = compile( astexpr, - filename="", - mode="eval", + filename='', + mode='eval', ) return Expression(code) @@ -219,5 +220,5 @@ class Expression: :returns: Whether the expression matches or not. """ - ret: bool = eval(self.code, {"__builtins__": {}}, MatcherAdapter(matcher)) + ret: bool = eval(self.code, {'__builtins__': {}}, MatcherAdapter(matcher)) return ret diff --git a/.venv/lib/python3.10/site-packages/_pytest/mark/structures.py b/.venv/lib/python3.10/site-packages/_pytest/mark/structures.py index 1da300c..70c0ae7 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/mark/structures.py +++ b/.venv/lib/python3.10/site-packages/_pytest/mark/structures.py @@ -1,7 +1,10 @@ # mypy: allow-untyped-defs +from __future__ import annotations + import collections.abc import dataclasses import inspect +import warnings from typing import Any from typing import Callable from typing import Collection @@ -21,37 +24,37 @@ from typing import Type from typing import TYPE_CHECKING from typing import TypeVar from typing import Union -import warnings -from .._code import getfslineno -from ..compat import ascii_escaped -from ..compat import NOTSET -from ..compat import NotSetType from _pytest.config import Config from _pytest.deprecated import check_ispytest from _pytest.deprecated import MARKED_FIXTURE from _pytest.outcomes import fail from _pytest.warning_types import PytestUnknownMarkWarning +from .._code import getfslineno +from ..compat import ascii_escaped +from ..compat import NOTSET +from ..compat import NotSetType + if TYPE_CHECKING: from ..nodes import Node -EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" +EMPTY_PARAMETERSET_OPTION = 'empty_parameter_set_mark' def istestfunc(func) -> bool: - return callable(func) and getattr(func, "__name__", "") != "" + return callable(func) and getattr(func, '__name__', '') != '' def get_empty_parameterset_mark( - config: Config, argnames: Sequence[str], func -) -> "MarkDecorator": + config: Config, argnames: Sequence[str], func, +) -> MarkDecorator: from ..nodes import Collector fs, lineno = getfslineno(func) - reason = "got empty parameter set %r, function %s at %s:%d" % ( + reason = 'got empty parameter set %r, function %s at %s:%d' % ( argnames, func.__name__, fs, @@ -59,15 +62,15 @@ def get_empty_parameterset_mark( ) requested_mark = config.getini(EMPTY_PARAMETERSET_OPTION) - if requested_mark in ("", None, "skip"): + if requested_mark in ('', None, 'skip'): mark = MARK_GEN.skip(reason=reason) - elif requested_mark == "xfail": + elif requested_mark == 'xfail': mark = MARK_GEN.xfail(reason=reason, run=False) - elif requested_mark == "fail_at_collect": + elif requested_mark == 'fail_at_collect': f_name = func.__name__ _, lineno = getfslineno(func) raise Collector.CollectError( - "Empty parameter set in '%s' at line %d" % (f_name, lineno + 1) + "Empty parameter set in '%s' at line %d" % (f_name, lineno + 1), ) else: raise LookupError(requested_mark) @@ -75,17 +78,17 @@ def get_empty_parameterset_mark( class ParameterSet(NamedTuple): - values: Sequence[Union[object, NotSetType]] - marks: Collection[Union["MarkDecorator", "Mark"]] - id: Optional[str] + values: Sequence[object | NotSetType] + marks: Collection[MarkDecorator | Mark] + id: str | None @classmethod def param( cls, *values: object, - marks: Union["MarkDecorator", Collection[Union["MarkDecorator", "Mark"]]] = (), - id: Optional[str] = None, - ) -> "ParameterSet": + marks: MarkDecorator | Collection[MarkDecorator | Mark] = (), + id: str | None = None, + ) -> ParameterSet: if isinstance(marks, MarkDecorator): marks = (marks,) else: @@ -93,16 +96,16 @@ class ParameterSet(NamedTuple): if id is not None: if not isinstance(id, str): - raise TypeError(f"Expected id to be a string, got {type(id)}: {id!r}") + raise TypeError(f'Expected id to be a string, got {type(id)}: {id!r}') id = ascii_escaped(id) return cls(values, marks, id) @classmethod def extract_from( cls, - parameterset: Union["ParameterSet", Sequence[object], object], + parameterset: ParameterSet | Sequence[object] | object, force_tuple: bool = False, - ) -> "ParameterSet": + ) -> ParameterSet: """Extract from an object or objects. :param parameterset: @@ -127,13 +130,13 @@ class ParameterSet(NamedTuple): @staticmethod def _parse_parametrize_args( - argnames: Union[str, Sequence[str]], - argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], *args, **kwargs, - ) -> Tuple[Sequence[str], bool]: + ) -> tuple[Sequence[str], bool]: if isinstance(argnames, str): - argnames = [x.strip() for x in argnames.split(",") if x.strip()] + argnames = [x.strip() for x in argnames.split(',') if x.strip()] force_tuple = len(argnames) == 1 else: force_tuple = False @@ -141,9 +144,9 @@ class ParameterSet(NamedTuple): @staticmethod def _parse_parametrize_parameters( - argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], + argvalues: Iterable[ParameterSet | Sequence[object] | object], force_tuple: bool, - ) -> List["ParameterSet"]: + ) -> list[ParameterSet]: return [ ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues ] @@ -151,12 +154,12 @@ class ParameterSet(NamedTuple): @classmethod def _for_parametrize( cls, - argnames: Union[str, Sequence[str]], - argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], func, config: Config, nodeid: str, - ) -> Tuple[Sequence[str], List["ParameterSet"]]: + ) -> tuple[Sequence[str], list[ParameterSet]]: argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) del argvalues @@ -167,9 +170,9 @@ class ParameterSet(NamedTuple): if len(param.values) != len(argnames): msg = ( '{nodeid}: in "parametrize" the number of names ({names_len}):\n' - " {names}\n" - "must be equal to the number of values ({values_len}):\n" - " {values}" + ' {names}\n' + 'must be equal to the number of values ({values_len}):\n' + ' {values}' ) fail( msg.format( @@ -186,7 +189,7 @@ class ParameterSet(NamedTuple): # parameter set with NOTSET values, with the "empty parameter set" mark applied to it. mark = get_empty_parameterset_mark(config, argnames, func) parameters.append( - ParameterSet(values=(NOTSET,) * len(argnames), marks=[mark], id=None) + ParameterSet(values=(NOTSET,) * len(argnames), marks=[mark], id=None), ) return argnames, parameters @@ -199,40 +202,40 @@ class Mark: #: Name of the mark. name: str #: Positional arguments of the mark decorator. - args: Tuple[Any, ...] + args: tuple[Any, ...] #: Keyword arguments of the mark decorator. kwargs: Mapping[str, Any] #: Source Mark for ids with parametrize Marks. - _param_ids_from: Optional["Mark"] = dataclasses.field(default=None, repr=False) + _param_ids_from: Mark | None = dataclasses.field(default=None, repr=False) #: Resolved/generated ids with parametrize Marks. - _param_ids_generated: Optional[Sequence[str]] = dataclasses.field( - default=None, repr=False + _param_ids_generated: Sequence[str] | None = dataclasses.field( + default=None, repr=False, ) def __init__( self, name: str, - args: Tuple[Any, ...], + args: tuple[Any, ...], kwargs: Mapping[str, Any], - param_ids_from: Optional["Mark"] = None, - param_ids_generated: Optional[Sequence[str]] = None, + param_ids_from: Mark | None = None, + param_ids_generated: Sequence[str] | None = None, *, _ispytest: bool = False, ) -> None: """:meta private:""" check_ispytest(_ispytest) # Weirdness to bypass frozen=True. - object.__setattr__(self, "name", name) - object.__setattr__(self, "args", args) - object.__setattr__(self, "kwargs", kwargs) - object.__setattr__(self, "_param_ids_from", param_ids_from) - object.__setattr__(self, "_param_ids_generated", param_ids_generated) + object.__setattr__(self, 'name', name) + object.__setattr__(self, 'args', args) + object.__setattr__(self, 'kwargs', kwargs) + object.__setattr__(self, '_param_ids_from', param_ids_from) + object.__setattr__(self, '_param_ids_generated', param_ids_generated) def _has_param_ids(self) -> bool: - return "ids" in self.kwargs or len(self.args) >= 4 + return 'ids' in self.kwargs or len(self.args) >= 4 - def combined_with(self, other: "Mark") -> "Mark": + def combined_with(self, other: Mark) -> Mark: """Return a new Mark which is a combination of this Mark and another Mark. @@ -244,8 +247,8 @@ class Mark: assert self.name == other.name # Remember source of ids with parametrize Marks. - param_ids_from: Optional[Mark] = None - if self.name == "parametrize": + param_ids_from: Mark | None = None + if self.name == 'parametrize': if other._has_param_ids(): param_ids_from = other elif self._has_param_ids(): @@ -263,7 +266,7 @@ class Mark: # A generic parameter designating an object to which a Mark may # be applied -- a test function (callable) or class. # Note: a lambda is not allowed, but this can't be represented. -Markable = TypeVar("Markable", bound=Union[Callable[..., object], type]) +Markable = TypeVar('Markable', bound=Union[Callable[..., object], type]) @dataclasses.dataclass @@ -315,7 +318,7 @@ class MarkDecorator: return self.mark.name @property - def args(self) -> Tuple[Any, ...]: + def args(self) -> tuple[Any, ...]: """Alias for mark.args.""" return self.mark.args @@ -329,7 +332,7 @@ class MarkDecorator: """:meta private:""" return self.name # for backward-compat (2.4.1 had this attr) - def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator": + def with_args(self, *args: object, **kwargs: object) -> MarkDecorator: """Return a MarkDecorator with extra arguments added. Unlike calling the MarkDecorator, with_args() can be used even @@ -346,7 +349,7 @@ class MarkDecorator: pass @overload - def __call__(self, *args: object, **kwargs: object) -> "MarkDecorator": + def __call__(self, *args: object, **kwargs: object) -> MarkDecorator: pass def __call__(self, *args: object, **kwargs: object): @@ -361,10 +364,10 @@ class MarkDecorator: def get_unpacked_marks( - obj: Union[object, type], + obj: object | type, *, consider_mro: bool = True, -) -> List[Mark]: +) -> list[Mark]: """Obtain the unpacked marks that are stored on an object. If obj is a class and consider_mro is true, return marks applied to @@ -373,10 +376,10 @@ def get_unpacked_marks( """ if isinstance(obj, type): if not consider_mro: - mark_lists = [obj.__dict__.get("pytestmark", [])] + mark_lists = [obj.__dict__.get('pytestmark', [])] else: mark_lists = [ - x.__dict__.get("pytestmark", []) for x in reversed(obj.__mro__) + x.__dict__.get('pytestmark', []) for x in reversed(obj.__mro__) ] mark_list = [] for item in mark_lists: @@ -385,7 +388,7 @@ def get_unpacked_marks( else: mark_list.append(item) else: - mark_attribute = getattr(obj, "pytestmark", []) + mark_attribute = getattr(obj, 'pytestmark', []) if isinstance(mark_attribute, list): mark_list = mark_attribute else: @@ -394,7 +397,7 @@ def get_unpacked_marks( def normalize_mark_list( - mark_list: Iterable[Union[Mark, MarkDecorator]], + mark_list: Iterable[Mark | MarkDecorator], ) -> Iterable[Mark]: """ Normalize an iterable of Mark or MarkDecorator objects into a list of marks @@ -404,9 +407,9 @@ def normalize_mark_list( :returns: A new list of the extracted Mark objects """ for mark in mark_list: - mark_obj = getattr(mark, "mark", mark) + mark_obj = getattr(mark, 'mark', mark) if not isinstance(mark_obj, Mark): - raise TypeError(f"got {mark_obj!r} instead of Mark") + raise TypeError(f'got {mark_obj!r} instead of Mark') yield mark_obj @@ -438,14 +441,14 @@ if TYPE_CHECKING: ... @overload - def __call__(self, reason: str = ...) -> "MarkDecorator": + def __call__(self, reason: str = ...) -> MarkDecorator: ... class _SkipifMarkDecorator(MarkDecorator): def __call__( # type: ignore[override] self, - condition: Union[str, bool] = ..., - *conditions: Union[str, bool], + condition: str | bool = ..., + *conditions: str | bool, reason: str = ..., ) -> MarkDecorator: ... @@ -458,13 +461,13 @@ if TYPE_CHECKING: @overload def __call__( self, - condition: Union[str, bool] = False, - *conditions: Union[str, bool], + condition: str | bool = False, + *conditions: str | bool, reason: str = ..., run: bool = ..., - raises: Union[ - None, Type[BaseException], Tuple[Type[BaseException], ...] - ] = ..., + raises: ( + None | type[BaseException] | tuple[type[BaseException], ...] + ) = ..., strict: bool = ..., ) -> MarkDecorator: ... @@ -472,17 +475,15 @@ if TYPE_CHECKING: class _ParametrizeMarkDecorator(MarkDecorator): def __call__( # type: ignore[override] self, - argnames: Union[str, Sequence[str]], - argvalues: Iterable[Union[ParameterSet, Sequence[object], object]], + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], *, - indirect: Union[bool, Sequence[str]] = ..., - ids: Optional[ - Union[ - Iterable[Union[None, str, float, int, bool]], - Callable[[Any], Optional[object]], - ] - ] = ..., - scope: Optional[_ScopeName] = ..., + indirect: bool | Sequence[str] = ..., + ids: None | ( + Iterable[None | str | float | int | bool] | + Callable[[Any], object | None] + ) = ..., + scope: _ScopeName | None = ..., ) -> MarkDecorator: ... @@ -523,24 +524,24 @@ class MarkGenerator: def __init__(self, *, _ispytest: bool = False) -> None: check_ispytest(_ispytest) - self._config: Optional[Config] = None - self._markers: Set[str] = set() + self._config: Config | None = None + self._markers: set[str] = set() def __getattr__(self, name: str) -> MarkDecorator: """Generate a new :class:`MarkDecorator` with the given name.""" - if name[0] == "_": - raise AttributeError("Marker name must NOT start with underscore") + if name[0] == '_': + raise AttributeError('Marker name must NOT start with underscore') if self._config is not None: # We store a set of markers as a performance optimisation - if a mark # name is in the set we definitely know it, but a mark may be known and # not in the set. We therefore start by updating the set! if name not in self._markers: - for line in self._config.getini("markers"): + for line in self._config.getini('markers'): # example lines: "skipif(condition): skip the given test if..." # or "hypothesis: tests which use Hypothesis", so to get the # marker name we split on both `:` and `(`. - marker = line.split(":")[0].split("(")[0].strip() + marker = line.split(':')[0].split('(')[0].strip() self._markers.add(marker) # If the name is not in the set of known marks after updating, @@ -548,19 +549,19 @@ class MarkGenerator: if name not in self._markers: if self._config.option.strict_markers or self._config.option.strict: fail( - f"{name!r} not found in `markers` configuration option", + f'{name!r} not found in `markers` configuration option', pytrace=False, ) # Raise a specific error for common misspellings of "parametrize". - if name in ["parameterize", "parametrise", "parameterise"]: + if name in ['parameterize', 'parametrise', 'parameterise']: __tracebackhide__ = True fail(f"Unknown '{name}' mark, did you mean 'parametrize'?") warnings.warn( - "Unknown pytest.mark.%s - is this a typo? You can register " - "custom marks to avoid this warning - for details, see " - "https://docs.pytest.org/en/stable/how-to/mark.html" % name, + 'Unknown pytest.mark.%s - is this a typo? You can register ' + 'custom marks to avoid this warning - for details, see ' + 'https://docs.pytest.org/en/stable/how-to/mark.html' % name, PytestUnknownMarkWarning, 2, ) @@ -573,9 +574,9 @@ MARK_GEN = MarkGenerator(_ispytest=True) @final class NodeKeywords(MutableMapping[str, Any]): - __slots__ = ("node", "parent", "_markers") + __slots__ = ('node', 'parent', '_markers') - def __init__(self, node: "Node") -> None: + def __init__(self, node: Node) -> None: self.node = node self.parent = node.parent self._markers = {node.name: True} @@ -596,21 +597,21 @@ class NodeKeywords(MutableMapping[str, Any]): def __contains__(self, key: object) -> bool: return ( - key in self._markers - or self.parent is not None - and key in self.parent.keywords + key in self._markers or + self.parent is not None and + key in self.parent.keywords ) def update( # type: ignore[override] self, - other: Union[Mapping[str, Any], Iterable[Tuple[str, Any]]] = (), + other: Mapping[str, Any] | Iterable[tuple[str, Any]] = (), **kwds: Any, ) -> None: self._markers.update(other) self._markers.update(kwds) def __delitem__(self, key: str) -> None: - raise ValueError("cannot delete key in keywords dict") + raise ValueError('cannot delete key in keywords dict') def __iter__(self) -> Iterator[str]: # Doesn't need to be fast. @@ -626,4 +627,4 @@ class NodeKeywords(MutableMapping[str, Any]): return sum(1 for keyword in self) def __repr__(self) -> str: - return f"" + return f'' diff --git a/.venv/lib/python3.10/site-packages/_pytest/monkeypatch.py b/.venv/lib/python3.10/site-packages/_pytest/monkeypatch.py index e96a938..067a15e 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/monkeypatch.py +++ b/.venv/lib/python3.10/site-packages/_pytest/monkeypatch.py @@ -1,9 +1,12 @@ # mypy: allow-untyped-defs """Monkeypatching and mocking functionality.""" -from contextlib import contextmanager +from __future__ import annotations + import os import re import sys +import warnings +from contextlib import contextmanager from typing import Any from typing import final from typing import Generator @@ -15,21 +18,20 @@ from typing import overload from typing import Tuple from typing import TypeVar from typing import Union -import warnings from _pytest.fixtures import fixture from _pytest.warning_types import PytestWarning -RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$") +RE_IMPORT_ERROR_NAME = re.compile(r'^No module named (.*)$') -K = TypeVar("K") -V = TypeVar("V") +K = TypeVar('K') +V = TypeVar('V') @fixture -def monkeypatch() -> Generator["MonkeyPatch", None, None]: +def monkeypatch() -> Generator[MonkeyPatch, None, None]: """A convenient fixture for monkey-patching. The fixture provides these methods to modify objects, dictionaries, or @@ -60,12 +62,12 @@ def monkeypatch() -> Generator["MonkeyPatch", None, None]: def resolve(name: str) -> object: # Simplified from zope.dottedname. - parts = name.split(".") + parts = name.split('.') used = parts.pop(0) found: object = __import__(used) for part in parts: - used += "." + part + used += '.' + part try: found = getattr(found, part) except AttributeError: @@ -81,7 +83,7 @@ def resolve(name: str) -> object: if expected == used: raise else: - raise ImportError(f"import error in {used}: {ex}") from ex + raise ImportError(f'import error in {used}: {ex}') from ex found = annotated_getattr(found, part, used) return found @@ -91,15 +93,15 @@ def annotated_getattr(obj: object, name: str, ann: str) -> object: obj = getattr(obj, name) except AttributeError as e: raise AttributeError( - f"{type(obj).__name__!r} object at {ann} has no attribute {name!r}" + f'{type(obj).__name__!r} object at {ann} has no attribute {name!r}', ) from e return obj -def derive_importpath(import_path: str, raising: bool) -> Tuple[str, object]: - if not isinstance(import_path, str) or "." not in import_path: - raise TypeError(f"must be absolute import path string, not {import_path!r}") - module, attr = import_path.rsplit(".", 1) +def derive_importpath(import_path: str, raising: bool) -> tuple[str, object]: + if not isinstance(import_path, str) or '.' not in import_path: + raise TypeError(f'must be absolute import path string, not {import_path!r}') + module, attr = import_path.rsplit('.', 1) target = resolve(module) if raising: annotated_getattr(target, attr, ann=module) @@ -108,7 +110,7 @@ def derive_importpath(import_path: str, raising: bool) -> Tuple[str, object]: class Notset: def __repr__(self) -> str: - return "" + return '' notset = Notset() @@ -129,14 +131,14 @@ class MonkeyPatch: """ def __init__(self) -> None: - self._setattr: List[Tuple[object, str, object]] = [] - self._setitem: List[Tuple[Mapping[Any, Any], object, object]] = [] - self._cwd: Optional[str] = None - self._savesyspath: Optional[List[str]] = None + self._setattr: list[tuple[object, str, object]] = [] + self._setitem: list[tuple[Mapping[Any, Any], object, object]] = [] + self._cwd: str | None = None + self._savesyspath: list[str] | None = None @classmethod @contextmanager - def context(cls) -> Generator["MonkeyPatch", None, None]: + def context(cls) -> Generator[MonkeyPatch, None, None]: """Context manager that returns a new :class:`MonkeyPatch` object which undoes any patching done inside the ``with`` block upon exit. @@ -182,8 +184,8 @@ class MonkeyPatch: def setattr( self, - target: Union[str, object], - name: Union[object, str], + target: str | object, + name: object | str, value: object = notset, raising: bool = True, ) -> None: @@ -228,23 +230,23 @@ class MonkeyPatch: if isinstance(value, Notset): if not isinstance(target, str): raise TypeError( - "use setattr(target, name, value) or " - "setattr(target, value) with target being a dotted " - "import string" + 'use setattr(target, name, value) or ' + 'setattr(target, value) with target being a dotted ' + 'import string', ) value = name name, target = derive_importpath(target, raising) else: if not isinstance(name, str): raise TypeError( - "use setattr(target, name, value) with name being a string or " - "setattr(target, value) with target being a dotted " - "import string" + 'use setattr(target, name, value) with name being a string or ' + 'setattr(target, value) with target being a dotted ' + 'import string', ) oldval = getattr(target, name, notset) if raising and oldval is notset: - raise AttributeError(f"{target!r} has no attribute {name!r}") + raise AttributeError(f'{target!r} has no attribute {name!r}') # avoid class descriptors like staticmethod/classmethod if inspect.isclass(target): @@ -254,8 +256,8 @@ class MonkeyPatch: def delattr( self, - target: Union[object, str], - name: Union[str, Notset] = notset, + target: object | str, + name: str | Notset = notset, raising: bool = True, ) -> None: """Delete attribute ``name`` from ``target``. @@ -273,9 +275,9 @@ class MonkeyPatch: if isinstance(name, Notset): if not isinstance(target, str): raise TypeError( - "use delattr(target, name) or " - "delattr(target) with target being a dotted " - "import string" + 'use delattr(target, name) or ' + 'delattr(target) with target being a dotted ' + 'import string', ) name, target = derive_importpath(target, raising) @@ -310,7 +312,7 @@ class MonkeyPatch: # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict del dic[name] # type: ignore[attr-defined] - def setenv(self, name: str, value: str, prepend: Optional[str] = None) -> None: + def setenv(self, name: str, value: str, prepend: str | None = None) -> None: """Set environment variable ``name`` to ``value``. If ``prepend`` is a character, read the current environment variable @@ -320,8 +322,8 @@ class MonkeyPatch: if not isinstance(value, str): warnings.warn( # type: ignore[unreachable] PytestWarning( - f"Value of environment variable {name} type should be str, but got " - f"{value!r} (type: {type(value).__name__}); converted to str implicitly" + f'Value of environment variable {name} type should be str, but got ' + f'{value!r} (type: {type(value).__name__}); converted to str implicitly', ), stacklevel=2, ) @@ -347,7 +349,7 @@ class MonkeyPatch: # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 # this is only needed when pkg_resources was already loaded by the namespace package - if "pkg_resources" in sys.modules: + if 'pkg_resources' in sys.modules: from pkg_resources import fixup_namespace_packages fixup_namespace_packages(str(path)) @@ -363,7 +365,7 @@ class MonkeyPatch: invalidate_caches() - def chdir(self, path: Union[str, "os.PathLike[str]"]) -> None: + def chdir(self, path: str | os.PathLike[str]) -> None: """Change the current working directory to the specified path. :param path: diff --git a/.venv/lib/python3.10/site-packages/_pytest/nodes.py b/.venv/lib/python3.10/site-packages/_pytest/nodes.py index cff1500..f590d22 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/nodes.py +++ b/.venv/lib/python3.10/site-packages/_pytest/nodes.py @@ -1,9 +1,12 @@ # mypy: allow-untyped-defs +from __future__ import annotations + import abc -from functools import cached_property -from inspect import signature import os import pathlib +import warnings +from functools import cached_property +from inspect import signature from pathlib import Path from typing import Any from typing import Callable @@ -21,11 +24,9 @@ from typing import Type from typing import TYPE_CHECKING from typing import TypeVar from typing import Union -import warnings - -import pluggy import _pytest._code +import pluggy from _pytest._code import getfslineno from _pytest._code.code import ExceptionInfo from _pytest._code.code import TerminalRepr @@ -53,18 +54,18 @@ if TYPE_CHECKING: from _pytest.main import Session -SEP = "/" +SEP = '/' tracebackcutdir = Path(_pytest.__file__).parent -_T = TypeVar("_T") +_T = TypeVar('_T') def _imply_path( - node_type: Type["Node"], - path: Optional[Path], - fspath: Optional[LEGACY_PATH], + node_type: type[Node], + path: Path | None, + fspath: LEGACY_PATH | None, ) -> Path: if fspath is not None: warnings.warn( @@ -82,7 +83,7 @@ def _imply_path( return Path(fspath) -_NodeType = TypeVar("_NodeType", bound="Node") +_NodeType = TypeVar('_NodeType', bound='Node') class NodeMeta(abc.ABCMeta): @@ -102,28 +103,28 @@ class NodeMeta(abc.ABCMeta): def __call__(cls, *k, **kw) -> NoReturn: msg = ( - "Direct construction of {name} has been deprecated, please use {name}.from_parent.\n" - "See " - "https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent" - " for more details." - ).format(name=f"{cls.__module__}.{cls.__name__}") + 'Direct construction of {name} has been deprecated, please use {name}.from_parent.\n' + 'See ' + 'https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent' + ' for more details.' + ).format(name=f'{cls.__module__}.{cls.__name__}') fail(msg, pytrace=False) - def _create(cls: Type[_T], *k, **kw) -> _T: + def _create(cls: type[_T], *k, **kw) -> _T: try: return super().__call__(*k, **kw) # type: ignore[no-any-return,misc] except TypeError: - sig = signature(getattr(cls, "__init__")) + sig = signature(getattr(cls, '__init__')) known_kw = {k: v for k, v in kw.items() if k in sig.parameters} from .warning_types import PytestDeprecationWarning warnings.warn( PytestDeprecationWarning( - f"{cls} is not using a cooperative constructor and only takes {set(known_kw)}.\n" - "See https://docs.pytest.org/en/stable/deprecations.html" - "#constructors-of-custom-pytest-node-subclasses-should-take-kwargs " - "for more details." - ) + f'{cls} is not using a cooperative constructor and only takes {set(known_kw)}.\n' + 'See https://docs.pytest.org/en/stable/deprecations.html' + '#constructors-of-custom-pytest-node-subclasses-should-take-kwargs ' + 'for more details.', + ), ) return super().__call__(*k, **known_kw) # type: ignore[no-any-return,misc] @@ -147,25 +148,25 @@ class Node(abc.ABC, metaclass=NodeMeta): # Use __slots__ to make attribute access faster. # Note that __dict__ is still available. __slots__ = ( - "name", - "parent", - "config", - "session", - "path", - "_nodeid", - "_store", - "__dict__", + 'name', + 'parent', + 'config', + 'session', + 'path', + '_nodeid', + '_store', + '__dict__', ) def __init__( self, name: str, - parent: "Optional[Node]" = None, - config: Optional[Config] = None, - session: "Optional[Session]" = None, - fspath: Optional[LEGACY_PATH] = None, - path: Optional[Path] = None, - nodeid: Optional[str] = None, + parent: Optional[Node] = None, + config: Config | None = None, + session: Optional[Session] = None, + fspath: LEGACY_PATH | None = None, + path: Path | None = None, + nodeid: str | None = None, ) -> None: #: A unique name within the scope of the parent node. self.name: str = name @@ -178,7 +179,7 @@ class Node(abc.ABC, metaclass=NodeMeta): self.config: Config = config else: if not parent: - raise TypeError("config or parent must be provided") + raise TypeError('config or parent must be provided') self.config = parent.config if session: @@ -186,11 +187,11 @@ class Node(abc.ABC, metaclass=NodeMeta): self.session: Session = session else: if not parent: - raise TypeError("session or parent must be provided") + raise TypeError('session or parent must be provided') self.session = parent.session if path is None and fspath is None: - path = getattr(parent, "path", None) + path = getattr(parent, 'path', None) #: Filesystem path where this node was collected from (can be None). self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath) @@ -199,18 +200,18 @@ class Node(abc.ABC, metaclass=NodeMeta): self.keywords: MutableMapping[str, Any] = NodeKeywords(self) #: The marker objects belonging to this node. - self.own_markers: List[Mark] = [] + self.own_markers: list[Mark] = [] #: Allow adding of extra keywords to use for matching. - self.extra_keyword_matches: Set[str] = set() + self.extra_keyword_matches: set[str] = set() if nodeid is not None: - assert "::()" not in nodeid + assert '::()' not in nodeid self._nodeid = nodeid else: if not self.parent: - raise TypeError("nodeid or parent must be provided") - self._nodeid = self.parent.nodeid + "::" + self.name + raise TypeError('nodeid or parent must be provided') + self._nodeid = self.parent.nodeid + '::' + self.name #: A place where plugins can store information on the node for their #: own use. @@ -219,7 +220,7 @@ class Node(abc.ABC, metaclass=NodeMeta): self._store = self.stash @classmethod - def from_parent(cls, parent: "Node", **kw) -> "Self": + def from_parent(cls, parent: Node, **kw) -> Self: """Public constructor for Nodes. This indirection got introduced in order to enable removing @@ -230,10 +231,10 @@ class Node(abc.ABC, metaclass=NodeMeta): :param parent: The parent node of this Node. """ - if "config" in kw: - raise TypeError("config is not a valid argument for from_parent") - if "session" in kw: - raise TypeError("session is not a valid argument for from_parent") + if 'config' in kw: + raise TypeError('config is not a valid argument for from_parent') + if 'session' in kw: + raise TypeError('session is not a valid argument for from_parent') return cls._create(parent=parent, **kw) @property @@ -242,7 +243,7 @@ class Node(abc.ABC, metaclass=NodeMeta): return self.session.gethookproxy(self.path) def __repr__(self) -> str: - return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None)) + return '<{} {}>'.format(self.__class__.__name__, getattr(self, 'name', None)) def warn(self, warning: Warning) -> None: """Issue a warning for this Node. @@ -268,7 +269,7 @@ class Node(abc.ABC, metaclass=NodeMeta): # enforce type checks here to avoid getting a generic type error later otherwise. if not isinstance(warning, Warning): raise ValueError( - f"warning must be an instance of Warning or subclass, got {warning!r}" + f'warning must be an instance of Warning or subclass, got {warning!r}', ) path, lineno = get_fslocation_from_item(self) assert lineno is not None @@ -295,22 +296,22 @@ class Node(abc.ABC, metaclass=NodeMeta): def teardown(self) -> None: pass - def iter_parents(self) -> Iterator["Node"]: + def iter_parents(self) -> Iterator[Node]: """Iterate over all parent collectors starting from and including self up to the root of the collection tree. .. versionadded:: 8.1 """ - parent: Optional[Node] = self + parent: Node | None = self while parent is not None: yield parent parent = parent.parent - def listchain(self) -> List["Node"]: + def listchain(self) -> list[Node]: """Return a list of all parent collectors starting from the root of the collection tree down to and including self.""" chain = [] - item: Optional[Node] = self + item: Node | None = self while item is not None: chain.append(item) item = item.parent @@ -318,7 +319,7 @@ class Node(abc.ABC, metaclass=NodeMeta): return chain def add_marker( - self, marker: Union[str, MarkDecorator], append: bool = True + self, marker: str | MarkDecorator, append: bool = True, ) -> None: """Dynamically add a marker object to the node. @@ -334,14 +335,14 @@ class Node(abc.ABC, metaclass=NodeMeta): elif isinstance(marker, str): marker_ = getattr(MARK_GEN, marker) else: - raise ValueError("is not a string or pytest.mark.* Marker") + raise ValueError('is not a string or pytest.mark.* Marker') self.keywords[marker_.name] = marker_ if append: self.own_markers.append(marker_.mark) else: self.own_markers.insert(0, marker_.mark) - def iter_markers(self, name: Optional[str] = None) -> Iterator[Mark]: + def iter_markers(self, name: str | None = None) -> Iterator[Mark]: """Iterate over all markers of the node. :param name: If given, filter the results by the name attribute. @@ -350,8 +351,8 @@ class Node(abc.ABC, metaclass=NodeMeta): return (x[1] for x in self.iter_markers_with_node(name=name)) def iter_markers_with_node( - self, name: Optional[str] = None - ) -> Iterator[Tuple["Node", Mark]]: + self, name: str | None = None, + ) -> Iterator[tuple[Node, Mark]]: """Iterate over all markers of the node. :param name: If given, filter the results by the name attribute. @@ -359,11 +360,11 @@ class Node(abc.ABC, metaclass=NodeMeta): """ for node in self.iter_parents(): for mark in node.own_markers: - if name is None or getattr(mark, "name", None) == name: + if name is None or getattr(mark, 'name', None) == name: yield node, mark @overload - def get_closest_marker(self, name: str) -> Optional[Mark]: + def get_closest_marker(self, name: str) -> Mark | None: ... @overload @@ -371,8 +372,8 @@ class Node(abc.ABC, metaclass=NodeMeta): ... def get_closest_marker( - self, name: str, default: Optional[Mark] = None - ) -> Optional[Mark]: + self, name: str, default: Mark | None = None, + ) -> Mark | None: """Return the first marker matching the name, from closest (for example function) to farther level (for example module level). @@ -381,14 +382,14 @@ class Node(abc.ABC, metaclass=NodeMeta): """ return next(self.iter_markers(name=name), default) - def listextrakeywords(self) -> Set[str]: + def listextrakeywords(self) -> set[str]: """Return a set of all extra keywords in self and any parents.""" - extra_keywords: Set[str] = set() + extra_keywords: set[str] = set() for item in self.listchain(): extra_keywords.update(item.extra_keyword_matches) return extra_keywords - def listnames(self) -> List[str]: + def listnames(self) -> list[str]: return [x.name for x in self.listchain()] def addfinalizer(self, fin: Callable[[], object]) -> None: @@ -400,7 +401,7 @@ class Node(abc.ABC, metaclass=NodeMeta): """ self.session._setupstate.addfinalizer(fin, self) - def getparent(self, cls: Type[_NodeType]) -> Optional[_NodeType]: + def getparent(self, cls: type[_NodeType]) -> _NodeType | None: """Get the closest parent node (including self) which is an instance of the given class. @@ -418,7 +419,7 @@ class Node(abc.ABC, metaclass=NodeMeta): def _repr_failure_py( self, excinfo: ExceptionInfo[BaseException], - style: "Optional[_TracebackStyle]" = None, + style: Optional[_TracebackStyle] = None, ) -> TerminalRepr: from _pytest.fixtures import FixtureLookupError @@ -426,26 +427,26 @@ class Node(abc.ABC, metaclass=NodeMeta): excinfo = ExceptionInfo.from_exception(excinfo.value.cause) if isinstance(excinfo.value, fail.Exception): if not excinfo.value.pytrace: - style = "value" + style = 'value' if isinstance(excinfo.value, FixtureLookupError): return excinfo.value.formatrepr() - tbfilter: Union[bool, Callable[[ExceptionInfo[BaseException]], Traceback]] - if self.config.getoption("fulltrace", False): - style = "long" + tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] + if self.config.getoption('fulltrace', False): + style = 'long' tbfilter = False else: tbfilter = self._traceback_filter - if style == "auto": - style = "long" + if style == 'auto': + style = 'long' # XXX should excinfo.getrepr record all data and toterminal() process it? if style is None: - if self.config.getoption("tbstyle", "auto") == "short": - style = "short" + if self.config.getoption('tbstyle', 'auto') == 'short': + style = 'short' else: - style = "long" + style = 'long' - if self.config.getoption("verbose", 0) > 1: + if self.config.getoption('verbose', 0) > 1: truncate_locals = False else: truncate_locals = True @@ -464,7 +465,7 @@ class Node(abc.ABC, metaclass=NodeMeta): return excinfo.getrepr( funcargs=True, abspath=abspath, - showlocals=self.config.getoption("showlocals", False), + showlocals=self.config.getoption('showlocals', False), style=style, tbfilter=tbfilter, truncate_locals=truncate_locals, @@ -473,8 +474,8 @@ class Node(abc.ABC, metaclass=NodeMeta): def repr_failure( self, excinfo: ExceptionInfo[BaseException], - style: "Optional[_TracebackStyle]" = None, - ) -> Union[str, TerminalRepr]: + style: Optional[_TracebackStyle] = None, + ) -> str | TerminalRepr: """Return a representation of a collection or test failure. .. seealso:: :ref:`non-python tests` @@ -484,7 +485,7 @@ class Node(abc.ABC, metaclass=NodeMeta): return self._repr_failure_py(excinfo, style) -def get_fslocation_from_item(node: "Node") -> Tuple[Union[str, Path], Optional[int]]: +def get_fslocation_from_item(node: Node) -> tuple[str | Path, int | None]: """Try to extract the actual location from a node, depending on available attributes: * "location": a pair (path, lineno) @@ -494,13 +495,13 @@ def get_fslocation_from_item(node: "Node") -> Tuple[Union[str, Path], Optional[i :rtype: A tuple of (str|Path, int) with filename and 0-based line number. """ # See Item.location. - location: Optional[Tuple[str, Optional[int], str]] = getattr(node, "location", None) + location: tuple[str, int | None, str] | None = getattr(node, 'location', None) if location is not None: return location[:2] - obj = getattr(node, "obj", None) + obj = getattr(node, 'obj', None) if obj is not None: return getfslineno(obj) - return getattr(node, "path", "unknown location"), -1 + return getattr(node, 'path', 'unknown location'), -1 class Collector(Node, abc.ABC): @@ -514,34 +515,34 @@ class Collector(Node, abc.ABC): """An error during collection, contains a custom message.""" @abc.abstractmethod - def collect(self) -> Iterable[Union["Item", "Collector"]]: + def collect(self) -> Iterable[Item | Collector]: """Collect children (items and collectors) for this collector.""" - raise NotImplementedError("abstract") + raise NotImplementedError('abstract') # TODO: This omits the style= parameter which breaks Liskov Substitution. def repr_failure( # type: ignore[override] - self, excinfo: ExceptionInfo[BaseException] - ) -> Union[str, TerminalRepr]: + self, excinfo: ExceptionInfo[BaseException], + ) -> str | TerminalRepr: """Return a representation of a collection failure. :param excinfo: Exception information for the failure. """ if isinstance(excinfo.value, self.CollectError) and not self.config.getoption( - "fulltrace", False + 'fulltrace', False, ): exc = excinfo.value return str(exc.args[0]) # Respect explicit tbstyle option, but default to "short" # (_repr_failure_py uses "long" with "fulltrace" option always). - tbstyle = self.config.getoption("tbstyle", "auto") - if tbstyle == "auto": - tbstyle = "short" + tbstyle = self.config.getoption('tbstyle', 'auto') + if tbstyle == 'auto': + tbstyle = 'short' return self._repr_failure_py(excinfo, style=tbstyle) def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: - if hasattr(self, "path"): + if hasattr(self, 'path'): traceback = excinfo.traceback ntraceback = traceback.cut(path=self.path) if ntraceback == traceback: @@ -550,11 +551,11 @@ class Collector(Node, abc.ABC): return excinfo.traceback -def _check_initialpaths_for_relpath(session: "Session", path: Path) -> Optional[str]: +def _check_initialpaths_for_relpath(session: Session, path: Path) -> str | None: for initial_path in session._initialpaths: if commonpath(path, initial_path) == initial_path: rel = str(path.relative_to(initial_path)) - return "" if rel == "." else rel + return '' if rel == '.' else rel return None @@ -563,14 +564,14 @@ class FSCollector(Collector, abc.ABC): def __init__( self, - fspath: Optional[LEGACY_PATH] = None, - path_or_parent: Optional[Union[Path, Node]] = None, - path: Optional[Path] = None, - name: Optional[str] = None, - parent: Optional[Node] = None, - config: Optional[Config] = None, - session: Optional["Session"] = None, - nodeid: Optional[str] = None, + fspath: LEGACY_PATH | None = None, + path_or_parent: Path | Node | None = None, + path: Path | None = None, + name: str | None = None, + parent: Node | None = None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, ) -> None: if path_or_parent: if isinstance(path_or_parent, Node): @@ -620,10 +621,10 @@ class FSCollector(Collector, abc.ABC): cls, parent, *, - fspath: Optional[LEGACY_PATH] = None, - path: Optional[Path] = None, + fspath: LEGACY_PATH | None = None, + path: Path | None = None, **kw, - ) -> "Self": + ) -> Self: """The public constructor.""" return super().from_parent(parent=parent, fspath=fspath, path=path, **kw) @@ -665,9 +666,9 @@ class Item(Node, abc.ABC): self, name, parent=None, - config: Optional[Config] = None, - session: Optional["Session"] = None, - nodeid: Optional[str] = None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, **kw, ) -> None: # The first two arguments are intentionally passed positionally, @@ -682,11 +683,11 @@ class Item(Node, abc.ABC): nodeid=nodeid, **kw, ) - self._report_sections: List[Tuple[str, str, str]] = [] + self._report_sections: list[tuple[str, str, str]] = [] #: A list of tuples (name, value) that holds user defined properties #: for this test. - self.user_properties: List[Tuple[str, object]] = [] + self.user_properties: list[tuple[str, object]] = [] self._check_item_and_collector_diamond_inheritance() @@ -701,21 +702,21 @@ class Item(Node, abc.ABC): # for the same class more than once, which is not helpful. # It is a hack, but was deemed acceptable in order to avoid # flooding the user in the common case. - attr_name = "_pytest_diamond_inheritance_warning_shown" + attr_name = '_pytest_diamond_inheritance_warning_shown' if getattr(cls, attr_name, False): return setattr(cls, attr_name, True) - problems = ", ".join( + problems = ', '.join( base.__name__ for base in cls.__bases__ if issubclass(base, Collector) ) if problems: warnings.warn( - f"{cls.__name__} is an Item subclass and should not be a collector, " - f"however its bases {problems} are collectors.\n" - "Please split the Collectors and the Item into separate node types.\n" - "Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n" - "example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/", + f'{cls.__name__} is an Item subclass and should not be a collector, ' + f'however its bases {problems} are collectors.\n' + 'Please split the Collectors and the Item into separate node types.\n' + 'Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n' + 'example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/', PytestWarning, ) @@ -727,7 +728,7 @@ class Item(Node, abc.ABC): .. seealso:: :ref:`non-python tests` """ - raise NotImplementedError("runtest must be implemented by Item subclass") + raise NotImplementedError('runtest must be implemented by Item subclass') def add_report_section(self, when: str, key: str, content: str) -> None: """Add a new report section, similar to what's done internally to add @@ -746,7 +747,7 @@ class Item(Node, abc.ABC): if content: self._report_sections.append((when, key, content)) - def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: """Get location information for this item for test reports. Returns a tuple with three elements: @@ -757,10 +758,10 @@ class Item(Node, abc.ABC): .. seealso:: :ref:`non-python tests` """ - return self.path, None, "" + return self.path, None, '' @cached_property - def location(self) -> Tuple[str, Optional[int], str]: + def location(self) -> tuple[str, int | None, str]: """ Returns a tuple of ``(relfspath, lineno, testname)`` for this item where ``relfspath`` is file path relative to ``config.rootpath`` diff --git a/.venv/lib/python3.10/site-packages/_pytest/outcomes.py b/.venv/lib/python3.10/site-packages/_pytest/outcomes.py index e2a816f..045026b 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/outcomes.py +++ b/.venv/lib/python3.10/site-packages/_pytest/outcomes.py @@ -1,5 +1,6 @@ """Exception classes and constants handling test outcomes as well as functions creating them.""" +from __future__ import annotations import sys from typing import Any @@ -16,11 +17,11 @@ class OutcomeException(BaseException): """OutcomeException and its subclass instances indicate and contain info about test and collection outcomes.""" - def __init__(self, msg: Optional[str] = None, pytrace: bool = True) -> None: + def __init__(self, msg: str | None = None, pytrace: bool = True) -> None: if msg is not None and not isinstance(msg, str): error_msg = ( # type: ignore[unreachable] "{} expected string as 'msg' parameter, got '{}' instead.\n" - "Perhaps you meant to use a mark?" + 'Perhaps you meant to use a mark?' ) raise TypeError(error_msg.format(type(self).__name__, type(msg).__name__)) super().__init__(msg) @@ -30,7 +31,7 @@ class OutcomeException(BaseException): def __repr__(self) -> str: if self.msg is not None: return self.msg - return f"<{self.__class__.__name__} instance>" + return f'<{self.__class__.__name__} instance>' __str__ = __repr__ @@ -41,11 +42,11 @@ TEST_OUTCOME = (OutcomeException, Exception) class Skipped(OutcomeException): # XXX hackish: on 3k we fake to live in the builtins # in order to have Skipped exception printing shorter/nicer - __module__ = "builtins" + __module__ = 'builtins' def __init__( self, - msg: Optional[str] = None, + msg: str | None = None, pytrace: bool = True, allow_module_level: bool = False, *, @@ -61,14 +62,14 @@ class Skipped(OutcomeException): class Failed(OutcomeException): """Raised from an explicit call to pytest.fail().""" - __module__ = "builtins" + __module__ = 'builtins' class Exit(Exception): """Raised for immediate program exits (no tracebacks/summaries).""" def __init__( - self, msg: str = "unknown reason", returncode: Optional[int] = None + self, msg: str = 'unknown reason', returncode: int | None = None, ) -> None: self.msg = msg self.returncode = returncode @@ -78,8 +79,8 @@ class Exit(Exception): # Elaborate hack to work around https://github.com/python/mypy/issues/2087. # Ideally would just be `exit.Exception = Exit` etc. -_F = TypeVar("_F", bound=Callable[..., object]) -_ET = TypeVar("_ET", bound=Type[BaseException]) +_F = TypeVar('_F', bound=Callable[..., object]) +_ET = TypeVar('_ET', bound=Type[BaseException]) class _WithException(Protocol[_F, _ET]): @@ -101,8 +102,8 @@ def _with_exception(exception_type: _ET) -> Callable[[_F], _WithException[_F, _E @_with_exception(Exit) def exit( - reason: str = "", - returncode: Optional[int] = None, + reason: str = '', + returncode: int | None = None, ) -> NoReturn: """Exit testing process. @@ -119,7 +120,7 @@ def exit( @_with_exception(Skipped) def skip( - reason: str = "", + reason: str = '', *, allow_module_level: bool = False, ) -> NoReturn: @@ -152,7 +153,7 @@ def skip( @_with_exception(Failed) -def fail(reason: str = "", pytrace: bool = True) -> NoReturn: +def fail(reason: str = '', pytrace: bool = True) -> NoReturn: """Explicitly fail an executing test with the given message. :param reason: @@ -171,7 +172,7 @@ class XFailed(Failed): @_with_exception(XFailed) -def xfail(reason: str = "") -> NoReturn: +def xfail(reason: str = '') -> NoReturn: """Imperatively xfail an executing test or setup function with the given reason. This function should be called only during testing (setup, call or teardown). @@ -192,7 +193,7 @@ def xfail(reason: str = "") -> NoReturn: def importorskip( - modname: str, minversion: Optional[str] = None, reason: Optional[str] = None + modname: str, minversion: str | None = None, reason: str | None = None, ) -> Any: """Import and return the requested module ``modname``, or skip the current test if the module cannot be imported. @@ -216,30 +217,30 @@ def importorskip( import warnings __tracebackhide__ = True - compile(modname, "", "eval") # to catch syntaxerrors + compile(modname, '', 'eval') # to catch syntaxerrors with warnings.catch_warnings(): # Make sure to ignore ImportWarnings that might happen because # of existing directories with the same name we're trying to # import but without a __init__.py file. - warnings.simplefilter("ignore") + warnings.simplefilter('ignore') try: __import__(modname) except ImportError as exc: if reason is None: - reason = f"could not import {modname!r}: {exc}" + reason = f'could not import {modname!r}: {exc}' raise Skipped(reason, allow_module_level=True) from None mod = sys.modules[modname] if minversion is None: return mod - verattr = getattr(mod, "__version__", None) + verattr = getattr(mod, '__version__', None) if minversion is not None: # Imported lazily to improve start-up time. from packaging.version import Version if verattr is None or Version(verattr) < Version(minversion): raise Skipped( - f"module {modname!r} has __version__ {verattr!r}, required is: {minversion!r}", + f'module {modname!r} has __version__ {verattr!r}, required is: {minversion!r}', allow_module_level=True, ) return mod diff --git a/.venv/lib/python3.10/site-packages/_pytest/pastebin.py b/.venv/lib/python3.10/site-packages/_pytest/pastebin.py index 98ba5c9..61c63cf 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/pastebin.py +++ b/.venv/lib/python3.10/site-packages/_pytest/pastebin.py @@ -1,50 +1,52 @@ # mypy: allow-untyped-defs """Submit failure or test session information to a pastebin service.""" -from io import StringIO +from __future__ import annotations + import tempfile +from io import StringIO from typing import IO from typing import Union +import pytest from _pytest.config import Config from _pytest.config import create_terminal_writer from _pytest.config.argparsing import Parser from _pytest.stash import StashKey from _pytest.terminal import TerminalReporter -import pytest pastebinfile_key = StashKey[IO[bytes]]() def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("terminal reporting") + group = parser.getgroup('terminal reporting') group._addoption( - "--pastebin", - metavar="mode", - action="store", - dest="pastebin", + '--pastebin', + metavar='mode', + action='store', + dest='pastebin', default=None, - choices=["failed", "all"], - help="Send failed|all info to bpaste.net pastebin service", + choices=['failed', 'all'], + help='Send failed|all info to bpaste.net pastebin service', ) @pytest.hookimpl(trylast=True) def pytest_configure(config: Config) -> None: - if config.option.pastebin == "all": - tr = config.pluginmanager.getplugin("terminalreporter") + if config.option.pastebin == 'all': + tr = config.pluginmanager.getplugin('terminalreporter') # If no terminal reporter plugin is present, nothing we can do here; # this can happen when this function executes in a worker node # when using pytest-xdist, for example. if tr is not None: # pastebin file will be UTF-8 encoded binary file. - config.stash[pastebinfile_key] = tempfile.TemporaryFile("w+b") + config.stash[pastebinfile_key] = tempfile.TemporaryFile('w+b') oldwrite = tr._tw.write def tee_write(s, **kwargs): oldwrite(s, **kwargs) if isinstance(s, str): - s = s.encode("utf-8") + s = s.encode('utf-8') config.stash[pastebinfile_key].write(s) tr._tw.write = tee_write @@ -59,15 +61,15 @@ def pytest_unconfigure(config: Config) -> None: pastebinfile.close() del config.stash[pastebinfile_key] # Undo our patching in the terminal reporter. - tr = config.pluginmanager.getplugin("terminalreporter") - del tr._tw.__dict__["write"] + tr = config.pluginmanager.getplugin('terminalreporter') + del tr._tw.__dict__['write'] # Write summary. - tr.write_sep("=", "Sending information to Paste Service") + tr.write_sep('=', 'Sending information to Paste Service') pastebinurl = create_new_paste(sessionlog) - tr.write_line("pastebin session-log: %s\n" % pastebinurl) + tr.write_line('pastebin session-log: %s\n' % pastebinurl) -def create_new_paste(contents: Union[str, bytes]) -> str: +def create_new_paste(contents: str | bytes) -> str: """Create a new paste using the bpaste.net service. :contents: Paste contents string. @@ -77,27 +79,27 @@ def create_new_paste(contents: Union[str, bytes]) -> str: from urllib.parse import urlencode from urllib.request import urlopen - params = {"code": contents, "lexer": "text", "expiry": "1week"} - url = "https://bpa.st" + params = {'code': contents, 'lexer': 'text', 'expiry': '1week'} + url = 'https://bpa.st' try: response: str = ( - urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8") + urlopen(url, data=urlencode(params).encode('ascii')).read().decode('utf-8') ) except OSError as exc_info: # urllib errors - return "bad response: %s" % exc_info + return 'bad response: %s' % exc_info m = re.search(r'href="/raw/(\w+)"', response) if m: - return f"{url}/show/{m.group(1)}" + return f'{url}/show/{m.group(1)}' else: return "bad response: invalid format ('" + response + "')" def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: - if terminalreporter.config.option.pastebin != "failed": + if terminalreporter.config.option.pastebin != 'failed': return - if "failed" in terminalreporter.stats: - terminalreporter.write_sep("=", "Sending information to Paste Service") - for rep in terminalreporter.stats["failed"]: + if 'failed' in terminalreporter.stats: + terminalreporter.write_sep('=', 'Sending information to Paste Service') + for rep in terminalreporter.stats['failed']: try: msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc except AttributeError: @@ -108,4 +110,4 @@ def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: s = file.getvalue() assert len(s) pastebinurl = create_new_paste(s) - terminalreporter.write_line(f"{msg} --> {pastebinurl}") + terminalreporter.write_line(f'{msg} --> {pastebinurl}') diff --git a/.venv/lib/python3.10/site-packages/_pytest/pathlib.py b/.venv/lib/python3.10/site-packages/_pytest/pathlib.py index e39f477..0500440 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/pathlib.py +++ b/.venv/lib/python3.10/site-packages/_pytest/pathlib.py @@ -1,16 +1,23 @@ # mypy: allow-untyped-defs +from __future__ import annotations + import atexit import contextlib +import fnmatch +import importlib.util +import itertools +import os +import shutil +import sys +import types +import uuid +import warnings from enum import Enum from errno import EBADF from errno import ELOOP from errno import ENOENT from errno import ENOTDIR -import fnmatch from functools import partial -import importlib.util -import itertools -import os from os.path import expanduser from os.path import expandvars from os.path import isabs @@ -18,9 +25,6 @@ from os.path import sep from pathlib import Path from pathlib import PurePath from posixpath import sep as posix_sep -import shutil -import sys -import types from types import ModuleType from typing import Callable from typing import Dict @@ -33,8 +37,6 @@ from typing import Tuple from typing import Type from typing import TypeVar from typing import Union -import uuid -import warnings from _pytest.compat import assert_never from _pytest.outcomes import skip @@ -44,7 +46,7 @@ from _pytest.warning_types import PytestWarning LOCK_TIMEOUT = 60 * 60 * 24 * 3 -_AnyPurePath = TypeVar("_AnyPurePath", bound=PurePath) +_AnyPurePath = TypeVar('_AnyPurePath', bound=PurePath) # The following function, variables and comments were # copied from cpython 3.9 Lib/pathlib.py file. @@ -60,22 +62,22 @@ _IGNORED_WINERRORS = ( def _ignore_error(exception): return ( - getattr(exception, "errno", None) in _IGNORED_ERRORS - or getattr(exception, "winerror", None) in _IGNORED_WINERRORS + getattr(exception, 'errno', None) in _IGNORED_ERRORS or + getattr(exception, 'winerror', None) in _IGNORED_WINERRORS ) def get_lock_path(path: _AnyPurePath) -> _AnyPurePath: - return path.joinpath(".lock") + return path.joinpath('.lock') def on_rm_rf_error( func, path: str, - excinfo: Union[ - BaseException, - Tuple[Type[BaseException], BaseException, Optional[types.TracebackType]], - ], + excinfo: ( + BaseException | + tuple[type[BaseException], BaseException, types.TracebackType | None] + ), *, start_path: Path, ) -> bool: @@ -95,7 +97,7 @@ def on_rm_rf_error( if not isinstance(exc, PermissionError): warnings.warn( - PytestWarning(f"(rm_rf) error removing {path}\n{type(exc)}: {exc}") + PytestWarning(f'(rm_rf) error removing {path}\n{type(exc)}: {exc}'), ) return False @@ -103,8 +105,8 @@ def on_rm_rf_error( if func not in (os.open,): warnings.warn( PytestWarning( - f"(rm_rf) unknown function {func} when removing {path}:\n{type(exc)}: {exc}" - ) + f'(rm_rf) unknown function {func} when removing {path}:\n{type(exc)}: {exc}', + ), ) return False @@ -142,7 +144,7 @@ def ensure_extended_length_path(path: Path) -> Path: On Windows, this function returns the extended-length absolute version of path. On other platforms it returns path unchanged. """ - if sys.platform.startswith("win32"): + if sys.platform.startswith('win32'): path = path.resolve() path = Path(get_extended_length_path_str(str(path))) return path @@ -150,12 +152,12 @@ def ensure_extended_length_path(path: Path) -> Path: def get_extended_length_path_str(path: str) -> str: """Convert a path to a Windows extended length path.""" - long_path_prefix = "\\\\?\\" - unc_long_path_prefix = "\\\\?\\UNC\\" + long_path_prefix = '\\\\?\\' + unc_long_path_prefix = '\\\\?\\UNC\\' if path.startswith((long_path_prefix, unc_long_path_prefix)): return path # UNC - if path.startswith("\\\\"): + if path.startswith('\\\\'): return unc_long_path_prefix + path[2:] return long_path_prefix + path @@ -171,7 +173,7 @@ def rm_rf(path: Path) -> None: shutil.rmtree(str(path), onerror=onerror) -def find_prefixed(root: Path, prefix: str) -> Iterator["os.DirEntry[str]"]: +def find_prefixed(root: Path, prefix: str) -> Iterator[os.DirEntry[str]]: """Find all elements in root that begin with the prefix, case insensitive.""" l_prefix = prefix.lower() for x in os.scandir(root): @@ -179,7 +181,7 @@ def find_prefixed(root: Path, prefix: str) -> Iterator["os.DirEntry[str]"]: yield x -def extract_suffixes(iter: Iterable["os.DirEntry[str]"], prefix: str) -> Iterator[str]: +def extract_suffixes(iter: Iterable[os.DirEntry[str]], prefix: str) -> Iterator[str]: """Return the parts of the paths following the prefix. :param iter: Iterator over path names. @@ -204,7 +206,7 @@ def parse_num(maybe_num) -> int: def _force_symlink( - root: Path, target: Union[str, PurePath], link_to: Union[str, Path] + root: Path, target: str | PurePath, link_to: str | Path, ) -> None: """Helper to create the current symlink. @@ -231,18 +233,18 @@ def make_numbered_dir(root: Path, prefix: str, mode: int = 0o700) -> Path: # try up to 10 times to create the folder max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) new_number = max_existing + 1 - new_path = root.joinpath(f"{prefix}{new_number}") + new_path = root.joinpath(f'{prefix}{new_number}') try: new_path.mkdir(mode=mode) except Exception: pass else: - _force_symlink(root, prefix + "current", new_path) + _force_symlink(root, prefix + 'current', new_path) return new_path else: raise OSError( - "could not create numbered dir with prefix " - f"{prefix} in {root} after 10 tries" + 'could not create numbered dir with prefix ' + f'{prefix} in {root} after 10 tries', ) @@ -252,14 +254,14 @@ def create_cleanup_lock(p: Path) -> Path: try: fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) except FileExistsError as e: - raise OSError(f"cannot create lockfile in {p}") from e + raise OSError(f'cannot create lockfile in {p}') from e else: pid = os.getpid() spid = str(pid).encode() os.write(fd, spid) os.close(fd) if not lock_path.is_file(): - raise OSError("lock path got renamed after successful creation") + raise OSError('lock path got renamed after successful creation') return lock_path @@ -289,7 +291,7 @@ def maybe_delete_a_numbered_dir(path: Path) -> None: lock_path = create_cleanup_lock(path) parent = path.parent - garbage = parent.joinpath(f"garbage-{uuid.uuid4()}") + garbage = parent.joinpath(f'garbage-{uuid.uuid4()}') path.rename(garbage) rm_rf(garbage) except OSError: @@ -362,14 +364,14 @@ def cleanup_dead_symlinks(root: Path): def cleanup_numbered_dir( - root: Path, prefix: str, keep: int, consider_lock_dead_if_created_before: float + root: Path, prefix: str, keep: int, consider_lock_dead_if_created_before: float, ) -> None: """Cleanup for lock driven numbered directories.""" if not root.exists(): return for path in cleanup_candidates(root, prefix, keep): try_cleanup(path, consider_lock_dead_if_created_before) - for path in root.glob("garbage-*"): + for path in root.glob('garbage-*'): try_cleanup(path, consider_lock_dead_if_created_before) cleanup_dead_symlinks(root) @@ -417,7 +419,7 @@ def resolve_from_str(input: str, rootpath: Path) -> Path: return rootpath.joinpath(input) -def fnmatch_ex(pattern: str, path: Union[str, "os.PathLike[str]"]) -> bool: +def fnmatch_ex(pattern: str, path: str | os.PathLike[str]) -> bool: """A port of FNMatcher from py.path.common which works with PurePath() instances. The difference between this algorithm and PurePath.match() is that the @@ -436,7 +438,7 @@ def fnmatch_ex(pattern: str, path: Union[str, "os.PathLike[str]"]) -> bool: * https://bugs.python.org/issue34731 """ path = PurePath(path) - iswin32 = sys.platform.startswith("win") + iswin32 = sys.platform.startswith('win') if iswin32 and sep not in pattern and posix_sep in pattern: # Running on Windows, the pattern has no Windows path separators, @@ -449,11 +451,11 @@ def fnmatch_ex(pattern: str, path: Union[str, "os.PathLike[str]"]) -> bool: else: name = str(path) if path.is_absolute() and not os.path.isabs(pattern): - pattern = f"*{os.sep}{pattern}" + pattern = f'*{os.sep}{pattern}' return fnmatch.fnmatch(name, pattern) -def parts(s: str) -> Set[str]: +def parts(s: str) -> set[str]: parts = s.split(sep) return {sep.join(parts[: i + 1]) or sep for i in range(len(parts))} @@ -463,15 +465,15 @@ def symlink_or_skip(src, dst, **kwargs): try: os.symlink(str(src), str(dst), **kwargs) except OSError as e: - skip(f"symlinks not supported: {e}") + skip(f'symlinks not supported: {e}') class ImportMode(Enum): """Possible values for `mode` parameter of `import_path`.""" - prepend = "prepend" - append = "append" - importlib = "importlib" + prepend = 'prepend' + append = 'append' + importlib = 'importlib' class ImportPathMismatchError(ImportError): @@ -484,9 +486,9 @@ class ImportPathMismatchError(ImportError): def import_path( - path: Union[str, "os.PathLike[str]"], + path: str | os.PathLike[str], *, - mode: Union[str, ImportMode] = ImportMode.prepend, + mode: str | ImportMode = ImportMode.prepend, root: Path, consider_namespace_packages: bool, ) -> ModuleType: @@ -534,7 +536,7 @@ def import_path( # without touching sys.path. try: pkg_root, module_name = resolve_pkg_root_and_module_name( - path, consider_namespace_packages=consider_namespace_packages + path, consider_namespace_packages=consider_namespace_packages, ) except CouldNotResolvePathError: pass @@ -544,7 +546,7 @@ def import_path( return sys.modules[module_name] mod = _import_module_using_spec( - module_name, path, pkg_root, insert_modules=False + module_name, path, pkg_root, insert_modules=False, ) if mod is not None: return mod @@ -556,7 +558,7 @@ def import_path( return sys.modules[module_name] mod = _import_module_using_spec( - module_name, path, path.parent, insert_modules=True + module_name, path, path.parent, insert_modules=True, ) if mod is None: raise ImportError(f"Can't find module {module_name} at location {path}") @@ -564,7 +566,7 @@ def import_path( try: pkg_root, module_name = resolve_pkg_root_and_module_name( - path, consider_namespace_packages=consider_namespace_packages + path, consider_namespace_packages=consider_namespace_packages, ) except CouldNotResolvePathError: pkg_root, module_name = path.parent, path.stem @@ -584,19 +586,19 @@ def import_path( importlib.import_module(module_name) mod = sys.modules[module_name] - if path.name == "__init__.py": + if path.name == '__init__.py': return mod - ignore = os.environ.get("PY_IGNORE_IMPORTMISMATCH", "") - if ignore != "1": + ignore = os.environ.get('PY_IGNORE_IMPORTMISMATCH', '') + if ignore != '1': module_file = mod.__file__ if module_file is None: raise ImportPathMismatchError(module_name, module_file, path) - if module_file.endswith((".pyc", ".pyo")): + if module_file.endswith(('.pyc', '.pyo')): module_file = module_file[:-1] - if module_file.endswith(os.sep + "__init__.py"): - module_file = module_file[: -(len(os.sep + "__init__.py"))] + if module_file.endswith(os.sep + '__init__.py'): + module_file = module_file[: -(len(os.sep + '__init__.py'))] try: is_same = _is_same(str(path), module_file) @@ -610,8 +612,8 @@ def import_path( def _import_module_using_spec( - module_name: str, module_path: Path, module_location: Path, *, insert_modules: bool -) -> Optional[ModuleType]: + module_name: str, module_path: Path, module_location: Path, *, insert_modules: bool, +) -> ModuleType | None: """ Tries to import a module by its canonical name, path to the .py file, and its parent location. @@ -645,7 +647,7 @@ def _import_module_using_spec( # Implement a special _is_same function on Windows which returns True if the two filenames # compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678). -if sys.platform.startswith("win"): +if sys.platform.startswith('win'): def _is_same(f1: str, f2: str) -> bool: return Path(f1) == Path(f2) or os.path.samefile(f1, f2) @@ -663,7 +665,7 @@ def module_name_from_path(path: Path, root: Path) -> str: For example: path="projects/src/tests/test_foo.py" and root="/projects", the resulting module name will be "src.tests.test_foo". """ - path = path.with_suffix("") + path = path.with_suffix('') try: relative_path = path.relative_to(root) except ValueError: @@ -676,18 +678,18 @@ def module_name_from_path(path: Path, root: Path) -> str: # Module name for packages do not contain the __init__ file, unless # the `__init__.py` file is at the root. - if len(path_parts) >= 2 and path_parts[-1] == "__init__": + if len(path_parts) >= 2 and path_parts[-1] == '__init__': path_parts = path_parts[:-1] # Module names cannot contain ".", normalize them to "_". This prevents # a directory having a "." in the name (".env.310" for example) causing extra intermediate modules. # Also, important to replace "." at the start of paths, as those are considered relative imports. - path_parts = tuple(x.replace(".", "_") for x in path_parts) + path_parts = tuple(x.replace('.', '_') for x in path_parts) - return ".".join(path_parts) + return '.'.join(path_parts) -def insert_missing_modules(modules: Dict[str, ModuleType], module_name: str) -> None: +def insert_missing_modules(modules: dict[str, ModuleType], module_name: str) -> None: """ Used by ``import_path`` to create intermediate modules when using mode=importlib. @@ -695,10 +697,10 @@ def insert_missing_modules(modules: Dict[str, ModuleType], module_name: str) -> to create empty modules "src" and "src.tests" after inserting "src.tests.test_foo", otherwise "src.tests.test_foo" is not importable by ``__import__``. """ - module_parts = module_name.split(".") - child_module: Union[ModuleType, None] = None - module: Union[ModuleType, None] = None - child_name: str = "" + module_parts = module_name.split('.') + child_module: ModuleType | None = None + module: ModuleType | None = None + child_name: str = '' while module_name: if module_name not in modules: try: @@ -723,12 +725,12 @@ def insert_missing_modules(modules: Dict[str, ModuleType], module_name: str) -> setattr(module, child_name, child_module) modules[module_name] = module # Keep track of the child module while moving up the tree. - child_module, child_name = module, module_name.rpartition(".")[-1] + child_module, child_name = module, module_name.rpartition('.')[-1] module_parts.pop(-1) - module_name = ".".join(module_parts) + module_name = '.'.join(module_parts) -def resolve_package_path(path: Path) -> Optional[Path]: +def resolve_package_path(path: Path) -> Path | None: """Return the Python package path by looking for the last directory upwards which still contains an __init__.py. @@ -737,7 +739,7 @@ def resolve_package_path(path: Path) -> Optional[Path]: result = None for parent in itertools.chain((path,), path.parents): if parent.is_dir(): - if not (parent / "__init__.py").is_file(): + if not (parent / '__init__.py').is_file(): break if not parent.name.isidentifier(): break @@ -746,8 +748,8 @@ def resolve_package_path(path: Path) -> Optional[Path]: def resolve_pkg_root_and_module_name( - path: Path, *, consider_namespace_packages: bool = False -) -> Tuple[Path, str]: + path: Path, *, consider_namespace_packages: bool = False, +) -> tuple[Path, str]: """ Return the path to the directory of the root package that contains the given Python file, and its module name: @@ -779,20 +781,20 @@ def resolve_pkg_root_and_module_name( for parent in pkg_root.parents: # If any of the parent paths has a __init__.py, it means it is not # a namespace package (see the docs linked above). - if (parent / "__init__.py").is_file(): + if (parent / '__init__.py').is_file(): break if str(parent) in sys.path: # Point the pkg_root to the root of the namespace package. pkg_root = parent break - names = list(path.with_suffix("").relative_to(pkg_root).parts) - if names[-1] == "__init__": + names = list(path.with_suffix('').relative_to(pkg_root).parts) + if names[-1] == '__init__': names.pop() - module_name = ".".join(names) + module_name = '.'.join(names) return pkg_root, module_name - raise CouldNotResolvePathError(f"Could not resolve for {path}") + raise CouldNotResolvePathError(f'Could not resolve for {path}') class CouldNotResolvePathError(Exception): @@ -800,9 +802,9 @@ class CouldNotResolvePathError(Exception): def scandir( - path: Union[str, "os.PathLike[str]"], - sort_key: Callable[["os.DirEntry[str]"], object] = lambda entry: entry.name, -) -> List["os.DirEntry[str]"]: + path: str | os.PathLike[str], + sort_key: Callable[[os.DirEntry[str]], object] = lambda entry: entry.name, +) -> list[os.DirEntry[str]]: """Scan a directory recursively, in breadth-first order. The returned entries are sorted according to the given key. @@ -825,8 +827,8 @@ def scandir( def visit( - path: Union[str, "os.PathLike[str]"], recurse: Callable[["os.DirEntry[str]"], bool] -) -> Iterator["os.DirEntry[str]"]: + path: str | os.PathLike[str], recurse: Callable[[os.DirEntry[str]], bool], +) -> Iterator[os.DirEntry[str]]: """Walk a directory recursively, in breadth-first order. The `recurse` predicate determines whether a directory is recursed. @@ -840,7 +842,7 @@ def visit( yield from visit(entry.path, recurse) -def absolutepath(path: Union[Path, str]) -> Path: +def absolutepath(path: Path | str) -> Path: """Convert a path to an absolute path using os.path.abspath. Prefer this over Path.resolve() (see #6523). @@ -849,7 +851,7 @@ def absolutepath(path: Union[Path, str]) -> Path: return Path(os.path.abspath(str(path))) -def commonpath(path1: Path, path2: Path) -> Optional[Path]: +def commonpath(path1: Path, path2: Path) -> Path | None: """Return the common part shared with the other path, or None if there is no common part. diff --git a/.venv/lib/python3.10/site-packages/_pytest/pytester.py b/.venv/lib/python3.10/site-packages/_pytest/pytester.py index 8002528..6502dce 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/pytester.py +++ b/.venv/lib/python3.10/site-packages/_pytest/pytester.py @@ -3,21 +3,23 @@ PYTEST_DONT_REWRITE """ +from __future__ import annotations + import collections.abc import contextlib -from fnmatch import fnmatch import gc import importlib -from io import StringIO import locale import os -from pathlib import Path import platform import re import shutil import subprocess import sys import traceback +from fnmatch import fnmatch +from io import StringIO +from pathlib import Path from typing import Any from typing import Callable from typing import Dict @@ -38,9 +40,6 @@ from typing import TYPE_CHECKING from typing import Union from weakref import WeakKeyDictionary -from iniconfig import IniConfig -from iniconfig import SectionWrapper - from _pytest import timing from _pytest._code import Source from _pytest.capture import _get_multicapture @@ -69,34 +68,36 @@ from _pytest.reports import CollectReport from _pytest.reports import TestReport from _pytest.tmpdir import TempPathFactory from _pytest.warning_types import PytestWarning +from iniconfig import IniConfig +from iniconfig import SectionWrapper if TYPE_CHECKING: import pexpect -pytest_plugins = ["pytester_assertions"] +pytest_plugins = ['pytester_assertions'] IGNORE_PAM = [ # filenames added when obtaining details about the current user - "/var/lib/sss/mc/passwd" + '/var/lib/sss/mc/passwd', ] def pytest_addoption(parser: Parser) -> None: parser.addoption( - "--lsof", - action="store_true", - dest="lsof", + '--lsof', + action='store_true', + dest='lsof', default=False, - help="Run FD checks if lsof is available", + help='Run FD checks if lsof is available', ) parser.addoption( - "--runpytest", - default="inprocess", - dest="runpytest", - choices=("inprocess", "subprocess"), + '--runpytest', + default='inprocess', + dest='runpytest', + choices=('inprocess', 'subprocess'), help=( "Run pytest sub runs in tests using an 'inprocess' " "or 'subprocess' (python -m main) method" @@ -104,32 +105,32 @@ def pytest_addoption(parser: Parser) -> None: ) parser.addini( - "pytester_example_dir", help="Directory to take the pytester example files from" + 'pytester_example_dir', help='Directory to take the pytester example files from', ) def pytest_configure(config: Config) -> None: - if config.getvalue("lsof"): + if config.getvalue('lsof'): checker = LsofFdLeakChecker() if checker.matching_platform(): config.pluginmanager.register(checker) config.addinivalue_line( - "markers", - "pytester_example_path(*path_segments): join the given path " - "segments to `pytester_example_dir` for this test.", + 'markers', + 'pytester_example_path(*path_segments): join the given path ' + 'segments to `pytester_example_dir` for this test.', ) class LsofFdLeakChecker: - def get_open_files(self) -> List[Tuple[str, str]]: + def get_open_files(self) -> list[tuple[str, str]]: if sys.version_info >= (3, 11): # New in Python 3.11, ignores utf-8 mode encoding = locale.getencoding() else: encoding = locale.getpreferredencoding(False) out = subprocess.run( - ("lsof", "-Ffn0", "-p", str(os.getpid())), + ('lsof', '-Ffn0', '-p', str(os.getpid())), stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True, @@ -138,30 +139,30 @@ class LsofFdLeakChecker: ).stdout def isopen(line: str) -> bool: - return line.startswith("f") and ( - "deleted" not in line - and "mem" not in line - and "txt" not in line - and "cwd" not in line + return line.startswith('f') and ( + 'deleted' not in line and + 'mem' not in line and + 'txt' not in line and + 'cwd' not in line ) open_files = [] - for line in out.split("\n"): + for line in out.split('\n'): if isopen(line): - fields = line.split("\0") + fields = line.split('\0') fd = fields[0][1:] filename = fields[1][1:] if filename in IGNORE_PAM: continue - if filename.startswith("/"): + if filename.startswith('/'): open_files.append((fd, filename)) return open_files def matching_platform(self) -> bool: try: - subprocess.run(("lsof", "-v"), check=True) + subprocess.run(('lsof', '-v'), check=True) except (OSError, subprocess.CalledProcessError): return False else: @@ -173,7 +174,7 @@ class LsofFdLeakChecker: try: return (yield) finally: - if hasattr(sys, "pypy_version_info"): + if hasattr(sys, 'pypy_version_info'): gc.collect() lines2 = self.get_open_files() @@ -181,24 +182,24 @@ class LsofFdLeakChecker: leaked_files = [t for t in lines2 if t[0] in new_fds] if leaked_files: error = [ - "***** %s FD leakage detected" % len(leaked_files), + '***** %s FD leakage detected' % len(leaked_files), *(str(f) for f in leaked_files), - "*** Before:", + '*** Before:', *(str(f) for f in lines1), - "*** After:", + '*** After:', *(str(f) for f in lines2), - "***** %s FD leakage detected" % len(leaked_files), - "*** function {}:{}: {} ".format(*item.location), - "See issue #2366", + '***** %s FD leakage detected' % len(leaked_files), + '*** function {}:{}: {} '.format(*item.location), + 'See issue #2366', ] - item.warn(PytestWarning("\n".join(error))) + item.warn(PytestWarning('\n'.join(error))) # used at least by pytest-xdist plugin @fixture -def _pytest(request: FixtureRequest) -> "PytestArg": +def _pytest(request: FixtureRequest) -> PytestArg: """Return a helper which offers a gethookrecorder(hook) method which returns a HookRecorder instance which helps to make assertions about called hooks.""" @@ -209,15 +210,15 @@ class PytestArg: def __init__(self, request: FixtureRequest) -> None: self._request = request - def gethookrecorder(self, hook) -> "HookRecorder": + def gethookrecorder(self, hook) -> HookRecorder: hookrecorder = HookRecorder(hook._pm) self._request.addfinalizer(hookrecorder.finish_recording) return hookrecorder -def get_public_names(values: Iterable[str]) -> List[str]: +def get_public_names(values: Iterable[str]) -> list[str]: """Only return names from iterator values without a leading underscore.""" - return [x for x in values if x[0] != "_"] + return [x for x in values if x[0] != '_'] @final @@ -240,8 +241,8 @@ class RecordedHookCall: def __repr__(self) -> str: d = self.__dict__.copy() - del d["_name"] - return f"" + del d['_name'] + return f'' if TYPE_CHECKING: # The class has undetermined attributes, this tells mypy about it. @@ -260,13 +261,13 @@ class HookRecorder: """ def __init__( - self, pluginmanager: PytestPluginManager, *, _ispytest: bool = False + self, pluginmanager: PytestPluginManager, *, _ispytest: bool = False, ) -> None: check_ispytest(_ispytest) self._pluginmanager = pluginmanager - self.calls: List[RecordedHookCall] = [] - self.ret: Optional[Union[int, ExitCode]] = None + self.calls: list[RecordedHookCall] = [] + self.ret: int | ExitCode | None = None def before(hook_name: str, hook_impls, kwargs) -> None: self.calls.append(RecordedHookCall(hook_name, kwargs)) @@ -279,13 +280,13 @@ class HookRecorder: def finish_recording(self) -> None: self._undo_wrapping() - def getcalls(self, names: Union[str, Iterable[str]]) -> List[RecordedHookCall]: + def getcalls(self, names: str | Iterable[str]) -> list[RecordedHookCall]: """Get all recorded calls to hooks with the given names (or name).""" if isinstance(names, str): names = names.split() return [call for call in self.calls if call._name in names] - def assert_contains(self, entries: Sequence[Tuple[str, str]]) -> None: + def assert_contains(self, entries: Sequence[tuple[str, str]]) -> None: __tracebackhide__ = True i = 0 entries = list(entries) @@ -294,17 +295,17 @@ class HookRecorder: name, check = entries.pop(0) for ind, call in enumerate(self.calls[i:]): if call._name == name: - print("NAMEMATCH", name, call) + print('NAMEMATCH', name, call) if eval(check, backlocals, call.__dict__): - print("CHECKERMATCH", repr(check), "->", call) + print('CHECKERMATCH', repr(check), '->', call) else: - print("NOCHECKERMATCH", repr(check), "-", call) + print('NOCHECKERMATCH', repr(check), '-', call) continue i += ind + 1 break - print("NONAMEMATCH", name, "with", call) + print('NONAMEMATCH', name, 'with', call) else: - fail(f"could not find {name!r} check {check!r}") + fail(f'could not find {name!r} check {check!r}') def popcall(self, name: str) -> RecordedHookCall: __tracebackhide__ = True @@ -312,9 +313,9 @@ class HookRecorder: if call._name == name: del self.calls[i] return call - lines = [f"could not find call {name!r}, in:"] - lines.extend([" %s" % x for x in self.calls]) - fail("\n".join(lines)) + lines = [f'could not find call {name!r}, in:'] + lines.extend([' %s' % x for x in self.calls]) + fail('\n'.join(lines)) def getcall(self, name: str) -> RecordedHookCall: values = self.getcalls(name) @@ -326,127 +327,127 @@ class HookRecorder: @overload def getreports( self, - names: "Literal['pytest_collectreport']", + names: Literal['pytest_collectreport'], ) -> Sequence[CollectReport]: ... @overload def getreports( self, - names: "Literal['pytest_runtest_logreport']", + names: Literal['pytest_runtest_logreport'], ) -> Sequence[TestReport]: ... @overload def getreports( self, - names: Union[str, Iterable[str]] = ( - "pytest_collectreport", - "pytest_runtest_logreport", + names: str | Iterable[str] = ( + 'pytest_collectreport', + 'pytest_runtest_logreport', ), - ) -> Sequence[Union[CollectReport, TestReport]]: + ) -> Sequence[CollectReport | TestReport]: ... def getreports( self, - names: Union[str, Iterable[str]] = ( - "pytest_collectreport", - "pytest_runtest_logreport", + names: str | Iterable[str] = ( + 'pytest_collectreport', + 'pytest_runtest_logreport', ), - ) -> Sequence[Union[CollectReport, TestReport]]: + ) -> Sequence[CollectReport | TestReport]: return [x.report for x in self.getcalls(names)] def matchreport( self, - inamepart: str = "", - names: Union[str, Iterable[str]] = ( - "pytest_runtest_logreport", - "pytest_collectreport", + inamepart: str = '', + names: str | Iterable[str] = ( + 'pytest_runtest_logreport', + 'pytest_collectreport', ), - when: Optional[str] = None, - ) -> Union[CollectReport, TestReport]: + when: str | None = None, + ) -> CollectReport | TestReport: """Return a testreport whose dotted import path matches.""" values = [] for rep in self.getreports(names=names): - if not when and rep.when != "call" and rep.passed: + if not when and rep.when != 'call' and rep.passed: # setup/teardown passing reports - let's ignore those continue if when and rep.when != when: continue - if not inamepart or inamepart in rep.nodeid.split("::"): + if not inamepart or inamepart in rep.nodeid.split('::'): values.append(rep) if not values: raise ValueError( - f"could not find test report matching {inamepart!r}: " - "no test reports at all!" + f'could not find test report matching {inamepart!r}: ' + 'no test reports at all!', ) if len(values) > 1: raise ValueError( - f"found 2 or more testreports matching {inamepart!r}: {values}" + f'found 2 or more testreports matching {inamepart!r}: {values}', ) return values[0] @overload def getfailures( self, - names: "Literal['pytest_collectreport']", + names: Literal['pytest_collectreport'], ) -> Sequence[CollectReport]: ... @overload def getfailures( self, - names: "Literal['pytest_runtest_logreport']", + names: Literal['pytest_runtest_logreport'], ) -> Sequence[TestReport]: ... @overload def getfailures( self, - names: Union[str, Iterable[str]] = ( - "pytest_collectreport", - "pytest_runtest_logreport", + names: str | Iterable[str] = ( + 'pytest_collectreport', + 'pytest_runtest_logreport', ), - ) -> Sequence[Union[CollectReport, TestReport]]: + ) -> Sequence[CollectReport | TestReport]: ... def getfailures( self, - names: Union[str, Iterable[str]] = ( - "pytest_collectreport", - "pytest_runtest_logreport", + names: str | Iterable[str] = ( + 'pytest_collectreport', + 'pytest_runtest_logreport', ), - ) -> Sequence[Union[CollectReport, TestReport]]: + ) -> Sequence[CollectReport | TestReport]: return [rep for rep in self.getreports(names) if rep.failed] def getfailedcollections(self) -> Sequence[CollectReport]: - return self.getfailures("pytest_collectreport") + return self.getfailures('pytest_collectreport') def listoutcomes( self, - ) -> Tuple[ + ) -> tuple[ Sequence[TestReport], - Sequence[Union[CollectReport, TestReport]], - Sequence[Union[CollectReport, TestReport]], + Sequence[CollectReport | TestReport], + Sequence[CollectReport | TestReport], ]: passed = [] skipped = [] failed = [] for rep in self.getreports( - ("pytest_collectreport", "pytest_runtest_logreport") + ('pytest_collectreport', 'pytest_runtest_logreport'), ): if rep.passed: - if rep.when == "call": + if rep.when == 'call': assert isinstance(rep, TestReport) passed.append(rep) elif rep.skipped: skipped.append(rep) else: - assert rep.failed, f"Unexpected outcome: {rep!r}" + assert rep.failed, f'Unexpected outcome: {rep!r}' failed.append(rep) return passed, skipped, failed - def countoutcomes(self) -> List[int]: + def countoutcomes(self) -> list[int]: return [len(x) for x in self.listoutcomes()] def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> None: @@ -466,14 +467,14 @@ class HookRecorder: @fixture -def linecomp() -> "LineComp": +def linecomp() -> LineComp: """A :class: `LineComp` instance for checking that an input linearly contains a sequence of strings.""" return LineComp() -@fixture(name="LineMatcher") -def LineMatcher_fixture(request: FixtureRequest) -> Type["LineMatcher"]: +@fixture(name='LineMatcher') +def LineMatcher_fixture(request: FixtureRequest) -> type[LineMatcher]: """A reference to the :class: `LineMatcher`. This is instantiable with a list of lines (without their trailing newlines). @@ -484,8 +485,8 @@ def LineMatcher_fixture(request: FixtureRequest) -> Type["LineMatcher"]: @fixture def pytester( - request: FixtureRequest, tmp_path_factory: TempPathFactory, monkeypatch: MonkeyPatch -) -> "Pytester": + request: FixtureRequest, tmp_path_factory: TempPathFactory, monkeypatch: MonkeyPatch, +) -> Pytester: """ Facilities to write tests/configuration files, execute pytest in isolation, and match against expected output, perfect for black-box testing of pytest plugins. @@ -518,9 +519,9 @@ def _config_for_test() -> Generator[Config, None, None]: # Regex to match the session duration string in the summary: "74.34s". -rex_session_duration = re.compile(r"\d+\.\d\ds") +rex_session_duration = re.compile(r'\d+\.\d\ds') # Regex to match all the counts and phrases in the summary line: "34 passed, 111 skipped". -rex_outcome = re.compile(r"(\d+) (\w+)") +rex_outcome = re.compile(r'(\d+) (\w+)') @final @@ -529,13 +530,13 @@ class RunResult: def __init__( self, - ret: Union[int, ExitCode], - outlines: List[str], - errlines: List[str], + ret: int | ExitCode, + outlines: list[str], + errlines: list[str], duration: float, ) -> None: try: - self.ret: Union[int, ExitCode] = ExitCode(ret) + self.ret: int | ExitCode = ExitCode(ret) """The return value.""" except ValueError: self.ret = ret @@ -556,11 +557,11 @@ class RunResult: def __repr__(self) -> str: return ( - "" + '' % (self.ret, len(self.stdout.lines), len(self.stderr.lines), self.duration) ) - def parseoutcomes(self) -> Dict[str, int]: + def parseoutcomes(self) -> dict[str, int]: """Return a dictionary of outcome noun -> count from parsing the terminal output that the test process produced. @@ -573,7 +574,7 @@ class RunResult: return self.parse_summary_nouns(self.outlines) @classmethod - def parse_summary_nouns(cls, lines) -> Dict[str, int]: + def parse_summary_nouns(cls, lines) -> dict[str, int]: """Extract the nouns from a pytest terminal summary line. It always returns the plural noun for consistency:: @@ -588,11 +589,11 @@ class RunResult: ret = {noun: int(count) for (count, noun) in outcomes} break else: - raise ValueError("Pytest terminal summary report not found") + raise ValueError('Pytest terminal summary report not found') to_plural = { - "warning": "warnings", - "error": "errors", + 'warning': 'warnings', + 'error': 'errors', } return {to_plural.get(k, k): v for k, v in ret.items()} @@ -604,8 +605,8 @@ class RunResult: errors: int = 0, xpassed: int = 0, xfailed: int = 0, - warnings: Optional[int] = None, - deselected: Optional[int] = None, + warnings: int | None = None, + deselected: int | None = None, ) -> None: """ Assert that the specified outcomes appear with the respective @@ -631,7 +632,7 @@ class RunResult: class SysModulesSnapshot: - def __init__(self, preserve: Optional[Callable[[str], bool]] = None) -> None: + def __init__(self, preserve: Callable[[str], bool] | None = None) -> None: self.__preserve = preserve self.__saved = dict(sys.modules) @@ -664,7 +665,7 @@ class Pytester: __test__ = False - CLOSE_STDIN: "Final" = NOTSET + CLOSE_STDIN: Final = NOTSET class TimeoutExpired(Exception): pass @@ -680,7 +681,7 @@ class Pytester: check_ispytest(_ispytest) self._request = request self._mod_collections: WeakKeyDictionary[ - Collector, List[Union[Item, Collector]] + Collector, list[Item | Collector], ] = WeakKeyDictionary() if request.function: name: str = request.function.__name__ @@ -692,26 +693,26 @@ class Pytester: #: :py:meth:`runpytest`. Initially this is an empty list but plugins can #: be added to the list. The type of items to add to the list depends on #: the method using them so refer to them for details. - self.plugins: List[Union[str, _PluggyPlugin]] = [] + self.plugins: list[str | _PluggyPlugin] = [] self._sys_path_snapshot = SysPathsSnapshot() self._sys_modules_snapshot = self.__take_sys_modules_snapshot() self._request.addfinalizer(self._finalize) - self._method = self._request.config.getoption("--runpytest") - self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True) + self._method = self._request.config.getoption('--runpytest') + self._test_tmproot = tmp_path_factory.mktemp(f'tmp-{name}', numbered=True) self._monkeypatch = mp = monkeypatch self.chdir() - mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot)) + mp.setenv('PYTEST_DEBUG_TEMPROOT', str(self._test_tmproot)) # Ensure no unexpected caching via tox. - mp.delenv("TOX_ENV_DIR", raising=False) + mp.delenv('TOX_ENV_DIR', raising=False) # Discard outer pytest options. - mp.delenv("PYTEST_ADDOPTS", raising=False) + mp.delenv('PYTEST_ADDOPTS', raising=False) # Ensure no user config is used. tmphome = str(self.path) - mp.setenv("HOME", tmphome) - mp.setenv("USERPROFILE", tmphome) + mp.setenv('HOME', tmphome) + mp.setenv('USERPROFILE', tmphome) # Do not use colors for inner runs by default. - mp.setenv("PY_COLORS", "0") + mp.setenv('PY_COLORS', '0') @property def path(self) -> Path: @@ -719,7 +720,7 @@ class Pytester: return self._path def __repr__(self) -> str: - return f"" + return f'' def _finalize(self) -> None: """ @@ -740,7 +741,7 @@ class Pytester: # Preserve readline due to https://bugs.python.org/issue41033. # pexpect issues a SIGWINCH. def preserve_module(name): - return name.startswith(("zope", "readline")) + return name.startswith(('zope', 'readline')) return SysModulesSnapshot(preserve=preserve_module) @@ -760,22 +761,22 @@ class Pytester: def _makefile( self, ext: str, - lines: Sequence[Union[Any, bytes]], - files: Dict[str, str], - encoding: str = "utf-8", + lines: Sequence[Any | bytes], + files: dict[str, str], + encoding: str = 'utf-8', ) -> Path: items = list(files.items()) - if ext and not ext.startswith("."): + if ext and not ext.startswith('.'): raise ValueError( - f"pytester.makefile expects a file extension, try .{ext} instead of {ext}" + f'pytester.makefile expects a file extension, try .{ext} instead of {ext}', ) - def to_text(s: Union[Any, bytes]) -> str: + def to_text(s: Any | bytes) -> str: return s.decode(encoding) if isinstance(s, bytes) else str(s) if lines: - source = "\n".join(to_text(x) for x in lines) + source = '\n'.join(to_text(x) for x in lines) basename = self._name items.insert(0, (basename, source)) @@ -784,7 +785,7 @@ class Pytester: p = self.path.joinpath(basename).with_suffix(ext) p.parent.mkdir(parents=True, exist_ok=True) source_ = Source(value) - source = "\n".join(to_text(line) for line in source_.lines) + source = '\n'.join(to_text(line) for line in source_.lines) p.write_text(source.strip(), encoding=encoding) if ret is None: ret = p @@ -836,12 +837,12 @@ class Pytester: :param source: The contents. :returns: The tox.ini file. """ - return self.makefile(".ini", tox=source) + return self.makefile('.ini', tox=source) def getinicfg(self, source: str) -> SectionWrapper: """Return the pytest section from the tox.ini config file.""" p = self.makeini(source) - return IniConfig(str(p))["pytest"] + return IniConfig(str(p))['pytest'] def makepyprojecttoml(self, source: str) -> Path: """Write a pyproject.toml file. @@ -851,7 +852,7 @@ class Pytester: .. versionadded:: 6.0 """ - return self.makefile(".toml", pyproject=source) + return self.makefile('.toml', pyproject=source) def makepyfile(self, *args, **kwargs) -> Path: r"""Shortcut for .makefile() with a .py extension. @@ -870,7 +871,7 @@ class Pytester: # At this point, both 'test_something.py' & 'custom.py' exist in the test directory. """ - return self._makefile(".py", args, kwargs) + return self._makefile('.py', args, kwargs) def maketxtfile(self, *args, **kwargs) -> Path: r"""Shortcut for .makefile() with a .txt extension. @@ -889,10 +890,10 @@ class Pytester: # At this point, both 'test_something.txt' & 'custom.txt' exist in the test directory. """ - return self._makefile(".txt", args, kwargs) + return self._makefile('.txt', args, kwargs) def syspathinsert( - self, path: Optional[Union[str, "os.PathLike[str]"]] = None + self, path: str | os.PathLike[str] | None = None, ) -> None: """Prepend a directory to sys.path, defaults to :attr:`path`. @@ -907,7 +908,7 @@ class Pytester: self._monkeypatch.syspath_prepend(str(path)) - def mkdir(self, name: Union[str, "os.PathLike[str]"]) -> Path: + def mkdir(self, name: str | os.PathLike[str]) -> Path: """Create a new (sub)directory. :param name: @@ -919,7 +920,7 @@ class Pytester: p.mkdir() return p - def mkpydir(self, name: Union[str, "os.PathLike[str]"]) -> Path: + def mkpydir(self, name: str | os.PathLike[str]) -> Path: """Create a new python package. This creates a (sub)directory with an empty ``__init__.py`` file so it @@ -927,10 +928,10 @@ class Pytester: """ p = self.path / name p.mkdir() - p.joinpath("__init__.py").touch() + p.joinpath('__init__.py').touch() return p - def copy_example(self, name: Optional[str] = None) -> Path: + def copy_example(self, name: str | None = None) -> Path: """Copy file from project's directory into the testdir. :param name: @@ -938,19 +939,19 @@ class Pytester: :return: Path to the copied directory (inside ``self.path``). """ - example_dir_ = self._request.config.getini("pytester_example_dir") + example_dir_ = self._request.config.getini('pytester_example_dir') if example_dir_ is None: raise ValueError("pytester_example_dir is unset, can't copy examples") example_dir: Path = self._request.config.rootpath / example_dir_ - for extra_element in self._request.node.iter_markers("pytester_example_path"): + for extra_element in self._request.node.iter_markers('pytester_example_path'): assert extra_element.args example_dir = example_dir.joinpath(*extra_element.args) if name is None: func_name = self._name maybe_dir = example_dir / func_name - maybe_file = example_dir / (func_name + ".py") + maybe_file = example_dir / (func_name + '.py') if maybe_dir.is_dir(): example_path = maybe_dir @@ -958,12 +959,12 @@ class Pytester: example_path = maybe_file else: raise LookupError( - f"{func_name} can't be found as module or package in {example_dir}" + f"{func_name} can't be found as module or package in {example_dir}", ) else: example_path = example_dir.joinpath(name) - if example_path.is_dir() and not example_path.joinpath("__init__.py").is_file(): + if example_path.is_dir() and not example_path.joinpath('__init__.py').is_file(): shutil.copytree(example_path, self.path, symlinks=True, dirs_exist_ok=True) return self.path elif example_path.is_file(): @@ -972,12 +973,12 @@ class Pytester: return result else: raise LookupError( - f'example "{example_path}" is not found as a file or directory' + f'example "{example_path}" is not found as a file or directory', ) def getnode( - self, config: Config, arg: Union[str, "os.PathLike[str]"] - ) -> Union[Collector, Item]: + self, config: Config, arg: str | os.PathLike[str], + ) -> Collector | Item: """Get the collection node of a file. :param config: @@ -989,7 +990,7 @@ class Pytester: The node. """ session = Session.from_config(config) - assert "::" not in str(arg) + assert '::' not in str(arg) p = Path(os.path.abspath(arg)) config.hook.pytest_sessionstart(session=session) res = session.perform_collect([str(p)], genitems=False)[0] @@ -997,8 +998,8 @@ class Pytester: return res def getpathnode( - self, path: Union[str, "os.PathLike[str]"] - ) -> Union[Collector, Item]: + self, path: str | os.PathLike[str], + ) -> Collector | Item: """Return the collection node of a file. This is like :py:meth:`getnode` but uses :py:meth:`parseconfigure` to @@ -1018,7 +1019,7 @@ class Pytester: config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) return res - def genitems(self, colitems: Sequence[Union[Item, Collector]]) -> List[Item]: + def genitems(self, colitems: Sequence[Item | Collector]) -> list[Item]: """Generate all test items from a collection node. This recurses into the collection node and returns a list of all the @@ -1030,7 +1031,7 @@ class Pytester: The collected items. """ session = colitems[0].session - result: List[Item] = [] + result: list[Item] = [] for colitem in colitems: result.extend(session.genitems(colitem)) return result @@ -1064,20 +1065,20 @@ class Pytester: values = [*list(cmdlineargs), p] return self.inline_run(*values) - def inline_genitems(self, *args) -> Tuple[List[Item], HookRecorder]: + def inline_genitems(self, *args) -> tuple[list[Item], HookRecorder]: """Run ``pytest.main(['--collect-only'])`` in-process. Runs the :py:func:`pytest.main` function to run all of pytest inside the test process itself like :py:meth:`inline_run`, but returns a tuple of the collected items and a :py:class:`HookRecorder` instance. """ - rec = self.inline_run("--collect-only", *args) - items = [x.item for x in rec.getcalls("pytest_itemcollected")] + rec = self.inline_run('--collect-only', *args) + items = [x.item for x in rec.getcalls('pytest_itemcollected')] return items, rec def inline_run( self, - *args: Union[str, "os.PathLike[str]"], + *args: str | os.PathLike[str], plugins=(), no_reraise_ctrlc: bool = False, ) -> HookRecorder: @@ -1138,7 +1139,7 @@ class Pytester: # Typically we reraise keyboard interrupts from the child run # because it's our user requesting interruption of the testing. if ret == ExitCode.INTERRUPTED and not no_reraise_ctrlc: - calls = reprec.getcalls("pytest_keyboard_interrupt") + calls = reprec.getcalls('pytest_keyboard_interrupt') if calls and calls[-1].excinfo.type == KeyboardInterrupt: raise KeyboardInterrupt() return reprec @@ -1147,16 +1148,16 @@ class Pytester: finalizer() def runpytest_inprocess( - self, *args: Union[str, "os.PathLike[str]"], **kwargs: Any + self, *args: str | os.PathLike[str], **kwargs: Any, ) -> RunResult: """Return result of running pytest in-process, providing a similar interface to what self.runpytest() provides.""" - syspathinsert = kwargs.pop("syspathinsert", False) + syspathinsert = kwargs.pop('syspathinsert', False) if syspathinsert: self.syspathinsert() now = timing.time() - capture = _get_multicapture("sys") + capture = _get_multicapture('sys') capture.start_capturing() try: try: @@ -1185,35 +1186,35 @@ class Pytester: assert reprec.ret is not None res = RunResult( - reprec.ret, out.splitlines(), err.splitlines(), timing.time() - now + reprec.ret, out.splitlines(), err.splitlines(), timing.time() - now, ) res.reprec = reprec # type: ignore return res def runpytest( - self, *args: Union[str, "os.PathLike[str]"], **kwargs: Any + self, *args: str | os.PathLike[str], **kwargs: Any, ) -> RunResult: """Run pytest inline or in a subprocess, depending on the command line option "--runpytest" and return a :py:class:`~pytest.RunResult`.""" new_args = self._ensure_basetemp(args) - if self._method == "inprocess": + if self._method == 'inprocess': return self.runpytest_inprocess(*new_args, **kwargs) - elif self._method == "subprocess": + elif self._method == 'subprocess': return self.runpytest_subprocess(*new_args, **kwargs) - raise RuntimeError(f"Unrecognized runpytest option: {self._method}") + raise RuntimeError(f'Unrecognized runpytest option: {self._method}') def _ensure_basetemp( - self, args: Sequence[Union[str, "os.PathLike[str]"]] - ) -> List[Union[str, "os.PathLike[str]"]]: + self, args: Sequence[str | os.PathLike[str]], + ) -> list[str | os.PathLike[str]]: new_args = list(args) for x in new_args: - if str(x).startswith("--basetemp"): + if str(x).startswith('--basetemp'): break else: - new_args.append("--basetemp=%s" % self.path.parent.joinpath("basetemp")) + new_args.append('--basetemp=%s' % self.path.parent.joinpath('basetemp')) return new_args - def parseconfig(self, *args: Union[str, "os.PathLike[str]"]) -> Config: + def parseconfig(self, *args: str | os.PathLike[str]) -> Config: """Return a new pytest :class:`pytest.Config` instance from given commandline args. @@ -1237,7 +1238,7 @@ class Pytester: self._request.addfinalizer(config._ensure_unconfigure) return config - def parseconfigure(self, *args: Union[str, "os.PathLike[str]"]) -> Config: + def parseconfigure(self, *args: str | os.PathLike[str]) -> Config: """Return a new pytest configured Config instance. Returns a new :py:class:`pytest.Config` instance like @@ -1249,7 +1250,7 @@ class Pytester: return config def getitem( - self, source: Union[str, "os.PathLike[str]"], funcname: str = "test_func" + self, source: str | os.PathLike[str], funcname: str = 'test_func', ) -> Item: """Return the test item for a test function. @@ -1268,9 +1269,9 @@ class Pytester: for item in items: if item.name == funcname: return item - assert 0, f"{funcname!r} item not found in module:\n{source}\nitems: {items}" + assert 0, f'{funcname!r} item not found in module:\n{source}\nitems: {items}' - def getitems(self, source: Union[str, "os.PathLike[str]"]) -> List[Item]: + def getitems(self, source: str | os.PathLike[str]) -> list[Item]: """Return all test items collected from the module. Writes the source to a Python file and runs pytest's collection on @@ -1281,7 +1282,7 @@ class Pytester: def getmodulecol( self, - source: Union[str, "os.PathLike[str]"], + source: str | os.PathLike[str], configargs=(), *, withinit: bool = False, @@ -1304,18 +1305,18 @@ class Pytester: """ if isinstance(source, os.PathLike): path = self.path.joinpath(source) - assert not withinit, "not supported for paths" + assert not withinit, 'not supported for paths' else: kw = {self._name: str(source)} path = self.makepyfile(**kw) if withinit: - self.makepyfile(__init__="#") + self.makepyfile(__init__='#') self.config = config = self.parseconfigure(path, *configargs) return self.getnode(config, path) def collect_by_name( - self, modcol: Collector, name: str - ) -> Optional[Union[Item, Collector]]: + self, modcol: Collector, name: str, + ) -> Item | Collector | None: """Return the collection node for name from the module collection. Searches a module collection node for a collection node matching the @@ -1333,10 +1334,10 @@ class Pytester: def popen( self, - cmdargs: Sequence[Union[str, "os.PathLike[str]"]], - stdout: Union[int, TextIO] = subprocess.PIPE, - stderr: Union[int, TextIO] = subprocess.PIPE, - stdin: Union[NotSetType, bytes, IO[Any], int] = CLOSE_STDIN, + cmdargs: Sequence[str | os.PathLike[str]], + stdout: int | TextIO = subprocess.PIPE, + stderr: int | TextIO = subprocess.PIPE, + stdin: NotSetType | bytes | IO[Any] | int = CLOSE_STDIN, **kw, ): """Invoke :py:class:`subprocess.Popen`. @@ -1347,17 +1348,17 @@ class Pytester: You probably want to use :py:meth:`run` instead. """ env = os.environ.copy() - env["PYTHONPATH"] = os.pathsep.join( - filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) + env['PYTHONPATH'] = os.pathsep.join( + filter(None, [os.getcwd(), env.get('PYTHONPATH', '')]), ) - kw["env"] = env + kw['env'] = env if stdin is self.CLOSE_STDIN: - kw["stdin"] = subprocess.PIPE + kw['stdin'] = subprocess.PIPE elif isinstance(stdin, bytes): - kw["stdin"] = subprocess.PIPE + kw['stdin'] = subprocess.PIPE else: - kw["stdin"] = stdin + kw['stdin'] = stdin popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) if stdin is self.CLOSE_STDIN: @@ -1371,9 +1372,9 @@ class Pytester: def run( self, - *cmdargs: Union[str, "os.PathLike[str]"], - timeout: Optional[float] = None, - stdin: Union[NotSetType, bytes, IO[Any], int] = CLOSE_STDIN, + *cmdargs: str | os.PathLike[str], + timeout: float | None = None, + stdin: NotSetType | bytes | IO[Any] | int = CLOSE_STDIN, ) -> RunResult: """Run a command with arguments. @@ -1407,19 +1408,19 @@ class Pytester: __tracebackhide__ = True cmdargs = tuple(os.fspath(arg) for arg in cmdargs) - p1 = self.path.joinpath("stdout") - p2 = self.path.joinpath("stderr") - print("running:", *cmdargs) - print(" in:", Path.cwd()) + p1 = self.path.joinpath('stdout') + p2 = self.path.joinpath('stderr') + print('running:', *cmdargs) + print(' in:', Path.cwd()) - with p1.open("w", encoding="utf8") as f1, p2.open("w", encoding="utf8") as f2: + with p1.open('w', encoding='utf8') as f1, p2.open('w', encoding='utf8') as f2: now = timing.time() popen = self.popen( cmdargs, stdin=stdin, stdout=f1, stderr=f2, - close_fds=(sys.platform != "win32"), + close_fds=(sys.platform != 'win32'), ) if popen.stdin is not None: popen.stdin.close() @@ -1427,7 +1428,7 @@ class Pytester: def handle_timeout() -> None: __tracebackhide__ = True - timeout_message = f"{timeout} second timeout expired running: {cmdargs}" + timeout_message = f'{timeout} second timeout expired running: {cmdargs}' popen.kill() popen.wait() @@ -1441,7 +1442,7 @@ class Pytester: except subprocess.TimeoutExpired: handle_timeout() - with p1.open(encoding="utf8") as f1, p2.open(encoding="utf8") as f2: + with p1.open(encoding='utf8') as f1, p2.open(encoding='utf8') as f2: out = f1.read().splitlines() err = f2.read().splitlines() @@ -1459,19 +1460,19 @@ class Pytester: except UnicodeEncodeError: print(f"couldn't print to {fp} because of encoding") - def _getpytestargs(self) -> Tuple[str, ...]: - return sys.executable, "-mpytest" + def _getpytestargs(self) -> tuple[str, ...]: + return sys.executable, '-mpytest' - def runpython(self, script: "os.PathLike[str]") -> RunResult: + def runpython(self, script: os.PathLike[str]) -> RunResult: """Run a python script using sys.executable as interpreter.""" return self.run(sys.executable, script) def runpython_c(self, command: str) -> RunResult: """Run ``python -c "command"``.""" - return self.run(sys.executable, "-c", command) + return self.run(sys.executable, '-c', command) def runpytest_subprocess( - self, *args: Union[str, "os.PathLike[str]"], timeout: Optional[float] = None + self, *args: str | os.PathLike[str], timeout: float | None = None, ) -> RunResult: """Run pytest as a subprocess with given arguments. @@ -1490,17 +1491,17 @@ class Pytester: The result. """ __tracebackhide__ = True - p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700) - args = ("--basetemp=%s" % p, *args) + p = make_numbered_dir(root=self.path, prefix='runpytest-', mode=0o700) + args = ('--basetemp=%s' % p, *args) plugins = [x for x in self.plugins if isinstance(x, str)] if plugins: - args = ("-p", plugins[0], *args) + args = ('-p', plugins[0], *args) args = self._getpytestargs() + args return self.run(*args, timeout=timeout) def spawn_pytest( - self, string: str, expect_timeout: float = 10.0 - ) -> "pexpect.spawn": + self, string: str, expect_timeout: float = 10.0, + ) -> pexpect.spawn: """Run pytest using pexpect. This makes sure to use the right pytest and sets up the temporary @@ -1508,23 +1509,23 @@ class Pytester: The pexpect child is returned. """ - basetemp = self.path / "temp-pexpect" + basetemp = self.path / 'temp-pexpect' basetemp.mkdir(mode=0o700) - invoke = " ".join(map(str, self._getpytestargs())) - cmd = f"{invoke} --basetemp={basetemp} {string}" + invoke = ' '.join(map(str, self._getpytestargs())) + cmd = f'{invoke} --basetemp={basetemp} {string}' return self.spawn(cmd, expect_timeout=expect_timeout) - def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn: """Run a command using pexpect. The pexpect child is returned. """ - pexpect = importorskip("pexpect", "3.0") - if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): - skip("pypy-64 bit not supported") - if not hasattr(pexpect, "spawn"): - skip("pexpect.spawn not available") - logfile = self.path.joinpath("spawn.out").open("wb") + pexpect = importorskip('pexpect', '3.0') + if hasattr(sys, 'pypy_version_info') and '64' in platform.machine(): + skip('pypy-64 bit not supported') + if not hasattr(pexpect, 'spawn'): + skip('pexpect.spawn not available') + logfile = self.path.joinpath('spawn.out').open('wb') child = pexpect.spawn(cmd, logfile=logfile, timeout=expect_timeout) self._request.addfinalizer(logfile.close) @@ -1545,7 +1546,7 @@ class LineComp: val = self.stringio.getvalue() self.stringio.truncate(0) self.stringio.seek(0) - lines1 = val.split("\n") + lines1 = val.split('\n') LineMatcher(lines1).fnmatch_lines(lines2) @@ -1559,9 +1560,9 @@ class LineMatcher: ``text.splitlines()``. """ - def __init__(self, lines: List[str]) -> None: + def __init__(self, lines: list[str]) -> None: self.lines = lines - self._log_output: List[str] = [] + self._log_output: list[str] = [] def __str__(self) -> str: """Return the entire original text. @@ -1569,9 +1570,9 @@ class LineMatcher: .. versionadded:: 6.2 You can use :meth:`str` in older versions. """ - return "\n".join(self.lines) + return '\n'.join(self.lines) - def _getlines(self, lines2: Union[str, Sequence[str], Source]) -> Sequence[str]: + def _getlines(self, lines2: str | Sequence[str] | Source) -> Sequence[str]: if isinstance(lines2, str): lines2 = Source(lines2) if isinstance(lines2, Source): @@ -1589,17 +1590,17 @@ class LineMatcher: self._match_lines_random(lines2, lambda name, pat: bool(re.match(pat, name))) def _match_lines_random( - self, lines2: Sequence[str], match_func: Callable[[str, str], bool] + self, lines2: Sequence[str], match_func: Callable[[str, str], bool], ) -> None: __tracebackhide__ = True lines2 = self._getlines(lines2) for line in lines2: for x in self.lines: if line == x or match_func(x, line): - self._log("matched: ", repr(line)) + self._log('matched: ', repr(line)) break else: - msg = "line %r not found in output" % line + msg = 'line %r not found in output' % line self._log(msg) self._fail(msg) @@ -1610,18 +1611,18 @@ class LineMatcher: """ for i, line in enumerate(self.lines): if fnline == line or fnmatch(line, fnline): - return self.lines[i + 1 :] - raise ValueError("line %r not found in output" % fnline) + return self.lines[i + 1:] + raise ValueError('line %r not found in output' % fnline) def _log(self, *args) -> None: - self._log_output.append(" ".join(str(x) for x in args)) + self._log_output.append(' '.join(str(x) for x in args)) @property def _log_text(self) -> str: - return "\n".join(self._log_output) + return '\n'.join(self._log_output) def fnmatch_lines( - self, lines2: Sequence[str], *, consecutive: bool = False + self, lines2: Sequence[str], *, consecutive: bool = False, ) -> None: """Check lines exist in the output (using :func:`python:fnmatch.fnmatch`). @@ -1633,10 +1634,10 @@ class LineMatcher: :param consecutive: Match lines consecutively? """ __tracebackhide__ = True - self._match_lines(lines2, fnmatch, "fnmatch", consecutive=consecutive) + self._match_lines(lines2, fnmatch, 'fnmatch', consecutive=consecutive) def re_match_lines( - self, lines2: Sequence[str], *, consecutive: bool = False + self, lines2: Sequence[str], *, consecutive: bool = False, ) -> None: """Check lines exist in the output (using :func:`python:re.match`). @@ -1652,7 +1653,7 @@ class LineMatcher: self._match_lines( lines2, lambda name, pat: bool(re.match(pat, name)), - "re.match", + 're.match', consecutive=consecutive, ) @@ -1680,7 +1681,7 @@ class LineMatcher: Match lines consecutively? """ if not isinstance(lines2, collections.abc.Sequence): - raise TypeError(f"invalid type for lines2: {type(lines2).__name__}") + raise TypeError(f'invalid type for lines2: {type(lines2).__name__}') lines2 = self._getlines(lines2) lines1 = self.lines[:] extralines = [] @@ -1692,33 +1693,33 @@ class LineMatcher: while lines1: nextline = lines1.pop(0) if line == nextline: - self._log("exact match:", repr(line)) + self._log('exact match:', repr(line)) started = True break elif match_func(nextline, line): - self._log("%s:" % match_nickname, repr(line)) + self._log('%s:' % match_nickname, repr(line)) self._log( - "{:>{width}}".format("with:", width=wnick), repr(nextline) + '{:>{width}}'.format('with:', width=wnick), repr(nextline), ) started = True break else: if consecutive and started: - msg = f"no consecutive match: {line!r}" + msg = f'no consecutive match: {line!r}' self._log(msg) self._log( - "{:>{width}}".format("with:", width=wnick), repr(nextline) + '{:>{width}}'.format('with:', width=wnick), repr(nextline), ) self._fail(msg) if not nomatchprinted: self._log( - "{:>{width}}".format("nomatch:", width=wnick), repr(line) + '{:>{width}}'.format('nomatch:', width=wnick), repr(line), ) nomatchprinted = True - self._log("{:>{width}}".format("and:", width=wnick), repr(nextline)) + self._log('{:>{width}}'.format('and:', width=wnick), repr(nextline)) extralines.append(nextline) else: - msg = f"remains unmatched: {line!r}" + msg = f'remains unmatched: {line!r}' self._log(msg) self._fail(msg) self._log_output = [] @@ -1729,7 +1730,7 @@ class LineMatcher: :param str pat: The pattern to match lines. """ __tracebackhide__ = True - self._no_match_line(pat, fnmatch, "fnmatch") + self._no_match_line(pat, fnmatch, 'fnmatch') def no_re_match_line(self, pat: str) -> None: """Ensure captured lines do not match the given pattern, using ``re.match``. @@ -1738,11 +1739,11 @@ class LineMatcher: """ __tracebackhide__ = True self._no_match_line( - pat, lambda name, pat: bool(re.match(pat, name)), "re.match" + pat, lambda name, pat: bool(re.match(pat, name)), 're.match', ) def _no_match_line( - self, pat: str, match_func: Callable[[str, str], bool], match_nickname: str + self, pat: str, match_func: Callable[[str, str], bool], match_nickname: str, ) -> None: """Ensure captured lines does not have a the given pattern, using ``fnmatch.fnmatch``. @@ -1753,15 +1754,15 @@ class LineMatcher: wnick = len(match_nickname) + 1 for line in self.lines: if match_func(line, pat): - msg = f"{match_nickname}: {pat!r}" + msg = f'{match_nickname}: {pat!r}' self._log(msg) - self._log("{:>{width}}".format("with:", width=wnick), repr(line)) + self._log('{:>{width}}'.format('with:', width=wnick), repr(line)) self._fail(msg) else: if not nomatch_printed: - self._log("{:>{width}}".format("nomatch:", width=wnick), repr(pat)) + self._log('{:>{width}}'.format('nomatch:', width=wnick), repr(pat)) nomatch_printed = True - self._log("{:>{width}}".format("and:", width=wnick), repr(line)) + self._log('{:>{width}}'.format('and:', width=wnick), repr(line)) self._log_output = [] def _fail(self, msg: str) -> None: diff --git a/.venv/lib/python3.10/site-packages/_pytest/pytester_assertions.py b/.venv/lib/python3.10/site-packages/_pytest/pytester_assertions.py index d20c2bb..c4c5f9a 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/pytester_assertions.py +++ b/.venv/lib/python3.10/site-packages/_pytest/pytester_assertions.py @@ -1,9 +1,10 @@ """Helper plugin for pytester; should not be loaded on its own.""" - # This plugin contains assertions used by pytester. pytester cannot # contain them itself, since it is imported by the `pytest` module, # hence cannot be subject to assertion rewriting, which requires a # module to not be already imported. +from __future__ import annotations + from typing import Dict from typing import Optional from typing import Sequence @@ -15,10 +16,10 @@ from _pytest.reports import TestReport def assertoutcome( - outcomes: Tuple[ + outcomes: tuple[ Sequence[TestReport], - Sequence[Union[CollectReport, TestReport]], - Sequence[Union[CollectReport, TestReport]], + Sequence[CollectReport | TestReport], + Sequence[CollectReport | TestReport], ], passed: int = 0, skipped: int = 0, @@ -28,49 +29,49 @@ def assertoutcome( realpassed, realskipped, realfailed = outcomes obtained = { - "passed": len(realpassed), - "skipped": len(realskipped), - "failed": len(realfailed), + 'passed': len(realpassed), + 'skipped': len(realskipped), + 'failed': len(realfailed), } - expected = {"passed": passed, "skipped": skipped, "failed": failed} + expected = {'passed': passed, 'skipped': skipped, 'failed': failed} assert obtained == expected, outcomes def assert_outcomes( - outcomes: Dict[str, int], + outcomes: dict[str, int], passed: int = 0, skipped: int = 0, failed: int = 0, errors: int = 0, xpassed: int = 0, xfailed: int = 0, - warnings: Optional[int] = None, - deselected: Optional[int] = None, + warnings: int | None = None, + deselected: int | None = None, ) -> None: """Assert that the specified outcomes appear with the respective numbers (0 means it didn't occur) in the text output from a test run.""" __tracebackhide__ = True obtained = { - "passed": outcomes.get("passed", 0), - "skipped": outcomes.get("skipped", 0), - "failed": outcomes.get("failed", 0), - "errors": outcomes.get("errors", 0), - "xpassed": outcomes.get("xpassed", 0), - "xfailed": outcomes.get("xfailed", 0), + 'passed': outcomes.get('passed', 0), + 'skipped': outcomes.get('skipped', 0), + 'failed': outcomes.get('failed', 0), + 'errors': outcomes.get('errors', 0), + 'xpassed': outcomes.get('xpassed', 0), + 'xfailed': outcomes.get('xfailed', 0), } expected = { - "passed": passed, - "skipped": skipped, - "failed": failed, - "errors": errors, - "xpassed": xpassed, - "xfailed": xfailed, + 'passed': passed, + 'skipped': skipped, + 'failed': failed, + 'errors': errors, + 'xpassed': xpassed, + 'xfailed': xfailed, } if warnings is not None: - obtained["warnings"] = outcomes.get("warnings", 0) - expected["warnings"] = warnings + obtained['warnings'] = outcomes.get('warnings', 0) + expected['warnings'] = warnings if deselected is not None: - obtained["deselected"] = outcomes.get("deselected", 0) - expected["deselected"] = deselected + obtained['deselected'] = outcomes.get('deselected', 0) + expected['deselected'] = deselected assert obtained == expected diff --git a/.venv/lib/python3.10/site-packages/_pytest/python.py b/.venv/lib/python3.10/site-packages/_pytest/python.py index 1bbe960..1cdb6fc 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/python.py +++ b/.venv/lib/python3.10/site-packages/_pytest/python.py @@ -1,17 +1,20 @@ # mypy: allow-untyped-defs """Python test discovery, setup and run of test functions.""" +from __future__ import annotations + import abc -from collections import Counter -from collections import defaultdict import dataclasses import enum import fnmatch -from functools import partial import inspect import itertools import os -from pathlib import Path import types +import warnings +from collections import Counter +from collections import defaultdict +from functools import partial +from pathlib import Path from typing import Any from typing import Callable from typing import Dict @@ -29,7 +32,6 @@ from typing import Set from typing import Tuple from typing import TYPE_CHECKING from typing import Union -import warnings import _pytest from _pytest import fixtures @@ -91,52 +93,52 @@ _PYTEST_DIR = Path(_pytest.__file__).parent def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") + group = parser.getgroup('general') group.addoption( - "--fixtures", - "--funcargs", - action="store_true", - dest="showfixtures", + '--fixtures', + '--funcargs', + action='store_true', + dest='showfixtures', default=False, - help="Show available fixtures, sorted by plugin appearance " + help='Show available fixtures, sorted by plugin appearance ' "(fixtures with leading '_' are only shown with '-v')", ) group.addoption( - "--fixtures-per-test", - action="store_true", - dest="show_fixtures_per_test", + '--fixtures-per-test', + action='store_true', + dest='show_fixtures_per_test', default=False, - help="Show fixtures per test", + help='Show fixtures per test', ) parser.addini( - "python_files", - type="args", + 'python_files', + type='args', # NOTE: default is also used in AssertionRewritingHook. - default=["test_*.py", "*_test.py"], - help="Glob-style file patterns for Python test module discovery", + default=['test_*.py', '*_test.py'], + help='Glob-style file patterns for Python test module discovery', ) parser.addini( - "python_classes", - type="args", - default=["Test"], - help="Prefixes or glob names for Python test class discovery", + 'python_classes', + type='args', + default=['Test'], + help='Prefixes or glob names for Python test class discovery', ) parser.addini( - "python_functions", - type="args", - default=["test"], - help="Prefixes or glob names for Python test function and method discovery", + 'python_functions', + type='args', + default=['test'], + help='Prefixes or glob names for Python test function and method discovery', ) parser.addini( - "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", - type="bool", + 'disable_test_id_escaping_and_forfeit_all_rights_to_community_support', + type='bool', default=False, - help="Disable string escape non-ASCII characters, might cause unwanted " - "side effects(use at your own risk)", + help='Disable string escape non-ASCII characters, might cause unwanted ' + 'side effects(use at your own risk)', ) -def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: if config.option.showfixtures: showfixtures(config) return 0 @@ -146,84 +148,84 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: return None -def pytest_generate_tests(metafunc: "Metafunc") -> None: - for marker in metafunc.definition.iter_markers(name="parametrize"): +def pytest_generate_tests(metafunc: Metafunc) -> None: + for marker in metafunc.definition.iter_markers(name='parametrize'): metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) def pytest_configure(config: Config) -> None: config.addinivalue_line( - "markers", - "parametrize(argnames, argvalues): call a test function multiple " - "times passing in different arguments in turn. argvalues generally " - "needs to be a list of values if argnames specifies only one name " - "or a list of tuples of values if argnames specifies multiple names. " + 'markers', + 'parametrize(argnames, argvalues): call a test function multiple ' + 'times passing in different arguments in turn. argvalues generally ' + 'needs to be a list of values if argnames specifies only one name ' + 'or a list of tuples of values if argnames specifies multiple names. ' "Example: @parametrize('arg1', [1,2]) would lead to two calls of the " - "decorated test function, one with arg1=1 and another with arg1=2." - "see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info " - "and examples.", + 'decorated test function, one with arg1=1 and another with arg1=2.' + 'see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info ' + 'and examples.', ) config.addinivalue_line( - "markers", - "usefixtures(fixturename1, fixturename2, ...): mark tests as needing " - "all of the specified fixtures. see " - "https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures ", + 'markers', + 'usefixtures(fixturename1, fixturename2, ...): mark tests as needing ' + 'all of the specified fixtures. see ' + 'https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures ', ) def async_warn_and_skip(nodeid: str) -> None: - msg = "async def functions are not natively supported and have been skipped.\n" + msg = 'async def functions are not natively supported and have been skipped.\n' msg += ( - "You need to install a suitable plugin for your async framework, for example:\n" + 'You need to install a suitable plugin for your async framework, for example:\n' ) - msg += " - anyio\n" - msg += " - pytest-asyncio\n" - msg += " - pytest-tornasync\n" - msg += " - pytest-trio\n" - msg += " - pytest-twisted" + msg += ' - anyio\n' + msg += ' - pytest-asyncio\n' + msg += ' - pytest-tornasync\n' + msg += ' - pytest-trio\n' + msg += ' - pytest-twisted' warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid))) - skip(reason="async def function and no async plugin installed (see warnings)") + skip(reason='async def function and no async plugin installed (see warnings)') @hookimpl(trylast=True) -def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: +def pytest_pyfunc_call(pyfuncitem: Function) -> object | None: testfunction = pyfuncitem.obj if is_async_function(testfunction): async_warn_and_skip(pyfuncitem.nodeid) funcargs = pyfuncitem.funcargs testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} result = testfunction(**testargs) - if hasattr(result, "__await__") or hasattr(result, "__aiter__"): + if hasattr(result, '__await__') or hasattr(result, '__aiter__'): async_warn_and_skip(pyfuncitem.nodeid) elif result is not None: warnings.warn( PytestReturnNotNoneWarning( - f"Expected None, but {pyfuncitem.nodeid} returned {result!r}, which will be an error in a " - "future version of pytest. Did you mean to use `assert` instead of `return`?" - ) + f'Expected None, but {pyfuncitem.nodeid} returned {result!r}, which will be an error in a ' + 'future version of pytest. Did you mean to use `assert` instead of `return`?', + ), ) return True def pytest_collect_directory( - path: Path, parent: nodes.Collector -) -> Optional[nodes.Collector]: - pkginit = path / "__init__.py" + path: Path, parent: nodes.Collector, +) -> nodes.Collector | None: + pkginit = path / '__init__.py' if pkginit.is_file(): return Package.from_parent(parent, path=path) return None -def pytest_collect_file(file_path: Path, parent: nodes.Collector) -> Optional["Module"]: - if file_path.suffix == ".py": +def pytest_collect_file(file_path: Path, parent: nodes.Collector) -> Module | None: + if file_path.suffix == '.py': if not parent.session.isinitpath(file_path): if not path_matches_patterns( - file_path, parent.config.getini("python_files") + file_path, parent.config.getini('python_files'), ): return None ihook = parent.session.gethookproxy(file_path) module: Module = ihook.pytest_pycollect_makemodule( - module_path=file_path, parent=parent + module_path=file_path, parent=parent, ) return module return None @@ -234,14 +236,14 @@ def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool: return any(fnmatch_ex(pattern, path) for pattern in patterns) -def pytest_pycollect_makemodule(module_path: Path, parent) -> "Module": +def pytest_pycollect_makemodule(module_path: Path, parent) -> Module: return Module.from_parent(parent, path=module_path) @hookimpl(trylast=True) def pytest_pycollect_makeitem( - collector: Union["Module", "Class"], name: str, obj: object -) -> Union[None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]]: + collector: Module | Class, name: str, obj: object, +) -> None | nodes.Item | nodes.Collector | list[nodes.Item | nodes.Collector]: assert isinstance(collector, (Class, Module)), type(collector) # Nothing was collected elsewhere, let's do it here. if safe_isclass(obj): @@ -249,7 +251,7 @@ def pytest_pycollect_makeitem( return Class.from_parent(collector, name=name, obj=obj) elif collector.istestfunction(obj, name): # mock seems to store unbound methods (issue473), normalize it. - obj = getattr(obj, "__func__", obj) + obj = getattr(obj, '__func__', obj) # We need to try and unwrap the function if it's a functools.partial # or a functools.wrapped. # We mustn't if it's been wrapped with mock.patch (python 2 only). @@ -257,17 +259,17 @@ def pytest_pycollect_makeitem( filename, lineno = getfslineno(obj) warnings.warn_explicit( message=PytestCollectionWarning( - "cannot collect %r because it is not a function." % name + 'cannot collect %r because it is not a function.' % name, ), category=None, filename=str(filename), lineno=lineno + 1, ) - elif getattr(obj, "__test__", True): + elif getattr(obj, '__test__', True): if is_generator(obj): res = Function.from_parent(collector, name=name) reason = ( - f"yield tests were removed in pytest 4.0 - {name} will be ignored" + f'yield tests were removed in pytest 4.0 - {name} will be ignored' ) res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) res.warn(PytestCollectionWarning(reason)) @@ -305,12 +307,12 @@ class PyobjMixin(nodes.Node): a staticmethod, a class or a module. """ node = self.getparent(Function) - return getattr(node.obj, "__self__", None) if node is not None else None + return getattr(node.obj, '__self__', None) if node is not None else None @property def obj(self): """Underlying Python object.""" - obj = getattr(self, "_obj", None) + obj = getattr(self, '_obj', None) if obj is None: self._obj = obj = self._getobj() # XXX evil hack @@ -346,9 +348,9 @@ class PyobjMixin(nodes.Node): break parts.append(name) parts.reverse() - return ".".join(parts) + return '.'.join(parts) - def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: # XXX caching? path, lineno = getfslineno(self.obj) modpath = self.getmodpath() @@ -363,9 +365,9 @@ class _EmptyClass: pass # noqa: E701 IGNORED_ATTRIBUTES = frozenset.union( frozenset(), # Module. - dir(types.ModuleType("empty_module")), + dir(types.ModuleType('empty_module')), # Some extra module attributes the above doesn't catch. - {"__builtins__", "__file__", "__cached__"}, + {'__builtins__', '__file__', '__cached__'}, # Class. dir(_EmptyClass), # Instance. @@ -377,7 +379,7 @@ del _EmptyClass class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): def funcnamefilter(self, name: str) -> bool: - return self._matches_prefix_or_glob_option("python_functions", name) + return self._matches_prefix_or_glob_option('python_functions', name) def isnosetest(self, obj: object) -> bool: """Look for the __test__ attribute, which is applied by the @@ -386,16 +388,16 @@ class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): # We explicitly check for "is True" here to not mistakenly treat # classes with a custom __getattr__ returning something truthy (like a # function) as test classes. - return safe_getattr(obj, "__test__", False) is True + return safe_getattr(obj, '__test__', False) is True def classnamefilter(self, name: str) -> bool: - return self._matches_prefix_or_glob_option("python_classes", name) + return self._matches_prefix_or_glob_option('python_classes', name) def istestfunction(self, obj: object, name: str) -> bool: if self.funcnamefilter(name) or self.isnosetest(obj): if isinstance(obj, (staticmethod, classmethod)): # staticmethods and classmethods need to be unwrapped. - obj = safe_getattr(obj, "__func__", False) + obj = safe_getattr(obj, '__func__', False) return callable(obj) and fixtures.getfixturemarker(obj) is None else: return False @@ -412,29 +414,29 @@ class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): # Check that name looks like a glob-string before calling fnmatch # because this is called for every name in each collected module, # and fnmatch is somewhat expensive to call. - elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch( - name, option + elif ('*' in option or '?' in option or '[' in option) and fnmatch.fnmatch( + name, option, ): return True return False - def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: - if not getattr(self.obj, "__test__", True): + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + if not getattr(self.obj, '__test__', True): return [] # Avoid random getattrs and peek in the __dict__ instead. - dicts = [getattr(self.obj, "__dict__", {})] + dicts = [getattr(self.obj, '__dict__', {})] if isinstance(self.obj, type): for basecls in self.obj.__mro__: dicts.append(basecls.__dict__) # In each class, nodes should be definition ordered. # __dict__ is definition ordered. - seen: Set[str] = set() - dict_values: List[List[Union[nodes.Item, nodes.Collector]]] = [] + seen: set[str] = set() + dict_values: list[list[nodes.Item | nodes.Collector]] = [] ihook = self.ihook for dic in dicts: - values: List[Union[nodes.Item, nodes.Collector]] = [] + values: list[nodes.Item | nodes.Collector] = [] # Note: seems like the dict can change during iteration - # be careful not to remove the list() without consideration. for name, obj in list(dic.items()): @@ -444,7 +446,7 @@ class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): continue seen.add(name) res = ihook.pytest_pycollect_makeitem( - collector=self, name=name, obj=obj + collector=self, name=name, obj=obj, ) if res is None: continue @@ -461,7 +463,7 @@ class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): result.extend(values) return result - def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]: + def _genfunctions(self, name: str, funcobj) -> Iterator[Function]: modulecol = self.getparent(Module) assert modulecol is not None module = modulecol.obj @@ -482,9 +484,9 @@ class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): _ispytest=True, ) methods = [] - if hasattr(module, "pytest_generate_tests"): + if hasattr(module, 'pytest_generate_tests'): methods.append(module.pytest_generate_tests) - if cls is not None and hasattr(cls, "pytest_generate_tests"): + if cls is not None and hasattr(cls, 'pytest_generate_tests'): methods.append(cls().pytest_generate_tests) self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc)) @@ -499,7 +501,7 @@ class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): fixtureinfo.prune_dependency_tree() for callspec in metafunc._calls: - subname = f"{name}[{callspec.id}]" + subname = f'{name}[{callspec.id}]' yield Function.from_parent( self, name=subname, @@ -515,52 +517,52 @@ def importtestmodule( config: Config, ): # We assume we are only called once per module. - importmode = config.getoption("--import-mode") + importmode = config.getoption('--import-mode') try: mod = import_path( path, mode=importmode, root=config.rootpath, - consider_namespace_packages=config.getini("consider_namespace_packages"), + consider_namespace_packages=config.getini('consider_namespace_packages'), ) except SyntaxError as e: raise nodes.Collector.CollectError( - ExceptionInfo.from_current().getrepr(style="short") + ExceptionInfo.from_current().getrepr(style='short'), ) from e except ImportPathMismatchError as e: raise nodes.Collector.CollectError( - "import file mismatch:\n" - "imported module {!r} has this __file__ attribute:\n" - " {}\n" - "which is not the same as the test file we want to collect:\n" - " {}\n" - "HINT: remove __pycache__ / .pyc files and/or use a " - "unique basename for your test file modules".format(*e.args) + 'import file mismatch:\n' + 'imported module {!r} has this __file__ attribute:\n' + ' {}\n' + 'which is not the same as the test file we want to collect:\n' + ' {}\n' + 'HINT: remove __pycache__ / .pyc files and/or use a ' + 'unique basename for your test file modules'.format(*e.args), ) from e except ImportError as e: exc_info = ExceptionInfo.from_current() - if config.getoption("verbose") < 2: + if config.getoption('verbose') < 2: exc_info.traceback = exc_info.traceback.filter(filter_traceback) exc_repr = ( - exc_info.getrepr(style="short") + exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly() ) formatted_tb = str(exc_repr) raise nodes.Collector.CollectError( f"ImportError while importing test module '{path}'.\n" - "Hint: make sure your test modules/packages have valid Python names.\n" - "Traceback:\n" - f"{formatted_tb}" + 'Hint: make sure your test modules/packages have valid Python names.\n' + 'Traceback:\n' + f'{formatted_tb}', ) from e except skip.Exception as e: if e.allow_module_level: raise raise nodes.Collector.CollectError( - "Using pytest.skip outside of a test will skip the entire module. " + 'Using pytest.skip outside of a test will skip the entire module. ' "If that's your intention, pass `allow_module_level=True`. " - "If you want to skip a specific test or an entire class, " - "use the @pytest.mark.skip or @pytest.mark.skipif decorators." + 'If you want to skip a specific test or an entire class, ' + 'use the @pytest.mark.skip or @pytest.mark.skipif decorators.', ) from e config.pluginmanager.consider_module(mod) return mod @@ -572,7 +574,7 @@ class Module(nodes.File, PyCollector): def _getobj(self): return importtestmodule(self.path, self.config) - def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: self._register_setup_module_fixture() self._register_setup_function_fixture() self.session._fixturemanager.parsefactories(self) @@ -586,10 +588,10 @@ class Module(nodes.File, PyCollector): other fixtures (#517). """ setup_module = _get_first_non_fixture_func( - self.obj, ("setUpModule", "setup_module") + self.obj, ('setUpModule', 'setup_module'), ) teardown_module = _get_first_non_fixture_func( - self.obj, ("tearDownModule", "teardown_module") + self.obj, ('tearDownModule', 'teardown_module'), ) if setup_module is None and teardown_module is None: @@ -605,10 +607,10 @@ class Module(nodes.File, PyCollector): self.session._fixturemanager._register_fixture( # Use a unique name to speed up lookup. - name=f"_xunit_setup_module_fixture_{self.obj.__name__}", + name=f'_xunit_setup_module_fixture_{self.obj.__name__}', func=xunit_setup_module_fixture, nodeid=self.nodeid, - scope="module", + scope='module', autouse=True, ) @@ -619,9 +621,9 @@ class Module(nodes.File, PyCollector): Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with other fixtures (#517). """ - setup_function = _get_first_non_fixture_func(self.obj, ("setup_function",)) + setup_function = _get_first_non_fixture_func(self.obj, ('setup_function',)) teardown_function = _get_first_non_fixture_func( - self.obj, ("teardown_function",) + self.obj, ('teardown_function',), ) if setup_function is None and teardown_function is None: return @@ -641,10 +643,10 @@ class Module(nodes.File, PyCollector): self.session._fixturemanager._register_fixture( # Use a unique name to speed up lookup. - name=f"_xunit_setup_function_fixture_{self.obj.__name__}", + name=f'_xunit_setup_function_fixture_{self.obj.__name__}', func=xunit_setup_function_fixture, nodeid=self.nodeid, - scope="function", + scope='function', autouse=True, ) @@ -666,13 +668,13 @@ class Package(nodes.Directory): def __init__( self, - fspath: Optional[LEGACY_PATH], + fspath: LEGACY_PATH | None, parent: nodes.Collector, # NOTE: following args are unused: config=None, session=None, nodeid=None, - path: Optional[Path] = None, + path: Path | None = None, ) -> None: # NOTE: Could be just the following, but kept as-is for compat. # super().__init__(self, fspath, parent=parent) @@ -687,30 +689,30 @@ class Package(nodes.Directory): ) def setup(self) -> None: - init_mod = importtestmodule(self.path / "__init__.py", self.config) + init_mod = importtestmodule(self.path / '__init__.py', self.config) # Not using fixtures to call setup_module here because autouse fixtures # from packages are not called automatically (#4085). setup_module = _get_first_non_fixture_func( - init_mod, ("setUpModule", "setup_module") + init_mod, ('setUpModule', 'setup_module'), ) if setup_module is not None: _call_with_optional_argument(setup_module, init_mod) teardown_module = _get_first_non_fixture_func( - init_mod, ("tearDownModule", "teardown_module") + init_mod, ('tearDownModule', 'teardown_module'), ) if teardown_module is not None: func = partial(_call_with_optional_argument, teardown_module, init_mod) self.addfinalizer(func) - def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: # Always collect __init__.py first. - def sort_key(entry: "os.DirEntry[str]") -> object: - return (entry.name != "__init__.py", entry.name) + def sort_key(entry: os.DirEntry[str]) -> object: + return (entry.name != '__init__.py', entry.name) config = self.config - col: Optional[nodes.Collector] + col: nodes.Collector | None cols: Sequence[nodes.Collector] ihook = self.ihook for direntry in scandir(self.path, sort_key): @@ -744,12 +746,12 @@ def _call_with_optional_argument(func, arg) -> None: func() -def _get_first_non_fixture_func(obj: object, names: Iterable[str]) -> Optional[object]: +def _get_first_non_fixture_func(obj: object, names: Iterable[str]) -> object | None: """Return the attribute from the given object to be used as a setup/teardown xunit-style function, but only if not marked as a fixture to avoid calling it twice. """ for name in names: - meth: Optional[object] = getattr(obj, name, None) + meth: object | None = getattr(obj, name, None) if meth is not None and fixtures.getfixturemarker(meth) is None: return meth return None @@ -759,32 +761,32 @@ class Class(PyCollector): """Collector for test methods (and nested classes) in a Python class.""" @classmethod - def from_parent(cls, parent, *, name, obj=None, **kw) -> "Self": # type: ignore[override] + def from_parent(cls, parent, *, name, obj=None, **kw) -> Self: # type: ignore[override] """The public constructor.""" return super().from_parent(name=name, parent=parent, **kw) def newinstance(self): return self.obj() - def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: - if not safe_getattr(self.obj, "__test__", True): + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + if not safe_getattr(self.obj, '__test__', True): return [] if hasinit(self.obj): assert self.parent is not None self.warn( PytestCollectionWarning( - f"cannot collect test class {self.obj.__name__!r} because it has a " - f"__init__ constructor (from: {self.parent.nodeid})" - ) + f'cannot collect test class {self.obj.__name__!r} because it has a ' + f'__init__ constructor (from: {self.parent.nodeid})', + ), ) return [] elif hasnew(self.obj): assert self.parent is not None self.warn( PytestCollectionWarning( - f"cannot collect test class {self.obj.__name__!r} because it has a " - f"__new__ constructor (from: {self.parent.nodeid})" - ) + f'cannot collect test class {self.obj.__name__!r} because it has a ' + f'__new__ constructor (from: {self.parent.nodeid})', + ), ) return [] @@ -802,8 +804,8 @@ class Class(PyCollector): Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with other fixtures (#517). """ - setup_class = _get_first_non_fixture_func(self.obj, ("setup_class",)) - teardown_class = _get_first_non_fixture_func(self.obj, ("teardown_class",)) + setup_class = _get_first_non_fixture_func(self.obj, ('setup_class',)) + teardown_class = _get_first_non_fixture_func(self.obj, ('teardown_class',)) if setup_class is None and teardown_class is None: return @@ -819,10 +821,10 @@ class Class(PyCollector): self.session._fixturemanager._register_fixture( # Use a unique name to speed up lookup. - name=f"_xunit_setup_class_fixture_{self.obj.__qualname__}", + name=f'_xunit_setup_class_fixture_{self.obj.__qualname__}', func=xunit_setup_class_fixture, nodeid=self.nodeid, - scope="class", + scope='class', autouse=True, ) @@ -833,9 +835,9 @@ class Class(PyCollector): Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with other fixtures (#517). """ - setup_name = "setup_method" + setup_name = 'setup_method' setup_method = _get_first_non_fixture_func(self.obj, (setup_name,)) - teardown_name = "teardown_method" + teardown_name = 'teardown_method' teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,)) if setup_method is None and teardown_method is None: return @@ -853,23 +855,23 @@ class Class(PyCollector): self.session._fixturemanager._register_fixture( # Use a unique name to speed up lookup. - name=f"_xunit_setup_method_fixture_{self.obj.__qualname__}", + name=f'_xunit_setup_method_fixture_{self.obj.__qualname__}', func=xunit_setup_method_fixture, nodeid=self.nodeid, - scope="function", + scope='function', autouse=True, ) def hasinit(obj: object) -> bool: - init: object = getattr(obj, "__init__", None) + init: object = getattr(obj, '__init__', None) if init: return init != object.__init__ return False def hasnew(obj: object) -> bool: - new: object = getattr(obj, "__new__", None) + new: object = getattr(obj, '__new__', None) if new: return new != object.__new__ return False @@ -881,13 +883,13 @@ class IdMaker: """Make IDs for a parametrization.""" __slots__ = ( - "argnames", - "parametersets", - "idfn", - "ids", - "config", - "nodeid", - "func_name", + 'argnames', + 'parametersets', + 'idfn', + 'ids', + 'config', + 'nodeid', + 'func_name', ) # The argnames of the parametrization. @@ -896,21 +898,21 @@ class IdMaker: parametersets: Sequence[ParameterSet] # Optionally, a user-provided callable to make IDs for parameters in a # ParameterSet. - idfn: Optional[Callable[[Any], Optional[object]]] + idfn: Callable[[Any], object | None] | None # Optionally, explicit IDs for ParameterSets by index. - ids: Optional[Sequence[Optional[object]]] + ids: Sequence[object | None] | None # Optionally, the pytest config. # Used for controlling ASCII escaping, and for calling the # :hook:`pytest_make_parametrize_id` hook. - config: Optional[Config] + config: Config | None # Optionally, the ID of the node being parametrized. # Used only for clearer error messages. - nodeid: Optional[str] + nodeid: str | None # Optionally, the ID of the function being parametrized. # Used only for clearer error messages. - func_name: Optional[str] + func_name: str | None - def make_unique_parameterset_ids(self) -> List[str]: + def make_unique_parameterset_ids(self) -> list[str]: """Make a unique identifier for each ParameterSet, that may be used to identify the parametrization in a node ID. @@ -927,22 +929,22 @@ class IdMaker: # Record the number of occurrences of each ID. id_counts = Counter(resolved_ids) # Map the ID to its next suffix. - id_suffixes: Dict[str, int] = defaultdict(int) + id_suffixes: dict[str, int] = defaultdict(int) # Suffix non-unique IDs to make them unique. for index, id in enumerate(resolved_ids): if id_counts[id] > 1: - suffix = "" + suffix = '' if id and id[-1].isdigit(): - suffix = "_" - new_id = f"{id}{suffix}{id_suffixes[id]}" + suffix = '_' + new_id = f'{id}{suffix}{id_suffixes[id]}' while new_id in set(resolved_ids): id_suffixes[id] += 1 - new_id = f"{id}{suffix}{id_suffixes[id]}" + new_id = f'{id}{suffix}{id_suffixes[id]}' resolved_ids[index] = new_id id_suffixes[id] += 1 assert len(resolved_ids) == len( - set(resolved_ids) - ), f"Internal error: {resolved_ids=}" + set(resolved_ids), + ), f'Internal error: {resolved_ids=}' return resolved_ids def _resolve_ids(self) -> Iterable[str]: @@ -956,7 +958,7 @@ class IdMaker: yield self._idval_from_value_required(self.ids[idx], idx) else: # ID not provided - generate it. - yield "-".join( + yield '-'.join( self._idval(val, argname, idx) for val, argname in zip(parameterset.values, self.argnames) ) @@ -975,8 +977,8 @@ class IdMaker: return self._idval_from_argname(argname, idx) def _idval_from_function( - self, val: object, argname: str, idx: int - ) -> Optional[str]: + self, val: object, argname: str, idx: int, + ) -> str | None: """Try to make an ID for a parameter in a ParameterSet using the user-provided id callable, if given.""" if self.idfn is None: @@ -984,7 +986,7 @@ class IdMaker: try: id = self.idfn(val) except Exception as e: - prefix = f"{self.nodeid}: " if self.nodeid is not None else "" + prefix = f'{self.nodeid}: ' if self.nodeid is not None else '' msg = "error raised while trying to determine id of parameter '{}' at position {}" msg = prefix + msg.format(argname, idx) raise ValueError(msg) from e @@ -992,17 +994,17 @@ class IdMaker: return None return self._idval_from_value(id) - def _idval_from_hook(self, val: object, argname: str) -> Optional[str]: + def _idval_from_hook(self, val: object, argname: str) -> str | None: """Try to make an ID for a parameter in a ParameterSet by calling the :hook:`pytest_make_parametrize_id` hook.""" if self.config: - id: Optional[str] = self.config.hook.pytest_make_parametrize_id( - config=self.config, val=val, argname=argname + id: str | None = self.config.hook.pytest_make_parametrize_id( + config=self.config, val=val, argname=argname, ) return id return None - def _idval_from_value(self, val: object) -> Optional[str]: + def _idval_from_value(self, val: object) -> str | None: """Try to make an ID for a parameter in a ParameterSet from its value, if the value type is supported.""" if isinstance(val, (str, bytes)): @@ -1016,9 +1018,9 @@ class IdMaker: pass elif isinstance(val, enum.Enum): return str(val) - elif isinstance(getattr(val, "__name__", None), str): + elif isinstance(getattr(val, '__name__', None), str): # Name of a class, function, module, etc. - name: str = getattr(val, "__name__") + name: str = getattr(val, '__name__') return name return None @@ -1030,14 +1032,14 @@ class IdMaker: # Fail. if self.func_name is not None: - prefix = f"In {self.func_name}: " + prefix = f'In {self.func_name}: ' elif self.nodeid is not None: - prefix = f"In {self.nodeid}: " + prefix = f'In {self.nodeid}: ' else: - prefix = "" + prefix = '' msg = ( - f"{prefix}ids contains unsupported value {saferepr(val)} (type: {type(val)!r}) at index {idx}. " - "Supported types are: str, bytes, int, float, complex, bool, enum, regex or anything with a __name__." + f'{prefix}ids contains unsupported value {saferepr(val)} (type: {type(val)!r}) at index {idx}. ' + 'Supported types are: str, bytes, int, float, complex, bool, enum, regex or anything with a __name__.' ) fail(msg, pytrace=False) @@ -1060,15 +1062,15 @@ class CallSpec2: # arg name -> arg value which will be passed to a fixture or pseudo-fixture # of the same name. (indirect or direct parametrization respectively) - params: Dict[str, object] = dataclasses.field(default_factory=dict) + params: dict[str, object] = dataclasses.field(default_factory=dict) # arg name -> arg index. - indices: Dict[str, int] = dataclasses.field(default_factory=dict) + indices: dict[str, int] = dataclasses.field(default_factory=dict) # Used for sorting parametrized resources. _arg2scope: Mapping[str, Scope] = dataclasses.field(default_factory=dict) # Parts which will be added to the item's name in `[..]` separated by "-". _idlist: Sequence[str] = dataclasses.field(default_factory=tuple) # Marks which will be applied to the item. - marks: List[Mark] = dataclasses.field(default_factory=list) + marks: list[Mark] = dataclasses.field(default_factory=list) def setmulti( self, @@ -1076,16 +1078,16 @@ class CallSpec2: argnames: Iterable[str], valset: Iterable[object], id: str, - marks: Iterable[Union[Mark, MarkDecorator]], + marks: Iterable[Mark | MarkDecorator], scope: Scope, param_index: int, - ) -> "CallSpec2": + ) -> CallSpec2: params = self.params.copy() indices = self.indices.copy() arg2scope = dict(self._arg2scope) for arg, val in zip(argnames, valset): if arg in params: - raise ValueError(f"duplicate parametrization of {arg!r}") + raise ValueError(f'duplicate parametrization of {arg!r}') params[arg] = val indices[arg] = param_index arg2scope[arg] = scope @@ -1105,7 +1107,7 @@ class CallSpec2: @property def id(self) -> str: - return "-".join(self._idlist) + return '-'.join(self._idlist) def get_direct_param_fixture_func(request: FixtureRequest) -> Any: @@ -1127,7 +1129,7 @@ class Metafunc: def __init__( self, - definition: "FunctionDefinition", + definition: FunctionDefinition, fixtureinfo: fixtures.FuncFixtureInfo, config: Config, cls=None, @@ -1158,19 +1160,19 @@ class Metafunc: self._arg2fixturedefs = fixtureinfo.name2fixturedefs # Result of parametrize(). - self._calls: List[CallSpec2] = [] + self._calls: list[CallSpec2] = [] def parametrize( self, - argnames: Union[str, Sequence[str]], - argvalues: Iterable[Union[ParameterSet, Sequence[object], object]], - indirect: Union[bool, Sequence[str]] = False, - ids: Optional[ - Union[Iterable[Optional[object]], Callable[[Any], Optional[object]]] - ] = None, - scope: Optional[_ScopeName] = None, + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], + indirect: bool | Sequence[str] = False, + ids: None | ( + Iterable[object | None] | Callable[[Any], object | None] + ) = None, + scope: _ScopeName | None = None, *, - _param_mark: Optional[Mark] = None, + _param_mark: Mark | None = None, ) -> None: """Add new invocations to the underlying test function using the list of argvalues for the given argnames. Parametrization is performed @@ -1243,7 +1245,7 @@ class Metafunc: ) del argvalues - if "request" in argnames: + if 'request' in argnames: fail( "'request' is a reserved name and cannot be used in @pytest.mark.parametrize", pytrace=False, @@ -1251,7 +1253,7 @@ class Metafunc: if scope is not None: scope_ = Scope.from_user( - scope, descr=f"parametrize() call in {self.function.__name__}" + scope, descr=f'parametrize() call in {self.function.__name__}', ) else: scope_ = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) @@ -1265,12 +1267,12 @@ class Metafunc: ids = generated_ids ids = self._resolve_parameter_set_ids( - argnames, ids, parametersets, nodeid=self.definition.nodeid + argnames, ids, parametersets, nodeid=self.definition.nodeid, ) # Store used (possibly generated) ids with parametrize Marks. if _param_mark and _param_mark._param_ids_from and generated_ids is None: - object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) + object.__setattr__(_param_mark._param_ids_from, '_param_ids_generated', ids) # Add funcargs as fixturedefs to fixtureinfo.arg2fixturedefs by registering # artificial "pseudo" FixtureDef's so that later at test execution time we can @@ -1295,24 +1297,24 @@ class Metafunc: elif scope_ is Scope.Package: node = collector.session else: - assert False, f"Unhandled missing scope: {scope}" + assert False, f'Unhandled missing scope: {scope}' if node is None: name2pseudofixturedef = None else: - default: Dict[str, FixtureDef[Any]] = {} + default: dict[str, FixtureDef[Any]] = {} name2pseudofixturedef = node.stash.setdefault( - name2pseudofixturedef_key, default + name2pseudofixturedef_key, default, ) arg_directness = self._resolve_args_directness(argnames, indirect) for argname in argnames: - if arg_directness[argname] == "indirect": + if arg_directness[argname] == 'indirect': continue if name2pseudofixturedef is not None and argname in name2pseudofixturedef: fixturedef = name2pseudofixturedef[argname] else: fixturedef = FixtureDef( config=self.config, - baseid="", + baseid='', argname=argname, func=get_direct_param_fixture_func, scope=scope_, @@ -1331,7 +1333,7 @@ class Metafunc: newcalls = [] for callspec in self._calls or [CallSpec2()]: for param_index, (param_id, param_set) in enumerate( - zip(ids, parametersets) + zip(ids, parametersets), ): newcallspec = callspec.setmulti( argnames=argnames, @@ -1347,12 +1349,12 @@ class Metafunc: def _resolve_parameter_set_ids( self, argnames: Sequence[str], - ids: Optional[ - Union[Iterable[Optional[object]], Callable[[Any], Optional[object]]] - ], + ids: None | ( + Iterable[object | None] | Callable[[Any], object | None] + ), parametersets: Sequence[ParameterSet], nodeid: str, - ) -> List[str]: + ) -> list[str]: """Resolve the actual ids for the given parameter sets. :param argnames: @@ -1390,22 +1392,22 @@ class Metafunc: def _validate_ids( self, - ids: Iterable[Optional[object]], + ids: Iterable[object | None], parametersets: Sequence[ParameterSet], func_name: str, - ) -> List[Optional[object]]: + ) -> list[object | None]: try: num_ids = len(ids) # type: ignore[arg-type] except TypeError: try: iter(ids) except TypeError as e: - raise TypeError("ids must be a callable or an iterable") from e + raise TypeError('ids must be a callable or an iterable') from e num_ids = len(parametersets) # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849 if num_ids != len(parametersets) and num_ids != 0: - msg = "In {}: {} parameter sets specified, with different number of ids: {}" + msg = 'In {}: {} parameter sets specified, with different number of ids: {}' fail(msg.format(func_name, len(parametersets), num_ids), pytrace=False) return list(itertools.islice(ids, num_ids)) @@ -1413,8 +1415,8 @@ class Metafunc: def _resolve_args_directness( self, argnames: Sequence[str], - indirect: Union[bool, Sequence[str]], - ) -> Dict[str, Literal["indirect", "direct"]]: + indirect: bool | Sequence[str], + ) -> dict[str, Literal['indirect', 'direct']]: """Resolve if each parametrized argument must be considered an indirect parameter to a fixture of the same name, or a direct parameter to the parametrized function, based on the ``indirect`` parameter of the @@ -1427,24 +1429,24 @@ class Metafunc: :returns A dict mapping each arg name to either "indirect" or "direct". """ - arg_directness: Dict[str, Literal["indirect", "direct"]] + arg_directness: dict[str, Literal['indirect', 'direct']] if isinstance(indirect, bool): arg_directness = dict.fromkeys( - argnames, "indirect" if indirect else "direct" + argnames, 'indirect' if indirect else 'direct', ) elif isinstance(indirect, Sequence): - arg_directness = dict.fromkeys(argnames, "direct") + arg_directness = dict.fromkeys(argnames, 'direct') for arg in indirect: if arg not in argnames: fail( f"In {self.function.__name__}: indirect fixture '{arg}' doesn't exist", pytrace=False, ) - arg_directness[arg] = "indirect" + arg_directness[arg] = 'indirect' else: fail( - f"In {self.function.__name__}: expected Sequence or boolean" - f" for indirect, got {type(indirect).__name__}", + f'In {self.function.__name__}: expected Sequence or boolean' + f' for indirect, got {type(indirect).__name__}', pytrace=False, ) return arg_directness @@ -1452,7 +1454,7 @@ class Metafunc: def _validate_if_using_arg_names( self, argnames: Sequence[str], - indirect: Union[bool, Sequence[str]], + indirect: bool | Sequence[str], ) -> None: """Check if all argnames are being used, by default values, or directly/indirectly. @@ -1471,9 +1473,9 @@ class Metafunc: ) else: if isinstance(indirect, Sequence): - name = "fixture" if arg in indirect else "argument" + name = 'fixture' if arg in indirect else 'argument' else: - name = "fixture" if indirect else "argument" + name = 'fixture' if indirect else 'argument' fail( f"In {func_name}: function uses no {name} '{arg}'", pytrace=False, @@ -1483,7 +1485,7 @@ class Metafunc: def _find_parametrized_scope( argnames: Sequence[str], arg2fixturedefs: Mapping[str, Sequence[fixtures.FixtureDef[object]]], - indirect: Union[bool, Sequence[str]], + indirect: bool | Sequence[str], ) -> Scope: """Find the most appropriate scope for a parametrized call based on its arguments. @@ -1512,12 +1514,12 @@ def _find_parametrized_scope( return Scope.Function -def _ascii_escaped_by_config(val: Union[str, bytes], config: Optional[Config]) -> str: +def _ascii_escaped_by_config(val: str | bytes, config: Config | None) -> str: if config is None: escape_option = False else: escape_option = config.getini( - "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" + 'disable_test_id_escaping_and_forfeit_all_rights_to_community_support', ) # TODO: If escaping is turned off and the user passes bytes, # will return a bytes. For now we ignore this but the @@ -1527,7 +1529,7 @@ def _ascii_escaped_by_config(val: Union[str, bytes], config: Optional[Config]) - def _pretty_fixture_path(invocation_dir: Path, func) -> str: loc = Path(getlocation(func, invocation_dir)) - prefix = Path("...", "_pytest") + prefix = Path('...', '_pytest') try: return str(prefix / loc.relative_to(_PYTEST_DIR)) except ValueError: @@ -1546,7 +1548,7 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None: session.perform_collect() invocation_dir = config.invocation_params.dir tw = _pytest.config.create_terminal_writer(config) - verbose = config.getvalue("verbose") + verbose = config.getvalue('verbose') def get_best_relpath(func) -> str: loc = getlocation(func, invocation_dir) @@ -1554,30 +1556,30 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None: def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None: argname = fixture_def.argname - if verbose <= 0 and argname.startswith("_"): + if verbose <= 0 and argname.startswith('_'): return prettypath = _pretty_fixture_path(invocation_dir, fixture_def.func) - tw.write(f"{argname}", green=True) - tw.write(f" -- {prettypath}", yellow=True) - tw.write("\n") + tw.write(f'{argname}', green=True) + tw.write(f' -- {prettypath}', yellow=True) + tw.write('\n') fixture_doc = inspect.getdoc(fixture_def.func) if fixture_doc: write_docstring( - tw, fixture_doc.split("\n\n")[0] if verbose <= 0 else fixture_doc + tw, fixture_doc.split('\n\n')[0] if verbose <= 0 else fixture_doc, ) else: - tw.line(" no docstring available", red=True) + tw.line(' no docstring available', red=True) def write_item(item: nodes.Item) -> None: # Not all items have _fixtureinfo attribute. - info: Optional[FuncFixtureInfo] = getattr(item, "_fixtureinfo", None) + info: FuncFixtureInfo | None = getattr(item, '_fixtureinfo', None) if info is None or not info.name2fixturedefs: # This test item does not use any fixtures. return tw.line() - tw.sep("-", f"fixtures used by {item.name}") + tw.sep('-', f'fixtures used by {item.name}') # TODO: Fix this type ignore. - tw.sep("-", f"({get_best_relpath(item.function)})") # type: ignore[attr-defined] + tw.sep('-', f'({get_best_relpath(item.function)})') # type: ignore[attr-defined] # dict key not used in loop but needed for sorting. for _, fixturedefs in sorted(info.name2fixturedefs.items()): assert fixturedefs is not None @@ -1590,7 +1592,7 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None: write_item(session_item) -def showfixtures(config: Config) -> Union[int, ExitCode]: +def showfixtures(config: Config) -> int | ExitCode: from _pytest.main import wrap_session return wrap_session(config, _showfixtures_main) @@ -1602,12 +1604,12 @@ def _showfixtures_main(config: Config, session: Session) -> None: session.perform_collect() invocation_dir = config.invocation_params.dir tw = _pytest.config.create_terminal_writer(config) - verbose = config.getvalue("verbose") + verbose = config.getvalue('verbose') fm = session._fixturemanager available = [] - seen: Set[Tuple[str, str]] = set() + seen: set[tuple[str, str]] = set() for argname, fixturedefs in fm._arg2fixturedefs.items(): assert fixturedefs is not None @@ -1625,34 +1627,34 @@ def _showfixtures_main(config: Config, session: Session) -> None: _pretty_fixture_path(invocation_dir, fixturedef.func), fixturedef.argname, fixturedef, - ) + ), ) available.sort() currentmodule = None for baseid, module, prettypath, argname, fixturedef in available: if currentmodule != module: - if not module.startswith("_pytest."): + if not module.startswith('_pytest.'): tw.line() - tw.sep("-", f"fixtures defined from {module}") + tw.sep('-', f'fixtures defined from {module}') currentmodule = module - if verbose <= 0 and argname.startswith("_"): + if verbose <= 0 and argname.startswith('_'): continue - tw.write(f"{argname}", green=True) - if fixturedef.scope != "function": - tw.write(" [%s scope]" % fixturedef.scope, cyan=True) - tw.write(f" -- {prettypath}", yellow=True) - tw.write("\n") + tw.write(f'{argname}', green=True) + if fixturedef.scope != 'function': + tw.write(' [%s scope]' % fixturedef.scope, cyan=True) + tw.write(f' -- {prettypath}', yellow=True) + tw.write('\n') doc = inspect.getdoc(fixturedef.func) if doc: - write_docstring(tw, doc.split("\n\n")[0] if verbose <= 0 else doc) + write_docstring(tw, doc.split('\n\n')[0] if verbose <= 0 else doc) else: - tw.line(" no docstring available", red=True) + tw.line(' no docstring available', red=True) tw.line() -def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None: - for line in doc.split("\n"): +def write_docstring(tw: TerminalWriter, doc: str, indent: str = ' ') -> None: + for line in doc.split('\n'): tw.line(indent + line) @@ -1692,13 +1694,13 @@ class Function(PyobjMixin, nodes.Item): self, name: str, parent, - config: Optional[Config] = None, - callspec: Optional[CallSpec2] = None, + config: Config | None = None, + callspec: CallSpec2 | None = None, callobj=NOTSET, - keywords: Optional[Mapping[str, Any]] = None, - session: Optional[Session] = None, - fixtureinfo: Optional[FuncFixtureInfo] = None, - originalname: Optional[str] = None, + keywords: Mapping[str, Any] | None = None, + session: Session | None = None, + fixtureinfo: FuncFixtureInfo | None = None, + originalname: str | None = None, ) -> None: super().__init__(name, parent, config=config, session=session) @@ -1740,12 +1742,12 @@ class Function(PyobjMixin, nodes.Item): # todo: determine sound type limitations @classmethod - def from_parent(cls, parent, **kw) -> "Self": + def from_parent(cls, parent, **kw) -> Self: """The public constructor.""" return super().from_parent(parent=parent, **kw) def _initrequest(self) -> None: - self.funcargs: Dict[str, object] = {} + self.funcargs: dict[str, object] = {} self._request = fixtures.TopRequest(self, _ispytest=True) @property @@ -1775,7 +1777,7 @@ class Function(PyobjMixin, nodes.Item): self._request._fillfixtures() def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: - if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): + if hasattr(self, '_obj') and not self.config.getoption('fulltrace', False): code = _pytest._code.Code.from_function(get_real_func(self.obj)) path, firstlineno = code.path, code.firstlineno traceback = excinfo.traceback @@ -1790,14 +1792,14 @@ class Function(PyobjMixin, nodes.Item): # issue364: mark all but first and last frames to # only show a single-line message for each frame. - if self.config.getoption("tbstyle", "auto") == "auto": + if self.config.getoption('tbstyle', 'auto') == 'auto': if len(ntraceback) > 2: ntraceback = Traceback( ( ntraceback[0], - *(t.with_repr_style("short") for t in ntraceback[1:-1]), + *(t.with_repr_style('short') for t in ntraceback[1:-1]), ntraceback[-1], - ) + ), ) return ntraceback @@ -1807,10 +1809,10 @@ class Function(PyobjMixin, nodes.Item): def repr_failure( # type: ignore[override] self, excinfo: ExceptionInfo[BaseException], - ) -> Union[str, TerminalRepr]: - style = self.config.getoption("tbstyle", "auto") - if style == "auto": - style = "long" + ) -> str | TerminalRepr: + style = self.config.getoption('tbstyle', 'auto') + if style == 'auto': + style = 'long' return self._repr_failure_py(excinfo, style=style) @@ -1819,6 +1821,6 @@ class FunctionDefinition(Function): definition nodes and manage to get rid of ``metafunc``.""" def runtest(self) -> None: - raise RuntimeError("function definitions are not supposed to be run as tests") + raise RuntimeError('function definitions are not supposed to be run as tests') setup = runtest diff --git a/.venv/lib/python3.10/site-packages/_pytest/python_api.py b/.venv/lib/python3.10/site-packages/_pytest/python_api.py index 7e51da3..c81a983 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/python_api.py +++ b/.venv/lib/python3.10/site-packages/_pytest/python_api.py @@ -1,10 +1,12 @@ # mypy: allow-untyped-defs +from __future__ import annotations + +import math +import pprint from collections.abc import Collection from collections.abc import Sized from decimal import Decimal -import math from numbers import Complex -import pprint from types import TracebackType from typing import Any from typing import Callable @@ -33,25 +35,25 @@ if TYPE_CHECKING: def _compare_approx( full_object: object, - message_data: Sequence[Tuple[str, str, str]], + message_data: Sequence[tuple[str, str, str]], number_of_elements: int, different_ids: Sequence[object], max_abs_diff: float, max_rel_diff: float, -) -> List[str]: +) -> list[str]: message_list = list(message_data) - message_list.insert(0, ("Index", "Obtained", "Expected")) + message_list.insert(0, ('Index', 'Obtained', 'Expected')) max_sizes = [0, 0, 0] for index, obtained, expected in message_list: max_sizes[0] = max(max_sizes[0], len(index)) max_sizes[1] = max(max_sizes[1], len(obtained)) max_sizes[2] = max(max_sizes[2], len(expected)) explanation = [ - f"comparison failed. Mismatched elements: {len(different_ids)} / {number_of_elements}:", - f"Max absolute difference: {max_abs_diff}", - f"Max relative difference: {max_rel_diff}", + f'comparison failed. Mismatched elements: {len(different_ids)} / {number_of_elements}:', + f'Max absolute difference: {max_abs_diff}', + f'Max relative difference: {max_rel_diff}', ] + [ - f"{indexes:<{max_sizes[0]}} | {obtained:<{max_sizes[1]}} | {expected:<{max_sizes[2]}}" + f'{indexes:<{max_sizes[0]}} | {obtained:<{max_sizes[1]}} | {expected:<{max_sizes[2]}}' for indexes, obtained, expected in message_list ] return explanation @@ -79,11 +81,11 @@ class ApproxBase: def __repr__(self) -> str: raise NotImplementedError - def _repr_compare(self, other_side: Any) -> List[str]: + def _repr_compare(self, other_side: Any) -> list[str]: return [ - "comparison failed", - f"Obtained: {other_side}", - f"Expected: {self}", + 'comparison failed', + f'Obtained: {other_side}', + f'Expected: {self}', ] def __eq__(self, actual) -> bool: @@ -94,7 +96,7 @@ class ApproxBase: def __bool__(self): __tracebackhide__ = True raise AssertionError( - "approx() is not supported in a boolean context.\nDid you mean: `assert a == approx(b)`?" + 'approx() is not supported in a boolean context.\nDid you mean: `assert a == approx(b)`?', ) # Ignore type because of https://github.com/python/mypy/issues/4266. @@ -103,7 +105,7 @@ class ApproxBase: def __ne__(self, actual) -> bool: return not (actual == self) - def _approx_scalar(self, x) -> "ApproxScalar": + def _approx_scalar(self, x) -> ApproxScalar: if isinstance(x, Decimal): return ApproxDecimal(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) @@ -138,16 +140,16 @@ class ApproxNumpy(ApproxBase): def __repr__(self) -> str: list_scalars = _recursive_sequence_map( - self._approx_scalar, self.expected.tolist() + self._approx_scalar, self.expected.tolist(), ) - return f"approx({list_scalars!r})" + return f'approx({list_scalars!r})' - def _repr_compare(self, other_side: "ndarray") -> List[str]: + def _repr_compare(self, other_side: ndarray) -> list[str]: import itertools import math def get_value_from_nested_list( - nested_list: List[Any], nd_index: Tuple[Any, ...] + nested_list: list[Any], nd_index: tuple[Any, ...], ) -> Any: """ Helper function to get the value out of a nested list, given an n-dimensional index. @@ -160,13 +162,13 @@ class ApproxNumpy(ApproxBase): np_array_shape = self.expected.shape approx_side_as_seq = _recursive_sequence_map( - self._approx_scalar, self.expected.tolist() + self._approx_scalar, self.expected.tolist(), ) if np_array_shape != other_side.shape: return [ - "Impossible to compare arrays with different shapes.", - f"Shapes: {np_array_shape} and {other_side.shape}", + 'Impossible to compare arrays with different shapes.', + f'Shapes: {np_array_shape} and {other_side.shape}', ] number_of_elements = self.expected.size @@ -238,9 +240,9 @@ class ApproxMapping(ApproxBase): with numeric values (the keys can be anything).""" def __repr__(self) -> str: - return f"approx({({k: self._approx_scalar(v) for k, v in self.expected.items()})!r})" + return f'approx({({k: self._approx_scalar(v) for k, v in self.expected.items()})!r})' - def _repr_compare(self, other_side: Mapping[object, float]) -> List[str]: + def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]: import math approx_side_as_map = { @@ -252,12 +254,12 @@ class ApproxMapping(ApproxBase): max_rel_diff = -math.inf different_ids = [] for (approx_key, approx_value), other_value in zip( - approx_side_as_map.items(), other_side.values() + approx_side_as_map.items(), other_side.values(), ): if approx_value != other_value: if approx_value.expected is not None and other_value is not None: max_abs_diff = max( - max_abs_diff, abs(approx_value.expected - other_value) + max_abs_diff, abs(approx_value.expected - other_value), ) if approx_value.expected == 0.0: max_rel_diff = math.inf @@ -265,8 +267,8 @@ class ApproxMapping(ApproxBase): max_rel_diff = max( max_rel_diff, abs( - (approx_value.expected - other_value) - / approx_value.expected + (approx_value.expected - other_value) / + approx_value.expected, ), ) different_ids.append(approx_key) @@ -302,7 +304,7 @@ class ApproxMapping(ApproxBase): __tracebackhide__ = True for key, value in self.expected.items(): if isinstance(value, type(self.expected)): - msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}" + msg = 'pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}' raise TypeError(msg.format(key, value, pprint.pformat(self.expected))) @@ -313,15 +315,15 @@ class ApproxSequenceLike(ApproxBase): seq_type = type(self.expected) if seq_type not in (tuple, list): seq_type = list - return f"approx({seq_type(self._approx_scalar(x) for x in self.expected)!r})" + return f'approx({seq_type(self._approx_scalar(x) for x in self.expected)!r})' - def _repr_compare(self, other_side: Sequence[float]) -> List[str]: + def _repr_compare(self, other_side: Sequence[float]) -> list[str]: import math if len(self.expected) != len(other_side): return [ - "Impossible to compare lists with different sizes.", - f"Lengths: {len(self.expected)} and {len(other_side)}", + 'Impossible to compare lists with different sizes.', + f'Lengths: {len(self.expected)} and {len(other_side)}', ] approx_side_as_map = _recursive_sequence_map(self._approx_scalar, self.expected) @@ -331,7 +333,7 @@ class ApproxSequenceLike(ApproxBase): max_rel_diff = -math.inf different_ids = [] for i, (approx_value, other_value) in enumerate( - zip(approx_side_as_map, other_side) + zip(approx_side_as_map, other_side), ): if approx_value != other_value: abs_diff = abs(approx_value.expected - other_value) @@ -371,7 +373,7 @@ class ApproxSequenceLike(ApproxBase): __tracebackhide__ = True for index, x in enumerate(self.expected): if isinstance(x, type(self.expected)): - msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}" + msg = 'pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}' raise TypeError(msg.format(x, index, pprint.pformat(self.expected))) @@ -380,8 +382,8 @@ class ApproxScalar(ApproxBase): # Using Real should be better than this Union, but not possible yet: # https://github.com/python/typeshed/pull/3108 - DEFAULT_ABSOLUTE_TOLERANCE: Union[float, Decimal] = 1e-12 - DEFAULT_RELATIVE_TOLERANCE: Union[float, Decimal] = 1e-6 + DEFAULT_ABSOLUTE_TOLERANCE: float | Decimal = 1e-12 + DEFAULT_RELATIVE_TOLERANCE: float | Decimal = 1e-6 def __repr__(self) -> str: """Return a string communicating both the expected value and the @@ -393,24 +395,24 @@ class ApproxScalar(ApproxBase): # tolerances, i.e. non-numerics and infinities. Need to call abs to # handle complex numbers, e.g. (inf + 1j). if (not isinstance(self.expected, (Complex, Decimal))) or math.isinf( - abs(self.expected) # type: ignore[arg-type] + abs(self.expected), # type: ignore[arg-type] ): return str(self.expected) # If a sensible tolerance can't be calculated, self.tolerance will # raise a ValueError. In this case, display '???'. try: - vetted_tolerance = f"{self.tolerance:.1e}" + vetted_tolerance = f'{self.tolerance:.1e}' if ( - isinstance(self.expected, Complex) - and self.expected.imag - and not math.isinf(self.tolerance) + isinstance(self.expected, Complex) and + self.expected.imag and + not math.isinf(self.tolerance) ): - vetted_tolerance += " ∠ ±180°" + vetted_tolerance += ' ∠ ±180°' except ValueError: - vetted_tolerance = "???" + vetted_tolerance = '???' - return f"{self.expected} ± {vetted_tolerance}" + return f'{self.expected} ± {vetted_tolerance}' def __eq__(self, actual) -> bool: """Return whether the given value is equal to the expected value @@ -429,8 +431,8 @@ class ApproxScalar(ApproxBase): # NB: we need Complex, rather than just Number, to ensure that __abs__, # __sub__, and __float__ are defined. if not ( - isinstance(self.expected, (Complex, Decimal)) - and isinstance(actual, (Complex, Decimal)) + isinstance(self.expected, (Complex, Decimal)) and + isinstance(actual, (Complex, Decimal)) ): return False @@ -473,7 +475,7 @@ class ApproxScalar(ApproxBase): if absolute_tolerance < 0: raise ValueError( - f"absolute tolerance can't be negative: {absolute_tolerance}" + f"absolute tolerance can't be negative: {absolute_tolerance}", ) if math.isnan(absolute_tolerance): raise ValueError("absolute tolerance can't be NaN.") @@ -490,12 +492,12 @@ class ApproxScalar(ApproxBase): # because we don't want to raise errors about the relative tolerance if # we aren't even going to use it. relative_tolerance = set_default( - self.rel, self.DEFAULT_RELATIVE_TOLERANCE + self.rel, self.DEFAULT_RELATIVE_TOLERANCE, ) * abs(self.expected) if relative_tolerance < 0: raise ValueError( - f"relative tolerance can't be negative: {relative_tolerance}" + f"relative tolerance can't be negative: {relative_tolerance}", ) if math.isnan(relative_tolerance): raise ValueError("relative tolerance can't be NaN.") @@ -507,8 +509,8 @@ class ApproxScalar(ApproxBase): class ApproxDecimal(ApproxScalar): """Perform approximate comparisons where the expected value is a Decimal.""" - DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12") - DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6") + DEFAULT_ABSOLUTE_TOLERANCE = Decimal('1e-12') + DEFAULT_RELATIVE_TOLERANCE = Decimal('1e-6') def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: @@ -711,20 +713,20 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: __tracebackhide__ = True if isinstance(expected, Decimal): - cls: Type[ApproxBase] = ApproxDecimal + cls: type[ApproxBase] = ApproxDecimal elif isinstance(expected, Mapping): cls = ApproxMapping elif _is_numpy_array(expected): expected = _as_numpy_array(expected) cls = ApproxNumpy elif ( - hasattr(expected, "__getitem__") - and isinstance(expected, Sized) - and not isinstance(expected, (str, bytes)) + hasattr(expected, '__getitem__') and + isinstance(expected, Sized) and + not isinstance(expected, (str, bytes)) ): cls = ApproxSequenceLike elif isinstance(expected, Collection) and not isinstance(expected, (str, bytes)): - msg = f"pytest.approx() only supports ordered sequences, but got: {expected!r}" + msg = f'pytest.approx() only supports ordered sequences, but got: {expected!r}' raise TypeError(msg) else: cls = ApproxScalar @@ -740,42 +742,42 @@ def _is_numpy_array(obj: object) -> bool: return _as_numpy_array(obj) is not None -def _as_numpy_array(obj: object) -> Optional["ndarray"]: +def _as_numpy_array(obj: object) -> ndarray | None: """ Return an ndarray if the given object is implicitly convertible to ndarray, and numpy is already imported, otherwise None. """ import sys - np: Any = sys.modules.get("numpy") + np: Any = sys.modules.get('numpy') if np is not None: # avoid infinite recursion on numpy scalars, which have __array__ if np.isscalar(obj): return None elif isinstance(obj, np.ndarray): return obj - elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"): + elif hasattr(obj, '__array__') or hasattr('obj', '__array_interface__'): return np.asarray(obj) return None # builtin pytest.raises helper -E = TypeVar("E", bound=BaseException) +E = TypeVar('E', bound=BaseException) @overload def raises( - expected_exception: Union[Type[E], Tuple[Type[E], ...]], + expected_exception: type[E] | tuple[type[E], ...], *, - match: Optional[Union[str, Pattern[str]]] = ..., -) -> "RaisesContext[E]": + match: str | Pattern[str] | None = ..., +) -> RaisesContext[E]: ... @overload def raises( - expected_exception: Union[Type[E], Tuple[Type[E], ...]], + expected_exception: type[E] | tuple[type[E], ...], func: Callable[..., Any], *args: Any, **kwargs: Any, @@ -784,8 +786,8 @@ def raises( def raises( - expected_exception: Union[Type[E], Tuple[Type[E], ...]], *args: Any, **kwargs: Any -) -> Union["RaisesContext[E]", _pytest._code.ExceptionInfo[E]]: + expected_exception: type[E] | tuple[type[E], ...], *args: Any, **kwargs: Any, +) -> RaisesContext[E] | _pytest._code.ExceptionInfo[E]: r"""Assert that a code block/function call raises an exception type, or one of its subclasses. :param expected_exception: @@ -928,34 +930,34 @@ def raises( if not expected_exception: raise ValueError( - f"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. " + f'Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. ' f"Raising exceptions is already understood as failing the test, so you don't need " - f"any special code to say 'this should never raise an exception'." + f"any special code to say 'this should never raise an exception'.", ) if isinstance(expected_exception, type): - expected_exceptions: Tuple[Type[E], ...] = (expected_exception,) + expected_exceptions: tuple[type[E], ...] = (expected_exception,) else: expected_exceptions = expected_exception for exc in expected_exceptions: if not isinstance(exc, type) or not issubclass(exc, BaseException): - msg = "expected exception must be a BaseException type, not {}" # type: ignore[unreachable] + msg = 'expected exception must be a BaseException type, not {}' # type: ignore[unreachable] not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__ raise TypeError(msg.format(not_a)) - message = f"DID NOT RAISE {expected_exception}" + message = f'DID NOT RAISE {expected_exception}' if not args: - match: Optional[Union[str, Pattern[str]]] = kwargs.pop("match", None) + match: str | Pattern[str] | None = kwargs.pop('match', None) if kwargs: - msg = "Unexpected keyword arguments passed to pytest.raises: " - msg += ", ".join(sorted(kwargs)) - msg += "\nUse context-manager form instead?" + msg = 'Unexpected keyword arguments passed to pytest.raises: ' + msg += ', '.join(sorted(kwargs)) + msg += '\nUse context-manager form instead?' raise TypeError(msg) return RaisesContext(expected_exception, message, match) else: func = args[0] if not callable(func): - raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") + raise TypeError(f'{func!r} object (type: {type(func)}) must be callable') try: func(*args[1:], **kwargs) except expected_exception as e: @@ -971,14 +973,14 @@ raises.Exception = fail.Exception # type: ignore class RaisesContext(ContextManager[_pytest._code.ExceptionInfo[E]]): def __init__( self, - expected_exception: Union[Type[E], Tuple[Type[E], ...]], + expected_exception: type[E] | tuple[type[E], ...], message: str, - match_expr: Optional[Union[str, Pattern[str]]] = None, + match_expr: str | Pattern[str] | None = None, ) -> None: self.expected_exception = expected_exception self.message = message self.match_expr = match_expr - self.excinfo: Optional[_pytest._code.ExceptionInfo[E]] = None + self.excinfo: _pytest._code.ExceptionInfo[E] | None = None def __enter__(self) -> _pytest._code.ExceptionInfo[E]: self.excinfo = _pytest._code.ExceptionInfo.for_later() @@ -986,9 +988,9 @@ class RaisesContext(ContextManager[_pytest._code.ExceptionInfo[E]]): def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> bool: __tracebackhide__ = True if exc_type is None: diff --git a/.venv/lib/python3.10/site-packages/_pytest/python_path.py b/.venv/lib/python3.10/site-packages/_pytest/python_path.py index cceabbc..2b91373 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/python_path.py +++ b/.venv/lib/python3.10/site-packages/_pytest/python_path.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys import pytest @@ -6,19 +8,19 @@ from pytest import Parser def pytest_addoption(parser: Parser) -> None: - parser.addini("pythonpath", type="paths", help="Add paths to sys.path", default=[]) + parser.addini('pythonpath', type='paths', help='Add paths to sys.path', default=[]) @pytest.hookimpl(tryfirst=True) def pytest_load_initial_conftests(early_config: Config) -> None: # `pythonpath = a b` will set `sys.path` to `[a, b, x, y, z, ...]` - for path in reversed(early_config.getini("pythonpath")): + for path in reversed(early_config.getini('pythonpath')): sys.path.insert(0, str(path)) @pytest.hookimpl(trylast=True) def pytest_unconfigure(config: Config) -> None: - for path in config.getini("pythonpath"): + for path in config.getini('pythonpath'): path_str = str(path) if path_str in sys.path: sys.path.remove(path_str) diff --git a/.venv/lib/python3.10/site-packages/_pytest/recwarn.py b/.venv/lib/python3.10/site-packages/_pytest/recwarn.py index bcf9f14..9e63076 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/recwarn.py +++ b/.venv/lib/python3.10/site-packages/_pytest/recwarn.py @@ -1,7 +1,10 @@ # mypy: allow-untyped-defs """Record warnings during test function execution.""" -from pprint import pformat +from __future__ import annotations + import re +import warnings +from pprint import pformat from types import TracebackType from typing import Any from typing import Callable @@ -16,7 +19,6 @@ from typing import Tuple from typing import Type from typing import TypeVar from typing import Union -import warnings from _pytest.deprecated import check_ispytest from _pytest.fixtures import fixture @@ -24,11 +26,11 @@ from _pytest.outcomes import Exit from _pytest.outcomes import fail -T = TypeVar("T") +T = TypeVar('T') @fixture -def recwarn() -> Generator["WarningsRecorder", None, None]: +def recwarn() -> Generator[WarningsRecorder, None, None]: """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. See https://docs.pytest.org/en/latest/how-to/capture-warnings.html for information @@ -36,14 +38,14 @@ def recwarn() -> Generator["WarningsRecorder", None, None]: """ wrec = WarningsRecorder(_ispytest=True) with wrec: - warnings.simplefilter("default") + warnings.simplefilter('default') yield wrec @overload def deprecated_call( - *, match: Optional[Union[str, Pattern[str]]] = ... -) -> "WarningsRecorder": + *, match: str | Pattern[str] | None = ..., +) -> WarningsRecorder: ... @@ -53,8 +55,8 @@ def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T: def deprecated_call( - func: Optional[Callable[..., Any]] = None, *args: Any, **kwargs: Any -) -> Union["WarningsRecorder", Any]: + func: Callable[..., Any] | None = None, *args: Any, **kwargs: Any, +) -> WarningsRecorder | Any: """Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning`` or ``FutureWarning``. This function can be used as a context manager:: @@ -82,22 +84,22 @@ def deprecated_call( if func is not None: args = (func, *args) return warns( - (DeprecationWarning, PendingDeprecationWarning, FutureWarning), *args, **kwargs + (DeprecationWarning, PendingDeprecationWarning, FutureWarning), *args, **kwargs, ) @overload def warns( - expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = ..., + expected_warning: type[Warning] | tuple[type[Warning], ...] = ..., *, - match: Optional[Union[str, Pattern[str]]] = ..., -) -> "WarningsChecker": + match: str | Pattern[str] | None = ..., +) -> WarningsChecker: ... @overload def warns( - expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]], + expected_warning: type[Warning] | tuple[type[Warning], ...], func: Callable[..., T], *args: Any, **kwargs: Any, @@ -106,11 +108,11 @@ def warns( def warns( - expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = Warning, + expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, *args: Any, - match: Optional[Union[str, Pattern[str]]] = None, + match: str | Pattern[str] | None = None, **kwargs: Any, -) -> Union["WarningsChecker", Any]: +) -> WarningsChecker | Any: r"""Assert that code raises a particular class of warning. Specifically, the parameter ``expected_warning`` can be a warning class or tuple @@ -155,16 +157,16 @@ def warns( __tracebackhide__ = True if not args: if kwargs: - argnames = ", ".join(sorted(kwargs)) + argnames = ', '.join(sorted(kwargs)) raise TypeError( - f"Unexpected keyword arguments passed to pytest.warns: {argnames}" - "\nUse context-manager form instead?" + f'Unexpected keyword arguments passed to pytest.warns: {argnames}' + '\nUse context-manager form instead?', ) return WarningsChecker(expected_warning, match_expr=match, _ispytest=True) else: func = args[0] if not callable(func): - raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") + raise TypeError(f'{func!r} object (type: {type(func)}) must be callable') with WarningsChecker(expected_warning, _ispytest=True): return func(*args[1:], **kwargs) @@ -187,18 +189,18 @@ class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg] # Type ignored due to the way typeshed handles warnings.catch_warnings. super().__init__(record=True) # type: ignore[call-arg] self._entered = False - self._list: List[warnings.WarningMessage] = [] + self._list: list[warnings.WarningMessage] = [] @property - def list(self) -> List["warnings.WarningMessage"]: + def list(self) -> list[warnings.WarningMessage]: """The list of recorded warnings.""" return self._list - def __getitem__(self, i: int) -> "warnings.WarningMessage": + def __getitem__(self, i: int) -> warnings.WarningMessage: """Get a recorded warning by index.""" return self._list[i] - def __iter__(self) -> Iterator["warnings.WarningMessage"]: + def __iter__(self) -> Iterator[warnings.WarningMessage]: """Iterate through the recorded warnings.""" return iter(self._list) @@ -206,24 +208,24 @@ class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg] """The number of recorded warnings.""" return len(self._list) - def pop(self, cls: Type[Warning] = Warning) -> "warnings.WarningMessage": + def pop(self, cls: type[Warning] = Warning) -> warnings.WarningMessage: """Pop the first recorded warning which is an instance of ``cls``, but not an instance of a child class of any other match. Raises ``AssertionError`` if there is no match. """ - best_idx: Optional[int] = None + best_idx: int | None = None for i, w in enumerate(self._list): if w.category == cls: return self._list.pop(i) # exact match, stop looking if issubclass(w.category, cls) and ( - best_idx is None - or not issubclass(w.category, self._list[best_idx].category) + best_idx is None or + not issubclass(w.category, self._list[best_idx].category) ): best_idx = i if best_idx is not None: return self._list.pop(best_idx) __tracebackhide__ = True - raise AssertionError(f"{cls!r} not found in warning list") + raise AssertionError(f'{cls!r} not found in warning list') def clear(self) -> None: """Clear the list of recorded warnings.""" @@ -231,26 +233,26 @@ class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg] # Type ignored because it doesn't exactly warnings.catch_warnings.__enter__ # -- it returns a List but we only emulate one. - def __enter__(self) -> "WarningsRecorder": # type: ignore + def __enter__(self) -> WarningsRecorder: # type: ignore if self._entered: __tracebackhide__ = True - raise RuntimeError(f"Cannot enter {self!r} twice") + raise RuntimeError(f'Cannot enter {self!r} twice') _list = super().__enter__() # record=True means it's None. assert _list is not None self._list = _list - warnings.simplefilter("always") + warnings.simplefilter('always') return self def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: if not self._entered: __tracebackhide__ = True - raise RuntimeError(f"Cannot exit {self!r} without entering first") + raise RuntimeError(f'Cannot exit {self!r} without entering first') super().__exit__(exc_type, exc_val, exc_tb) @@ -263,22 +265,22 @@ class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg] class WarningsChecker(WarningsRecorder): def __init__( self, - expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = Warning, - match_expr: Optional[Union[str, Pattern[str]]] = None, + expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, + match_expr: str | Pattern[str] | None = None, *, _ispytest: bool = False, ) -> None: check_ispytest(_ispytest) super().__init__(_ispytest=True) - msg = "exceptions must be derived from Warning, not %s" + msg = 'exceptions must be derived from Warning, not %s' if isinstance(expected_warning, tuple): for exc in expected_warning: if not issubclass(exc, Warning): raise TypeError(msg % type(exc)) expected_warning_tup = expected_warning elif isinstance(expected_warning, type) and issubclass( - expected_warning, Warning + expected_warning, Warning, ): expected_warning_tup = (expected_warning,) else: @@ -290,14 +292,14 @@ class WarningsChecker(WarningsRecorder): def matches(self, warning: warnings.WarningMessage) -> bool: assert self.expected_warning is not None return issubclass(warning.category, self.expected_warning) and bool( - self.match_expr is None or re.search(self.match_expr, str(warning.message)) + self.match_expr is None or re.search(self.match_expr, str(warning.message)), ) def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: super().__exit__(exc_type, exc_val, exc_tb) @@ -308,9 +310,9 @@ class WarningsChecker(WarningsRecorder): # when the warning doesn't happen. Control-flow exceptions should always # propagate. if exc_val is not None and ( - not isinstance(exc_val, Exception) + not isinstance(exc_val, Exception) or # Exit is an Exception, not a BaseException, for some reason. - or isinstance(exc_val, Exit) + isinstance(exc_val, Exit) ): return @@ -320,14 +322,14 @@ class WarningsChecker(WarningsRecorder): try: if not any(issubclass(w.category, self.expected_warning) for w in self): fail( - f"DID NOT WARN. No warnings of type {self.expected_warning} were emitted.\n" - f" Emitted warnings: {found_str()}." + f'DID NOT WARN. No warnings of type {self.expected_warning} were emitted.\n' + f' Emitted warnings: {found_str()}.', ) elif not any(self.matches(w) for w in self): fail( - f"DID NOT WARN. No warnings of type {self.expected_warning} matching the regex were emitted.\n" - f" Regex: {self.match_expr}\n" - f" Emitted warnings: {found_str()}." + f'DID NOT WARN. No warnings of type {self.expected_warning} matching the regex were emitted.\n' + f' Regex: {self.match_expr}\n' + f' Emitted warnings: {found_str()}.', ) finally: # Whether or not any warnings matched, we want to re-emit all unmatched warnings. @@ -366,5 +368,5 @@ class WarningsChecker(WarningsRecorder): # its first argument was not a string. But that case can't be # distinguished from an invalid type. raise TypeError( - f"Warning must be str or Warning, got {msg!r} (type {type(msg).__name__})" + f'Warning must be str or Warning, got {msg!r} (type {type(msg).__name__})', ) diff --git a/.venv/lib/python3.10/site-packages/_pytest/reports.py b/.venv/lib/python3.10/site-packages/_pytest/reports.py index 7cdb70e..ac72743 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/reports.py +++ b/.venv/lib/python3.10/site-packages/_pytest/reports.py @@ -1,7 +1,9 @@ # mypy: allow-untyped-defs +from __future__ import annotations + import dataclasses -from io import StringIO import os +from io import StringIO from pprint import pprint from typing import Any from typing import cast @@ -47,25 +49,25 @@ def getworkerinfoline(node): return node._workerinfocache except AttributeError: d = node.workerinfo - ver = "{}.{}.{}".format(*d["version_info"][:3]) - node._workerinfocache = s = "[{}] {} -- Python {} {}".format( - d["id"], d["sysplatform"], ver, d["executable"] + ver = '{}.{}.{}'.format(*d['version_info'][:3]) + node._workerinfocache = s = '[{}] {} -- Python {} {}'.format( + d['id'], d['sysplatform'], ver, d['executable'], ) return s -_R = TypeVar("_R", bound="BaseReport") +_R = TypeVar('_R', bound='BaseReport') class BaseReport: - when: Optional[str] - location: Optional[Tuple[str, Optional[int], str]] - longrepr: Union[ - None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr - ] - sections: List[Tuple[str, str]] + when: str | None + location: tuple[str, int | None, str] | None + longrepr: ( + None | ExceptionInfo[BaseException] | tuple[str, int, str] | str | TerminalRepr + ) + sections: list[tuple[str, str]] nodeid: str - outcome: Literal["passed", "failed", "skipped"] + outcome: Literal['passed', 'failed', 'skipped'] def __init__(self, **kw: Any) -> None: self.__dict__.update(kw) @@ -76,7 +78,7 @@ class BaseReport: ... def toterminal(self, out: TerminalWriter) -> None: - if hasattr(self, "node"): + if hasattr(self, 'node'): worker_info = getworkerinfoline(self.node) if worker_info: out.line(worker_info) @@ -85,17 +87,17 @@ class BaseReport: if longrepr is None: return - if hasattr(longrepr, "toterminal"): + if hasattr(longrepr, 'toterminal'): longrepr_terminal = cast(TerminalRepr, longrepr) longrepr_terminal.toterminal(out) else: try: s = str(longrepr) except UnicodeEncodeError: - s = "" + s = '' out.line(s) - def get_sections(self, prefix: str) -> Iterator[Tuple[str, str]]: + def get_sections(self, prefix: str) -> Iterator[tuple[str, str]]: for name, content in self.sections: if name.startswith(prefix): yield prefix, content @@ -120,8 +122,8 @@ class BaseReport: .. versionadded:: 3.5 """ - return "\n".join( - content for (prefix, content) in self.get_sections("Captured log") + return '\n'.join( + content for (prefix, content) in self.get_sections('Captured log') ) @property @@ -130,8 +132,8 @@ class BaseReport: .. versionadded:: 3.0 """ - return "".join( - content for (prefix, content) in self.get_sections("Captured stdout") + return ''.join( + content for (prefix, content) in self.get_sections('Captured stdout') ) @property @@ -140,29 +142,29 @@ class BaseReport: .. versionadded:: 3.0 """ - return "".join( - content for (prefix, content) in self.get_sections("Captured stderr") + return ''.join( + content for (prefix, content) in self.get_sections('Captured stderr') ) @property def passed(self) -> bool: """Whether the outcome is passed.""" - return self.outcome == "passed" + return self.outcome == 'passed' @property def failed(self) -> bool: """Whether the outcome is failed.""" - return self.outcome == "failed" + return self.outcome == 'failed' @property def skipped(self) -> bool: """Whether the outcome is skipped.""" - return self.outcome == "skipped" + return self.outcome == 'skipped' @property def fspath(self) -> str: """The path portion of the reported node, as a string.""" - return self.nodeid.split("::")[0] + return self.nodeid.split('::')[0] @property def count_towards_summary(self) -> bool: @@ -177,7 +179,7 @@ class BaseReport: return True @property - def head_line(self) -> Optional[str]: + def head_line(self) -> str | None: """**Experimental** The head line shown with longrepr output for this report, more commonly during traceback representation during failures:: @@ -199,11 +201,11 @@ class BaseReport: def _get_verbose_word(self, config: Config): _category, _short, verbose = config.hook.pytest_report_teststatus( - report=self, config=config + report=self, config=config, ) return verbose - def _to_json(self) -> Dict[str, Any]: + def _to_json(self) -> dict[str, Any]: """Return the contents of this report as a dict of builtin entries, suitable for serialization. @@ -214,7 +216,7 @@ class BaseReport: return _report_to_json(self) @classmethod - def _from_json(cls: Type[_R], reportdict: Dict[str, object]) -> _R: + def _from_json(cls: type[_R], reportdict: dict[str, object]) -> _R: """Create either a TestReport or CollectReport, depending on the calling class. It is the callers responsibility to know which class to pass here. @@ -228,16 +230,16 @@ class BaseReport: def _report_unserialization_failure( - type_name: str, report_class: Type[BaseReport], reportdict + type_name: str, report_class: type[BaseReport], reportdict, ) -> NoReturn: - url = "https://github.com/pytest-dev/pytest/issues" + url = 'https://github.com/pytest-dev/pytest/issues' stream = StringIO() - pprint("-" * 100, stream=stream) - pprint("INTERNALERROR: Unknown entry type returned: %s" % type_name, stream=stream) - pprint("report_name: %s" % report_class, stream=stream) + pprint('-' * 100, stream=stream) + pprint('INTERNALERROR: Unknown entry type returned: %s' % type_name, stream=stream) + pprint('report_name: %s' % report_class, stream=stream) pprint(reportdict, stream=stream) - pprint("Please report this bug at %s" % url, stream=stream) - pprint("-" * 100, stream=stream) + pprint('Please report this bug at %s' % url, stream=stream) + pprint('-' * 100, stream=stream) raise RuntimeError(stream.getvalue()) @@ -257,18 +259,18 @@ class TestReport(BaseReport): def __init__( self, nodeid: str, - location: Tuple[str, Optional[int], str], + location: tuple[str, int | None, str], keywords: Mapping[str, Any], - outcome: Literal["passed", "failed", "skipped"], - longrepr: Union[ - None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr - ], - when: Literal["setup", "call", "teardown"], - sections: Iterable[Tuple[str, str]] = (), + outcome: Literal['passed', 'failed', 'skipped'], + longrepr: ( + None | ExceptionInfo[BaseException] | tuple[str, int, str] | str | TerminalRepr + ), + when: Literal['setup', 'call', 'teardown'], + sections: Iterable[tuple[str, str]] = (), duration: float = 0, start: float = 0, stop: float = 0, - user_properties: Optional[Iterable[Tuple[str, object]]] = None, + user_properties: Iterable[tuple[str, object]] | None = None, **extra, ) -> None: #: Normalized collection nodeid. @@ -279,7 +281,7 @@ class TestReport(BaseReport): #: collected one e.g. if a method is inherited from a different module. #: The filesystempath may be relative to ``config.rootdir``. #: The line number is 0-based. - self.location: Tuple[str, Optional[int], str] = location + self.location: tuple[str, int | None, str] = location #: A name -> value dictionary containing all keywords and #: markers associated with a test invocation. @@ -315,10 +317,10 @@ class TestReport(BaseReport): self.__dict__.update(extra) def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.nodeid!r} when={self.when!r} outcome={self.outcome!r}>" + return f'<{self.__class__.__name__} {self.nodeid!r} when={self.when!r} outcome={self.outcome!r}>' @classmethod - def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport": + def from_item_and_call(cls, item: Item, call: CallInfo[None]) -> TestReport: """Create and fill a TestReport with standard item and call info. :param item: The item. @@ -326,7 +328,7 @@ class TestReport(BaseReport): """ when = call.when # Remove "collect" from the Literal type -- only for collection calls. - assert when != "collect" + assert when != 'collect' duration = call.duration start = call.start stop = call.stop @@ -334,24 +336,24 @@ class TestReport(BaseReport): excinfo = call.excinfo sections = [] if not call.excinfo: - outcome: Literal["passed", "failed", "skipped"] = "passed" - longrepr: Union[ - None, - ExceptionInfo[BaseException], - Tuple[str, int, str], - str, - TerminalRepr, - ] = None + outcome: Literal['passed', 'failed', 'skipped'] = 'passed' + longrepr: ( + None | + ExceptionInfo[BaseException] | + tuple[str, int, str] | + str | + TerminalRepr + ) = None else: if not isinstance(excinfo, ExceptionInfo): - outcome = "failed" + outcome = 'failed' longrepr = excinfo elif isinstance(excinfo.value, skip.Exception): - outcome = "skipped" + outcome = 'skipped' r = excinfo._getreprcrash() assert ( r is not None - ), "There should always be a traceback entry for skipping a test." + ), 'There should always be a traceback entry for skipping a test.' if excinfo.value._use_item_location: path, line = item.reportinfo()[:2] assert line is not None @@ -359,15 +361,15 @@ class TestReport(BaseReport): else: longrepr = (str(r.path), r.lineno, r.message) else: - outcome = "failed" - if call.when == "call": + outcome = 'failed' + if call.when == 'call': longrepr = item.repr_failure(excinfo) else: # exception in setup or teardown longrepr = item._repr_failure_py( - excinfo, style=item.config.getoption("tbstyle", "auto") + excinfo, style=item.config.getoption('tbstyle', 'auto'), ) for rwhen, key, content in item._report_sections: - sections.append((f"Captured {key} {rwhen}", content)) + sections.append((f'Captured {key} {rwhen}', content)) return cls( item.nodeid, item.location, @@ -390,17 +392,17 @@ class CollectReport(BaseReport): Reports can contain arbitrary extra attributes. """ - when = "collect" + when = 'collect' def __init__( self, nodeid: str, - outcome: "Literal['passed', 'failed', 'skipped']", - longrepr: Union[ - None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr - ], - result: Optional[List[Union[Item, Collector]]], - sections: Iterable[Tuple[str, str]] = (), + outcome: Literal['passed', 'failed', 'skipped'], + longrepr: ( + None | ExceptionInfo[BaseException] | tuple[str, int, str] | str | TerminalRepr + ), + result: list[Item | Collector] | None, + sections: Iterable[tuple[str, str]] = (), **extra, ) -> None: #: Normalized collection nodeid. @@ -426,11 +428,11 @@ class CollectReport(BaseReport): @property def location( # type:ignore[override] self, - ) -> Optional[Tuple[str, Optional[int], str]]: + ) -> tuple[str, int | None, str] | None: return (self.fspath, None, self.fspath) def __repr__(self) -> str: - return f"" + return f'' class CollectErrorRepr(TerminalRepr): @@ -442,31 +444,31 @@ class CollectErrorRepr(TerminalRepr): def pytest_report_to_serializable( - report: Union[CollectReport, TestReport], -) -> Optional[Dict[str, Any]]: + report: CollectReport | TestReport, +) -> dict[str, Any] | None: if isinstance(report, (TestReport, CollectReport)): data = report._to_json() - data["$report_type"] = report.__class__.__name__ + data['$report_type'] = report.__class__.__name__ return data # TODO: Check if this is actually reachable. return None # type: ignore[unreachable] def pytest_report_from_serializable( - data: Dict[str, Any], -) -> Optional[Union[CollectReport, TestReport]]: - if "$report_type" in data: - if data["$report_type"] == "TestReport": + data: dict[str, Any], +) -> CollectReport | TestReport | None: + if '$report_type' in data: + if data['$report_type'] == 'TestReport': return TestReport._from_json(data) - elif data["$report_type"] == "CollectReport": + elif data['$report_type'] == 'CollectReport': return CollectReport._from_json(data) - assert False, "Unknown report_type unserialize data: {}".format( - data["$report_type"] + assert False, 'Unknown report_type unserialize data: {}'.format( + data['$report_type'], ) return None -def _report_to_json(report: BaseReport) -> Dict[str, Any]: +def _report_to_json(report: BaseReport) -> dict[str, Any]: """Return the contents of this report as a dict of builtin entries, suitable for serialization. @@ -474,72 +476,72 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]: """ def serialize_repr_entry( - entry: Union[ReprEntry, ReprEntryNative], - ) -> Dict[str, Any]: + entry: ReprEntry | ReprEntryNative, + ) -> dict[str, Any]: data = dataclasses.asdict(entry) for key, value in data.items(): - if hasattr(value, "__dict__"): + if hasattr(value, '__dict__'): data[key] = dataclasses.asdict(value) - entry_data = {"type": type(entry).__name__, "data": data} + entry_data = {'type': type(entry).__name__, 'data': data} return entry_data - def serialize_repr_traceback(reprtraceback: ReprTraceback) -> Dict[str, Any]: + def serialize_repr_traceback(reprtraceback: ReprTraceback) -> dict[str, Any]: result = dataclasses.asdict(reprtraceback) - result["reprentries"] = [ + result['reprentries'] = [ serialize_repr_entry(x) for x in reprtraceback.reprentries ] return result def serialize_repr_crash( - reprcrash: Optional[ReprFileLocation], - ) -> Optional[Dict[str, Any]]: + reprcrash: ReprFileLocation | None, + ) -> dict[str, Any] | None: if reprcrash is not None: return dataclasses.asdict(reprcrash) else: return None - def serialize_exception_longrepr(rep: BaseReport) -> Dict[str, Any]: + def serialize_exception_longrepr(rep: BaseReport) -> dict[str, Any]: assert rep.longrepr is not None # TODO: Investigate whether the duck typing is really necessary here. longrepr = cast(ExceptionRepr, rep.longrepr) - result: Dict[str, Any] = { - "reprcrash": serialize_repr_crash(longrepr.reprcrash), - "reprtraceback": serialize_repr_traceback(longrepr.reprtraceback), - "sections": longrepr.sections, + result: dict[str, Any] = { + 'reprcrash': serialize_repr_crash(longrepr.reprcrash), + 'reprtraceback': serialize_repr_traceback(longrepr.reprtraceback), + 'sections': longrepr.sections, } if isinstance(longrepr, ExceptionChainRepr): - result["chain"] = [] + result['chain'] = [] for repr_traceback, repr_crash, description in longrepr.chain: - result["chain"].append( + result['chain'].append( ( serialize_repr_traceback(repr_traceback), serialize_repr_crash(repr_crash), description, - ) + ), ) else: - result["chain"] = None + result['chain'] = None return result d = report.__dict__.copy() - if hasattr(report.longrepr, "toterminal"): - if hasattr(report.longrepr, "reprtraceback") and hasattr( - report.longrepr, "reprcrash" + if hasattr(report.longrepr, 'toterminal'): + if hasattr(report.longrepr, 'reprtraceback') and hasattr( + report.longrepr, 'reprcrash', ): - d["longrepr"] = serialize_exception_longrepr(report) + d['longrepr'] = serialize_exception_longrepr(report) else: - d["longrepr"] = str(report.longrepr) + d['longrepr'] = str(report.longrepr) else: - d["longrepr"] = report.longrepr + d['longrepr'] = report.longrepr for name in d: if isinstance(d[name], os.PathLike): d[name] = os.fspath(d[name]) - elif name == "result": + elif name == 'result': d[name] = None # for now return d -def _report_kwargs_from_json(reportdict: Dict[str, Any]) -> Dict[str, Any]: +def _report_kwargs_from_json(reportdict: dict[str, Any]) -> dict[str, Any]: """Return **kwargs that can be used to construct a TestReport or CollectReport instance. @@ -547,76 +549,76 @@ def _report_kwargs_from_json(reportdict: Dict[str, Any]) -> Dict[str, Any]: """ def deserialize_repr_entry(entry_data): - data = entry_data["data"] - entry_type = entry_data["type"] - if entry_type == "ReprEntry": + data = entry_data['data'] + entry_type = entry_data['type'] + if entry_type == 'ReprEntry': reprfuncargs = None reprfileloc = None reprlocals = None - if data["reprfuncargs"]: - reprfuncargs = ReprFuncArgs(**data["reprfuncargs"]) - if data["reprfileloc"]: - reprfileloc = ReprFileLocation(**data["reprfileloc"]) - if data["reprlocals"]: - reprlocals = ReprLocals(data["reprlocals"]["lines"]) + if data['reprfuncargs']: + reprfuncargs = ReprFuncArgs(**data['reprfuncargs']) + if data['reprfileloc']: + reprfileloc = ReprFileLocation(**data['reprfileloc']) + if data['reprlocals']: + reprlocals = ReprLocals(data['reprlocals']['lines']) - reprentry: Union[ReprEntry, ReprEntryNative] = ReprEntry( - lines=data["lines"], + reprentry: ReprEntry | ReprEntryNative = ReprEntry( + lines=data['lines'], reprfuncargs=reprfuncargs, reprlocals=reprlocals, reprfileloc=reprfileloc, - style=data["style"], + style=data['style'], ) - elif entry_type == "ReprEntryNative": - reprentry = ReprEntryNative(data["lines"]) + elif entry_type == 'ReprEntryNative': + reprentry = ReprEntryNative(data['lines']) else: _report_unserialization_failure(entry_type, TestReport, reportdict) return reprentry def deserialize_repr_traceback(repr_traceback_dict): - repr_traceback_dict["reprentries"] = [ - deserialize_repr_entry(x) for x in repr_traceback_dict["reprentries"] + repr_traceback_dict['reprentries'] = [ + deserialize_repr_entry(x) for x in repr_traceback_dict['reprentries'] ] return ReprTraceback(**repr_traceback_dict) - def deserialize_repr_crash(repr_crash_dict: Optional[Dict[str, Any]]): + def deserialize_repr_crash(repr_crash_dict: dict[str, Any] | None): if repr_crash_dict is not None: return ReprFileLocation(**repr_crash_dict) else: return None if ( - reportdict["longrepr"] - and "reprcrash" in reportdict["longrepr"] - and "reprtraceback" in reportdict["longrepr"] + reportdict['longrepr'] and + 'reprcrash' in reportdict['longrepr'] and + 'reprtraceback' in reportdict['longrepr'] ): reprtraceback = deserialize_repr_traceback( - reportdict["longrepr"]["reprtraceback"] + reportdict['longrepr']['reprtraceback'], ) - reprcrash = deserialize_repr_crash(reportdict["longrepr"]["reprcrash"]) - if reportdict["longrepr"]["chain"]: + reprcrash = deserialize_repr_crash(reportdict['longrepr']['reprcrash']) + if reportdict['longrepr']['chain']: chain = [] for repr_traceback_data, repr_crash_data, description in reportdict[ - "longrepr" - ]["chain"]: + 'longrepr' + ]['chain']: chain.append( ( deserialize_repr_traceback(repr_traceback_data), deserialize_repr_crash(repr_crash_data), description, - ) + ), ) - exception_info: Union[ - ExceptionChainRepr, ReprExceptionInfo - ] = ExceptionChainRepr(chain) + exception_info: ( + ExceptionChainRepr | ReprExceptionInfo + ) = ExceptionChainRepr(chain) else: exception_info = ReprExceptionInfo( reprtraceback=reprtraceback, reprcrash=reprcrash, ) - for section in reportdict["longrepr"]["sections"]: + for section in reportdict['longrepr']['sections']: exception_info.addsection(*section) - reportdict["longrepr"] = exception_info + reportdict['longrepr'] = exception_info return reportdict diff --git a/.venv/lib/python3.10/site-packages/_pytest/runner.py b/.venv/lib/python3.10/site-packages/_pytest/runner.py index 16abb89..344bf0f 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/runner.py +++ b/.venv/lib/python3.10/site-packages/_pytest/runner.py @@ -1,5 +1,7 @@ # mypy: allow-untyped-defs """Basic collect and runtest protocol implementations.""" +from __future__ import annotations + import bdb import dataclasses import os @@ -18,10 +20,6 @@ from typing import TYPE_CHECKING from typing import TypeVar from typing import Union -from .reports import BaseReport -from .reports import CollectErrorRepr -from .reports import CollectReport -from .reports import TestReport from _pytest import timing from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionInfo @@ -37,6 +35,11 @@ from _pytest.outcomes import OutcomeException from _pytest.outcomes import Skipped from _pytest.outcomes import TEST_OUTCOME +from .reports import BaseReport +from .reports import CollectErrorRepr +from .reports import CollectReport +from .reports import TestReport + if sys.version_info[:2] < (3, 11): from exceptiongroup import BaseExceptionGroup @@ -50,66 +53,66 @@ if TYPE_CHECKING: def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("terminal reporting", "Reporting", after="general") + group = parser.getgroup('terminal reporting', 'Reporting', after='general') group.addoption( - "--durations", - action="store", + '--durations', + action='store', type=int, default=None, - metavar="N", - help="Show N slowest setup/test durations (N=0 for all)", + metavar='N', + help='Show N slowest setup/test durations (N=0 for all)', ) group.addoption( - "--durations-min", - action="store", + '--durations-min', + action='store', type=float, default=0.005, - metavar="N", - help="Minimal duration in seconds for inclusion in slowest list. " - "Default: 0.005.", + metavar='N', + help='Minimal duration in seconds for inclusion in slowest list. ' + 'Default: 0.005.', ) -def pytest_terminal_summary(terminalreporter: "TerminalReporter") -> None: +def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: durations = terminalreporter.config.option.durations durations_min = terminalreporter.config.option.durations_min - verbose = terminalreporter.config.getvalue("verbose") + verbose = terminalreporter.config.getvalue('verbose') if durations is None: return tr = terminalreporter dlist = [] for replist in tr.stats.values(): for rep in replist: - if hasattr(rep, "duration"): + if hasattr(rep, 'duration'): dlist.append(rep) if not dlist: return dlist.sort(key=lambda x: x.duration, reverse=True) # type: ignore[no-any-return] if not durations: - tr.write_sep("=", "slowest durations") + tr.write_sep('=', 'slowest durations') else: - tr.write_sep("=", "slowest %s durations" % durations) + tr.write_sep('=', 'slowest %s durations' % durations) dlist = dlist[:durations] for i, rep in enumerate(dlist): if verbose < 2 and rep.duration < durations_min: - tr.write_line("") + tr.write_line('') tr.write_line( - f"({len(dlist) - i} durations < {durations_min:g}s hidden. Use -vv to show these durations.)" + f'({len(dlist) - i} durations < {durations_min:g}s hidden. Use -vv to show these durations.)', ) break - tr.write_line(f"{rep.duration:02.2f}s {rep.when:<8} {rep.nodeid}") + tr.write_line(f'{rep.duration:02.2f}s {rep.when:<8} {rep.nodeid}') -def pytest_sessionstart(session: "Session") -> None: +def pytest_sessionstart(session: Session) -> None: session._setupstate = SetupState() -def pytest_sessionfinish(session: "Session") -> None: +def pytest_sessionfinish(session: Session) -> None: session._setupstate.teardown_exact(None) -def pytest_runtest_protocol(item: Item, nextitem: Optional[Item]) -> bool: +def pytest_runtest_protocol(item: Item, nextitem: Item | None) -> bool: ihook = item.ihook ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location) runtestprotocol(item, nextitem=nextitem) @@ -118,21 +121,21 @@ def pytest_runtest_protocol(item: Item, nextitem: Optional[Item]) -> bool: def runtestprotocol( - item: Item, log: bool = True, nextitem: Optional[Item] = None -) -> List[TestReport]: - hasrequest = hasattr(item, "_request") + item: Item, log: bool = True, nextitem: Item | None = None, +) -> list[TestReport]: + hasrequest = hasattr(item, '_request') if hasrequest and not item._request: # type: ignore[attr-defined] # This only happens if the item is re-run, as is done by # pytest-rerunfailures. item._initrequest() # type: ignore[attr-defined] - rep = call_and_report(item, "setup", log) + rep = call_and_report(item, 'setup', log) reports = [rep] if rep.passed: - if item.config.getoption("setupshow", False): + if item.config.getoption('setupshow', False): show_test_item(item) - if not item.config.getoption("setuponly", False): - reports.append(call_and_report(item, "call", log)) - reports.append(call_and_report(item, "teardown", log, nextitem=nextitem)) + if not item.config.getoption('setuponly', False): + reports.append(call_and_report(item, 'call', log)) + reports.append(call_and_report(item, 'teardown', log, nextitem=nextitem)) # After all teardown hooks have been called # want funcargs and request info to go away. if hasrequest: @@ -145,21 +148,21 @@ def show_test_item(item: Item) -> None: """Show test function, parameters and the fixtures of the test item.""" tw = item.config.get_terminal_writer() tw.line() - tw.write(" " * 8) + tw.write(' ' * 8) tw.write(item.nodeid) - used_fixtures = sorted(getattr(item, "fixturenames", [])) + used_fixtures = sorted(getattr(item, 'fixturenames', [])) if used_fixtures: - tw.write(" (fixtures used: {})".format(", ".join(used_fixtures))) + tw.write(' (fixtures used: {})'.format(', '.join(used_fixtures))) tw.flush() def pytest_runtest_setup(item: Item) -> None: - _update_current_test_var(item, "setup") + _update_current_test_var(item, 'setup') item.session._setupstate.setup(item) def pytest_runtest_call(item: Item) -> None: - _update_current_test_var(item, "call") + _update_current_test_var(item, 'call') try: del sys.last_type del sys.last_value @@ -182,38 +185,38 @@ def pytest_runtest_call(item: Item) -> None: raise e -def pytest_runtest_teardown(item: Item, nextitem: Optional[Item]) -> None: - _update_current_test_var(item, "teardown") +def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: + _update_current_test_var(item, 'teardown') item.session._setupstate.teardown_exact(nextitem) _update_current_test_var(item, None) def _update_current_test_var( - item: Item, when: Optional[Literal["setup", "call", "teardown"]] + item: Item, when: Literal['setup', 'call', 'teardown'] | None, ) -> None: """Update :envvar:`PYTEST_CURRENT_TEST` to reflect the current item and stage. If ``when`` is None, delete ``PYTEST_CURRENT_TEST`` from the environment. """ - var_name = "PYTEST_CURRENT_TEST" + var_name = 'PYTEST_CURRENT_TEST' if when: - value = f"{item.nodeid} ({when})" + value = f'{item.nodeid} ({when})' # don't allow null bytes on environment variables (see #2644, #2957) - value = value.replace("\x00", "(null)") + value = value.replace('\x00', '(null)') os.environ[var_name] = value else: os.environ.pop(var_name) -def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]: - if report.when in ("setup", "teardown"): +def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str] | None: + if report.when in ('setup', 'teardown'): if report.failed: # category, shortletter, verbose-word - return "error", "E", "ERROR" + return 'error', 'E', 'ERROR' elif report.skipped: - return "skipped", "s", "SKIPPED" + return 'skipped', 's', 'SKIPPED' else: - return "", "", "" + return '', '', '' return None @@ -222,22 +225,22 @@ def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str def call_and_report( - item: Item, when: Literal["setup", "call", "teardown"], log: bool = True, **kwds + item: Item, when: Literal['setup', 'call', 'teardown'], log: bool = True, **kwds, ) -> TestReport: ihook = item.ihook - if when == "setup": + if when == 'setup': runtest_hook: Callable[..., None] = ihook.pytest_runtest_setup - elif when == "call": + elif when == 'call': runtest_hook = ihook.pytest_runtest_call - elif when == "teardown": + elif when == 'teardown': runtest_hook = ihook.pytest_runtest_teardown else: - assert False, f"Unhandled runtest hook case: {when}" - reraise: Tuple[Type[BaseException], ...] = (Exit,) - if not item.config.getoption("usepdb", False): + assert False, f'Unhandled runtest hook case: {when}' + reraise: tuple[type[BaseException], ...] = (Exit,) + if not item.config.getoption('usepdb', False): reraise += (KeyboardInterrupt,) call = CallInfo.from_call( - lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise + lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise, ) report: TestReport = ihook.pytest_runtest_makereport(item=item, call=call) if log: @@ -247,13 +250,13 @@ def call_and_report( return report -def check_interactive_exception(call: "CallInfo[object]", report: BaseReport) -> bool: +def check_interactive_exception(call: CallInfo[object], report: BaseReport) -> bool: """Check whether the call raised an exception that should be reported as interactive.""" if call.excinfo is None: # Didn't raise. return False - if hasattr(report, "wasxfail"): + if hasattr(report, 'wasxfail'): # Exception was expected. return False if isinstance(call.excinfo.value, (Skipped, bdb.BdbQuit)): @@ -262,7 +265,7 @@ def check_interactive_exception(call: "CallInfo[object]", report: BaseReport) -> return True -TResult = TypeVar("TResult", covariant=True) +TResult = TypeVar('TResult', covariant=True) @final @@ -270,9 +273,9 @@ TResult = TypeVar("TResult", covariant=True) class CallInfo(Generic[TResult]): """Result/Exception info of a function invocation.""" - _result: Optional[TResult] + _result: TResult | None #: The captured exception of the call, if it raised. - excinfo: Optional[ExceptionInfo[BaseException]] + excinfo: ExceptionInfo[BaseException] | None #: The system time when the call started, in seconds since the epoch. start: float #: The system time when the call ended, in seconds since the epoch. @@ -280,16 +283,16 @@ class CallInfo(Generic[TResult]): #: The call duration, in seconds. duration: float #: The context of invocation: "collect", "setup", "call" or "teardown". - when: Literal["collect", "setup", "call", "teardown"] + when: Literal['collect', 'setup', 'call', 'teardown'] def __init__( self, - result: Optional[TResult], - excinfo: Optional[ExceptionInfo[BaseException]], + result: TResult | None, + excinfo: ExceptionInfo[BaseException] | None, start: float, stop: float, duration: float, - when: Literal["collect", "setup", "call", "teardown"], + when: Literal['collect', 'setup', 'call', 'teardown'], *, _ispytest: bool = False, ) -> None: @@ -308,7 +311,7 @@ class CallInfo(Generic[TResult]): Can only be accessed if excinfo is None. """ if self.excinfo is not None: - raise AttributeError(f"{self!r} has no valid result") + raise AttributeError(f'{self!r} has no valid result') # The cast is safe because an exception wasn't raised, hence # _result has the expected function return type (which may be # None, that's why a cast and not an assert). @@ -318,11 +321,11 @@ class CallInfo(Generic[TResult]): def from_call( cls, func: Callable[[], TResult], - when: Literal["collect", "setup", "call", "teardown"], - reraise: Optional[ - Union[Type[BaseException], Tuple[Type[BaseException], ...]] - ] = None, - ) -> "CallInfo[TResult]": + when: Literal['collect', 'setup', 'call', 'teardown'], + reraise: None | ( + type[BaseException] | tuple[type[BaseException], ...] + ) = None, + ) -> CallInfo[TResult]: """Call func, wrapping the result in a CallInfo. :param func: @@ -337,7 +340,7 @@ class CallInfo(Generic[TResult]): start = timing.time() precise_start = timing.perf_counter() try: - result: Optional[TResult] = func() + result: TResult | None = func() except BaseException: excinfo = ExceptionInfo.from_current() if reraise is not None and isinstance(excinfo.value, reraise): @@ -359,8 +362,8 @@ class CallInfo(Generic[TResult]): def __repr__(self) -> str: if self.excinfo is None: - return f"" - return f"" + return f'' + return f'' def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport: @@ -368,7 +371,7 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport: def pytest_make_collect_report(collector: Collector) -> CollectReport: - def collect() -> List[Union[Item, Collector]]: + def collect() -> list[Item | Collector]: # Before collecting, if this is a Directory, load the conftests. # If a conftest import fails to load, it is considered a collection # error of the Directory collector. This is why it's done inside of the @@ -378,36 +381,36 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport: if isinstance(collector, Directory): collector.config.pluginmanager._loadconftestmodules( collector.path, - collector.config.getoption("importmode"), + collector.config.getoption('importmode'), rootpath=collector.config.rootpath, consider_namespace_packages=collector.config.getini( - "consider_namespace_packages" + 'consider_namespace_packages', ), ) return list(collector.collect()) - call = CallInfo.from_call(collect, "collect") - longrepr: Union[None, Tuple[str, int, str], str, TerminalRepr] = None + call = CallInfo.from_call(collect, 'collect') + longrepr: None | tuple[str, int, str] | str | TerminalRepr = None if not call.excinfo: - outcome: Literal["passed", "skipped", "failed"] = "passed" + outcome: Literal['passed', 'skipped', 'failed'] = 'passed' else: skip_exceptions = [Skipped] - unittest = sys.modules.get("unittest") + unittest = sys.modules.get('unittest') if unittest is not None: # Type ignored because unittest is loaded dynamically. skip_exceptions.append(unittest.SkipTest) # type: ignore if isinstance(call.excinfo.value, tuple(skip_exceptions)): - outcome = "skipped" - r_ = collector._repr_failure_py(call.excinfo, "line") + outcome = 'skipped' + r_ = collector._repr_failure_py(call.excinfo, 'line') assert isinstance(r_, ExceptionChainRepr), repr(r_) r = r_.reprcrash assert r longrepr = (str(r.path), r.lineno, r.message) else: - outcome = "failed" + outcome = 'failed' errorinfo = collector.repr_failure(call.excinfo) - if not hasattr(errorinfo, "toterminal"): + if not hasattr(errorinfo, 'toterminal'): assert isinstance(errorinfo, str) errorinfo = CollectErrorRepr(errorinfo) longrepr = errorinfo @@ -483,13 +486,13 @@ class SetupState: def __init__(self) -> None: # The stack is in the dict insertion order. - self.stack: Dict[ + self.stack: dict[ Node, - Tuple[ + tuple[ # Node's finalizers. - List[Callable[[], object]], + list[Callable[[], object]], # Node's exception, if its setup raised. - Optional[Union[OutcomeException, Exception]], + OutcomeException | Exception | None, ], ] = {} @@ -500,11 +503,11 @@ class SetupState: # If a collector fails its setup, fail its entire subtree of items. # The setup is not retried for each item - the same exception is used. for col, (finalizers, exc) in self.stack.items(): - assert col in needed_collectors, "previous item was not torn down properly" + assert col in needed_collectors, 'previous item was not torn down properly' if exc: raise exc - for col in needed_collectors[len(self.stack) :]: + for col in needed_collectors[len(self.stack):]: assert col not in self.stack # Push onto the stack. self.stack[col] = ([col.teardown], None) @@ -524,7 +527,7 @@ class SetupState: assert node in self.stack, (node, self.stack) self.stack[node][0].append(finalizer) - def teardown_exact(self, nextitem: Optional[Item]) -> None: + def teardown_exact(self, nextitem: Item | None) -> None: """Teardown the current stack up until reaching nodes that nextitem also descends from. @@ -532,7 +535,7 @@ class SetupState: stack is torn down. """ needed_collectors = nextitem and nextitem.listchain() or [] - exceptions: List[BaseException] = [] + exceptions: list[BaseException] = [] while self.stack: if list(self.stack.keys()) == needed_collectors[: len(self.stack)]: break @@ -548,13 +551,13 @@ class SetupState: if len(these_exceptions) == 1: exceptions.extend(these_exceptions) elif these_exceptions: - msg = f"errors while tearing down {node!r}" + msg = f'errors while tearing down {node!r}' exceptions.append(BaseExceptionGroup(msg, these_exceptions[::-1])) if len(exceptions) == 1: raise exceptions[0] elif exceptions: - raise BaseExceptionGroup("errors during test teardown", exceptions[::-1]) + raise BaseExceptionGroup('errors during test teardown', exceptions[::-1]) if nextitem is None: assert not self.stack @@ -563,7 +566,7 @@ def collect_one_node(collector: Collector) -> CollectReport: ihook = collector.ihook ihook.pytest_collectstart(collector=collector) rep: CollectReport = ihook.pytest_make_collect_report(collector=collector) - call = rep.__dict__.pop("call", None) + call = rep.__dict__.pop('call', None) if call and check_interactive_exception(call, rep): ihook.pytest_exception_interact(node=collector, call=call, report=rep) return rep diff --git a/.venv/lib/python3.10/site-packages/_pytest/scope.py b/.venv/lib/python3.10/site-packages/_pytest/scope.py index 2c6e232..bf931a1 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/scope.py +++ b/.venv/lib/python3.10/site-packages/_pytest/scope.py @@ -7,6 +7,7 @@ would cause circular references. Also this makes the module light to import, as it should. """ +from __future__ import annotations from enum import Enum from functools import total_ordering @@ -14,7 +15,7 @@ from typing import Literal from typing import Optional -_ScopeName = Literal["session", "package", "module", "class", "function"] +_ScopeName = Literal['session', 'package', 'module', 'class', 'function'] @total_ordering @@ -32,35 +33,35 @@ class Scope(Enum): """ # Scopes need to be listed from lower to higher. - Function: _ScopeName = "function" - Class: _ScopeName = "class" - Module: _ScopeName = "module" - Package: _ScopeName = "package" - Session: _ScopeName = "session" + Function: _ScopeName = 'function' + Class: _ScopeName = 'class' + Module: _ScopeName = 'module' + Package: _ScopeName = 'package' + Session: _ScopeName = 'session' - def next_lower(self) -> "Scope": + def next_lower(self) -> Scope: """Return the next lower scope.""" index = _SCOPE_INDICES[self] if index == 0: - raise ValueError(f"{self} is the lower-most scope") + raise ValueError(f'{self} is the lower-most scope') return _ALL_SCOPES[index - 1] - def next_higher(self) -> "Scope": + def next_higher(self) -> Scope: """Return the next higher scope.""" index = _SCOPE_INDICES[self] if index == len(_SCOPE_INDICES) - 1: - raise ValueError(f"{self} is the upper-most scope") + raise ValueError(f'{self} is the upper-most scope') return _ALL_SCOPES[index + 1] - def __lt__(self, other: "Scope") -> bool: + def __lt__(self, other: Scope) -> bool: self_index = _SCOPE_INDICES[self] other_index = _SCOPE_INDICES[other] return self_index < other_index @classmethod def from_user( - cls, scope_name: _ScopeName, descr: str, where: Optional[str] = None - ) -> "Scope": + cls, scope_name: _ScopeName, descr: str, where: str | None = None, + ) -> Scope: """ Given a scope name from the user, return the equivalent Scope enum. Should be used whenever we want to convert a user provided scope name to its enum object. @@ -75,7 +76,7 @@ class Scope(Enum): except ValueError: fail( "{} {}got an unexpected scope value '{}'".format( - descr, f"from {where} " if where else "", scope_name + descr, f'from {where} ' if where else '', scope_name, ), pytrace=False, ) diff --git a/.venv/lib/python3.10/site-packages/_pytest/setuponly.py b/.venv/lib/python3.10/site-packages/_pytest/setuponly.py index c87de1e..f12cdde 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/setuponly.py +++ b/.venv/lib/python3.10/site-packages/_pytest/setuponly.py @@ -1,7 +1,10 @@ +from __future__ import annotations + from typing import Generator from typing import Optional from typing import Union +import pytest from _pytest._io.saferepr import saferepr from _pytest.config import Config from _pytest.config import ExitCode @@ -9,34 +12,33 @@ from _pytest.config.argparsing import Parser from _pytest.fixtures import FixtureDef from _pytest.fixtures import SubRequest from _pytest.scope import Scope -import pytest def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("debugconfig") + group = parser.getgroup('debugconfig') group.addoption( - "--setuponly", - "--setup-only", - action="store_true", - help="Only setup fixtures, do not execute tests", + '--setuponly', + '--setup-only', + action='store_true', + help='Only setup fixtures, do not execute tests', ) group.addoption( - "--setupshow", - "--setup-show", - action="store_true", - help="Show setup of fixtures while executing tests", + '--setupshow', + '--setup-show', + action='store_true', + help='Show setup of fixtures while executing tests', ) @pytest.hookimpl(wrapper=True) def pytest_fixture_setup( - fixturedef: FixtureDef[object], request: SubRequest + fixturedef: FixtureDef[object], request: SubRequest, ) -> Generator[None, object, object]: try: return (yield) finally: if request.config.option.setupshow: - if hasattr(request, "param"): + if hasattr(request, 'param'): # Save the fixture parameter so ._show_fixture_action() can # display it now and during the teardown (in .finish()). if fixturedef.ids: @@ -47,24 +49,24 @@ def pytest_fixture_setup( else: param = request.param fixturedef.cached_param = param # type: ignore[attr-defined] - _show_fixture_action(fixturedef, request.config, "SETUP") + _show_fixture_action(fixturedef, request.config, 'SETUP') def pytest_fixture_post_finalizer( - fixturedef: FixtureDef[object], request: SubRequest + fixturedef: FixtureDef[object], request: SubRequest, ) -> None: if fixturedef.cached_result is not None: config = request.config if config.option.setupshow: - _show_fixture_action(fixturedef, request.config, "TEARDOWN") - if hasattr(fixturedef, "cached_param"): + _show_fixture_action(fixturedef, request.config, 'TEARDOWN') + if hasattr(fixturedef, 'cached_param'): del fixturedef.cached_param # type: ignore[attr-defined] def _show_fixture_action( - fixturedef: FixtureDef[object], config: Config, msg: str + fixturedef: FixtureDef[object], config: Config, msg: str, ) -> None: - capman = config.pluginmanager.getplugin("capturemanager") + capman = config.pluginmanager.getplugin('capturemanager') if capman: capman.suspend_global_capture() @@ -72,22 +74,22 @@ def _show_fixture_action( tw.line() # Use smaller indentation the higher the scope: Session = 0, Package = 1, etc. scope_indent = list(reversed(Scope)).index(fixturedef._scope) - tw.write(" " * 2 * scope_indent) + tw.write(' ' * 2 * scope_indent) tw.write( - "{step} {scope} {fixture}".format( # noqa: UP032 (Readability) + '{step} {scope} {fixture}'.format( # noqa: UP032 (Readability) step=msg.ljust(8), # align the output to TEARDOWN scope=fixturedef.scope[0].upper(), fixture=fixturedef.argname, - ) + ), ) - if msg == "SETUP": - deps = sorted(arg for arg in fixturedef.argnames if arg != "request") + if msg == 'SETUP': + deps = sorted(arg for arg in fixturedef.argnames if arg != 'request') if deps: - tw.write(" (fixtures used: {})".format(", ".join(deps))) + tw.write(' (fixtures used: {})'.format(', '.join(deps))) - if hasattr(fixturedef, "cached_param"): - tw.write(f"[{saferepr(fixturedef.cached_param, maxsize=42)}]") # type: ignore[attr-defined] + if hasattr(fixturedef, 'cached_param'): + tw.write(f'[{saferepr(fixturedef.cached_param, maxsize=42)}]') # type: ignore[attr-defined] tw.flush() @@ -96,7 +98,7 @@ def _show_fixture_action( @pytest.hookimpl(tryfirst=True) -def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: if config.option.setuponly: config.option.setupshow = True return None diff --git a/.venv/lib/python3.10/site-packages/_pytest/setupplan.py b/.venv/lib/python3.10/site-packages/_pytest/setupplan.py index 13c0df8..eff822a 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/setupplan.py +++ b/.venv/lib/python3.10/site-packages/_pytest/setupplan.py @@ -1,29 +1,31 @@ +from __future__ import annotations + from typing import Optional from typing import Union +import pytest from _pytest.config import Config from _pytest.config import ExitCode from _pytest.config.argparsing import Parser from _pytest.fixtures import FixtureDef from _pytest.fixtures import SubRequest -import pytest def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("debugconfig") + group = parser.getgroup('debugconfig') group.addoption( - "--setupplan", - "--setup-plan", - action="store_true", - help="Show what fixtures and tests would be executed but " + '--setupplan', + '--setup-plan', + action='store_true', + help='Show what fixtures and tests would be executed but ' "don't execute anything", ) @pytest.hookimpl(tryfirst=True) def pytest_fixture_setup( - fixturedef: FixtureDef[object], request: SubRequest -) -> Optional[object]: + fixturedef: FixtureDef[object], request: SubRequest, +) -> object | None: # Will return a dummy fixture if the setuponly option is provided. if request.config.option.setupplan: my_cache_key = fixturedef.cache_key(request) @@ -33,7 +35,7 @@ def pytest_fixture_setup( @pytest.hookimpl(tryfirst=True) -def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: if config.option.setupplan: config.option.setuponly = True config.option.setupshow = True diff --git a/.venv/lib/python3.10/site-packages/_pytest/skipping.py b/.venv/lib/python3.10/site-packages/_pytest/skipping.py index 4799ae6..380dfff 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/skipping.py +++ b/.venv/lib/python3.10/site-packages/_pytest/skipping.py @@ -1,11 +1,13 @@ # mypy: allow-untyped-defs """Support for skip/xfail functions and markers.""" -from collections.abc import Mapping +from __future__ import annotations + import dataclasses import os import platform import sys import traceback +from collections.abc import Mapping from typing import Generator from typing import Optional from typing import Tuple @@ -26,21 +28,21 @@ from _pytest.stash import StashKey def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") + group = parser.getgroup('general') group.addoption( - "--runxfail", - action="store_true", - dest="runxfail", + '--runxfail', + action='store_true', + dest='runxfail', default=False, - help="Report the results of xfail tests as if they were not marked", + help='Report the results of xfail tests as if they were not marked', ) parser.addini( - "xfail_strict", - "Default for the strict parameter of xfail " - "markers when not given explicitly (default: False)", + 'xfail_strict', + 'Default for the strict parameter of xfail ' + 'markers when not given explicitly (default: False)', default=False, - type="bool", + type='bool', ) @@ -50,40 +52,40 @@ def pytest_configure(config: Config) -> None: import pytest old = pytest.xfail - config.add_cleanup(lambda: setattr(pytest, "xfail", old)) + config.add_cleanup(lambda: setattr(pytest, 'xfail', old)) def nop(*args, **kwargs): pass nop.Exception = xfail.Exception # type: ignore[attr-defined] - setattr(pytest, "xfail", nop) + setattr(pytest, 'xfail', nop) config.addinivalue_line( - "markers", - "skip(reason=None): skip the given test function with an optional reason. " + 'markers', + 'skip(reason=None): skip the given test function with an optional reason. ' 'Example: skip(reason="no way of currently testing this") skips the ' - "test.", + 'test.', ) config.addinivalue_line( - "markers", - "skipif(condition, ..., *, reason=...): " - "skip the given test function if any of the conditions evaluate to True. " + 'markers', + 'skipif(condition, ..., *, reason=...): ' + 'skip the given test function if any of the conditions evaluate to True. ' "Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. " - "See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif", + 'See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif', ) config.addinivalue_line( - "markers", - "xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): " - "mark the test function as an expected failure if any of the conditions " - "evaluate to True. Optionally specify a reason for better reporting " + 'markers', + 'xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): ' + 'mark the test function as an expected failure if any of the conditions ' + 'evaluate to True. Optionally specify a reason for better reporting ' "and run=False if you don't even want to execute the test function. " - "If only specific exception(s) are expected, you can list them in " - "raises, and if the test fails in other ways, it will be reported as " - "a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail", + 'If only specific exception(s) are expected, you can list them in ' + 'raises, and if the test fails in other ways, it will be reported as ' + 'a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail', ) -def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool, str]: +def evaluate_condition(item: Item, mark: Mark, condition: object) -> tuple[bool, str]: """Evaluate a single skipif/xfail condition. If an old-style string condition is given, it is eval()'d, otherwise the @@ -95,40 +97,40 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool, # String condition. if isinstance(condition, str): globals_ = { - "os": os, - "sys": sys, - "platform": platform, - "config": item.config, + 'os': os, + 'sys': sys, + 'platform': platform, + 'config': item.config, } for dictionary in reversed( - item.ihook.pytest_markeval_namespace(config=item.config) + item.ihook.pytest_markeval_namespace(config=item.config), ): if not isinstance(dictionary, Mapping): raise ValueError( - f"pytest_markeval_namespace() needs to return a dict, got {dictionary!r}" + f'pytest_markeval_namespace() needs to return a dict, got {dictionary!r}', ) globals_.update(dictionary) - if hasattr(item, "obj"): + if hasattr(item, 'obj'): globals_.update(item.obj.__globals__) # type: ignore[attr-defined] try: - filename = f"<{mark.name} condition>" - condition_code = compile(condition, filename, "eval") + filename = f'<{mark.name} condition>' + condition_code = compile(condition, filename, 'eval') result = eval(condition_code, globals_) except SyntaxError as exc: msglines = [ - "Error evaluating %r condition" % mark.name, - " " + condition, - " " + " " * (exc.offset or 0) + "^", - "SyntaxError: invalid syntax", + 'Error evaluating %r condition' % mark.name, + ' ' + condition, + ' ' + ' ' * (exc.offset or 0) + '^', + 'SyntaxError: invalid syntax', ] - fail("\n".join(msglines), pytrace=False) + fail('\n'.join(msglines), pytrace=False) except Exception as exc: msglines = [ - "Error evaluating %r condition" % mark.name, - " " + condition, + 'Error evaluating %r condition' % mark.name, + ' ' + condition, *traceback.format_exception_only(type(exc), exc), ] - fail("\n".join(msglines), pytrace=False) + fail('\n'.join(msglines), pytrace=False) # Boolean condition. else: @@ -136,20 +138,20 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool, result = bool(condition) except Exception as exc: msglines = [ - "Error evaluating %r condition as a boolean" % mark.name, + 'Error evaluating %r condition as a boolean' % mark.name, *traceback.format_exception_only(type(exc), exc), ] - fail("\n".join(msglines), pytrace=False) + fail('\n'.join(msglines), pytrace=False) - reason = mark.kwargs.get("reason", None) + reason = mark.kwargs.get('reason', None) if reason is None: if isinstance(condition, str): - reason = "condition: " + condition + reason = 'condition: ' + condition else: # XXX better be checked at collection time msg = ( - "Error evaluating %r: " % mark.name - + "you need to specify reason=STRING when using booleans as conditions." + 'Error evaluating %r: ' % mark.name + + 'you need to specify reason=STRING when using booleans as conditions.' ) fail(msg, pytrace=False) @@ -160,20 +162,20 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool, class Skip: """The result of evaluate_skip_marks().""" - reason: str = "unconditional skip" + reason: str = 'unconditional skip' -def evaluate_skip_marks(item: Item) -> Optional[Skip]: +def evaluate_skip_marks(item: Item) -> Skip | None: """Evaluate skip and skipif marks on item, returning Skip if triggered.""" - for mark in item.iter_markers(name="skipif"): - if "condition" not in mark.kwargs: + for mark in item.iter_markers(name='skipif'): + if 'condition' not in mark.kwargs: conditions = mark.args else: - conditions = (mark.kwargs["condition"],) + conditions = (mark.kwargs['condition'],) # Unconditional. if not conditions: - reason = mark.kwargs.get("reason", "") + reason = mark.kwargs.get('reason', '') return Skip(reason) # If any of the conditions are true. @@ -182,11 +184,11 @@ def evaluate_skip_marks(item: Item) -> Optional[Skip]: if result: return Skip(reason) - for mark in item.iter_markers(name="skip"): + for mark in item.iter_markers(name='skip'): try: return Skip(*mark.args, **mark.kwargs) except TypeError as e: - raise TypeError(str(e) + " - maybe you meant pytest.mark.skipif?") from None + raise TypeError(str(e) + ' - maybe you meant pytest.mark.skipif?') from None return None @@ -195,28 +197,28 @@ def evaluate_skip_marks(item: Item) -> Optional[Skip]: class Xfail: """The result of evaluate_xfail_marks().""" - __slots__ = ("reason", "run", "strict", "raises") + __slots__ = ('reason', 'run', 'strict', 'raises') reason: str run: bool strict: bool - raises: Optional[Tuple[Type[BaseException], ...]] + raises: tuple[type[BaseException], ...] | None -def evaluate_xfail_marks(item: Item) -> Optional[Xfail]: +def evaluate_xfail_marks(item: Item) -> Xfail | None: """Evaluate xfail marks on item, returning Xfail if triggered.""" - for mark in item.iter_markers(name="xfail"): - run = mark.kwargs.get("run", True) - strict = mark.kwargs.get("strict", item.config.getini("xfail_strict")) - raises = mark.kwargs.get("raises", None) - if "condition" not in mark.kwargs: + for mark in item.iter_markers(name='xfail'): + run = mark.kwargs.get('run', True) + strict = mark.kwargs.get('strict', item.config.getini('xfail_strict')) + raises = mark.kwargs.get('raises', None) + if 'condition' not in mark.kwargs: conditions = mark.args else: - conditions = (mark.kwargs["condition"],) + conditions = (mark.kwargs['condition'],) # Unconditional. if not conditions: - reason = mark.kwargs.get("reason", "") + reason = mark.kwargs.get('reason', '') return Xfail(reason, run, strict, raises) # If any of the conditions are true. @@ -240,7 +242,7 @@ def pytest_runtest_setup(item: Item) -> None: item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) if xfailed and not item.config.option.runxfail and not xfailed.run: - xfail("[NOTRUN] " + xfailed.reason) + xfail('[NOTRUN] ' + xfailed.reason) @hookimpl(wrapper=True) @@ -250,7 +252,7 @@ def pytest_runtest_call(item: Item) -> Generator[None, None, None]: item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) if xfailed and not item.config.option.runxfail and not xfailed.run: - xfail("[NOTRUN] " + xfailed.reason) + xfail('[NOTRUN] ' + xfailed.reason) try: return (yield) @@ -263,7 +265,7 @@ def pytest_runtest_call(item: Item) -> Generator[None, None, None]: @hookimpl(wrapper=True) def pytest_runtest_makereport( - item: Item, call: CallInfo[None] + item: Item, call: CallInfo[None], ) -> Generator[None, TestReport, TestReport]: rep = yield xfailed = item.stash.get(xfailed_key, None) @@ -271,30 +273,30 @@ def pytest_runtest_makereport( pass # don't interfere elif call.excinfo and isinstance(call.excinfo.value, xfail.Exception): assert call.excinfo.value.msg is not None - rep.wasxfail = "reason: " + call.excinfo.value.msg - rep.outcome = "skipped" + rep.wasxfail = 'reason: ' + call.excinfo.value.msg + rep.outcome = 'skipped' elif not rep.skipped and xfailed: if call.excinfo: raises = xfailed.raises if raises is not None and not isinstance(call.excinfo.value, raises): - rep.outcome = "failed" + rep.outcome = 'failed' else: - rep.outcome = "skipped" + rep.outcome = 'skipped' rep.wasxfail = xfailed.reason - elif call.when == "call": + elif call.when == 'call': if xfailed.strict: - rep.outcome = "failed" - rep.longrepr = "[XPASS(strict)] " + xfailed.reason + rep.outcome = 'failed' + rep.longrepr = '[XPASS(strict)] ' + xfailed.reason else: - rep.outcome = "passed" + rep.outcome = 'passed' rep.wasxfail = xfailed.reason return rep -def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]: - if hasattr(report, "wasxfail"): +def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str] | None: + if hasattr(report, 'wasxfail'): if report.skipped: - return "xfailed", "x", "XFAIL" + return 'xfailed', 'x', 'XFAIL' elif report.passed: - return "xpassed", "X", "XPASS" + return 'xpassed', 'X', 'XPASS' return None diff --git a/.venv/lib/python3.10/site-packages/_pytest/stash.py b/.venv/lib/python3.10/site-packages/_pytest/stash.py index e61d75b..49d19a8 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/stash.py +++ b/.venv/lib/python3.10/site-packages/_pytest/stash.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import Any from typing import cast from typing import Dict @@ -6,11 +8,11 @@ from typing import TypeVar from typing import Union -__all__ = ["Stash", "StashKey"] +__all__ = ['Stash', 'StashKey'] -T = TypeVar("T") -D = TypeVar("D") +T = TypeVar('T') +D = TypeVar('D') class StashKey(Generic[T]): @@ -63,10 +65,10 @@ class Stash: some_bool = stash[some_bool_key] """ - __slots__ = ("_storage",) + __slots__ = ('_storage',) def __init__(self) -> None: - self._storage: Dict[StashKey[Any], object] = {} + self._storage: dict[StashKey[Any], object] = {} def __setitem__(self, key: StashKey[T], value: T) -> None: """Set a value for key.""" @@ -79,7 +81,7 @@ class Stash: """ return cast(T, self._storage[key]) - def get(self, key: StashKey[T], default: D) -> Union[T, D]: + def get(self, key: StashKey[T], default: D) -> T | D: """Get the value for key, or return default if the key wasn't set before.""" try: diff --git a/.venv/lib/python3.10/site-packages/_pytest/stepwise.py b/.venv/lib/python3.10/site-packages/_pytest/stepwise.py index 3ebebc2..8719b17 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/stepwise.py +++ b/.venv/lib/python3.10/site-packages/_pytest/stepwise.py @@ -1,39 +1,41 @@ +from __future__ import annotations + from typing import List from typing import Optional from typing import TYPE_CHECKING +import pytest from _pytest import nodes from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.main import Session from _pytest.reports import TestReport -import pytest if TYPE_CHECKING: from _pytest.cacheprovider import Cache -STEPWISE_CACHE_DIR = "cache/stepwise" +STEPWISE_CACHE_DIR = 'cache/stepwise' def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") + group = parser.getgroup('general') group.addoption( - "--sw", - "--stepwise", - action="store_true", + '--sw', + '--stepwise', + action='store_true', default=False, - dest="stepwise", - help="Exit on test failure and continue from last failing test next time", + dest='stepwise', + help='Exit on test failure and continue from last failing test next time', ) group.addoption( - "--sw-skip", - "--stepwise-skip", - action="store_true", + '--sw-skip', + '--stepwise-skip', + action='store_true', default=False, - dest="stepwise_skip", - help="Ignore the first failing test but stop on the next failing test. " - "Implicitly enables --stepwise.", + dest='stepwise_skip', + help='Ignore the first failing test but stop on the next failing test. ' + 'Implicitly enables --stepwise.', ) @@ -42,14 +44,14 @@ def pytest_configure(config: Config) -> None: if config.option.stepwise_skip: # allow --stepwise-skip to work on it's own merits. config.option.stepwise = True - if config.getoption("stepwise"): - config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") + if config.getoption('stepwise'): + config.pluginmanager.register(StepwisePlugin(config), 'stepwiseplugin') def pytest_sessionfinish(session: Session) -> None: - if not session.config.getoption("stepwise"): + if not session.config.getoption('stepwise'): assert session.config.cache is not None - if hasattr(session.config, "workerinput"): + if hasattr(session.config, 'workerinput'): # Do not update cache if this process is a xdist worker to prevent # race conditions (#10641). return @@ -60,21 +62,21 @@ def pytest_sessionfinish(session: Session) -> None: class StepwisePlugin: def __init__(self, config: Config) -> None: self.config = config - self.session: Optional[Session] = None - self.report_status = "" + self.session: Session | None = None + self.report_status = '' assert config.cache is not None self.cache: Cache = config.cache - self.lastfailed: Optional[str] = self.cache.get(STEPWISE_CACHE_DIR, None) - self.skip: bool = config.getoption("stepwise_skip") + self.lastfailed: str | None = self.cache.get(STEPWISE_CACHE_DIR, None) + self.skip: bool = config.getoption('stepwise_skip') def pytest_sessionstart(self, session: Session) -> None: self.session = session def pytest_collection_modifyitems( - self, config: Config, items: List[nodes.Item] + self, config: Config, items: list[nodes.Item], ) -> None: if not self.lastfailed: - self.report_status = "no previously failed tests, not skipping." + self.report_status = 'no previously failed tests, not skipping.' return # check all item nodes until we find a match on last failed @@ -87,9 +89,9 @@ class StepwisePlugin: # If the previously failed test was not found among the test items, # do not skip any tests. if failed_index is None: - self.report_status = "previously failed test not found, not skipping." + self.report_status = 'previously failed test not found, not skipping.' else: - self.report_status = f"skipping {failed_index} already passed items." + self.report_status = f'skipping {failed_index} already passed items.' deselected = items[:failed_index] del items[:failed_index] config.hook.pytest_deselected(items=deselected) @@ -108,23 +110,23 @@ class StepwisePlugin: self.lastfailed = report.nodeid assert self.session is not None self.session.shouldstop = ( - "Test failed, continuing from this test next run." + 'Test failed, continuing from this test next run.' ) else: # If the test was actually run and did pass. - if report.when == "call": + if report.when == 'call': # Remove test from the failed ones, if exists. if report.nodeid == self.lastfailed: self.lastfailed = None - def pytest_report_collectionfinish(self) -> Optional[str]: - if self.config.getoption("verbose") >= 0 and self.report_status: - return f"stepwise: {self.report_status}" + def pytest_report_collectionfinish(self) -> str | None: + if self.config.getoption('verbose') >= 0 and self.report_status: + return f'stepwise: {self.report_status}' return None def pytest_sessionfinish(self) -> None: - if hasattr(self.config, "workerinput"): + if hasattr(self.config, 'workerinput'): # Do not update cache if this process is a xdist worker to prevent # race conditions (#10641). return diff --git a/.venv/lib/python3.10/site-packages/_pytest/terminal.py b/.venv/lib/python3.10/site-packages/_pytest/terminal.py index 75d5719..3fe73d5 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/terminal.py +++ b/.venv/lib/python3.10/site-packages/_pytest/terminal.py @@ -3,16 +3,19 @@ This is a good source for looking at the various reporting hooks. """ +from __future__ import annotations + import argparse -from collections import Counter import dataclasses import datetime -from functools import partial import inspect -from pathlib import Path import platform import sys import textwrap +import warnings +from collections import Counter +from functools import partial +from pathlib import Path from typing import Any from typing import Callable from typing import ClassVar @@ -30,17 +33,15 @@ from typing import TextIO from typing import Tuple from typing import TYPE_CHECKING from typing import Union -import warnings +import _pytest._version import pluggy - from _pytest import nodes from _pytest import timing from _pytest._code import ExceptionInfo from _pytest._code.code import ExceptionRepr from _pytest._io import TerminalWriter from _pytest._io.wcwidth import wcswidth -import _pytest._version from _pytest.assertion.util import running_on_ci from _pytest.config import _PluggyPlugin from _pytest.config import Config @@ -63,17 +64,17 @@ if TYPE_CHECKING: REPORT_COLLECTING_RESOLUTION = 0.5 KNOWN_TYPES = ( - "failed", - "passed", - "skipped", - "deselected", - "xfailed", - "xpassed", - "warnings", - "error", + 'failed', + 'passed', + 'skipped', + 'deselected', + 'xfailed', + 'xpassed', + 'warnings', + 'error', ) -_REPORTCHARS_DEFAULT = "fE" +_REPORTCHARS_DEFAULT = 'fE' class MoreQuietAction(argparse.Action): @@ -89,7 +90,7 @@ class MoreQuietAction(argparse.Action): dest: str, default: object = None, required: bool = False, - help: Optional[str] = None, + help: str | None = None, ) -> None: super().__init__( option_strings=option_strings, @@ -104,13 +105,13 @@ class MoreQuietAction(argparse.Action): self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - values: Union[str, Sequence[object], None], - option_string: Optional[str] = None, + values: str | Sequence[object] | None, + option_string: str | None = None, ) -> None: new_count = getattr(namespace, self.dest, 0) - 1 setattr(namespace, self.dest, new_count) # todo Deprecate config.quiet - namespace.quiet = getattr(namespace, "quiet", 0) + 1 + namespace.quiet = getattr(namespace, 'quiet', 0) + 1 class TestShortLogReport(NamedTuple): @@ -130,190 +131,190 @@ class TestShortLogReport(NamedTuple): category: str letter: str - word: Union[str, Tuple[str, Mapping[str, bool]]] + word: str | tuple[str, Mapping[str, bool]] def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("terminal reporting", "Reporting", after="general") + group = parser.getgroup('terminal reporting', 'Reporting', after='general') group._addoption( - "-v", - "--verbose", - action="count", + '-v', + '--verbose', + action='count', default=0, - dest="verbose", - help="Increase verbosity", + dest='verbose', + help='Increase verbosity', ) group._addoption( - "--no-header", - action="store_true", + '--no-header', + action='store_true', default=False, - dest="no_header", - help="Disable header", + dest='no_header', + help='Disable header', ) group._addoption( - "--no-summary", - action="store_true", + '--no-summary', + action='store_true', default=False, - dest="no_summary", - help="Disable summary", + dest='no_summary', + help='Disable summary', ) group._addoption( - "-q", - "--quiet", + '-q', + '--quiet', action=MoreQuietAction, default=0, - dest="verbose", - help="Decrease verbosity", + dest='verbose', + help='Decrease verbosity', ) group._addoption( - "--verbosity", - dest="verbose", + '--verbosity', + dest='verbose', type=int, default=0, - help="Set verbosity. Default: 0.", + help='Set verbosity. Default: 0.', ) group._addoption( - "-r", - action="store", - dest="reportchars", + '-r', + action='store', + dest='reportchars', default=_REPORTCHARS_DEFAULT, - metavar="chars", - help="Show extra test summary info as specified by chars: (f)ailed, " - "(E)rror, (s)kipped, (x)failed, (X)passed, " - "(p)assed, (P)assed with output, (a)ll except passed (p/P), or (A)ll. " - "(w)arnings are enabled by default (see --disable-warnings), " + metavar='chars', + help='Show extra test summary info as specified by chars: (f)ailed, ' + '(E)rror, (s)kipped, (x)failed, (X)passed, ' + '(p)assed, (P)assed with output, (a)ll except passed (p/P), or (A)ll. ' + '(w)arnings are enabled by default (see --disable-warnings), ' "'N' can be used to reset the list. (default: 'fE').", ) group._addoption( - "--disable-warnings", - "--disable-pytest-warnings", + '--disable-warnings', + '--disable-pytest-warnings', default=False, - dest="disable_warnings", - action="store_true", - help="Disable warnings summary", + dest='disable_warnings', + action='store_true', + help='Disable warnings summary', ) group._addoption( - "-l", - "--showlocals", - action="store_true", - dest="showlocals", + '-l', + '--showlocals', + action='store_true', + dest='showlocals', default=False, - help="Show locals in tracebacks (disabled by default)", + help='Show locals in tracebacks (disabled by default)', ) group._addoption( - "--no-showlocals", - action="store_false", - dest="showlocals", - help="Hide locals in tracebacks (negate --showlocals passed through addopts)", + '--no-showlocals', + action='store_false', + dest='showlocals', + help='Hide locals in tracebacks (negate --showlocals passed through addopts)', ) group._addoption( - "--tb", - metavar="style", - action="store", - dest="tbstyle", - default="auto", - choices=["auto", "long", "short", "no", "line", "native"], - help="Traceback print mode (auto/long/short/line/native/no)", + '--tb', + metavar='style', + action='store', + dest='tbstyle', + default='auto', + choices=['auto', 'long', 'short', 'no', 'line', 'native'], + help='Traceback print mode (auto/long/short/line/native/no)', ) group._addoption( - "--show-capture", - action="store", - dest="showcapture", - choices=["no", "stdout", "stderr", "log", "all"], - default="all", - help="Controls how captured stdout/stderr/log is shown on failed tests. " - "Default: all.", + '--show-capture', + action='store', + dest='showcapture', + choices=['no', 'stdout', 'stderr', 'log', 'all'], + default='all', + help='Controls how captured stdout/stderr/log is shown on failed tests. ' + 'Default: all.', ) group._addoption( - "--fulltrace", - "--full-trace", - action="store_true", + '--fulltrace', + '--full-trace', + action='store_true', default=False, help="Don't cut any tracebacks (default is to cut)", ) group._addoption( - "--color", - metavar="color", - action="store", - dest="color", - default="auto", - choices=["yes", "no", "auto"], - help="Color terminal output (yes/no/auto)", + '--color', + metavar='color', + action='store', + dest='color', + default='auto', + choices=['yes', 'no', 'auto'], + help='Color terminal output (yes/no/auto)', ) group._addoption( - "--code-highlight", - default="yes", - choices=["yes", "no"], - help="Whether code should be highlighted (only if --color is also enabled). " - "Default: yes.", + '--code-highlight', + default='yes', + choices=['yes', 'no'], + help='Whether code should be highlighted (only if --color is also enabled). ' + 'Default: yes.', ) parser.addini( - "console_output_style", + 'console_output_style', help='Console output: "classic", or with additional progress information ' '("progress" (percentage) | "count" | "progress-even-when-capture-no" (forces ' - "progress even when capture=no)", - default="progress", + 'progress even when capture=no)', + default='progress', ) Config._add_verbosity_ini( parser, Config.VERBOSITY_TEST_CASES, help=( - "Specify a verbosity level for test case execution, overriding the main level. " - "Higher levels will provide more detailed information about each test case executed." + 'Specify a verbosity level for test case execution, overriding the main level. ' + 'Higher levels will provide more detailed information about each test case executed.' ), ) def pytest_configure(config: Config) -> None: reporter = TerminalReporter(config, sys.stdout) - config.pluginmanager.register(reporter, "terminalreporter") + config.pluginmanager.register(reporter, 'terminalreporter') if config.option.debug or config.option.traceconfig: def mywriter(tags, args): - msg = " ".join(map(str, args)) - reporter.write_line("[traceconfig] " + msg) + msg = ' '.join(map(str, args)) + reporter.write_line('[traceconfig] ' + msg) - config.trace.root.setprocessor("pytest:config", mywriter) + config.trace.root.setprocessor('pytest:config', mywriter) def getreportopt(config: Config) -> str: reportchars: str = config.option.reportchars - old_aliases = {"F", "S"} - reportopts = "" + old_aliases = {'F', 'S'} + reportopts = '' for char in reportchars: if char in old_aliases: char = char.lower() - if char == "a": - reportopts = "sxXEf" - elif char == "A": - reportopts = "PpsxXEf" - elif char == "N": - reportopts = "" + if char == 'a': + reportopts = 'sxXEf' + elif char == 'A': + reportopts = 'PpsxXEf' + elif char == 'N': + reportopts = '' elif char not in reportopts: reportopts += char - if not config.option.disable_warnings and "w" not in reportopts: - reportopts = "w" + reportopts - elif config.option.disable_warnings and "w" in reportopts: - reportopts = reportopts.replace("w", "") + if not config.option.disable_warnings and 'w' not in reportopts: + reportopts = 'w' + reportopts + elif config.option.disable_warnings and 'w' in reportopts: + reportopts = reportopts.replace('w', '') return reportopts @hookimpl(trylast=True) # after _pytest.runner -def pytest_report_teststatus(report: BaseReport) -> Tuple[str, str, str]: - letter = "F" +def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str]: + letter = 'F' if report.passed: - letter = "." + letter = '.' elif report.skipped: - letter = "s" + letter = 's' outcome: str = report.outcome - if report.when in ("collect", "setup", "teardown") and outcome == "failed": - outcome = "error" - letter = "E" + if report.when in ('collect', 'setup', 'teardown') and outcome == 'failed': + outcome = 'error' + letter = 'E' return outcome, letter, outcome.upper() @@ -331,68 +332,68 @@ class WarningReport: """ message: str - nodeid: Optional[str] = None - fslocation: Optional[Tuple[str, int]] = None + nodeid: str | None = None + fslocation: tuple[str, int] | None = None count_towards_summary: ClassVar = True - def get_location(self, config: Config) -> Optional[str]: + def get_location(self, config: Config) -> str | None: """Return the more user-friendly information about the location of a warning, or None.""" if self.nodeid: return self.nodeid if self.fslocation: filename, linenum = self.fslocation relpath = bestrelpath(config.invocation_params.dir, absolutepath(filename)) - return f"{relpath}:{linenum}" + return f'{relpath}:{linenum}' return None @final class TerminalReporter: - def __init__(self, config: Config, file: Optional[TextIO] = None) -> None: + def __init__(self, config: Config, file: TextIO | None = None) -> None: import _pytest.config self.config = config self._numcollected = 0 - self._session: Optional[Session] = None - self._showfspath: Optional[bool] = None + self._session: Session | None = None + self._showfspath: bool | None = None - self.stats: Dict[str, List[Any]] = {} - self._main_color: Optional[str] = None - self._known_types: Optional[List[str]] = None + self.stats: dict[str, list[Any]] = {} + self._main_color: str | None = None + self._known_types: list[str] | None = None self.startpath = config.invocation_params.dir if file is None: file = sys.stdout self._tw = _pytest.config.create_terminal_writer(config, file) self._screen_width = self._tw.fullwidth - self.currentfspath: Union[None, Path, str, int] = None + self.currentfspath: None | Path | str | int = None self.reportchars = getreportopt(config) self.hasmarkup = self._tw.hasmarkup self.isatty = file.isatty() - self._progress_nodeids_reported: Set[str] = set() + self._progress_nodeids_reported: set[str] = set() self._show_progress_info = self._determine_show_progress_info() - self._collect_report_last_write: Optional[float] = None - self._already_displayed_warnings: Optional[int] = None - self._keyboardinterrupt_memo: Optional[ExceptionRepr] = None + self._collect_report_last_write: float | None = None + self._already_displayed_warnings: int | None = None + self._keyboardinterrupt_memo: ExceptionRepr | None = None - def _determine_show_progress_info(self) -> Literal["progress", "count", False]: + def _determine_show_progress_info(self) -> Literal['progress', 'count', False]: """Return whether we should display progress information based on the current config.""" # do not show progress if we are not capturing output (#3038) unless explicitly # overridden by progress-even-when-capture-no if ( - self.config.getoption("capture", "no") == "no" - and self.config.getini("console_output_style") - != "progress-even-when-capture-no" + self.config.getoption('capture', 'no') == 'no' and + self.config.getini('console_output_style') != + 'progress-even-when-capture-no' ): return False # do not show progress if we are showing fixture setup/teardown - if self.config.getoption("setupshow", False): + if self.config.getoption('setupshow', False): return False - cfg: str = self.config.getini("console_output_style") - if cfg in {"progress", "progress-even-when-capture-no"}: - return "progress" - elif cfg == "count": - return "count" + cfg: str = self.config.getini('console_output_style') + if cfg in {'progress', 'progress-even-when-capture-no'}: + return 'progress' + elif cfg == 'count': + return 'count' else: return False @@ -420,7 +421,7 @@ class TerminalReporter: return self._showfspath @showfspath.setter - def showfspath(self, value: Optional[bool]) -> None: + def showfspath(self, value: bool | None) -> None: self._showfspath = value @property @@ -428,21 +429,21 @@ class TerminalReporter: return self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) > 0 def hasopt(self, char: str) -> bool: - char = {"xfailed": "x", "skipped": "s"}.get(char, char) + char = {'xfailed': 'x', 'skipped': 's'}.get(char, char) return char in self.reportchars def write_fspath_result(self, nodeid: str, res, **markup: bool) -> None: - fspath = self.config.rootpath / nodeid.split("::")[0] + fspath = self.config.rootpath / nodeid.split('::')[0] if self.currentfspath is None or fspath != self.currentfspath: if self.currentfspath is not None and self._show_progress_info: self._write_progress_information_filling_space() self.currentfspath = fspath relfspath = bestrelpath(self.startpath, fspath) self._tw.line() - self._tw.write(relfspath + " ") + self._tw.write(relfspath + ' ') self._tw.write(res, flush=True, **markup) - def write_ensure_prefix(self, prefix: str, extra: str = "", **kwargs) -> None: + def write_ensure_prefix(self, prefix: str, extra: str = '', **kwargs) -> None: if self.currentfspath != prefix: self._tw.line() self.currentfspath = prefix @@ -462,14 +463,14 @@ class TerminalReporter: *, flush: bool = False, margin: int = 8, - line_sep: str = "\n", + line_sep: str = '\n', **markup: bool, ) -> None: """Wrap message with margin for progress info.""" width_of_current_line = self._tw.width_of_current_line wrapped = line_sep.join( textwrap.wrap( - " " * width_of_current_line + content, + ' ' * width_of_current_line + content, width=self._screen_width - margin, drop_whitespace=True, replace_whitespace=False, @@ -484,9 +485,9 @@ class TerminalReporter: def flush(self) -> None: self._tw.flush() - def write_line(self, line: Union[str, bytes], **markup: bool) -> None: + def write_line(self, line: str | bytes, **markup: bool) -> None: if not isinstance(line, str): - line = str(line, errors="replace") + line = str(line, errors='replace') self.ensure_newline() self._tw.line(line, **markup) @@ -499,26 +500,26 @@ class TerminalReporter: The rest of the keyword arguments are markup instructions. """ - erase = markup.pop("erase", False) + erase = markup.pop('erase', False) if erase: fill_count = self._tw.fullwidth - len(line) - 1 - fill = " " * fill_count + fill = ' ' * fill_count else: - fill = "" + fill = '' line = str(line) - self._tw.write("\r" + line + fill, **markup) + self._tw.write('\r' + line + fill, **markup) def write_sep( self, sep: str, - title: Optional[str] = None, - fullwidth: Optional[int] = None, + title: str | None = None, + fullwidth: int | None = None, **markup: bool, ) -> None: self.ensure_newline() self._tw.sep(sep, title, fullwidth, **markup) - def section(self, title: str, sep: str = "=", **kw: bool) -> None: + def section(self, title: str, sep: str = '=', **kw: bool) -> None: self._tw.sep(sep, title, **kw) def line(self, msg: str, **kw: bool) -> None: @@ -531,8 +532,8 @@ class TerminalReporter: self._set_main_color() def pytest_internalerror(self, excrepr: ExceptionRepr) -> bool: - for line in str(excrepr).split("\n"): - self.write_line("INTERNALERROR> " + line) + for line in str(excrepr).split('\n'): + self.write_line('INTERNALERROR> ' + line) return True def pytest_warning_recorded( @@ -546,32 +547,32 @@ class TerminalReporter: message = warning_record_to_str(warning_message) warning_report = WarningReport( - fslocation=fslocation, message=message, nodeid=nodeid + fslocation=fslocation, message=message, nodeid=nodeid, ) - self._add_stats("warnings", [warning_report]) + self._add_stats('warnings', [warning_report]) def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: if self.config.option.traceconfig: - msg = f"PLUGIN registered: {plugin}" + msg = f'PLUGIN registered: {plugin}' # XXX This event may happen during setup/teardown time # which unfortunately captures our output here # which garbles our output if we use self.write_line. self.write_line(msg) def pytest_deselected(self, items: Sequence[Item]) -> None: - self._add_stats("deselected", items) + self._add_stats('deselected', items) def pytest_runtest_logstart( - self, nodeid: str, location: Tuple[str, Optional[int], str] + self, nodeid: str, location: tuple[str, int | None, str], ) -> None: # Ensure that the path is printed before the # 1st test of a module starts running. if self.showlongtestinfo: line = self._locationline(nodeid, *location) - self.write_ensure_prefix(line, "") + self.write_ensure_prefix(line, '') self.flush() elif self.showfspath: - self.write_fspath_result(nodeid, "") + self.write_fspath_result(nodeid, '') self.flush() def pytest_runtest_logreport(self, report: TestReport) -> None: @@ -579,7 +580,7 @@ class TerminalReporter: rep = report res = TestShortLogReport( - *self.config.hook.pytest_report_teststatus(report=rep, config=self.config) + *self.config.hook.pytest_report_teststatus(report=rep, config=self.config), ) category, letter, word = res.category, res.letter, res.word if not isinstance(word, tuple): @@ -590,17 +591,17 @@ class TerminalReporter: if not letter and not word: # Probably passed setup/teardown. return - running_xdist = hasattr(rep, "node") + running_xdist = hasattr(rep, 'node') if markup is None: - was_xfail = hasattr(report, "wasxfail") + was_xfail = hasattr(report, 'wasxfail') if rep.passed and not was_xfail: - markup = {"green": True} + markup = {'green': True} elif rep.passed and was_xfail: - markup = {"yellow": True} + markup = {'yellow': True} elif rep.failed: - markup = {"red": True} + markup = {'red': True} elif rep.skipped: - markup = {"yellow": True} + markup = {'yellow': True} else: markup = {} if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0: @@ -610,19 +611,19 @@ class TerminalReporter: line = self._locationline(rep.nodeid, *rep.location) if not running_xdist: self.write_ensure_prefix(line, word, **markup) - if rep.skipped or hasattr(report, "wasxfail"): + if rep.skipped or hasattr(report, 'wasxfail'): reason = _get_raw_skip_reason(rep) if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) < 2: available_width = ( - (self._tw.fullwidth - self._tw.width_of_current_line) - - len(" [100%]") - - 1 + (self._tw.fullwidth - self._tw.width_of_current_line) - + len(' [100%]') - + 1 ) formatted_reason = _format_trimmed( - " ({})", reason, available_width + ' ({})', reason, available_width, ) else: - formatted_reason = f" ({reason})" + formatted_reason = f' ({reason})' if reason and formatted_reason is not None: self.wrap_write(formatted_reason) @@ -630,15 +631,15 @@ class TerminalReporter: self._write_progress_information_filling_space() else: self.ensure_newline() - self._tw.write("[%s]" % rep.node.gateway.id) + self._tw.write('[%s]' % rep.node.gateway.id) if self._show_progress_info: self._tw.write( - self._get_progress_information_message() + " ", cyan=True + self._get_progress_information_message() + ' ', cyan=True, ) else: - self._tw.write(" ") + self._tw.write(' ') self._tw.write(word, **markup) - self._tw.write(" " + line) + self._tw.write(' ' + line) self.currentfspath = -2 self.flush() @@ -650,14 +651,14 @@ class TerminalReporter: def pytest_runtest_logfinish(self, nodeid: str) -> None: assert self._session if ( - self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0 - and self._show_progress_info + self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0 and + self._show_progress_info ): - if self._show_progress_info == "count": + if self._show_progress_info == 'count': num_tests = self._session.testscollected - progress_length = len(f" [{num_tests}/{num_tests}]") + progress_length = len(f' [{num_tests}/{num_tests}]') else: - progress_length = len(" [100%]") + progress_length = len(' [100%]') self._progress_nodeids_reported.add(nodeid) @@ -669,24 +670,24 @@ class TerminalReporter: past_edge = w + progress_length + 1 >= self._screen_width if past_edge: msg = self._get_progress_information_message() - self._tw.write(msg + "\n", **{main_color: True}) + self._tw.write(msg + '\n', **{main_color: True}) def _get_progress_information_message(self) -> str: assert self._session collected = self._session.testscollected - if self._show_progress_info == "count": + if self._show_progress_info == 'count': if collected: progress = self._progress_nodeids_reported - counter_format = f"{{:{len(str(collected))}d}}" - format_string = f" [{counter_format}/{{}}]" + counter_format = f'{{:{len(str(collected))}d}}' + format_string = f' [{counter_format}/{{}}]' return format_string.format(len(progress), collected) - return f" [ {collected} / {collected} ]" + return f' [ {collected} / {collected} ]' else: if collected: return ( - f" [{len(self._progress_nodeids_reported) * 100 // collected:3d}%]" + f' [{len(self._progress_nodeids_reported) * 100 // collected:3d}%]' ) - return " [100%]" + return ' [100%]' def _write_progress_information_filling_space(self) -> None: color, _ = self._get_main_color() @@ -703,16 +704,16 @@ class TerminalReporter: def pytest_collection(self) -> None: if self.isatty: if self.config.option.verbose >= 0: - self.write("collecting ... ", flush=True, bold=True) + self.write('collecting ... ', flush=True, bold=True) self._collect_report_last_write = timing.time() elif self.config.option.verbose >= 1: - self.write("collecting ... ", flush=True, bold=True) + self.write('collecting ... ', flush=True, bold=True) def pytest_collectreport(self, report: CollectReport) -> None: if report.failed: - self._add_stats("error", [report]) + self._add_stats('error', [report]) elif report.skipped: - self._add_stats("skipped", [report]) + self._add_stats('skipped', [report]) items = [x for x in report.result if isinstance(x, Item)] self._numcollected += len(items) if self.isatty: @@ -726,64 +727,64 @@ class TerminalReporter: # Only write "collecting" report every 0.5s. t = timing.time() if ( - self._collect_report_last_write is not None - and self._collect_report_last_write > t - REPORT_COLLECTING_RESOLUTION + self._collect_report_last_write is not None and + self._collect_report_last_write > t - REPORT_COLLECTING_RESOLUTION ): return self._collect_report_last_write = t - errors = len(self.stats.get("error", [])) - skipped = len(self.stats.get("skipped", [])) - deselected = len(self.stats.get("deselected", [])) + errors = len(self.stats.get('error', [])) + skipped = len(self.stats.get('skipped', [])) + deselected = len(self.stats.get('deselected', [])) selected = self._numcollected - deselected - line = "collected " if final else "collecting " + line = 'collected ' if final else 'collecting ' line += ( - str(self._numcollected) + " item" + ("" if self._numcollected == 1 else "s") + str(self._numcollected) + ' item' + ('' if self._numcollected == 1 else 's') ) if errors: - line += " / %d error%s" % (errors, "s" if errors != 1 else "") + line += ' / %d error%s' % (errors, 's' if errors != 1 else '') if deselected: - line += " / %d deselected" % deselected + line += ' / %d deselected' % deselected if skipped: - line += " / %d skipped" % skipped + line += ' / %d skipped' % skipped if self._numcollected > selected: - line += " / %d selected" % selected + line += ' / %d selected' % selected if self.isatty: self.rewrite(line, bold=True, erase=True) if final: - self.write("\n") + self.write('\n') else: self.write_line(line) @hookimpl(trylast=True) - def pytest_sessionstart(self, session: "Session") -> None: + def pytest_sessionstart(self, session: Session) -> None: self._session = session self._sessionstarttime = timing.time() if not self.showheader: return - self.write_sep("=", "test session starts", bold=True) + self.write_sep('=', 'test session starts', bold=True) verinfo = platform.python_version() if not self.no_header: - msg = f"platform {sys.platform} -- Python {verinfo}" - pypy_version_info = getattr(sys, "pypy_version_info", None) + msg = f'platform {sys.platform} -- Python {verinfo}' + pypy_version_info = getattr(sys, 'pypy_version_info', None) if pypy_version_info: - verinfo = ".".join(map(str, pypy_version_info[:3])) - msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" - msg += f", pytest-{_pytest._version.version}, pluggy-{pluggy.__version__}" + verinfo = '.'.join(map(str, pypy_version_info[:3])) + msg += f'[pypy-{verinfo}-{pypy_version_info[3]}]' + msg += f', pytest-{_pytest._version.version}, pluggy-{pluggy.__version__}' if ( - self.verbosity > 0 - or self.config.option.debug - or getattr(self.config.option, "pastebin", None) + self.verbosity > 0 or + self.config.option.debug or + getattr(self.config.option, 'pastebin', None) ): - msg += " -- " + str(sys.executable) + msg += ' -- ' + str(sys.executable) self.write_line(msg) lines = self.config.hook.pytest_report_header( - config=self.config, start_path=self.startpath + config=self.config, start_path=self.startpath, ) self._write_report_lines_from_hooks(lines) def _write_report_lines_from_hooks( - self, lines: Sequence[Union[str, Sequence[str]]] + self, lines: Sequence[str | Sequence[str]], ) -> None: for line_or_lines in reversed(lines): if isinstance(line_or_lines, str): @@ -792,22 +793,22 @@ class TerminalReporter: for line in line_or_lines: self.write_line(line) - def pytest_report_header(self, config: Config) -> List[str]: - result = [f"rootdir: {config.rootpath}"] + def pytest_report_header(self, config: Config) -> list[str]: + result = [f'rootdir: {config.rootpath}'] if config.inipath: - result.append("configfile: " + bestrelpath(config.rootpath, config.inipath)) + result.append('configfile: ' + bestrelpath(config.rootpath, config.inipath)) if config.args_source == Config.ArgsSource.TESTPATHS: - testpaths: List[str] = config.getini("testpaths") - result.append("testpaths: {}".format(", ".join(testpaths))) + testpaths: list[str] = config.getini('testpaths') + result.append('testpaths: {}'.format(', '.join(testpaths))) plugininfo = config.pluginmanager.list_plugin_distinfo() if plugininfo: - result.append("plugins: %s" % ", ".join(_plugin_nameversions(plugininfo))) + result.append('plugins: %s' % ', '.join(_plugin_nameversions(plugininfo))) return result - def pytest_collection_finish(self, session: "Session") -> None: + def pytest_collection_finish(self, session: Session) -> None: self.report_collect(True) lines = self.config.hook.pytest_report_collectionfinish( @@ -817,15 +818,15 @@ class TerminalReporter: ) self._write_report_lines_from_hooks(lines) - if self.config.getoption("collectonly"): + if self.config.getoption('collectonly'): if session.items: if self.config.option.verbose > -1: - self._tw.line("") + self._tw.line('') self._printcollecteditems(session.items) - failed = self.stats.get("failed") + failed = self.stats.get('failed') if failed: - self._tw.sep("!", "collection failures") + self._tw.sep('!', 'collection failures') for rep in failed: rep.toterminal(self._tw) @@ -833,38 +834,38 @@ class TerminalReporter: test_cases_verbosity = self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) if test_cases_verbosity < 0: if test_cases_verbosity < -1: - counts = Counter(item.nodeid.split("::", 1)[0] for item in items) + counts = Counter(item.nodeid.split('::', 1)[0] for item in items) for name, count in sorted(counts.items()): - self._tw.line("%s: %d" % (name, count)) + self._tw.line('%s: %d' % (name, count)) else: for item in items: self._tw.line(item.nodeid) return - stack: List[Node] = [] - indent = "" + stack: list[Node] = [] + indent = '' for item in items: needed_collectors = item.listchain()[1:] # strip root node while stack: if stack == needed_collectors[: len(stack)]: break stack.pop() - for col in needed_collectors[len(stack) :]: + for col in needed_collectors[len(stack):]: stack.append(col) - indent = (len(stack) - 1) * " " - self._tw.line(f"{indent}{col}") + indent = (len(stack) - 1) * ' ' + self._tw.line(f'{indent}{col}') if test_cases_verbosity >= 1: - obj = getattr(col, "obj", None) + obj = getattr(col, 'obj', None) doc = inspect.getdoc(obj) if obj else None if doc: for line in doc.splitlines(): - self._tw.line("{}{}".format(indent + " ", line)) + self._tw.line('{}{}'.format(indent + ' ', line)) @hookimpl(wrapper=True) def pytest_sessionfinish( - self, session: "Session", exitstatus: Union[int, ExitCode] + self, session: Session, exitstatus: int | ExitCode, ) -> Generator[None, None, None]: result = yield - self._tw.line("") + self._tw.line('') summary_exit_codes = ( ExitCode.OK, ExitCode.TESTS_FAILED, @@ -874,15 +875,15 @@ class TerminalReporter: ) if exitstatus in summary_exit_codes and not self.no_summary: self.config.hook.pytest_terminal_summary( - terminalreporter=self, exitstatus=exitstatus, config=self.config + terminalreporter=self, exitstatus=exitstatus, config=self.config, ) if session.shouldfail: - self.write_sep("!", str(session.shouldfail), red=True) + self.write_sep('!', str(session.shouldfail), red=True) if exitstatus == ExitCode.INTERRUPTED: self._report_keyboardinterrupt() self._keyboardinterrupt_memo = None elif session.shouldstop: - self.write_sep("!", str(session.shouldstop), red=True) + self.write_sep('!', str(session.shouldstop), red=True) self.summary_stats() return result @@ -913,45 +914,45 @@ class TerminalReporter: assert excrepr is not None assert excrepr.reprcrash is not None msg = excrepr.reprcrash.message - self.write_sep("!", msg) - if "KeyboardInterrupt" in msg: + self.write_sep('!', msg) + if 'KeyboardInterrupt' in msg: if self.config.option.fulltrace: excrepr.toterminal(self._tw) else: excrepr.reprcrash.toterminal(self._tw) self._tw.line( - "(to show a full traceback on KeyboardInterrupt use --full-trace)", + '(to show a full traceback on KeyboardInterrupt use --full-trace)', yellow=True, ) def _locationline( - self, nodeid: str, fspath: str, lineno: Optional[int], domain: str + self, nodeid: str, fspath: str, lineno: int | None, domain: str, ) -> str: def mkrel(nodeid: str) -> str: line = self.config.cwd_relative_nodeid(nodeid) if domain and line.endswith(domain): line = line[: -len(domain)] - values = domain.split("[") - values[0] = values[0].replace(".", "::") # don't replace '.' in params - line += "[".join(values) + values = domain.split('[') + values[0] = values[0].replace('.', '::') # don't replace '.' in params + line += '['.join(values) return line # collect_fspath comes from testid which has a "/"-normalized path. if fspath: res = mkrel(nodeid) - if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace( - "\\", nodes.SEP + if self.verbosity >= 2 and nodeid.split('::')[0] != fspath.replace( + '\\', nodes.SEP, ): - res += " <- " + bestrelpath(self.startpath, Path(fspath)) + res += ' <- ' + bestrelpath(self.startpath, Path(fspath)) else: - res = "[location]" - return res + " " + res = '[location]' + return res + ' ' def _getfailureheadline(self, rep): head_line = rep.head_line if head_line: return head_line - return "test session" # XXX? + return 'test session' # XXX? def _getcrashline(self, rep): try: @@ -960,34 +961,34 @@ class TerminalReporter: try: return str(rep.longrepr)[:50] except AttributeError: - return "" + return '' # # Summaries for sessionfinish. # def getreports(self, name: str): - return [x for x in self.stats.get(name, ()) if not hasattr(x, "_pdbshown")] + return [x for x in self.stats.get(name, ()) if not hasattr(x, '_pdbshown')] def summary_warnings(self) -> None: - if self.hasopt("w"): - all_warnings: Optional[List[WarningReport]] = self.stats.get("warnings") + if self.hasopt('w'): + all_warnings: list[WarningReport] | None = self.stats.get('warnings') if not all_warnings: return final = self._already_displayed_warnings is not None if final: - warning_reports = all_warnings[self._already_displayed_warnings :] + warning_reports = all_warnings[self._already_displayed_warnings:] else: warning_reports = all_warnings self._already_displayed_warnings = len(warning_reports) if not warning_reports: return - reports_grouped_by_message: Dict[str, List[WarningReport]] = {} + reports_grouped_by_message: dict[str, list[WarningReport]] = {} for wr in warning_reports: reports_grouped_by_message.setdefault(wr.message, []).append(wr) - def collapsed_location_report(reports: List[WarningReport]) -> str: + def collapsed_location_report(reports: list[WarningReport]) -> str: locations = [] for w in reports: location = w.get_location(self.config) @@ -995,61 +996,61 @@ class TerminalReporter: locations.append(location) if len(locations) < 10: - return "\n".join(map(str, locations)) + return '\n'.join(map(str, locations)) counts_by_filename = Counter( - str(loc).split("::", 1)[0] for loc in locations + str(loc).split('::', 1)[0] for loc in locations ) - return "\n".join( - "{}: {} warning{}".format(k, v, "s" if v > 1 else "") + return '\n'.join( + '{}: {} warning{}'.format(k, v, 's' if v > 1 else '') for k, v in counts_by_filename.items() ) - title = "warnings summary (final)" if final else "warnings summary" - self.write_sep("=", title, yellow=True, bold=False) + title = 'warnings summary (final)' if final else 'warnings summary' + self.write_sep('=', title, yellow=True, bold=False) for message, message_reports in reports_grouped_by_message.items(): maybe_location = collapsed_location_report(message_reports) if maybe_location: self._tw.line(maybe_location) lines = message.splitlines() - indented = "\n".join(" " + x for x in lines) + indented = '\n'.join(' ' + x for x in lines) message = indented.rstrip() else: message = message.rstrip() self._tw.line(message) self._tw.line() self._tw.line( - "-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html" + '-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html', ) def summary_passes(self) -> None: - self.summary_passes_combined("passed", "PASSES", "P") + self.summary_passes_combined('passed', 'PASSES', 'P') def summary_xpasses(self) -> None: - self.summary_passes_combined("xpassed", "XPASSES", "X") + self.summary_passes_combined('xpassed', 'XPASSES', 'X') def summary_passes_combined( - self, which_reports: str, sep_title: str, needed_opt: str + self, which_reports: str, sep_title: str, needed_opt: str, ) -> None: - if self.config.option.tbstyle != "no": + if self.config.option.tbstyle != 'no': if self.hasopt(needed_opt): - reports: List[TestReport] = self.getreports(which_reports) + reports: list[TestReport] = self.getreports(which_reports) if not reports: return - self.write_sep("=", sep_title) + self.write_sep('=', sep_title) for rep in reports: if rep.sections: msg = self._getfailureheadline(rep) - self.write_sep("_", msg, green=True, bold=True) + self.write_sep('_', msg, green=True, bold=True) self._outrep_summary(rep) self._handle_teardown_sections(rep.nodeid) - def _get_teardown_reports(self, nodeid: str) -> List[TestReport]: - reports = self.getreports("") + def _get_teardown_reports(self, nodeid: str) -> list[TestReport]: + reports = self.getreports('') return [ report for report in reports - if report.when == "teardown" and report.nodeid == nodeid + if report.when == 'teardown' and report.nodeid == nodeid ] def _handle_teardown_sections(self, nodeid: str) -> None: @@ -1058,68 +1059,68 @@ class TerminalReporter: def print_teardown_sections(self, rep: TestReport) -> None: showcapture = self.config.option.showcapture - if showcapture == "no": + if showcapture == 'no': return for secname, content in rep.sections: - if showcapture != "all" and showcapture not in secname: + if showcapture != 'all' and showcapture not in secname: continue - if "teardown" in secname: - self._tw.sep("-", secname) - if content[-1:] == "\n": + if 'teardown' in secname: + self._tw.sep('-', secname) + if content[-1:] == '\n': content = content[:-1] self._tw.line(content) def summary_failures(self) -> None: - self.summary_failures_combined("failed", "FAILURES") + self.summary_failures_combined('failed', 'FAILURES') def summary_xfailures(self) -> None: - self.summary_failures_combined("xfailed", "XFAILURES", "x") + self.summary_failures_combined('xfailed', 'XFAILURES', 'x') def summary_failures_combined( - self, which_reports: str, sep_title: str, needed_opt: Optional[str] = None + self, which_reports: str, sep_title: str, needed_opt: str | None = None, ) -> None: - if self.config.option.tbstyle != "no": + if self.config.option.tbstyle != 'no': if not needed_opt or self.hasopt(needed_opt): - reports: List[BaseReport] = self.getreports(which_reports) + reports: list[BaseReport] = self.getreports(which_reports) if not reports: return - self.write_sep("=", sep_title) - if self.config.option.tbstyle == "line": + self.write_sep('=', sep_title) + if self.config.option.tbstyle == 'line': for rep in reports: line = self._getcrashline(rep) self.write_line(line) else: for rep in reports: msg = self._getfailureheadline(rep) - self.write_sep("_", msg, red=True, bold=True) + self.write_sep('_', msg, red=True, bold=True) self._outrep_summary(rep) self._handle_teardown_sections(rep.nodeid) def summary_errors(self) -> None: - if self.config.option.tbstyle != "no": - reports: List[BaseReport] = self.getreports("error") + if self.config.option.tbstyle != 'no': + reports: list[BaseReport] = self.getreports('error') if not reports: return - self.write_sep("=", "ERRORS") - for rep in self.stats["error"]: + self.write_sep('=', 'ERRORS') + for rep in self.stats['error']: msg = self._getfailureheadline(rep) - if rep.when == "collect": - msg = "ERROR collecting " + msg + if rep.when == 'collect': + msg = 'ERROR collecting ' + msg else: - msg = f"ERROR at {rep.when} of {msg}" - self.write_sep("_", msg, red=True, bold=True) + msg = f'ERROR at {rep.when} of {msg}' + self.write_sep('_', msg, red=True, bold=True) self._outrep_summary(rep) def _outrep_summary(self, rep: BaseReport) -> None: rep.toterminal(self._tw) showcapture = self.config.option.showcapture - if showcapture == "no": + if showcapture == 'no': return for secname, content in rep.sections: - if showcapture != "all" and showcapture not in secname: + if showcapture != 'all' and showcapture not in secname: continue - self._tw.sep("-", secname) - if content[-1:] == "\n": + self._tw.sep('-', secname) + if content[-1:] == '\n': content = content[:-1] self._tw.line(content) @@ -1139,24 +1140,24 @@ class TerminalReporter: if display_sep: fullwidth += len(with_markup) - len(text) line_parts.append(with_markup) - msg = ", ".join(line_parts) + msg = ', '.join(line_parts) main_markup = {main_color: True} - duration = f" in {format_session_duration(session_duration)}" + duration = f' in {format_session_duration(session_duration)}' duration_with_markup = self._tw.markup(duration, **main_markup) if display_sep: fullwidth += len(duration_with_markup) - len(duration) msg += duration_with_markup if display_sep: - markup_for_end_sep = self._tw.markup("", **main_markup) - if markup_for_end_sep.endswith("\x1b[0m"): + markup_for_end_sep = self._tw.markup('', **main_markup) + if markup_for_end_sep.endswith('\x1b[0m'): markup_for_end_sep = markup_for_end_sep[:-4] fullwidth += len(markup_for_end_sep) msg += markup_for_end_sep if display_sep: - self.write_sep("=", msg, fullwidth=fullwidth, **main_markup) + self.write_sep('=', msg, fullwidth=fullwidth, **main_markup) else: self.write_line(msg, **main_markup) @@ -1164,7 +1165,7 @@ class TerminalReporter: if not self.reportchars: return - def show_simple(lines: List[str], *, stat: str) -> None: + def show_simple(lines: list[str], *, stat: str) -> None: failed = self.stats.get(stat, []) if not failed: return @@ -1172,80 +1173,80 @@ class TerminalReporter: for rep in failed: color = _color_for_type.get(stat, _color_for_type_default) line = _get_line_with_reprcrash_message( - config, rep, self._tw, {color: True} + config, rep, self._tw, {color: True}, ) lines.append(line) - def show_xfailed(lines: List[str]) -> None: - xfailed = self.stats.get("xfailed", []) + def show_xfailed(lines: list[str]) -> None: + xfailed = self.stats.get('xfailed', []) for rep in xfailed: verbose_word = rep._get_verbose_word(self.config) markup_word = self._tw.markup( - verbose_word, **{_color_for_type["warnings"]: True} + verbose_word, **{_color_for_type['warnings']: True}, ) nodeid = _get_node_id_with_markup(self._tw, self.config, rep) - line = f"{markup_word} {nodeid}" + line = f'{markup_word} {nodeid}' reason = rep.wasxfail if reason: - line += " - " + str(reason) + line += ' - ' + str(reason) lines.append(line) - def show_xpassed(lines: List[str]) -> None: - xpassed = self.stats.get("xpassed", []) + def show_xpassed(lines: list[str]) -> None: + xpassed = self.stats.get('xpassed', []) for rep in xpassed: verbose_word = rep._get_verbose_word(self.config) markup_word = self._tw.markup( - verbose_word, **{_color_for_type["warnings"]: True} + verbose_word, **{_color_for_type['warnings']: True}, ) nodeid = _get_node_id_with_markup(self._tw, self.config, rep) - line = f"{markup_word} {nodeid}" + line = f'{markup_word} {nodeid}' reason = rep.wasxfail if reason: - line += " - " + str(reason) + line += ' - ' + str(reason) lines.append(line) - def show_skipped(lines: List[str]) -> None: - skipped: List[CollectReport] = self.stats.get("skipped", []) + def show_skipped(lines: list[str]) -> None: + skipped: list[CollectReport] = self.stats.get('skipped', []) fskips = _folded_skips(self.startpath, skipped) if skipped else [] if not fskips: return verbose_word = skipped[0]._get_verbose_word(self.config) markup_word = self._tw.markup( - verbose_word, **{_color_for_type["warnings"]: True} + verbose_word, **{_color_for_type['warnings']: True}, ) - prefix = "Skipped: " + prefix = 'Skipped: ' for num, fspath, lineno, reason in fskips: if reason.startswith(prefix): - reason = reason[len(prefix) :] + reason = reason[len(prefix):] if lineno is not None: lines.append( - "%s [%d] %s:%d: %s" % (markup_word, num, fspath, lineno, reason) + '%s [%d] %s:%d: %s' % (markup_word, num, fspath, lineno, reason), ) else: - lines.append("%s [%d] %s: %s" % (markup_word, num, fspath, reason)) + lines.append('%s [%d] %s: %s' % (markup_word, num, fspath, reason)) - REPORTCHAR_ACTIONS: Mapping[str, Callable[[List[str]], None]] = { - "x": show_xfailed, - "X": show_xpassed, - "f": partial(show_simple, stat="failed"), - "s": show_skipped, - "p": partial(show_simple, stat="passed"), - "E": partial(show_simple, stat="error"), + REPORTCHAR_ACTIONS: Mapping[str, Callable[[list[str]], None]] = { + 'x': show_xfailed, + 'X': show_xpassed, + 'f': partial(show_simple, stat='failed'), + 's': show_skipped, + 'p': partial(show_simple, stat='passed'), + 'E': partial(show_simple, stat='error'), } - lines: List[str] = [] + lines: list[str] = [] for char in self.reportchars: action = REPORTCHAR_ACTIONS.get(char) if action: # skipping e.g. "P" (passed with output) here. action(lines) if lines: - self.write_sep("=", "short test summary info", cyan=True, bold=True) + self.write_sep('=', 'short test summary info', cyan=True, bold=True) for line in lines: self.write_line(line) - def _get_main_color(self) -> Tuple[str, List[str]]: + def _get_main_color(self) -> tuple[str, list[str]]: if self._main_color is None or self._known_types is None or self._is_last_item: self._set_main_color() assert self._main_color @@ -1254,18 +1255,18 @@ class TerminalReporter: def _determine_main_color(self, unknown_type_seen: bool) -> str: stats = self.stats - if "failed" in stats or "error" in stats: - main_color = "red" - elif "warnings" in stats or "xpassed" in stats or unknown_type_seen: - main_color = "yellow" - elif "passed" in stats or not self._is_last_item: - main_color = "green" + if 'failed' in stats or 'error' in stats: + main_color = 'red' + elif 'warnings' in stats or 'xpassed' in stats or unknown_type_seen: + main_color = 'yellow' + elif 'passed' in stats or not self._is_last_item: + main_color = 'green' else: - main_color = "yellow" + main_color = 'yellow' return main_color def _set_main_color(self) -> None: - unknown_types: List[str] = [] + unknown_types: list[str] = [] for found_type in self.stats.keys(): if found_type: # setup/teardown reports have an empty key, ignore them if found_type not in KNOWN_TYPES and found_type not in unknown_types: @@ -1273,7 +1274,7 @@ class TerminalReporter: self._known_types = list(KNOWN_TYPES) + unknown_types self._main_color = self._determine_main_color(bool(unknown_types)) - def build_summary_stats_line(self) -> Tuple[List[Tuple[str, Dict[str, bool]]], str]: + def build_summary_stats_line(self) -> tuple[list[tuple[str, dict[str, bool]]], str]: """ Build the parts used in the last summary stats line. @@ -1293,19 +1294,19 @@ class TerminalReporter: The final color of the line is also determined by this function, and is the second element of the returned tuple. """ - if self.config.getoption("collectonly"): + if self.config.getoption('collectonly'): return self._build_collect_only_summary_stats_line() else: return self._build_normal_summary_stats_line() - def _get_reports_to_display(self, key: str) -> List[Any]: + def _get_reports_to_display(self, key: str) -> list[Any]: """Get test/collection reports for the given status key, such as `passed` or `error`.""" reports = self.stats.get(key, []) - return [x for x in reports if getattr(x, "count_towards_summary", True)] + return [x for x in reports if getattr(x, 'count_towards_summary', True)] def _build_normal_summary_stats_line( self, - ) -> Tuple[List[Tuple[str, Dict[str, bool]]], str]: + ) -> tuple[list[tuple[str, dict[str, bool]]], str]: main_color, known_types = self._get_main_color() parts = [] @@ -1314,69 +1315,69 @@ class TerminalReporter: if reports: count = len(reports) color = _color_for_type.get(key, _color_for_type_default) - markup = {color: True, "bold": color == main_color} - parts.append(("%d %s" % pluralize(count, key), markup)) + markup = {color: True, 'bold': color == main_color} + parts.append(('%d %s' % pluralize(count, key), markup)) if not parts: - parts = [("no tests ran", {_color_for_type_default: True})] + parts = [('no tests ran', {_color_for_type_default: True})] return parts, main_color def _build_collect_only_summary_stats_line( self, - ) -> Tuple[List[Tuple[str, Dict[str, bool]]], str]: - deselected = len(self._get_reports_to_display("deselected")) - errors = len(self._get_reports_to_display("error")) + ) -> tuple[list[tuple[str, dict[str, bool]]], str]: + deselected = len(self._get_reports_to_display('deselected')) + errors = len(self._get_reports_to_display('error')) if self._numcollected == 0: - parts = [("no tests collected", {"yellow": True})] - main_color = "yellow" + parts = [('no tests collected', {'yellow': True})] + main_color = 'yellow' elif deselected == 0: - main_color = "green" - collected_output = "%d %s collected" % pluralize(self._numcollected, "test") + main_color = 'green' + collected_output = '%d %s collected' % pluralize(self._numcollected, 'test') parts = [(collected_output, {main_color: True})] else: all_tests_were_deselected = self._numcollected == deselected if all_tests_were_deselected: - main_color = "yellow" - collected_output = f"no tests collected ({deselected} deselected)" + main_color = 'yellow' + collected_output = f'no tests collected ({deselected} deselected)' else: - main_color = "green" + main_color = 'green' selected = self._numcollected - deselected - collected_output = f"{selected}/{self._numcollected} tests collected ({deselected} deselected)" + collected_output = f'{selected}/{self._numcollected} tests collected ({deselected} deselected)' parts = [(collected_output, {main_color: True})] if errors: - main_color = _color_for_type["error"] - parts += [("%d %s" % pluralize(errors, "error"), {main_color: True})] + main_color = _color_for_type['error'] + parts += [('%d %s' % pluralize(errors, 'error'), {main_color: True})] return parts, main_color def _get_node_id_with_markup(tw: TerminalWriter, config: Config, rep: BaseReport): nodeid = config.cwd_relative_nodeid(rep.nodeid) - path, *parts = nodeid.split("::") + path, *parts = nodeid.split('::') if parts: - parts_markup = tw.markup("::".join(parts), bold=True) - return path + "::" + parts_markup + parts_markup = tw.markup('::'.join(parts), bold=True) + return path + '::' + parts_markup else: return path -def _format_trimmed(format: str, msg: str, available_width: int) -> Optional[str]: +def _format_trimmed(format: str, msg: str, available_width: int) -> str | None: """Format msg into format, ellipsizing it if doesn't fit in available_width. Returns None if even the ellipsis can't fit. """ # Only use the first line. - i = msg.find("\n") + i = msg.find('\n') if i != -1: msg = msg[:i] - ellipsis = "..." - format_width = wcswidth(format.format("")) + ellipsis = '...' + format_width = wcswidth(format.format('')) if format_width + len(ellipsis) > available_width: return None @@ -1391,14 +1392,14 @@ def _format_trimmed(format: str, msg: str, available_width: int) -> Optional[str def _get_line_with_reprcrash_message( - config: Config, rep: BaseReport, tw: TerminalWriter, word_markup: Dict[str, bool] + config: Config, rep: BaseReport, tw: TerminalWriter, word_markup: dict[str, bool], ) -> str: """Get summary line for a report, trying to add reprcrash message.""" verbose_word = rep._get_verbose_word(config) word = tw.markup(verbose_word, **word_markup) node = _get_node_id_with_markup(tw, config, rep) - line = f"{word} {node}" + line = f'{word} {node}' line_width = wcswidth(line) try: @@ -1409,9 +1410,9 @@ def _get_line_with_reprcrash_message( else: if not running_on_ci(): available_width = tw.fullwidth - line_width - msg = _format_trimmed(" - {}", msg, available_width) + msg = _format_trimmed(' - {}', msg, available_width) else: - msg = f" - {msg}" + msg = f' - {msg}' if msg is not None: line += msg @@ -1421,8 +1422,8 @@ def _get_line_with_reprcrash_message( def _folded_skips( startpath: Path, skipped: Sequence[CollectReport], -) -> List[Tuple[int, str, Optional[int], str]]: - d: Dict[Tuple[str, Optional[int], str], List[CollectReport]] = {} +) -> list[tuple[int, str, int | None, str]]: + d: dict[tuple[str, int | None, str], list[CollectReport]] = {} for event in skipped: assert event.longrepr is not None assert isinstance(event.longrepr, tuple), (event, event.longrepr) @@ -1430,54 +1431,54 @@ def _folded_skips( fspath, lineno, reason = event.longrepr # For consistency, report all fspaths in relative form. fspath = bestrelpath(startpath, Path(fspath)) - keywords = getattr(event, "keywords", {}) + keywords = getattr(event, 'keywords', {}) # Folding reports with global pytestmark variable. # This is a workaround, because for now we cannot identify the scope of a skip marker # TODO: Revisit after marks scope would be fixed. if ( - event.when == "setup" - and "skip" in keywords - and "pytestmark" not in keywords + event.when == 'setup' and + 'skip' in keywords and + 'pytestmark' not in keywords ): - key: Tuple[str, Optional[int], str] = (fspath, None, reason) + key: tuple[str, int | None, str] = (fspath, None, reason) else: key = (fspath, lineno, reason) d.setdefault(key, []).append(event) - values: List[Tuple[int, str, Optional[int], str]] = [] + values: list[tuple[int, str, int | None, str]] = [] for key, events in d.items(): values.append((len(events), *key)) return values _color_for_type = { - "failed": "red", - "error": "red", - "warnings": "yellow", - "passed": "green", + 'failed': 'red', + 'error': 'red', + 'warnings': 'yellow', + 'passed': 'green', } -_color_for_type_default = "yellow" +_color_for_type_default = 'yellow' -def pluralize(count: int, noun: str) -> Tuple[int, str]: +def pluralize(count: int, noun: str) -> tuple[int, str]: # No need to pluralize words such as `failed` or `passed`. - if noun not in ["error", "warnings", "test"]: + if noun not in ['error', 'warnings', 'test']: return count, noun # The `warnings` key is plural. To avoid API breakage, we keep it that way but # set it to singular here so we can determine plurality in the same way as we do # for `error`. - noun = noun.replace("warnings", "warning") + noun = noun.replace('warnings', 'warning') - return count, noun + "s" if count != 1 else noun + return count, noun + 's' if count != 1 else noun -def _plugin_nameversions(plugininfo) -> List[str]: - values: List[str] = [] +def _plugin_nameversions(plugininfo) -> list[str]: + values: list[str] = [] for plugin, dist in plugininfo: # Gets us name and version! - name = f"{dist.project_name}-{dist.version}" + name = f'{dist.project_name}-{dist.version}' # Questionable convenience, but it keeps things short. - if name.startswith("pytest-"): + if name.startswith('pytest-'): name = name[7:] # We decided to print python package names they can have more than one plugin. if name not in values: @@ -1488,10 +1489,10 @@ def _plugin_nameversions(plugininfo) -> List[str]: def format_session_duration(seconds: float) -> str: """Format the given seconds in a human readable manner to show in the final summary.""" if seconds < 60: - return f"{seconds:.2f}s" + return f'{seconds:.2f}s' else: dt = datetime.timedelta(seconds=int(seconds)) - return f"{seconds:.2f}s ({dt})" + return f'{seconds:.2f}s ({dt})' def _get_raw_skip_reason(report: TestReport) -> str: @@ -1499,17 +1500,17 @@ def _get_raw_skip_reason(report: TestReport) -> str: The string is just the part given by the user. """ - if hasattr(report, "wasxfail"): + if hasattr(report, 'wasxfail'): reason = report.wasxfail - if reason.startswith("reason: "): - reason = reason[len("reason: ") :] + if reason.startswith('reason: '): + reason = reason[len('reason: '):] return reason else: assert report.skipped assert isinstance(report.longrepr, tuple) _, _, reason = report.longrepr - if reason.startswith("Skipped: "): - reason = reason[len("Skipped: ") :] - elif reason == "Skipped": - reason = "" + if reason.startswith('Skipped: '): + reason = reason[len('Skipped: '):] + elif reason == 'Skipped': + reason = '' return reason diff --git a/.venv/lib/python3.10/site-packages/_pytest/threadexception.py b/.venv/lib/python3.10/site-packages/_pytest/threadexception.py index 09faf66..db3c8ec 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/threadexception.py +++ b/.venv/lib/python3.10/site-packages/_pytest/threadexception.py @@ -1,12 +1,14 @@ +from __future__ import annotations + import threading import traceback +import warnings from types import TracebackType from typing import Any from typing import Callable from typing import Generator from typing import Optional from typing import Type -import warnings import pytest @@ -34,22 +36,22 @@ class catch_threading_exception: """ def __init__(self) -> None: - self.args: Optional["threading.ExceptHookArgs"] = None - self._old_hook: Optional[Callable[["threading.ExceptHookArgs"], Any]] = None + self.args: threading.ExceptHookArgs | None = None + self._old_hook: Callable[[threading.ExceptHookArgs], Any] | None = None - def _hook(self, args: "threading.ExceptHookArgs") -> None: + def _hook(self, args: threading.ExceptHookArgs) -> None: self.args = args - def __enter__(self) -> "catch_threading_exception": + def __enter__(self) -> catch_threading_exception: self._old_hook = threading.excepthook threading.excepthook = self._hook return self def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: assert self._old_hook is not None threading.excepthook = self._old_hook @@ -64,15 +66,15 @@ def thread_exception_runtest_hook() -> Generator[None, None, None]: finally: if cm.args: thread_name = ( - "" if cm.args.thread is None else cm.args.thread.name + '' if cm.args.thread is None else cm.args.thread.name ) - msg = f"Exception in thread {thread_name}\n\n" - msg += "".join( + msg = f'Exception in thread {thread_name}\n\n' + msg += ''.join( traceback.format_exception( cm.args.exc_type, cm.args.exc_value, cm.args.exc_traceback, - ) + ), ) warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) diff --git a/.venv/lib/python3.10/site-packages/_pytest/timing.py b/.venv/lib/python3.10/site-packages/_pytest/timing.py index 0541dc8..1596dda 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/timing.py +++ b/.venv/lib/python3.10/site-packages/_pytest/timing.py @@ -5,10 +5,11 @@ pytest runtime information (issue #185). Fixture "mock_timing" also interacts with this module for pytest's own tests. """ +from __future__ import annotations from time import perf_counter from time import sleep from time import time -__all__ = ["perf_counter", "sleep", "time"] +__all__ = ['perf_counter', 'sleep', 'time'] diff --git a/.venv/lib/python3.10/site-packages/_pytest/tmpdir.py b/.venv/lib/python3.10/site-packages/_pytest/tmpdir.py index 1cb9fbb..6ba2000 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/tmpdir.py +++ b/.venv/lib/python3.10/site-packages/_pytest/tmpdir.py @@ -1,11 +1,13 @@ # mypy: allow-untyped-defs """Support for providing temporary directories to test functions.""" +from __future__ import annotations + import dataclasses import os -from pathlib import Path import re -from shutil import rmtree import tempfile +from pathlib import Path +from shutil import rmtree from typing import Any from typing import Dict from typing import final @@ -14,11 +16,6 @@ from typing import Literal from typing import Optional from typing import Union -from .pathlib import cleanup_dead_symlinks -from .pathlib import LOCK_TIMEOUT -from .pathlib import make_numbered_dir -from .pathlib import make_numbered_dir_with_cleanup -from .pathlib import rm_rf from _pytest.compat import get_user_id from _pytest.config import Config from _pytest.config import ExitCode @@ -32,9 +29,15 @@ from _pytest.nodes import Item from _pytest.reports import TestReport from _pytest.stash import StashKey +from .pathlib import cleanup_dead_symlinks +from .pathlib import LOCK_TIMEOUT +from .pathlib import make_numbered_dir +from .pathlib import make_numbered_dir_with_cleanup +from .pathlib import rm_rf + tmppath_result_key = StashKey[Dict[str, bool]]() -RetentionType = Literal["all", "failed", "none"] +RetentionType = Literal['all', 'failed', 'none'] @final @@ -45,20 +48,20 @@ class TempPathFactory: The base directory can be configured using the ``--basetemp`` option. """ - _given_basetemp: Optional[Path] + _given_basetemp: Path | None # pluggy TagTracerSub, not currently exposed, so Any. _trace: Any - _basetemp: Optional[Path] + _basetemp: Path | None _retention_count: int _retention_policy: RetentionType def __init__( self, - given_basetemp: Optional[Path], + given_basetemp: Path | None, retention_count: int, retention_policy: RetentionType, trace, - basetemp: Optional[Path] = None, + basetemp: Path | None = None, *, _ispytest: bool = False, ) -> None: @@ -81,27 +84,27 @@ class TempPathFactory: config: Config, *, _ispytest: bool = False, - ) -> "TempPathFactory": + ) -> TempPathFactory: """Create a factory according to pytest configuration. :meta private: """ check_ispytest(_ispytest) - count = int(config.getini("tmp_path_retention_count")) + count = int(config.getini('tmp_path_retention_count')) if count < 0: raise ValueError( - f"tmp_path_retention_count must be >= 0. Current input: {count}." + f'tmp_path_retention_count must be >= 0. Current input: {count}.', ) - policy = config.getini("tmp_path_retention_policy") - if policy not in ("all", "failed", "none"): + policy = config.getini('tmp_path_retention_policy') + if policy not in ('all', 'failed', 'none'): raise ValueError( - f"tmp_path_retention_policy must be either all, failed, none. Current input: {policy}." + f'tmp_path_retention_policy must be either all, failed, none. Current input: {policy}.', ) return cls( given_basetemp=config.option.basetemp, - trace=config.trace.get("tmpdir"), + trace=config.trace.get('tmpdir'), retention_count=count, retention_policy=policy, _ispytest=True, @@ -110,7 +113,7 @@ class TempPathFactory: def _ensure_relative_to_basetemp(self, basename: str) -> str: basename = os.path.normpath(basename) if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): - raise ValueError(f"{basename} is not a normalized and relative path") + raise ValueError(f'{basename} is not a normalized and relative path') return basename def mktemp(self, basename: str, numbered: bool = True) -> Path: @@ -134,7 +137,7 @@ class TempPathFactory: p.mkdir(mode=0o700) else: p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) - self._trace("mktemp", p) + self._trace('mktemp', p) return p def getbasetemp(self) -> Path: @@ -153,17 +156,17 @@ class TempPathFactory: basetemp.mkdir(mode=0o700) basetemp = basetemp.resolve() else: - from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") + from_env = os.environ.get('PYTEST_DEBUG_TEMPROOT') temproot = Path(from_env or tempfile.gettempdir()).resolve() - user = get_user() or "unknown" + user = get_user() or 'unknown' # use a sub-directory in the temproot to speed-up # make_numbered_dir() call - rootdir = temproot.joinpath(f"pytest-of-{user}") + rootdir = temproot.joinpath(f'pytest-of-{user}') try: rootdir.mkdir(mode=0o700, exist_ok=True) except OSError: # getuser() likely returned illegal characters for the platform, use unknown back off mechanism - rootdir = temproot.joinpath("pytest-of-unknown") + rootdir = temproot.joinpath('pytest-of-unknown') rootdir.mkdir(mode=0o700, exist_ok=True) # Because we use exist_ok=True with a predictable name, make sure # we are the owners, to prevent any funny business (on unix, where @@ -176,16 +179,16 @@ class TempPathFactory: rootdir_stat = rootdir.stat() if rootdir_stat.st_uid != uid: raise OSError( - f"The temporary directory {rootdir} is not owned by the current user. " - "Fix this and try again." + f'The temporary directory {rootdir} is not owned by the current user. ' + 'Fix this and try again.', ) if (rootdir_stat.st_mode & 0o077) != 0: os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) keep = self._retention_count - if self._retention_policy == "none": + if self._retention_policy == 'none': keep = 0 basetemp = make_numbered_dir_with_cleanup( - prefix="pytest-", + prefix='pytest-', root=rootdir, keep=keep, lock_timeout=LOCK_TIMEOUT, @@ -193,11 +196,11 @@ class TempPathFactory: ) assert basetemp is not None, basetemp self._basetemp = basetemp - self._trace("new basetemp", basetemp) + self._trace('new basetemp', basetemp) return basetemp -def get_user() -> Optional[str]: +def get_user() -> str | None: """Return the current user name, or None if getuser() does not work in the current environment (see #1010).""" try: @@ -219,25 +222,25 @@ def pytest_configure(config: Config) -> None: mp = MonkeyPatch() config.add_cleanup(mp.undo) _tmp_path_factory = TempPathFactory.from_config(config, _ispytest=True) - mp.setattr(config, "_tmp_path_factory", _tmp_path_factory, raising=False) + mp.setattr(config, '_tmp_path_factory', _tmp_path_factory, raising=False) def pytest_addoption(parser: Parser) -> None: parser.addini( - "tmp_path_retention_count", - help="How many sessions should we keep the `tmp_path` directories, according to `tmp_path_retention_policy`.", + 'tmp_path_retention_count', + help='How many sessions should we keep the `tmp_path` directories, according to `tmp_path_retention_policy`.', default=3, ) parser.addini( - "tmp_path_retention_policy", - help="Controls which directories created by the `tmp_path` fixture are kept around, based on test outcome. " - "(all/failed/none)", - default="all", + 'tmp_path_retention_policy', + help='Controls which directories created by the `tmp_path` fixture are kept around, based on test outcome. ' + '(all/failed/none)', + default='all', ) -@fixture(scope="session") +@fixture(scope='session') def tmp_path_factory(request: FixtureRequest) -> TempPathFactory: """Return a :class:`pytest.TempPathFactory` instance for the test session.""" # Set dynamically by pytest_configure() above. @@ -246,7 +249,7 @@ def tmp_path_factory(request: FixtureRequest) -> TempPathFactory: def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: name = request.node.name - name = re.sub(r"[\W]", "_", name) + name = re.sub(r'[\W]', '_', name) MAXVAL = 30 name = name[:MAXVAL] return factory.mktemp(name, numbered=True) @@ -254,7 +257,7 @@ def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: @fixture def tmp_path( - request: FixtureRequest, tmp_path_factory: TempPathFactory + request: FixtureRequest, tmp_path_factory: TempPathFactory, ) -> Generator[Path, None, None]: """Return a temporary directory path object which is unique to each test function invocation, created as a sub directory of the base temporary @@ -277,7 +280,7 @@ def tmp_path( policy = tmp_path_factory._retention_policy result_dict = request.node.stash[tmppath_result_key] - if policy == "failed" and result_dict.get("call", True): + if policy == 'failed' and result_dict.get('call', True): # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, # permissions, etc, in which case we ignore it. rmtree(path, ignore_errors=True) @@ -285,7 +288,7 @@ def tmp_path( del request.node.stash[tmppath_result_key] -def pytest_sessionfinish(session, exitstatus: Union[int, ExitCode]): +def pytest_sessionfinish(session, exitstatus: int | ExitCode): """After each session, remove base directory if all the tests passed, the policy is "failed", and the basetemp is not specified by a user. """ @@ -296,9 +299,9 @@ def pytest_sessionfinish(session, exitstatus: Union[int, ExitCode]): policy = tmp_path_factory._retention_policy if ( - exitstatus == 0 - and policy == "failed" - and tmp_path_factory._given_basetemp is None + exitstatus == 0 and + policy == 'failed' and + tmp_path_factory._given_basetemp is None ): if basetemp.is_dir(): # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, @@ -312,10 +315,10 @@ def pytest_sessionfinish(session, exitstatus: Union[int, ExitCode]): @hookimpl(wrapper=True, tryfirst=True) def pytest_runtest_makereport( - item: Item, call + item: Item, call, ) -> Generator[None, TestReport, TestReport]: rep = yield assert rep.when is not None - empty: Dict[str, bool] = {} + empty: dict[str, bool] = {} item.stash.setdefault(tmppath_result_key, empty)[rep.when] = rep.passed return rep diff --git a/.venv/lib/python3.10/site-packages/_pytest/unittest.py b/.venv/lib/python3.10/site-packages/_pytest/unittest.py index 2b79665..3f9ad76 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/unittest.py +++ b/.venv/lib/python3.10/site-packages/_pytest/unittest.py @@ -1,5 +1,7 @@ # mypy: allow-untyped-defs """Discover and run std-library "unittest" style tests.""" +from __future__ import annotations + import sys import traceback import types @@ -15,6 +17,7 @@ from typing import TYPE_CHECKING from typing import Union import _pytest._code +import pytest from _pytest.compat import getimfunc from _pytest.compat import is_async_function from _pytest.config import hookimpl @@ -29,7 +32,6 @@ from _pytest.python import Class from _pytest.python import Function from _pytest.python import Module from _pytest.runner import CallInfo -import pytest if TYPE_CHECKING: @@ -44,11 +46,11 @@ if TYPE_CHECKING: def pytest_pycollect_makeitem( - collector: Union[Module, Class], name: str, obj: object -) -> Optional["UnitTestCase"]: + collector: Module | Class, name: str, obj: object, +) -> UnitTestCase | None: # Has unittest been imported and is obj a subclass of its TestCase? try: - ut = sys.modules["unittest"] + ut = sys.modules['unittest'] # Type ignored because `ut` is an opaque module. if not issubclass(obj, ut.TestCase): # type: ignore return None @@ -63,11 +65,11 @@ class UnitTestCase(Class): # to declare that our children do not support funcargs. nofuncargs = True - def collect(self) -> Iterable[Union[Item, Collector]]: + def collect(self) -> Iterable[Item | Collector]: from unittest import TestLoader cls = self.obj - if not getattr(cls, "__test__", True): + if not getattr(cls, '__test__', True): return skipped = _is_skipped(cls) @@ -81,28 +83,28 @@ class UnitTestCase(Class): foundsomething = False for name in loader.getTestCaseNames(self.obj): x = getattr(self.obj, name) - if not getattr(x, "__test__", True): + if not getattr(x, '__test__', True): continue funcobj = getimfunc(x) yield TestCaseFunction.from_parent(self, name=name, callobj=funcobj) foundsomething = True if not foundsomething: - runtest = getattr(self.obj, "runTest", None) + runtest = getattr(self.obj, 'runTest', None) if runtest is not None: - ut = sys.modules.get("twisted.trial.unittest", None) + ut = sys.modules.get('twisted.trial.unittest', None) # Type ignored because `ut` is an opaque module. if ut is None or runtest != ut.TestCase.runTest: # type: ignore - yield TestCaseFunction.from_parent(self, name="runTest") + yield TestCaseFunction.from_parent(self, name='runTest') def _register_unittest_setup_class_fixture(self, cls: type) -> None: """Register an auto-use fixture to invoke setUpClass and tearDownClass (#517).""" - setup = getattr(cls, "setUpClass", None) - teardown = getattr(cls, "tearDownClass", None) + setup = getattr(cls, 'setUpClass', None) + teardown = getattr(cls, 'tearDownClass', None) if setup is None and teardown is None: return None - cleanup = getattr(cls, "doClassCleanups", lambda: None) + cleanup = getattr(cls, 'doClassCleanups', lambda: None) def unittest_setup_class_fixture( request: FixtureRequest, @@ -128,18 +130,18 @@ class UnitTestCase(Class): self.session._fixturemanager._register_fixture( # Use a unique name to speed up lookup. - name=f"_unittest_setUpClass_fixture_{cls.__qualname__}", + name=f'_unittest_setUpClass_fixture_{cls.__qualname__}', func=unittest_setup_class_fixture, nodeid=self.nodeid, - scope="class", + scope='class', autouse=True, ) def _register_unittest_setup_method_fixture(self, cls: type) -> None: """Register an auto-use fixture to invoke setup_method and teardown_method (#517).""" - setup = getattr(cls, "setup_method", None) - teardown = getattr(cls, "teardown_method", None) + setup = getattr(cls, 'setup_method', None) + teardown = getattr(cls, 'teardown_method', None) if setup is None and teardown is None: return None @@ -158,18 +160,18 @@ class UnitTestCase(Class): self.session._fixturemanager._register_fixture( # Use a unique name to speed up lookup. - name=f"_unittest_setup_method_fixture_{cls.__qualname__}", + name=f'_unittest_setup_method_fixture_{cls.__qualname__}', func=unittest_setup_method_fixture, nodeid=self.nodeid, - scope="function", + scope='function', autouse=True, ) class TestCaseFunction(Function): nofuncargs = True - _excinfo: Optional[List[_pytest._code.ExceptionInfo[BaseException]]] = None - _testcase: Optional["unittest.TestCase"] = None + _excinfo: list[_pytest._code.ExceptionInfo[BaseException]] | None = None + _testcase: unittest.TestCase | None = None def _getobj(self): assert self.parent is not None @@ -182,7 +184,7 @@ class TestCaseFunction(Function): def setup(self) -> None: # A bound method to be called during teardown() if set (see 'runtest()'). - self._explicit_tearDown: Optional[Callable[[], None]] = None + self._explicit_tearDown: Callable[[], None] | None = None assert self.parent is not None self._testcase = self.parent.obj(self.name) # type: ignore[attr-defined] self._obj = getattr(self._testcase, self.name) @@ -196,15 +198,15 @@ class TestCaseFunction(Function): self._testcase = None self._obj = None - def startTest(self, testcase: "unittest.TestCase") -> None: + def startTest(self, testcase: unittest.TestCase) -> None: pass - def _addexcinfo(self, rawexcinfo: "_SysExcInfoType") -> None: + def _addexcinfo(self, rawexcinfo: _SysExcInfoType) -> None: # Unwrap potential exception info (see twisted trial support below). - rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo) + rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo) try: excinfo = _pytest._code.ExceptionInfo[BaseException].from_exc_info( - rawexcinfo # type: ignore[arg-type] + rawexcinfo, # type: ignore[arg-type] ) # Invoke the attributes to trigger storing the traceback # trial causes some issue there. @@ -216,26 +218,26 @@ class TestCaseFunction(Function): values = traceback.format_exception(*rawexcinfo) values.insert( 0, - "NOTE: Incompatible Exception Representation, " - "displaying natively:\n\n", + 'NOTE: Incompatible Exception Representation, ' + 'displaying natively:\n\n', ) - fail("".join(values), pytrace=False) + fail(''.join(values), pytrace=False) except (fail.Exception, KeyboardInterrupt): raise except BaseException: fail( - "ERROR: Unknown Incompatible Exception " - f"representation:\n{rawexcinfo!r}", + 'ERROR: Unknown Incompatible Exception ' + f'representation:\n{rawexcinfo!r}', pytrace=False, ) except KeyboardInterrupt: raise except fail.Exception: excinfo = _pytest._code.ExceptionInfo.from_current() - self.__dict__.setdefault("_excinfo", []).append(excinfo) + self.__dict__.setdefault('_excinfo', []).append(excinfo) def addError( - self, testcase: "unittest.TestCase", rawexcinfo: "_SysExcInfoType" + self, testcase: unittest.TestCase, rawexcinfo: _SysExcInfoType, ) -> None: try: if isinstance(rawexcinfo[1], exit.Exception): @@ -245,11 +247,11 @@ class TestCaseFunction(Function): self._addexcinfo(rawexcinfo) def addFailure( - self, testcase: "unittest.TestCase", rawexcinfo: "_SysExcInfoType" + self, testcase: unittest.TestCase, rawexcinfo: _SysExcInfoType, ) -> None: self._addexcinfo(rawexcinfo) - def addSkip(self, testcase: "unittest.TestCase", reason: str) -> None: + def addSkip(self, testcase: unittest.TestCase, reason: str) -> None: try: raise pytest.skip.Exception(reason, _use_item_location=True) except skip.Exception: @@ -257,9 +259,9 @@ class TestCaseFunction(Function): def addExpectedFailure( self, - testcase: "unittest.TestCase", - rawexcinfo: "_SysExcInfoType", - reason: str = "", + testcase: unittest.TestCase, + rawexcinfo: _SysExcInfoType, + reason: str = '', ) -> None: try: xfail(str(reason)) @@ -268,25 +270,25 @@ class TestCaseFunction(Function): def addUnexpectedSuccess( self, - testcase: "unittest.TestCase", - reason: Optional["twisted.trial.unittest.Todo"] = None, + testcase: unittest.TestCase, + reason: twisted.trial.unittest.Todo | None = None, ) -> None: - msg = "Unexpected success" + msg = 'Unexpected success' if reason: - msg += f": {reason.reason}" + msg += f': {reason.reason}' # Preserve unittest behaviour - fail the test. Explicitly not an XPASS. try: fail(msg, pytrace=False) except fail.Exception: self._addexcinfo(sys.exc_info()) - def addSuccess(self, testcase: "unittest.TestCase") -> None: + def addSuccess(self, testcase: unittest.TestCase) -> None: pass - def stopTest(self, testcase: "unittest.TestCase") -> None: + def stopTest(self, testcase: unittest.TestCase) -> None: pass - def addDuration(self, testcase: "unittest.TestCase", elapsed: float) -> None: + def addDuration(self, testcase: unittest.TestCase, elapsed: float) -> None: pass def runtest(self) -> None: @@ -310,9 +312,9 @@ class TestCaseFunction(Function): # We need to consider if the test itself is skipped, or the whole class. assert isinstance(self.parent, UnitTestCase) skipped = _is_skipped(self.obj) or _is_skipped(self.parent.obj) - if self.config.getoption("usepdb") and not skipped: + if self.config.getoption('usepdb') and not skipped: self._explicit_tearDown = self._testcase.tearDown - setattr(self._testcase, "tearDown", lambda *args: None) + setattr(self._testcase, 'tearDown', lambda *args: None) # We need to update the actual bound method with self.obj, because # wrap_pytest_function_for_tracing replaces self.obj by a wrapper. @@ -323,11 +325,11 @@ class TestCaseFunction(Function): delattr(self._testcase, self.name) def _traceback_filter( - self, excinfo: _pytest._code.ExceptionInfo[BaseException] + self, excinfo: _pytest._code.ExceptionInfo[BaseException], ) -> _pytest._code.Traceback: traceback = super()._traceback_filter(excinfo) ntraceback = traceback.filter( - lambda x: not x.frame.f_globals.get("__unittest"), + lambda x: not x.frame.f_globals.get('__unittest'), ) if not ntraceback: ntraceback = traceback @@ -348,13 +350,13 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: # This is actually only needed for nose, which reuses unittest.SkipTest for # its own nose.SkipTest. For unittest TestCases, SkipTest is already # handled internally, and doesn't reach here. - unittest = sys.modules.get("unittest") + unittest = sys.modules.get('unittest') if ( unittest and call.excinfo and isinstance(call.excinfo.value, unittest.SkipTest) # type: ignore[attr-defined] ): excinfo = call.excinfo call2 = CallInfo[None].from_call( - lambda: pytest.skip(str(excinfo.value)), call.when + lambda: pytest.skip(str(excinfo.value)), call.when, ) call.excinfo = call2.excinfo @@ -365,8 +367,8 @@ classImplements_has_run = False @hookimpl(wrapper=True) def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: - if isinstance(item, TestCaseFunction) and "twisted.trial.unittest" in sys.modules: - ut: Any = sys.modules["twisted.python.failure"] + if isinstance(item, TestCaseFunction) and 'twisted.trial.unittest' in sys.modules: + ut: Any = sys.modules['twisted.python.failure'] global classImplements_has_run Failure__init__ = ut.Failure.__init__ if not classImplements_has_run: @@ -377,7 +379,7 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: classImplements_has_run = True def excstore( - self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None + self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None, ): if exc_value is None: self._rawexcinfo = sys.exc_info() @@ -387,7 +389,7 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: self._rawexcinfo = (exc_type, exc_value, exc_tb) try: Failure__init__( - self, exc_value, exc_type, exc_tb, captureVars=captureVars + self, exc_value, exc_type, exc_tb, captureVars=captureVars, ) except TypeError: Failure__init__(self, exc_value, exc_type, exc_tb) @@ -404,4 +406,4 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: def _is_skipped(obj) -> bool: """Return True if the given object has been marked with @unittest.skip.""" - return bool(getattr(obj, "__unittest_skip__", False)) + return bool(getattr(obj, '__unittest_skip__', False)) diff --git a/.venv/lib/python3.10/site-packages/_pytest/unraisableexception.py b/.venv/lib/python3.10/site-packages/_pytest/unraisableexception.py index f649267..0ae6e04 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/unraisableexception.py +++ b/.venv/lib/python3.10/site-packages/_pytest/unraisableexception.py @@ -1,12 +1,14 @@ +from __future__ import annotations + import sys import traceback +import warnings from types import TracebackType from typing import Any from typing import Callable from typing import Generator from typing import Optional from typing import Type -import warnings import pytest @@ -34,24 +36,24 @@ class catch_unraisable_exception: """ def __init__(self) -> None: - self.unraisable: Optional["sys.UnraisableHookArgs"] = None - self._old_hook: Optional[Callable[["sys.UnraisableHookArgs"], Any]] = None + self.unraisable: sys.UnraisableHookArgs | None = None + self._old_hook: Callable[[sys.UnraisableHookArgs], Any] | None = None - def _hook(self, unraisable: "sys.UnraisableHookArgs") -> None: + def _hook(self, unraisable: sys.UnraisableHookArgs) -> None: # Storing unraisable.object can resurrect an object which is being # finalized. Storing unraisable.exc_value creates a reference cycle. self.unraisable = unraisable - def __enter__(self) -> "catch_unraisable_exception": + def __enter__(self) -> catch_unraisable_exception: self._old_hook = sys.unraisablehook sys.unraisablehook = self._hook return self def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: assert self._old_hook is not None sys.unraisablehook = self._old_hook @@ -68,14 +70,14 @@ def unraisable_exception_runtest_hook() -> Generator[None, None, None]: if cm.unraisable.err_msg is not None: err_msg = cm.unraisable.err_msg else: - err_msg = "Exception ignored in" - msg = f"{err_msg}: {cm.unraisable.object!r}\n\n" - msg += "".join( + err_msg = 'Exception ignored in' + msg = f'{err_msg}: {cm.unraisable.object!r}\n\n' + msg += ''.join( traceback.format_exception( cm.unraisable.exc_type, cm.unraisable.exc_value, cm.unraisable.exc_traceback, - ) + ), ) warnings.warn(pytest.PytestUnraisableExceptionWarning(msg)) diff --git a/.venv/lib/python3.10/site-packages/_pytest/warning_types.py b/.venv/lib/python3.10/site-packages/_pytest/warning_types.py index a5884f2..2de538a 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/warning_types.py +++ b/.venv/lib/python3.10/site-packages/_pytest/warning_types.py @@ -1,64 +1,66 @@ +from __future__ import annotations + import dataclasses import inspect +import warnings from types import FunctionType from typing import Any from typing import final from typing import Generic from typing import Type from typing import TypeVar -import warnings class PytestWarning(UserWarning): """Base class for all warnings emitted by pytest.""" - __module__ = "pytest" + __module__ = 'pytest' @final class PytestAssertRewriteWarning(PytestWarning): """Warning emitted by the pytest assert rewrite module.""" - __module__ = "pytest" + __module__ = 'pytest' @final class PytestCacheWarning(PytestWarning): """Warning emitted by the cache plugin in various situations.""" - __module__ = "pytest" + __module__ = 'pytest' @final class PytestConfigWarning(PytestWarning): """Warning emitted for configuration issues.""" - __module__ = "pytest" + __module__ = 'pytest' @final class PytestCollectionWarning(PytestWarning): """Warning emitted when pytest is not able to collect a file or symbol in a module.""" - __module__ = "pytest" + __module__ = 'pytest' class PytestDeprecationWarning(PytestWarning, DeprecationWarning): """Warning class for features that will be removed in a future version.""" - __module__ = "pytest" + __module__ = 'pytest' class PytestRemovedIn9Warning(PytestDeprecationWarning): """Warning class for features that will be removed in pytest 9.""" - __module__ = "pytest" + __module__ = 'pytest' class PytestReturnNotNoneWarning(PytestWarning): """Warning emitted when a test function is returning value other than None.""" - __module__ = "pytest" + __module__ = 'pytest' @final @@ -69,11 +71,11 @@ class PytestExperimentalApiWarning(PytestWarning, FutureWarning): future version. """ - __module__ = "pytest" + __module__ = 'pytest' @classmethod - def simple(cls, apiname: str) -> "PytestExperimentalApiWarning": - return cls(f"{apiname} is an experimental api that may change over time") + def simple(cls, apiname: str) -> PytestExperimentalApiWarning: + return cls(f'{apiname} is an experimental api that may change over time') @final @@ -85,7 +87,7 @@ class PytestUnhandledCoroutineWarning(PytestReturnNotNoneWarning): Coroutine test functions are not natively supported. """ - __module__ = "pytest" + __module__ = 'pytest' @final @@ -95,7 +97,7 @@ class PytestUnknownMarkWarning(PytestWarning): See :ref:`mark` for details. """ - __module__ = "pytest" + __module__ = 'pytest' @final @@ -107,7 +109,7 @@ class PytestUnraisableExceptionWarning(PytestWarning): as normal. """ - __module__ = "pytest" + __module__ = 'pytest' @final @@ -117,10 +119,10 @@ class PytestUnhandledThreadExceptionWarning(PytestWarning): Such exceptions don't propagate normally. """ - __module__ = "pytest" + __module__ = 'pytest' -_W = TypeVar("_W", bound=PytestWarning) +_W = TypeVar('_W', bound=PytestWarning) @final @@ -132,7 +134,7 @@ class UnformattedWarning(Generic[_W]): as opposed to a direct message. """ - category: Type["_W"] + category: type[_W] template: str def format(self, **kwargs: Any) -> _W: @@ -157,9 +159,9 @@ def warn_explicit_for(method: FunctionType, message: PytestWarning) -> None: type(message), filename=filename, module=module, - registry=mod_globals.setdefault("__warningregistry__", {}), + registry=mod_globals.setdefault('__warningregistry__', {}), lineno=lineno, ) except Warning as w: # If warnings are errors (e.g. -Werror), location information gets lost, so we add it to the message. - raise type(w)(f"{w}\n at {filename}:{lineno}") from None + raise type(w)(f'{w}\n at {filename}:{lineno}') from None diff --git a/.venv/lib/python3.10/site-packages/_pytest/warnings.py b/.venv/lib/python3.10/site-packages/_pytest/warnings.py index 2259089..6a30e19 100644 --- a/.venv/lib/python3.10/site-packages/_pytest/warnings.py +++ b/.venv/lib/python3.10/site-packages/_pytest/warnings.py @@ -1,25 +1,27 @@ # mypy: allow-untyped-defs -from contextlib import contextmanager +from __future__ import annotations + import sys +import warnings +from contextlib import contextmanager from typing import Generator from typing import Literal from typing import Optional -import warnings +import pytest from _pytest.config import apply_warning_filters from _pytest.config import Config from _pytest.config import parse_warning_filter from _pytest.main import Session from _pytest.nodes import Item from _pytest.terminal import TerminalReporter -import pytest def pytest_configure(config: Config) -> None: config.addinivalue_line( - "markers", - "filterwarnings(warning): add a warning filter to the given test. " - "see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings ", + 'markers', + 'filterwarnings(warning): add a warning filter to the given test. ' + 'see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings ', ) @@ -27,8 +29,8 @@ def pytest_configure(config: Config) -> None: def catch_warnings_for_item( config: Config, ihook, - when: Literal["config", "collect", "runtest"], - item: Optional[Item], + when: Literal['config', 'collect', 'runtest'], + item: Item | None, ) -> Generator[None, None, None]: """Context manager that catches warnings generated in the contained execution block. @@ -36,7 +38,7 @@ def catch_warnings_for_item( Each warning captured triggers the ``pytest_warning_recorded`` hook. """ - config_filters = config.getini("filterwarnings") + config_filters = config.getini('filterwarnings') cmdline_filters = config.known_args_namespace.pythonwarnings or [] with warnings.catch_warnings(record=True) as log: # mypy can't infer that record=True means log is not None; help it. @@ -44,8 +46,8 @@ def catch_warnings_for_item( if not sys.warnoptions: # If user is not explicitly configuring warning filters, show deprecation warnings by default (#2908). - warnings.filterwarnings("always", category=DeprecationWarning) - warnings.filterwarnings("always", category=PendingDeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) + warnings.filterwarnings('always', category=PendingDeprecationWarning) # To be enabled in pytest 9.0.0. # warnings.filterwarnings("error", category=pytest.PytestRemovedIn9Warning) @@ -53,9 +55,9 @@ def catch_warnings_for_item( apply_warning_filters(config_filters, cmdline_filters) # apply filters from "filterwarnings" marks - nodeid = "" if item is None else item.nodeid + nodeid = '' if item is None else item.nodeid if item is not None: - for mark in item.iter_markers(name="filterwarnings"): + for mark in item.iter_markers(name='filterwarnings'): for arg in mark.args: warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) @@ -69,7 +71,7 @@ def catch_warnings_for_item( nodeid=nodeid, when=when, location=None, - ) + ), ) @@ -91,22 +93,22 @@ def warning_record_to_str(warning_message: warnings.WarningMessage) -> str: else: tb = tracemalloc.get_object_traceback(warning_message.source) if tb is not None: - formatted_tb = "\n".join(tb.format()) + formatted_tb = '\n'.join(tb.format()) # Use a leading new line to better separate the (large) output # from the traceback to the previous warning text. - msg += f"\nObject allocated at:\n{formatted_tb}" + msg += f'\nObject allocated at:\n{formatted_tb}' else: # No need for a leading new line. - url = "https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings" - msg += "Enable tracemalloc to get traceback where the object was allocated.\n" - msg += f"See {url} for more info." + url = 'https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings' + msg += 'Enable tracemalloc to get traceback where the object was allocated.\n' + msg += f'See {url} for more info.' return msg @pytest.hookimpl(wrapper=True, tryfirst=True) def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: with catch_warnings_for_item( - config=item.config, ihook=item.ihook, when="runtest", item=item + config=item.config, ihook=item.ihook, when='runtest', item=item, ): return (yield) @@ -115,7 +117,7 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: def pytest_collection(session: Session) -> Generator[None, object, object]: config = session.config with catch_warnings_for_item( - config=config, ihook=config.hook, when="collect", item=None + config=config, ihook=config.hook, when='collect', item=None, ): return (yield) @@ -126,7 +128,7 @@ def pytest_terminal_summary( ) -> Generator[None, None, None]: config = terminalreporter.config with catch_warnings_for_item( - config=config, ihook=config.hook, when="config", item=None + config=config, ihook=config.hook, when='config', item=None, ): return (yield) @@ -135,16 +137,16 @@ def pytest_terminal_summary( def pytest_sessionfinish(session: Session) -> Generator[None, None, None]: config = session.config with catch_warnings_for_item( - config=config, ihook=config.hook, when="config", item=None + config=config, ihook=config.hook, when='config', item=None, ): return (yield) @pytest.hookimpl(wrapper=True) def pytest_load_initial_conftests( - early_config: "Config", + early_config: Config, ) -> Generator[None, None, None]: with catch_warnings_for_item( - config=early_config, ihook=early_config.hook, when="config", item=None + config=early_config, ihook=early_config.hook, when='config', item=None, ): return (yield) diff --git a/.venv/lib/python3.10/site-packages/covdefaults-2.3.0.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/covdefaults-2.3.0.dist-info/WHEEL index 0b18a28..488b6b9 100644 --- a/.venv/lib/python3.10/site-packages/covdefaults-2.3.0.dist-info/WHEEL +++ b/.venv/lib/python3.10/site-packages/covdefaults-2.3.0.dist-info/WHEEL @@ -3,4 +3,3 @@ Generator: bdist_wheel (0.37.1) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any - diff --git a/.venv/lib/python3.10/site-packages/coverage-7.4.4.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/coverage-7.4.4.dist-info/WHEEL index 934924b..40d87cd 100644 --- a/.venv/lib/python3.10/site-packages/coverage-7.4.4.dist-info/WHEEL +++ b/.venv/lib/python3.10/site-packages/coverage-7.4.4.dist-info/WHEEL @@ -2,4 +2,3 @@ Wheel-Version: 1.0 Generator: bdist_wheel (0.43.0) Root-Is-Purelib: false Tag: cp310-cp310-macosx_10_9_x86_64 - diff --git a/.venv/lib/python3.10/site-packages/coverage/__init__.py b/.venv/lib/python3.10/site-packages/coverage/__init__.py index c3403d4..680071a 100644 --- a/.venv/lib/python3.10/site-packages/coverage/__init__.py +++ b/.venv/lib/python3.10/site-packages/coverage/__init__.py @@ -1,6 +1,5 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """ Code coverage measurement for Python. @@ -8,31 +7,22 @@ Ned Batchelder https://coverage.readthedocs.io """ - from __future__ import annotations +from coverage.control import Coverage as Coverage +from coverage.control import process_startup as process_startup +from coverage.data import CoverageData as CoverageData +from coverage.exceptions import CoverageException as CoverageException +from coverage.plugin import CoveragePlugin as CoveragePlugin +from coverage.plugin import FileReporter as FileReporter +from coverage.plugin import FileTracer as FileTracer +from coverage.version import __version__ as __version__ +from coverage.version import version_info as version_info # mypy's convention is that "import as" names are public from the module. # We import names as themselves to indicate that. Pylint sees it as pointless, # so disable its warning. # pylint: disable=useless-import-alias -from coverage.version import ( - __version__ as __version__, - version_info as version_info, -) - -from coverage.control import ( - Coverage as Coverage, - process_startup as process_startup, -) -from coverage.data import CoverageData as CoverageData -from coverage.exceptions import CoverageException as CoverageException -from coverage.plugin import ( - CoveragePlugin as CoveragePlugin, - FileReporter as FileReporter, - FileTracer as FileTracer, -) - # Backward compatibility. coverage = Coverage diff --git a/.venv/lib/python3.10/site-packages/coverage/__main__.py b/.venv/lib/python3.10/site-packages/coverage/__main__.py index ce2d8db..da13115 100644 --- a/.venv/lib/python3.10/site-packages/coverage/__main__.py +++ b/.venv/lib/python3.10/site-packages/coverage/__main__.py @@ -1,10 +1,9 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Coverage.py's main entry point.""" - from __future__ import annotations import sys + from coverage.cmdline import main sys.exit(main()) diff --git a/.venv/lib/python3.10/site-packages/coverage/annotate.py b/.venv/lib/python3.10/site-packages/coverage/annotate.py index 46a82a8..7c99885 100644 --- a/.venv/lib/python3.10/site-packages/coverage/annotate.py +++ b/.venv/lib/python3.10/site-packages/coverage/annotate.py @@ -1,17 +1,16 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Source file annotation for coverage.py.""" - from __future__ import annotations import os import re - -from typing import Iterable, TYPE_CHECKING +from typing import Iterable +from typing import TYPE_CHECKING from coverage.files import flat_rootname -from coverage.misc import ensure_dir, isolate_module +from coverage.misc import ensure_dir +from coverage.misc import isolate_module from coverage.plugin import FileReporter from coverage.report_core import get_analysis_to_report from coverage.results import Analysis @@ -50,8 +49,8 @@ class AnnotateReporter: self.config = self.coverage.config self.directory: str | None = None - blank_re = re.compile(r"\s*(#|$)") - else_re = re.compile(r"\s*else\s*:\s*(#|$)") + blank_re = re.compile(r'\s*(#|$)') + else_re = re.compile(r'\s*else\s*:\s*(#|$)') def report(self, morfs: Iterable[TMorf] | None, directory: str | None = None) -> None: """Run the report. @@ -77,13 +76,13 @@ class AnnotateReporter: if self.directory: ensure_dir(self.directory) dest_file = os.path.join(self.directory, flat_rootname(fr.relative_filename())) - if dest_file.endswith("_py"): - dest_file = dest_file[:-3] + ".py" - dest_file += ",cover" + if dest_file.endswith('_py'): + dest_file = dest_file[:-3] + '.py' + dest_file += ',cover' else: - dest_file = fr.filename + ",cover" + dest_file = fr.filename + ',cover' - with open(dest_file, "w", encoding="utf-8") as dest: + with open(dest_file, 'w', encoding='utf-8') as dest: i = j = 0 covered = True source = fr.source() @@ -95,20 +94,20 @@ class AnnotateReporter: if i < len(statements) and statements[i] == lineno: covered = j >= len(missing) or missing[j] > lineno if self.blank_re.match(line): - dest.write(" ") + dest.write(' ') elif self.else_re.match(line): # Special logic for lines containing only "else:". if j >= len(missing): - dest.write("> ") + dest.write('> ') elif statements[i] == missing[j]: - dest.write("! ") + dest.write('! ') else: - dest.write("> ") + dest.write('> ') elif lineno in excluded: - dest.write("- ") + dest.write('- ') elif covered: - dest.write("> ") + dest.write('> ') else: - dest.write("! ") + dest.write('! ') dest.write(line) diff --git a/.venv/lib/python3.10/site-packages/coverage/bytecode.py b/.venv/lib/python3.10/site-packages/coverage/bytecode.py index 2cad4f9..1c67c6c 100644 --- a/.venv/lib/python3.10/site-packages/coverage/bytecode.py +++ b/.venv/lib/python3.10/site-packages/coverage/bytecode.py @@ -1,8 +1,6 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Bytecode manipulation for coverage.py""" - from __future__ import annotations from types import CodeType diff --git a/.venv/lib/python3.10/site-packages/coverage/cmdline.py b/.venv/lib/python3.10/site-packages/coverage/cmdline.py index 463ea8f..41b074f 100644 --- a/.venv/lib/python3.10/site-packages/coverage/cmdline.py +++ b/.venv/lib/python3.10/site-packages/coverage/cmdline.py @@ -1,20 +1,18 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Command-line support for coverage.py.""" - from __future__ import annotations import glob import optparse # pylint: disable=deprecated-module -import os import os.path import shlex import sys import textwrap import traceback - -from typing import cast, Any, NoReturn +from typing import Any +from typing import cast +from typing import NoReturn import coverage from coverage import Coverage @@ -22,16 +20,23 @@ from coverage import env from coverage.collector import HAS_CTRACER from coverage.config import CoverageConfig from coverage.control import DEFAULT_DATAFILE -from coverage.data import combinable_files, debug_data_file -from coverage.debug import info_header, short_stack, write_formatted_info -from coverage.exceptions import _BaseCoverageException, _ExceptionDuringRun, NoSource +from coverage.data import combinable_files +from coverage.data import debug_data_file +from coverage.debug import info_header +from coverage.debug import short_stack +from coverage.debug import write_formatted_info +from coverage.exceptions import _BaseCoverageException +from coverage.exceptions import _ExceptionDuringRun +from coverage.exceptions import NoSource from coverage.execfile import PyRunner -from coverage.results import Numbers, should_fail_under +from coverage.results import Numbers +from coverage.results import should_fail_under from coverage.version import __url__ # When adding to this file, alphabetization is important. Look for # "alphabetize" comments throughout. + class Opts: """A namespace class for individual options we'll build parsers from.""" @@ -39,193 +44,193 @@ class Opts: # appears on the command line. append = optparse.make_option( - "-a", "--append", action="store_true", - help="Append coverage data to .coverage, otherwise it starts clean each time.", + '-a', '--append', action='store_true', + help='Append coverage data to .coverage, otherwise it starts clean each time.', ) branch = optparse.make_option( - "", "--branch", action="store_true", - help="Measure branch coverage in addition to statement coverage.", + '', '--branch', action='store_true', + help='Measure branch coverage in addition to statement coverage.', ) concurrency = optparse.make_option( - "", "--concurrency", action="store", metavar="LIBS", + '', '--concurrency', action='store', metavar='LIBS', help=( - "Properly measure code using a concurrency library. " + - "Valid values are: {}, or a comma-list of them." - ).format(", ".join(sorted(CoverageConfig.CONCURRENCY_CHOICES))), + 'Properly measure code using a concurrency library. ' + + 'Valid values are: {}, or a comma-list of them.' + ).format(', '.join(sorted(CoverageConfig.CONCURRENCY_CHOICES))), ) context = optparse.make_option( - "", "--context", action="store", metavar="LABEL", - help="The context label to record for this coverage run.", + '', '--context', action='store', metavar='LABEL', + help='The context label to record for this coverage run.', ) contexts = optparse.make_option( - "", "--contexts", action="store", metavar="REGEX1,REGEX2,...", + '', '--contexts', action='store', metavar='REGEX1,REGEX2,...', help=( - "Only display data from lines covered in the given contexts. " + - "Accepts Python regexes, which must be quoted." + 'Only display data from lines covered in the given contexts. ' + + 'Accepts Python regexes, which must be quoted.' ), ) datafile = optparse.make_option( - "", "--data-file", action="store", metavar="DATAFILE", + '', '--data-file', action='store', metavar='DATAFILE', help=( - "Base name of the data files to operate on. " + + 'Base name of the data files to operate on. ' + "Defaults to '.coverage'. [env: COVERAGE_FILE]" ), ) datafle_input = optparse.make_option( - "", "--data-file", action="store", metavar="INFILE", + '', '--data-file', action='store', metavar='INFILE', help=( - "Read coverage data for report generation from this file. " + + 'Read coverage data for report generation from this file. ' + "Defaults to '.coverage'. [env: COVERAGE_FILE]" ), ) datafile_output = optparse.make_option( - "", "--data-file", action="store", metavar="OUTFILE", + '', '--data-file', action='store', metavar='OUTFILE', help=( - "Write the recorded coverage data to this file. " + + 'Write the recorded coverage data to this file. ' + "Defaults to '.coverage'. [env: COVERAGE_FILE]" ), ) debug = optparse.make_option( - "", "--debug", action="store", metavar="OPTS", - help="Debug options, separated by commas. [env: COVERAGE_DEBUG]", + '', '--debug', action='store', metavar='OPTS', + help='Debug options, separated by commas. [env: COVERAGE_DEBUG]', ) directory = optparse.make_option( - "-d", "--directory", action="store", metavar="DIR", - help="Write the output files to DIR.", + '-d', '--directory', action='store', metavar='DIR', + help='Write the output files to DIR.', ) fail_under = optparse.make_option( - "", "--fail-under", action="store", metavar="MIN", type="float", - help="Exit with a status of 2 if the total coverage is less than MIN.", + '', '--fail-under', action='store', metavar='MIN', type='float', + help='Exit with a status of 2 if the total coverage is less than MIN.', ) format = optparse.make_option( - "", "--format", action="store", metavar="FORMAT", - help="Output format, either text (default), markdown, or total.", + '', '--format', action='store', metavar='FORMAT', + help='Output format, either text (default), markdown, or total.', ) help = optparse.make_option( - "-h", "--help", action="store_true", - help="Get help on this command.", + '-h', '--help', action='store_true', + help='Get help on this command.', ) ignore_errors = optparse.make_option( - "-i", "--ignore-errors", action="store_true", - help="Ignore errors while reading source files.", + '-i', '--ignore-errors', action='store_true', + help='Ignore errors while reading source files.', ) include = optparse.make_option( - "", "--include", action="store", metavar="PAT1,PAT2,...", + '', '--include', action='store', metavar='PAT1,PAT2,...', help=( - "Include only files whose paths match one of these patterns. " + - "Accepts shell-style wildcards, which must be quoted." + 'Include only files whose paths match one of these patterns. ' + + 'Accepts shell-style wildcards, which must be quoted.' ), ) keep = optparse.make_option( - "", "--keep", action="store_true", - help="Keep original coverage files, otherwise they are deleted.", + '', '--keep', action='store_true', + help='Keep original coverage files, otherwise they are deleted.', ) pylib = optparse.make_option( - "-L", "--pylib", action="store_true", + '-L', '--pylib', action='store_true', help=( - "Measure coverage even inside the Python installed library, " + + 'Measure coverage even inside the Python installed library, ' + "which isn't done by default." ), ) show_missing = optparse.make_option( - "-m", "--show-missing", action="store_true", + '-m', '--show-missing', action='store_true', help="Show line numbers of statements in each module that weren't executed.", ) module = optparse.make_option( - "-m", "--module", action="store_true", + '-m', '--module', action='store_true', help=( - " is an importable Python module, not a script path, " + + ' is an importable Python module, not a script path, ' + "to be run as 'python -m' would run it." ), ) omit = optparse.make_option( - "", "--omit", action="store", metavar="PAT1,PAT2,...", + '', '--omit', action='store', metavar='PAT1,PAT2,...', help=( - "Omit files whose paths match one of these patterns. " + - "Accepts shell-style wildcards, which must be quoted." + 'Omit files whose paths match one of these patterns. ' + + 'Accepts shell-style wildcards, which must be quoted.' ), ) output_xml = optparse.make_option( - "-o", "", action="store", dest="outfile", metavar="OUTFILE", + '-o', '', action='store', dest='outfile', metavar='OUTFILE', help="Write the XML report to this file. Defaults to 'coverage.xml'", ) output_json = optparse.make_option( - "-o", "", action="store", dest="outfile", metavar="OUTFILE", + '-o', '', action='store', dest='outfile', metavar='OUTFILE', help="Write the JSON report to this file. Defaults to 'coverage.json'", ) output_lcov = optparse.make_option( - "-o", "", action="store", dest="outfile", metavar="OUTFILE", + '-o', '', action='store', dest='outfile', metavar='OUTFILE', help="Write the LCOV report to this file. Defaults to 'coverage.lcov'", ) json_pretty_print = optparse.make_option( - "", "--pretty-print", action="store_true", - help="Format the JSON for human readers.", + '', '--pretty-print', action='store_true', + help='Format the JSON for human readers.', ) parallel_mode = optparse.make_option( - "-p", "--parallel-mode", action="store_true", + '-p', '--parallel-mode', action='store_true', help=( - "Append the machine name, process id and random number to the " + - "data file name to simplify collecting data from " + - "many processes." + 'Append the machine name, process id and random number to the ' + + 'data file name to simplify collecting data from ' + + 'many processes.' ), ) precision = optparse.make_option( - "", "--precision", action="store", metavar="N", type=int, + '', '--precision', action='store', metavar='N', type=int, help=( - "Number of digits after the decimal point to display for " + - "reported coverage percentages." + 'Number of digits after the decimal point to display for ' + + 'reported coverage percentages.' ), ) quiet = optparse.make_option( - "-q", "--quiet", action="store_true", + '-q', '--quiet', action='store_true', help="Don't print messages about what is happening.", ) rcfile = optparse.make_option( - "", "--rcfile", action="store", + '', '--rcfile', action='store', help=( - "Specify configuration file. " + + 'Specify configuration file. ' + "By default '.coveragerc', 'setup.cfg', 'tox.ini', and " + "'pyproject.toml' are tried. [env: COVERAGE_RCFILE]" ), ) show_contexts = optparse.make_option( - "--show-contexts", action="store_true", - help="Show contexts for covered lines.", + '--show-contexts', action='store_true', + help='Show contexts for covered lines.', ) skip_covered = optparse.make_option( - "--skip-covered", action="store_true", - help="Skip files with 100% coverage.", + '--skip-covered', action='store_true', + help='Skip files with 100% coverage.', ) no_skip_covered = optparse.make_option( - "--no-skip-covered", action="store_false", dest="skip_covered", - help="Disable --skip-covered.", + '--no-skip-covered', action='store_false', dest='skip_covered', + help='Disable --skip-covered.', ) skip_empty = optparse.make_option( - "--skip-empty", action="store_true", - help="Skip files with no code.", + '--skip-empty', action='store_true', + help='Skip files with no code.', ) sort = optparse.make_option( - "--sort", action="store", metavar="COLUMN", + '--sort', action='store', metavar='COLUMN', help=( - "Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. " + - "Default is name." + 'Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. ' + + 'Default is name.' ), ) source = optparse.make_option( - "", "--source", action="store", metavar="SRC1,SRC2,...", - help="A list of directories or importable names of code to measure.", + '', '--source', action='store', metavar='SRC1,SRC2,...', + help='A list of directories or importable names of code to measure.', ) timid = optparse.make_option( - "", "--timid", action="store_true", - help="Use the slower Python trace function core.", + '', '--timid', action='store_true', + help='Use the slower Python trace function core.', ) title = optparse.make_option( - "", "--title", action="store", metavar="TITLE", - help="A text string to use as the title on the HTML.", + '', '--title', action='store', metavar='TITLE', + help='A text string to use as the title on the HTML.', ) version = optparse.make_option( - "", "--version", action="store_true", - help="Display version information and exit.", + '', '--version', action='store_true', + help='Display version information and exit.', ) @@ -238,7 +243,7 @@ class CoverageOptionParser(optparse.OptionParser): """ def __init__(self, *args: Any, **kwargs: Any) -> None: - kwargs["add_help_option"] = False + kwargs['add_help_option'] = False super().__init__(*args, **kwargs) self.set_defaults( # Keep these arguments alphabetized by their names. @@ -330,7 +335,7 @@ class CmdOptionParser(CoverageOptionParser): """ if usage: - usage = "%prog " + usage + usage = '%prog ' + usage super().__init__( usage=usage, description=description, @@ -342,7 +347,7 @@ class CmdOptionParser(CoverageOptionParser): def __eq__(self, other: str) -> bool: # type: ignore[override] # A convenience equality, so that I can put strings in unit test # results, and they will compare equal to objects. - return (other == f"") + return (other == f'') __hash__ = None # type: ignore[assignment] @@ -351,7 +356,7 @@ class CmdOptionParser(CoverageOptionParser): program_name = super().get_prog_name() # Include the sub-command for this parser as part of the command. - return f"{program_name} {self.cmd}" + return f'{program_name} {self.cmd}' # In lists of Opts, keep them alphabetized by the option names as they appear # on the command line, since these lists determine the order of the options in @@ -359,6 +364,7 @@ class CmdOptionParser(CoverageOptionParser): # # In COMMANDS, keep the keys (command names) alphabetized. + GLOBAL_ARGS = [ Opts.debug, Opts.help, @@ -366,72 +372,72 @@ GLOBAL_ARGS = [ ] COMMANDS = { - "annotate": CmdOptionParser( - "annotate", + 'annotate': CmdOptionParser( + 'annotate', [ Opts.directory, Opts.datafle_input, Opts.ignore_errors, Opts.include, Opts.omit, - ] + GLOBAL_ARGS, - usage="[options] [modules]", + ] + GLOBAL_ARGS, + usage='[options] [modules]', description=( - "Make annotated copies of the given files, marking statements that are executed " + - "with > and statements that are missed with !." + 'Make annotated copies of the given files, marking statements that are executed ' + + 'with > and statements that are missed with !.' ), ), - "combine": CmdOptionParser( - "combine", + 'combine': CmdOptionParser( + 'combine', [ Opts.append, Opts.datafile, Opts.keep, Opts.quiet, - ] + GLOBAL_ARGS, - usage="[options] ... ", + ] + GLOBAL_ARGS, + usage='[options] ... ', description=( - "Combine data from multiple coverage files. " + - "The combined results are written to a single " + - "file representing the union of the data. The positional " + - "arguments are data files or directories containing data files. " + + 'Combine data from multiple coverage files. ' + + 'The combined results are written to a single ' + + 'file representing the union of the data. The positional ' + + 'arguments are data files or directories containing data files. ' + "If no paths are provided, data files in the default data file's " + - "directory are combined." + 'directory are combined.' ), ), - "debug": CmdOptionParser( - "debug", GLOBAL_ARGS, - usage="", + 'debug': CmdOptionParser( + 'debug', GLOBAL_ARGS, + usage='', description=( - "Display information about the internals of coverage.py, " + - "for diagnosing problems. " + - "Topics are: " + - "'data' to show a summary of the collected data; " + - "'sys' to show installation information; " + - "'config' to show the configuration; " + - "'premain' to show what is calling coverage; " + - "'pybehave' to show internal flags describing Python behavior." + 'Display information about the internals of coverage.py, ' + + 'for diagnosing problems. ' + + 'Topics are: ' + + "'data' to show a summary of the collected data; " + + "'sys' to show installation information; " + + "'config' to show the configuration; " + + "'premain' to show what is calling coverage; " + + "'pybehave' to show internal flags describing Python behavior." ), ), - "erase": CmdOptionParser( - "erase", + 'erase': CmdOptionParser( + 'erase', [ Opts.datafile, - ] + GLOBAL_ARGS, - description="Erase previously collected coverage data.", + ] + GLOBAL_ARGS, + description='Erase previously collected coverage data.', ), - "help": CmdOptionParser( - "help", GLOBAL_ARGS, - usage="[command]", - description="Describe how to use coverage.py", + 'help': CmdOptionParser( + 'help', GLOBAL_ARGS, + usage='[command]', + description='Describe how to use coverage.py', ), - "html": CmdOptionParser( - "html", + 'html': CmdOptionParser( + 'html', [ Opts.contexts, Opts.directory, @@ -447,17 +453,17 @@ COMMANDS = { Opts.no_skip_covered, Opts.skip_empty, Opts.title, - ] + GLOBAL_ARGS, - usage="[options] [modules]", + ] + GLOBAL_ARGS, + usage='[options] [modules]', description=( - "Create an HTML report of the coverage of the files. " + - "Each file gets its own page, with the source decorated to show " + - "executed, excluded, and missed lines." + 'Create an HTML report of the coverage of the files. ' + + 'Each file gets its own page, with the source decorated to show ' + + 'executed, excluded, and missed lines.' ), ), - "json": CmdOptionParser( - "json", + 'json': CmdOptionParser( + 'json', [ Opts.contexts, Opts.datafle_input, @@ -469,13 +475,13 @@ COMMANDS = { Opts.json_pretty_print, Opts.quiet, Opts.show_contexts, - ] + GLOBAL_ARGS, - usage="[options] [modules]", - description="Generate a JSON report of coverage results.", + ] + GLOBAL_ARGS, + usage='[options] [modules]', + description='Generate a JSON report of coverage results.', ), - "lcov": CmdOptionParser( - "lcov", + 'lcov': CmdOptionParser( + 'lcov', [ Opts.datafle_input, Opts.fail_under, @@ -484,13 +490,13 @@ COMMANDS = { Opts.output_lcov, Opts.omit, Opts.quiet, - ] + GLOBAL_ARGS, - usage="[options] [modules]", - description="Generate an LCOV report of coverage results.", + ] + GLOBAL_ARGS, + usage='[options] [modules]', + description='Generate an LCOV report of coverage results.', ), - "report": CmdOptionParser( - "report", + 'report': CmdOptionParser( + 'report', [ Opts.contexts, Opts.datafle_input, @@ -505,13 +511,13 @@ COMMANDS = { Opts.skip_covered, Opts.no_skip_covered, Opts.skip_empty, - ] + GLOBAL_ARGS, - usage="[options] [modules]", - description="Report coverage statistics on modules.", + ] + GLOBAL_ARGS, + usage='[options] [modules]', + description='Report coverage statistics on modules.', ), - "run": CmdOptionParser( - "run", + 'run': CmdOptionParser( + 'run', [ Opts.append, Opts.branch, @@ -525,13 +531,13 @@ COMMANDS = { Opts.parallel_mode, Opts.source, Opts.timid, - ] + GLOBAL_ARGS, - usage="[options] [program options]", - description="Run a Python program, measuring code execution.", + ] + GLOBAL_ARGS, + usage='[options] [program options]', + description='Run a Python program, measuring code execution.', ), - "xml": CmdOptionParser( - "xml", + 'xml': CmdOptionParser( + 'xml', [ Opts.datafle_input, Opts.fail_under, @@ -541,9 +547,9 @@ COMMANDS = { Opts.output_xml, Opts.quiet, Opts.skip_empty, - ] + GLOBAL_ARGS, - usage="[options] [modules]", - description="Generate an XML report of coverage results.", + ] + GLOBAL_ARGS, + usage='[options] [modules]', + description='Generate an XML report of coverage results.', ), } @@ -557,7 +563,7 @@ def show_help( assert error or topic or parser program_path = sys.argv[0] - if program_path.endswith(os.path.sep + "__main__.py"): + if program_path.endswith(os.path.sep + '__main__.py'): # The path is the main module of a package; get that path instead. program_path = os.path.dirname(program_path) program_name = os.path.basename(program_path) @@ -567,17 +573,17 @@ def show_help( # invoke coverage-script.py, coverage3-script.py, and # coverage-3.5-script.py. argv[0] is the .py file, but we want to # get back to the original form. - auto_suffix = "-script.py" + auto_suffix = '-script.py' if program_name.endswith(auto_suffix): program_name = program_name[:-len(auto_suffix)] help_params = dict(coverage.__dict__) - help_params["__url__"] = __url__ - help_params["program_name"] = program_name + help_params['__url__'] = __url__ + help_params['program_name'] = program_name if HAS_CTRACER: - help_params["extension_modifier"] = "with C extension" + help_params['extension_modifier'] = 'with C extension' else: - help_params["extension_modifier"] = "without C extension" + help_params['extension_modifier'] = 'without C extension' if error: print(error, file=sys.stderr) @@ -587,12 +593,12 @@ def show_help( print() else: assert topic is not None - help_msg = textwrap.dedent(HELP_TOPICS.get(topic, "")).strip() + help_msg = textwrap.dedent(HELP_TOPICS.get(topic, '')).strip() if help_msg: print(help_msg.format(**help_params)) else: print(f"Don't know topic {topic!r}") - print("Full documentation is at {__url__}".format(**help_params)) + print('Full documentation is at {__url__}'.format(**help_params)) OK, ERR, FAIL_UNDER = 0, 1, 2 @@ -615,19 +621,19 @@ class CoverageScript: """ # Collect the command-line options. if not argv: - show_help(topic="minimum_help") + show_help(topic='minimum_help') return OK # The command syntax we parse depends on the first argument. Global # switch syntax always starts with an option. parser: optparse.OptionParser | None - self.global_option = argv[0].startswith("-") + self.global_option = argv[0].startswith('-') if self.global_option: parser = GlobalOptionParser() else: parser = COMMANDS.get(argv[0]) if not parser: - show_help(f"Unknown command: {argv[0]!r}") + show_help(f'Unknown command: {argv[0]!r}') return ERR argv = argv[1:] @@ -648,7 +654,7 @@ class CoverageScript: contexts = unshell_list(options.contexts) if options.concurrency is not None: - concurrency = options.concurrency.split(",") + concurrency = options.concurrency.split(',') else: concurrency = None @@ -670,17 +676,17 @@ class CoverageScript: messages=not options.quiet, ) - if options.action == "debug": + if options.action == 'debug': return self.do_debug(args) - elif options.action == "erase": + elif options.action == 'erase': self.coverage.erase() return OK - elif options.action == "run": + elif options.action == 'run': return self.do_run(options, args) - elif options.action == "combine": + elif options.action == 'combine': if options.append: self.coverage.load() data_paths = args or None @@ -699,12 +705,12 @@ class CoverageScript: # We need to be able to import from the current directory, because # plugins may try to, for example, to read Django settings. - sys.path.insert(0, "") + sys.path.insert(0, '') self.coverage.load() total = None - if options.action == "report": + if options.action == 'report': total = self.coverage.report( precision=options.precision, show_missing=options.show_missing, @@ -714,9 +720,9 @@ class CoverageScript: output_format=options.format, **report_args, ) - elif options.action == "annotate": + elif options.action == 'annotate': self.coverage.annotate(directory=options.directory, **report_args) - elif options.action == "html": + elif options.action == 'html': total = self.coverage.html_report( directory=options.directory, precision=options.precision, @@ -726,20 +732,20 @@ class CoverageScript: title=options.title, **report_args, ) - elif options.action == "xml": + elif options.action == 'xml': total = self.coverage.xml_report( outfile=options.outfile, skip_empty=options.skip_empty, **report_args, ) - elif options.action == "json": + elif options.action == 'json': total = self.coverage.json_report( outfile=options.outfile, pretty_print=options.pretty_print, show_contexts=options.show_contexts, **report_args, ) - elif options.action == "lcov": + elif options.action == 'lcov': total = self.coverage.lcov_report( outfile=options.outfile, **report_args, @@ -752,19 +758,19 @@ class CoverageScript: # Apply the command line fail-under options, and then use the config # value, so we can get fail_under from the config file. if options.fail_under is not None: - self.coverage.set_option("report:fail_under", options.fail_under) + self.coverage.set_option('report:fail_under', options.fail_under) if options.precision is not None: - self.coverage.set_option("report:precision", options.precision) + self.coverage.set_option('report:precision', options.precision) - fail_under = cast(float, self.coverage.get_option("report:fail_under")) - precision = cast(int, self.coverage.get_option("report:precision")) + fail_under = cast(float, self.coverage.get_option('report:fail_under')) + precision = cast(int, self.coverage.get_option('report:precision')) if should_fail_under(total, fail_under, precision): - msg = "total of {total} is less than fail-under={fail_under:.{p}f}".format( + msg = 'total of {total} is less than fail-under={fail_under:.{p}f}'.format( total=Numbers(precision=precision).display_covered(total), fail_under=fail_under, p=precision, ) - print("Coverage failure:", msg) + print('Coverage failure:', msg) return FAIL_UNDER return OK @@ -783,12 +789,12 @@ class CoverageScript: # Handle help. if options.help: if self.global_option: - show_help(topic="help") + show_help(topic='help') else: show_help(parser=parser) return True - if options.action == "help": + if options.action == 'help': if args: for a in args: parser_maybe = COMMANDS.get(a) @@ -797,12 +803,12 @@ class CoverageScript: else: show_help(topic=a) else: - show_help(topic="help") + show_help(topic='help') return True # Handle version. if options.version: - show_help(topic="version") + show_help(topic='version') return True return False @@ -813,37 +819,37 @@ class CoverageScript: if not args: if options.module: # Specified -m with nothing else. - show_help("No module specified for -m") + show_help('No module specified for -m') return ERR - command_line = cast(str, self.coverage.get_option("run:command_line")) + command_line = cast(str, self.coverage.get_option('run:command_line')) if command_line is not None: args = shlex.split(command_line) - if args and args[0] in {"-m", "--module"}: + if args and args[0] in {'-m', '--module'}: options.module = True args = args[1:] if not args: - show_help("Nothing to do.") + show_help('Nothing to do.') return ERR - if options.append and self.coverage.get_option("run:parallel"): + if options.append and self.coverage.get_option('run:parallel'): show_help("Can't append to data files in parallel mode.") return ERR - if options.concurrency == "multiprocessing": + if options.concurrency == 'multiprocessing': # Can't set other run-affecting command line options with # multiprocessing. - for opt_name in ["branch", "include", "omit", "pylib", "source", "timid"]: + for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']: # As it happens, all of these options have no default, meaning # they will be None if they have not been specified. if getattr(options, opt_name) is not None: show_help( - "Options affecting multiprocessing must only be specified " + - "in a configuration file.\n" + - f"Remove --{opt_name} from the command line.", + 'Options affecting multiprocessing must only be specified ' + + 'in a configuration file.\n' + + f'Remove --{opt_name} from the command line.', ) return ERR - os.environ["COVERAGE_RUN"] = "true" + os.environ['COVERAGE_RUN'] = 'true' runner = PyRunner(args, as_module=bool(options.module)) runner.prepare() @@ -870,28 +876,28 @@ class CoverageScript: """Implementation of 'coverage debug'.""" if not args: - show_help("What information would you like: config, data, sys, premain, pybehave?") + show_help('What information would you like: config, data, sys, premain, pybehave?') return ERR if args[1:]: - show_help("Only one topic at a time, please") + show_help('Only one topic at a time, please') return ERR - if args[0] == "sys": - write_formatted_info(print, "sys", self.coverage.sys_info()) - elif args[0] == "data": - print(info_header("data")) + if args[0] == 'sys': + write_formatted_info(print, 'sys', self.coverage.sys_info()) + elif args[0] == 'data': + print(info_header('data')) data_file = self.coverage.config.data_file debug_data_file(data_file) for filename in combinable_files(data_file): - print("-----") + print('-----') debug_data_file(filename) - elif args[0] == "config": - write_formatted_info(print, "config", self.coverage.config.debug_info()) - elif args[0] == "premain": - print(info_header("premain")) + elif args[0] == 'config': + write_formatted_info(print, 'config', self.coverage.config.debug_info()) + elif args[0] == 'premain': + print(info_header('premain')) print(short_stack(full=True)) - elif args[0] == "pybehave": - write_formatted_info(print, "pybehave", env.debug_info()) + elif args[0] == 'pybehave': + write_formatted_info(print, 'pybehave', env.debug_info()) else: show_help(f"Don't know what you mean by {args[0]!r}") return ERR @@ -910,7 +916,7 @@ def unshell_list(s: str) -> list[str] | None: # line, but (not) helpfully, the single quotes are included in the # argument, so we have to strip them off here. s = s.strip("'") - return s.split(",") + return s.split(',') def unglob_args(args: list[str]) -> list[str]: @@ -918,7 +924,7 @@ def unglob_args(args: list[str]) -> list[str]: if env.WINDOWS: globbed = [] for arg in args: - if "?" in arg or "*" in arg: + if '?' in arg or '*' in arg: globbed.extend(glob.glob(arg)) else: globbed.append(arg) @@ -927,7 +933,7 @@ def unglob_args(args: list[str]) -> list[str]: HELP_TOPICS = { - "help": """\ + 'help': """\ Coverage.py, version {__version__} {extension_modifier} Measure, collect, and report on code coverage in Python programs. @@ -949,12 +955,12 @@ HELP_TOPICS = { Use "{program_name} help " for detailed help on any command. """, - "minimum_help": ( - "Code coverage for Python, version {__version__} {extension_modifier}. " + + 'minimum_help': ( + 'Code coverage for Python, version {__version__} {extension_modifier}. ' + "Use '{program_name} help' for help." ), - "version": "Coverage.py, version {__version__} {extension_modifier}", + 'version': 'Coverage.py, version {__version__} {extension_modifier}', } @@ -987,11 +993,12 @@ def main(argv: list[str] | None = None) -> int | None: status = None return status + # Profiling using ox_profile. Install it from GitHub: # pip install git+https://github.com/emin63/ox_profile.git # # $set_env.py: COVERAGE_PROFILE - Set to use ox_profile. -_profile = os.getenv("COVERAGE_PROFILE") +_profile = os.getenv('COVERAGE_PROFILE') if _profile: # pragma: debugging from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error original_main = main @@ -1004,6 +1011,6 @@ if _profile: # pragma: debugging try: return original_main(argv) finally: - data, _ = profiler.query(re_filter="coverage", max_records=100) - print(profiler.show(query=data, limit=100, sep="", col="")) + data, _ = profiler.query(re_filter='coverage', max_records=100) + print(profiler.show(query=data, limit=100, sep='', col='')) profiler.cancel() diff --git a/.venv/lib/python3.10/site-packages/coverage/collector.py b/.venv/lib/python3.10/site-packages/coverage/collector.py index 9bd380c..f3e527e 100644 --- a/.venv/lib/python3.10/site-packages/coverage/collector.py +++ b/.venv/lib/python3.10/site-packages/coverage/collector.py @@ -1,18 +1,20 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Raw data collector for coverage.py.""" - from __future__ import annotations import functools import os import sys - from types import FrameType -from typing import ( - cast, Any, Callable, Dict, List, Mapping, Set, TypeVar, -) +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import List +from typing import Mapping +from typing import Set +from typing import TypeVar from coverage import env from coverage.config import CoverageConfig @@ -20,13 +22,17 @@ from coverage.data import CoverageData from coverage.debug import short_stack from coverage.disposition import FileDisposition from coverage.exceptions import ConfigError -from coverage.misc import human_sorted_items, isolate_module +from coverage.misc import human_sorted_items +from coverage.misc import isolate_module from coverage.plugin import CoveragePlugin from coverage.pytracer import PyTracer from coverage.sysmon import SysMonitor -from coverage.types import ( - TArc, TFileDisposition, TTraceData, TTraceFn, TracerCore, TWarnFn, -) +from coverage.types import TArc +from coverage.types import TFileDisposition +from coverage.types import TracerCore +from coverage.types import TTraceData +from coverage.types import TTraceFn +from coverage.types import TWarnFn os = isolate_module(os) @@ -37,7 +43,7 @@ try: HAS_CTRACER = True except ImportError: # Couldn't import the C extension, maybe it isn't built. - if os.getenv("COVERAGE_CORE") == "ctrace": # pragma: part covered + if os.getenv('COVERAGE_CORE') == 'ctrace': # pragma: part covered # During testing, we use the COVERAGE_CORE environment variable # to indicate that we've fiddled with the environment to test this # fallback code. If we thought we had a C tracer, but couldn't import @@ -48,7 +54,7 @@ except ImportError: sys.exit(1) HAS_CTRACER = False -T = TypeVar("T") +T = TypeVar('T') class Collector: @@ -73,7 +79,7 @@ class Collector: _collectors: list[Collector] = [] # The concurrency settings we support here. - LIGHT_THREADS = {"greenlet", "eventlet", "gevent"} + LIGHT_THREADS = {'greenlet', 'eventlet', 'gevent'} def __init__( self, @@ -130,7 +136,7 @@ class Collector: self.branch = branch self.warn = warn self.concurrency = concurrency - assert isinstance(self.concurrency, list), f"Expected a list: {self.concurrency!r}" + assert isinstance(self.concurrency, list), f'Expected a list: {self.concurrency!r}' self.pid = os.getpid() @@ -147,12 +153,12 @@ class Collector: core: str | None if timid: - core = "pytrace" + core = 'pytrace' else: - core = os.getenv("COVERAGE_CORE") + core = os.getenv('COVERAGE_CORE') - if core == "sysmon" and not env.PYBEHAVIOR.pep669: - self.warn("sys.monitoring isn't available, using default core", slug="no-sysmon") + if core == 'sysmon' and not env.PYBEHAVIOR.pep669: + self.warn("sys.monitoring isn't available, using default core", slug='no-sysmon') core = None if not core: @@ -160,25 +166,25 @@ class Collector: # if env.PYBEHAVIOR.pep669 and self.should_start_context is None: # core = "sysmon" if HAS_CTRACER: - core = "ctrace" + core = 'ctrace' else: - core = "pytrace" + core = 'pytrace' - if core == "sysmon": + if core == 'sysmon': self._trace_class = SysMonitor - self._core_kwargs = {"tool_id": 3 if metacov else 1} + self._core_kwargs = {'tool_id': 3 if metacov else 1} self.file_disposition_class = FileDisposition self.supports_plugins = False self.packed_arcs = False self.systrace = False - elif core == "ctrace": + elif core == 'ctrace': self._trace_class = CTracer self._core_kwargs = {} self.file_disposition_class = CFileDisposition self.supports_plugins = True self.packed_arcs = True self.systrace = True - elif core == "pytrace": + elif core == 'pytrace': self._trace_class = PyTracer self._core_kwargs = {} self.file_disposition_class = FileDisposition @@ -186,42 +192,42 @@ class Collector: self.packed_arcs = False self.systrace = True else: - raise ConfigError(f"Unknown core value: {core!r}") + raise ConfigError(f'Unknown core value: {core!r}') # We can handle a few concurrency options here, but only one at a time. concurrencies = set(self.concurrency) unknown = concurrencies - CoverageConfig.CONCURRENCY_CHOICES if unknown: - show = ", ".join(sorted(unknown)) - raise ConfigError(f"Unknown concurrency choices: {show}") + show = ', '.join(sorted(unknown)) + raise ConfigError(f'Unknown concurrency choices: {show}') light_threads = concurrencies & self.LIGHT_THREADS if len(light_threads) > 1: - show = ", ".join(sorted(light_threads)) - raise ConfigError(f"Conflicting concurrency settings: {show}") + show = ', '.join(sorted(light_threads)) + raise ConfigError(f'Conflicting concurrency settings: {show}') do_threading = False - tried = "nothing" # to satisfy pylint + tried = 'nothing' # to satisfy pylint try: - if "greenlet" in concurrencies: - tried = "greenlet" + if 'greenlet' in concurrencies: + tried = 'greenlet' import greenlet self.concur_id_func = greenlet.getcurrent - elif "eventlet" in concurrencies: - tried = "eventlet" + elif 'eventlet' in concurrencies: + tried = 'eventlet' import eventlet.greenthread # pylint: disable=import-error,useless-suppression self.concur_id_func = eventlet.greenthread.getcurrent - elif "gevent" in concurrencies: - tried = "gevent" + elif 'gevent' in concurrencies: + tried = 'gevent' import gevent # pylint: disable=import-error,useless-suppression self.concur_id_func = gevent.getcurrent - if "thread" in concurrencies: + if 'thread' in concurrencies: do_threading = True except ImportError as ex: msg = f"Couldn't trace with concurrency={tried}, the module isn't installed." raise ConfigError(msg) from ex - if self.concur_id_func and not hasattr(self._trace_class, "concur_id_func"): + if self.concur_id_func and not hasattr(self._trace_class, 'concur_id_func'): raise ConfigError( "Can't support concurrency={} with {}, only threads are supported.".format( tried, self.tracer_name(), @@ -238,7 +244,7 @@ class Collector: self.reset() def __repr__(self) -> str: - return f"" + return f'' def use_data(self, covdata: CoverageData, context: str | None) -> None: """Use `covdata` for recording data.""" @@ -296,7 +302,7 @@ class Collector: # # This gives a 20% benefit on the workload described at # https://bitbucket.org/pypy/pypy/issue/1871/10x-slower-than-cpython-under-coverage - self.should_trace_cache = __pypy__.newdict("module") + self.should_trace_cache = __pypy__.newdict('module') else: self.should_trace_cache = {} @@ -394,9 +400,9 @@ class Collector: """Stop collecting trace information.""" assert self._collectors if self._collectors[-1] is not self: - print("self._collectors:") + print('self._collectors:') for c in self._collectors: - print(f" {c!r}\n{c.origin}") + print(f' {c!r}\n{c.origin}') assert self._collectors[-1] is self, ( f"Expected current collector to be {self!r}, but it's {self._collectors[-1]!r}" ) @@ -414,9 +420,9 @@ class Collector: tracer.stop() stats = tracer.get_stats() if stats: - print("\nCoverage.py tracer stats:") + print('\nCoverage.py tracer stats:') for k, v in human_sorted_items(stats.items()): - print(f"{k:>20}: {v}") + print(f'{k:>20}: {v}') if self.threading: self.threading.settrace(None) @@ -433,7 +439,7 @@ class Collector: def post_fork(self) -> None: """After a fork, tracers might need to adjust.""" for tracer in self.tracers: - if hasattr(tracer, "post_fork"): + if hasattr(tracer, 'post_fork'): tracer.post_fork() def _activity(self) -> bool: @@ -451,7 +457,7 @@ class Collector: if self.static_context: context = self.static_context if new_context: - context += "|" + new_context + context += '|' + new_context else: context = new_context self.covdata.set_context(context) @@ -462,7 +468,7 @@ class Collector: assert file_tracer is not None plugin = file_tracer._coverage_plugin plugin_name = plugin._coverage_plugin_name - self.warn(f"Disabling plug-in {plugin_name!r} due to previous exception") + self.warn(f'Disabling plug-in {plugin_name!r} due to previous exception') plugin._coverage_enabled = False disposition.trace = False diff --git a/.venv/lib/python3.10/site-packages/coverage/config.py b/.venv/lib/python3.10/site-packages/coverage/config.py index 7a7cd54..648cb00 100644 --- a/.venv/lib/python3.10/site-packages/coverage/config.py +++ b/.venv/lib/python3.10/site-packages/coverage/config.py @@ -1,28 +1,30 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Config file for coverage.py""" - from __future__ import annotations import collections import configparser import copy -import os import os.path import re - -from typing import ( - Any, Callable, Iterable, Union, -) +from typing import Any +from typing import Callable +from typing import Iterable +from typing import Union from coverage.exceptions import ConfigError -from coverage.misc import isolate_module, human_sorted_items, substitute_variables -from coverage.tomlconfig import TomlConfigParser, TomlDecodeError -from coverage.types import ( - TConfigurable, TConfigSectionIn, TConfigValueIn, TConfigSectionOut, - TConfigValueOut, TPluginConfig, -) +from coverage.misc import human_sorted_items +from coverage.misc import isolate_module +from coverage.misc import substitute_variables +from coverage.tomlconfig import TomlConfigParser +from coverage.tomlconfig import TomlDecodeError +from coverage.types import TConfigSectionIn +from coverage.types import TConfigSectionOut +from coverage.types import TConfigurable +from coverage.types import TConfigValueIn +from coverage.types import TConfigValueOut +from coverage.types import TPluginConfig os = isolate_module(os) @@ -39,17 +41,17 @@ class HandyConfigParser(configparser.ConfigParser): """ super().__init__(interpolation=None) - self.section_prefixes = ["coverage:"] + self.section_prefixes = ['coverage:'] if our_file: - self.section_prefixes.append("") + self.section_prefixes.append('') - def read( # type: ignore[override] + def read( # type: ignore[override] self, filenames: Iterable[str], encoding_unused: str | None = None, ) -> list[str]: """Read a file name as UTF-8 configuration data.""" - return super().read(filenames, encoding="utf-8") + return super().read(filenames, encoding='utf-8') def real_section(self, section: str) -> str | None: """Get the actual name of a section.""" @@ -73,7 +75,7 @@ class HandyConfigParser(configparser.ConfigParser): real_section = self.real_section(section) if real_section is not None: return super().options(real_section) - raise ConfigError(f"No section: {section!r}") + raise ConfigError(f'No section: {section!r}') def get_section(self, section: str) -> TConfigSectionOut: """Get the contents of a section, as a dictionary.""" @@ -82,7 +84,7 @@ class HandyConfigParser(configparser.ConfigParser): d[opt] = self.get(section, opt) return d - def get(self, section: str, option: str, *args: Any, **kwargs: Any) -> str: # type: ignore + def get(self, section: str, option: str, *args: Any, **kwargs: Any) -> str: # type: ignore """Get a value, replacing environment variables also. The arguments are the same as `ConfigParser.get`, but in the found @@ -97,7 +99,7 @@ class HandyConfigParser(configparser.ConfigParser): if super().has_option(real_section, option): break else: - raise ConfigError(f"No option {option!r} in section: {section!r}") + raise ConfigError(f'No option {option!r} in section: {section!r}') v: str = super().get(real_section, option, *args, **kwargs) v = substitute_variables(v, os.environ) @@ -114,8 +116,8 @@ class HandyConfigParser(configparser.ConfigParser): """ value_list = self.get(section, option) values = [] - for value_line in value_list.split("\n"): - for value in value_line.split(","): + for value_line in value_list.split('\n'): + for value in value_line.split(','): value = value.strip() if value: values.append(value) @@ -138,7 +140,7 @@ class HandyConfigParser(configparser.ConfigParser): re.compile(value) except re.error as e: raise ConfigError( - f"Invalid [{section}].{option} value {value!r}: {e}", + f'Invalid [{section}].{option} value {value!r}: {e}', ) from e if value: value_list.append(value) @@ -150,20 +152,20 @@ TConfigParser = Union[HandyConfigParser, TomlConfigParser] # The default line exclusion regexes. DEFAULT_EXCLUDE = [ - r"#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)", + r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)', ] # The default partial branch regexes, to be modified by the user. DEFAULT_PARTIAL = [ - r"#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(branch|BRANCH)", + r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(branch|BRANCH)', ] # The default partial branch regexes, based on Python semantics. # These are any Python branching constructs that can't actually execute all # their branches. DEFAULT_PARTIAL_ALWAYS = [ - "while (True|1|False|0):", - "if (True|1|False|0):", + 'while (True|1|False|0):', + 'if (True|1|False|0):', ] @@ -197,7 +199,7 @@ class CoverageConfig(TConfigurable, TPluginConfig): self.concurrency: list[str] = [] self.context: str | None = None self.cover_pylib = False - self.data_file = ".coverage" + self.data_file = '.coverage' self.debug: list[str] = [] self.debug_file: str | None = None self.disable_warnings: list[str] = [] @@ -233,23 +235,23 @@ class CoverageConfig(TConfigurable, TPluginConfig): # Defaults for [html] self.extra_css: str | None = None - self.html_dir = "htmlcov" + self.html_dir = 'htmlcov' self.html_skip_covered: bool | None = None self.html_skip_empty: bool | None = None - self.html_title = "Coverage report" + self.html_title = 'Coverage report' self.show_contexts = False # Defaults for [xml] - self.xml_output = "coverage.xml" + self.xml_output = 'coverage.xml' self.xml_package_depth = 99 # Defaults for [json] - self.json_output = "coverage.json" + self.json_output = 'coverage.json' self.json_pretty_print = False self.json_show_contexts = False # Defaults for [lcov] - self.lcov_output = "coverage.lcov" + self.lcov_output = 'coverage.lcov' # Defaults for [paths] self.paths: dict[str, list[str]] = {} @@ -258,9 +260,9 @@ class CoverageConfig(TConfigurable, TPluginConfig): self.plugin_options: dict[str, TConfigSectionOut] = {} MUST_BE_LIST = { - "debug", "concurrency", "plugins", - "report_omit", "report_include", - "run_omit", "run_include", + 'debug', 'concurrency', 'plugins', + 'report_omit', 'report_include', + 'run_omit', 'run_include', } def from_args(self, **kwargs: TConfigValueIn) -> None: @@ -286,7 +288,7 @@ class CoverageConfig(TConfigurable, TPluginConfig): """ _, ext = os.path.splitext(filename) cp: TConfigParser - if ext == ".toml": + if ext == '.toml': cp = TomlConfigParser(our_file) else: cp = HandyConfigParser(our_file) @@ -314,7 +316,7 @@ class CoverageConfig(TConfigurable, TPluginConfig): # Check that there are no unrecognized options. all_options = collections.defaultdict(set) for option_spec in self.CONFIG_FILE_OPTIONS: - section, option = option_spec[1].split(":") + section, option = option_spec[1].split(':') all_options[section].add(option) for section, options in all_options.items(): @@ -328,9 +330,9 @@ class CoverageConfig(TConfigurable, TPluginConfig): ) # [paths] is special - if cp.has_section("paths"): - for option in cp.options("paths"): - self.paths[option] = cp.getlist("paths", option) + if cp.has_section('paths'): + for option in cp.options('paths'): + self.paths[option] = cp.getlist('paths', option) any_set = True # plugins can have options @@ -349,7 +351,7 @@ class CoverageConfig(TConfigurable, TPluginConfig): if used: self.config_file = os.path.abspath(filename) - with open(filename, "rb") as f: + with open(filename, 'rb') as f: self._config_contents = f.read() return used @@ -358,7 +360,7 @@ class CoverageConfig(TConfigurable, TPluginConfig): """Return a copy of the configuration.""" return copy.deepcopy(self) - CONCURRENCY_CHOICES = {"thread", "gevent", "greenlet", "eventlet", "multiprocessing"} + CONCURRENCY_CHOICES = {'thread', 'gevent', 'greenlet', 'eventlet', 'multiprocessing'} CONFIG_FILE_OPTIONS = [ # These are *args for _set_attr_from_config_option: @@ -370,64 +372,64 @@ class CoverageConfig(TConfigurable, TPluginConfig): # configuration value from the file. # [run] - ("branch", "run:branch", "boolean"), - ("command_line", "run:command_line"), - ("concurrency", "run:concurrency", "list"), - ("context", "run:context"), - ("cover_pylib", "run:cover_pylib", "boolean"), - ("data_file", "run:data_file"), - ("debug", "run:debug", "list"), - ("debug_file", "run:debug_file"), - ("disable_warnings", "run:disable_warnings", "list"), - ("dynamic_context", "run:dynamic_context"), - ("parallel", "run:parallel", "boolean"), - ("plugins", "run:plugins", "list"), - ("relative_files", "run:relative_files", "boolean"), - ("run_include", "run:include", "list"), - ("run_omit", "run:omit", "list"), - ("sigterm", "run:sigterm", "boolean"), - ("source", "run:source", "list"), - ("source_pkgs", "run:source_pkgs", "list"), - ("timid", "run:timid", "boolean"), - ("_crash", "run:_crash"), + ('branch', 'run:branch', 'boolean'), + ('command_line', 'run:command_line'), + ('concurrency', 'run:concurrency', 'list'), + ('context', 'run:context'), + ('cover_pylib', 'run:cover_pylib', 'boolean'), + ('data_file', 'run:data_file'), + ('debug', 'run:debug', 'list'), + ('debug_file', 'run:debug_file'), + ('disable_warnings', 'run:disable_warnings', 'list'), + ('dynamic_context', 'run:dynamic_context'), + ('parallel', 'run:parallel', 'boolean'), + ('plugins', 'run:plugins', 'list'), + ('relative_files', 'run:relative_files', 'boolean'), + ('run_include', 'run:include', 'list'), + ('run_omit', 'run:omit', 'list'), + ('sigterm', 'run:sigterm', 'boolean'), + ('source', 'run:source', 'list'), + ('source_pkgs', 'run:source_pkgs', 'list'), + ('timid', 'run:timid', 'boolean'), + ('_crash', 'run:_crash'), # [report] - ("exclude_list", "report:exclude_lines", "regexlist"), - ("exclude_also", "report:exclude_also", "regexlist"), - ("fail_under", "report:fail_under", "float"), - ("format", "report:format"), - ("ignore_errors", "report:ignore_errors", "boolean"), - ("include_namespace_packages", "report:include_namespace_packages", "boolean"), - ("partial_always_list", "report:partial_branches_always", "regexlist"), - ("partial_list", "report:partial_branches", "regexlist"), - ("precision", "report:precision", "int"), - ("report_contexts", "report:contexts", "list"), - ("report_include", "report:include", "list"), - ("report_omit", "report:omit", "list"), - ("show_missing", "report:show_missing", "boolean"), - ("skip_covered", "report:skip_covered", "boolean"), - ("skip_empty", "report:skip_empty", "boolean"), - ("sort", "report:sort"), + ('exclude_list', 'report:exclude_lines', 'regexlist'), + ('exclude_also', 'report:exclude_also', 'regexlist'), + ('fail_under', 'report:fail_under', 'float'), + ('format', 'report:format'), + ('ignore_errors', 'report:ignore_errors', 'boolean'), + ('include_namespace_packages', 'report:include_namespace_packages', 'boolean'), + ('partial_always_list', 'report:partial_branches_always', 'regexlist'), + ('partial_list', 'report:partial_branches', 'regexlist'), + ('precision', 'report:precision', 'int'), + ('report_contexts', 'report:contexts', 'list'), + ('report_include', 'report:include', 'list'), + ('report_omit', 'report:omit', 'list'), + ('show_missing', 'report:show_missing', 'boolean'), + ('skip_covered', 'report:skip_covered', 'boolean'), + ('skip_empty', 'report:skip_empty', 'boolean'), + ('sort', 'report:sort'), # [html] - ("extra_css", "html:extra_css"), - ("html_dir", "html:directory"), - ("html_skip_covered", "html:skip_covered", "boolean"), - ("html_skip_empty", "html:skip_empty", "boolean"), - ("html_title", "html:title"), - ("show_contexts", "html:show_contexts", "boolean"), + ('extra_css', 'html:extra_css'), + ('html_dir', 'html:directory'), + ('html_skip_covered', 'html:skip_covered', 'boolean'), + ('html_skip_empty', 'html:skip_empty', 'boolean'), + ('html_title', 'html:title'), + ('show_contexts', 'html:show_contexts', 'boolean'), # [xml] - ("xml_output", "xml:output"), - ("xml_package_depth", "xml:package_depth", "int"), + ('xml_output', 'xml:output'), + ('xml_package_depth', 'xml:package_depth', 'int'), # [json] - ("json_output", "json:output"), - ("json_pretty_print", "json:pretty_print", "boolean"), - ("json_show_contexts", "json:show_contexts", "boolean"), + ('json_output', 'json:output'), + ('json_pretty_print', 'json:pretty_print', 'boolean'), + ('json_show_contexts', 'json:show_contexts', 'boolean'), # [lcov] - ("lcov_output", "lcov:output"), + ('lcov_output', 'lcov:output'), ] def _set_attr_from_config_option( @@ -435,16 +437,16 @@ class CoverageConfig(TConfigurable, TPluginConfig): cp: TConfigParser, attr: str, where: str, - type_: str = "", + type_: str = '', ) -> bool: """Set an attribute on self if it exists in the ConfigParser. Returns True if the attribute was set. """ - section, option = where.split(":") + section, option = where.split(':') if cp.has_option(section, option): - method = getattr(cp, "get" + type_) + method = getattr(cp, 'get' + type_) setattr(self, attr, method(section, option)) return True return False @@ -464,7 +466,7 @@ class CoverageConfig(TConfigurable, TPluginConfig): """ # Special-cased options. - if option_name == "paths": + if option_name == 'paths': self.paths = value # type: ignore[assignment] return @@ -476,13 +478,13 @@ class CoverageConfig(TConfigurable, TPluginConfig): return # See if it's a plugin option. - plugin_name, _, key = option_name.partition(":") + plugin_name, _, key = option_name.partition(':') if key and plugin_name in self.plugins: - self.plugin_options.setdefault(plugin_name, {})[key] = value # type: ignore[index] + self.plugin_options.setdefault(plugin_name, {})[key] = value # type: ignore[index] return # If we get here, we didn't find the option. - raise ConfigError(f"No such option: {option_name!r}") + raise ConfigError(f'No such option: {option_name!r}') def get_option(self, option_name: str) -> TConfigValueOut | None: """Get an option from the configuration. @@ -495,7 +497,7 @@ class CoverageConfig(TConfigurable, TPluginConfig): """ # Special-cased options. - if option_name == "paths": + if option_name == 'paths': return self.paths # type: ignore[return-value] # Check all the hard-coded options. @@ -505,12 +507,12 @@ class CoverageConfig(TConfigurable, TPluginConfig): return getattr(self, attr) # type: ignore[no-any-return] # See if it's a plugin option. - plugin_name, _, key = option_name.partition(":") + plugin_name, _, key = option_name.partition(':') if key and plugin_name in self.plugins: return self.plugin_options.get(plugin_name, {}).get(key) # If we get here, we didn't find the option. - raise ConfigError(f"No such option: {option_name!r}") + raise ConfigError(f'No such option: {option_name!r}') def post_process_file(self, path: str) -> str: """Make final adjustments to a file path to make it usable.""" @@ -530,7 +532,7 @@ class CoverageConfig(TConfigurable, TPluginConfig): def debug_info(self) -> list[tuple[str, Any]]: """Make a list of (name, value) pairs for writing debug info.""" return human_sorted_items( - (k, v) for k, v in self.__dict__.items() if not k.startswith("_") + (k, v) for k, v in self.__dict__.items() if not k.startswith('_') ) @@ -543,24 +545,24 @@ def config_files_to_try(config_file: bool | str) -> list[tuple[str, bool, bool]] # Some API users were specifying ".coveragerc" to mean the same as # True, so make it so. - if config_file == ".coveragerc": + if config_file == '.coveragerc': config_file = True specified_file = (config_file is not True) if not specified_file: # No file was specified. Check COVERAGE_RCFILE. - rcfile = os.getenv("COVERAGE_RCFILE") + rcfile = os.getenv('COVERAGE_RCFILE') if rcfile: config_file = rcfile specified_file = True if not specified_file: # Still no file specified. Default to .coveragerc - config_file = ".coveragerc" + config_file = '.coveragerc' assert isinstance(config_file, str) files_to_try = [ (config_file, True, specified_file), - ("setup.cfg", False, False), - ("tox.ini", False, False), - ("pyproject.toml", False, False), + ('setup.cfg', False, False), + ('tox.ini', False, False), + ('pyproject.toml', False, False), ] return files_to_try @@ -601,13 +603,13 @@ def read_coverage_config( raise ConfigError(f"Couldn't read {fname!r} as a config file") # 3) from environment variables: - env_data_file = os.getenv("COVERAGE_FILE") + env_data_file = os.getenv('COVERAGE_FILE') if env_data_file: config.data_file = env_data_file # $set_env.py: COVERAGE_DEBUG - Debug options: https://coverage.rtfd.io/cmd.html#debug - debugs = os.getenv("COVERAGE_DEBUG") + debugs = os.getenv('COVERAGE_DEBUG') if debugs: - config.debug.extend(d.strip() for d in debugs.split(",")) + config.debug.extend(d.strip() for d in debugs.split(',')) # 4) from constructor arguments: config.from_args(**kwargs) diff --git a/.venv/lib/python3.10/site-packages/coverage/context.py b/.venv/lib/python3.10/site-packages/coverage/context.py index c8ee712..401504d 100644 --- a/.venv/lib/python3.10/site-packages/coverage/context.py +++ b/.venv/lib/python3.10/site-packages/coverage/context.py @@ -1,12 +1,12 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Determine contexts for coverage.py""" - from __future__ import annotations from types import FrameType -from typing import cast, Callable, Sequence +from typing import Callable +from typing import cast +from typing import Sequence def combine_context_switchers( @@ -44,7 +44,7 @@ def combine_context_switchers( def should_start_context_test_function(frame: FrameType) -> str | None: """Is this frame calling a test_* function?""" co_name = frame.f_code.co_name - if co_name.startswith("test") or co_name == "runTest": + if co_name.startswith('test') or co_name == 'runTest': return qualname_from_frame(frame) return None @@ -54,19 +54,19 @@ def qualname_from_frame(frame: FrameType) -> str | None: co = frame.f_code fname = co.co_name method = None - if co.co_argcount and co.co_varnames[0] == "self": - self = frame.f_locals.get("self", None) + if co.co_argcount and co.co_varnames[0] == 'self': + self = frame.f_locals.get('self', None) method = getattr(self, fname, None) if method is None: func = frame.f_globals.get(fname) if func is None: return None - return cast(str, func.__module__ + "." + fname) + return cast(str, func.__module__ + '.' + fname) - func = getattr(method, "__func__", None) + func = getattr(method, '__func__', None) if func is None: cls = self.__class__ - return cast(str, cls.__module__ + "." + cls.__name__ + "." + fname) + return cast(str, cls.__module__ + '.' + cls.__name__ + '.' + fname) - return cast(str, func.__module__ + "." + func.__qualname__) + return cast(str, func.__module__ + '.' + func.__qualname__) diff --git a/.venv/lib/python3.10/site-packages/coverage/control.py b/.venv/lib/python3.10/site-packages/coverage/control.py index 6f7f9a3..4a58dfe 100644 --- a/.venv/lib/python3.10/site-packages/coverage/control.py +++ b/.venv/lib/python3.10/site-packages/coverage/control.py @@ -1,14 +1,11 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Central control stuff for coverage.py.""" - from __future__ import annotations import atexit import collections import contextlib -import os import os.path import platform import signal @@ -16,31 +13,48 @@ import sys import threading import time import warnings - from types import FrameType -from typing import ( - cast, - Any, Callable, IO, Iterable, Iterator, List, -) +from typing import Any +from typing import Callable +from typing import cast +from typing import IO +from typing import Iterable +from typing import Iterator +from typing import List from coverage import env from coverage.annotate import AnnotateReporter -from coverage.collector import Collector, HAS_CTRACER -from coverage.config import CoverageConfig, read_coverage_config -from coverage.context import should_start_context_test_function, combine_context_switchers -from coverage.data import CoverageData, combine_parallel_data -from coverage.debug import ( - DebugControl, NoDebugging, short_stack, write_formatted_info, relevant_environment_display, -) +from coverage.collector import Collector +from coverage.collector import HAS_CTRACER +from coverage.config import CoverageConfig +from coverage.config import read_coverage_config +from coverage.context import combine_context_switchers +from coverage.context import should_start_context_test_function +from coverage.data import combine_parallel_data +from coverage.data import CoverageData +from coverage.debug import DebugControl +from coverage.debug import NoDebugging +from coverage.debug import relevant_environment_display +from coverage.debug import short_stack +from coverage.debug import write_formatted_info from coverage.disposition import disposition_debug_msg -from coverage.exceptions import ConfigError, CoverageException, CoverageWarning, PluginError -from coverage.files import PathAliases, abs_file, relative_filename, set_relative_directory +from coverage.exceptions import ConfigError +from coverage.exceptions import CoverageException +from coverage.exceptions import CoverageWarning +from coverage.exceptions import PluginError +from coverage.files import abs_file +from coverage.files import PathAliases +from coverage.files import relative_filename +from coverage.files import set_relative_directory from coverage.html import HtmlReporter from coverage.inorout import InOrOut from coverage.jsonreport import JsonReporter from coverage.lcovreport import LcovReporter -from coverage.misc import bool_or_none, join_regex -from coverage.misc import DefaultValue, ensure_dir_for_file, isolate_module +from coverage.misc import bool_or_none +from coverage.misc import DefaultValue +from coverage.misc import ensure_dir_for_file +from coverage.misc import isolate_module +from coverage.misc import join_regex from coverage.multiproc import patch_multiprocessing from coverage.plugin import FileReporter from coverage.plugin_support import Plugins @@ -48,14 +62,19 @@ from coverage.python import PythonFileReporter from coverage.report import SummaryReporter from coverage.report_core import render_report from coverage.results import Analysis -from coverage.types import ( - FilePath, TConfigurable, TConfigSectionIn, TConfigValueIn, TConfigValueOut, - TFileDisposition, TLineNo, TMorf, -) +from coverage.types import FilePath +from coverage.types import TConfigSectionIn +from coverage.types import TConfigurable +from coverage.types import TConfigValueIn +from coverage.types import TConfigValueOut +from coverage.types import TFileDisposition +from coverage.types import TLineNo +from coverage.types import TMorf from coverage.xmlreport import XmlReporter os = isolate_module(os) + @contextlib.contextmanager def override_config(cov: Coverage, **kwargs: TConfigValueIn) -> Iterator[None]: """Temporarily tweak the configuration of `cov`. @@ -72,9 +91,10 @@ def override_config(cov: Coverage, **kwargs: TConfigValueIn) -> Iterator[None]: cov.config = original_config -DEFAULT_DATAFILE = DefaultValue("MISSING") +DEFAULT_DATAFILE = DefaultValue('MISSING') _DEFAULT_DATAFILE = DEFAULT_DATAFILE # Just in case, for backwards compatibility + class Coverage(TConfigurable): """Programmatic access to coverage.py. @@ -323,10 +343,10 @@ class Coverage(TConfigurable): # Create and configure the debugging controller. self._debug = DebugControl(self.config.debug, self._debug_file, self.config.debug_file) - if self._debug.should("process"): - self._debug.write("Coverage._init") + if self._debug.should('process'): + self._debug.write('Coverage._init') - if "multiprocessing" in (self.config.concurrency or ()): + if 'multiprocessing' in (self.config.concurrency or ()): # Multi-processing uses parallel for the subprocesses, so also use # it for the main process. self.config.parallel = True @@ -358,31 +378,31 @@ class Coverage(TConfigurable): # "[run] _crash" will raise an exception if the value is close by in # the call stack, for testing error handling. if self.config._crash and self.config._crash in short_stack(): - raise RuntimeError(f"Crashing because called by {self.config._crash}") + raise RuntimeError(f'Crashing because called by {self.config._crash}') def _write_startup_debug(self) -> None: """Write out debug info at startup if needed.""" wrote_any = False with self._debug.without_callers(): - if self._debug.should("config"): + if self._debug.should('config'): config_info = self.config.debug_info() - write_formatted_info(self._debug.write, "config", config_info) + write_formatted_info(self._debug.write, 'config', config_info) wrote_any = True - if self._debug.should("sys"): - write_formatted_info(self._debug.write, "sys", self.sys_info()) + if self._debug.should('sys'): + write_formatted_info(self._debug.write, 'sys', self.sys_info()) for plugin in self._plugins: - header = "sys: " + plugin._coverage_plugin_name + header = 'sys: ' + plugin._coverage_plugin_name info = plugin.sys_info() write_formatted_info(self._debug.write, header, info) wrote_any = True - if self._debug.should("pybehave"): - write_formatted_info(self._debug.write, "pybehave", env.debug_info()) + if self._debug.should('pybehave'): + write_formatted_info(self._debug.write, 'pybehave', env.debug_info()) wrote_any = True if wrote_any: - write_formatted_info(self._debug.write, "end", ()) + write_formatted_info(self._debug.write, 'end', ()) def _should_trace(self, filename: str, frame: FrameType) -> TFileDisposition: """Decide whether to trace execution in `filename`. @@ -392,7 +412,7 @@ class Coverage(TConfigurable): """ assert self._inorout is not None disp = self._inorout.should_trace(filename, frame) - if self._debug.should("trace"): + if self._debug.should('trace'): self._debug.write(disposition_debug_msg(disp)) return disp @@ -404,11 +424,11 @@ class Coverage(TConfigurable): """ assert self._inorout is not None reason = self._inorout.check_include_omit_etc(filename, frame) - if self._debug.should("trace"): + if self._debug.should('trace'): if not reason: - msg = f"Including {filename!r}" + msg = f'Including {filename!r}' else: - msg = f"Not including {filename!r}: {reason}" + msg = f'Not including {filename!r}: {reason}' self._debug.write(msg) return not reason @@ -431,9 +451,9 @@ class Coverage(TConfigurable): self._warnings.append(msg) if slug: - msg = f"{msg} ({slug})" - if self._debug.should("pid"): - msg = f"[{os.getpid()}] {msg}" + msg = f'{msg} ({slug})' + if self._debug.should('pid'): + msg = f'[{os.getpid()}] {msg}' warnings.warn(msg, category=CoverageWarning, stacklevel=2) if once: @@ -512,15 +532,15 @@ class Coverage(TConfigurable): """Initialization for start()""" # Construct the collector. concurrency: list[str] = self.config.concurrency or [] - if "multiprocessing" in concurrency: + if 'multiprocessing' in concurrency: if self.config.config_file is None: - raise ConfigError("multiprocessing requires a configuration file") + raise ConfigError('multiprocessing requires a configuration file') patch_multiprocessing(rcfile=self.config.config_file) dycon = self.config.dynamic_context - if not dycon or dycon == "none": + if not dycon or dycon == 'none': context_switchers = [] - elif dycon == "test_function": + elif dycon == 'test_function': context_switchers = [should_start_context_test_function] else: raise ConfigError(f"Don't understand dynamic_context setting: {dycon!r}") @@ -565,9 +585,9 @@ class Coverage(TConfigurable): if self._plugins.file_tracers and not self._collector.supports_plugins: self._warn( "Plugin file tracers ({}) aren't supported with {}".format( - ", ".join( + ', '.join( plugin._coverage_plugin_name - for plugin in self._plugins.file_tracers + for plugin in self._plugins.file_tracers ), self._collector.tracer_name(), ), @@ -579,7 +599,7 @@ class Coverage(TConfigurable): self._inorout = InOrOut( config=self.config, warn=self._warn, - debug=(self._debug if self._debug.should("trace") else None), + debug=(self._debug if self._debug.should('trace') else None), include_namespace_packages=self.config.include_namespace_packages, ) self._inorout.plugins = self._plugins @@ -676,18 +696,18 @@ class Coverage(TConfigurable): finally: self.stop() - def _atexit(self, event: str = "atexit") -> None: + def _atexit(self, event: str = 'atexit') -> None: """Clean up on process shutdown.""" - if self._debug.should("process"): - self._debug.write(f"{event}: pid: {os.getpid()}, instance: {self!r}") + if self._debug.should('process'): + self._debug.write(f'{event}: pid: {os.getpid()}, instance: {self!r}') if self._started: self.stop() - if self._auto_save or event == "sigterm": + if self._auto_save or event == 'sigterm': self.save() def _on_sigterm(self, signum_unused: int, frame_unused: FrameType | None) -> None: """A handler for signal.SIGTERM.""" - self._atexit("sigterm") + self._atexit('sigterm') # Statements after here won't be seen by metacov because we just wrote # the data, and are about to kill the process. signal.signal(signal.SIGTERM, self._old_sigterm) # pragma: not covered @@ -724,21 +744,21 @@ class Coverage(TConfigurable): """ if not self._started: # pragma: part started - raise CoverageException("Cannot switch context, coverage is not started") + raise CoverageException('Cannot switch context, coverage is not started') assert self._collector is not None if self._collector.should_start_context: - self._warn("Conflicting dynamic contexts", slug="dynamic-conflict", once=True) + self._warn('Conflicting dynamic contexts', slug='dynamic-conflict', once=True) self._collector.switch_context(new_context) - def clear_exclude(self, which: str = "exclude") -> None: + def clear_exclude(self, which: str = 'exclude') -> None: """Clear the exclude list.""" self._init() - setattr(self.config, which + "_list", []) + setattr(self.config, which + '_list', []) self._exclude_regex_stale() - def exclude(self, regex: str, which: str = "exclude") -> None: + def exclude(self, regex: str, which: str = 'exclude') -> None: """Exclude source lines from execution consideration. A number of lists of regular expressions are maintained. Each list @@ -754,7 +774,7 @@ class Coverage(TConfigurable): """ self._init() - excl_list = getattr(self.config, which + "_list") + excl_list = getattr(self.config, which + '_list') excl_list.append(regex) self._exclude_regex_stale() @@ -765,11 +785,11 @@ class Coverage(TConfigurable): def _exclude_regex(self, which: str) -> str: """Return a regex string for the given exclusion list.""" if which not in self._exclude_re: - excl_list = getattr(self.config, which + "_list") + excl_list = getattr(self.config, which + '_list') self._exclude_re[which] = join_regex(excl_list) return self._exclude_re[which] - def get_exclude_list(self, which: str = "exclude") -> list[str]: + def get_exclude_list(self, which: str = 'exclude') -> list[str]: """Return a list of excluded regex strings. `which` indicates which list is desired. See :meth:`exclude` for the @@ -777,7 +797,7 @@ class Coverage(TConfigurable): """ self._init() - return cast(List[str], getattr(self.config, which + "_list")) + return cast(List[str], getattr(self.config, which + '_list')) def save(self) -> None: """Save the collected coverage data to the data file.""" @@ -787,7 +807,7 @@ class Coverage(TConfigurable): def _make_aliases(self) -> PathAliases: """Create a PathAliases from our configuration.""" aliases = PathAliases( - debugfn=(self._debug.write if self._debug.should("pathmap") else None), + debugfn=(self._debug.write if self._debug.should('pathmap') else None), relative=self.config.relative_files, ) for paths in self.config.paths.values(): @@ -884,7 +904,7 @@ class Coverage(TConfigurable): # Find out if we got any data. if not self._data and self._warn_no_data: - self._warn("No data was collected.", slug="no-data-collected") + self._warn('No data was collected.', slug='no-data-collected') # Touch all the files that could have executed, so that we can # mark completely un-executed files as 0% covered. @@ -952,7 +972,7 @@ class Coverage(TConfigurable): """Get a FileReporter for a module or file name.""" assert self._data is not None plugin = None - file_reporter: str | FileReporter = "python" + file_reporter: str | FileReporter = 'python' if isinstance(morf, str): mapped_morf = self._file_mapper(morf) @@ -964,12 +984,12 @@ class Coverage(TConfigurable): file_reporter = plugin.file_reporter(mapped_morf) if file_reporter is None: raise PluginError( - "Plugin {!r} did not provide a file reporter for {!r}.".format( + 'Plugin {!r} did not provide a file reporter for {!r}.'.format( plugin._coverage_plugin_name, morf, ), ) - if file_reporter == "python": + if file_reporter == 'python': file_reporter = PythonFileReporter(morf, self) assert isinstance(file_reporter, FileReporter) @@ -1290,36 +1310,37 @@ class Coverage(TConfigurable): for plugin in plugins: entry = plugin._coverage_plugin_name if not plugin._coverage_enabled: - entry += " (disabled)" + entry += ' (disabled)' entries.append(entry) return entries info = [ - ("coverage_version", covmod.__version__), - ("coverage_module", covmod.__file__), - ("core", self._collector.tracer_name() if self._collector is not None else "-none-"), - ("CTracer", "available" if HAS_CTRACER else "unavailable"), - ("plugins.file_tracers", plugin_info(self._plugins.file_tracers)), - ("plugins.configurers", plugin_info(self._plugins.configurers)), - ("plugins.context_switchers", plugin_info(self._plugins.context_switchers)), - ("configs_attempted", self.config.attempted_config_files), - ("configs_read", self.config.config_files_read), - ("config_file", self.config.config_file), - ("config_contents", - repr(self.config._config_contents) if self.config._config_contents else "-none-", + ('coverage_version', covmod.__version__), + ('coverage_module', covmod.__file__), + ('core', self._collector.tracer_name() if self._collector is not None else '-none-'), + ('CTracer', 'available' if HAS_CTRACER else 'unavailable'), + ('plugins.file_tracers', plugin_info(self._plugins.file_tracers)), + ('plugins.configurers', plugin_info(self._plugins.configurers)), + ('plugins.context_switchers', plugin_info(self._plugins.context_switchers)), + ('configs_attempted', self.config.attempted_config_files), + ('configs_read', self.config.config_files_read), + ('config_file', self.config.config_file), + ( + 'config_contents', + repr(self.config._config_contents) if self.config._config_contents else '-none-', ), - ("data_file", self._data.data_filename() if self._data is not None else "-none-"), - ("python", sys.version.replace("\n", "")), - ("platform", platform.platform()), - ("implementation", platform.python_implementation()), - ("executable", sys.executable), - ("def_encoding", sys.getdefaultencoding()), - ("fs_encoding", sys.getfilesystemencoding()), - ("pid", os.getpid()), - ("cwd", os.getcwd()), - ("path", sys.path), - ("environment", [f"{k} = {v}" for k, v in relevant_environment_display(os.environ)]), - ("command_line", " ".join(getattr(sys, "argv", ["-none-"]))), + ('data_file', self._data.data_filename() if self._data is not None else '-none-'), + ('python', sys.version.replace('\n', '')), + ('platform', platform.platform()), + ('implementation', platform.python_implementation()), + ('executable', sys.executable), + ('def_encoding', sys.getdefaultencoding()), + ('fs_encoding', sys.getfilesystemencoding()), + ('pid', os.getpid()), + ('cwd', os.getcwd()), + ('path', sys.path), + ('environment', [f'{k} = {v}' for k, v in relevant_environment_display(os.environ)]), + ('command_line', ' '.join(getattr(sys, 'argv', ['-none-']))), ] if self._inorout is not None: @@ -1332,12 +1353,12 @@ class Coverage(TConfigurable): # Mega debugging... # $set_env.py: COVERAGE_DEBUG_CALLS - Lots and lots of output about calls to Coverage. -if int(os.getenv("COVERAGE_DEBUG_CALLS", 0)): # pragma: debugging +if int(os.getenv('COVERAGE_DEBUG_CALLS', 0)): # pragma: debugging from coverage.debug import decorate_methods, show_calls Coverage = decorate_methods( # type: ignore[misc] show_calls(show_args=True), - butnot=["get_data"], + butnot=['get_data'], )(Coverage) @@ -1364,7 +1385,7 @@ def process_startup() -> Coverage | None: not started by this call. """ - cps = os.getenv("COVERAGE_PROCESS_START") + cps = os.getenv('COVERAGE_PROCESS_START') if not cps: # No request for coverage, nothing to do. return None @@ -1378,7 +1399,7 @@ def process_startup() -> Coverage | None: # # https://github.com/nedbat/coveragepy/issues/340 has more details. - if hasattr(process_startup, "coverage"): + if hasattr(process_startup, 'coverage'): # We've annotated this function before, so we must have already # started coverage.py in this process. Nothing to do. return None @@ -1396,6 +1417,6 @@ def process_startup() -> Coverage | None: def _prevent_sub_process_measurement() -> None: """Stop any subprocess auto-measurement from writing data.""" - auto_created_coverage = getattr(process_startup, "coverage", None) + auto_created_coverage = getattr(process_startup, 'coverage', None) if auto_created_coverage is not None: auto_created_coverage._auto_save = False diff --git a/.venv/lib/python3.10/site-packages/coverage/data.py b/.venv/lib/python3.10/site-packages/coverage/data.py index 9513adf..04e6a86 100644 --- a/.venv/lib/python3.10/site-packages/coverage/data.py +++ b/.venv/lib/python3.10/site-packages/coverage/data.py @@ -1,6 +1,5 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Coverage data for coverage.py. This file had the 4.x JSON data support, which is now gone. This file still @@ -9,18 +8,21 @@ CoverageData is now defined in sqldata.py, and imported here to keep the imports working. """ - from __future__ import annotations import glob import hashlib import os.path +from typing import Callable +from typing import Iterable -from typing import Callable, Iterable - -from coverage.exceptions import CoverageException, NoDataError +from coverage.exceptions import CoverageException +from coverage.exceptions import NoDataError from coverage.files import PathAliases -from coverage.misc import Hasher, file_be_gone, human_sorted, plural +from coverage.misc import file_be_gone +from coverage.misc import Hasher +from coverage.misc import human_sorted +from coverage.misc import plural from coverage.sqldata import CoverageData @@ -38,7 +40,7 @@ def line_counts(data: CoverageData, fullpath: bool = False) -> dict[str, int]: filename_fn: Callable[[str], str] if fullpath: # pylint: disable=unnecessary-lambda-assignment - filename_fn = lambda f: f + def filename_fn(f): return f else: filename_fn = os.path.basename for filename in data.measured_files(): @@ -79,14 +81,14 @@ def combinable_files(data_file: str, data_paths: Iterable[str] | None = None) -> if os.path.isfile(p): files_to_combine.append(os.path.abspath(p)) elif os.path.isdir(p): - pattern = glob.escape(os.path.join(os.path.abspath(p), local)) +".*" + pattern = glob.escape(os.path.join(os.path.abspath(p), local)) + '.*' files_to_combine.extend(glob.glob(pattern)) else: raise NoDataError(f"Couldn't combine from non-existent path '{p}'") # SQLite might have made journal files alongside our database files. # We never want to combine those. - files_to_combine = [fnm for fnm in files_to_combine if not fnm.endswith("-journal")] + files_to_combine = [fnm for fnm in files_to_combine if not fnm.endswith('-journal')] # Sorting isn't usually needed, since it shouldn't matter what order files # are combined, but sorting makes tests more predictable, and makes @@ -132,7 +134,7 @@ def combine_parallel_data( files_to_combine = combinable_files(data.base_filename(), data_paths) if strict and not files_to_combine: - raise NoDataError("No data to combine") + raise NoDataError('No data to combine') file_hashes = set() combined_any = False @@ -141,8 +143,8 @@ def combine_parallel_data( if f == data.data_filename(): # Sometimes we are combining into a file which is one of the # parallel files. Skip that file. - if data._debug.should("dataio"): - data._debug.write(f"Skipping combining ourself: {f!r}") + if data._debug.should('dataio'): + data._debug.write(f'Skipping combining ourself: {f!r}') continue try: @@ -153,16 +155,16 @@ def combine_parallel_data( # we print the original value of f instead of its relative path rel_file_name = f - with open(f, "rb") as fobj: - hasher = hashlib.new("sha3_256") + with open(f, 'rb') as fobj: + hasher = hashlib.new('sha3_256') hasher.update(fobj.read()) sha = hasher.digest() combine_this_one = sha not in file_hashes delete_this_one = not keep if combine_this_one: - if data._debug.should("dataio"): - data._debug.write(f"Combining data file {f!r}") + if data._debug.should('dataio'): + data._debug.write(f'Combining data file {f!r}') file_hashes.add(sha) try: new_data = CoverageData(f, debug=data._debug) @@ -179,39 +181,39 @@ def combine_parallel_data( data.update(new_data, aliases=aliases) combined_any = True if message: - message(f"Combined data file {rel_file_name}") + message(f'Combined data file {rel_file_name}') else: if message: - message(f"Skipping duplicate data {rel_file_name}") + message(f'Skipping duplicate data {rel_file_name}') if delete_this_one: - if data._debug.should("dataio"): - data._debug.write(f"Deleting data file {f!r}") + if data._debug.should('dataio'): + data._debug.write(f'Deleting data file {f!r}') file_be_gone(f) if strict and not combined_any: - raise NoDataError("No usable data files") + raise NoDataError('No usable data files') def debug_data_file(filename: str) -> None: """Implementation of 'coverage debug data'.""" data = CoverageData(filename) filename = data.data_filename() - print(f"path: {filename}") + print(f'path: {filename}') if not os.path.exists(filename): print("No data collected: file doesn't exist") return data.read() - print(f"has_arcs: {data.has_arcs()!r}") + print(f'has_arcs: {data.has_arcs()!r}') summary = line_counts(data, fullpath=True) filenames = human_sorted(summary.keys()) nfiles = len(filenames) - print(f"{nfiles} file{plural(nfiles)}:") + print(f'{nfiles} file{plural(nfiles)}:') for f in filenames: - line = f"{f}: {summary[f]} line{plural(summary[f])}" + line = f'{f}: {summary[f]} line{plural(summary[f])}' plugin = data.file_tracer(f) if plugin: - line += f" [{plugin}]" + line += f' [{plugin}]' print(line) diff --git a/.venv/lib/python3.10/site-packages/coverage/debug.py b/.venv/lib/python3.10/site-packages/coverage/debug.py index e4bed8b..b9d2dc6 100644 --- a/.venv/lib/python3.10/site-packages/coverage/debug.py +++ b/.venv/lib/python3.10/site-packages/coverage/debug.py @@ -1,10 +1,9 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Control of and utilities for debugging.""" - from __future__ import annotations +import _thread import atexit import contextlib import functools @@ -17,15 +16,18 @@ import reprlib import sys import traceback import types -import _thread +from typing import Any +from typing import Callable +from typing import IO +from typing import Iterable +from typing import Iterator +from typing import Mapping +from typing import overload -from typing import ( - overload, - Any, Callable, IO, Iterable, Iterator, Mapping, -) - -from coverage.misc import human_sorted_items, isolate_module -from coverage.types import AnyCallable, TWritable +from coverage.misc import human_sorted_items +from coverage.misc import isolate_module +from coverage.types import AnyCallable +from coverage.types import TWritable os = isolate_module(os) @@ -53,12 +55,12 @@ class DebugControl: self.suppress_callers = False filters = [] - if self.should("process"): + if self.should('process'): filters.append(CwdTracker().filter) filters.append(ProcessTracker().filter) - if self.should("pytest"): + if self.should('pytest'): filters.append(PytestTracker().filter) - if self.should("pid"): + if self.should('pid'): filters.append(add_pid_and_tid) self.output = DebugOutputFile.get_one( @@ -69,11 +71,11 @@ class DebugControl: self.raw_output = self.output.outfile def __repr__(self) -> str: - return f"" + return f'' def should(self, option: str) -> bool: """Decide whether to output debug information in category `option`.""" - if option == "callers" and self.suppress_callers: + if option == 'callers' and self.suppress_callers: return False return (option in self.options) @@ -96,20 +98,21 @@ class DebugControl: after the message. """ - self.output.write(msg + "\n") + self.output.write(msg + '\n') if exc is not None: - self.output.write("".join(traceback.format_exception(None, exc, exc.__traceback__))) - if self.should("self"): - caller_self = inspect.stack()[1][0].f_locals.get("self") + self.output.write(''.join(traceback.format_exception(None, exc, exc.__traceback__))) + if self.should('self'): + caller_self = inspect.stack()[1][0].f_locals.get('self') if caller_self is not None: - self.output.write(f"self: {caller_self!r}\n") - if self.should("callers"): + self.output.write(f'self: {caller_self!r}\n') + if self.should('callers'): dump_stack_frames(out=self.output, skip=1) self.output.flush() class NoDebugging(DebugControl): """A replacement for DebugControl that will never try to do anything.""" + def __init__(self) -> None: # pylint: disable=super-init-not-called ... @@ -120,12 +123,12 @@ class NoDebugging(DebugControl): def write(self, msg: str, *, exc: BaseException | None = None) -> None: """This will never be called.""" - raise AssertionError("NoDebugging.write should never be called.") + raise AssertionError('NoDebugging.write should never be called.') def info_header(label: str) -> str: """Make a nice header string.""" - return "--{:-<60s}".format(" "+label+" ") + return '--{:-<60s}'.format(' ' + label + ' ') def info_formatter(info: Iterable[tuple[str, Any]]) -> Iterator[str]: @@ -142,17 +145,17 @@ def info_formatter(info: Iterable[tuple[str, Any]]) -> Iterator[str]: assert all(len(l) < label_len for l, _ in info) for label, data in info: if data == []: - data = "-none-" + data = '-none-' if isinstance(data, tuple) and len(repr(tuple(data))) < 30: # Convert to tuple to scrub namedtuples. - yield "%*s: %r" % (label_len, label, tuple(data)) + yield '%*s: %r' % (label_len, label, tuple(data)) elif isinstance(data, (list, set, tuple)): - prefix = "%*s:" % (label_len, label) + prefix = '%*s:' % (label_len, label) for e in data: - yield "%*s %s" % (label_len+1, prefix, e) - prefix = "" + yield '%*s %s' % (label_len + 1, prefix, e) + prefix = '' else: - yield "%*s: %s" % (label_len, label, data) + yield '%*s: %s' % (label_len, label, data) def write_formatted_info( @@ -170,35 +173,38 @@ def write_formatted_info( """ write(info_header(header)) for line in info_formatter(info): - write(f" {line}") + write(f' {line}') def exc_one_line(exc: Exception) -> str: """Get a one-line summary of an exception, including class name and message.""" lines = traceback.format_exception_only(type(exc), exc) - return "|".join(l.rstrip() for l in lines) + return '|'.join(l.rstrip() for l in lines) _FILENAME_REGEXES: list[tuple[str, str]] = [ - (r".*[/\\]pytest-of-.*[/\\]pytest-\d+([/\\]popen-gw\d+)?", "tmp:"), + (r'.*[/\\]pytest-of-.*[/\\]pytest-\d+([/\\]popen-gw\d+)?', 'tmp:'), ] _FILENAME_SUBS: list[tuple[str, str]] = [] + @overload def short_filename(filename: str) -> str: pass + @overload def short_filename(filename: None) -> None: pass + def short_filename(filename: str | None) -> str | None: """Shorten a file name. Directories are replaced by prefixes like 'syspath:'""" if not _FILENAME_SUBS: for pathdir in sys.path: - _FILENAME_SUBS.append((pathdir, "syspath:")) + _FILENAME_SUBS.append((pathdir, 'syspath:')) import coverage - _FILENAME_SUBS.append((os.path.dirname(coverage.__file__), "cov:")) + _FILENAME_SUBS.append((os.path.dirname(coverage.__file__), 'cov:')) _FILENAME_SUBS.sort(key=(lambda pair: len(pair[0])), reverse=True) if filename is not None: for pat, sub in _FILENAME_REGEXES: @@ -237,9 +243,9 @@ def short_stack( """ # Regexes in initial frames that we don't care about. BORING_PRELUDE = [ - "", # pytest-xdist has string execution. - r"\bigor.py$", # Our test runner. - r"\bsite-packages\b", # pytest etc getting to our tests. + '', # pytest-xdist has string execution. + r'\bigor.py$', # Our test runner. + r'\bsite-packages\b', # pytest etc getting to our tests. ] stack: Iterable[inspect.FrameInfo] = inspect.stack()[:skip:-1] @@ -251,20 +257,20 @@ def short_stack( ) lines = [] for frame_info in stack: - line = f"{frame_info.function:>30s} : " + line = f'{frame_info.function:>30s} : ' if frame_ids: - line += f"{id(frame_info.frame):#x} " + line += f'{id(frame_info.frame):#x} ' filename = frame_info.filename if short_filenames: filename = short_filename(filename) - line += f"{filename}:{frame_info.lineno}" + line += f'{filename}:{frame_info.lineno}' lines.append(line) - return "\n".join(lines) + return '\n'.join(lines) def dump_stack_frames(out: TWritable, skip: int = 0) -> None: """Print a summary of the stack to `out`.""" - out.write(short_stack(skip=skip+1) + "\n") + out.write(short_stack(skip=skip + 1) + '\n') def clipped_repr(text: str, numchars: int = 50) -> str: @@ -285,36 +291,37 @@ def short_id(id64: int) -> int: def add_pid_and_tid(text: str) -> str: """A filter to add pid and tid to debug messages.""" # Thread ids are useful, but too long. Make a shorter one. - tid = f"{short_id(_thread.get_ident()):04x}" - text = f"{os.getpid():5d}.{tid}: {text}" + tid = f'{short_id(_thread.get_ident()):04x}' + text = f'{os.getpid():5d}.{tid}: {text}' return text -AUTO_REPR_IGNORE = {"$coverage.object_id"} +AUTO_REPR_IGNORE = {'$coverage.object_id'} + def auto_repr(self: Any) -> str: """A function implementing an automatic __repr__ for debugging.""" show_attrs = ( (k, v) for k, v in self.__dict__.items() - if getattr(v, "show_repr_attr", True) - and not inspect.ismethod(v) - and k not in AUTO_REPR_IGNORE + if getattr(v, 'show_repr_attr', True) and + not inspect.ismethod(v) and + k not in AUTO_REPR_IGNORE ) - return "<{klass} @{id:#x}{attrs}>".format( + return '<{klass} @{id:#x}{attrs}>'.format( klass=self.__class__.__name__, id=id(self), - attrs="".join(f" {k}={v!r}" for k, v in show_attrs), + attrs=''.join(f' {k}={v!r}' for k, v in show_attrs), ) def simplify(v: Any) -> Any: # pragma: debugging """Turn things which are nearly dict/list/etc into dict/list/etc.""" if isinstance(v, dict): - return {k:simplify(vv) for k, vv in v.items()} + return {k: simplify(vv) for k, vv in v.items()} elif isinstance(v, (list, tuple)): return type(v)(simplify(vv) for vv in v) - elif hasattr(v, "__dict__"): - return simplify({"."+k: v for k, v in v.__dict__.items()}) + elif hasattr(v, '__dict__'): + return simplify({'.' + k: v for k, v in v.__dict__.items()}) else: return v @@ -343,12 +350,13 @@ def filter_text(text: str, filters: Iterable[Callable[[str], str]]) -> str: lines = [] for line in text.splitlines(): lines.extend(filter_fn(line).splitlines()) - text = "\n".join(lines) + text = '\n'.join(lines) return text + ending class CwdTracker: """A class to add cwd info to debug messages.""" + def __init__(self) -> None: self.cwd: str | None = None @@ -356,32 +364,33 @@ class CwdTracker: """Add a cwd message for each new cwd.""" cwd = os.getcwd() if cwd != self.cwd: - text = f"cwd is now {cwd!r}\n" + text + text = f'cwd is now {cwd!r}\n' + text self.cwd = cwd return text class ProcessTracker: """Track process creation for debug logging.""" + def __init__(self) -> None: self.pid: int = os.getpid() self.did_welcome = False def filter(self, text: str) -> str: """Add a message about how new processes came to be.""" - welcome = "" + welcome = '' pid = os.getpid() if self.pid != pid: - welcome = f"New process: forked {self.pid} -> {pid}\n" + welcome = f'New process: forked {self.pid} -> {pid}\n' self.pid = pid elif not self.did_welcome: - argv = getattr(sys, "argv", None) + argv = getattr(sys, 'argv', None) welcome = ( - f"New process: {pid=}, executable: {sys.executable!r}\n" - + f"New process: cmd: {argv!r}\n" + f'New process: {pid=}, executable: {sys.executable!r}\n' + + f'New process: cmd: {argv!r}\n' ) - if hasattr(os, "getppid"): - welcome += f"New process parent pid: {os.getppid()!r}\n" + if hasattr(os, 'getppid'): + welcome += f'New process parent pid: {os.getppid()!r}\n' if welcome: self.did_welcome = True @@ -392,20 +401,22 @@ class ProcessTracker: class PytestTracker: """Track the current pytest test name to add to debug messages.""" + def __init__(self) -> None: self.test_name: str | None = None def filter(self, text: str) -> str: """Add a message when the pytest test changes.""" - test_name = os.getenv("PYTEST_CURRENT_TEST") + test_name = os.getenv('PYTEST_CURRENT_TEST') if test_name != self.test_name: - text = f"Pytest context: {test_name}\n" + text + text = f'Pytest context: {test_name}\n' + text self.test_name = test_name return text class DebugOutputFile: """A file-like object that includes pid and cwd information.""" + def __init__( self, outfile: IO[str] | None, @@ -444,21 +455,21 @@ class DebugOutputFile: the_one, is_interim = cls._get_singleton_data() if the_one is None or is_interim: if file_name is not None: - fileobj = open(file_name, "a", encoding="utf-8") + fileobj = open(file_name, 'a', encoding='utf-8') else: # $set_env.py: COVERAGE_DEBUG_FILE - Where to write debug output - file_name = os.getenv("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE) - if file_name in ("stdout", "stderr"): + file_name = os.getenv('COVERAGE_DEBUG_FILE', FORCED_DEBUG_FILE) + if file_name in ('stdout', 'stderr'): fileobj = getattr(sys, file_name) elif file_name: - fileobj = open(file_name, "a", encoding="utf-8") + fileobj = open(file_name, 'a', encoding='utf-8') atexit.register(fileobj.close) else: fileobj = sys.stderr the_one = cls(fileobj, filters) cls._set_singleton_data(the_one, interim) - if not(the_one.filters): + if not (the_one.filters): the_one.filters = list(filters) return the_one @@ -467,8 +478,8 @@ class DebugOutputFile: # a process-wide singleton. So stash it in sys.modules instead of # on a class attribute. Yes, this is aggressively gross. - SYS_MOD_NAME = "$coverage.debug.DebugOutputFile.the_one" - SINGLETON_ATTR = "the_one_and_is_interim" + SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one' + SINGLETON_ATTR = 'the_one_and_is_interim' @classmethod def _set_singleton_data(cls, the_one: DebugOutputFile, interim: bool) -> None: @@ -504,7 +515,7 @@ class DebugOutputFile: def log(msg: str, stack: bool = False) -> None: # pragma: debugging """Write a log message as forcefully as possible.""" out = DebugOutputFile.get_one(interim=True) - out.write(msg+"\n") + out.write(msg + '\n') if stack: dump_stack_frames(out=out, skip=1) @@ -519,8 +530,8 @@ def decorate_methods( for name, meth in inspect.getmembers(cls, inspect.isroutine): if name not in cls.__dict__: continue - if name != "__init__": - if not private and name.startswith("_"): + if name != '__init__': + if not private and name.startswith('_'): continue if name in butnot: continue @@ -542,7 +553,8 @@ def break_in_pudb(func: AnyCallable) -> AnyCallable: # pragma: debugging OBJ_IDS = itertools.count() CALLS = itertools.count() -OBJ_ID_ATTR = "$coverage.object_id" +OBJ_ID_ATTR = '$coverage.object_id' + def show_calls( show_args: bool = True, @@ -555,27 +567,27 @@ def show_calls( def _wrapper(self: Any, *args: Any, **kwargs: Any) -> Any: oid = getattr(self, OBJ_ID_ATTR, None) if oid is None: - oid = f"{os.getpid():08d} {next(OBJ_IDS):04d}" + oid = f'{os.getpid():08d} {next(OBJ_IDS):04d}' setattr(self, OBJ_ID_ATTR, oid) - extra = "" + extra = '' if show_args: - eargs = ", ".join(map(repr, args)) - ekwargs = ", ".join("{}={!r}".format(*item) for item in kwargs.items()) - extra += "(" + eargs = ', '.join(map(repr, args)) + ekwargs = ', '.join('{}={!r}'.format(*item) for item in kwargs.items()) + extra += '(' extra += eargs if eargs and ekwargs: - extra += ", " + extra += ', ' extra += ekwargs - extra += ")" + extra += ')' if show_stack: - extra += " @ " - extra += "; ".join(short_stack(short_filenames=True).splitlines()) + extra += ' @ ' + extra += '; '.join(short_stack(short_filenames=True).splitlines()) callid = next(CALLS) - msg = f"{oid} {callid:04d} {func.__name__}{extra}\n" + msg = f'{oid} {callid:04d} {func.__name__}{extra}\n' DebugOutputFile.get_one(interim=True).write(msg) ret = func(self, *args, **kwargs) if show_return: - msg = f"{oid} {callid:04d} {func.__name__} return {ret!r}\n" + msg = f'{oid} {callid:04d} {func.__name__} return {ret!r}\n' DebugOutputFile.get_one(interim=True).write(msg) return ret return _wrapper @@ -595,9 +607,9 @@ def relevant_environment_display(env: Mapping[str, str]) -> list[tuple[str, str] A list of pairs (name, value) to show. """ - slugs = {"COV", "PY"} - include = {"HOME", "TEMP", "TMP"} - cloak = {"API", "TOKEN", "KEY", "SECRET", "PASS", "SIGNATURE"} + slugs = {'COV', 'PY'} + include = {'HOME', 'TEMP', 'TMP'} + cloak = {'API', 'TOKEN', 'KEY', 'SECRET', 'PASS', 'SIGNATURE'} to_show = [] for name, val in env.items(): @@ -608,6 +620,6 @@ def relevant_environment_display(env: Mapping[str, str]) -> list[tuple[str, str] keep = True if keep: if any(slug in name for slug in cloak): - val = re.sub(r"\w", "*", val) + val = re.sub(r'\w', '*', val) to_show.append((name, val)) return human_sorted_items(to_show) diff --git a/.venv/lib/python3.10/site-packages/coverage/disposition.py b/.venv/lib/python3.10/site-packages/coverage/disposition.py index 7aa15e9..84f682b 100644 --- a/.venv/lib/python3.10/site-packages/coverage/disposition.py +++ b/.venv/lib/python3.10/site-packages/coverage/disposition.py @@ -1,8 +1,6 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Simple value objects for tracking what to do with files.""" - from __future__ import annotations from typing import TYPE_CHECKING @@ -25,7 +23,7 @@ class FileDisposition: has_dynamic_filename: bool def __repr__(self) -> str: - return f"" + return f'' # FileDisposition "methods": FileDisposition is a pure value object, so it can @@ -39,7 +37,7 @@ def disposition_init(cls: type[TFileDisposition], original_filename: str) -> TFi disp.canonical_filename = original_filename disp.source_filename = None disp.trace = False - disp.reason = "" + disp.reason = '' disp.file_tracer = None disp.has_dynamic_filename = False return disp @@ -48,11 +46,11 @@ def disposition_init(cls: type[TFileDisposition], original_filename: str) -> TFi def disposition_debug_msg(disp: TFileDisposition) -> str: """Make a nice debug message of what the FileDisposition is doing.""" if disp.trace: - msg = f"Tracing {disp.original_filename!r}" + msg = f'Tracing {disp.original_filename!r}' if disp.original_filename != disp.source_filename: - msg += f" as {disp.source_filename!r}" + msg += f' as {disp.source_filename!r}' if disp.file_tracer: - msg += f": will be traced by {disp.file_tracer!r}" + msg += f': will be traced by {disp.file_tracer!r}' else: - msg = f"Not tracing {disp.original_filename!r}: {disp.reason}" + msg = f'Not tracing {disp.original_filename!r}: {disp.reason}' return msg diff --git a/.venv/lib/python3.10/site-packages/coverage/env.py b/.venv/lib/python3.10/site-packages/coverage/env.py index 063f9d1..53d5dd8 100644 --- a/.venv/lib/python3.10/site-packages/coverage/env.py +++ b/.venv/lib/python3.10/site-packages/coverage/env.py @@ -1,48 +1,48 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Determine facts about the environment.""" - from __future__ import annotations import os import platform import sys - -from typing import Any, Iterable +from typing import Any +from typing import Iterable # debug_info() at the bottom wants to show all the globals, but not imports. # Grab the global names here to know which names to not show. Nothing defined # above this line will be in the output. _UNINTERESTING_GLOBALS = list(globals()) # These names also shouldn't be shown. -_UNINTERESTING_GLOBALS += ["PYBEHAVIOR", "debug_info"] +_UNINTERESTING_GLOBALS += ['PYBEHAVIOR', 'debug_info'] # Operating systems. -WINDOWS = sys.platform == "win32" -LINUX = sys.platform.startswith("linux") -OSX = sys.platform == "darwin" +WINDOWS = sys.platform == 'win32' +LINUX = sys.platform.startswith('linux') +OSX = sys.platform == 'darwin' # Python implementations. -CPYTHON = (platform.python_implementation() == "CPython") -PYPY = (platform.python_implementation() == "PyPy") +CPYTHON = (platform.python_implementation() == 'CPython') +PYPY = (platform.python_implementation() == 'PyPy') # Python versions. We amend version_info with one more value, a zero if an # official version, or 1 if built from source beyond an official version. # Only use sys.version_info directly where tools like mypy need it to understand # version-specfic code, otherwise use PYVERSION. -PYVERSION = sys.version_info + (int(platform.python_version()[-1] == "+"),) +PYVERSION = sys.version_info + (int(platform.python_version()[-1] == '+'),) if PYPY: PYPYVERSION = sys.pypy_version_info # type: ignore[attr-defined] # Python behavior. + + class PYBEHAVIOR: """Flags indicating this Python's behavior.""" # Does Python conform to PEP626, Precise line numbers for debugging and other tools. # https://www.python.org/dev/peps/pep-0626 - pep626 = (PYVERSION > (3, 10, 0, "alpha", 4)) + pep626 = (PYVERSION > (3, 10, 0, 'alpha', 4)) # Is "if __debug__" optimized away? optimize_if_debug = not pep626 @@ -69,19 +69,19 @@ class PYBEHAVIOR: # CPython 3.11 now jumps to the decorator line again while executing # the decorator. - trace_decorator_line_again = (CPYTHON and PYVERSION > (3, 11, 0, "alpha", 3, 0)) + trace_decorator_line_again = (CPYTHON and PYVERSION > (3, 11, 0, 'alpha', 3, 0)) # CPython 3.9a1 made sys.argv[0] and other reported files absolute paths. report_absolute_files = ( - (CPYTHON or (PYPY and PYPYVERSION >= (7, 3, 10))) - and PYVERSION >= (3, 9) + (CPYTHON or (PYPY and PYPYVERSION >= (7, 3, 10))) and + PYVERSION >= (3, 9) ) # Lines after break/continue/return/raise are no longer compiled into the # bytecode. They used to be marked as missing, now they aren't executable. omit_after_jump = ( - pep626 - or (PYPY and PYVERSION >= (3, 9) and PYPYVERSION >= (7, 3, 12)) + pep626 or + (PYPY and PYVERSION >= (3, 9) and PYPYVERSION >= (7, 3, 12)) ) # PyPy has always omitted statements after return. @@ -98,7 +98,7 @@ class PYBEHAVIOR: keep_constant_test = pep626 # When leaving a with-block, do we visit the with-line again for the exit? - exit_through_with = (PYVERSION >= (3, 10, 0, "beta")) + exit_through_with = (PYVERSION >= (3, 10, 0, 'beta')) # Match-case construct. match_case = (PYVERSION >= (3, 10)) @@ -108,14 +108,14 @@ class PYBEHAVIOR: # Modules start with a line numbered zero. This means empty modules have # only a 0-number line, which is ignored, giving a truly empty module. - empty_is_empty = (PYVERSION >= (3, 11, 0, "beta", 4)) + empty_is_empty = (PYVERSION >= (3, 11, 0, 'beta', 4)) # Are comprehensions inlined (new) or compiled as called functions (old)? # Changed in https://github.com/python/cpython/pull/101441 - comprehensions_are_functions = (PYVERSION <= (3, 12, 0, "alpha", 7, 0)) + comprehensions_are_functions = (PYVERSION <= (3, 12, 0, 'alpha', 7, 0)) # PEP669 Low Impact Monitoring: https://peps.python.org/pep-0669/ - pep669 = bool(getattr(sys, "monitoring", None)) + pep669 = bool(getattr(sys, 'monitoring', None)) # Where does frame.f_lasti point when yielding from a generator? # It used to point at the YIELD, now it points at the RESUME. @@ -126,22 +126,22 @@ class PYBEHAVIOR: # Coverage.py specifics, about testing scenarios. See tests/testenv.py also. # Are we coverage-measuring ourselves? -METACOV = os.getenv("COVERAGE_COVERAGE") is not None +METACOV = os.getenv('COVERAGE_COVERAGE') is not None # Are we running our test suite? # Even when running tests, you can use COVERAGE_TESTING=0 to disable the # test-specific behavior like AST checking. -TESTING = os.getenv("COVERAGE_TESTING") == "True" +TESTING = os.getenv('COVERAGE_TESTING') == 'True' def debug_info() -> Iterable[tuple[str, Any]]: """Return a list of (name, value) pairs for printing debug information.""" info = [ (name, value) for name, value in globals().items() - if not name.startswith("_") and name not in _UNINTERESTING_GLOBALS + if not name.startswith('_') and name not in _UNINTERESTING_GLOBALS ] info += [ (name, value) for name, value in PYBEHAVIOR.__dict__.items() - if not name.startswith("_") + if not name.startswith('_') ] return sorted(info) diff --git a/.venv/lib/python3.10/site-packages/coverage/exceptions.py b/.venv/lib/python3.10/site-packages/coverage/exceptions.py index ecd1b5e..81a22d9 100644 --- a/.venv/lib/python3.10/site-packages/coverage/exceptions.py +++ b/.venv/lib/python3.10/site-packages/coverage/exceptions.py @@ -1,10 +1,9 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Exceptions coverage.py can raise.""" - from __future__ import annotations + class _BaseCoverageException(Exception): """The base-base of all Coverage exceptions.""" pass @@ -24,6 +23,7 @@ class DataError(CoverageException): """An error in using a data file.""" pass + class NoDataError(CoverageException): """We didn't have data to work with.""" pass diff --git a/.venv/lib/python3.10/site-packages/coverage/execfile.py b/.venv/lib/python3.10/site-packages/coverage/execfile.py index 7011c70..5f5e723 100644 --- a/.venv/lib/python3.10/site-packages/coverage/execfile.py +++ b/.venv/lib/python3.10/site-packages/coverage/execfile.py @@ -1,8 +1,6 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Execute files of Python code.""" - from __future__ import annotations import importlib.machinery @@ -12,14 +10,18 @@ import marshal import os import struct import sys - from importlib.machinery import ModuleSpec -from types import CodeType, ModuleType +from types import CodeType +from types import ModuleType from typing import Any from coverage import env -from coverage.exceptions import CoverageException, _ExceptionDuringRun, NoCode, NoSource -from coverage.files import canonical_filename, python_reported_file +from coverage.exceptions import _ExceptionDuringRun +from coverage.exceptions import CoverageException +from coverage.exceptions import NoCode +from coverage.exceptions import NoSource +from coverage.files import canonical_filename +from coverage.files import python_reported_file from coverage.misc import isolate_module from coverage.python import get_python_source @@ -28,11 +30,13 @@ os = isolate_module(os) PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER + class DummyLoader: """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader. Currently only implements the .fullname attribute """ + def __init__(self, fullname: str, *_args: Any) -> None: self.fullname = fullname @@ -50,20 +54,20 @@ def find_module( except ImportError as err: raise NoSource(str(err)) from err if not spec: - raise NoSource(f"No module named {modulename!r}") + raise NoSource(f'No module named {modulename!r}') pathname = spec.origin packagename = spec.name if spec.submodule_search_locations: - mod_main = modulename + ".__main__" + mod_main = modulename + '.__main__' spec = importlib.util.find_spec(mod_main) if not spec: raise NoSource( - f"No module named {mod_main}; " + - f"{modulename!r} is a package and cannot be directly executed", + f'No module named {mod_main}; ' + + f'{modulename!r} is a package and cannot be directly executed', ) pathname = spec.origin packagename = spec.name - packagename = packagename.rpartition(".")[0] + packagename = packagename.rpartition('.')[0] return pathname, packagename, spec @@ -73,6 +77,7 @@ class PyRunner: This is meant to emulate real Python execution as closely as possible. """ + def __init__(self, args: list[str], as_module: bool = False) -> None: self.args = args self.as_module = as_module @@ -142,8 +147,8 @@ class PyRunner: elif os.path.isdir(self.arg0): # Running a directory means running the __main__.py file in that # directory. - for ext in [".py", ".pyc", ".pyo"]: - try_filename = os.path.join(self.arg0, "__main__" + ext) + for ext in ['.py', '.pyc', '.pyo']: + try_filename = os.path.join(self.arg0, '__main__' + ext) # 3.8.10 changed how files are reported when running a # directory. But I'm not sure how far this change is going to # spread, so I'll just hard-code it here for now. @@ -157,12 +162,12 @@ class PyRunner: # Make a spec. I don't know if this is the right way to do it. try_filename = python_reported_file(try_filename) - self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename) + self.spec = importlib.machinery.ModuleSpec('__main__', None, origin=try_filename) self.spec.has_location = True - self.package = "" - self.loader = DummyLoader("__main__") + self.package = '' + self.loader = DummyLoader('__main__') else: - self.loader = DummyLoader("__main__") + self.loader = DummyLoader('__main__') self.arg0 = python_reported_file(self.arg0) @@ -172,9 +177,9 @@ class PyRunner: self._prepare2() # Create a module to serve as __main__ - main_mod = ModuleType("__main__") + main_mod = ModuleType('__main__') - from_pyc = self.arg0.endswith((".pyc", ".pyo")) + from_pyc = self.arg0.endswith(('.pyc', '.pyo')) main_mod.__file__ = self.arg0 if from_pyc: main_mod.__file__ = main_mod.__file__[:-1] @@ -184,9 +189,9 @@ class PyRunner: if self.spec is not None: main_mod.__spec__ = self.spec - main_mod.__builtins__ = sys.modules["builtins"] # type: ignore[attr-defined] + main_mod.__builtins__ = sys.modules['builtins'] # type: ignore[attr-defined] - sys.modules["__main__"] = main_mod + sys.modules['__main__'] = main_mod # Set sys.argv properly. sys.argv = self.args @@ -228,7 +233,7 @@ class PyRunner: # is non-None when the exception is reported at the upper layer, # and a nested exception is shown to the user. This getattr fixes # it somehow? https://bitbucket.org/pypy/pypy/issue/1903 - getattr(err, "__context__", None) + getattr(err, '__context__', None) # Call the excepthook. try: @@ -240,7 +245,7 @@ class PyRunner: except Exception as exc: # Getting the output right in the case of excepthook # shenanigans is kind of involved. - sys.stderr.write("Error in sys.excepthook:\n") + sys.stderr.write('Error in sys.excepthook:\n') typ2, err2, tb2 = sys.exc_info() assert typ2 is not None assert err2 is not None @@ -249,7 +254,7 @@ class PyRunner: assert err2.__traceback__ is not None err2.__traceback__ = err2.__traceback__.tb_next sys.__excepthook__(typ2, err2, tb2.tb_next) - sys.stderr.write("\nOriginal exception was:\n") + sys.stderr.write('\nOriginal exception was:\n') raise _ExceptionDuringRun(typ, err, tb.tb_next) from exc else: sys.exit(1) @@ -294,13 +299,13 @@ def make_code_from_py(filename: str) -> CodeType: except (OSError, NoSource) as exc: raise NoSource(f"No file to run: '{filename}'") from exc - return compile(source, filename, "exec", dont_inherit=True) + return compile(source, filename, 'exec', dont_inherit=True) def make_code_from_pyc(filename: str) -> CodeType: """Get a code object from a .pyc file.""" try: - fpyc = open(filename, "rb") + fpyc = open(filename, 'rb') except OSError as exc: raise NoCode(f"No file to run: '{filename}'") from exc @@ -309,9 +314,9 @@ def make_code_from_pyc(filename: str) -> CodeType: # match or we won't run the file. magic = fpyc.read(4) if magic != PYC_MAGIC_NUMBER: - raise NoCode(f"Bad magic number in .pyc file: {magic!r} != {PYC_MAGIC_NUMBER!r}") + raise NoCode(f'Bad magic number in .pyc file: {magic!r} != {PYC_MAGIC_NUMBER!r}') - flags = struct.unpack(" None: """Set the directory that `relative_filename` will be relative to.""" global RELATIVE_DIR, CANONICAL_FILENAME_CACHE @@ -73,7 +73,7 @@ def canonical_filename(filename: str) -> str: if not os.path.isabs(filename): for path in [os.curdir] + sys.path: if path is None: - continue # type: ignore[unreachable] + continue # type: ignore[unreachable] f = os.path.join(path, filename) try: exists = os.path.exists(f) @@ -89,6 +89,7 @@ def canonical_filename(filename: str) -> str: MAX_FLAT = 100 + def flat_rootname(filename: str) -> str: """A base for a flat file name to correspond to this file. @@ -101,11 +102,11 @@ def flat_rootname(filename: str) -> str: """ dirname, basename = ntpath.split(filename) if dirname: - fp = hashlib.new("sha3_256", dirname.encode("UTF-8")).hexdigest()[:16] - prefix = f"d_{fp}_" + fp = hashlib.new('sha3_256', dirname.encode('UTF-8')).hexdigest()[:16] + prefix = f'd_{fp}_' else: - prefix = "" - return prefix + basename.replace(".", "_") + prefix = '' + return prefix + basename.replace('.', '_') if env.WINDOWS: @@ -163,7 +164,7 @@ def zip_location(filename: str) -> tuple[str, str] | None: name is in the zipfile. """ - for ext in [".zip", ".whl", ".egg", ".pex"]: + for ext in ['.zip', '.whl', '.egg', '.pex']: zipbase, extension, inner = filename.partition(ext + sep(filename)) if extension: zipfile = zipbase + ext @@ -210,7 +211,7 @@ def prep_patterns(patterns: Iterable[str]) -> list[str]: prepped = [] for p in patterns or []: prepped.append(p) - if not p.startswith(("*", "?")): + if not p.startswith(('*', '?')): prepped.append(abs_file(p)) return prepped @@ -223,14 +224,15 @@ class TreeMatcher: somewhere in a subtree rooted at one of the directories. """ - def __init__(self, paths: Iterable[str], name: str = "unknown") -> None: + + def __init__(self, paths: Iterable[str], name: str = 'unknown') -> None: self.original_paths: list[str] = human_sorted(paths) #self.paths = list(map(os.path.normcase, paths)) self.paths = [os.path.normcase(p) for p in paths] self.name = name def __repr__(self) -> str: - return f"" + return f'' def info(self) -> list[str]: """A list of strings for displaying when dumping state.""" @@ -252,12 +254,13 @@ class TreeMatcher: class ModuleMatcher: """A matcher for modules in a tree.""" - def __init__(self, module_names: Iterable[str], name:str = "unknown") -> None: + + def __init__(self, module_names: Iterable[str], name: str = 'unknown') -> None: self.modules = list(module_names) self.name = name def __repr__(self) -> str: - return f"" + return f'' def info(self) -> list[str]: """A list of strings for displaying when dumping state.""" @@ -272,7 +275,7 @@ class ModuleMatcher: if module_name.startswith(m): if module_name == m: return True - if module_name[len(m)] == ".": + if module_name[len(m)] == '.': # This is a module in the package return True @@ -281,13 +284,14 @@ class ModuleMatcher: class GlobMatcher: """A matcher for files by file name pattern.""" - def __init__(self, pats: Iterable[str], name: str = "unknown") -> None: + + def __init__(self, pats: Iterable[str], name: str = 'unknown') -> None: self.pats = list(pats) self.re = globs_to_regex(self.pats, case_insensitive=env.WINDOWS) self.name = name def __repr__(self) -> str: - return f"" + return f'' def info(self) -> list[str]: """A list of strings for displaying when dumping state.""" @@ -300,7 +304,7 @@ class GlobMatcher: def sep(s: str) -> str: """Find the path separator used in this string, or os.sep if none.""" - if sep_match := re.search(r"[\\/]", s): + if sep_match := re.search(r'[\\/]', s): the_sep = sep_match[0] else: the_sep = os.sep @@ -309,29 +313,32 @@ def sep(s: str) -> str: # Tokenizer for _glob_to_regex. # None as a sub means disallowed. -G2RX_TOKENS = [(re.compile(rx), sub) for rx, sub in [ - (r"\*\*\*+", None), # Can't have *** - (r"[^/]+\*\*+", None), # Can't have x** - (r"\*\*+[^/]+", None), # Can't have **x - (r"\*\*/\*\*", None), # Can't have **/** - (r"^\*+/", r"(.*[/\\\\])?"), # ^*/ matches any prefix-slash, or nothing. - (r"/\*+$", r"[/\\\\].*"), # /*$ matches any slash-suffix. - (r"\*\*/", r"(.*[/\\\\])?"), # **/ matches any subdirs, including none - (r"/", r"[/\\\\]"), # / matches either slash or backslash - (r"\*", r"[^/\\\\]*"), # * matches any number of non slash-likes - (r"\?", r"[^/\\\\]"), # ? matches one non slash-like - (r"\[.*?\]", r"\g<0>"), # [a-f] matches [a-f] - (r"[a-zA-Z0-9_-]+", r"\g<0>"), # word chars match themselves - (r"[\[\]]", None), # Can't have single square brackets - (r".", r"\\\g<0>"), # Anything else is escaped to be safe -]] +G2RX_TOKENS = [ + (re.compile(rx), sub) for rx, sub in [ + (r'\*\*\*+', None), # Can't have *** + (r'[^/]+\*\*+', None), # Can't have x** + (r'\*\*+[^/]+', None), # Can't have **x + (r'\*\*/\*\*', None), # Can't have **/** + (r'^\*+/', r'(.*[/\\\\])?'), # ^*/ matches any prefix-slash, or nothing. + (r'/\*+$', r'[/\\\\].*'), # /*$ matches any slash-suffix. + (r'\*\*/', r'(.*[/\\\\])?'), # **/ matches any subdirs, including none + (r'/', r'[/\\\\]'), # / matches either slash or backslash + (r'\*', r'[^/\\\\]*'), # * matches any number of non slash-likes + (r'\?', r'[^/\\\\]'), # ? matches one non slash-like + (r'\[.*?\]', r'\g<0>'), # [a-f] matches [a-f] + (r'[a-zA-Z0-9_-]+', r'\g<0>'), # word chars match themselves + (r'[\[\]]', None), # Can't have single square brackets + (r'.', r'\\\g<0>'), # Anything else is escaped to be safe + ] +] + def _glob_to_regex(pattern: str) -> str: """Convert a file-path glob pattern into a regex.""" # Turn all backslashes into slashes to simplify the tokenizer. - pattern = pattern.replace("\\", "/") - if "/" not in pattern: - pattern = "**/" + pattern + pattern = pattern.replace('\\', '/') + if '/' not in pattern: + pattern = '**/' + pattern path_rx = [] pos = 0 while pos < len(pattern): @@ -342,7 +349,7 @@ def _glob_to_regex(pattern: str) -> str: path_rx.append(m.expand(sub)) pos = m.end() break - return "".join(path_rx) + return ''.join(path_rx) def globs_to_regex( @@ -371,7 +378,7 @@ def globs_to_regex( flags |= re.IGNORECASE rx = join_regex(map(_glob_to_regex, patterns)) if not partial: - rx = fr"(?:{rx})\Z" + rx = fr'(?:{rx})\Z' compiled = re.compile(rx, flags=flags) return compiled @@ -387,6 +394,7 @@ class PathAliases: map a path through those aliases to produce a unified path. """ + def __init__( self, debugfn: Callable[[str], None] | None = None, @@ -400,9 +408,9 @@ class PathAliases: def pprint(self) -> None: """Dump the important parts of the PathAliases, for debugging.""" - self.debugfn(f"Aliases (relative={self.relative}):") + self.debugfn(f'Aliases (relative={self.relative}):') for original_pattern, regex, result in self.aliases: - self.debugfn(f" Rule: {original_pattern!r} -> {result!r} using regex {regex.pattern!r}") + self.debugfn(f' Rule: {original_pattern!r} -> {result!r} using regex {regex.pattern!r}') def add(self, pattern: str, result: str) -> None: """Add the `pattern`/`result` pair to the list of aliases. @@ -421,16 +429,16 @@ class PathAliases: pattern_sep = sep(pattern) if len(pattern) > 1: - pattern = pattern.rstrip(r"\/") + pattern = pattern.rstrip(r'\/') # The pattern can't end with a wildcard component. - if pattern.endswith("*"): - raise ConfigError("Pattern must not end with wildcards.") + if pattern.endswith('*'): + raise ConfigError('Pattern must not end with wildcards.') # The pattern is meant to match a file path. Let's make it absolute # unless it already is, or is meant to match any prefix. if not self.relative: - if not pattern.startswith("*") and not isabs_anywhere(pattern + pattern_sep): + if not pattern.startswith('*') and not isabs_anywhere(pattern + pattern_sep): pattern = abs_file(pattern) if not pattern.endswith(pattern_sep): pattern += pattern_sep @@ -440,10 +448,10 @@ class PathAliases: # Normalize the result: it must end with a path separator. result_sep = sep(result) - result = result.rstrip(r"\/") + result_sep + result = result.rstrip(r'\/') + result_sep self.aliases.append((original_pattern, regex, result)) - def map(self, path: str, exists:Callable[[str], bool] = source_exists) -> str: + def map(self, path: str, exists: Callable[[str], bool] = source_exists) -> str: """Map `path` through the aliases. `path` is checked against all of the patterns. The first pattern to @@ -472,18 +480,18 @@ class PathAliases: new = new.replace(sep(path), sep(result)) if not self.relative: new = canonical_filename(new) - dot_start = result.startswith(("./", ".\\")) and len(result) > 2 - if new.startswith(("./", ".\\")) and not dot_start: + dot_start = result.startswith(('./', '.\\')) and len(result) > 2 + if new.startswith(('./', '.\\')) and not dot_start: new = new[2:] if not exists(new): self.debugfn( - f"Rule {original_pattern!r} changed {path!r} to {new!r} " + + f'Rule {original_pattern!r} changed {path!r} to {new!r} ' + "which doesn't exist, continuing", ) continue self.debugfn( - f"Matched path {path!r} to rule {original_pattern!r} -> {result!r}, " + - f"producing {new!r}", + f'Matched path {path!r} to rule {original_pattern!r} -> {result!r}, ' + + f'producing {new!r}', ) return new @@ -494,21 +502,21 @@ class PathAliases: if self.relative and not isabs_anywhere(path): # Auto-generate a pattern to implicitly match relative files - parts = re.split(r"[/\\]", path) + parts = re.split(r'[/\\]', path) if len(parts) > 1: dir1 = parts[0] - pattern = f"*/{dir1}" - regex_pat = fr"^(.*[\\/])?{re.escape(dir1)}[\\/]" - result = f"{dir1}{os.sep}" + pattern = f'*/{dir1}' + regex_pat = fr'^(.*[\\/])?{re.escape(dir1)}[\\/]' + result = f'{dir1}{os.sep}' # Only add a new pattern if we don't already have this pattern. if not any(p == pattern for p, _, _ in self.aliases): self.debugfn( - f"Generating rule: {pattern!r} -> {result!r} using regex {regex_pat!r}", + f'Generating rule: {pattern!r} -> {result!r} using regex {regex_pat!r}', ) self.aliases.append((pattern, re.compile(regex_pat), result)) return self.map(path, exists=exists) - self.debugfn(f"No rules match, path {path!r} is unchanged") + self.debugfn(f'No rules match, path {path!r} is unchanged') return path @@ -530,7 +538,7 @@ def find_python_files(dirname: str, include_namespace_packages: bool) -> Iterabl """ for i, (dirpath, dirnames, filenames) in enumerate(os.walk(dirname)): if not include_namespace_packages: - if i > 0 and "__init__.py" not in filenames: + if i > 0 and '__init__.py' not in filenames: # If a directory doesn't have __init__.py, then it isn't # importable and neither are its files del dirnames[:] @@ -539,7 +547,7 @@ def find_python_files(dirname: str, include_namespace_packages: bool) -> Iterabl # We're only interested in files that look like reasonable Python # files: Must end with .py or .pyw, and must not have certain funny # characters that probably mean they are editor junk. - if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename): + if re.match(r'^[^.#~!$@%^&*()+=,]+\.pyw?$', filename): yield os.path.join(dirpath, filename) diff --git a/.venv/lib/python3.10/site-packages/coverage/html.py b/.venv/lib/python3.10/site-packages/coverage/html.py index e2bae1d..2a9f36b 100644 --- a/.venv/lib/python3.10/site-packages/coverage/html.py +++ b/.venv/lib/python3.10/site-packages/coverage/html.py @@ -1,8 +1,6 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """HTML reporting for coverage.py.""" - from __future__ import annotations import collections @@ -13,20 +11,31 @@ import os import re import shutil import string - from dataclasses import dataclass -from typing import Any, Iterable, TYPE_CHECKING, cast +from typing import Any +from typing import cast +from typing import Iterable +from typing import TYPE_CHECKING import coverage -from coverage.data import CoverageData, add_data_to_hash +from coverage.data import add_data_to_hash +from coverage.data import CoverageData from coverage.exceptions import NoDataError from coverage.files import flat_rootname -from coverage.misc import ensure_dir, file_be_gone, Hasher, isolate_module, format_local_datetime -from coverage.misc import human_sorted, plural, stdout_link +from coverage.misc import ensure_dir +from coverage.misc import file_be_gone +from coverage.misc import format_local_datetime +from coverage.misc import Hasher +from coverage.misc import human_sorted +from coverage.misc import isolate_module +from coverage.misc import plural +from coverage.misc import stdout_link from coverage.report_core import get_analysis_to_report -from coverage.results import Analysis, Numbers +from coverage.results import Analysis +from coverage.results import Numbers from coverage.templite import Templite -from coverage.types import TLineNo, TMorf +from coverage.types import TLineNo +from coverage.types import TMorf from coverage.version import __url__ @@ -56,7 +65,7 @@ os = isolate_module(os) def data_filename(fname: str) -> str: """Return the path to an "htmlfiles" data file of ours. """ - static_dir = os.path.join(os.path.dirname(__file__), "htmlfiles") + static_dir = os.path.join(os.path.dirname(__file__), 'htmlfiles') static_filename = os.path.join(static_dir, fname) return static_filename @@ -69,9 +78,9 @@ def read_data(fname: str) -> str: def write_html(fname: str, html: str) -> None: """Write `html` to `fname`, properly encoded.""" - html = re.sub(r"(\A\s+)|(\s+$)", "", html, flags=re.MULTILINE) + "\n" - with open(fname, "wb") as fout: - fout.write(html.encode("ascii", "xmlcharrefreplace")) + html = re.sub(r'(\A\s+)|(\s+$)', '', html, flags=re.MULTILINE) + '\n' + with open(fname, 'wb') as fout: + fout.write(html.encode('ascii', 'xmlcharrefreplace')) @dataclass @@ -86,11 +95,11 @@ class LineData: context_list: list[str] short_annotations: list[str] long_annotations: list[str] - html: str = "" + html: str = '' context_str: str | None = None annotate: str | None = None annotate_long: str | None = None - css_class: str = "" + css_class: str = '' @dataclass @@ -104,7 +113,7 @@ class FileData: class HtmlDataGeneration: """Generate structured data to be turned into HTML reports.""" - EMPTY = "(empty)" + EMPTY = '(empty)' def __init__(self, cov: Coverage) -> None: self.coverage = cov @@ -112,8 +121,8 @@ class HtmlDataGeneration: data = self.coverage.get_data() self.has_arcs = data.has_arcs() if self.config.show_contexts: - if data.measured_contexts() == {""}: - self.coverage._warn("No contexts were measured") + if data.measured_contexts() == {''}: + self.coverage._warn('No contexts were measured') data.set_query_contexts(self.config.report_contexts) def data_for_file(self, fr: FileReporter, analysis: Analysis) -> FileData: @@ -129,47 +138,49 @@ class HtmlDataGeneration: for lineno, tokens in enumerate(fr.source_token_lines(), start=1): # Figure out how to mark this line. - category = "" + category = '' short_annotations = [] long_annotations = [] if lineno in analysis.excluded: - category = "exc" + category = 'exc' elif lineno in analysis.missing: - category = "mis" + category = 'mis' elif self.has_arcs and lineno in missing_branch_arcs: - category = "par" + category = 'par' for b in missing_branch_arcs[lineno]: if b < 0: - short_annotations.append("exit") + short_annotations.append('exit') else: short_annotations.append(str(b)) long_annotations.append(fr.missing_arc_description(lineno, b, arcs_executed)) elif lineno in analysis.statements: - category = "run" + category = 'run' contexts = [] - contexts_label = "" + contexts_label = '' context_list = [] if category and self.config.show_contexts: contexts = human_sorted(c or self.EMPTY for c in contexts_by_lineno.get(lineno, ())) if contexts == [self.EMPTY]: contexts_label = self.EMPTY else: - contexts_label = f"{len(contexts)} ctx" + contexts_label = f'{len(contexts)} ctx' context_list = contexts - lines.append(LineData( - tokens=tokens, - number=lineno, - category=category, - statement=(lineno in analysis.statements), - contexts=contexts, - contexts_label=contexts_label, - context_list=context_list, - short_annotations=short_annotations, - long_annotations=long_annotations, - )) + lines.append( + LineData( + tokens=tokens, + number=lineno, + category=category, + statement=(lineno in analysis.statements), + contexts=contexts, + contexts_label=contexts_label, + context_list=context_list, + short_annotations=short_annotations, + long_annotations=long_annotations, + ), + ) file_data = FileData( relative_filename=fr.relative_filename(), @@ -182,15 +193,17 @@ class HtmlDataGeneration: class FileToReport: """A file we're considering reporting.""" + def __init__(self, fr: FileReporter, analysis: Analysis) -> None: self.fr = fr self.analysis = analysis self.rootname = flat_rootname(fr.relative_filename()) - self.html_filename = self.rootname + ".html" + self.html_filename = self.rootname + '.html' HTML_SAFE = string.ascii_letters + string.digits + "!#$%'()*+,-./:;=?@[]^_`{|}~" + @functools.lru_cache(maxsize=None) def encode_int(n: int) -> str: """Create a short HTML-safe string from an integer, using HTML_SAFE.""" @@ -201,7 +214,7 @@ def encode_int(n: int) -> str: while n: n, t = divmod(n, len(HTML_SAFE)) r.append(HTML_SAFE[t]) - return "".join(r) + return ''.join(r) class HtmlReporter: @@ -210,11 +223,11 @@ class HtmlReporter: # These files will be copied from the htmlfiles directory to the output # directory. STATIC_FILES = [ - "style.css", - "coverage_html.js", - "keybd_closed.png", - "keybd_open.png", - "favicon_32.png", + 'style.css', + 'coverage_html.js', + 'keybd_closed.png', + 'keybd_open.png', + 'favicon_32.png', ] def __init__(self, cov: Coverage) -> None: @@ -253,29 +266,29 @@ class HtmlReporter: self.template_globals = { # Functions available in the templates. - "escape": escape, - "pair": pair, - "len": len, + 'escape': escape, + 'pair': pair, + 'len': len, # Constants for this report. - "__url__": __url__, - "__version__": coverage.__version__, - "title": title, - "time_stamp": format_local_datetime(datetime.datetime.now()), - "extra_css": self.extra_css, - "has_arcs": self.has_arcs, - "show_contexts": self.config.show_contexts, + '__url__': __url__, + '__version__': coverage.__version__, + 'title': title, + 'time_stamp': format_local_datetime(datetime.datetime.now()), + 'extra_css': self.extra_css, + 'has_arcs': self.has_arcs, + 'show_contexts': self.config.show_contexts, # Constants for all reports. # These css classes determine which lines are highlighted by default. - "category": { - "exc": "exc show_exc", - "mis": "mis show_mis", - "par": "par run show_par", - "run": "run", + 'category': { + 'exc': 'exc show_exc', + 'mis': 'mis show_mis', + 'par': 'par run show_par', + 'run': 'run', }, } - self.pyfile_html_source = read_data("pyfile.html") + self.pyfile_html_source = read_data('pyfile.html') self.source_tmpl = Templite(self.pyfile_html_source, self.template_globals) def report(self, morfs: Iterable[TMorf] | None) -> float: @@ -303,17 +316,17 @@ class HtmlReporter: for i, ftr in enumerate(files_to_report): if i == 0: - prev_html = "index.html" + prev_html = 'index.html' else: prev_html = files_to_report[i - 1].html_filename if i == len(files_to_report) - 1: - next_html = "index.html" + next_html = 'index.html' else: next_html = files_to_report[i + 1].html_filename self.write_html_file(ftr, prev_html, next_html) if not self.all_files_nums: - raise NoDataError("No data to report.") + raise NoDataError('No data to report.') self.totals = cast(Numbers, sum(self.all_files_nums)) @@ -322,7 +335,7 @@ class HtmlReporter: first_html = files_to_report[0].html_filename final_html = files_to_report[-1].html_filename else: - first_html = final_html = "index.html" + first_html = final_html = 'index.html' self.index_file(first_html, final_html) self.make_local_static_report_files() @@ -344,8 +357,8 @@ class HtmlReporter: # .gitignore can't be copied from the source tree because it would # prevent the static files from being checked in. if self.directory_was_empty: - with open(os.path.join(self.directory, ".gitignore"), "w") as fgi: - fgi.write("# Created by coverage.py\n*\n") + with open(os.path.join(self.directory, '.gitignore'), 'w') as fgi: + fgi.write('# Created by coverage.py\n*\n') # The user may have extra CSS they want copied. if self.extra_css: @@ -401,29 +414,29 @@ class HtmlReporter: # Build the HTML for the line. html_parts = [] for tok_type, tok_text in ldata.tokens: - if tok_type == "ws": + if tok_type == 'ws': html_parts.append(escape(tok_text)) else: - tok_html = escape(tok_text) or " " + tok_html = escape(tok_text) or ' ' html_parts.append(f'{tok_html}') - ldata.html = "".join(html_parts) + ldata.html = ''.join(html_parts) if ldata.context_list: encoded_contexts = [ encode_int(context_codes[c_context]) for c_context in ldata.context_list ] code_width = max(len(ec) for ec in encoded_contexts) ldata.context_str = ( - str(code_width) - + "".join(ec.ljust(code_width) for ec in encoded_contexts) + str(code_width) + + ''.join(ec.ljust(code_width) for ec in encoded_contexts) ) else: - ldata.context_str = "" + ldata.context_str = '' if ldata.short_annotations: # 202F is NARROW NO-BREAK SPACE. # 219B is RIGHTWARDS ARROW WITH STROKE. - ldata.annotate = ",   ".join( - f"{ldata.number} ↛ {d}" + ldata.annotate = ',   '.join( + f'{ldata.number} ↛ {d}' for d in ldata.short_annotations ) else: @@ -434,10 +447,10 @@ class HtmlReporter: if len(longs) == 1: ldata.annotate_long = longs[0] else: - ldata.annotate_long = "{:d} missed branches: {}".format( + ldata.annotate_long = '{:d} missed branches: {}'.format( len(longs), - ", ".join( - f"{num:d}) {ann_long}" + ', '.join( + f'{num:d}) {ann_long}' for num, ann_long in enumerate(longs, start=1) ), ) @@ -447,24 +460,24 @@ class HtmlReporter: css_classes = [] if ldata.category: css_classes.append( - self.template_globals["category"][ldata.category], # type: ignore[index] + self.template_globals['category'][ldata.category], # type: ignore[index] ) - ldata.css_class = " ".join(css_classes) or "pln" + ldata.css_class = ' '.join(css_classes) or 'pln' html_path = os.path.join(self.directory, ftr.html_filename) html = self.source_tmpl.render({ **file_data.__dict__, - "contexts_json": contexts_json, - "prev_html": prev_html, - "next_html": next_html, + 'contexts_json': contexts_json, + 'prev_html': prev_html, + 'next_html': next_html, }) write_html(html_path, html) # Save this file's information for the index file. index_info: IndexInfoDict = { - "nums": ftr.analysis.numbers, - "html_filename": ftr.html_filename, - "relative_filename": ftr.fr.relative_filename(), + 'nums': ftr.analysis.numbers, + 'html_filename': ftr.html_filename, + 'relative_filename': ftr.fr.relative_filename(), } self.file_summaries.append(index_info) self.incr.set_index_info(ftr.rootname, index_info) @@ -472,30 +485,30 @@ class HtmlReporter: def index_file(self, first_html: str, final_html: str) -> None: """Write the index.html file for this report.""" self.make_directory() - index_tmpl = Templite(read_data("index.html"), self.template_globals) + index_tmpl = Templite(read_data('index.html'), self.template_globals) - skipped_covered_msg = skipped_empty_msg = "" + skipped_covered_msg = skipped_empty_msg = '' if self.skipped_covered_count: n = self.skipped_covered_count - skipped_covered_msg = f"{n} file{plural(n)} skipped due to complete coverage." + skipped_covered_msg = f'{n} file{plural(n)} skipped due to complete coverage.' if self.skipped_empty_count: n = self.skipped_empty_count - skipped_empty_msg = f"{n} empty file{plural(n)} skipped." + skipped_empty_msg = f'{n} empty file{plural(n)} skipped.' html = index_tmpl.render({ - "files": self.file_summaries, - "totals": self.totals, - "skipped_covered_msg": skipped_covered_msg, - "skipped_empty_msg": skipped_empty_msg, - "first_html": first_html, - "final_html": final_html, + 'files': self.file_summaries, + 'totals': self.totals, + 'skipped_covered_msg': skipped_covered_msg, + 'skipped_empty_msg': skipped_empty_msg, + 'first_html': first_html, + 'final_html': final_html, }) - index_file = os.path.join(self.directory, "index.html") + index_file = os.path.join(self.directory, 'index.html') write_html(index_file, html) - print_href = stdout_link(index_file, f"file://{os.path.abspath(index_file)}") - self.coverage._message(f"Wrote HTML report to {print_href}") + print_href = stdout_link(index_file, f'file://{os.path.abspath(index_file)}') + self.coverage._message(f'Wrote HTML report to {print_href}') # Write the latest hashes for next time. self.incr.write() @@ -504,12 +517,12 @@ class HtmlReporter: class IncrementalChecker: """Logic and data to support incremental reporting.""" - STATUS_FILE = "status.json" + STATUS_FILE = 'status.json' STATUS_FORMAT = 2 NOTE = ( - "This file is an internal implementation detail to speed up HTML report" - + " generation. Its format can change at any time. You might be looking" - + " for the JSON report: https://coverage.rtfd.io/cmd.html#cmd-json" + 'This file is an internal implementation detail to speed up HTML report' + + ' generation. Its format can change at any time. You might be looking' + + ' for the JSON report: https://coverage.rtfd.io/cmd.html#cmd-json' ) # The data looks like: @@ -545,7 +558,7 @@ class IncrementalChecker: def reset(self) -> None: """Initialize to empty. Causes all files to be reported.""" - self.globals = "" + self.globals = '' self.files: dict[str, FileInfoDict] = {} def read(self) -> None: @@ -559,17 +572,17 @@ class IncrementalChecker: usable = False else: usable = True - if status["format"] != self.STATUS_FORMAT: + if status['format'] != self.STATUS_FORMAT: usable = False - elif status["version"] != coverage.__version__: + elif status['version'] != coverage.__version__: usable = False if usable: self.files = {} - for filename, fileinfo in status["files"].items(): - fileinfo["index"]["nums"] = Numbers(*fileinfo["index"]["nums"]) + for filename, fileinfo in status['files'].items(): + fileinfo['index']['nums'] = Numbers(*fileinfo['index']['nums']) self.files[filename] = fileinfo - self.globals = status["globals"] + self.globals = status['globals'] else: self.reset() @@ -578,19 +591,19 @@ class IncrementalChecker: status_file = os.path.join(self.directory, self.STATUS_FILE) files = {} for filename, fileinfo in self.files.items(): - index = fileinfo["index"] - index["nums"] = index["nums"].init_args() # type: ignore[typeddict-item] + index = fileinfo['index'] + index['nums'] = index['nums'].init_args() # type: ignore[typeddict-item] files[filename] = fileinfo status = { - "note": self.NOTE, - "format": self.STATUS_FORMAT, - "version": coverage.__version__, - "globals": self.globals, - "files": files, + 'note': self.NOTE, + 'format': self.STATUS_FORMAT, + 'version': coverage.__version__, + 'globals': self.globals, + 'files': files, } - with open(status_file, "w") as fout: - json.dump(status, fout, separators=(",", ":")) + with open(status_file, 'w') as fout: + json.dump(status, fout, separators=(',', ':')) def check_global_data(self, *data: Any) -> None: """Check the global data that can affect incremental reporting.""" @@ -609,7 +622,7 @@ class IncrementalChecker: `rootname` is the name being used for the file. """ m = Hasher() - m.update(fr.source().encode("utf-8")) + m.update(fr.source().encode('utf-8')) add_data_to_hash(data, fr.filename, m) this_hash = m.hexdigest() @@ -624,19 +637,19 @@ class IncrementalChecker: def file_hash(self, fname: str) -> str: """Get the hash of `fname`'s contents.""" - return self.files.get(fname, {}).get("hash", "") # type: ignore[call-overload] + return self.files.get(fname, {}).get('hash', '') # type: ignore[call-overload] def set_file_hash(self, fname: str, val: str) -> None: """Set the hash of `fname`'s contents.""" - self.files.setdefault(fname, {})["hash"] = val # type: ignore[typeddict-item] + self.files.setdefault(fname, {})['hash'] = val # type: ignore[typeddict-item] def index_info(self, fname: str) -> IndexInfoDict: """Get the information for index.html for `fname`.""" - return self.files.get(fname, {}).get("index", {}) # type: ignore + return self.files.get(fname, {}).get('index', {}) # type: ignore def set_index_info(self, fname: str, info: IndexInfoDict) -> None: """Set the information for index.html for `fname`.""" - self.files.setdefault(fname, {})["index"] = info # type: ignore[typeddict-item] + self.files.setdefault(fname, {})['index'] = info # type: ignore[typeddict-item] # Helpers for templates and generating HTML @@ -648,9 +661,9 @@ def escape(t: str) -> str: """ # Convert HTML special chars into HTML entities. - return t.replace("&", "&").replace("<", "<") + return t.replace('&', '&').replace('<', '<') def pair(ratio: tuple[int, int]) -> str: """Format a pair of numbers so JavaScript can read them in an attribute.""" - return "{} {}".format(*ratio) + return '{} {}'.format(*ratio) diff --git a/.venv/lib/python3.10/site-packages/coverage/inorout.py b/.venv/lib/python3.10/site-packages/coverage/inorout.py index 5ea29ed..96c1103 100644 --- a/.venv/lib/python3.10/site-packages/coverage/inorout.py +++ b/.venv/lib/python3.10/site-packages/coverage/inorout.py @@ -1,8 +1,6 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Determining whether files are being measured/reported or not.""" - from __future__ import annotations import importlib.util @@ -14,20 +12,31 @@ import re import sys import sysconfig import traceback - -from types import FrameType, ModuleType -from typing import ( - cast, Any, Iterable, TYPE_CHECKING, -) +from types import FrameType +from types import ModuleType +from typing import Any +from typing import cast +from typing import Iterable +from typing import TYPE_CHECKING from coverage import env -from coverage.disposition import FileDisposition, disposition_init -from coverage.exceptions import CoverageException, PluginError -from coverage.files import TreeMatcher, GlobMatcher, ModuleMatcher -from coverage.files import prep_patterns, find_python_files, canonical_filename +from coverage.disposition import disposition_init +from coverage.disposition import FileDisposition +from coverage.exceptions import CoverageException +from coverage.exceptions import PluginError +from coverage.files import canonical_filename +from coverage.files import find_python_files +from coverage.files import GlobMatcher +from coverage.files import ModuleMatcher +from coverage.files import prep_patterns +from coverage.files import TreeMatcher from coverage.misc import sys_modules_saved -from coverage.python import source_for_file, source_for_morf -from coverage.types import TFileDisposition, TMorf, TWarnFn, TDebugCtl +from coverage.python import source_for_file +from coverage.python import source_for_morf +from coverage.types import TDebugCtl +from coverage.types import TFileDisposition +from coverage.types import TMorf +from coverage.types import TWarnFn if TYPE_CHECKING: from coverage.config import CoverageConfig @@ -65,7 +74,7 @@ def canonical_path(morf: TMorf, directory: bool = False) -> str: """ morf_path = canonical_filename(source_for_morf(morf)) - if morf_path.endswith("__init__.py") or directory: + if morf_path.endswith('__init__.py') or directory: morf_path = os.path.split(morf_path)[0] return morf_path @@ -83,16 +92,16 @@ def name_for_module(filename: str, frame: FrameType | None) -> str: """ module_globals = frame.f_globals if frame is not None else {} - dunder_name: str = module_globals.get("__name__", None) + dunder_name: str = module_globals.get('__name__', None) - if isinstance(dunder_name, str) and dunder_name != "__main__": + if isinstance(dunder_name, str) and dunder_name != '__main__': # This is the usual case: an imported module. return dunder_name - spec = module_globals.get("__spec__", None) + spec = module_globals.get('__spec__', None) if spec: fullname = spec.name - if isinstance(fullname, str) and fullname != "__main__": + if isinstance(fullname, str) and fullname != '__main__': # Module loaded via: runpy -m return fullname @@ -106,12 +115,12 @@ def name_for_module(filename: str, frame: FrameType | None) -> str: def module_is_namespace(mod: ModuleType) -> bool: """Is the module object `mod` a PEP420 namespace module?""" - return hasattr(mod, "__path__") and getattr(mod, "__file__", None) is None + return hasattr(mod, '__path__') and getattr(mod, '__file__', None) is None def module_has_file(mod: ModuleType) -> bool: """Does the module object `mod` have an existing __file__ ?""" - mod__file__ = getattr(mod, "__file__", None) + mod__file__ = getattr(mod, '__file__', None) if mod__file__ is None: return False return os.path.exists(mod__file__) @@ -146,7 +155,7 @@ def add_stdlib_paths(paths: set[str]) -> None: # spread across a few locations. Look at all the candidate modules # we've imported, and take all the different ones. for m in modules_we_happen_to_have: - if hasattr(m, "__file__"): + if hasattr(m, '__file__'): paths.add(canonical_path(m, directory=True)) @@ -157,10 +166,10 @@ def add_third_party_paths(paths: set[str]) -> None: for scheme in scheme_names: # https://foss.heptapod.net/pypy/pypy/-/issues/3433 - better_scheme = "pypy_posix" if scheme == "pypy" else scheme - if os.name in better_scheme.split("_"): + better_scheme = 'pypy_posix' if scheme == 'pypy' else scheme + if os.name in better_scheme.split('_'): config_paths = sysconfig.get_paths(scheme) - for path_name in ["platlib", "purelib", "scripts"]: + for path_name in ['platlib', 'purelib', 'scripts']: paths.add(config_paths[path_name]) @@ -170,7 +179,7 @@ def add_coverage_paths(paths: set[str]) -> None: paths.add(cover_path) if env.TESTING: # Don't include our own test code. - paths.add(os.path.join(cover_path, "tests")) + paths.add(os.path.join(cover_path, 'tests')) class InOrOut: @@ -221,7 +230,7 @@ class InOrOut: # The matchers for should_trace. # Generally useful information - _debug("sys.path:" + "".join(f"\n {p}" for p in sys.path)) + _debug('sys.path:' + ''.join(f'\n {p}' for p in sys.path)) # Create the matchers we need for should_trace self.source_match = None @@ -232,28 +241,28 @@ class InOrOut: if self.source or self.source_pkgs: against = [] if self.source: - self.source_match = TreeMatcher(self.source, "source") - against.append(f"trees {self.source_match!r}") + self.source_match = TreeMatcher(self.source, 'source') + against.append(f'trees {self.source_match!r}') if self.source_pkgs: - self.source_pkgs_match = ModuleMatcher(self.source_pkgs, "source_pkgs") - against.append(f"modules {self.source_pkgs_match!r}") - _debug("Source matching against " + " and ".join(against)) + self.source_pkgs_match = ModuleMatcher(self.source_pkgs, 'source_pkgs') + against.append(f'modules {self.source_pkgs_match!r}') + _debug('Source matching against ' + ' and '.join(against)) else: if self.pylib_paths: - self.pylib_match = TreeMatcher(self.pylib_paths, "pylib") - _debug(f"Python stdlib matching: {self.pylib_match!r}") + self.pylib_match = TreeMatcher(self.pylib_paths, 'pylib') + _debug(f'Python stdlib matching: {self.pylib_match!r}') if self.include: - self.include_match = GlobMatcher(self.include, "include") - _debug(f"Include matching: {self.include_match!r}") + self.include_match = GlobMatcher(self.include, 'include') + _debug(f'Include matching: {self.include_match!r}') if self.omit: - self.omit_match = GlobMatcher(self.omit, "omit") - _debug(f"Omit matching: {self.omit_match!r}") + self.omit_match = GlobMatcher(self.omit, 'omit') + _debug(f'Omit matching: {self.omit_match!r}') - self.cover_match = TreeMatcher(self.cover_paths, "coverage") - _debug(f"Coverage code matching: {self.cover_match!r}") + self.cover_match = TreeMatcher(self.cover_paths, 'coverage') + _debug(f'Coverage code matching: {self.cover_match!r}') - self.third_match = TreeMatcher(self.third_paths, "third") - _debug(f"Third-party lib matching: {self.third_match!r}") + self.third_match = TreeMatcher(self.third_paths, 'third') + _debug(f'Third-party lib matching: {self.third_match!r}') # Check if the source we want to measure has been installed as a # third-party package. @@ -263,30 +272,30 @@ class InOrOut: for pkg in self.source_pkgs: try: modfile, path = file_and_path_for_module(pkg) - _debug(f"Imported source package {pkg!r} as {modfile!r}") + _debug(f'Imported source package {pkg!r} as {modfile!r}') except CoverageException as exc: _debug(f"Couldn't import source package {pkg!r}: {exc}") continue if modfile: if self.third_match.match(modfile): _debug( - f"Source in third-party: source_pkg {pkg!r} at {modfile!r}", + f'Source in third-party: source_pkg {pkg!r} at {modfile!r}', ) self.source_in_third_paths.add(canonical_path(source_for_file(modfile))) else: for pathdir in path: if self.third_match.match(pathdir): _debug( - f"Source in third-party: {pkg!r} path directory at {pathdir!r}", + f'Source in third-party: {pkg!r} path directory at {pathdir!r}', ) self.source_in_third_paths.add(pathdir) for src in self.source: if self.third_match.match(src): - _debug(f"Source in third-party: source directory {src!r}") + _debug(f'Source in third-party: source directory {src!r}') self.source_in_third_paths.add(src) - self.source_in_third_match = TreeMatcher(self.source_in_third_paths, "source_in_third") - _debug(f"Source in third-party matching: {self.source_in_third_match}") + self.source_in_third_match = TreeMatcher(self.source_in_third_paths, 'source_in_third') + _debug(f'Source in third-party matching: {self.source_in_third_match}') self.plugins: Plugins self.disp_class: type[TFileDisposition] = FileDisposition @@ -309,8 +318,8 @@ class InOrOut: disp.reason = reason return disp - if original_filename.startswith("<"): - return nope(disp, "original file name is not real") + if original_filename.startswith('<'): + return nope(disp, 'original file name is not real') if frame is not None: # Compiled Python files have two file names: frame.f_code.co_filename is @@ -319,10 +328,10 @@ class InOrOut: # .pyc files can be moved after compilation (for example, by being # installed), we look for __file__ in the frame and prefer it to the # co_filename value. - dunder_file = frame.f_globals and frame.f_globals.get("__file__") + dunder_file = frame.f_globals and frame.f_globals.get('__file__') if dunder_file: filename = source_for_file(dunder_file) - if original_filename and not original_filename.startswith("<"): + if original_filename and not original_filename.startswith('<'): orig = os.path.basename(original_filename) if orig != os.path.basename(filename): # Files shouldn't be renamed when moved. This happens when @@ -334,15 +343,15 @@ class InOrOut: # Empty string is pretty useless. return nope(disp, "empty string isn't a file name") - if filename.startswith("memory:"): + if filename.startswith('memory:'): return nope(disp, "memory isn't traceable") - if filename.startswith("<"): + if filename.startswith('<'): # Lots of non-file execution is represented with artificial # file names like "", "", or # "". Don't ever trace these executions, since we # can't do anything with the data later anyway. - return nope(disp, "file name is not real") + return nope(disp, 'file name is not real') canonical = canonical_filename(filename) disp.canonical_filename = canonical @@ -369,7 +378,7 @@ class InOrOut: except Exception: plugin_name = plugin._coverage_plugin_name tb = traceback.format_exc() - self.warn(f"Disabling plug-in {plugin_name!r} due to an exception:\n{tb}") + self.warn(f'Disabling plug-in {plugin_name!r} due to an exception:\n{tb}') plugin._coverage_enabled = False continue else: @@ -402,7 +411,7 @@ class InOrOut: # any canned exclusions. If they didn't, then we have to exclude the # stdlib and coverage.py directories. if self.source_match or self.source_pkgs_match: - extra = "" + extra = '' ok = False if self.source_pkgs_match: if self.source_pkgs_match.match(modulename): @@ -410,41 +419,41 @@ class InOrOut: if modulename in self.source_pkgs_unmatched: self.source_pkgs_unmatched.remove(modulename) else: - extra = f"module {modulename!r} " + extra = f'module {modulename!r} ' if not ok and self.source_match: if self.source_match.match(filename): ok = True if not ok: - return extra + "falls outside the --source spec" + return extra + 'falls outside the --source spec' if self.third_match.match(filename) and not self.source_in_third_match.match(filename): - return "inside --source, but is third-party" + return 'inside --source, but is third-party' elif self.include_match: if not self.include_match.match(filename): - return "falls outside the --include trees" + return 'falls outside the --include trees' else: # We exclude the coverage.py code itself, since a little of it # will be measured otherwise. if self.cover_match.match(filename): - return "is part of coverage.py" + return 'is part of coverage.py' # If we aren't supposed to trace installed code, then check if this # is near the Python standard library and skip it if so. if self.pylib_match and self.pylib_match.match(filename): - return "is in the stdlib" + return 'is in the stdlib' # Exclude anything in the third-party installation areas. if self.third_match.match(filename): - return "is a third-party module" + return 'is a third-party module' # Check the file against the omit pattern. if self.omit_match and self.omit_match.match(filename): - return "is inside an --omit pattern" + return 'is inside an --omit pattern' # No point tracing a file we can't later write to SQLite. try: - filename.encode("utf-8") + filename.encode('utf-8') except UnicodeEncodeError: - return "non-encodable filename" + return 'non-encodable filename' # No reason found to skip this file. return None @@ -453,20 +462,20 @@ class InOrOut: """Warn if there are settings that conflict.""" if self.include: if self.source or self.source_pkgs: - self.warn("--include is ignored because --source is set", slug="include-ignored") + self.warn('--include is ignored because --source is set', slug='include-ignored') def warn_already_imported_files(self) -> None: """Warn if files have already been imported that we will be measuring.""" if self.include or self.source or self.source_pkgs: warned = set() for mod in list(sys.modules.values()): - filename = getattr(mod, "__file__", None) + filename = getattr(mod, '__file__', None) if filename is None: continue if filename in warned: continue - if len(getattr(mod, "__path__", ())) > 1: + if len(getattr(mod, '__path__', ())) > 1: # A namespace package, which confuses this code, so ignore it. continue @@ -477,10 +486,10 @@ class InOrOut: # of tracing anyway. continue if disp.trace: - msg = f"Already imported a file that will be measured: {filename}" - self.warn(msg, slug="already-imported") + msg = f'Already imported a file that will be measured: {filename}' + self.warn(msg, slug='already-imported') warned.add(filename) - elif self.debug and self.debug.should("trace"): + elif self.debug and self.debug.should('trace'): self.debug.write( "Didn't trace already imported file {!r}: {}".format( disp.original_filename, disp.reason, @@ -500,7 +509,7 @@ class InOrOut: """ mod = sys.modules.get(pkg) if mod is None: - self.warn(f"Module {pkg} was never imported.", slug="module-not-imported") + self.warn(f'Module {pkg} was never imported.', slug='module-not-imported') return if module_is_namespace(mod): @@ -509,14 +518,14 @@ class InOrOut: return if not module_has_file(mod): - self.warn(f"Module {pkg} has no Python source.", slug="module-not-python") + self.warn(f'Module {pkg} has no Python source.', slug='module-not-python') return # The module was in sys.modules, and seems like a module with code, but # we never measured it. I guess that means it was imported before # coverage even started. - msg = f"Module {pkg} was previously imported, but not measured" - self.warn(msg, slug="module-not-measured") + msg = f'Module {pkg} was previously imported, but not measured' + self.warn(msg, slug='module-not-measured') def find_possibly_unexecuted_files(self) -> Iterable[tuple[str, str | None]]: """Find files in the areas of interest that might be untraced. @@ -524,8 +533,10 @@ class InOrOut: Yields pairs: file path, and responsible plug-in name. """ for pkg in self.source_pkgs: - if (pkg not in sys.modules or - not module_has_file(sys.modules[pkg])): + if ( + pkg not in sys.modules or + not module_has_file(sys.modules[pkg]) + ): continue pkg_file = source_for_file(cast(str, sys.modules[pkg].__file__)) yield from self._find_executable_files(canonical_path(pkg_file)) @@ -569,16 +580,16 @@ class InOrOut: Returns a list of (key, value) pairs. """ info = [ - ("coverage_paths", self.cover_paths), - ("stdlib_paths", self.pylib_paths), - ("third_party_paths", self.third_paths), - ("source_in_third_party_paths", self.source_in_third_paths), + ('coverage_paths', self.cover_paths), + ('stdlib_paths', self.pylib_paths), + ('third_party_paths', self.third_paths), + ('source_in_third_party_paths', self.source_in_third_paths), ] matcher_names = [ - "source_match", "source_pkgs_match", - "include_match", "omit_match", - "cover_match", "pylib_match", "third_match", "source_in_third_match", + 'source_match', 'source_pkgs_match', + 'include_match', 'omit_match', + 'cover_match', 'pylib_match', 'third_match', 'source_in_third_match', ] for matcher_name in matcher_names: @@ -586,7 +597,7 @@ class InOrOut: if matcher: matcher_info = matcher.info() else: - matcher_info = "-none-" + matcher_info = '-none-' info.append((matcher_name, matcher_info)) return info diff --git a/.venv/lib/python3.10/site-packages/coverage/jsonreport.py b/.venv/lib/python3.10/site-packages/coverage/jsonreport.py index 4d4dec7..749175d 100644 --- a/.venv/lib/python3.10/site-packages/coverage/jsonreport.py +++ b/.venv/lib/python3.10/site-packages/coverage/jsonreport.py @@ -1,20 +1,22 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Json reporting for coverage.py""" - from __future__ import annotations import datetime import json import sys - -from typing import Any, IO, Iterable, TYPE_CHECKING +from typing import Any +from typing import IO +from typing import Iterable +from typing import TYPE_CHECKING from coverage import __version__ from coverage.report_core import get_analysis_to_report -from coverage.results import Analysis, Numbers -from coverage.types import TMorf, TLineNo +from coverage.results import Analysis +from coverage.results import Numbers +from coverage.types import TLineNo +from coverage.types import TMorf if TYPE_CHECKING: from coverage import Coverage @@ -25,10 +27,11 @@ if TYPE_CHECKING: # 2: add the meta.format field. FORMAT_VERSION = 2 + class JsonReporter: """A reporter for writing JSON coverage results.""" - report_type = "JSON report" + report_type = 'JSON report' def __init__(self, coverage: Coverage) -> None: self.coverage = coverage @@ -47,12 +50,12 @@ class JsonReporter: outfile = outfile or sys.stdout coverage_data = self.coverage.get_data() coverage_data.set_query_contexts(self.config.report_contexts) - self.report_data["meta"] = { - "format": FORMAT_VERSION, - "version": __version__, - "timestamp": datetime.datetime.now().isoformat(), - "branch_coverage": coverage_data.has_arcs(), - "show_contexts": self.config.json_show_contexts, + self.report_data['meta'] = { + 'format': FORMAT_VERSION, + 'version': __version__, + 'timestamp': datetime.datetime.now().isoformat(), + 'branch_coverage': coverage_data.has_arcs(), + 'show_contexts': self.config.json_show_contexts, } measured_files = {} @@ -62,23 +65,23 @@ class JsonReporter: analysis, ) - self.report_data["files"] = measured_files + self.report_data['files'] = measured_files - self.report_data["totals"] = { - "covered_lines": self.total.n_executed, - "num_statements": self.total.n_statements, - "percent_covered": self.total.pc_covered, - "percent_covered_display": self.total.pc_covered_str, - "missing_lines": self.total.n_missing, - "excluded_lines": self.total.n_excluded, + self.report_data['totals'] = { + 'covered_lines': self.total.n_executed, + 'num_statements': self.total.n_statements, + 'percent_covered': self.total.pc_covered, + 'percent_covered_display': self.total.pc_covered_str, + 'missing_lines': self.total.n_missing, + 'excluded_lines': self.total.n_excluded, } if coverage_data.has_arcs(): - self.report_data["totals"].update({ - "num_branches": self.total.n_branches, - "num_partial_branches": self.total.n_partial_branches, - "covered_branches": self.total.n_executed_branches, - "missing_branches": self.total.n_missing_branches, + self.report_data['totals'].update({ + 'num_branches': self.total.n_branches, + 'num_partial_branches': self.total.n_partial_branches, + 'covered_branches': self.total.n_executed_branches, + 'missing_branches': self.total.n_missing_branches, }) json.dump( @@ -94,32 +97,32 @@ class JsonReporter: nums = analysis.numbers self.total += nums summary = { - "covered_lines": nums.n_executed, - "num_statements": nums.n_statements, - "percent_covered": nums.pc_covered, - "percent_covered_display": nums.pc_covered_str, - "missing_lines": nums.n_missing, - "excluded_lines": nums.n_excluded, + 'covered_lines': nums.n_executed, + 'num_statements': nums.n_statements, + 'percent_covered': nums.pc_covered, + 'percent_covered_display': nums.pc_covered_str, + 'missing_lines': nums.n_missing, + 'excluded_lines': nums.n_excluded, } reported_file = { - "executed_lines": sorted(analysis.executed), - "summary": summary, - "missing_lines": sorted(analysis.missing), - "excluded_lines": sorted(analysis.excluded), + 'executed_lines': sorted(analysis.executed), + 'summary': summary, + 'missing_lines': sorted(analysis.missing), + 'excluded_lines': sorted(analysis.excluded), } if self.config.json_show_contexts: - reported_file["contexts"] = analysis.data.contexts_by_lineno(analysis.filename) + reported_file['contexts'] = analysis.data.contexts_by_lineno(analysis.filename) if coverage_data.has_arcs(): summary.update({ - "num_branches": nums.n_branches, - "num_partial_branches": nums.n_partial_branches, - "covered_branches": nums.n_executed_branches, - "missing_branches": nums.n_missing_branches, + 'num_branches': nums.n_branches, + 'num_partial_branches': nums.n_partial_branches, + 'covered_branches': nums.n_executed_branches, + 'missing_branches': nums.n_missing_branches, }) - reported_file["executed_branches"] = list( + reported_file['executed_branches'] = list( _convert_branch_arcs(analysis.executed_branch_arcs()), ) - reported_file["missing_branches"] = list( + reported_file['missing_branches'] = list( _convert_branch_arcs(analysis.missing_branch_arcs()), ) return reported_file diff --git a/.venv/lib/python3.10/site-packages/coverage/lcovreport.py b/.venv/lib/python3.10/site-packages/coverage/lcovreport.py index e54bae8..e110ca4 100644 --- a/.venv/lib/python3.10/site-packages/coverage/lcovreport.py +++ b/.venv/lib/python3.10/site-packages/coverage/lcovreport.py @@ -1,19 +1,19 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """LCOV reporting for coverage.py.""" - from __future__ import annotations import base64 import hashlib import sys - -from typing import IO, Iterable, TYPE_CHECKING +from typing import IO +from typing import Iterable +from typing import TYPE_CHECKING from coverage.plugin import FileReporter from coverage.report_core import get_analysis_to_report -from coverage.results import Analysis, Numbers +from coverage.results import Analysis +from coverage.results import Numbers from coverage.types import TMorf if TYPE_CHECKING: @@ -22,14 +22,14 @@ if TYPE_CHECKING: def line_hash(line: str) -> str: """Produce a hash of a source line for use in the LCOV file.""" - hashed = hashlib.md5(line.encode("utf-8")).digest() - return base64.b64encode(hashed).decode("ascii").rstrip("=") + hashed = hashlib.md5(line.encode('utf-8')).digest() + return base64.b64encode(hashed).decode('ascii').rstrip('=') class LcovReporter: """A reporter for writing LCOV coverage reports.""" - report_type = "LCOV report" + report_type = 'LCOV report' def __init__(self, coverage: Coverage) -> None: self.coverage = coverage @@ -59,8 +59,8 @@ class LcovReporter: """ self.total += analysis.numbers - outfile.write("TN:\n") - outfile.write(f"SF:{fr.relative_filename()}\n") + outfile.write('TN:\n') + outfile.write(f'SF:{fr.relative_filename()}\n') source_lines = fr.source().splitlines() for covered in sorted(analysis.executed): if covered in analysis.excluded: @@ -76,22 +76,22 @@ class LcovReporter: # characters of the encoding ("==") are removed from the hash to # allow genhtml to run on the resulting lcov file. if source_lines: - if covered-1 >= len(source_lines): + if covered - 1 >= len(source_lines): break - line = source_lines[covered-1] + line = source_lines[covered - 1] else: - line = "" - outfile.write(f"DA:{covered},1,{line_hash(line)}\n") + line = '' + outfile.write(f'DA:{covered},1,{line_hash(line)}\n') for missed in sorted(analysis.missing): # We don't have to skip excluded lines here, because `missing` # already doesn't have them. assert source_lines - line = source_lines[missed-1] - outfile.write(f"DA:{missed},0,{line_hash(line)}\n") + line = source_lines[missed - 1] + outfile.write(f'DA:{missed},0,{line_hash(line)}\n') - outfile.write(f"LF:{analysis.numbers.n_statements}\n") - outfile.write(f"LH:{analysis.numbers.n_executed}\n") + outfile.write(f'LF:{analysis.numbers.n_statements}\n') + outfile.write(f'LH:{analysis.numbers.n_executed}\n') # More information dense branch coverage data. missing_arcs = analysis.missing_branch_arcs() @@ -107,7 +107,7 @@ class LcovReporter: # the line number of the exit branch to 0 will allow # for valid lcov, while preserving the data. line_number = max(line_number, 0) - outfile.write(f"BRDA:{line_number},{block_number},{branch_number},-\n") + outfile.write(f'BRDA:{line_number},{block_number},{branch_number},-\n') # The start value below allows for the block number to be # preserved between these two for loops (stopping the loop from @@ -117,14 +117,14 @@ class LcovReporter: start=len(missing_arcs[block_line_number]), ): line_number = max(line_number, 0) - outfile.write(f"BRDA:{line_number},{block_number},{branch_number},1\n") + outfile.write(f'BRDA:{line_number},{block_number},{branch_number},1\n') # Summary of the branch coverage. if analysis.has_arcs(): branch_stats = analysis.branch_stats() brf = sum(t for t, k in branch_stats.values()) brh = brf - sum(t - k for t, k in branch_stats.values()) - outfile.write(f"BRF:{brf}\n") - outfile.write(f"BRH:{brh}\n") + outfile.write(f'BRF:{brf}\n') + outfile.write(f'BRH:{brh}\n') - outfile.write("end_of_record\n") + outfile.write('end_of_record\n') diff --git a/.venv/lib/python3.10/site-packages/coverage/misc.py b/.venv/lib/python3.10/site-packages/coverage/misc.py index 2b27efc..2fa626d 100644 --- a/.venv/lib/python3.10/site-packages/coverage/misc.py +++ b/.venv/lib/python3.10/site-packages/coverage/misc.py @@ -1,37 +1,37 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Miscellaneous stuff for coverage.py.""" - from __future__ import annotations import contextlib import datetime import errno import hashlib -import importlib import importlib.util import inspect import locale -import os import os.path import re import sys import types - from types import ModuleType -from typing import ( - Any, Callable, IO, Iterable, Iterator, Mapping, NoReturn, Sequence, TypeVar, -) +from typing import Any +from typing import Callable +from typing import IO +from typing import Iterable +from typing import Iterator +from typing import Mapping +from typing import NoReturn +from typing import Sequence +from typing import TypeVar from coverage import env +from coverage.exceptions import * # pylint: disable=wildcard-import from coverage.exceptions import CoverageException from coverage.types import TArc - # In 6.0, the exceptions moved from misc.py to exceptions.py. But a number of # other packages were importing the exceptions from misc, so import them here. # pylint: disable=unused-wildcard-import -from coverage.exceptions import * # pylint: disable=wildcard-import ISOLATED_MODULES: dict[ModuleType, ModuleType] = {} @@ -54,11 +54,13 @@ def isolate_module(mod: ModuleType) -> ModuleType: setattr(new_mod, name, value) return ISOLATED_MODULES[mod] + os = isolate_module(os) class SysModuleSaver: """Saves the contents of sys.modules, and removes new modules later.""" + def __init__(self) -> None: self.old_modules = set(sys.modules) @@ -111,13 +113,14 @@ def nice_pair(pair: TArc) -> str: """ start, end = pair if start == end: - return "%d" % start + return '%d' % start else: - return "%d-%d" % (start, end) + return '%d-%d' % (start, end) -TSelf = TypeVar("TSelf") -TRetVal = TypeVar("TRetVal") +TSelf = TypeVar('TSelf') +TRetVal = TypeVar('TRetVal') + def expensive(fn: Callable[[TSelf], TRetVal]) -> Callable[[TSelf], TRetVal]: """A decorator to indicate that a method shouldn't be called more than once. @@ -127,7 +130,7 @@ def expensive(fn: Callable[[TSelf], TRetVal]) -> Callable[[TSelf], TRetVal]: """ if env.TESTING: - attr = "_once_" + fn.__name__ + attr = '_once_' + fn.__name__ def _wrapper(self: TSelf) -> TRetVal: if hasattr(self, attr): @@ -153,7 +156,7 @@ def join_regex(regexes: Iterable[str]) -> str: if len(regexes) == 1: return regexes[0] else: - return "|".join(f"(?:{r})" for r in regexes) + return '|'.join(f'(?:{r})' for r in regexes) def file_be_gone(path: str) -> None: @@ -184,8 +187,8 @@ def output_encoding(outfile: IO[str] | None = None) -> str: if outfile is None: outfile = sys.stdout encoding = ( - getattr(outfile, "encoding", None) or - getattr(sys.__stdout__, "encoding", None) or + getattr(outfile, 'encoding', None) or + getattr(sys.__stdout__, 'encoding', None) or locale.getpreferredencoding() ) return encoding @@ -193,20 +196,21 @@ def output_encoding(outfile: IO[str] | None = None) -> str: class Hasher: """Hashes Python data for fingerprinting.""" + def __init__(self) -> None: - self.hash = hashlib.new("sha3_256") + self.hash = hashlib.new('sha3_256') def update(self, v: Any) -> None: """Add `v` to the hash, recursively if needed.""" - self.hash.update(str(type(v)).encode("utf-8")) + self.hash.update(str(type(v)).encode('utf-8')) if isinstance(v, str): - self.hash.update(v.encode("utf-8")) + self.hash.update(v.encode('utf-8')) elif isinstance(v, bytes): self.hash.update(v) elif v is None: pass elif isinstance(v, (int, float)): - self.hash.update(str(v).encode("utf-8")) + self.hash.update(str(v).encode('utf-8')) elif isinstance(v, (tuple, list)): for e in v: self.update(e) @@ -217,14 +221,14 @@ class Hasher: self.update(v[k]) else: for k in dir(v): - if k.startswith("__"): + if k.startswith('__'): continue a = getattr(v, k) if inspect.isroutine(a): continue self.update(k) self.update(a) - self.hash.update(b".") + self.hash.update(b'.') def hexdigest(self) -> str: """Retrieve the hex digest of the hash.""" @@ -233,16 +237,16 @@ class Hasher: def _needs_to_implement(that: Any, func_name: str) -> NoReturn: """Helper to raise NotImplementedError in interface stubs.""" - if hasattr(that, "_coverage_plugin_name"): - thing = "Plugin" + if hasattr(that, '_coverage_plugin_name'): + thing = 'Plugin' name = that._coverage_plugin_name else: - thing = "Class" + thing = 'Class' klass = that.__class__ - name = f"{klass.__module__}.{klass.__name__}" + name = f'{klass.__module__}.{klass.__name__}' raise NotImplementedError( - f"{thing} {name!r} needs to implement {func_name}()", + f'{thing} {name!r} needs to implement {func_name}()', ) @@ -253,6 +257,7 @@ class DefaultValue: and Sphinx output. """ + def __init__(self, display_as: str) -> None: self.display_as = display_as @@ -291,21 +296,21 @@ def substitute_variables(text: str, variables: Mapping[str, str]) -> str: ) """ - dollar_groups = ("dollar", "word1", "word2") + dollar_groups = ('dollar', 'word1', 'word2') def dollar_replace(match: re.Match[str]) -> str: """Called for each $replacement.""" # Only one of the dollar_groups will have matched, just get its text. word = next(g for g in match.group(*dollar_groups) if g) # pragma: always breaks - if word == "$": - return "$" + if word == '$': + return '$' elif word in variables: return variables[word] - elif match["strict"]: - msg = f"Variable {word} is undefined: {text!r}" + elif match['strict']: + msg = f'Variable {word} is undefined: {text!r}' raise CoverageException(msg) else: - return match["defval"] + return match['defval'] text = re.sub(dollar_pattern, dollar_replace, text) return text @@ -314,7 +319,7 @@ def substitute_variables(text: str, variables: Mapping[str, str]) -> str: def format_local_datetime(dt: datetime.datetime) -> str: """Return a string with local timezone representing the date. """ - return dt.astimezone().strftime("%Y-%m-%d %H:%M %z") + return dt.astimezone().strftime('%Y-%m-%d %H:%M %z') def import_local_file(modname: str, modfile: str | None = None) -> ModuleType: @@ -326,7 +331,7 @@ def import_local_file(modname: str, modfile: str | None = None) -> ModuleType: """ if modfile is None: - modfile = modname + ".py" + modfile = modname + '.py' spec = importlib.util.spec_from_file_location(modname, modfile) assert spec is not None mod = importlib.util.module_from_spec(spec) @@ -352,7 +357,8 @@ def _human_key(s: str) -> tuple[list[str | int], str]: except ValueError: return s - return ([tryint(c) for c in re.split(r"(\d+)", s)], s) + return ([tryint(c) for c in re.split(r'(\d+)', s)], s) + def human_sorted(strings: Iterable[str]) -> list[str]: """Sort the given iterable of strings the way that humans expect. @@ -364,7 +370,9 @@ def human_sorted(strings: Iterable[str]) -> list[str]: """ return sorted(strings, key=_human_key) -SortableItem = TypeVar("SortableItem", bound=Sequence[Any]) + +SortableItem = TypeVar('SortableItem', bound=Sequence[Any]) + def human_sorted_items( items: Iterable[SortableItem], @@ -380,7 +388,7 @@ def human_sorted_items( return sorted(items, key=lambda item: (_human_key(item[0]), *item[1:]), reverse=reverse) -def plural(n: int, thing: str = "", things: str = "") -> str: +def plural(n: int, thing: str = '', things: str = '') -> str: """Pluralize a word. If n is 1, return thing. Otherwise return things, or thing+s. @@ -388,7 +396,7 @@ def plural(n: int, thing: str = "", things: str = "") -> str: if n == 1: return thing else: - return things or (thing + "s") + return things or (thing + 's') def stdout_link(text: str, url: str) -> str: @@ -397,7 +405,7 @@ def stdout_link(text: str, url: str) -> str: If attached to a terminal, use escape sequences. Otherwise, just return the text. """ - if hasattr(sys.stdout, "isatty") and sys.stdout.isatty(): - return f"\033]8;;{url}\a{text}\033]8;;\a" + if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty(): + return f'\033]8;;{url}\a{text}\033]8;;\a' else: return text diff --git a/.venv/lib/python3.10/site-packages/coverage/multiproc.py b/.venv/lib/python3.10/site-packages/coverage/multiproc.py index 6d5a827..cf9dd3f 100644 --- a/.venv/lib/python3.10/site-packages/coverage/multiproc.py +++ b/.venv/lib/python3.10/site-packages/coverage/multiproc.py @@ -1,29 +1,25 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Monkey-patching to add multiprocessing support for coverage.py""" - from __future__ import annotations -import multiprocessing import multiprocessing.process -import os import os.path import sys import traceback - from typing import Any from coverage.debug import DebugControl # An attribute that will be set on the module to indicate that it has been # monkey-patched. -PATCHED_MARKER = "_coverage$patched" +PATCHED_MARKER = '_coverage$patched' OriginalProcess = multiprocessing.process.BaseProcess original_bootstrap = OriginalProcess._bootstrap # type: ignore[attr-defined] + class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-method """A replacement for multiprocess.Process that starts coverage.""" @@ -37,12 +33,12 @@ class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-m cov.start() _debug = cov._debug assert _debug is not None - if _debug.should("multiproc"): + if _debug.should('multiproc'): debug = _debug if debug: - debug.write("Calling multiprocessing bootstrap") + debug.write('Calling multiprocessing bootstrap') except Exception: - print("Exception during multiprocessing bootstrap init:", file=sys.stderr) + print('Exception during multiprocessing bootstrap init:', file=sys.stderr) traceback.print_exc(file=sys.stderr) sys.stderr.flush() raise @@ -50,27 +46,29 @@ class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-m return original_bootstrap(self, *args, **kwargs) finally: if debug: - debug.write("Finished multiprocessing bootstrap") + debug.write('Finished multiprocessing bootstrap') try: cov.stop() cov.save() except Exception as exc: if debug: - debug.write("Exception during multiprocessing bootstrap cleanup", exc=exc) + debug.write('Exception during multiprocessing bootstrap cleanup', exc=exc) raise if debug: - debug.write("Saved multiprocessing data") + debug.write('Saved multiprocessing data') + class Stowaway: """An object to pickle, so when it is unpickled, it can apply the monkey-patch.""" + def __init__(self, rcfile: str) -> None: self.rcfile = rcfile def __getstate__(self) -> dict[str, str]: - return {"rcfile": self.rcfile} + return {'rcfile': self.rcfile} def __setstate__(self, state: dict[str, str]) -> None: - patch_multiprocessing(state["rcfile"]) + patch_multiprocessing(state['rcfile']) def patch_multiprocessing(rcfile: str) -> None: @@ -90,7 +88,7 @@ def patch_multiprocessing(rcfile: str) -> None: # Set the value in ProcessWithCoverage that will be pickled into the child # process. - os.environ["COVERAGE_RCFILE"] = os.path.abspath(rcfile) + os.environ['COVERAGE_RCFILE'] = os.path.abspath(rcfile) # When spawning processes rather than forking them, we have no state in the # new process. We sneak in there with a Stowaway: we stuff one of our own @@ -107,7 +105,7 @@ def patch_multiprocessing(rcfile: str) -> None: def get_preparation_data_with_stowaway(name: str) -> dict[str, Any]: """Get the original preparation data, and also insert our stowaway.""" d = original_get_preparation_data(name) - d["stowaway"] = Stowaway(rcfile) + d['stowaway'] = Stowaway(rcfile) return d spawn.get_preparation_data = get_preparation_data_with_stowaway diff --git a/.venv/lib/python3.10/site-packages/coverage/numbits.py b/.venv/lib/python3.10/site-packages/coverage/numbits.py index a4eedfa..8f7bb92 100644 --- a/.venv/lib/python3.10/site-packages/coverage/numbits.py +++ b/.venv/lib/python3.10/site-packages/coverage/numbits.py @@ -1,6 +1,5 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """ Functions to manipulate packed binary representations of number sets. @@ -13,12 +12,10 @@ in the blobs should be considered an implementation detail that might change in the future. Use these functions to work with those binary blobs of data. """ - from __future__ import annotations import json import sqlite3 - from itertools import zip_longest from typing import Iterable @@ -36,10 +33,10 @@ def nums_to_numbits(nums: Iterable[int]) -> bytes: nbytes = max(nums) // 8 + 1 except ValueError: # nums was empty. - return b"" + return b'' b = bytearray(nbytes) for num in nums: - b[num//8] |= 1 << num % 8 + b[num // 8] |= 1 << num % 8 return bytes(b) @@ -82,7 +79,7 @@ def numbits_intersection(numbits1: bytes, numbits2: bytes) -> bytes: """ byte_pairs = zip_longest(numbits1, numbits2, fillvalue=0) intersection_bytes = bytes(b1 & b2 for b1, b2 in byte_pairs) - return intersection_bytes.rstrip(b"\0") + return intersection_bytes.rstrip(b'\0') def numbits_any_intersection(numbits1: bytes, numbits2: bytes) -> bool: @@ -140,8 +137,8 @@ def register_sqlite_functions(connection: sqlite3.Connection) -> None: (47,) ) """ - connection.create_function("numbits_union", 2, numbits_union) - connection.create_function("numbits_intersection", 2, numbits_intersection) - connection.create_function("numbits_any_intersection", 2, numbits_any_intersection) - connection.create_function("num_in_numbits", 2, num_in_numbits) - connection.create_function("numbits_to_nums", 1, lambda b: json.dumps(numbits_to_nums(b))) + connection.create_function('numbits_union', 2, numbits_union) + connection.create_function('numbits_intersection', 2, numbits_intersection) + connection.create_function('numbits_any_intersection', 2, numbits_any_intersection) + connection.create_function('num_in_numbits', 2, num_in_numbits) + connection.create_function('numbits_to_nums', 1, lambda b: json.dumps(numbits_to_nums(b))) diff --git a/.venv/lib/python3.10/site-packages/coverage/parser.py b/.venv/lib/python3.10/site-packages/coverage/parser.py index 959174c..d7b467b 100644 --- a/.venv/lib/python3.10/site-packages/coverage/parser.py +++ b/.venv/lib/python3.10/site-packages/coverage/parser.py @@ -1,8 +1,6 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Code parsing for coverage.py.""" - from __future__ import annotations import ast @@ -12,21 +10,30 @@ import re import sys import token import tokenize - from dataclasses import dataclass from types import CodeType -from typing import ( - cast, Any, Callable, Dict, Iterable, List, Optional, Protocol, Sequence, - Set, Tuple, -) +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import Iterable +from typing import List +from typing import Optional +from typing import Protocol +from typing import Sequence +from typing import Set +from typing import Tuple from coverage import env from coverage.bytecode import code_objects from coverage.debug import short_stack -from coverage.exceptions import NoSource, NotPython -from coverage.misc import join_regex, nice_pair +from coverage.exceptions import NoSource +from coverage.exceptions import NotPython +from coverage.misc import join_regex +from coverage.misc import nice_pair from coverage.phystokens import generate_tokens -from coverage.types import TArc, TLineNo +from coverage.types import TArc +from coverage.types import TLineNo class PythonParser: @@ -36,6 +43,7 @@ class PythonParser: involved. """ + def __init__( self, text: str | None = None, @@ -48,8 +56,8 @@ class PythonParser: `exclude`, a regex string. """ - assert text or filename, "PythonParser needs either text or filename" - self.filename = filename or "" + assert text or filename, 'PythonParser needs either text or filename' + self.filename = filename or '' if text is not None: self.text: str = text else: @@ -62,7 +70,7 @@ class PythonParser: self.exclude = exclude # The text lines of the parsed code. - self.lines: list[str] = self.text.split("\n") + self.lines: list[str] = self.text.split('\n') # The normalized line numbers of the statements in the code. Exclusions # are taken into account, and statements are adjusted to their first @@ -152,25 +160,27 @@ class PythonParser: tokgen = generate_tokens(self.text) for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen: if self.show_tokens: # pragma: debugging - print("%10s %5s %-20r %r" % ( - tokenize.tok_name.get(toktype, toktype), - nice_pair((slineno, elineno)), ttext, ltext, - )) + print( + '%10s %5s %-20r %r' % ( + tokenize.tok_name.get(toktype, toktype), + nice_pair((slineno, elineno)), ttext, ltext, + ), + ) if toktype == token.INDENT: indent += 1 elif toktype == token.DEDENT: indent -= 1 elif toktype == token.NAME: - if ttext == "class": + if ttext == 'class': # Class definitions look like branches in the bytecode, so # we need to exclude them. The simplest way is to note the # lines with the "class" keyword. self.raw_classdefs.add(slineno) elif toktype == token.OP: - if ttext == ":" and nesting == 0: + if ttext == ':' and nesting == 0: should_exclude = ( - self.raw_excluded.intersection(range(first_line, elineno + 1)) - or excluding_decorators + self.raw_excluded.intersection(range(first_line, elineno + 1)) or + excluding_decorators ) if not excluding and should_exclude: # Start excluding a suite. We trigger off of the colon @@ -180,28 +190,28 @@ class PythonParser: exclude_indent = indent excluding = True excluding_decorators = False - elif ttext == "@" and first_on_line: + elif ttext == '@' and first_on_line: # A decorator. if elineno in self.raw_excluded: excluding_decorators = True if excluding_decorators: self.raw_excluded.add(elineno) - elif ttext in "([{": + elif ttext in '([{': nesting += 1 - elif ttext in ")]}": + elif ttext in ')]}': nesting -= 1 elif toktype == token.STRING: if prev_toktype == token.INDENT: # Strings that are first on an indented line are docstrings. # (a trick from trace.py in the stdlib.) This works for # 99.9999% of cases. - self.raw_docstrings.update(range(slineno, elineno+1)) + self.raw_docstrings.update(range(slineno, elineno + 1)) elif toktype == token.NEWLINE: if first_line and elineno != first_line: # We're at the end of a line, and we've ended on a # different line than the first line of the statement, # so record a multi-line range. - for l in range(first_line, elineno+1): + for l in range(first_line, elineno + 1): self._multiline[l] = first_line first_line = 0 first_on_line = True @@ -267,13 +277,13 @@ class PythonParser: try: self._raw_parse() except (tokenize.TokenError, IndentationError, SyntaxError) as err: - if hasattr(err, "lineno"): + if hasattr(err, 'lineno'): lineno = err.lineno # IndentationError else: lineno = err.args[1][0] # TokenError raise NotPython( f"Couldn't parse '{self.filename}' as Python source: " + - f"{err.args[0]!r} at line {lineno}", + f'{err.args[0]!r} at line {lineno}', ) from err self.excluded = self.first_lines(self.raw_excluded) @@ -376,13 +386,13 @@ class PythonParser: emsg = "didn't jump to line {lineno}" emsg = emsg.format(lineno=end) - msg = f"line {actual_start} {emsg}" + msg = f'line {actual_start} {emsg}' if smsg is not None: - msg += f", because {smsg.format(lineno=actual_start)}" + msg += f', because {smsg.format(lineno=actual_start)}' msgs.append(msg) - return " or ".join(msgs) + return ' or '.join(msgs) class ByteParser: @@ -400,7 +410,7 @@ class ByteParser: else: assert filename is not None try: - self.code = compile(text, filename, "exec", dont_inherit=True) + self.code = compile(text, filename, 'exec', dont_inherit=True) except SyntaxError as synerr: raise NotPython( "Couldn't parse '%s' as Python source: '%s' at line %d" % ( @@ -422,7 +432,7 @@ class ByteParser: Uses co_lnotab described in Python/compile.c to find the line numbers. Produces a sequence: l0, l1, ... """ - if hasattr(self.code, "co_lines"): + if hasattr(self.code, 'co_lines'): # PYVERSIONS: new in 3.10 for _, _, line in self.code.co_lines(): if line: @@ -477,11 +487,12 @@ class ArcStart: """ lineno: TLineNo - cause: str = "" + cause: str = '' class TAddArcFn(Protocol): """The type for AstArcAnalyzer.add_arc().""" + def __call__( self, start: TLineNo, @@ -491,8 +502,10 @@ class TAddArcFn(Protocol): ) -> None: ... + TArcFragments = Dict[TArc, List[Tuple[Optional[str], Optional[str]]]] + class Block: """ Blocks need to handle various exiting statements in their own ways. @@ -503,6 +516,7 @@ class Block: stack. """ # pylint: disable=unused-argument + def process_break_exits(self, exits: set[ArcStart], add_arc: TAddArcFn) -> bool: """Process break exits.""" # Because break can only appear in loops, and most subclasses @@ -526,6 +540,7 @@ class Block: class LoopBlock(Block): """A block on the block stack representing a `for` or `while` loop.""" + def __init__(self, start: TLineNo) -> None: # The line number where the loop starts. self.start = start @@ -544,6 +559,7 @@ class LoopBlock(Block): class FunctionBlock(Block): """A block on the block stack representing a function definition.""" + def __init__(self, start: TLineNo, name: str) -> None: # The line number where the function starts. self.start = start @@ -569,6 +585,7 @@ class FunctionBlock(Block): class TryBlock(Block): """A block on the block stack representing a `try` block.""" + def __init__(self, handler_start: TLineNo | None, final_start: TLineNo | None) -> None: # The line number of the first "except" handler, if any. self.handler_start = handler_start @@ -612,6 +629,7 @@ class TryBlock(Block): class WithBlock(Block): """A block on the block stack representing a `with` block.""" + def __init__(self, start: TLineNo) -> None: # We only ever use this block if it is needed, so that we don't have to # check this setting in all the methods. @@ -659,6 +677,7 @@ class NodeList(ast.AST): unconditional execution of one of the clauses. """ + def __init__(self, body: Sequence[ast.AST]) -> None: self.body = body self.lineno = body[0].lineno @@ -667,8 +686,10 @@ class NodeList(ast.AST): # TODO: the cause messages have too many commas. # TODO: Shouldn't the cause messages join with "and" instead of "or"? + def _make_expression_code_method(noun: str) -> Callable[[AstArcAnalyzer, ast.AST], None]: """A function to make methods for expression-based callable _code_object__ methods.""" + def _code_object__expression_callable(self: AstArcAnalyzer, node: ast.AST) -> None: start = self.line_for_node(node) self.add_arc(-start, start, None, f"didn't run the {noun} on line {start}") @@ -692,15 +713,15 @@ class AstArcAnalyzer: # Turn on AST dumps with an environment variable. # $set_env.py: COVERAGE_AST_DUMP - Dump the AST nodes when parsing code. - dump_ast = bool(int(os.getenv("COVERAGE_AST_DUMP", "0"))) + dump_ast = bool(int(os.getenv('COVERAGE_AST_DUMP', '0'))) if dump_ast: # pragma: debugging # Dump the AST so that failing tests have helpful output. - print(f"Statements: {self.statements}") - print(f"Multiline map: {self.multiline}") + print(f'Statements: {self.statements}') + print(f'Multiline map: {self.multiline}') dumpkw: dict[str, Any] = {} if sys.version_info >= (3, 9): - dumpkw["indent"] = 4 + dumpkw['indent'] = 4 print(ast.dump(self.root_node, include_attributes=True, **dumpkw)) self.arcs: set[TArc] = set() @@ -714,7 +735,7 @@ class AstArcAnalyzer: self.block_stack: list[Block] = [] # $set_env.py: COVERAGE_TRACK_ARCS - Trace possible arcs added while parsing code. - self.debug = bool(int(os.getenv("COVERAGE_TRACK_ARCS", "0"))) + self.debug = bool(int(os.getenv('COVERAGE_TRACK_ARCS', '0'))) def analyze(self) -> None: """Examine the AST tree from `root_node` to determine possible arcs. @@ -725,7 +746,7 @@ class AstArcAnalyzer: """ for node in ast.walk(self.root_node): node_name = node.__class__.__name__ - code_object_handler = getattr(self, "_code_object__" + node_name, None) + code_object_handler = getattr(self, '_code_object__' + node_name, None) if code_object_handler is not None: code_object_handler(node) @@ -738,7 +759,7 @@ class AstArcAnalyzer: ) -> None: """Add an arc, including message fragments to use if it is missing.""" if self.debug: # pragma: debugging - print(f"\nAdding possible arc: ({start}, {end}): {smsg!r}, {emsg!r}") + print(f'\nAdding possible arc: ({start}, {end}): {smsg!r}, {emsg!r}') print(short_stack()) self.arcs.add((start, end)) @@ -758,7 +779,7 @@ class AstArcAnalyzer: node_name = node.__class__.__name__ handler = cast( Optional[Callable[[ast.AST], TLineNo]], - getattr(self, "_line__" + node_name, None), + getattr(self, '_line__' + node_name, None), ) if handler is not None: return handler(node) @@ -809,8 +830,8 @@ class AstArcAnalyzer: # The node types that just flow to the next node with no complications. OK_TO_DEFAULT = { - "AnnAssign", "Assign", "Assert", "AugAssign", "Delete", "Expr", "Global", - "Import", "ImportFrom", "Nonlocal", "Pass", + 'AnnAssign', 'Assign', 'Assert', 'AugAssign', 'Delete', 'Expr', 'Global', + 'Import', 'ImportFrom', 'Nonlocal', 'Pass', } def add_arcs(self, node: ast.AST) -> set[ArcStart]: @@ -832,7 +853,7 @@ class AstArcAnalyzer: node_name = node.__class__.__name__ handler = cast( Optional[Callable[[ast.AST], Set[ArcStart]]], - getattr(self, "_handle__" + node_name, None), + getattr(self, '_handle__' + node_name, None), ) if handler is not None: return handler(node) @@ -841,7 +862,7 @@ class AstArcAnalyzer: # statement), or it's something we overlooked. if env.TESTING: if node_name not in self.OK_TO_DEFAULT: - raise RuntimeError(f"*** Unhandled: {node}") # pragma: only failure + raise RuntimeError(f'*** Unhandled: {node}') # pragma: only failure # Default for simple statements: one exit from this node. return {ArcStart(self.line_for_node(node))} @@ -898,7 +919,7 @@ class AstArcAnalyzer: missing_fn = cast( Optional[Callable[[ast.AST], Optional[ast.AST]]], - getattr(self, "_missing__" + node.__class__.__name__, None), + getattr(self, '_missing__' + node.__class__.__name__, None), ) if missing_fn is not None: ret_node = missing_fn(node) @@ -949,8 +970,8 @@ class AstArcAnalyzer: new_while.lineno = body_nodes.lineno new_while.test = ast.Name() new_while.test.lineno = body_nodes.lineno - new_while.test.id = "True" - assert hasattr(body_nodes, "body") + new_while.test.id = 'True' + assert hasattr(body_nodes, 'body') new_while.body = body_nodes.body new_while.orelse = [] return new_while @@ -958,11 +979,11 @@ class AstArcAnalyzer: def is_constant_expr(self, node: ast.AST) -> str | None: """Is this a compile-time constant?""" node_name = node.__class__.__name__ - if node_name in ["Constant", "NameConstant", "Num"]: - return "Num" + if node_name in ['Constant', 'NameConstant', 'Num']: + return 'Num' elif isinstance(node, ast.Name): - if node.id in ["True", "False", "None", "__debug__"]: - return "Name" + if node.id in ['True', 'False', 'None', '__debug__']: + return 'Name' return None # In the fullness of time, these might be good tests to write: @@ -1063,7 +1084,7 @@ class AstArcAnalyzer: def _handle__For(self, node: ast.For) -> set[ArcStart]: start = self.line_for_node(node.iter) self.block_stack.append(LoopBlock(start=start)) - from_start = ArcStart(start, cause="the loop on line {lineno} never started") + from_start = ArcStart(start, cause='the loop on line {lineno} never started') exits = self.add_body_arcs(node.body, from_start=from_start) # Any exit from the body will go back to the top of the loop. for xit in exits: @@ -1087,9 +1108,9 @@ class AstArcAnalyzer: def _handle__If(self, node: ast.If) -> set[ArcStart]: start = self.line_for_node(node.test) - from_start = ArcStart(start, cause="the condition on line {lineno} was never true") + from_start = ArcStart(start, cause='the condition on line {lineno} was never true') exits = self.add_body_arcs(node.body, from_start=from_start) - from_start = ArcStart(start, cause="the condition on line {lineno} was never false") + from_start = ArcStart(start, cause='the condition on line {lineno} was never false') exits |= self.add_body_arcs(node.orelse, from_start=from_start) return exits @@ -1106,16 +1127,16 @@ class AstArcAnalyzer: pattern = pattern.patterns[-1] if isinstance(pattern, ast.MatchAs): had_wildcard = True - self.add_arc(last_start, case_start, "the pattern on line {lineno} always matched") + self.add_arc(last_start, case_start, 'the pattern on line {lineno} always matched') from_start = ArcStart( case_start, - cause="the pattern on line {lineno} never matched", + cause='the pattern on line {lineno} never matched', ) exits |= self.add_body_arcs(case.body, from_start=from_start) last_start = case_start if not had_wildcard: exits.add( - ArcStart(case_start, cause="the pattern on line {lineno} always matched"), + ArcStart(case_start, cause='the pattern on line {lineno} always matched'), ) return exits @@ -1260,7 +1281,7 @@ class AstArcAnalyzer: for start in sorted(starts): if start.cause: causes.append(start.cause.format(lineno=start.lineno)) - cause = " or ".join(causes) + cause = ' or '.join(causes) exits = {ArcStart(xit.lineno, cause) for xit in exits} return exits @@ -1275,7 +1296,7 @@ class AstArcAnalyzer: if top_is_body0: to_top = self.line_for_node(node.body[0]) self.block_stack.append(LoopBlock(start=to_top)) - from_start = ArcStart(start, cause="the condition on line {lineno} was never true") + from_start = ArcStart(start, cause='the condition on line {lineno} was never true') exits = self.add_body_arcs(node.body, from_start=from_start) for xit in exits: self.add_arc(xit.lineno, to_top, xit.cause) @@ -1283,7 +1304,7 @@ class AstArcAnalyzer: my_block = self.block_stack.pop() assert isinstance(my_block, LoopBlock) exits.update(my_block.break_exits) - from_start = ArcStart(start, cause="the condition on line {lineno} was never false") + from_start = ArcStart(start, cause='the condition on line {lineno} was never false') if node.orelse: else_exits = self.add_body_arcs(node.orelse, from_start=from_start) exits |= else_exits @@ -1357,9 +1378,9 @@ class AstArcAnalyzer: f"didn't exit the body of class {node.name!r}", ) - _code_object__Lambda = _make_expression_code_method("lambda") - _code_object__GeneratorExp = _make_expression_code_method("generator expression") + _code_object__Lambda = _make_expression_code_method('lambda') + _code_object__GeneratorExp = _make_expression_code_method('generator expression') if env.PYBEHAVIOR.comprehensions_are_functions: - _code_object__DictComp = _make_expression_code_method("dictionary comprehension") - _code_object__SetComp = _make_expression_code_method("set comprehension") - _code_object__ListComp = _make_expression_code_method("list comprehension") + _code_object__DictComp = _make_expression_code_method('dictionary comprehension') + _code_object__SetComp = _make_expression_code_method('set comprehension') + _code_object__ListComp = _make_expression_code_method('list comprehension') diff --git a/.venv/lib/python3.10/site-packages/coverage/phystokens.py b/.venv/lib/python3.10/site-packages/coverage/phystokens.py index 7d8b30c..3ab3ccd 100644 --- a/.venv/lib/python3.10/site-packages/coverage/phystokens.py +++ b/.venv/lib/python3.10/site-packages/coverage/phystokens.py @@ -1,8 +1,6 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Better tokenizing for coverage.py.""" - from __future__ import annotations import ast @@ -12,11 +10,11 @@ import re import sys import token import tokenize - from typing import Iterable from coverage import env -from coverage.types import TLineNo, TSourceTokenLines +from coverage.types import TLineNo +from coverage.types import TSourceTokenLines TokenInfos = Iterable[tokenize.TokenInfo] @@ -34,10 +32,10 @@ def _phys_tokens(toks: TokenInfos) -> TokenInfos: """ last_line: str | None = None last_lineno = -1 - last_ttext: str = "" + last_ttext: str = '' for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks: if last_lineno != elineno: - if last_line and last_line.endswith("\\\n"): + if last_line and last_line.endswith('\\\n'): # We are at the beginning of a new line, and the last line # ended with a backslash. We probably have to inject a # backslash token into the stream. Unfortunately, there's more @@ -54,20 +52,20 @@ def _phys_tokens(toks: TokenInfos) -> TokenInfos: # so we need to figure out if the backslash is already in the # string token or not. inject_backslash = True - if last_ttext.endswith("\\"): + if last_ttext.endswith('\\'): inject_backslash = False elif ttype == token.STRING: - if "\n" in ttext and ttext.split("\n", 1)[0][-1] == "\\": + if '\n' in ttext and ttext.split('\n', 1)[0][-1] == '\\': # It's a multi-line string and the first line ends with # a backslash, so we don't need to inject another. inject_backslash = False if inject_backslash: # Figure out what column the backslash is in. - ccol = len(last_line.split("\n")[-2]) - 1 + ccol = len(last_line.split('\n')[-2]) - 1 # Yield the token, with a fake token type. yield tokenize.TokenInfo( - 99999, "\\\n", - (slineno, ccol), (slineno, ccol+2), + 99999, '\\\n', + (slineno, ccol), (slineno, ccol + 2), last_line, ) last_line = ltext @@ -79,6 +77,7 @@ def _phys_tokens(toks: TokenInfos) -> TokenInfos: class SoftKeywordFinder(ast.NodeVisitor): """Helper for finding lines with soft keywords, like match/case lines.""" + def __init__(self, source: str) -> None: # This will be the set of line numbers that start with a soft keyword. self.soft_key_lines: set[TLineNo] = set() @@ -119,7 +118,7 @@ def source_token_lines(source: str) -> TSourceTokenLines: line: list[tuple[str, str]] = [] col = 0 - source = source.expandtabs(8).replace("\r\n", "\n") + source = source.expandtabs(8).replace('\r\n', '\n') tokgen = generate_tokens(source) if env.PYBEHAVIOR.soft_keywords: @@ -127,25 +126,25 @@ def source_token_lines(source: str) -> TSourceTokenLines: for ttype, ttext, (sline, scol), (_, ecol), _ in _phys_tokens(tokgen): mark_start = True - for part in re.split("(\n)", ttext): - if part == "\n": + for part in re.split('(\n)', ttext): + if part == '\n': yield line line = [] col = 0 mark_end = False - elif part == "": + elif part == '': mark_end = False elif ttype in ws_tokens: mark_end = False else: if mark_start and scol > col: - line.append(("ws", " " * (scol - col))) + line.append(('ws', ' ' * (scol - col))) mark_start = False - tok_class = tokenize.tok_name.get(ttype, "xx").lower()[:3] + tok_class = tokenize.tok_name.get(ttype, 'xx').lower()[:3] if ttype == token.NAME: if keyword.iskeyword(ttext): # Hard keywords are always keywords. - tok_class = "key" + tok_class = 'key' elif sys.version_info >= (3, 10): # PYVERSIONS # Need the version_info check to keep mypy from borking # on issoftkeyword here. @@ -154,12 +153,12 @@ def source_token_lines(source: str) -> TSourceTokenLines: # on lines that start match or case statements. if len(line) == 0: is_start_of_line = True - elif (len(line) == 1) and line[0][0] == "ws": + elif (len(line) == 1) and line[0][0] == 'ws': is_start_of_line = True else: is_start_of_line = False if is_start_of_line and sline in soft_key_lines: - tok_class = "key" + tok_class = 'key' line.append((tok_class, part)) mark_end = True scol = 0 @@ -181,6 +180,7 @@ class CachedTokenizer: actually tokenize twice. """ + def __init__(self) -> None: self.last_text: str | None = None self.last_tokens: list[tokenize.TokenInfo] = [] @@ -197,6 +197,7 @@ class CachedTokenizer: raise return self.last_tokens + # Create our generate_tokens cache as a callable replacement function. generate_tokens = CachedTokenizer().generate_tokens diff --git a/.venv/lib/python3.10/site-packages/coverage/plugin.py b/.venv/lib/python3.10/site-packages/coverage/plugin.py index 7614066..a6bbb5b 100644 --- a/.venv/lib/python3.10/site-packages/coverage/plugin.py +++ b/.venv/lib/python3.10/site-packages/coverage/plugin.py @@ -1,6 +1,5 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """ .. versionadded:: 4.0 @@ -111,17 +110,19 @@ In your ``coverage_init`` function, use the ``add_dynamic_context`` method to register your dynamic context switcher. """ - from __future__ import annotations import functools - from types import FrameType -from typing import Any, Iterable +from typing import Any +from typing import Iterable from coverage import files from coverage.misc import _needs_to_implement -from coverage.types import TArc, TConfigurable, TLineNo, TSourceTokenLines +from coverage.types import TArc +from coverage.types import TConfigurable +from coverage.types import TLineNo +from coverage.types import TSourceTokenLines class CoveragePlugin: @@ -130,7 +131,7 @@ class CoveragePlugin: _coverage_plugin_name: str _coverage_enabled: bool - def file_tracer(self, filename: str) -> FileTracer | None: # pylint: disable=unused-argument + def file_tracer(self, filename: str) -> FileTracer | None: # pylint: disable=unused-argument """Get a :class:`FileTracer` object for a file. Plug-in type: file tracer. @@ -185,7 +186,7 @@ class CoveragePlugin: or the string `"python"` to have coverage.py treat the file as Python. """ - _needs_to_implement(self, "file_reporter") + _needs_to_implement(self, 'file_reporter') def dynamic_context( self, @@ -287,7 +288,7 @@ class FileTracer(CoveragePluginBase): Returns the file name to credit with this execution. """ - _needs_to_implement(self, "source_filename") + _needs_to_implement(self, 'source_filename') def has_dynamic_source_filename(self) -> bool: """Does this FileTracer have dynamic source file names? @@ -369,7 +370,7 @@ class FileReporter(CoveragePluginBase): self.filename = filename def __repr__(self) -> str: - return f"<{self.__class__.__name__} filename={self.filename!r}>" + return f'<{self.__class__.__name__} filename={self.filename!r}>' def relative_filename(self) -> str: """Get the relative file name for this file. @@ -392,7 +393,7 @@ class FileReporter(CoveragePluginBase): as a text file, or if you need other encoding support. """ - with open(self.filename, encoding="utf-8") as f: + with open(self.filename, encoding='utf-8') as f: return f.read() def lines(self) -> set[TLineNo]: @@ -404,7 +405,7 @@ class FileReporter(CoveragePluginBase): Returns a set of line numbers. """ - _needs_to_implement(self, "lines") + _needs_to_implement(self, 'lines') def excluded_lines(self) -> set[TLineNo]: """Get the excluded executable lines in this file. @@ -541,7 +542,7 @@ class FileReporter(CoveragePluginBase): """ for line in self.source().splitlines(): - yield [("txt", line)] + yield [('txt', line)] def __eq__(self, other: Any) -> bool: return isinstance(other, FileReporter) and self.filename == other.filename diff --git a/.venv/lib/python3.10/site-packages/coverage/plugin_support.py b/.venv/lib/python3.10/site-packages/coverage/plugin_support.py index 7b843a1..fa5d48a 100644 --- a/.venv/lib/python3.10/site-packages/coverage/plugin_support.py +++ b/.venv/lib/python3.10/site-packages/coverage/plugin_support.py @@ -1,23 +1,26 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Support for plugins.""" - from __future__ import annotations -import os import os.path import sys - from types import FrameType -from typing import Any, Iterable, Iterator +from typing import Any +from typing import Iterable +from typing import Iterator from coverage.exceptions import PluginError from coverage.misc import isolate_module -from coverage.plugin import CoveragePlugin, FileTracer, FileReporter -from coverage.types import ( - TArc, TConfigurable, TDebugCtl, TLineNo, TPluginConfig, TSourceTokenLines, -) +from coverage.plugin import CoveragePlugin +from coverage.plugin import FileReporter +from coverage.plugin import FileTracer +from coverage.types import TArc +from coverage.types import TConfigurable +from coverage.types import TDebugCtl +from coverage.types import TLineNo +from coverage.types import TPluginConfig +from coverage.types import TSourceTokenLines os = isolate_module(os) @@ -55,7 +58,7 @@ class Plugins: __import__(module) mod = sys.modules[module] - coverage_init = getattr(mod, "coverage_init", None) + coverage_init = getattr(mod, 'coverage_init', None) if not coverage_init: raise PluginError( f"Plugin module {module!r} didn't define a coverage_init function", @@ -113,10 +116,10 @@ class Plugins: is a list to append the plugin to. """ - plugin_name = f"{self.current_module}.{plugin.__class__.__name__}" - if self.debug and self.debug.should("plugin"): - self.debug.write(f"Loaded plugin {self.current_module!r}: {plugin!r}") - labelled = LabelledDebug(f"plugin {self.current_module!r}", self.debug) + plugin_name = f'{self.current_module}.{plugin.__class__.__name__}' + if self.debug and self.debug.should('plugin'): + self.debug.write(f'Loaded plugin {self.current_module!r}: {plugin!r}') + labelled = LabelledDebug(f'plugin {self.current_module!r}', self.debug) plugin = DebugPluginWrapper(plugin, labelled) plugin._coverage_plugin_name = plugin_name @@ -150,12 +153,12 @@ class LabelledDebug: def message_prefix(self) -> str: """The prefix to use on messages, combining the labels.""" - prefixes = self.labels + [""] - return ":\n".join(" "*i+label for i, label in enumerate(prefixes)) + prefixes = self.labels + [''] + return ':\n'.join(' ' * i + label for i, label in enumerate(prefixes)) def write(self, message: str) -> None: """Write `message`, but with the labels prepended.""" - self.debug.write(f"{self.message_prefix()}{message}") + self.debug.write(f'{self.message_prefix()}{message}') class DebugPluginWrapper(CoveragePlugin): @@ -168,33 +171,33 @@ class DebugPluginWrapper(CoveragePlugin): def file_tracer(self, filename: str) -> FileTracer | None: tracer = self.plugin.file_tracer(filename) - self.debug.write(f"file_tracer({filename!r}) --> {tracer!r}") + self.debug.write(f'file_tracer({filename!r}) --> {tracer!r}') if tracer: - debug = self.debug.add_label(f"file {filename!r}") + debug = self.debug.add_label(f'file {filename!r}') tracer = DebugFileTracerWrapper(tracer, debug) return tracer def file_reporter(self, filename: str) -> FileReporter | str: reporter = self.plugin.file_reporter(filename) assert isinstance(reporter, FileReporter) - self.debug.write(f"file_reporter({filename!r}) --> {reporter!r}") + self.debug.write(f'file_reporter({filename!r}) --> {reporter!r}') if reporter: - debug = self.debug.add_label(f"file {filename!r}") + debug = self.debug.add_label(f'file {filename!r}') reporter = DebugFileReporterWrapper(filename, reporter, debug) return reporter def dynamic_context(self, frame: FrameType) -> str | None: context = self.plugin.dynamic_context(frame) - self.debug.write(f"dynamic_context({frame!r}) --> {context!r}") + self.debug.write(f'dynamic_context({frame!r}) --> {context!r}') return context def find_executable_files(self, src_dir: str) -> Iterable[str]: executable_files = self.plugin.find_executable_files(src_dir) - self.debug.write(f"find_executable_files({src_dir!r}) --> {executable_files!r}") + self.debug.write(f'find_executable_files({src_dir!r}) --> {executable_files!r}') return executable_files def configure(self, config: TConfigurable) -> None: - self.debug.write(f"configure({config!r})") + self.debug.write(f'configure({config!r})') self.plugin.configure(config) def sys_info(self) -> Iterable[tuple[str, Any]]: @@ -210,31 +213,33 @@ class DebugFileTracerWrapper(FileTracer): def _show_frame(self, frame: FrameType) -> str: """A short string identifying a frame, for debug messages.""" - return "%s@%d" % ( + return '%s@%d' % ( os.path.basename(frame.f_code.co_filename), frame.f_lineno, ) def source_filename(self) -> str: sfilename = self.tracer.source_filename() - self.debug.write(f"source_filename() --> {sfilename!r}") + self.debug.write(f'source_filename() --> {sfilename!r}') return sfilename def has_dynamic_source_filename(self) -> bool: has = self.tracer.has_dynamic_source_filename() - self.debug.write(f"has_dynamic_source_filename() --> {has!r}") + self.debug.write(f'has_dynamic_source_filename() --> {has!r}') return has def dynamic_source_filename(self, filename: str, frame: FrameType) -> str | None: dyn = self.tracer.dynamic_source_filename(filename, frame) - self.debug.write("dynamic_source_filename({!r}, {}) --> {!r}".format( - filename, self._show_frame(frame), dyn, - )) + self.debug.write( + 'dynamic_source_filename({!r}, {}) --> {!r}'.format( + filename, self._show_frame(frame), dyn, + ), + ) return dyn def line_number_range(self, frame: FrameType) -> tuple[TLineNo, TLineNo]: pair = self.tracer.line_number_range(frame) - self.debug.write(f"line_number_range({self._show_frame(frame)}) --> {pair!r}") + self.debug.write(f'line_number_range({self._show_frame(frame)}) --> {pair!r}') return pair @@ -248,50 +253,50 @@ class DebugFileReporterWrapper(FileReporter): def relative_filename(self) -> str: ret = self.reporter.relative_filename() - self.debug.write(f"relative_filename() --> {ret!r}") + self.debug.write(f'relative_filename() --> {ret!r}') return ret def lines(self) -> set[TLineNo]: ret = self.reporter.lines() - self.debug.write(f"lines() --> {ret!r}") + self.debug.write(f'lines() --> {ret!r}') return ret def excluded_lines(self) -> set[TLineNo]: ret = self.reporter.excluded_lines() - self.debug.write(f"excluded_lines() --> {ret!r}") + self.debug.write(f'excluded_lines() --> {ret!r}') return ret def translate_lines(self, lines: Iterable[TLineNo]) -> set[TLineNo]: ret = self.reporter.translate_lines(lines) - self.debug.write(f"translate_lines({lines!r}) --> {ret!r}") + self.debug.write(f'translate_lines({lines!r}) --> {ret!r}') return ret def translate_arcs(self, arcs: Iterable[TArc]) -> set[TArc]: ret = self.reporter.translate_arcs(arcs) - self.debug.write(f"translate_arcs({arcs!r}) --> {ret!r}") + self.debug.write(f'translate_arcs({arcs!r}) --> {ret!r}') return ret def no_branch_lines(self) -> set[TLineNo]: ret = self.reporter.no_branch_lines() - self.debug.write(f"no_branch_lines() --> {ret!r}") + self.debug.write(f'no_branch_lines() --> {ret!r}') return ret def exit_counts(self) -> dict[TLineNo, int]: ret = self.reporter.exit_counts() - self.debug.write(f"exit_counts() --> {ret!r}") + self.debug.write(f'exit_counts() --> {ret!r}') return ret def arcs(self) -> set[TArc]: ret = self.reporter.arcs() - self.debug.write(f"arcs() --> {ret!r}") + self.debug.write(f'arcs() --> {ret!r}') return ret def source(self) -> str: ret = self.reporter.source() - self.debug.write("source() --> %d chars" % (len(ret),)) + self.debug.write('source() --> %d chars' % (len(ret),)) return ret def source_token_lines(self) -> TSourceTokenLines: ret = list(self.reporter.source_token_lines()) - self.debug.write("source_token_lines() --> %d tokens" % (len(ret),)) + self.debug.write('source_token_lines() --> %d tokens' % (len(ret),)) return ret diff --git a/.venv/lib/python3.10/site-packages/coverage/python.py b/.venv/lib/python3.10/site-packages/coverage/python.py index 0a522d6..2cc24c4 100644 --- a/.venv/lib/python3.10/site-packages/coverage/python.py +++ b/.venv/lib/python3.10/site-packages/coverage/python.py @@ -1,24 +1,31 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Python source expertise for coverage.py""" - from __future__ import annotations import os.path import types import zipimport - -from typing import Iterable, TYPE_CHECKING +from typing import Iterable +from typing import TYPE_CHECKING from coverage import env -from coverage.exceptions import CoverageException, NoSource -from coverage.files import canonical_filename, relative_filename, zip_location -from coverage.misc import expensive, isolate_module, join_regex +from coverage.exceptions import CoverageException +from coverage.exceptions import NoSource +from coverage.files import canonical_filename +from coverage.files import relative_filename +from coverage.files import zip_location +from coverage.misc import expensive +from coverage.misc import isolate_module +from coverage.misc import join_regex from coverage.parser import PythonParser -from coverage.phystokens import source_token_lines, source_encoding +from coverage.phystokens import source_encoding +from coverage.phystokens import source_token_lines from coverage.plugin import FileReporter -from coverage.types import TArc, TLineNo, TMorf, TSourceTokenLines +from coverage.types import TArc +from coverage.types import TLineNo +from coverage.types import TMorf +from coverage.types import TSourceTokenLines if TYPE_CHECKING: from coverage import Coverage @@ -32,17 +39,17 @@ def read_python_source(filename: str) -> bytes: Returns bytes. """ - with open(filename, "rb") as f: + with open(filename, 'rb') as f: source = f.read() - return source.replace(b"\r\n", b"\n").replace(b"\r", b"\n") + return source.replace(b'\r\n', b'\n').replace(b'\r', b'\n') def get_python_source(filename: str) -> str: """Return the source code, as unicode.""" base, ext = os.path.splitext(filename) - if ext == ".py" and env.WINDOWS: - exts = [".py", ".pyw"] + if ext == '.py' and env.WINDOWS: + exts = ['.py', '.pyw'] else: exts = [ext] @@ -63,12 +70,12 @@ def get_python_source(filename: str) -> str: raise NoSource(f"No source for code: '{filename}'.") # Replace \f because of http://bugs.python.org/issue19035 - source_bytes = source_bytes.replace(b"\f", b" ") - source = source_bytes.decode(source_encoding(source_bytes), "replace") + source_bytes = source_bytes.replace(b'\f', b' ') + source = source_bytes.decode(source_encoding(source_bytes), 'replace') # Python code should always end with a line with a newline. - if source and source[-1] != "\n": - source += "\n" + if source and source[-1] != '\n': + source += '\n' return source @@ -103,11 +110,11 @@ def source_for_file(filename: str) -> str: file to attribute it to. """ - if filename.endswith(".py"): + if filename.endswith('.py'): # .py files are themselves source files. return filename - elif filename.endswith((".pyc", ".pyo")): + elif filename.endswith(('.pyc', '.pyo')): # Bytecode files probably have source files near them. py_filename = filename[:-1] if os.path.exists(py_filename): @@ -115,7 +122,7 @@ def source_for_file(filename: str) -> str: return py_filename if env.WINDOWS: # On Windows, it could be a .pyw file. - pyw_filename = py_filename + "w" + pyw_filename = py_filename + 'w' if os.path.exists(pyw_filename): return pyw_filename # Didn't find source, but it's probably the .py file we want. @@ -127,12 +134,12 @@ def source_for_file(filename: str) -> str: def source_for_morf(morf: TMorf) -> str: """Get the source filename for the module-or-file `morf`.""" - if hasattr(morf, "__file__") and morf.__file__: + if hasattr(morf, '__file__') and morf.__file__: filename = morf.__file__ elif isinstance(morf, types.ModuleType): # A module should have had .__file__, otherwise we can't use it. # This could be a PEP-420 namespace package. - raise CoverageException(f"Module {morf} has no file") + raise CoverageException(f'Module {morf} has no file') else: filename = morf @@ -157,11 +164,11 @@ class PythonFileReporter(FileReporter): fname = canonical_filename(filename) super().__init__(fname) - if hasattr(morf, "__name__"): - name = morf.__name__.replace(".", os.sep) - if os.path.basename(filename).startswith("__init__."): - name += os.sep + "__init__" - name += ".py" + if hasattr(morf, '__name__'): + name = morf.__name__.replace('.', os.sep) + if os.path.basename(filename).startswith('__init__.'): + name += os.sep + '__init__' + name += '.py' else: name = relative_filename(filename) self.relname = name @@ -171,7 +178,7 @@ class PythonFileReporter(FileReporter): self._excluded = None def __repr__(self) -> str: - return f"" + return f'' def relative_filename(self) -> str: return self.relname @@ -183,7 +190,7 @@ class PythonFileReporter(FileReporter): if self._parser is None: self._parser = PythonParser( filename=self.filename, - exclude=self.coverage._exclude_regex("exclude"), + exclude=self.coverage._exclude_regex('exclude'), ) self._parser.parse_source() return self._parser @@ -244,7 +251,7 @@ class PythonFileReporter(FileReporter): _, ext = os.path.splitext(self.filename) # Anything named *.py* should be Python. - if ext.startswith(".py"): + if ext.startswith('.py'): return True # A file with no extension should be Python. if not ext: diff --git a/.venv/lib/python3.10/site-packages/coverage/pytracer.py b/.venv/lib/python3.10/site-packages/coverage/pytracer.py index 69d52c9..454ca90 100644 --- a/.venv/lib/python3.10/site-packages/coverage/pytracer.py +++ b/.venv/lib/python3.10/site-packages/coverage/pytracer.py @@ -1,8 +1,6 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Raw data collector for coverage.py.""" - from __future__ import annotations import atexit @@ -10,29 +8,37 @@ import dis import itertools import sys import threading - -from types import FrameType, ModuleType -from typing import Any, Callable, Set, cast +from types import FrameType +from types import ModuleType +from typing import Any +from typing import Callable +from typing import cast +from typing import Set from coverage import env -from coverage.types import ( - TArc, TFileDisposition, TLineNo, TTraceData, TTraceFileData, TTraceFn, - TracerCore, TWarnFn, -) +from coverage.types import TArc +from coverage.types import TFileDisposition +from coverage.types import TLineNo +from coverage.types import TracerCore +from coverage.types import TTraceData +from coverage.types import TTraceFileData +from coverage.types import TTraceFn +from coverage.types import TWarnFn # We need the YIELD_VALUE opcode below, in a comparison-friendly form. # PYVERSIONS: RESUME is new in Python3.11 -RESUME = dis.opmap.get("RESUME") -RETURN_VALUE = dis.opmap["RETURN_VALUE"] +RESUME = dis.opmap.get('RESUME') +RETURN_VALUE = dis.opmap['RETURN_VALUE'] if RESUME is None: - YIELD_VALUE = dis.opmap["YIELD_VALUE"] - YIELD_FROM = dis.opmap["YIELD_FROM"] + YIELD_VALUE = dis.opmap['YIELD_VALUE'] + YIELD_FROM = dis.opmap['YIELD_FROM'] YIELD_FROM_OFFSET = 0 if env.PYPY else 2 # When running meta-coverage, this file can try to trace itself, which confuses # everything. Don't trace ourselves. -THIS_FILE = __file__.rstrip("co") +THIS_FILE = __file__.rstrip('co') + class PyTracer(TracerCore): """Python implementation of the raw data tracer.""" @@ -92,7 +98,7 @@ class PyTracer(TracerCore): self.in_atexit = False # On exit, self.in_atexit = True - atexit.register(setattr, self, "in_atexit", True) + atexit.register(setattr, self, 'in_atexit', True) # Cache a bound method on the instance, so that we don't have to # re-create a bound method object all the time. @@ -101,26 +107,28 @@ class PyTracer(TracerCore): def __repr__(self) -> str: points = sum(len(v) for v in self.data.values()) files = len(self.data) - return f"" + return f'' def log(self, marker: str, *args: Any) -> None: """For hard-core logging of what this tracer is doing.""" - with open("/tmp/debug_trace.txt", "a") as f: - f.write(f"{marker} {self.id}[{len(self.data_stack)}]") + with open('/tmp/debug_trace.txt', 'a') as f: + f.write(f'{marker} {self.id}[{len(self.data_stack)}]') if 0: # if you want thread ids.. - f.write(".{:x}.{:x}".format( # type: ignore[unreachable] - self.thread.ident, - self.threading.current_thread().ident, - )) - f.write(" {}".format(" ".join(map(str, args)))) + f.write( + '.{:x}.{:x}'.format( # type: ignore[unreachable] + self.thread.ident, + self.threading.current_thread().ident, + ), + ) + f.write(' {}'.format(' '.join(map(str, args)))) if 0: # if you want callers.. - f.write(" | ") # type: ignore[unreachable] - stack = " / ".join( - (fname or "???").rpartition("/")[-1] + f.write(' | ') # type: ignore[unreachable] + stack = ' / '.join( + (fname or '???').rpartition('/')[-1] for _, fname, _, _ in self.data_stack ) f.write(stack) - f.write("\n") + f.write('\n') def _trace( self, @@ -142,9 +150,9 @@ class PyTracer(TracerCore): # thread, let's deactivate ourselves now. if 0: f = frame # type: ignore[unreachable] - self.log("---\nX", f.f_code.co_filename, f.f_lineno) + self.log('---\nX', f.f_code.co_filename, f.f_lineno) while f: - self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace) + self.log('>', f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace) f = f.f_back sys.settrace(None) try: @@ -153,7 +161,7 @@ class PyTracer(TracerCore): ) except IndexError: self.log( - "Empty stack!", + 'Empty stack!', frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name, @@ -163,7 +171,7 @@ class PyTracer(TracerCore): # if event != "call" and frame.f_code.co_filename != self.cur_file_name: # self.log("---\n*", frame.f_code.co_filename, self.cur_file_name, frame.f_lineno) - if event == "call": + if event == 'call': # Should we start a new context? if self.should_start_context and self.context is None: context_maybe = self.should_start_context(frame) @@ -225,13 +233,13 @@ class PyTracer(TracerCore): oparg = frame.f_code.co_code[frame.f_lasti + 1] real_call = (oparg == 0) else: - real_call = (getattr(frame, "f_lasti", -1) < 0) + real_call = (getattr(frame, 'f_lasti', -1) < 0) if real_call: self.last_line = -frame.f_code.co_firstlineno else: self.last_line = frame.f_lineno - elif event == "line": + elif event == 'line': # Record an executed line. if self.cur_file_data is not None: flineno: TLineNo = frame.f_lineno @@ -242,7 +250,7 @@ class PyTracer(TracerCore): cast(Set[TLineNo], self.cur_file_data).add(flineno) self.last_line = flineno - elif event == "return": + elif event == 'return': if self.trace_arcs and self.cur_file_data: # Record an arc leaving the function, but beware that a # "return" event might just mean yielding from a generator. @@ -322,15 +330,15 @@ class PyTracer(TracerCore): # has changed to None. Metacoverage also messes this up, so don't # warn if we are measuring ourselves. suppress_warning = ( - (env.PYPY and self.in_atexit and tf is None) - or env.METACOV + (env.PYPY and self.in_atexit and tf is None) or + env.METACOV ) if self.warn and not suppress_warning: if tf != self._cached_bound_method_trace: # pylint: disable=comparison-with-callable self.warn( - "Trace function changed, data is likely wrong: " + - f"{tf!r} != {self._cached_bound_method_trace!r}", - slug="trace-changed", + 'Trace function changed, data is likely wrong: ' + + f'{tf!r} != {self._cached_bound_method_trace!r}', + slug='trace-changed', ) def activity(self) -> bool: diff --git a/.venv/lib/python3.10/site-packages/coverage/report.py b/.venv/lib/python3.10/site-packages/coverage/report.py index 42f7b5a..51e36f3 100644 --- a/.venv/lib/python3.10/site-packages/coverage/report.py +++ b/.venv/lib/python3.10/site-packages/coverage/report.py @@ -1,19 +1,21 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Summary reporting""" - from __future__ import annotations import sys +from typing import Any +from typing import IO +from typing import Iterable +from typing import TYPE_CHECKING -from typing import Any, IO, Iterable, TYPE_CHECKING - -from coverage.exceptions import ConfigError, NoDataError +from coverage.exceptions import ConfigError +from coverage.exceptions import NoDataError from coverage.misc import human_sorted_items from coverage.plugin import FileReporter from coverage.report_core import get_analysis_to_report -from coverage.results import Analysis, Numbers +from coverage.results import Analysis +from coverage.results import Numbers from coverage.types import TMorf if TYPE_CHECKING: @@ -28,9 +30,9 @@ class SummaryReporter: self.config = self.coverage.config self.branches = coverage.get_data().has_arcs() self.outfile: IO[str] | None = None - self.output_format = self.config.format or "text" - if self.output_format not in {"text", "markdown", "total"}: - raise ConfigError(f"Unknown report format choice: {self.output_format!r}") + self.output_format = self.config.format or 'text' + if self.output_format not in {'text', 'markdown', 'total'}: + raise ConfigError(f'Unknown report format choice: {self.output_format!r}') self.fr_analysis: list[tuple[FileReporter, Analysis]] = [] self.skipped_count = 0 self.empty_count = 0 @@ -40,11 +42,11 @@ class SummaryReporter: """Write a line to the output, adding a newline.""" assert self.outfile is not None self.outfile.write(line.rstrip()) - self.outfile.write("\n") + self.outfile.write('\n') def write_items(self, items: Iterable[str]) -> None: """Write a list of strings, joined together.""" - self.write("".join(items)) + self.write(''.join(items)) def _report_text( self, @@ -63,34 +65,36 @@ class SummaryReporter: """ # Prepare the formatting strings, header, and column sorting. max_name = max([len(line[0]) for line in lines_values] + [5]) + 1 - max_n = max(len(total_line[header.index("Cover")]) + 2, len(" Cover")) + 1 - max_n = max([max_n] + [len(line[header.index("Cover")]) + 2 for line in lines_values]) + max_n = max(len(total_line[header.index('Cover')]) + 2, len(' Cover')) + 1 + max_n = max([max_n] + [len(line[header.index('Cover')]) + 2 for line in lines_values]) formats = dict( - Name="{:{name_len}}", - Stmts="{:>7}", - Miss="{:>7}", - Branch="{:>7}", - BrPart="{:>7}", - Cover="{:>{n}}", - Missing="{:>10}", + Name='{:{name_len}}', + Stmts='{:>7}', + Miss='{:>7}', + Branch='{:>7}', + BrPart='{:>7}', + Cover='{:>{n}}', + Missing='{:>10}', ) header_items = [ formats[item].format(item, name_len=max_name, n=max_n) for item in header ] - header_str = "".join(header_items) - rule = "-" * len(header_str) + header_str = ''.join(header_items) + rule = '-' * len(header_str) # Write the header self.write(header_str) self.write(rule) - formats.update(dict(Cover="{:>{n}}%"), Missing=" {:9}") + formats.update(dict(Cover='{:>{n}}%'), Missing=' {:9}') for values in lines_values: # build string with line values line_items = [ - formats[item].format(str(value), - name_len=max_name, n=max_n-1) for item, value in zip(header, values) + formats[item].format( + str(value), + name_len=max_name, n=max_n - 1, + ) for item, value in zip(header, values) ] self.write_items(line_items) @@ -99,8 +103,10 @@ class SummaryReporter: self.write(rule) line_items = [ - formats[item].format(str(value), - name_len=max_name, n=max_n-1) for item, value in zip(header, total_line) + formats[item].format( + str(value), + name_len=max_name, n=max_n - 1, + ) for item, value in zip(header, total_line) ] self.write_items(line_items) @@ -123,22 +129,23 @@ class SummaryReporter: """ # Prepare the formatting strings, header, and column sorting. - max_name = max((len(line[0].replace("_", "\\_")) for line in lines_values), default=0) - max_name = max(max_name, len("**TOTAL**")) + 1 + max_name = max((len(line[0].replace('_', '\\_')) for line in lines_values), default=0) + max_name = max(max_name, len('**TOTAL**')) + 1 formats = dict( - Name="| {:{name_len}}|", - Stmts="{:>9} |", - Miss="{:>9} |", - Branch="{:>9} |", - BrPart="{:>9} |", - Cover="{:>{n}} |", - Missing="{:>10} |", + Name='| {:{name_len}}|', + Stmts='{:>9} |', + Miss='{:>9} |', + Branch='{:>9} |', + BrPart='{:>9} |', + Cover='{:>{n}} |', + Missing='{:>10} |', ) - max_n = max(len(total_line[header.index("Cover")]) + 6, len(" Cover ")) + max_n = max(len(total_line[header.index('Cover')]) + 6, len(' Cover ')) header_items = [formats[item].format(item, name_len=max_name, n=max_n) for item in header] - header_str = "".join(header_items) - rule_str = "|" + " ".join(["- |".rjust(len(header_items[0])-1, "-")] + - ["-: |".rjust(len(item)-1, "-") for item in header_items[1:]], + header_str = ''.join(header_items) + rule_str = '|' + ' '.join( + ['- |'.rjust(len(header_items[0]) - 1, '-')] + + ['-: |'.rjust(len(item) - 1, '-') for item in header_items[1:]], ) # Write the header @@ -147,23 +154,23 @@ class SummaryReporter: for values in lines_values: # build string with line values - formats.update(dict(Cover="{:>{n}}% |")) + formats.update(dict(Cover='{:>{n}}% |')) line_items = [ - formats[item].format(str(value).replace("_", "\\_"), name_len=max_name, n=max_n-1) + formats[item].format(str(value).replace('_', '\\_'), name_len=max_name, n=max_n - 1) for item, value in zip(header, values) ] self.write_items(line_items) # Write the TOTAL line - formats.update(dict(Name="|{:>{name_len}} |", Cover="{:>{n}} |")) + formats.update(dict(Name='|{:>{name_len}} |', Cover='{:>{n}} |')) total_line_items: list[str] = [] for item, value in zip(header, total_line): - if value == "": + if value == '': insert = value - elif item == "Cover": - insert = f" **{value}%**" + elif item == 'Cover': + insert = f' **{value}%**' else: - insert = f" **{value}**" + insert = f' **{value}**' total_line_items += formats[item].format(insert, name_len=max_name, n=max_n) self.write_items(total_line_items) for end_line in end_lines: @@ -182,9 +189,9 @@ class SummaryReporter: self.report_one_file(fr, analysis) if not self.total.n_files and not self.skipped_count: - raise NoDataError("No data to report.") + raise NoDataError('No data to report.') - if self.output_format == "total": + if self.output_format == 'total': self.write(self.total.pc_covered_str) else: self.tabular_report() @@ -194,12 +201,12 @@ class SummaryReporter: def tabular_report(self) -> None: """Writes tabular report formats.""" # Prepare the header line and column sorting. - header = ["Name", "Stmts", "Miss"] + header = ['Name', 'Stmts', 'Miss'] if self.branches: - header += ["Branch", "BrPart"] - header += ["Cover"] + header += ['Branch', 'BrPart'] + header += ['Cover'] if self.config.show_missing: - header += ["Missing"] + header += ['Missing'] column_order = dict(name=0, stmts=1, miss=2, cover=-1) if self.branches: @@ -221,17 +228,17 @@ class SummaryReporter: lines_values.append(args) # Line sorting. - sort_option = (self.config.sort or "name").lower() + sort_option = (self.config.sort or 'name').lower() reverse = False - if sort_option[0] == "-": + if sort_option[0] == '-': reverse = True sort_option = sort_option[1:] - elif sort_option[0] == "+": + elif sort_option[0] == '+': sort_option = sort_option[1:] sort_idx = column_order.get(sort_option) if sort_idx is None: - raise ConfigError(f"Invalid sorting option: {self.config.sort!r}") - if sort_option == "name": + raise ConfigError(f'Invalid sorting option: {self.config.sort!r}') + if sort_option == 'name': lines_values = human_sorted_items(lines_values, reverse=reverse) else: lines_values.sort( @@ -240,25 +247,25 @@ class SummaryReporter: ) # Calculate total if we had at least one file. - total_line = ["TOTAL", self.total.n_statements, self.total.n_missing] + total_line = ['TOTAL', self.total.n_statements, self.total.n_missing] if self.branches: total_line += [self.total.n_branches, self.total.n_partial_branches] total_line += [self.total.pc_covered_str] if self.config.show_missing: - total_line += [""] + total_line += [''] # Create other final lines. end_lines = [] if self.config.skip_covered and self.skipped_count: - file_suffix = "s" if self.skipped_count>1 else "" + file_suffix = 's' if self.skipped_count > 1 else '' end_lines.append( - f"\n{self.skipped_count} file{file_suffix} skipped due to complete coverage.", + f'\n{self.skipped_count} file{file_suffix} skipped due to complete coverage.', ) if self.config.skip_empty and self.empty_count: - file_suffix = "s" if self.empty_count > 1 else "" - end_lines.append(f"\n{self.empty_count} empty file{file_suffix} skipped.") + file_suffix = 's' if self.empty_count > 1 else '' + end_lines.append(f'\n{self.empty_count} empty file{file_suffix} skipped.') - if self.output_format == "markdown": + if self.output_format == 'markdown': formatter = self._report_markdown else: formatter = self._report_text diff --git a/.venv/lib/python3.10/site-packages/coverage/report_core.py b/.venv/lib/python3.10/site-packages/coverage/report_core.py index f6bb1f3..d369b5e 100644 --- a/.venv/lib/python3.10/site-packages/coverage/report_core.py +++ b/.venv/lib/python3.10/site-packages/coverage/report_core.py @@ -1,19 +1,22 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Reporter foundation for coverage.py.""" - from __future__ import annotations import sys +from typing import Callable +from typing import IO +from typing import Iterable +from typing import Iterator +from typing import Protocol +from typing import TYPE_CHECKING -from typing import ( - Callable, Iterable, Iterator, IO, Protocol, TYPE_CHECKING, -) - -from coverage.exceptions import NoDataError, NotPython -from coverage.files import prep_patterns, GlobMatcher -from coverage.misc import ensure_dir_for_file, file_be_gone +from coverage.exceptions import NoDataError +from coverage.exceptions import NotPython +from coverage.files import GlobMatcher +from coverage.files import prep_patterns +from coverage.misc import ensure_dir_for_file +from coverage.misc import file_be_gone from coverage.plugin import FileReporter from coverage.results import Analysis from coverage.types import TMorf @@ -46,21 +49,21 @@ def render_report( file_to_close = None delete_file = False - if output_path == "-": + if output_path == '-': outfile = sys.stdout else: # Ensure that the output directory is created; done here because this # report pre-opens the output file. HtmlReporter does this on its own # because its task is more complex, being multiple files. ensure_dir_for_file(output_path) - outfile = open(output_path, "w", encoding="utf-8") + outfile = open(output_path, 'w', encoding='utf-8') file_to_close = outfile delete_file = True try: ret = reporter.report(morfs, outfile=outfile) if file_to_close is not None: - msgfn(f"Wrote {reporter.report_type} to {output_path}") + msgfn(f'Wrote {reporter.report_type} to {output_path}') delete_file = False return ret finally: @@ -85,15 +88,15 @@ def get_analysis_to_report( config = coverage.config if config.report_include: - matcher = GlobMatcher(prep_patterns(config.report_include), "report_include") + matcher = GlobMatcher(prep_patterns(config.report_include), 'report_include') file_reporters = [fr for fr in file_reporters if matcher.match(fr.filename)] if config.report_omit: - matcher = GlobMatcher(prep_patterns(config.report_omit), "report_omit") + matcher = GlobMatcher(prep_patterns(config.report_omit), 'report_omit') file_reporters = [fr for fr in file_reporters if not matcher.match(fr.filename)] if not file_reporters: - raise NoDataError("No data to report.") + raise NoDataError('No data to report.') for fr in sorted(file_reporters): try: @@ -106,13 +109,13 @@ def get_analysis_to_report( if fr.should_be_python(): # type: ignore[attr-defined] if config.ignore_errors: msg = f"Couldn't parse Python file '{fr.filename}'" - coverage._warn(msg, slug="couldnt-parse") + coverage._warn(msg, slug='couldnt-parse') else: raise except Exception as exc: if config.ignore_errors: msg = f"Couldn't parse '{fr.filename}': {exc}".rstrip() - coverage._warn(msg, slug="couldnt-parse") + coverage._warn(msg, slug='couldnt-parse') else: raise else: diff --git a/.venv/lib/python3.10/site-packages/coverage/results.py b/.venv/lib/python3.10/site-packages/coverage/results.py index 45cc4f1..8fd60c8 100644 --- a/.venv/lib/python3.10/site-packages/coverage/results.py +++ b/.venv/lib/python3.10/site-packages/coverage/results.py @@ -1,18 +1,18 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Results of coverage measurement.""" - from __future__ import annotations import collections - -from typing import Callable, Iterable, TYPE_CHECKING +from typing import Callable +from typing import Iterable +from typing import TYPE_CHECKING from coverage.debug import auto_repr from coverage.exceptions import ConfigError from coverage.misc import nice_pair -from coverage.types import TArc, TLineNo +from coverage.types import TArc +from coverage.types import TLineNo if TYPE_CHECKING: from coverage.data import CoverageData @@ -48,8 +48,8 @@ class Analysis: self.no_branch = self.file_reporter.no_branch_lines() n_branches = self._total_branches() mba = self.missing_branch_arcs() - n_partial_branches = sum(len(v) for k,v in mba.items() if k not in self.missing) - n_missing_branches = sum(len(v) for k,v in mba.items()) + n_partial_branches = sum(len(v) for k, v in mba.items() if k not in self.missing) + n_missing_branches = sum(len(v) for k, v in mba.items()) else: self._arc_possibilities = [] self.exit_counts = {} @@ -103,9 +103,9 @@ class Analysis: executed = self.arcs_executed() missing = ( p for p in possible - if p not in executed - and p[0] not in self.no_branch - and p[1] not in self.excluded + if p not in executed and + p[0] not in self.no_branch and + p[1] not in self.excluded ) return sorted(missing) @@ -120,15 +120,15 @@ class Analysis: # make sure we have at least one positive value. unpredicted = ( e for e in executed - if e not in possible - and e[0] != e[1] - and (e[0] > 0 or e[1] > 0) + if e not in possible and + e[0] != e[1] and + (e[0] > 0 or e[1] > 0) ) return sorted(unpredicted) def _branch_lines(self) -> list[TLineNo]: """Returns a list of line numbers that have more than one exit.""" - return [l1 for l1,count in self.exit_counts.items() if count > 1] + return [l1 for l1, count in self.exit_counts.items() if count > 1] def _total_branches(self) -> int: """How many total branches are there?""" @@ -264,7 +264,7 @@ class Numbers: pc = self._near100 else: pc = round(pc, self._precision) - return "%.*f" % (self._precision, pc) + return '%.*f' % (self._precision, pc) def pc_str_width(self) -> int: """How many characters wide can pc_covered_str be?""" @@ -356,10 +356,10 @@ def format_lines( for line, exits in line_exits: for ex in sorted(exits): if line not in lines and ex not in lines: - dest = (ex if ex > 0 else "exit") - line_items.append((line, f"{line}->{dest}")) + dest = (ex if ex > 0 else 'exit') + line_items.append((line, f'{line}->{dest}')) - ret = ", ".join(t[-1] for t in sorted(line_items)) + ret = ', '.join(t[-1] for t in sorted(line_items)) return ret @@ -375,7 +375,7 @@ def should_fail_under(total: float, fail_under: float, precision: int) -> bool: """ # We can never achieve higher than 100% coverage, or less than zero. if not (0 <= fail_under <= 100.0): - msg = f"fail_under={fail_under} is invalid. Must be between 0 and 100." + msg = f'fail_under={fail_under} is invalid. Must be between 0 and 100.' raise ConfigError(msg) # Special case for fail_under=100, it must really be 100. diff --git a/.venv/lib/python3.10/site-packages/coverage/sqldata.py b/.venv/lib/python3.10/site-packages/coverage/sqldata.py index f12ccd7..abb6360 100644 --- a/.venv/lib/python3.10/site-packages/coverage/sqldata.py +++ b/.venv/lib/python3.10/site-packages/coverage/sqldata.py @@ -1,8 +1,6 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """SQLite coverage data.""" - from __future__ import annotations import collections @@ -19,19 +17,29 @@ import sys import textwrap import threading import zlib +from typing import Any +from typing import cast +from typing import Collection +from typing import Mapping +from typing import Sequence -from typing import ( - cast, Any, Collection, Mapping, - Sequence, -) - -from coverage.debug import NoDebugging, auto_repr -from coverage.exceptions import CoverageException, DataError +from coverage.debug import auto_repr +from coverage.debug import NoDebugging +from coverage.exceptions import CoverageException +from coverage.exceptions import DataError from coverage.files import PathAliases -from coverage.misc import file_be_gone, isolate_module -from coverage.numbits import numbits_to_nums, numbits_union, nums_to_numbits +from coverage.misc import file_be_gone +from coverage.misc import isolate_module +from coverage.numbits import numbits_to_nums +from coverage.numbits import numbits_union +from coverage.numbits import nums_to_numbits from coverage.sqlitedb import SqliteDb -from coverage.types import AnyCallable, FilePath, TArc, TDebugCtl, TLineNo, TWarnFn +from coverage.types import AnyCallable +from coverage.types import FilePath +from coverage.types import TArc +from coverage.types import TDebugCtl +from coverage.types import TLineNo +from coverage.types import TWarnFn from coverage.version import __version__ os = isolate_module(os) @@ -112,15 +120,16 @@ CREATE TABLE tracer ( ); """ + def _locked(method: AnyCallable) -> AnyCallable: """A decorator for methods that should hold self._lock.""" @functools.wraps(method) def _wrapped(self: CoverageData, *args: Any, **kwargs: Any) -> Any: - if self._debug.should("lock"): - self._debug.write(f"Locking {self._lock!r} for {method.__name__}") + if self._debug.should('lock'): + self._debug.write(f'Locking {self._lock!r} for {method.__name__}') with self._lock: - if self._debug.should("lock"): - self._debug.write(f"Locked {self._lock!r} for {method.__name__}") + if self._debug.should('lock'): + self._debug.write(f'Locked {self._lock!r} for {method.__name__}') return method(self, *args, **kwargs) return _wrapped @@ -233,7 +242,7 @@ class CoverageData: """ self._no_disk = no_disk - self._basename = os.path.abspath(basename or ".coverage") + self._basename = os.path.abspath(basename or '.coverage') self._suffix = suffix self._warn = warn self._debug = debug or NoDebugging() @@ -262,12 +271,12 @@ class CoverageData: def _choose_filename(self) -> None: """Set self._filename based on inited attributes.""" if self._no_disk: - self._filename = ":memory:" + self._filename = ':memory:' else: self._filename = self._basename suffix = filename_suffix(self._suffix) if suffix: - self._filename += "." + suffix + self._filename += '.' + suffix def _reset(self) -> None: """Reset our attributes.""" @@ -281,8 +290,8 @@ class CoverageData: def _open_db(self) -> None: """Open an existing db file, and read its metadata.""" - if self._debug.should("dataio"): - self._debug.write(f"Opening data file {self._filename!r}") + if self._debug.should('dataio'): + self._debug.write(f'Opening data file {self._filename!r}') self._dbs[threading.get_ident()] = SqliteDb(self._filename, self._debug) self._read_db() @@ -290,10 +299,10 @@ class CoverageData: """Read the metadata from a database so that we are ready to use it.""" with self._dbs[threading.get_ident()] as db: try: - row = db.execute_one("select version from coverage_schema") + row = db.execute_one('select version from coverage_schema') assert row is not None except Exception as exc: - if "no such table: coverage_schema" in str(exc): + if 'no such table: coverage_schema' in str(exc): self._init_db(db) else: raise DataError( @@ -315,28 +324,28 @@ class CoverageData: self._has_arcs = bool(int(row[0])) self._has_lines = not self._has_arcs - with db.execute("select id, path from file") as cur: + with db.execute('select id, path from file') as cur: for file_id, path in cur: self._file_map[path] = file_id def _init_db(self, db: SqliteDb) -> None: """Write the initial contents of the database.""" - if self._debug.should("dataio"): - self._debug.write(f"Initing data file {self._filename!r}") + if self._debug.should('dataio'): + self._debug.write(f'Initing data file {self._filename!r}') db.executescript(SCHEMA) - db.execute_void("insert into coverage_schema (version) values (?)", (SCHEMA_VERSION,)) + db.execute_void('insert into coverage_schema (version) values (?)', (SCHEMA_VERSION,)) # When writing metadata, avoid information that will needlessly change # the hash of the data file, unless we're debugging processes. meta_data = [ - ("version", __version__), + ('version', __version__), ] - if self._debug.should("process"): + if self._debug.should('process'): meta_data.extend([ - ("sys_argv", str(getattr(sys, "argv", None))), - ("when", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")), + ('sys_argv', str(getattr(sys, 'argv', None))), + ('when', datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')), ]) - db.executemany_void("insert or ignore into meta (key, value) values (?, ?)", meta_data) + db.executemany_void('insert or ignore into meta (key, value) values (?, ?)', meta_data) def _connect(self) -> SqliteDb: """Get the SqliteDb object to use.""" @@ -349,7 +358,7 @@ class CoverageData: return False try: with self._connect() as con: - with con.execute("select * from file limit 1") as cur: + with con.execute('select * from file limit 1') as cur: return bool(list(cur)) except CoverageException: return False @@ -371,11 +380,11 @@ class CoverageData: .. versionadded:: 5.0 """ - if self._debug.should("dataio"): - self._debug.write(f"Dumping data from data file {self._filename!r}") + if self._debug.should('dataio'): + self._debug.write(f'Dumping data from data file {self._filename!r}') with self._connect() as con: script = con.dump() - return b"z" + zlib.compress(script.encode("utf-8")) + return b'z' + zlib.compress(script.encode('utf-8')) def loads(self, data: bytes) -> None: """Deserialize data from :meth:`dumps`. @@ -392,13 +401,13 @@ class CoverageData: .. versionadded:: 5.0 """ - if self._debug.should("dataio"): - self._debug.write(f"Loading data into data file {self._filename!r}") - if data[:1] != b"z": + if self._debug.should('dataio'): + self._debug.write(f'Loading data into data file {self._filename!r}') + if data[:1] != b'z': raise DataError( - f"Unrecognized serialization: {data[:40]!r} (head of {len(data)} bytes)", + f'Unrecognized serialization: {data[:40]!r} (head of {len(data)} bytes)', ) - script = zlib.decompress(data[1:]).decode("utf-8") + script = zlib.decompress(data[1:]).decode('utf-8') self._dbs[threading.get_ident()] = db = SqliteDb(self._filename, self._debug) with db: db.executescript(script) @@ -415,7 +424,7 @@ class CoverageData: if add: with self._connect() as con: self._file_map[filename] = con.execute_for_rowid( - "insert or replace into file (path) values (?)", + 'insert or replace into file (path) values (?)', (filename,), ) return self._file_map.get(filename) @@ -425,7 +434,7 @@ class CoverageData: assert context is not None self._start_using() with self._connect() as con: - row = con.execute_one("select id from context where context = ?", (context,)) + row = con.execute_one('select id from context where context = ?', (context,)) if row is not None: return cast(int, row[0]) else: @@ -441,21 +450,21 @@ class CoverageData: .. versionadded:: 5.0 """ - if self._debug.should("dataop"): - self._debug.write(f"Setting coverage context: {context!r}") + if self._debug.should('dataop'): + self._debug.write(f'Setting coverage context: {context!r}') self._current_context = context self._current_context_id = None def _set_context_id(self) -> None: """Use the _current_context to set _current_context_id.""" - context = self._current_context or "" + context = self._current_context or '' context_id = self._context_id(context) if context_id is not None: self._current_context_id = context_id else: with self._connect() as con: self._current_context_id = con.execute_for_rowid( - "insert into context (context) values (?)", + 'insert into context (context) values (?)', (context,), ) @@ -484,13 +493,15 @@ class CoverageData: { filename: { line1, line2, ... }, ...} """ - if self._debug.should("dataop"): - self._debug.write("Adding lines: %d files, %d lines total" % ( - len(line_data), sum(len(lines) for lines in line_data.values()), - )) - if self._debug.should("dataop2"): + if self._debug.should('dataop'): + self._debug.write( + 'Adding lines: %d files, %d lines total' % ( + len(line_data), sum(len(lines) for lines in line_data.values()), + ), + ) + if self._debug.should('dataop2'): for filename, linenos in sorted(line_data.items()): - self._debug.write(f" {filename}: {linenos}") + self._debug.write(f' {filename}: {linenos}') self._start_using() self._choose_lines_or_arcs(lines=True) if not line_data: @@ -500,15 +511,15 @@ class CoverageData: for filename, linenos in line_data.items(): linemap = nums_to_numbits(linenos) file_id = self._file_id(filename, add=True) - query = "select numbits from line_bits where file_id = ? and context_id = ?" + query = 'select numbits from line_bits where file_id = ? and context_id = ?' with con.execute(query, (file_id, self._current_context_id)) as cur: existing = list(cur) if existing: linemap = numbits_union(linemap, existing[0][0]) con.execute_void( - "insert or replace into line_bits " + - " (file_id, context_id, numbits) values (?, ?, ?)", + 'insert or replace into line_bits ' + + ' (file_id, context_id, numbits) values (?, ?, ?)', (file_id, self._current_context_id, linemap), ) @@ -522,13 +533,15 @@ class CoverageData: { filename: { (l1,l2), (l1,l2), ... }, ...} """ - if self._debug.should("dataop"): - self._debug.write("Adding arcs: %d files, %d arcs total" % ( - len(arc_data), sum(len(arcs) for arcs in arc_data.values()), - )) - if self._debug.should("dataop2"): + if self._debug.should('dataop'): + self._debug.write( + 'Adding arcs: %d files, %d arcs total' % ( + len(arc_data), sum(len(arcs) for arcs in arc_data.values()), + ), + ) + if self._debug.should('dataop2'): for filename, arcs in sorted(arc_data.items()): - self._debug.write(f" {filename}: {arcs}") + self._debug.write(f' {filename}: {arcs}') self._start_using() self._choose_lines_or_arcs(arcs=True) if not arc_data: @@ -541,8 +554,8 @@ class CoverageData: file_id = self._file_id(filename, add=True) data = [(file_id, self._current_context_id, fromno, tono) for fromno, tono in arcs] con.executemany_void( - "insert or ignore into arc " + - "(file_id, context_id, fromno, tono) values (?, ?, ?, ?)", + 'insert or ignore into arc ' + + '(file_id, context_id, fromno, tono) values (?, ?, ?, ?)', data, ) @@ -551,11 +564,11 @@ class CoverageData: assert lines or arcs assert not (lines and arcs) if lines and self._has_arcs: - if self._debug.should("dataop"): + if self._debug.should('dataop'): self._debug.write("Error: Can't add line measurements to existing branch data") raise DataError("Can't add line measurements to existing branch data") if arcs and self._has_lines: - if self._debug.should("dataop"): + if self._debug.should('dataop'): self._debug.write("Error: Can't add branch measurements to existing line data") raise DataError("Can't add branch measurements to existing line data") if not self._has_arcs and not self._has_lines: @@ -563,8 +576,8 @@ class CoverageData: self._has_arcs = arcs with self._connect() as con: con.execute_void( - "insert or ignore into meta (key, value) values (?, ?)", - ("has_arcs", str(int(arcs))), + 'insert or ignore into meta (key, value) values (?, ?)', + ('has_arcs', str(int(arcs))), ) @_locked @@ -574,8 +587,8 @@ class CoverageData: `file_tracers` is { filename: plugin_name, ... } """ - if self._debug.should("dataop"): - self._debug.write("Adding file tracers: %d files" % (len(file_tracers),)) + if self._debug.should('dataop'): + self._debug.write('Adding file tracers: %d files' % (len(file_tracers),)) if not file_tracers: return self._start_using() @@ -592,11 +605,11 @@ class CoverageData: ) elif plugin_name: con.execute_void( - "insert into tracer (file_id, tracer) values (?, ?)", + 'insert into tracer (file_id, tracer) values (?, ?)', (file_id, plugin_name), ) - def touch_file(self, filename: str, plugin_name: str = "") -> None: + def touch_file(self, filename: str, plugin_name: str = '') -> None: """Ensure that `filename` appears in the data, empty if needed. `plugin_name` is the name of the plugin responsible for this file. @@ -610,10 +623,10 @@ class CoverageData: `plugin_name` is the name of the plugin responsible for these files. It is used to associate the right filereporter, etc. """ - if self._debug.should("dataop"): - self._debug.write(f"Touching {filenames!r}") + if self._debug.should('dataop'): + self._debug.write(f'Touching {filenames!r}') self._start_using() - with self._connect(): # Use this to get one transaction. + with self._connect(): # Use this to get one transaction. if not self._has_arcs and not self._has_lines: raise DataError("Can't touch files in an empty CoverageData") @@ -629,15 +642,15 @@ class CoverageData: .. versionadded:: 7.2 """ - if self._debug.should("dataop"): - self._debug.write(f"Purging data for {filenames!r}") + if self._debug.should('dataop'): + self._debug.write(f'Purging data for {filenames!r}') self._start_using() with self._connect() as con: if self._has_lines: - sql = "delete from line_bits where file_id=?" + sql = 'delete from line_bits where file_id=?' elif self._has_arcs: - sql = "delete from arc where file_id=?" + sql = 'delete from arc where file_id=?' else: raise DataError("Can't purge files in an empty CoverageData") @@ -655,10 +668,12 @@ class CoverageData: only when called directly from the test suite. """ - if self._debug.should("dataop"): - self._debug.write("Updating with data from {!r}".format( - getattr(other_data, "_filename", "???"), - )) + if self._debug.should('dataop'): + self._debug.write( + 'Updating with data from {!r}'.format( + getattr(other_data, '_filename', '???'), + ), + ) if self._has_lines and other_data._has_arcs: raise DataError("Can't combine arc data with line data") if self._has_arcs and other_data._has_lines: @@ -673,19 +688,19 @@ class CoverageData: other_data.read() with other_data._connect() as con: # Get files data. - with con.execute("select path from file") as cur: + with con.execute('select path from file') as cur: files = {path: aliases.map(path) for (path,) in cur} # Get contexts data. - with con.execute("select context from context") as cur: + with con.execute('select context from context') as cur: contexts = [context for (context,) in cur] # Get arc data. with con.execute( - "select file.path, context.context, arc.fromno, arc.tono " + - "from arc " + - "inner join file on file.id = arc.file_id " + - "inner join context on context.id = arc.context_id", + 'select file.path, context.context, arc.fromno, arc.tono ' + + 'from arc ' + + 'inner join file on file.id = arc.file_id ' + + 'inner join context on context.id = arc.context_id', ) as cur: arcs = [ (files[path], context, fromno, tono) @@ -694,10 +709,10 @@ class CoverageData: # Get line data. with con.execute( - "select file.path, context.context, line_bits.numbits " + - "from line_bits " + - "inner join file on file.id = line_bits.file_id " + - "inner join context on context.id = line_bits.context_id", + 'select file.path, context.context, line_bits.numbits ' + + 'from line_bits ' + + 'inner join file on file.id = line_bits.file_id ' + + 'inner join context on context.id = line_bits.context_id', ) as cur: lines: dict[tuple[str, str], bytes] = {} for path, context, numbits in cur: @@ -708,25 +723,25 @@ class CoverageData: # Get tracer data. with con.execute( - "select file.path, tracer " + - "from tracer " + - "inner join file on file.id = tracer.file_id", + 'select file.path, tracer ' + + 'from tracer ' + + 'inner join file on file.id = tracer.file_id', ) as cur: tracers = {files[path]: tracer for (path, tracer) in cur} with self._connect() as con: assert con.con is not None - con.con.isolation_level = "IMMEDIATE" + con.con.isolation_level = 'IMMEDIATE' # Get all tracers in the DB. Files not in the tracers are assumed # to have an empty string tracer. Since Sqlite does not support # full outer joins, we have to make two queries to fill the # dictionary. - with con.execute("select path from file") as cur: - this_tracers = {path: "" for path, in cur} + with con.execute('select path from file') as cur: + this_tracers = {path: '' for path, in cur} with con.execute( - "select file.path, tracer from tracer " + - "inner join file on file.id = tracer.file_id", + 'select file.path, tracer from tracer ' + + 'inner join file on file.id = tracer.file_id', ) as cur: this_tracers.update({ aliases.map(path): tracer @@ -735,17 +750,17 @@ class CoverageData: # Create all file and context rows in the DB. con.executemany_void( - "insert or ignore into file (path) values (?)", + 'insert or ignore into file (path) values (?)', ((file,) for file in files.values()), ) - with con.execute("select id, path from file") as cur: + with con.execute('select id, path from file') as cur: file_ids = {path: id for id, path in cur} self._file_map.update(file_ids) con.executemany_void( - "insert or ignore into context (context) values (?)", + 'insert or ignore into context (context) values (?)', ((context,) for context in contexts), ) - with con.execute("select id, context from context") as cur: + with con.execute('select id, context from context') as cur: context_ids = {context: id for id, context in cur} # Prepare tracers and fail, if a conflict is found. @@ -754,7 +769,7 @@ class CoverageData: tracer_map = {} for path in files.values(): this_tracer = this_tracers.get(path) - other_tracer = tracers.get(path, "") + other_tracer = tracers.get(path, '') # If there is no tracer, there is always the None tracer. if this_tracer is not None and this_tracer != other_tracer: raise DataError( @@ -774,10 +789,10 @@ class CoverageData: # Get line data. with con.execute( - "select file.path, context.context, line_bits.numbits " + - "from line_bits " + - "inner join file on file.id = line_bits.file_id " + - "inner join context on context.id = line_bits.context_id", + 'select file.path, context.context, line_bits.numbits ' + + 'from line_bits ' + + 'inner join file on file.id = line_bits.file_id ' + + 'inner join context on context.id = line_bits.context_id', ) as cur: for path, context, numbits in cur: key = (aliases.map(path), context) @@ -790,24 +805,24 @@ class CoverageData: # Write the combined data. con.executemany_void( - "insert or ignore into arc " + - "(file_id, context_id, fromno, tono) values (?, ?, ?, ?)", + 'insert or ignore into arc ' + + '(file_id, context_id, fromno, tono) values (?, ?, ?, ?)', arc_rows, ) if lines: self._choose_lines_or_arcs(lines=True) - con.execute_void("delete from line_bits") + con.execute_void('delete from line_bits') con.executemany_void( - "insert into line_bits " + - "(file_id, context_id, numbits) values (?, ?, ?)", + 'insert into line_bits ' + + '(file_id, context_id, numbits) values (?, ?, ?)', [ (file_ids[file], context_ids[context], numbits) for (file, context), numbits in lines.items() ], ) con.executemany_void( - "insert or ignore into tracer (file_id, tracer) values (?, ?)", + 'insert or ignore into tracer (file_id, tracer) values (?, ?)', ((file_ids[filename], tracer) for filename, tracer in tracer_map.items()), ) @@ -826,16 +841,16 @@ class CoverageData: self._reset() if self._no_disk: return - if self._debug.should("dataio"): - self._debug.write(f"Erasing data file {self._filename!r}") + if self._debug.should('dataio'): + self._debug.write(f'Erasing data file {self._filename!r}') file_be_gone(self._filename) if parallel: data_dir, local = os.path.split(self._filename) local_abs_path = os.path.join(os.path.abspath(data_dir), local) - pattern = glob.escape(local_abs_path) + ".*" + pattern = glob.escape(local_abs_path) + '.*' for filename in glob.glob(pattern): - if self._debug.should("dataio"): - self._debug.write(f"Erasing parallel data file {filename!r}") + if self._debug.should('dataio'): + self._debug.write(f'Erasing parallel data file {filename!r}') file_be_gone(filename) def read(self) -> None: @@ -880,7 +895,7 @@ class CoverageData: """ self._start_using() with self._connect() as con: - with con.execute("select distinct(context) from context") as cur: + with con.execute('select distinct(context) from context') as cur: contexts = {row[0] for row in cur} return contexts @@ -897,10 +912,10 @@ class CoverageData: file_id = self._file_id(filename) if file_id is None: return None - row = con.execute_one("select tracer from tracer where file_id = ?", (file_id,)) + row = con.execute_one('select tracer from tracer where file_id = ?', (file_id,)) if row is not None: - return row[0] or "" - return "" # File was measured, but no tracer associated. + return row[0] or '' + return '' # File was measured, but no tracer associated. def set_query_context(self, context: str) -> None: """Set a context for subsequent querying. @@ -915,7 +930,7 @@ class CoverageData: """ self._start_using() with self._connect() as con: - with con.execute("select id from context where context = ?", (context,)) as cur: + with con.execute('select id from context where context = ?', (context,)) as cur: self._query_context_ids = [row[0] for row in cur.fetchall()] def set_query_contexts(self, contexts: Sequence[str] | None) -> None: @@ -933,8 +948,8 @@ class CoverageData: self._start_using() if contexts: with self._connect() as con: - context_clause = " or ".join(["context regexp ?"] * len(contexts)) - with con.execute("select id from context where " + context_clause, contexts) as cur: + context_clause = ' or '.join(['context regexp ?'] * len(contexts)) + with con.execute('select id from context where ' + context_clause, contexts) as cur: self._query_context_ids = [row[0] for row in cur.fetchall()] else: self._query_context_ids = None @@ -961,11 +976,11 @@ class CoverageData: if file_id is None: return None else: - query = "select numbits from line_bits where file_id = ?" + query = 'select numbits from line_bits where file_id = ?' data = [file_id] if self._query_context_ids is not None: - ids_array = ", ".join("?" * len(self._query_context_ids)) - query += " and context_id in (" + ids_array + ")" + ids_array = ', '.join('?' * len(self._query_context_ids)) + query += ' and context_id in (' + ids_array + ')' data += self._query_context_ids with con.execute(query, data) as cur: bitmaps = list(cur) @@ -997,11 +1012,11 @@ class CoverageData: if file_id is None: return None else: - query = "select distinct fromno, tono from arc where file_id = ?" + query = 'select distinct fromno, tono from arc where file_id = ?' data = [file_id] if self._query_context_ids is not None: - ids_array = ", ".join("?" * len(self._query_context_ids)) - query += " and context_id in (" + ids_array + ")" + ids_array = ', '.join('?' * len(self._query_context_ids)) + query += ' and context_id in (' + ids_array + ')' data += self._query_context_ids with con.execute(query, data) as cur: return list(cur) @@ -1024,14 +1039,14 @@ class CoverageData: lineno_contexts_map = collections.defaultdict(set) if self.has_arcs(): query = ( - "select arc.fromno, arc.tono, context.context " + - "from arc, context " + - "where arc.file_id = ? and arc.context_id = context.id" + 'select arc.fromno, arc.tono, context.context ' + + 'from arc, context ' + + 'where arc.file_id = ? and arc.context_id = context.id' ) data = [file_id] if self._query_context_ids is not None: - ids_array = ", ".join("?" * len(self._query_context_ids)) - query += " and arc.context_id in (" + ids_array + ")" + ids_array = ', '.join('?' * len(self._query_context_ids)) + query += ' and arc.context_id in (' + ids_array + ')' data += self._query_context_ids with con.execute(query, data) as cur: for fromno, tono, context in cur: @@ -1041,14 +1056,14 @@ class CoverageData: lineno_contexts_map[tono].add(context) else: query = ( - "select l.numbits, c.context from line_bits l, context c " + - "where l.context_id = c.id " + - "and file_id = ?" + 'select l.numbits, c.context from line_bits l, context c ' + + 'where l.context_id = c.id ' + + 'and file_id = ?' ) data = [file_id] if self._query_context_ids is not None: - ids_array = ", ".join("?" * len(self._query_context_ids)) - query += " and l.context_id in (" + ids_array + ")" + ids_array = ', '.join('?' * len(self._query_context_ids)) + query += ' and l.context_id in (' + ids_array + ')' data += self._query_context_ids with con.execute(query, data) as cur: for numbits, context in cur: @@ -1064,17 +1079,17 @@ class CoverageData: Returns a list of (key, value) pairs. """ - with SqliteDb(":memory:", debug=NoDebugging()) as db: - with db.execute("pragma temp_store") as cur: + with SqliteDb(':memory:', debug=NoDebugging()) as db: + with db.execute('pragma temp_store') as cur: temp_store = [row[0] for row in cur] - with db.execute("pragma compile_options") as cur: + with db.execute('pragma compile_options') as cur: copts = [row[0] for row in cur] - copts = textwrap.wrap(", ".join(copts), width=75) + copts = textwrap.wrap(', '.join(copts), width=75) return [ - ("sqlite3_sqlite_version", sqlite3.sqlite_version), - ("sqlite3_temp_store", temp_store), - ("sqlite3_compile_options", copts), + ('sqlite3_sqlite_version', sqlite3.sqlite_version), + ('sqlite3_temp_store', temp_store), + ('sqlite3_compile_options', copts), ] @@ -1095,8 +1110,8 @@ def filename_suffix(suffix: str | bool | None) -> str | None: # if the process forks. die = random.Random(os.urandom(8)) letters = string.ascii_uppercase + string.ascii_lowercase - rolls = "".join(die.choice(letters) for _ in range(6)) - suffix = f"{socket.gethostname()}.{os.getpid()}.X{rolls}x" + rolls = ''.join(die.choice(letters) for _ in range(6)) + suffix = f'{socket.gethostname()}.{os.getpid()}.X{rolls}x' elif suffix is False: suffix = None return suffix diff --git a/.venv/lib/python3.10/site-packages/coverage/sqlitedb.py b/.venv/lib/python3.10/site-packages/coverage/sqlitedb.py index 0a3e837..f1bea48 100644 --- a/.venv/lib/python3.10/site-packages/coverage/sqlitedb.py +++ b/.venv/lib/python3.10/site-packages/coverage/sqlitedb.py @@ -1,17 +1,20 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """SQLite abstraction for coverage.py""" - from __future__ import annotations import contextlib import re import sqlite3 +from typing import Any +from typing import cast +from typing import Iterable +from typing import Iterator +from typing import Tuple -from typing import cast, Any, Iterable, Iterator, Tuple - -from coverage.debug import auto_repr, clipped_repr, exc_one_line +from coverage.debug import auto_repr +from coverage.debug import clipped_repr +from coverage.debug import exc_one_line from coverage.exceptions import DataError from coverage.types import TDebugCtl @@ -28,6 +31,7 @@ class SqliteDb: etc(a, b) """ + def __init__(self, filename: str, debug: TDebugCtl) -> None: self.debug = debug self.filename = filename @@ -46,40 +50,40 @@ class SqliteDb: # effectively causing a nested context. However, given the idempotent # nature of the tracer operations, sharing a connection among threads # is not a problem. - if self.debug.should("sql"): - self.debug.write(f"Connecting to {self.filename!r}") + if self.debug.should('sql'): + self.debug.write(f'Connecting to {self.filename!r}') try: self.con = sqlite3.connect(self.filename, check_same_thread=False) except sqlite3.Error as exc: raise DataError(f"Couldn't use data file {self.filename!r}: {exc}") from exc - if self.debug.should("sql"): - self.debug.write(f"Connected to {self.filename!r} as {self.con!r}") + if self.debug.should('sql'): + self.debug.write(f'Connected to {self.filename!r} as {self.con!r}') - self.con.create_function("REGEXP", 2, lambda txt, pat: re.search(txt, pat) is not None) + self.con.create_function('REGEXP', 2, lambda txt, pat: re.search(txt, pat) is not None) # Turning off journal_mode can speed up writing. It can't always be # disabled, so we have to be prepared for *-journal files elsewhere. # In Python 3.12+, we can change the config to allow journal_mode=off. - if hasattr(sqlite3, "SQLITE_DBCONFIG_DEFENSIVE"): + if hasattr(sqlite3, 'SQLITE_DBCONFIG_DEFENSIVE'): # Turn off defensive mode, so that journal_mode=off can succeed. self.con.setconfig( # type: ignore[attr-defined, unused-ignore] sqlite3.SQLITE_DBCONFIG_DEFENSIVE, False, ) # This pragma makes writing faster. It disables rollbacks, but we never need them. - self.execute_void("pragma journal_mode=off") + self.execute_void('pragma journal_mode=off') # This pragma makes writing faster. It can fail in unusual situations # (https://github.com/nedbat/coveragepy/issues/1646), so use fail_ok=True # to keep things going. - self.execute_void("pragma synchronous=off", fail_ok=True) + self.execute_void('pragma synchronous=off', fail_ok=True) def close(self) -> None: """If needed, close the connection.""" - if self.con is not None and self.filename != ":memory:": - if self.debug.should("sql"): - self.debug.write(f"Closing {self.con!r} on {self.filename!r}") + if self.con is not None and self.filename != ':memory:': + if self.debug.should('sql'): + self.debug.write(f'Closing {self.con!r} on {self.filename!r}') self.con.close() self.con = None @@ -99,15 +103,15 @@ class SqliteDb: self.con.__exit__(exc_type, exc_value, traceback) self.close() except Exception as exc: - if self.debug.should("sql"): - self.debug.write(f"EXCEPTION from __exit__: {exc_one_line(exc)}") + if self.debug.should('sql'): + self.debug.write(f'EXCEPTION from __exit__: {exc_one_line(exc)}') raise DataError(f"Couldn't end data file {self.filename!r}: {exc}") from exc def _execute(self, sql: str, parameters: Iterable[Any]) -> sqlite3.Cursor: """Same as :meth:`python:sqlite3.Connection.execute`.""" - if self.debug.should("sql"): - tail = f" with {parameters!r}" if parameters else "" - self.debug.write(f"Executing {sql!r}{tail}") + if self.debug.should('sql'): + tail = f' with {parameters!r}' if parameters else '' + self.debug.write(f'Executing {sql!r}{tail}') try: assert self.con is not None try: @@ -119,21 +123,21 @@ class SqliteDb: return self.con.execute(sql, parameters) # type: ignore[arg-type] except sqlite3.Error as exc: msg = str(exc) - if self.filename != ":memory:": + if self.filename != ':memory:': try: # `execute` is the first thing we do with the database, so try # hard to provide useful hints if something goes wrong now. - with open(self.filename, "rb") as bad_file: - cov4_sig = b"!coverage.py: This is a private format" + with open(self.filename, 'rb') as bad_file: + cov4_sig = b'!coverage.py: This is a private format' if bad_file.read(len(cov4_sig)) == cov4_sig: msg = ( - "Looks like a coverage 4.x data file. " + - "Are you mixing versions of coverage?" + 'Looks like a coverage 4.x data file. ' + + 'Are you mixing versions of coverage?' ) except Exception: pass - if self.debug.should("sql"): - self.debug.write(f"EXCEPTION from execute: {exc_one_line(exc)}") + if self.debug.should('sql'): + self.debug.write(f'EXCEPTION from execute: {exc_one_line(exc)}') raise DataError(f"Couldn't use data file {self.filename!r}: {msg}") from exc @contextlib.contextmanager @@ -170,8 +174,8 @@ class SqliteDb: with self.execute(sql, parameters) as cur: assert cur.lastrowid is not None rowid: int = cur.lastrowid - if self.debug.should("sqldata"): - self.debug.write(f"Row id result: {rowid!r}") + if self.debug.should('sqldata'): + self.debug.write(f'Row id result: {rowid!r}') return rowid def execute_one(self, sql: str, parameters: Iterable[Any] = ()) -> tuple[Any, ...] | None: @@ -194,12 +198,12 @@ class SqliteDb: def _executemany(self, sql: str, data: list[Any]) -> sqlite3.Cursor: """Same as :meth:`python:sqlite3.Connection.executemany`.""" - if self.debug.should("sql"): - final = ":" if self.debug.should("sqldata") else "" - self.debug.write(f"Executing many {sql!r} with {len(data)} rows{final}") - if self.debug.should("sqldata"): + if self.debug.should('sql'): + final = ':' if self.debug.should('sqldata') else '' + self.debug.write(f'Executing many {sql!r} with {len(data)} rows{final}') + if self.debug.should('sqldata'): for i, row in enumerate(data): - self.debug.write(f"{i:4d}: {row!r}") + self.debug.write(f'{i:4d}: {row!r}') assert self.con is not None try: return self.con.executemany(sql, data) @@ -217,14 +221,16 @@ class SqliteDb: def executescript(self, script: str) -> None: """Same as :meth:`python:sqlite3.Connection.executescript`.""" - if self.debug.should("sql"): - self.debug.write("Executing script with {} chars: {}".format( - len(script), clipped_repr(script, 100), - )) + if self.debug.should('sql'): + self.debug.write( + 'Executing script with {} chars: {}'.format( + len(script), clipped_repr(script, 100), + ), + ) assert self.con is not None self.con.executescript(script).close() def dump(self) -> str: """Return a multi-line string, the SQL dump of the database.""" assert self.con is not None - return "\n".join(self.con.iterdump()) + return '\n'.join(self.con.iterdump()) diff --git a/.venv/lib/python3.10/site-packages/coverage/sysmon.py b/.venv/lib/python3.10/site-packages/coverage/sysmon.py index 65c5b6e..5d421df 100644 --- a/.venv/lib/python3.10/site-packages/coverage/sysmon.py +++ b/.venv/lib/python3.10/site-packages/coverage/sysmon.py @@ -1,39 +1,33 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """Callback functions and support for sys.monitoring data collection.""" - from __future__ import annotations import functools import inspect -import os import os.path import sys import threading import traceback - from dataclasses import dataclass -from types import CodeType, FrameType -from typing import ( - Any, - Callable, - Set, - TYPE_CHECKING, - cast, -) +from types import CodeType +from types import FrameType +from typing import Any +from typing import Callable +from typing import cast +from typing import Set +from typing import TYPE_CHECKING -from coverage.debug import short_filename, short_stack -from coverage.types import ( - AnyCallable, - TArc, - TFileDisposition, - TLineNo, - TTraceData, - TTraceFileData, - TracerCore, - TWarnFn, -) +from coverage.debug import short_filename +from coverage.debug import short_stack +from coverage.types import AnyCallable +from coverage.types import TArc +from coverage.types import TFileDisposition +from coverage.types import TLineNo +from coverage.types import TracerCore +from coverage.types import TTraceData +from coverage.types import TTraceFileData +from coverage.types import TWarnFn # pylint: disable=unused-argument @@ -41,7 +35,7 @@ LOG = False # This module will be imported in all versions of Python, but only used in 3.12+ # It will be type-checked for 3.12, but not for earlier versions. -sys_monitoring = getattr(sys, "monitoring", None) +sys_monitoring = getattr(sys, 'monitoring', None) if TYPE_CHECKING: assert sys_monitoring is not None @@ -61,12 +55,12 @@ if LOG: # pragma: debugging def __getattr__(self, name: str) -> Callable[..., Any]: def _wrapped(*args: Any, **kwargs: Any) -> Any: - log(f"{self.namespace}.{name}{args}{kwargs}") + log(f'{self.namespace}.{name}{args}{kwargs}') return getattr(self.wrapped, name)(*args, **kwargs) return _wrapped - sys_monitoring = LoggingWrapper(sys_monitoring, "sys.monitoring") + sys_monitoring = LoggingWrapper(sys_monitoring, 'sys.monitoring') assert sys_monitoring is not None short_stack = functools.partial( @@ -80,28 +74,28 @@ if LOG: # pragma: debugging # Make a shorter number more likely to be unique. pid = os.getpid() tid = cast(int, threading.current_thread().ident) - tslug = f"{(pid * tid) % 9_999_991:07d}" + tslug = f'{(pid * tid) % 9_999_991:07d}' if tid not in seen_threads: seen_threads.add(tid) - log(f"New thread {tid} {tslug}:\n{short_stack()}") + log(f'New thread {tid} {tslug}:\n{short_stack()}') # log_seq = int(os.getenv("PANSEQ", "0")) # root = f"/tmp/pan.{log_seq:03d}" for filename in [ - "/tmp/foo.out", + '/tmp/foo.out', # f"{root}.out", # f"{root}-{pid}.out", # f"{root}-{pid}-{tslug}.out", ]: - with open(filename, "a") as f: - print(f"{pid}:{tslug}: {msg}", file=f, flush=True) + with open(filename, 'a') as f: + print(f'{pid}:{tslug}: {msg}', file=f, flush=True) def arg_repr(arg: Any) -> str: """Make a customized repr for logged values.""" if isinstance(arg, CodeType): return ( - f"" + f'' ) return repr(arg) @@ -117,20 +111,20 @@ if LOG: # pragma: debugging for name, arg in zip(names, args): if name is None: continue - args_reprs.append(f"{name}={arg_repr(arg)}") + args_reprs.append(f'{name}={arg_repr(arg)}') log(f"{id(self):#x}:{method.__name__}({', '.join(args_reprs)})") ret = method(self, *args) # log(f" end {id(self):#x}:{method.__name__}({', '.join(args_reprs)})") return ret except Exception as exc: - log(f"!!{exc.__class__.__name__}: {exc}") - log("".join(traceback.format_exception(exc))) # pylint: disable=[no-value-for-parameter] + log(f'!!{exc.__class__.__name__}: {exc}') + log(''.join(traceback.format_exception(exc))) # pylint: disable=[no-value-for-parameter] try: assert sys_monitoring is not None sys_monitoring.set_events(sys.monitoring.COVERAGE_ID, 0) except ValueError: # We might have already shut off monitoring. - log("oops, shutting off events with disabled tool id") + log('oops, shutting off events with disabled tool id') raise return _wrapped @@ -202,7 +196,7 @@ class SysMonitor(TracerCore): self.sysmon_on = False self.stats = { - "starts": 0, + 'starts': 0, } self.stopped = False @@ -211,7 +205,7 @@ class SysMonitor(TracerCore): def __repr__(self) -> str: points = sum(len(v) for v in self.data.values()) files = len(self.data) - return f"" + return f'' @panopticon() def start(self) -> None: @@ -219,7 +213,7 @@ class SysMonitor(TracerCore): self.stopped = False assert sys_monitoring is not None - sys_monitoring.use_tool_id(self.myid, "coverage.py") + sys_monitoring.use_tool_id(self.myid, 'coverage.py') register = functools.partial(sys_monitoring.register_callback, self.myid) events = sys_monitoring.events if self.trace_arcs: @@ -286,12 +280,12 @@ class SysMonitor(TracerCore): """Get the frame of the Python code we're monitoring.""" return inspect.currentframe().f_back.f_back # type: ignore[union-attr,return-value] - @panopticon("code", "@") + @panopticon('code', '@') def sysmon_py_start(self, code: CodeType, instruction_offset: int) -> MonitorReturn: """Handle sys.monitoring.events.PY_START events.""" # Entering a new frame. Decide if we should trace in this file. self._activity = True - self.stats["starts"] += 1 + self.stats['starts'] += 1 code_info = self.code_infos.get(id(code)) tracing_code: bool | None = None @@ -337,11 +331,11 @@ class SysMonitor(TracerCore): sys_monitoring.set_local_events( self.myid, code, - events.PY_RETURN + events.PY_RETURN | # - | events.PY_RESUME + events.PY_RESUME | # | events.PY_YIELD - | events.LINE, + events.LINE, # | events.BRANCH # | events.JUMP ) @@ -354,7 +348,7 @@ class SysMonitor(TracerCore): else: return sys.monitoring.DISABLE - @panopticon("code", "@") + @panopticon('code', '@') def sysmon_py_resume_arcs( self, code: CodeType, instruction_offset: int, ) -> MonitorReturn: @@ -362,7 +356,7 @@ class SysMonitor(TracerCore): frame = self.callers_frame() self.last_lines[frame] = frame.f_lineno - @panopticon("code", "@", None) + @panopticon('code', '@', None) def sysmon_py_return_arcs( self, code: CodeType, instruction_offset: int, retval: object, ) -> MonitorReturn: @@ -379,7 +373,7 @@ class SysMonitor(TracerCore): # Leaving this function, no need for the frame any more. self.last_lines.pop(frame, None) - @panopticon("code", "@", "exc") + @panopticon('code', '@', 'exc') def sysmon_py_unwind_arcs( self, code: CodeType, instruction_offset: int, exception: BaseException, ) -> MonitorReturn: @@ -397,8 +391,7 @@ class SysMonitor(TracerCore): # log(f"adding {arc=}") cast(Set[TArc], code_info.file_data).add(arc) - - @panopticon("code", "line") + @panopticon('code', 'line') def sysmon_line_lines(self, code: CodeType, line_number: int) -> MonitorReturn: """Handle sys.monitoring.events.LINE events for line coverage.""" code_info = self.code_infos[id(code)] @@ -407,7 +400,7 @@ class SysMonitor(TracerCore): # log(f"adding {line_number=}") return sys.monitoring.DISABLE - @panopticon("code", "line") + @panopticon('code', 'line') def sysmon_line_arcs(self, code: CodeType, line_number: int) -> MonitorReturn: """Handle sys.monitoring.events.LINE events for branch coverage.""" code_info = self.code_infos[id(code)] diff --git a/.venv/lib/python3.10/site-packages/coverage/templite.py b/.venv/lib/python3.10/site-packages/coverage/templite.py index 4e74912..43928ce 100644 --- a/.venv/lib/python3.10/site-packages/coverage/templite.py +++ b/.venv/lib/python3.10/site-packages/coverage/templite.py @@ -1,22 +1,20 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """A simple Python template renderer, for a nano-subset of Django syntax. For a detailed discussion of this code, see this chapter from 500 Lines: http://aosabook.org/en/500L/a-template-engine.html """ - # Coincidentally named the same as http://code.activestate.com/recipes/496702/ - from __future__ import annotations import re - -from typing import ( - Any, Callable, Dict, NoReturn, cast, -) +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import NoReturn class TempliteSyntaxError(ValueError): @@ -37,7 +35,7 @@ class CodeBuilder: self.indent_level = indent def __str__(self) -> str: - return "".join(str(c) for c in self.code) + return ''.join(str(c) for c in self.code) def add_line(self, line: str) -> None: """Add a line of source to the code. @@ -45,7 +43,7 @@ class CodeBuilder: Indentation and newline will be added for you, don't provide them. """ - self.code.extend([" " * self.indent_level, line, "\n"]) + self.code.extend([' ' * self.indent_level, line, '\n']) def add_section(self) -> CodeBuilder: """Add a section, a sub-CodeBuilder.""" @@ -117,6 +115,7 @@ class Templite: }) """ + def __init__(self, text: str, *contexts: dict[str, Any]) -> None: """Construct a Templite with the given `text`. @@ -135,82 +134,82 @@ class Templite: # it, and execute it to render the template. code = CodeBuilder() - code.add_line("def render_function(context, do_dots):") + code.add_line('def render_function(context, do_dots):') code.indent() vars_code = code.add_section() - code.add_line("result = []") - code.add_line("append_result = result.append") - code.add_line("extend_result = result.extend") - code.add_line("to_str = str") + code.add_line('result = []') + code.add_line('append_result = result.append') + code.add_line('extend_result = result.extend') + code.add_line('to_str = str') buffered: list[str] = [] def flush_output() -> None: """Force `buffered` to the code builder.""" if len(buffered) == 1: - code.add_line("append_result(%s)" % buffered[0]) + code.add_line('append_result(%s)' % buffered[0]) elif len(buffered) > 1: - code.add_line("extend_result([%s])" % ", ".join(buffered)) + code.add_line('extend_result([%s])' % ', '.join(buffered)) del buffered[:] ops_stack = [] # Split the text to form a list of tokens. - tokens = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text) + tokens = re.split(r'(?s)({{.*?}}|{%.*?%}|{#.*?#})', text) squash = in_joined = False for token in tokens: - if token.startswith("{"): + if token.startswith('{'): start, end = 2, -2 - squash = (token[-3] == "-") + squash = (token[-3] == '-') if squash: end = -3 - if token.startswith("{#"): + if token.startswith('{#'): # Comment: ignore it and move on. continue - elif token.startswith("{{"): + elif token.startswith('{{'): # An expression to evaluate. expr = self._expr_code(token[start:end].strip()) - buffered.append("to_str(%s)" % expr) + buffered.append('to_str(%s)' % expr) else: # token.startswith("{%") # Action tag: split into words and parse further. flush_output() words = token[start:end].strip().split() - if words[0] == "if": + if words[0] == 'if': # An if statement: evaluate the expression to determine if. if len(words) != 2: self._syntax_error("Don't understand if", token) - ops_stack.append("if") - code.add_line("if %s:" % self._expr_code(words[1])) + ops_stack.append('if') + code.add_line('if %s:' % self._expr_code(words[1])) code.indent() - elif words[0] == "for": + elif words[0] == 'for': # A loop: iterate over expression result. - if len(words) != 4 or words[2] != "in": + if len(words) != 4 or words[2] != 'in': self._syntax_error("Don't understand for", token) - ops_stack.append("for") + ops_stack.append('for') self._variable(words[1], self.loop_vars) code.add_line( - f"for c_{words[1]} in {self._expr_code(words[3])}:", + f'for c_{words[1]} in {self._expr_code(words[3])}:', ) code.indent() - elif words[0] == "joined": - ops_stack.append("joined") + elif words[0] == 'joined': + ops_stack.append('joined') in_joined = True - elif words[0].startswith("end"): + elif words[0].startswith('end'): # Endsomething. Pop the ops stack. if len(words) != 1: self._syntax_error("Don't understand end", token) end_what = words[0][3:] if not ops_stack: - self._syntax_error("Too many ends", token) + self._syntax_error('Too many ends', token) start_what = ops_stack.pop() if start_what != end_what: - self._syntax_error("Mismatched end tag", end_what) - if end_what == "joined": + self._syntax_error('Mismatched end tag', end_what) + if end_what == 'joined': in_joined = False else: code.dedent() @@ -219,19 +218,19 @@ class Templite: else: # Literal content. If it isn't empty, output it. if in_joined: - token = re.sub(r"\s*\n\s*", "", token.strip()) + token = re.sub(r'\s*\n\s*', '', token.strip()) elif squash: token = token.lstrip() if token: buffered.append(repr(token)) if ops_stack: - self._syntax_error("Unmatched action tag", ops_stack[-1]) + self._syntax_error('Unmatched action tag', ops_stack[-1]) flush_output() for var_name in self.all_vars - self.loop_vars: - vars_code.add_line(f"c_{var_name} = context[{var_name!r}]") + vars_code.add_line(f'c_{var_name} = context[{var_name!r}]') code.add_line("return ''.join(result)") code.dedent() @@ -240,30 +239,30 @@ class Templite: [Dict[str, Any], Callable[..., Any]], str, ], - code.get_globals()["render_function"], + code.get_globals()['render_function'], ) def _expr_code(self, expr: str) -> str: """Generate a Python expression for `expr`.""" - if "|" in expr: - pipes = expr.split("|") + if '|' in expr: + pipes = expr.split('|') code = self._expr_code(pipes[0]) for func in pipes[1:]: self._variable(func, self.all_vars) - code = f"c_{func}({code})" - elif "." in expr: - dots = expr.split(".") + code = f'c_{func}({code})' + elif '.' in expr: + dots = expr.split('.') code = self._expr_code(dots[0]) - args = ", ".join(repr(d) for d in dots[1:]) - code = f"do_dots({code}, {args})" + args = ', '.join(repr(d) for d in dots[1:]) + code = f'do_dots({code}, {args})' else: self._variable(expr, self.all_vars) - code = "c_%s" % expr + code = 'c_%s' % expr return code def _syntax_error(self, msg: str, thing: Any) -> NoReturn: """Raise a syntax error using `msg`, and showing `thing`.""" - raise TempliteSyntaxError(f"{msg}: {thing!r}") + raise TempliteSyntaxError(f'{msg}: {thing!r}') def _variable(self, name: str, vars_set: set[str]) -> None: """Track that `name` is used as a variable. @@ -273,8 +272,8 @@ class Templite: Raises an syntax error if `name` is not a valid name. """ - if not re.match(r"[_a-zA-Z][_a-zA-Z0-9]*$", name): - self._syntax_error("Not a valid name", name) + if not re.match(r'[_a-zA-Z][_a-zA-Z0-9]*$', name): + self._syntax_error('Not a valid name', name) vars_set.add(name) def render(self, context: dict[str, Any] | None = None) -> str: diff --git a/.venv/lib/python3.10/site-packages/coverage/tomlconfig.py b/.venv/lib/python3.10/site-packages/coverage/tomlconfig.py index 1ba282d..d7ddad9 100644 --- a/.venv/lib/python3.10/site-packages/coverage/tomlconfig.py +++ b/.venv/lib/python3.10/site-packages/coverage/tomlconfig.py @@ -1,27 +1,29 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """TOML configuration support for coverage.py""" - from __future__ import annotations import os import re - -from typing import Any, Callable, Iterable, TypeVar +from typing import Any +from typing import Callable +from typing import Iterable +from typing import TypeVar from coverage import env from coverage.exceptions import ConfigError -from coverage.misc import import_third_party, substitute_variables -from coverage.types import TConfigSectionOut, TConfigValueOut +from coverage.misc import import_third_party +from coverage.misc import substitute_variables +from coverage.types import TConfigSectionOut +from coverage.types import TConfigValueOut -if env.PYVERSION >= (3, 11, 0, "alpha", 7): +if env.PYVERSION >= (3, 11, 0, 'alpha', 7): import tomllib # pylint: disable=import-error has_tomllib = True else: # TOML support on Python 3.10 and below is an install-time extra option. - tomllib, has_tomllib = import_third_party("tomli") + tomllib, has_tomllib = import_third_party('tomli') class TomlDecodeError(Exception): @@ -29,7 +31,8 @@ class TomlDecodeError(Exception): pass -TWant = TypeVar("TWant") +TWant = TypeVar('TWant') + class TomlConfigParser: """TOML file reading with the interface of HandyConfigParser.""" @@ -60,7 +63,7 @@ class TomlConfigParser: raise TomlDecodeError(str(err)) from err return [filename] else: - has_toml = re.search(r"^\[tool\.coverage(\.|])", toml_text, flags=re.MULTILINE) + has_toml = re.search(r'^\[tool\.coverage(\.|])', toml_text, flags=re.MULTILINE) if self.our_file or has_toml: # Looks like they meant to read TOML, but we can't read it. msg = "Can't read {!r} without TOML support. Install with [toml] extra" @@ -79,10 +82,10 @@ class TomlConfigParser: data (str): the dict of data in the section, or None if not found. """ - prefixes = ["tool.coverage."] + prefixes = ['tool.coverage.'] for prefix in prefixes: real_section = prefix + section - parts = real_section.split(".") + parts = real_section.split('.') try: data = self.data[parts[0]] for part in parts[1:]: @@ -98,12 +101,12 @@ class TomlConfigParser: """Like .get, but returns the real section name and the value.""" name, data = self._get_section(section) if data is None: - raise ConfigError(f"No section: {section!r}") + raise ConfigError(f'No section: {section!r}') assert name is not None try: value = data[option] except KeyError: - raise ConfigError(f"No option {option!r} in section: {name!r}") from None + raise ConfigError(f'No option {option!r} in section: {name!r}') from None return name, value def _get_single(self, section: str, option: str) -> Any: @@ -134,7 +137,7 @@ class TomlConfigParser: def options(self, section: str) -> list[str]: _, data = self._get_section(section) if data is None: - raise ConfigError(f"No section: {section!r}") + raise ConfigError(f'No section: {section!r}') return list(data.keys()) def get_section(self, section: str) -> TConfigSectionOut: @@ -168,18 +171,18 @@ class TomlConfigParser: f"Option [{section}]{option} couldn't convert to {type_desc}: {value!r}", ) from e raise ValueError( - f"Option [{section}]{option} is not {type_desc}: {value!r}", + f'Option [{section}]{option} is not {type_desc}: {value!r}', ) def getboolean(self, section: str, option: str) -> bool: name, value = self._get_single(section, option) - bool_strings = {"true": True, "false": False} - return self._check_type(name, option, value, bool, bool_strings.__getitem__, "a boolean") + bool_strings = {'true': True, 'false': False} + return self._check_type(name, option, value, bool, bool_strings.__getitem__, 'a boolean') def _get_list(self, section: str, option: str) -> tuple[str, list[str]]: """Get a list of strings, substituting environment variables in the elements.""" name, values = self._get(section, option) - values = self._check_type(name, option, values, list, None, "a list") + values = self._check_type(name, option, values, list, None, 'a list') values = [substitute_variables(value, os.environ) for value in values] return name, values @@ -194,15 +197,15 @@ class TomlConfigParser: try: re.compile(value) except re.error as e: - raise ConfigError(f"Invalid [{name}].{option} value {value!r}: {e}") from e + raise ConfigError(f'Invalid [{name}].{option} value {value!r}: {e}') from e return values def getint(self, section: str, option: str) -> int: name, value = self._get_single(section, option) - return self._check_type(name, option, value, int, int, "an integer") + return self._check_type(name, option, value, int, int, 'an integer') def getfloat(self, section: str, option: str) -> float: name, value = self._get_single(section, option) if isinstance(value, int): value = float(value) - return self._check_type(name, option, value, float, float, "a float") + return self._check_type(name, option, value, float, float, 'a float') diff --git a/.venv/lib/python3.10/site-packages/coverage/types.py b/.venv/lib/python3.10/site-packages/coverage/types.py index d2e0bb9..55b5267 100644 --- a/.venv/lib/python3.10/site-packages/coverage/types.py +++ b/.venv/lib/python3.10/site-packages/coverage/types.py @@ -1,20 +1,27 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """ Types for use throughout coverage.py. """ - from __future__ import annotations import os import pathlib - -from types import FrameType, ModuleType -from typing import ( - Any, Callable, Dict, Iterable, List, Mapping, Optional, Protocol, - Set, Tuple, Type, Union, TYPE_CHECKING, -) +from types import FrameType +from types import ModuleType +from typing import Any +from typing import Callable +from typing import Dict +from typing import Iterable +from typing import List +from typing import Mapping +from typing import Optional +from typing import Protocol +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union if TYPE_CHECKING: from coverage.plugin import FileTracer @@ -22,7 +29,7 @@ if TYPE_CHECKING: AnyCallable = Callable[..., Any] -## File paths +# File paths # For arguments that are file paths: if TYPE_CHECKING: @@ -34,10 +41,12 @@ else: FilePathClasses = [str, pathlib.Path] FilePathType = Union[Type[str], Type[pathlib.Path]] -## Python tracing +# Python tracing + class TTraceFn(Protocol): """A Python trace function.""" + def __call__( self, frame: FrameType, @@ -47,13 +56,15 @@ class TTraceFn(Protocol): ) -> TTraceFn | None: ... -## Coverage.py tracing +# Coverage.py tracing + # Line numbers are pervasive enough that they deserve their own type. TLineNo = int TArc = Tuple[TLineNo, TLineNo] + class TFileDisposition(Protocol): """A simple value type for recording what to do with a file.""" @@ -78,6 +89,7 @@ TTraceFileData = Union[Set[TLineNo], Set[TArc], Set[int]] TTraceData = Dict[str, TTraceFileData] + class TracerCore(Protocol): """Anything that can report on Python execution.""" @@ -108,13 +120,13 @@ class TracerCore(Protocol): """Return a dictionary of statistics, or None.""" -## Coverage +# Coverage # Many places use kwargs as Coverage kwargs. TCovKwargs = Any -## Configuration +# Configuration # One value read from a config file. TConfigValueIn = Optional[Union[bool, int, float, str, Iterable[str]]] @@ -123,6 +135,7 @@ TConfigValueOut = Optional[Union[bool, int, float, str, List[str]]] TConfigSectionIn = Mapping[str, TConfigValueIn] TConfigSectionOut = Mapping[str, TConfigValueOut] + class TConfigurable(Protocol): """Something that can proxy to the coverage configuration settings.""" @@ -148,6 +161,7 @@ class TConfigurable(Protocol): """ + class TPluginConfig(Protocol): """Something that can provide options to a plugin.""" @@ -155,13 +169,14 @@ class TPluginConfig(Protocol): """Get the options for a plugin.""" -## Parsing +# Parsing TMorf = Union[ModuleType, str] TSourceTokenLines = Iterable[List[Tuple[str, str]]] -## Plugins +# Plugins + class TPlugin(Protocol): """What all plugins have in common.""" @@ -169,10 +184,11 @@ class TPlugin(Protocol): _coverage_enabled: bool -## Debugging +# Debugging class TWarnFn(Protocol): """A callable warn() function.""" + def __call__(self, msg: str, slug: str | None = None, once: bool = False) -> None: ... diff --git a/.venv/lib/python3.10/site-packages/coverage/version.py b/.venv/lib/python3.10/site-packages/coverage/version.py index 10f4115..3ce5ba8 100644 --- a/.venv/lib/python3.10/site-packages/coverage/version.py +++ b/.venv/lib/python3.10/site-packages/coverage/version.py @@ -1,14 +1,12 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """The version and URL for coverage.py""" # This file is exec'ed in setup.py, don't import anything! - from __future__ import annotations # version_info: same semantics as sys.version_info. # _dev: the .devN suffix if any. -version_info = (7, 4, 4, "final", 0) +version_info = (7, 4, 4, 'final', 0) _dev = 0 @@ -16,18 +14,18 @@ def _make_version( major: int, minor: int, micro: int, - releaselevel: str = "final", + releaselevel: str = 'final', serial: int = 0, dev: int = 0, ) -> str: """Create a readable version string from version_info tuple components.""" - assert releaselevel in ["alpha", "beta", "candidate", "final"] - version = "%d.%d.%d" % (major, minor, micro) - if releaselevel != "final": - short = {"alpha": "a", "beta": "b", "candidate": "rc"}[releaselevel] - version += f"{short}{serial}" + assert releaselevel in ['alpha', 'beta', 'candidate', 'final'] + version = '%d.%d.%d' % (major, minor, micro) + if releaselevel != 'final': + short = {'alpha': 'a', 'beta': 'b', 'candidate': 'rc'}[releaselevel] + version += f'{short}{serial}' if dev != 0: - version += f".dev{dev}" + version += f'.dev{dev}' return version @@ -41,8 +39,8 @@ def _make_url( ) -> str: """Make the URL people should start at for this version of coverage.py.""" return ( - "https://coverage.readthedocs.io/en/" - + _make_version(major, minor, micro, releaselevel, serial, dev) + 'https://coverage.readthedocs.io/en/' + + _make_version(major, minor, micro, releaselevel, serial, dev) ) diff --git a/.venv/lib/python3.10/site-packages/coverage/xmlreport.py b/.venv/lib/python3.10/site-packages/coverage/xmlreport.py index b346a2d..1ae2bea 100644 --- a/.venv/lib/python3.10/site-packages/coverage/xmlreport.py +++ b/.venv/lib/python3.10/site-packages/coverage/xmlreport.py @@ -1,21 +1,23 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - """XML reporting for coverage.py""" - from __future__ import annotations -import os import os.path import sys import time import xml.dom.minidom - from dataclasses import dataclass -from typing import Any, IO, Iterable, TYPE_CHECKING +from typing import Any +from typing import IO +from typing import Iterable +from typing import TYPE_CHECKING -from coverage import __version__, files -from coverage.misc import isolate_module, human_sorted, human_sorted_items +from coverage import __version__ +from coverage import files +from coverage.misc import human_sorted +from coverage.misc import human_sorted_items +from coverage.misc import isolate_module from coverage.plugin import FileReporter from coverage.report_core import get_analysis_to_report from coverage.results import Analysis @@ -28,15 +30,15 @@ if TYPE_CHECKING: os = isolate_module(os) -DTD_URL = "https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd" +DTD_URL = 'https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd' def rate(hit: int, num: int) -> str: """Return the fraction of `hit`/`num`, as a string.""" if num == 0: - return "1" + return '1' else: - return "%.4g" % (hit / num) + return '%.4g' % (hit / num) @dataclass @@ -57,7 +59,7 @@ def appendChild(parent: Any, child: Any) -> None: class XmlReporter: """A reporter for writing Cobertura-style XML coverage results.""" - report_type = "XML report" + report_type = 'XML report' def __init__(self, coverage: Coverage) -> None: self.coverage = coverage @@ -68,7 +70,7 @@ class XmlReporter: for src in self.config.source: if os.path.exists(src): if self.config.relative_files: - src = src.rstrip(r"\/") + src = src.rstrip(r'\/') else: src = files.canonical_filename(src) self.source_paths.add(src) @@ -90,27 +92,29 @@ class XmlReporter: # Create the DOM that will store the data. impl = xml.dom.minidom.getDOMImplementation() assert impl is not None - self.xml_out = impl.createDocument(None, "coverage", None) + self.xml_out = impl.createDocument(None, 'coverage', None) # Write header stuff. xcoverage = self.xml_out.documentElement - xcoverage.setAttribute("version", __version__) - xcoverage.setAttribute("timestamp", str(int(time.time()*1000))) - xcoverage.appendChild(self.xml_out.createComment( - f" Generated by coverage.py: {__url__} ", - )) - xcoverage.appendChild(self.xml_out.createComment(f" Based on {DTD_URL} ")) + xcoverage.setAttribute('version', __version__) + xcoverage.setAttribute('timestamp', str(int(time.time() * 1000))) + xcoverage.appendChild( + self.xml_out.createComment( + f' Generated by coverage.py: {__url__} ', + ), + ) + xcoverage.appendChild(self.xml_out.createComment(f' Based on {DTD_URL} ')) # Call xml_file for each file in the data. for fr, analysis in get_analysis_to_report(self.coverage, morfs): self.xml_file(fr, analysis, has_arcs) - xsources = self.xml_out.createElement("sources") + xsources = self.xml_out.createElement('sources') xcoverage.appendChild(xsources) # Populate the XML DOM with the source info. for path in human_sorted(self.source_paths): - xsource = self.xml_out.createElement("source") + xsource = self.xml_out.createElement('source') appendChild(xsources, xsource) txt = self.xml_out.createTextNode(path) appendChild(xsource, txt) @@ -118,43 +122,43 @@ class XmlReporter: lnum_tot, lhits_tot = 0, 0 bnum_tot, bhits_tot = 0, 0 - xpackages = self.xml_out.createElement("packages") + xpackages = self.xml_out.createElement('packages') xcoverage.appendChild(xpackages) # Populate the XML DOM with the package info. for pkg_name, pkg_data in human_sorted_items(self.packages.items()): - xpackage = self.xml_out.createElement("package") + xpackage = self.xml_out.createElement('package') appendChild(xpackages, xpackage) - xclasses = self.xml_out.createElement("classes") + xclasses = self.xml_out.createElement('classes') appendChild(xpackage, xclasses) for _, class_elt in human_sorted_items(pkg_data.elements.items()): appendChild(xclasses, class_elt) - xpackage.setAttribute("name", pkg_name.replace(os.sep, ".")) - xpackage.setAttribute("line-rate", rate(pkg_data.hits, pkg_data.lines)) + xpackage.setAttribute('name', pkg_name.replace(os.sep, '.')) + xpackage.setAttribute('line-rate', rate(pkg_data.hits, pkg_data.lines)) if has_arcs: branch_rate = rate(pkg_data.br_hits, pkg_data.branches) else: - branch_rate = "0" - xpackage.setAttribute("branch-rate", branch_rate) - xpackage.setAttribute("complexity", "0") + branch_rate = '0' + xpackage.setAttribute('branch-rate', branch_rate) + xpackage.setAttribute('complexity', '0') lhits_tot += pkg_data.hits lnum_tot += pkg_data.lines bhits_tot += pkg_data.br_hits bnum_tot += pkg_data.branches - xcoverage.setAttribute("lines-valid", str(lnum_tot)) - xcoverage.setAttribute("lines-covered", str(lhits_tot)) - xcoverage.setAttribute("line-rate", rate(lhits_tot, lnum_tot)) + xcoverage.setAttribute('lines-valid', str(lnum_tot)) + xcoverage.setAttribute('lines-covered', str(lhits_tot)) + xcoverage.setAttribute('line-rate', rate(lhits_tot, lnum_tot)) if has_arcs: - xcoverage.setAttribute("branches-valid", str(bnum_tot)) - xcoverage.setAttribute("branches-covered", str(bhits_tot)) - xcoverage.setAttribute("branch-rate", rate(bhits_tot, bnum_tot)) + xcoverage.setAttribute('branches-valid', str(bnum_tot)) + xcoverage.setAttribute('branches-covered', str(bhits_tot)) + xcoverage.setAttribute('branch-rate', rate(bhits_tot, bnum_tot)) else: - xcoverage.setAttribute("branches-covered", "0") - xcoverage.setAttribute("branches-valid", "0") - xcoverage.setAttribute("branch-rate", "0") - xcoverage.setAttribute("complexity", "0") + xcoverage.setAttribute('branches-covered', '0') + xcoverage.setAttribute('branches-valid', '0') + xcoverage.setAttribute('branch-rate', '0') + xcoverage.setAttribute('complexity', '0') # Write the output file. outfile.write(serialize_xml(self.xml_out)) @@ -176,57 +180,57 @@ class XmlReporter: # Create the "lines" and "package" XML elements, which # are populated later. Note that a package == a directory. - filename = fr.filename.replace("\\", "/") + filename = fr.filename.replace('\\', '/') for source_path in self.source_paths: if not self.config.relative_files: source_path = files.canonical_filename(source_path) - if filename.startswith(source_path.replace("\\", "/") + "/"): - rel_name = filename[len(source_path)+1:] + if filename.startswith(source_path.replace('\\', '/') + '/'): + rel_name = filename[len(source_path) + 1:] break else: - rel_name = fr.relative_filename().replace("\\", "/") - self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/")) + rel_name = fr.relative_filename().replace('\\', '/') + self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r'\/')) - dirname = os.path.dirname(rel_name) or "." - dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth]) - package_name = dirname.replace("/", ".") + dirname = os.path.dirname(rel_name) or '.' + dirname = '/'.join(dirname.split('/')[:self.config.xml_package_depth]) + package_name = dirname.replace('/', '.') package = self.packages.setdefault(package_name, PackageData({}, 0, 0, 0, 0)) - xclass: xml.dom.minidom.Element = self.xml_out.createElement("class") + xclass: xml.dom.minidom.Element = self.xml_out.createElement('class') - appendChild(xclass, self.xml_out.createElement("methods")) + appendChild(xclass, self.xml_out.createElement('methods')) - xlines = self.xml_out.createElement("lines") + xlines = self.xml_out.createElement('lines') appendChild(xclass, xlines) - xclass.setAttribute("name", os.path.relpath(rel_name, dirname)) - xclass.setAttribute("filename", rel_name.replace("\\", "/")) - xclass.setAttribute("complexity", "0") + xclass.setAttribute('name', os.path.relpath(rel_name, dirname)) + xclass.setAttribute('filename', rel_name.replace('\\', '/')) + xclass.setAttribute('complexity', '0') branch_stats = analysis.branch_stats() missing_branch_arcs = analysis.missing_branch_arcs() # For each statement, create an XML "line" element. for line in sorted(analysis.statements): - xline = self.xml_out.createElement("line") - xline.setAttribute("number", str(line)) + xline = self.xml_out.createElement('line') + xline.setAttribute('number', str(line)) # Q: can we get info about the number of times a statement is # executed? If so, that should be recorded here. - xline.setAttribute("hits", str(int(line not in analysis.missing))) + xline.setAttribute('hits', str(int(line not in analysis.missing))) if has_arcs: if line in branch_stats: total, taken = branch_stats[line] - xline.setAttribute("branch", "true") + xline.setAttribute('branch', 'true') xline.setAttribute( - "condition-coverage", - "%d%% (%d/%d)" % (100*taken//total, taken, total), + 'condition-coverage', + '%d%% (%d/%d)' % (100 * taken // total, taken, total), ) if line in missing_branch_arcs: - annlines = ["exit" if b < 0 else str(b) for b in missing_branch_arcs[line]] - xline.setAttribute("missing-branches", ",".join(annlines)) + annlines = ['exit' if b < 0 else str(b) for b in missing_branch_arcs[line]] + xline.setAttribute('missing-branches', ','.join(annlines)) appendChild(xlines, xline) class_lines = len(analysis.statements) @@ -241,12 +245,12 @@ class XmlReporter: class_br_hits = 0 # Finalize the statistics that are collected in the XML DOM. - xclass.setAttribute("line-rate", rate(class_hits, class_lines)) + xclass.setAttribute('line-rate', rate(class_hits, class_lines)) if has_arcs: branch_rate = rate(class_br_hits, class_branches) else: - branch_rate = "0" - xclass.setAttribute("branch-rate", branch_rate) + branch_rate = '0' + xclass.setAttribute('branch-rate', branch_rate) package.elements[rel_name] = xclass package.hits += class_hits diff --git a/.venv/lib/python3.10/site-packages/distutils-precedence.pth b/.venv/lib/python3.10/site-packages/distutils-precedence.pth index 6de4198..03f075b 100644 --- a/.venv/lib/python3.10/site-packages/distutils-precedence.pth +++ b/.venv/lib/python3.10/site-packages/distutils-precedence.pth @@ -1 +1 @@ -import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'stdlib') == 'local'; enabled and __import__('_distutils_hack').add_shim(); +import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'stdlib') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/.venv/lib/python3.10/site-packages/exceptiongroup-1.2.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/exceptiongroup-1.2.0.dist-info/METADATA index e202ce2..0de6bd0 100644 --- a/.venv/lib/python3.10/site-packages/exceptiongroup-1.2.0.dist-info/METADATA +++ b/.venv/lib/python3.10/site-packages/exceptiongroup-1.2.0.dist-info/METADATA @@ -152,4 +152,3 @@ Particularly in cases where a library installs its own exception hook, it is rec to use these special versions to do the actual formatting of exceptions/tracebacks. .. _PEP 654: https://www.python.org/dev/peps/pep-0654/ - diff --git a/.venv/lib/python3.10/site-packages/exceptiongroup/__init__.py b/.venv/lib/python3.10/site-packages/exceptiongroup/__init__.py index d8e36b2..13bb35d 100644 --- a/.venv/lib/python3.10/site-packages/exceptiongroup/__init__.py +++ b/.venv/lib/python3.10/site-packages/exceptiongroup/__init__.py @@ -1,12 +1,13 @@ +from __future__ import annotations __all__ = [ - "BaseExceptionGroup", - "ExceptionGroup", - "catch", - "format_exception", - "format_exception_only", - "print_exception", - "print_exc", - "suppress", + 'BaseExceptionGroup', + 'ExceptionGroup', + 'catch', + 'format_exception', + 'format_exception_only', + 'print_exception', + 'print_exc', + 'suppress', ] import os @@ -24,7 +25,7 @@ if sys.version_info < (3, 11): print_exception, ) - if os.getenv("EXCEPTIONGROUP_NO_PATCH") != "1": + if os.getenv('EXCEPTIONGROUP_NO_PATCH') != '1': from . import _formatting # noqa: F401 BaseExceptionGroup.__module__ = __name__ diff --git a/.venv/lib/python3.10/site-packages/exceptiongroup/_catch.py b/.venv/lib/python3.10/site-packages/exceptiongroup/_catch.py index 4a39b41..85e8eae 100644 --- a/.venv/lib/python3.10/site-packages/exceptiongroup/_catch.py +++ b/.venv/lib/python3.10/site-packages/exceptiongroup/_catch.py @@ -2,10 +2,13 @@ from __future__ import annotations import inspect import sys -from collections.abc import Callable, Iterable, Mapping +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Mapping from contextlib import AbstractContextManager from types import TracebackType -from typing import TYPE_CHECKING, Any +from typing import Any +from typing import TYPE_CHECKING if sys.version_info < (3, 11): from ._exceptions import BaseExceptionGroup @@ -52,7 +55,7 @@ class _Catcher: if isinstance(exc, BaseExceptionGroup): excgroup = exc else: - excgroup = BaseExceptionGroup("", [exc]) + excgroup = BaseExceptionGroup('', [exc]) new_exceptions: list[BaseException] = [] for exc_types, handler in self._handler_map.items(): @@ -73,8 +76,8 @@ class _Catcher: else: if inspect.iscoroutine(result): raise TypeError( - f"Error trying to handle {matched!r} with {handler!r}. " - "Exception handler must be a sync function." + f'Error trying to handle {matched!r} with {handler!r}. ' + 'Exception handler must be a sync function.', ) from exc if not excgroup: @@ -84,7 +87,7 @@ class _Catcher: if len(new_exceptions) == 1: return new_exceptions[0] - return BaseExceptionGroup("", new_exceptions) + return BaseExceptionGroup('', new_exceptions) elif ( excgroup and len(excgroup.exceptions) == 1 and excgroup.exceptions[0] is exc ): @@ -94,43 +97,43 @@ class _Catcher: def catch( - __handlers: Mapping[type[BaseException] | Iterable[type[BaseException]], _Handler] + __handlers: Mapping[type[BaseException] | Iterable[type[BaseException]], _Handler], ) -> AbstractContextManager[None]: if not isinstance(__handlers, Mapping): - raise TypeError("the argument must be a mapping") + raise TypeError('the argument must be a mapping') handler_map: dict[ - tuple[type[BaseException], ...], Callable[[BaseExceptionGroup]] + tuple[type[BaseException], ...], Callable[[BaseExceptionGroup]], ] = {} for type_or_iterable, handler in __handlers.items(): iterable: tuple[type[BaseException]] if isinstance(type_or_iterable, type) and issubclass( - type_or_iterable, BaseException + type_or_iterable, BaseException, ): iterable = (type_or_iterable,) elif isinstance(type_or_iterable, Iterable): iterable = tuple(type_or_iterable) else: raise TypeError( - "each key must be either an exception classes or an iterable thereof" + 'each key must be either an exception classes or an iterable thereof', ) if not callable(handler): - raise TypeError("handlers must be callable") + raise TypeError('handlers must be callable') for exc_type in iterable: if not isinstance(exc_type, type) or not issubclass( - exc_type, BaseException + exc_type, BaseException, ): raise TypeError( - "each key must be either an exception classes or an iterable " - "thereof" + 'each key must be either an exception classes or an iterable ' + 'thereof', ) if issubclass(exc_type, BaseExceptionGroup): raise TypeError( - "catching ExceptionGroup with catch() is not allowed. " - "Use except instead." + 'catching ExceptionGroup with catch() is not allowed. ' + 'Use except instead.', ) handler_map[iterable] = handler diff --git a/.venv/lib/python3.10/site-packages/exceptiongroup/_exceptions.py b/.venv/lib/python3.10/site-packages/exceptiongroup/_exceptions.py index 82a129c..f16b8bb 100644 --- a/.venv/lib/python3.10/site-packages/exceptiongroup/_exceptions.py +++ b/.venv/lib/python3.10/site-packages/exceptiongroup/_exceptions.py @@ -1,21 +1,28 @@ from __future__ import annotations -from collections.abc import Callable, Sequence +from collections.abc import Callable +from collections.abc import Sequence from functools import partial -from inspect import getmro, isclass -from typing import TYPE_CHECKING, Generic, Type, TypeVar, cast, overload +from inspect import getmro +from inspect import isclass +from typing import cast +from typing import Generic +from typing import overload +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar if TYPE_CHECKING: from typing import Self -_BaseExceptionT_co = TypeVar("_BaseExceptionT_co", bound=BaseException, covariant=True) -_BaseExceptionT = TypeVar("_BaseExceptionT", bound=BaseException) -_ExceptionT_co = TypeVar("_ExceptionT_co", bound=Exception, covariant=True) -_ExceptionT = TypeVar("_ExceptionT", bound=Exception) +_BaseExceptionT_co = TypeVar('_BaseExceptionT_co', bound=BaseException, covariant=True) +_BaseExceptionT = TypeVar('_BaseExceptionT', bound=BaseException) +_ExceptionT_co = TypeVar('_ExceptionT_co', bound=Exception, covariant=True) +_ExceptionT = TypeVar('_ExceptionT', bound=Exception) def check_direct_subclass( - exc: BaseException, parents: tuple[type[BaseException]] + exc: BaseException, parents: tuple[type[BaseException]], ) -> bool: for cls in getmro(exc.__class__)[:-1]: if cls in parents: @@ -25,42 +32,42 @@ def check_direct_subclass( def get_condition_filter( - condition: type[_BaseExceptionT] - | tuple[type[_BaseExceptionT], ...] - | Callable[[_BaseExceptionT_co], bool], + condition: type[_BaseExceptionT] | + tuple[type[_BaseExceptionT], ...] | + Callable[[_BaseExceptionT_co], bool], ) -> Callable[[_BaseExceptionT_co], bool]: if isclass(condition) and issubclass( - cast(Type[BaseException], condition), BaseException + cast(Type[BaseException], condition), BaseException, ): return partial(check_direct_subclass, parents=(condition,)) elif isinstance(condition, tuple): if all(isclass(x) and issubclass(x, BaseException) for x in condition): return partial(check_direct_subclass, parents=condition) elif callable(condition): - return cast("Callable[[BaseException], bool]", condition) + return cast('Callable[[BaseException], bool]', condition) - raise TypeError("expected a function, exception type or tuple of exception types") + raise TypeError('expected a function, exception type or tuple of exception types') class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): """A combination of multiple unrelated exceptions.""" def __new__( - cls, __message: str, __exceptions: Sequence[_BaseExceptionT_co] + cls, __message: str, __exceptions: Sequence[_BaseExceptionT_co], ) -> Self: if not isinstance(__message, str): - raise TypeError(f"argument 1 must be str, not {type(__message)}") + raise TypeError(f'argument 1 must be str, not {type(__message)}') if not isinstance(__exceptions, Sequence): - raise TypeError("second argument (exceptions) must be a sequence") + raise TypeError('second argument (exceptions) must be a sequence') if not __exceptions: raise ValueError( - "second argument (exceptions) must be a non-empty sequence" + 'second argument (exceptions) must be a non-empty sequence', ) for i, exc in enumerate(__exceptions): if not isinstance(exc, BaseException): raise ValueError( - f"Item {i} of second argument (exceptions) is not an exception" + f'Item {i} of second argument (exceptions) is not an exception', ) if cls is BaseExceptionGroup: @@ -72,11 +79,11 @@ class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): if not isinstance(exc, Exception): if cls is ExceptionGroup: raise TypeError( - "Cannot nest BaseExceptions in an ExceptionGroup" + 'Cannot nest BaseExceptions in an ExceptionGroup', ) else: raise TypeError( - f"Cannot nest BaseExceptions in {cls.__name__!r}" + f'Cannot nest BaseExceptions in {cls.__name__!r}', ) instance = super().__new__(cls, __message, __exceptions) @@ -87,10 +94,10 @@ class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): def add_note(self, note: str) -> None: if not isinstance(note, str): raise TypeError( - f"Expected a string, got note={note!r} (type {type(note).__name__})" + f'Expected a string, got note={note!r} (type {type(note).__name__})', ) - if not hasattr(self, "__notes__"): + if not hasattr(self, '__notes__'): self.__notes__: list[str] = [] self.__notes__.append(note) @@ -107,27 +114,27 @@ class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): @overload def subgroup( - self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], ) -> ExceptionGroup[_ExceptionT] | None: ... @overload def subgroup( - self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...] + self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], ) -> BaseExceptionGroup[_BaseExceptionT] | None: ... @overload def subgroup( - self, __condition: Callable[[_BaseExceptionT_co | Self], bool] + self, __condition: Callable[[_BaseExceptionT_co | Self], bool], ) -> BaseExceptionGroup[_BaseExceptionT_co] | None: ... def subgroup( self, - __condition: type[_BaseExceptionT] - | tuple[type[_BaseExceptionT], ...] - | Callable[[_BaseExceptionT_co | Self], bool], + __condition: type[_BaseExceptionT] | + tuple[type[_BaseExceptionT], ...] | + Callable[[_BaseExceptionT_co | Self], bool], ) -> BaseExceptionGroup[_BaseExceptionT] | None: condition = get_condition_filter(__condition) modified = False @@ -161,7 +168,7 @@ class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): @overload def split( - self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], ) -> tuple[ ExceptionGroup[_ExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None, @@ -170,7 +177,7 @@ class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): @overload def split( - self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...] + self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], ) -> tuple[ BaseExceptionGroup[_BaseExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None, @@ -179,7 +186,7 @@ class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): @overload def split( - self, __condition: Callable[[_BaseExceptionT_co | Self], bool] + self, __condition: Callable[[_BaseExceptionT_co | Self], bool], ) -> tuple[ BaseExceptionGroup[_BaseExceptionT_co] | None, BaseExceptionGroup[_BaseExceptionT_co] | None, @@ -188,19 +195,19 @@ class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): def split( self, - __condition: type[_BaseExceptionT] - | tuple[type[_BaseExceptionT], ...] - | Callable[[_BaseExceptionT_co], bool], + __condition: type[_BaseExceptionT] | + tuple[type[_BaseExceptionT], ...] | + Callable[[_BaseExceptionT_co], bool], ) -> ( tuple[ ExceptionGroup[_ExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None, - ] - | tuple[ + ] | + tuple[ BaseExceptionGroup[_BaseExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None, - ] - | tuple[ + ] | + tuple[ BaseExceptionGroup[_BaseExceptionT_co] | None, BaseExceptionGroup[_BaseExceptionT_co] | None, ] @@ -246,26 +253,26 @@ class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): @overload def derive( - self, __excs: Sequence[_BaseExceptionT] + self, __excs: Sequence[_BaseExceptionT], ) -> BaseExceptionGroup[_BaseExceptionT]: ... def derive( - self, __excs: Sequence[_BaseExceptionT] + self, __excs: Sequence[_BaseExceptionT], ) -> BaseExceptionGroup[_BaseExceptionT]: eg = BaseExceptionGroup(self.message, __excs) - if hasattr(self, "__notes__"): + if hasattr(self, '__notes__'): # Create a new list so that add_note() only affects one exceptiongroup eg.__notes__ = list(self.__notes__) return eg def __str__(self) -> str: - suffix = "" if len(self._exceptions) == 1 else "s" - return f"{self.message} ({len(self._exceptions)} sub-exception{suffix})" + suffix = '' if len(self._exceptions) == 1 else 's' + return f'{self.message} ({len(self._exceptions)} sub-exception{suffix})' def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.message!r}, {self._exceptions!r})" + return f'{self.__class__.__name__}({self.message!r}, {self._exceptions!r})' class ExceptionGroup(BaseExceptionGroup[_ExceptionT_co], Exception): @@ -282,46 +289,46 @@ class ExceptionGroup(BaseExceptionGroup[_ExceptionT_co], Exception): @overload # type: ignore[override] def subgroup( - self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], ) -> ExceptionGroup[_ExceptionT] | None: ... @overload def subgroup( - self, __condition: Callable[[_ExceptionT_co | Self], bool] + self, __condition: Callable[[_ExceptionT_co | Self], bool], ) -> ExceptionGroup[_ExceptionT_co] | None: ... def subgroup( self, - __condition: type[_ExceptionT] - | tuple[type[_ExceptionT], ...] - | Callable[[_ExceptionT_co], bool], + __condition: type[_ExceptionT] | + tuple[type[_ExceptionT], ...] | + Callable[[_ExceptionT_co], bool], ) -> ExceptionGroup[_ExceptionT] | None: return super().subgroup(__condition) @overload def split( - self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], ) -> tuple[ - ExceptionGroup[_ExceptionT] | None, ExceptionGroup[_ExceptionT_co] | None + ExceptionGroup[_ExceptionT] | None, ExceptionGroup[_ExceptionT_co] | None, ]: ... @overload def split( - self, __condition: Callable[[_ExceptionT_co | Self], bool] + self, __condition: Callable[[_ExceptionT_co | Self], bool], ) -> tuple[ - ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None + ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None, ]: ... def split( self: Self, - __condition: type[_ExceptionT] - | tuple[type[_ExceptionT], ...] - | Callable[[_ExceptionT_co], bool], + __condition: type[_ExceptionT] | + tuple[type[_ExceptionT], ...] | + Callable[[_ExceptionT_co], bool], ) -> tuple[ - ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None + ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None, ]: return super().split(__condition) diff --git a/.venv/lib/python3.10/site-packages/exceptiongroup/_formatting.py b/.venv/lib/python3.10/site-packages/exceptiongroup/_formatting.py index e3835e4..b0957eb 100644 --- a/.venv/lib/python3.10/site-packages/exceptiongroup/_formatting.py +++ b/.venv/lib/python3.10/site-packages/exceptiongroup/_formatting.py @@ -10,27 +10,29 @@ import textwrap import traceback from functools import singledispatch from types import TracebackType -from typing import Any, List, Optional +from typing import Any +from typing import List +from typing import Optional from ._exceptions import BaseExceptionGroup max_group_width = 15 max_group_depth = 10 _cause_message = ( - "\nThe above exception was the direct cause of the following exception:\n\n" + '\nThe above exception was the direct cause of the following exception:\n\n' ) _context_message = ( - "\nDuring handling of the above exception, another exception occurred:\n\n" + '\nDuring handling of the above exception, another exception occurred:\n\n' ) def _format_final_exc_line(etype, value): - valuestr = _safe_string(value, "exception") + valuestr = _safe_string(value, 'exception') if value is None or not valuestr: - line = f"{etype}\n" + line = f'{etype}\n' else: - line = f"{etype}: {valuestr}\n" + line = f'{etype}: {valuestr}\n' return line @@ -39,7 +41,7 @@ def _safe_string(value, what, func=str): try: return func(value) except BaseException: - return f"<{what} {func.__name__}() failed>" + return f'<{what} {func.__name__}() failed>' class _ExceptionPrintContext: @@ -49,14 +51,14 @@ class _ExceptionPrintContext: self.need_close = False def indent(self): - return " " * (2 * self.exception_group_depth) + return ' ' * (2 * self.exception_group_depth) def emit(self, text_gen, margin_char=None): if margin_char is None: - margin_char = "|" + margin_char = '|' indent_str = self.indent() if self.exception_group_depth: - indent_str += margin_char + " " + indent_str += margin_char + ' ' if isinstance(text_gen, str): yield textwrap.indent(text_gen, indent_str, lambda line: True) @@ -66,9 +68,9 @@ class _ExceptionPrintContext: def exceptiongroup_excepthook( - etype: type[BaseException], value: BaseException, tb: TracebackType | None + etype: type[BaseException], value: BaseException, tb: TracebackType | None, ) -> None: - sys.stderr.write("".join(traceback.format_exception(etype, value, tb))) + sys.stderr.write(''.join(traceback.format_exception(etype, value, tb))) class PatchedTracebackException(traceback.TracebackException): @@ -86,7 +88,7 @@ class PatchedTracebackException(traceback.TracebackException): ) -> None: kwargs: dict[str, Any] = {} if sys.version_info >= (3, 10): - kwargs["compact"] = compact + kwargs['compact'] = compact is_recursive_call = _seen is not None if _seen is None: @@ -102,13 +104,13 @@ class PatchedTracebackException(traceback.TracebackException): self.exc_type = exc_type # Capture now to permit freeing resources: only complication is in the # unofficial API _format_final_exc_line - self._str = _safe_string(exc_value, "exception") + self._str = _safe_string(exc_value, 'exception') try: - self.__notes__ = getattr(exc_value, "__notes__", None) + self.__notes__ = getattr(exc_value, '__notes__', None) except KeyError: # Workaround for https://github.com/python/cpython/issues/98778 on Python # <= 3.9, and some 3.10 and 3.11 patch versions. - HTTPError = getattr(sys.modules.get("urllib.error", None), "HTTPError", ()) + HTTPError = getattr(sys.modules.get('urllib.error', None), 'HTTPError', ()) if sys.version_info[:2] <= (3, 11) and isinstance(exc_value, HTTPError): self.__notes__ = None else: @@ -127,9 +129,9 @@ class PatchedTracebackException(traceback.TracebackException): self.end_lineno = str(end_lno) if end_lno is not None else None self.end_offset = exc_value.end_offset elif ( - exc_type - and issubclass(exc_type, (NameError, AttributeError)) - and getattr(exc_value, "name", None) is not None + exc_type and + issubclass(exc_type, (NameError, AttributeError)) and + getattr(exc_value, 'name', None) is not None ): suggestion = _compute_suggestion_error(exc_value, exc_traceback) if suggestion: @@ -171,10 +173,10 @@ class PatchedTracebackException(traceback.TracebackException): else: need_context = True if ( - e - and e.__context__ is not None - and need_context - and id(e.__context__) not in _seen + e and + e.__context__ is not None and + need_context and + id(e.__context__) not in _seen ): context = PatchedTracebackException( type(e.__context__), @@ -243,12 +245,12 @@ class PatchedTracebackException(traceback.TracebackException): yield from _ctx.emit(msg) if exc.exceptions is None: if exc.stack: - yield from _ctx.emit("Traceback (most recent call last):\n") + yield from _ctx.emit('Traceback (most recent call last):\n') yield from _ctx.emit(exc.stack.format()) yield from _ctx.emit(exc.format_exception_only()) elif _ctx.exception_group_depth > max_group_depth: # exception group, but depth exceeds limit - yield from _ctx.emit(f"... (max_group_depth is {max_group_depth})\n") + yield from _ctx.emit(f'... (max_group_depth is {max_group_depth})\n') else: # format exception group is_toplevel = _ctx.exception_group_depth == 0 @@ -257,8 +259,8 @@ class PatchedTracebackException(traceback.TracebackException): if exc.stack: yield from _ctx.emit( - "Exception Group Traceback (most recent call last):\n", - margin_char="+" if is_toplevel else None, + 'Exception Group Traceback (most recent call last):\n', + margin_char='+' if is_toplevel else None, ) yield from _ctx.emit(exc.stack.format()) @@ -279,24 +281,24 @@ class PatchedTracebackException(traceback.TracebackException): truncated = i >= max_group_width else: truncated = False - title = f"{i + 1}" if not truncated else "..." + title = f'{i + 1}' if not truncated else '...' yield ( - _ctx.indent() - + ("+-" if i == 0 else " ") - + f"+---------------- {title} ----------------\n" + _ctx.indent() + + ('+-' if i == 0 else ' ') + + f'+---------------- {title} ----------------\n' ) _ctx.exception_group_depth += 1 if not truncated: yield from exc.exceptions[i].format(chain=chain, _ctx=_ctx) else: remaining = num_excs - max_group_width - plural = "s" if remaining > 1 else "" + plural = 's' if remaining > 1 else '' yield from _ctx.emit( - f"and {remaining} more exception{plural}\n" + f'and {remaining} more exception{plural}\n', ) if last_exc and _ctx.need_close: - yield _ctx.indent() + "+------------------------------------\n" + yield _ctx.indent() + '+------------------------------------\n' _ctx.need_close = False _ctx.exception_group_depth -= 1 @@ -320,10 +322,10 @@ class PatchedTracebackException(traceback.TracebackException): stype = self.exc_type.__qualname__ smod = self.exc_type.__module__ - if smod not in ("__main__", "builtins"): + if smod not in ('__main__', 'builtins'): if not isinstance(smod, str): - smod = "" - stype = smod + "." + stype + smod = '' + stype = smod + '.' + stype if not issubclass(self.exc_type, SyntaxError): yield _format_final_exc_line(stype, self._str) @@ -334,10 +336,10 @@ class PatchedTracebackException(traceback.TracebackException): if isinstance(self.__notes__, collections.abc.Sequence): for note in self.__notes__: - note = _safe_string(note, "note") - yield from [line + "\n" for line in note.split("\n")] + note = _safe_string(note, 'note') + yield from [line + '\n' for line in note.split('\n')] elif self.__notes__ is not None: - yield _safe_string(self.__notes__, "__notes__", func=repr) + yield _safe_string(self.__notes__, '__notes__', func=repr) traceback_exception_original_format = traceback.TracebackException.format @@ -345,7 +347,7 @@ traceback_exception_original_format_exception_only = ( traceback.TracebackException.format_exception_only ) traceback_exception_format_syntax_error = getattr( - traceback.TracebackException, "_format_syntax_error", None + traceback.TracebackException, '_format_syntax_error', None, ) if sys.excepthook is sys.__excepthook__: traceback.TracebackException.__init__ = ( # type: ignore[assignment] @@ -371,10 +373,10 @@ if sys.excepthook is sys.__excepthook__: # hook. # # More details: https://github.com/python-trio/trio/issues/1065 -if getattr(sys.excepthook, "__name__", None) in ( - "apport_excepthook", +if getattr(sys.excepthook, '__name__', None) in ( + 'apport_excepthook', # on ubuntu 22.10 the hook was renamed to partial_apport_excepthook - "partial_apport_excepthook", + 'partial_apport_excepthook', ): # patch traceback like above traceback.TracebackException.__init__ = ( # type: ignore[assignment] @@ -394,36 +396,36 @@ if getattr(sys.excepthook, "__name__", None) in ( assert sys.excepthook is apport_python_hook.apport_excepthook # monkeypatch the sys module that apport has imported - fake_sys = ModuleType("exceptiongroup_fake_sys") + fake_sys = ModuleType('exceptiongroup_fake_sys') fake_sys.__dict__.update(sys.__dict__) fake_sys.__excepthook__ = exceptiongroup_excepthook apport_python_hook.sys = fake_sys @singledispatch -def format_exception_only(__exc: BaseException) -> List[str]: +def format_exception_only(__exc: BaseException) -> list[str]: return list( PatchedTracebackException( - type(__exc), __exc, None, compact=True - ).format_exception_only() + type(__exc), __exc, None, compact=True, + ).format_exception_only(), ) @format_exception_only.register -def _(__exc: type, value: BaseException) -> List[str]: +def _(__exc: type, value: BaseException) -> list[str]: return format_exception_only(value) @singledispatch def format_exception( __exc: BaseException, - limit: Optional[int] = None, + limit: int | None = None, chain: bool = True, -) -> List[str]: +) -> list[str]: return list( PatchedTracebackException( - type(__exc), __exc, __exc.__traceback__, limit=limit, compact=True - ).format(chain=chain) + type(__exc), __exc, __exc.__traceback__, limit=limit, compact=True, + ).format(chain=chain), ) @@ -432,16 +434,16 @@ def _( __exc: type, value: BaseException, tb: TracebackType, - limit: Optional[int] = None, + limit: int | None = None, chain: bool = True, -) -> List[str]: +) -> list[str]: return format_exception(value, limit, chain) @singledispatch def print_exception( __exc: BaseException, - limit: Optional[int] = None, + limit: int | None = None, file: Any = None, chain: bool = True, ) -> None: @@ -449,9 +451,9 @@ def print_exception( file = sys.stderr for line in PatchedTracebackException( - type(__exc), __exc, __exc.__traceback__, limit=limit + type(__exc), __exc, __exc.__traceback__, limit=limit, ).format(chain=chain): - print(line, file=file, end="") + print(line, file=file, end='') @print_exception.register @@ -459,7 +461,7 @@ def _( __exc: type, value: BaseException, tb: TracebackType, - limit: Optional[int] = None, + limit: int | None = None, file: Any = None, chain: bool = True, ) -> None: @@ -467,7 +469,7 @@ def _( def print_exc( - limit: Optional[int] = None, + limit: int | None = None, file: Any | None = None, chain: bool = True, ) -> None: @@ -494,11 +496,11 @@ def _substitution_cost(ch_a, ch_b): def _compute_suggestion_error(exc_value, tb): - wrong_name = getattr(exc_value, "name", None) + wrong_name = getattr(exc_value, 'name', None) if wrong_name is None or not isinstance(wrong_name, str): return None if isinstance(exc_value, AttributeError): - obj = getattr(exc_value, "obj", _SENTINEL) + obj = getattr(exc_value, 'obj', _SENTINEL) if obj is _SENTINEL: return None obj = exc_value.obj @@ -532,7 +534,7 @@ def _compute_suggestion_error(exc_value, tb): # Don't take matches we've already beaten. max_distance = min(max_distance, best_distance - 1) current_distance = _levenshtein_distance( - wrong_name, possible_name, max_distance + wrong_name, possible_name, max_distance, ) if current_distance > max_distance: continue diff --git a/.venv/lib/python3.10/site-packages/exceptiongroup/_suppress.py b/.venv/lib/python3.10/site-packages/exceptiongroup/_suppress.py index 6741563..76f8201 100644 --- a/.venv/lib/python3.10/site-packages/exceptiongroup/_suppress.py +++ b/.venv/lib/python3.10/site-packages/exceptiongroup/_suppress.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from contextlib import AbstractContextManager diff --git a/.venv/lib/python3.10/site-packages/exceptiongroup/_version.py b/.venv/lib/python3.10/site-packages/exceptiongroup/_version.py index ba0e352..fc89b6f 100644 --- a/.venv/lib/python3.10/site-packages/exceptiongroup/_version.py +++ b/.venv/lib/python3.10/site-packages/exceptiongroup/_version.py @@ -1,5 +1,6 @@ # file generated by setuptools_scm # don't change, don't track in version control +from __future__ import annotations TYPE_CHECKING = False if TYPE_CHECKING: from typing import Tuple, Union diff --git a/.venv/lib/python3.10/site-packages/iniconfig-2.0.0.dist-info/licenses/LICENSE b/.venv/lib/python3.10/site-packages/iniconfig-2.0.0.dist-info/licenses/LICENSE index 31ecdfb..ff33b8f 100644 --- a/.venv/lib/python3.10/site-packages/iniconfig-2.0.0.dist-info/licenses/LICENSE +++ b/.venv/lib/python3.10/site-packages/iniconfig-2.0.0.dist-info/licenses/LICENSE @@ -5,10 +5,10 @@ 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 @@ -16,4 +16,3 @@ 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. - diff --git a/.venv/lib/python3.10/site-packages/iniconfig/__init__.py b/.venv/lib/python3.10/site-packages/iniconfig/__init__.py index c18a8e4..e7242e4 100644 --- a/.venv/lib/python3.10/site-packages/iniconfig/__init__.py +++ b/.venv/lib/python3.10/site-packages/iniconfig/__init__.py @@ -2,34 +2,32 @@ (C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed """ from __future__ import annotations -from typing import ( - Callable, - Iterator, - Mapping, - Optional, - Tuple, - TypeVar, - Union, - TYPE_CHECKING, - NoReturn, - NamedTuple, - overload, - cast, -) import os +from typing import Callable +from typing import cast +from typing import Iterator +from typing import Mapping +from typing import NamedTuple +from typing import NoReturn +from typing import Optional +from typing import overload +from typing import Tuple +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union if TYPE_CHECKING: - from typing_extensions import Final + from typing import Final -__all__ = ["IniConfig", "ParseError", "COMMENTCHARS", "iscommentline"] +__all__ = ['IniConfig', 'ParseError', 'COMMENTCHARS', 'iscommentline'] from .exceptions import ParseError from . import _parse from ._parse import COMMENTCHARS, iscommentline -_D = TypeVar("_D") -_T = TypeVar("_T") +_D = TypeVar('_D') +_T = TypeVar('_T') class SectionWrapper: @@ -110,7 +108,7 @@ class IniConfig: self, path: str | os.PathLike[str], data: str | None = None, - encoding: str = "utf-8", + encoding: str = 'utf-8', ) -> None: self.path = os.fspath(path) if data is None: @@ -125,17 +123,17 @@ class IniConfig: for lineno, section, name, value in tokens: if section is None: - raise ParseError(self.path, lineno, "no section header defined") + raise ParseError(self.path, lineno, 'no section header defined') self._sources[section, name] = lineno if name is None: if section in self.sections: raise ParseError( - self.path, lineno, f"duplicate section {section!r}" + self.path, lineno, f'duplicate section {section!r}', ) sections_data[section] = {} else: if name in self.sections[section]: - raise ParseError(self.path, lineno, f"duplicate name {name!r}") + raise ParseError(self.path, lineno, f'duplicate name {name!r}') assert value is not None sections_data[section][name] = value @@ -172,7 +170,7 @@ class IniConfig: @overload def get( - self, section: str, name: str, default: _D, convert: None = None + self, section: str, name: str, default: _D, convert: None = None, ) -> str | _D: ... diff --git a/.venv/lib/python3.10/site-packages/iniconfig/_parse.py b/.venv/lib/python3.10/site-packages/iniconfig/_parse.py index 2d03437..2ea6e8f 100644 --- a/.venv/lib/python3.10/site-packages/iniconfig/_parse.py +++ b/.venv/lib/python3.10/site-packages/iniconfig/_parse.py @@ -1,10 +1,11 @@ from __future__ import annotations -from .exceptions import ParseError from typing import NamedTuple +from .exceptions import ParseError -COMMENTCHARS = "#;" + +COMMENTCHARS = '#;' class _ParsedLine(NamedTuple): @@ -25,19 +26,19 @@ def parse_lines(path: str, line_iter: list[str]) -> list[_ParsedLine]: # new section elif name is not None and data is None: if not name: - raise ParseError(path, lineno, "empty section name") + raise ParseError(path, lineno, 'empty section name') section = name result.append(_ParsedLine(lineno, section, None, None)) # continuation elif name is None and data is not None: if not result: - raise ParseError(path, lineno, "unexpected value continuation") + raise ParseError(path, lineno, 'unexpected value continuation') last = result.pop() if last.name is None: - raise ParseError(path, lineno, "unexpected value continuation") + raise ParseError(path, lineno, 'unexpected value continuation') if last.value: - last = last._replace(value=f"{last.value}\n{data}") + last = last._replace(value=f'{last.value}\n{data}') else: last = last._replace(value=data) result.append(last) @@ -47,30 +48,30 @@ def parse_lines(path: str, line_iter: list[str]) -> list[_ParsedLine]: def _parseline(path: str, line: str, lineno: int) -> tuple[str | None, str | None]: # blank lines if iscommentline(line): - line = "" + line = '' else: line = line.rstrip() if not line: return None, None # section - if line[0] == "[": + if line[0] == '[': realline = line for c in COMMENTCHARS: line = line.split(c)[0].rstrip() - if line[-1] == "]": + if line[-1] == ']': return line[1:-1], None return None, realline.strip() # value elif not line[0].isspace(): try: - name, value = line.split("=", 1) - if ":" in name: + name, value = line.split('=', 1) + if ':' in name: raise ValueError() except ValueError: try: - name, value = line.split(":", 1) + name, value = line.split(':', 1) except ValueError: - raise ParseError(path, lineno, "unexpected line: %r" % line) + raise ParseError(path, lineno, 'unexpected line: %r' % line) return name.strip(), value.strip() # continuation else: diff --git a/.venv/lib/python3.10/site-packages/iniconfig/_version.py b/.venv/lib/python3.10/site-packages/iniconfig/_version.py index dd1883d..9153835 100644 --- a/.venv/lib/python3.10/site-packages/iniconfig/_version.py +++ b/.venv/lib/python3.10/site-packages/iniconfig/_version.py @@ -1,4 +1,5 @@ # file generated by setuptools_scm # don't change, don't track in version control +from __future__ import annotations __version__ = version = '2.0.0' __version_tuple__ = version_tuple = (2, 0, 0) diff --git a/.venv/lib/python3.10/site-packages/iniconfig/exceptions.py b/.venv/lib/python3.10/site-packages/iniconfig/exceptions.py index bc898e6..63eae2f 100644 --- a/.venv/lib/python3.10/site-packages/iniconfig/exceptions.py +++ b/.venv/lib/python3.10/site-packages/iniconfig/exceptions.py @@ -1,8 +1,9 @@ from __future__ import annotations + from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing_extensions import Final + from typing import Final class ParseError(Exception): @@ -17,4 +18,4 @@ class ParseError(Exception): self.msg = msg def __str__(self) -> str: - return f"{self.path}:{self.lineno + 1}: {self.msg}" + return f'{self.path}:{self.lineno + 1}: {self.msg}' diff --git a/.venv/lib/python3.10/site-packages/packaging-24.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/packaging-24.0.dist-info/METADATA index 10ab439..088991c 100644 --- a/.venv/lib/python3.10/site-packages/packaging-24.0.dist-info/METADATA +++ b/.venv/lib/python3.10/site-packages/packaging-24.0.dist-info/METADATA @@ -99,4 +99,3 @@ Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for recent changes and project history. .. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/ - diff --git a/.venv/lib/python3.10/site-packages/packaging/__init__.py b/.venv/lib/python3.10/site-packages/packaging/__init__.py index e7c0aa1..c045dd6 100644 --- a/.venv/lib/python3.10/site-packages/packaging/__init__.py +++ b/.venv/lib/python3.10/site-packages/packaging/__init__.py @@ -1,15 +1,16 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +from __future__ import annotations -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" +__title__ = 'packaging' +__summary__ = 'Core utilities for Python packages' +__uri__ = 'https://github.com/pypa/packaging' -__version__ = "24.0" +__version__ = '24.0' -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" +__author__ = 'Donald Stufft and individual contributors' +__email__ = 'donald@stufft.io' -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "2014 %s" % __author__ +__license__ = 'BSD-2-Clause or Apache-2.0' +__copyright__ = '2014 %s' % __author__ diff --git a/.venv/lib/python3.10/site-packages/packaging/_elffile.py b/.venv/lib/python3.10/site-packages/packaging/_elffile.py index 6fb19b3..1708d4c 100644 --- a/.venv/lib/python3.10/site-packages/packaging/_elffile.py +++ b/.venv/lib/python3.10/site-packages/packaging/_elffile.py @@ -7,11 +7,14 @@ interface to ``ZipFile``. Only the read interface is implemented. Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html """ +from __future__ import annotations import enum import os import struct -from typing import IO, Optional, Tuple +from typing import IO +from typing import Optional +from typing import Tuple class ELFInvalid(ValueError): @@ -45,12 +48,12 @@ class ELFFile: self._f = f try: - ident = self._read("16B") + ident = self._read('16B') except struct.error: - raise ELFInvalid("unable to parse identification") + raise ELFInvalid('unable to parse identification') magic = bytes(ident[:4]) - if magic != b"\x7fELF": - raise ELFInvalid(f"invalid magic: {magic!r}") + if magic != b'\x7fELF': + raise ELFInvalid(f'invalid magic: {magic!r}') self.capacity = ident[4] # Format for program header (bitness). self.encoding = ident[5] # Data structure encoding (endianness). @@ -60,15 +63,15 @@ class ELFFile: # p_fmt: Format for section header. # p_idx: Indexes to find p_type, p_offset, and p_filesz. e_fmt, self._p_fmt, self._p_idx = { - (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. - (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + (1, 1): ('HHIIIIIHHH', '>IIIIIIII', (0, 1, 4)), # 32-bit MSB. + (2, 1): ('HHIQQQIHHH', '>IIQQQQQQ', (0, 2, 5)), # 64-bit MSB. }[(self.capacity, self.encoding)] except KeyError: raise ELFInvalid( - f"unrecognized capacity ({self.capacity}) or " - f"encoding ({self.encoding})" + f'unrecognized capacity ({self.capacity}) or ' + f'encoding ({self.encoding})', ) try: @@ -85,13 +88,13 @@ class ELFFile: self._e_phnum, # Number of sections. ) = self._read(e_fmt) except struct.error as e: - raise ELFInvalid("unable to parse machine and section information") from e + raise ELFInvalid('unable to parse machine and section information') from e - def _read(self, fmt: str) -> Tuple[int, ...]: + def _read(self, fmt: str) -> tuple[int, ...]: return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) @property - def interpreter(self) -> Optional[str]: + def interpreter(self) -> str | None: """ The path recorded in the ``PT_INTERP`` section header. """ @@ -104,5 +107,5 @@ class ELFFile: if data[self._p_idx[0]] != 3: # Not PT_INTERP. continue self._f.seek(data[self._p_idx[1]]) - return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip('\0') return None diff --git a/.venv/lib/python3.10/site-packages/packaging/_manylinux.py b/.venv/lib/python3.10/site-packages/packaging/_manylinux.py index ad62505..80edda2 100644 --- a/.venv/lib/python3.10/site-packages/packaging/_manylinux.py +++ b/.venv/lib/python3.10/site-packages/packaging/_manylinux.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import collections import contextlib import functools @@ -5,9 +7,18 @@ import os import re import sys import warnings -from typing import Dict, Generator, Iterator, NamedTuple, Optional, Sequence, Tuple +from typing import Dict +from typing import Generator +from typing import Iterator +from typing import NamedTuple +from typing import Optional +from typing import Sequence +from typing import Tuple -from ._elffile import EIClass, EIData, ELFFile, EMachine +from ._elffile import EIClass +from ._elffile import EIData +from ._elffile import ELFFile +from ._elffile import EMachine EF_ARM_ABIMASK = 0xFF000000 EF_ARM_ABI_VER5 = 0x05000000 @@ -17,9 +28,9 @@ EF_ARM_ABI_FLOAT_HARD = 0x00000400 # `os.PathLike` not a generic type until Python 3.9, so sticking with `str` # as the type for `path` until then. @contextlib.contextmanager -def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]: +def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]: try: - with open(path, "rb") as f: + with open(path, 'rb') as f: yield ELFFile(f) except (OSError, TypeError, ValueError): yield None @@ -31,38 +42,38 @@ def _is_linux_armhf(executable: str) -> bool: # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf with _parse_elf(executable) as f: return ( - f is not None - and f.capacity == EIClass.C32 - and f.encoding == EIData.Lsb - and f.machine == EMachine.Arm - and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 - and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + f is not None and + f.capacity == EIClass.C32 and + f.encoding == EIData.Lsb and + f.machine == EMachine.Arm and + f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 and + f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD ) def _is_linux_i686(executable: str) -> bool: with _parse_elf(executable) as f: return ( - f is not None - and f.capacity == EIClass.C32 - and f.encoding == EIData.Lsb - and f.machine == EMachine.I386 + f is not None and + f.capacity == EIClass.C32 and + f.encoding == EIData.Lsb and + f.machine == EMachine.I386 ) def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool: - if "armv7l" in archs: + if 'armv7l' in archs: return _is_linux_armhf(executable) - if "i686" in archs: + if 'i686' in archs: return _is_linux_i686(executable) allowed_archs = { - "x86_64", - "aarch64", - "ppc64", - "ppc64le", - "s390x", - "loongarch64", - "riscv64", + 'x86_64', + 'aarch64', + 'ppc64', + 'ppc64le', + 's390x', + 'loongarch64', + 'riscv64', } return any(arch in allowed_archs for arch in archs) @@ -72,7 +83,7 @@ def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool: # For now, guess what the highest minor version might be, assume it will # be 50 for testing. Once this actually happens, update the dictionary # with the actual value. -_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50) +_LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50) class _GLibCVersion(NamedTuple): @@ -80,7 +91,7 @@ class _GLibCVersion(NamedTuple): minor: int -def _glibc_version_string_confstr() -> Optional[str]: +def _glibc_version_string_confstr() -> str | None: """ Primary implementation of glibc_version_string using os.confstr. """ @@ -90,7 +101,7 @@ def _glibc_version_string_confstr() -> Optional[str]: # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 try: # Should be a string like "glibc 2.17". - version_string: Optional[str] = os.confstr("CS_GNU_LIBC_VERSION") + version_string: str | None = os.confstr('CS_GNU_LIBC_VERSION') assert version_string is not None _, version = version_string.rsplit() except (AssertionError, AttributeError, OSError, ValueError): @@ -99,7 +110,7 @@ def _glibc_version_string_confstr() -> Optional[str]: return version -def _glibc_version_string_ctypes() -> Optional[str]: +def _glibc_version_string_ctypes() -> str | None: """ Fallback implementation of glibc_version_string using ctypes. """ @@ -138,17 +149,17 @@ def _glibc_version_string_ctypes() -> Optional[str]: version_str: str = gnu_get_libc_version() # py2 / py3 compatibility: if not isinstance(version_str, str): - version_str = version_str.decode("ascii") + version_str = version_str.decode('ascii') return version_str -def _glibc_version_string() -> Optional[str]: +def _glibc_version_string() -> str | None: """Returns glibc version string, or None if not using glibc.""" return _glibc_version_string_confstr() or _glibc_version_string_ctypes() -def _parse_glibc_version(version_str: str) -> Tuple[int, int]: +def _parse_glibc_version(version_str: str) -> tuple[int, int]: """Parse glibc version. We use a regexp instead of str.split because we want to discard any @@ -156,19 +167,19 @@ def _parse_glibc_version(version_str: str) -> Tuple[int, int]: in patched/forked versions of glibc (e.g. Linaro's version of glibc uses version strings like "2.20-2014.11"). See gh-3588. """ - m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + m = re.match(r'(?P[0-9]+)\.(?P[0-9]+)', version_str) if not m: warnings.warn( - f"Expected glibc version with 2 components major.minor," - f" got: {version_str}", + f'Expected glibc version with 2 components major.minor,' + f' got: {version_str}', RuntimeWarning, ) return -1, -1 - return int(m.group("major")), int(m.group("minor")) + return int(m.group('major')), int(m.group('minor')) -@functools.lru_cache() -def _get_glibc_version() -> Tuple[int, int]: +@functools.lru_cache +def _get_glibc_version() -> tuple[int, int]: version_str = _glibc_version_string() if version_str is None: return (-1, -1) @@ -185,30 +196,30 @@ def _is_compatible(arch: str, version: _GLibCVersion) -> bool: import _manylinux except ImportError: return True - if hasattr(_manylinux, "manylinux_compatible"): + if hasattr(_manylinux, 'manylinux_compatible'): result = _manylinux.manylinux_compatible(version[0], version[1], arch) if result is not None: return bool(result) return True if version == _GLibCVersion(2, 5): - if hasattr(_manylinux, "manylinux1_compatible"): + if hasattr(_manylinux, 'manylinux1_compatible'): return bool(_manylinux.manylinux1_compatible) if version == _GLibCVersion(2, 12): - if hasattr(_manylinux, "manylinux2010_compatible"): + if hasattr(_manylinux, 'manylinux2010_compatible'): return bool(_manylinux.manylinux2010_compatible) if version == _GLibCVersion(2, 17): - if hasattr(_manylinux, "manylinux2014_compatible"): + if hasattr(_manylinux, 'manylinux2014_compatible'): return bool(_manylinux.manylinux2014_compatible) return True _LEGACY_MANYLINUX_MAP = { # CentOS 7 w/ glibc 2.17 (PEP 599) - (2, 17): "manylinux2014", + (2, 17): 'manylinux2014', # CentOS 6 w/ glibc 2.12 (PEP 571) - (2, 12): "manylinux2010", + (2, 12): 'manylinux2010', # CentOS 5 w/ glibc 2.5 (PEP 513) - (2, 5): "manylinux1", + (2, 5): 'manylinux1', } @@ -227,7 +238,7 @@ def platform_tags(archs: Sequence[str]) -> Iterator[str]: return # Oldest glibc to be supported regardless of architecture is (2, 17). too_old_glibc2 = _GLibCVersion(2, 16) - if set(archs) & {"x86_64", "i686"}: + if set(archs) & {'x86_64', 'i686'}: # On x86/i686 also oldest glibc to be supported is (2, 5). too_old_glibc2 = _GLibCVersion(2, 4) current_glibc = _GLibCVersion(*_get_glibc_version()) @@ -250,11 +261,11 @@ def platform_tags(archs: Sequence[str]) -> Iterator[str]: min_minor = -1 for glibc_minor in range(glibc_max.minor, min_minor, -1): glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) - tag = "manylinux_{}_{}".format(*glibc_version) + tag = 'manylinux_{}_{}'.format(*glibc_version) if _is_compatible(arch, glibc_version): - yield f"{tag}_{arch}" + yield f'{tag}_{arch}' # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. if glibc_version in _LEGACY_MANYLINUX_MAP: legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] if _is_compatible(arch, glibc_version): - yield f"{legacy_tag}_{arch}" + yield f'{legacy_tag}_{arch}' diff --git a/.venv/lib/python3.10/site-packages/packaging/_musllinux.py b/.venv/lib/python3.10/site-packages/packaging/_musllinux.py index 86419df..91de389 100644 --- a/.venv/lib/python3.10/site-packages/packaging/_musllinux.py +++ b/.venv/lib/python3.10/site-packages/packaging/_musllinux.py @@ -3,12 +3,16 @@ This module implements logic to detect if the currently running Python is linked against musl, and what musl version is used. """ +from __future__ import annotations import functools import re import subprocess import sys -from typing import Iterator, NamedTuple, Optional, Sequence +from typing import Iterator +from typing import NamedTuple +from typing import Optional +from typing import Sequence from ._elffile import ELFFile @@ -18,18 +22,18 @@ class _MuslVersion(NamedTuple): minor: int -def _parse_musl_version(output: str) -> Optional[_MuslVersion]: +def _parse_musl_version(output: str) -> _MuslVersion | None: lines = [n for n in (n.strip() for n in output.splitlines()) if n] - if len(lines) < 2 or lines[0][:4] != "musl": + if len(lines) < 2 or lines[0][:4] != 'musl': return None - m = re.match(r"Version (\d+)\.(\d+)", lines[1]) + m = re.match(r'Version (\d+)\.(\d+)', lines[1]) if not m: return None return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) -@functools.lru_cache() -def _get_musl_version(executable: str) -> Optional[_MuslVersion]: +@functools.lru_cache +def _get_musl_version(executable: str) -> _MuslVersion | None: """Detect currently-running musl runtime version. This is done by checking the specified executable's dynamic linking @@ -41,11 +45,11 @@ def _get_musl_version(executable: str) -> Optional[_MuslVersion]: Dynamic Program Loader """ try: - with open(executable, "rb") as f: + with open(executable, 'rb') as f: ld = ELFFile(f).interpreter except (OSError, TypeError, ValueError): return None - if ld is None or "musl" not in ld: + if ld is None or 'musl' not in ld: return None proc = subprocess.run([ld], stderr=subprocess.PIPE, text=True) return _parse_musl_version(proc.stderr) @@ -67,17 +71,17 @@ def platform_tags(archs: Sequence[str]) -> Iterator[str]: return for arch in archs: for minor in range(sys_musl.minor, -1, -1): - yield f"musllinux_{sys_musl.major}_{minor}_{arch}" + yield f'musllinux_{sys_musl.major}_{minor}_{arch}' -if __name__ == "__main__": # pragma: no cover +if __name__ == '__main__': # pragma: no cover import sysconfig plat = sysconfig.get_platform() - assert plat.startswith("linux-"), "not linux" + assert plat.startswith('linux-'), 'not linux' - print("plat:", plat) - print("musl:", _get_musl_version(sys.executable)) - print("tags:", end=" ") - for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])): - print(t, end="\n ") + print('plat:', plat) + print('musl:', _get_musl_version(sys.executable)) + print('tags:', end=' ') + for t in platform_tags(re.sub(r'[.-]', '_', plat.split('-', 1)[-1])): + print(t, end='\n ') diff --git a/.venv/lib/python3.10/site-packages/packaging/_parser.py b/.venv/lib/python3.10/site-packages/packaging/_parser.py index 684df75..9c2a7c3 100644 --- a/.venv/lib/python3.10/site-packages/packaging/_parser.py +++ b/.venv/lib/python3.10/site-packages/packaging/_parser.py @@ -3,11 +3,18 @@ The docstring for each __parse_* function contains ENBF-inspired grammar representing the implementation. """ +from __future__ import annotations import ast -from typing import Any, List, NamedTuple, Optional, Tuple, Union +from typing import Any +from typing import List +from typing import NamedTuple +from typing import Optional +from typing import Tuple +from typing import Union -from ._tokenizer import DEFAULT_RULES, Tokenizer +from ._tokenizer import DEFAULT_RULES +from ._tokenizer import Tokenizer class Node: @@ -52,9 +59,9 @@ MarkerList = List[Any] class ParsedRequirement(NamedTuple): name: str url: str - extras: List[str] + extras: list[str] specifier: str - marker: Optional[MarkerList] + marker: MarkerList | None # -------------------------------------------------------------------------------------- @@ -68,68 +75,68 @@ def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: """ requirement = WS? IDENTIFIER WS? extras WS? requirement_details """ - tokenizer.consume("WS") + tokenizer.consume('WS') name_token = tokenizer.expect( - "IDENTIFIER", expected="package name at the start of dependency specifier" + 'IDENTIFIER', expected='package name at the start of dependency specifier', ) name = name_token.text - tokenizer.consume("WS") + tokenizer.consume('WS') extras = _parse_extras(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') url, specifier, marker = _parse_requirement_details(tokenizer) - tokenizer.expect("END", expected="end of dependency specifier") + tokenizer.expect('END', expected='end of dependency specifier') return ParsedRequirement(name, url, extras, specifier, marker) def _parse_requirement_details( tokenizer: Tokenizer, -) -> Tuple[str, str, Optional[MarkerList]]: +) -> tuple[str, str, MarkerList | None]: """ requirement_details = AT URL (WS requirement_marker?)? | specifier WS? (requirement_marker)? """ - specifier = "" - url = "" + specifier = '' + url = '' marker = None - if tokenizer.check("AT"): + if tokenizer.check('AT'): tokenizer.read() - tokenizer.consume("WS") + tokenizer.consume('WS') url_start = tokenizer.position - url = tokenizer.expect("URL", expected="URL after @").text - if tokenizer.check("END", peek=True): + url = tokenizer.expect('URL', expected='URL after @').text + if tokenizer.check('END', peek=True): return (url, specifier, marker) - tokenizer.expect("WS", expected="whitespace after URL") + tokenizer.expect('WS', expected='whitespace after URL') # The input might end after whitespace. - if tokenizer.check("END", peek=True): + if tokenizer.check('END', peek=True): return (url, specifier, marker) marker = _parse_requirement_marker( - tokenizer, span_start=url_start, after="URL and whitespace" + tokenizer, span_start=url_start, after='URL and whitespace', ) else: specifier_start = tokenizer.position specifier = _parse_specifier(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') - if tokenizer.check("END", peek=True): + if tokenizer.check('END', peek=True): return (url, specifier, marker) marker = _parse_requirement_marker( tokenizer, span_start=specifier_start, after=( - "version specifier" + 'version specifier' if specifier - else "name and no valid version specifier" + else 'name and no valid version specifier' ), ) @@ -137,66 +144,66 @@ def _parse_requirement_details( def _parse_requirement_marker( - tokenizer: Tokenizer, *, span_start: int, after: str + tokenizer: Tokenizer, *, span_start: int, after: str, ) -> MarkerList: """ requirement_marker = SEMICOLON marker WS? """ - if not tokenizer.check("SEMICOLON"): + if not tokenizer.check('SEMICOLON'): tokenizer.raise_syntax_error( - f"Expected end or semicolon (after {after})", + f'Expected end or semicolon (after {after})', span_start=span_start, ) tokenizer.read() marker = _parse_marker(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') return marker -def _parse_extras(tokenizer: Tokenizer) -> List[str]: +def _parse_extras(tokenizer: Tokenizer) -> list[str]: """ extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? """ - if not tokenizer.check("LEFT_BRACKET", peek=True): + if not tokenizer.check('LEFT_BRACKET', peek=True): return [] with tokenizer.enclosing_tokens( - "LEFT_BRACKET", - "RIGHT_BRACKET", - around="extras", + 'LEFT_BRACKET', + 'RIGHT_BRACKET', + around='extras', ): - tokenizer.consume("WS") + tokenizer.consume('WS') extras = _parse_extras_list(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') return extras -def _parse_extras_list(tokenizer: Tokenizer) -> List[str]: +def _parse_extras_list(tokenizer: Tokenizer) -> list[str]: """ extras_list = identifier (wsp* ',' wsp* identifier)* """ - extras: List[str] = [] + extras: list[str] = [] - if not tokenizer.check("IDENTIFIER"): + if not tokenizer.check('IDENTIFIER'): return extras extras.append(tokenizer.read().text) while True: - tokenizer.consume("WS") - if tokenizer.check("IDENTIFIER", peek=True): - tokenizer.raise_syntax_error("Expected comma between extra names") - elif not tokenizer.check("COMMA"): + tokenizer.consume('WS') + if tokenizer.check('IDENTIFIER', peek=True): + tokenizer.raise_syntax_error('Expected comma between extra names') + elif not tokenizer.check('COMMA'): break tokenizer.read() - tokenizer.consume("WS") + tokenizer.consume('WS') - extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extra_token = tokenizer.expect('IDENTIFIER', expected='extra name after comma') extras.append(extra_token.text) return extras @@ -208,13 +215,13 @@ def _parse_specifier(tokenizer: Tokenizer) -> str: | WS? version_many WS? """ with tokenizer.enclosing_tokens( - "LEFT_PARENTHESIS", - "RIGHT_PARENTHESIS", - around="version specifier", + 'LEFT_PARENTHESIS', + 'RIGHT_PARENTHESIS', + around='version specifier', ): - tokenizer.consume("WS") + tokenizer.consume('WS') parsed_specifiers = _parse_version_many(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') return parsed_specifiers @@ -223,27 +230,27 @@ def _parse_version_many(tokenizer: Tokenizer) -> str: """ version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? """ - parsed_specifiers = "" - while tokenizer.check("SPECIFIER"): + parsed_specifiers = '' + while tokenizer.check('SPECIFIER'): span_start = tokenizer.position parsed_specifiers += tokenizer.read().text - if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True): + if tokenizer.check('VERSION_PREFIX_TRAIL', peek=True): tokenizer.raise_syntax_error( - ".* suffix can only be used with `==` or `!=` operators", + '.* suffix can only be used with `==` or `!=` operators', span_start=span_start, span_end=tokenizer.position + 1, ) - if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True): + if tokenizer.check('VERSION_LOCAL_LABEL_TRAIL', peek=True): tokenizer.raise_syntax_error( - "Local version label can only be used with `==` or `!=` operators", + 'Local version label can only be used with `==` or `!=` operators', span_start=span_start, span_end=tokenizer.position, ) - tokenizer.consume("WS") - if not tokenizer.check("COMMA"): + tokenizer.consume('WS') + if not tokenizer.check('COMMA'): break parsed_specifiers += tokenizer.read().text - tokenizer.consume("WS") + tokenizer.consume('WS') return parsed_specifiers @@ -257,7 +264,7 @@ def parse_marker(source: str) -> MarkerList: def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList: retval = _parse_marker(tokenizer) - tokenizer.expect("END", expected="end of marker expression") + tokenizer.expect('END', expected='end of marker expression') return retval @@ -266,7 +273,7 @@ def _parse_marker(tokenizer: Tokenizer) -> MarkerList: marker = marker_atom (BOOLOP marker_atom)+ """ expression = [_parse_marker_atom(tokenizer)] - while tokenizer.check("BOOLOP"): + while tokenizer.check('BOOLOP'): token = tokenizer.read() expr_right = _parse_marker_atom(tokenizer) expression.extend((token.text, expr_right)) @@ -279,19 +286,19 @@ def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: | WS? marker_item WS? """ - tokenizer.consume("WS") - if tokenizer.check("LEFT_PARENTHESIS", peek=True): + tokenizer.consume('WS') + if tokenizer.check('LEFT_PARENTHESIS', peek=True): with tokenizer.enclosing_tokens( - "LEFT_PARENTHESIS", - "RIGHT_PARENTHESIS", - around="marker expression", + 'LEFT_PARENTHESIS', + 'RIGHT_PARENTHESIS', + around='marker expression', ): - tokenizer.consume("WS") + tokenizer.consume('WS') marker: MarkerAtom = _parse_marker(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') else: marker = _parse_marker_item(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') return marker @@ -299,13 +306,13 @@ def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: """ marker_item = WS? marker_var WS? marker_op WS? marker_var WS? """ - tokenizer.consume("WS") + tokenizer.consume('WS') marker_var_left = _parse_marker_var(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') marker_op = _parse_marker_op(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') marker_var_right = _parse_marker_var(tokenizer) - tokenizer.consume("WS") + tokenizer.consume('WS') return (marker_var_left, marker_op, marker_var_right) @@ -313,19 +320,19 @@ def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: """ marker_var = VARIABLE | QUOTED_STRING """ - if tokenizer.check("VARIABLE"): - return process_env_var(tokenizer.read().text.replace(".", "_")) - elif tokenizer.check("QUOTED_STRING"): + if tokenizer.check('VARIABLE'): + return process_env_var(tokenizer.read().text.replace('.', '_')) + elif tokenizer.check('QUOTED_STRING'): return process_python_str(tokenizer.read().text) else: tokenizer.raise_syntax_error( - message="Expected a marker variable or quoted string" + message='Expected a marker variable or quoted string', ) def process_env_var(env_var: str) -> Variable: - if env_var in ("platform_python_implementation", "python_implementation"): - return Variable("platform_python_implementation") + if env_var in ('platform_python_implementation', 'python_implementation'): + return Variable('platform_python_implementation') else: return Variable(env_var) @@ -339,18 +346,18 @@ def _parse_marker_op(tokenizer: Tokenizer) -> Op: """ marker_op = IN | NOT IN | OP """ - if tokenizer.check("IN"): + if tokenizer.check('IN'): tokenizer.read() - return Op("in") - elif tokenizer.check("NOT"): + return Op('in') + elif tokenizer.check('NOT'): tokenizer.read() - tokenizer.expect("WS", expected="whitespace after 'not'") - tokenizer.expect("IN", expected="'in' after 'not'") - return Op("not in") - elif tokenizer.check("OP"): + tokenizer.expect('WS', expected="whitespace after 'not'") + tokenizer.expect('IN', expected="'in' after 'not'") + return Op('not in') + elif tokenizer.check('OP'): return Op(tokenizer.read().text) else: return tokenizer.raise_syntax_error( - "Expected marker operator, one of " - "<=, <, !=, ==, >=, >, ~=, ===, in, not in" + 'Expected marker operator, one of ' + '<=, <, !=, ==, >=, >, ~=, ===, in, not in', ) diff --git a/.venv/lib/python3.10/site-packages/packaging/_structures.py b/.venv/lib/python3.10/site-packages/packaging/_structures.py index 90a6465..a882659 100644 --- a/.venv/lib/python3.10/site-packages/packaging/_structures.py +++ b/.venv/lib/python3.10/site-packages/packaging/_structures.py @@ -1,11 +1,12 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +from __future__ import annotations class InfinityType: def __repr__(self) -> str: - return "Infinity" + return 'Infinity' def __hash__(self) -> int: return hash(repr(self)) @@ -25,7 +26,7 @@ class InfinityType: def __ge__(self, other: object) -> bool: return True - def __neg__(self: object) -> "NegativeInfinityType": + def __neg__(self: object) -> NegativeInfinityType: return NegativeInfinity @@ -34,7 +35,7 @@ Infinity = InfinityType() class NegativeInfinityType: def __repr__(self) -> str: - return "-Infinity" + return '-Infinity' def __hash__(self) -> int: return hash(repr(self)) diff --git a/.venv/lib/python3.10/site-packages/packaging/_tokenizer.py b/.venv/lib/python3.10/site-packages/packaging/_tokenizer.py index dd0d648..feaff2c 100644 --- a/.venv/lib/python3.10/site-packages/packaging/_tokenizer.py +++ b/.venv/lib/python3.10/site-packages/packaging/_tokenizer.py @@ -1,7 +1,14 @@ +from __future__ import annotations + import contextlib import re from dataclasses import dataclass -from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union +from typing import Dict +from typing import Iterator +from typing import NoReturn +from typing import Optional +from typing import Tuple +from typing import Union from .specifiers import Specifier @@ -21,7 +28,7 @@ class ParserSyntaxError(Exception): message: str, *, source: str, - span: Tuple[int, int], + span: tuple[int, int], ) -> None: self.span = span self.message = message @@ -30,18 +37,18 @@ class ParserSyntaxError(Exception): super().__init__() def __str__(self) -> str: - marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" - return "\n ".join([self.message, self.source, marker]) + marker = ' ' * self.span[0] + '~' * (self.span[1] - self.span[0]) + '^' + return '\n '.join([self.message, self.source, marker]) -DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { - "LEFT_PARENTHESIS": r"\(", - "RIGHT_PARENTHESIS": r"\)", - "LEFT_BRACKET": r"\[", - "RIGHT_BRACKET": r"\]", - "SEMICOLON": r";", - "COMMA": r",", - "QUOTED_STRING": re.compile( +DEFAULT_RULES: Dict[str, Union[str, re.Pattern[str]]] = { + 'LEFT_PARENTHESIS': r'\(', + 'RIGHT_PARENTHESIS': r'\)', + 'LEFT_BRACKET': r'\[', + 'RIGHT_BRACKET': r'\]', + 'SEMICOLON': r';', + 'COMMA': r',', + 'QUOTED_STRING': re.compile( r""" ( ('[^']*') @@ -51,11 +58,11 @@ DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { """, re.VERBOSE, ), - "OP": r"(===|==|~=|!=|<=|>=|<|>)", - "BOOLOP": r"\b(or|and)\b", - "IN": r"\bin\b", - "NOT": r"\bnot\b", - "VARIABLE": re.compile( + 'OP': r'(===|==|~=|!=|<=|>=|<|>)', + 'BOOLOP': r'\b(or|and)\b', + 'IN': r'\bin\b', + 'NOT': r'\bnot\b', + 'VARIABLE': re.compile( r""" \b( python_version @@ -71,17 +78,17 @@ DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { """, re.VERBOSE, ), - "SPECIFIER": re.compile( + 'SPECIFIER': re.compile( Specifier._operator_regex_str + Specifier._version_regex_str, re.VERBOSE | re.IGNORECASE, ), - "AT": r"\@", - "URL": r"[^ \t]+", - "IDENTIFIER": r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b", - "VERSION_PREFIX_TRAIL": r"\.\*", - "VERSION_LOCAL_LABEL_TRAIL": r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*", - "WS": r"[ \t]+", - "END": r"$", + 'AT': r'\@', + 'URL': r'[^ \t]+', + 'IDENTIFIER': r'\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b', + 'VERSION_PREFIX_TRAIL': r'\.\*', + 'VERSION_LOCAL_LABEL_TRAIL': r'\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*', + 'WS': r'[ \t]+', + 'END': r'$', } @@ -96,13 +103,13 @@ class Tokenizer: self, source: str, *, - rules: "Dict[str, Union[str, re.Pattern[str]]]", + rules: Dict[str, Union[str, re.Pattern[str]]], ) -> None: self.source = source - self.rules: Dict[str, re.Pattern[str]] = { + self.rules: dict[str, re.Pattern[str]] = { name: re.compile(pattern) for name, pattern in rules.items() } - self.next_token: Optional[Token] = None + self.next_token: Token | None = None self.position = 0 def consume(self, name: str) -> None: @@ -119,8 +126,8 @@ class Tokenizer: """ assert ( self.next_token is None - ), f"Cannot check for {name!r}, already have {self.next_token!r}" - assert name in self.rules, f"Unknown token name: {name!r}" + ), f'Cannot check for {name!r}, already have {self.next_token!r}' + assert name in self.rules, f'Unknown token name: {name!r}' expression = self.rules[name] @@ -137,7 +144,7 @@ class Tokenizer: The token is *not* read. """ if not self.check(name): - raise self.raise_syntax_error(f"Expected {expected}") + raise self.raise_syntax_error(f'Expected {expected}') return self.read() def read(self) -> Token: @@ -154,8 +161,8 @@ class Tokenizer: self, message: str, *, - span_start: Optional[int] = None, - span_end: Optional[int] = None, + span_start: int | None = None, + span_end: int | None = None, ) -> NoReturn: """Raise ParserSyntaxError at the given position.""" span = ( @@ -170,7 +177,7 @@ class Tokenizer: @contextlib.contextmanager def enclosing_tokens( - self, open_token: str, close_token: str, *, around: str + self, open_token: str, close_token: str, *, around: str, ) -> Iterator[None]: if self.check(open_token): open_position = self.position @@ -185,7 +192,7 @@ class Tokenizer: if not self.check(close_token): self.raise_syntax_error( - f"Expected matching {close_token} for {open_token}, after {around}", + f'Expected matching {close_token} for {open_token}, after {around}', span_start=open_position, ) diff --git a/.venv/lib/python3.10/site-packages/packaging/markers.py b/.venv/lib/python3.10/site-packages/packaging/markers.py index 8b98fca..af11fca 100644 --- a/.venv/lib/python3.10/site-packages/packaging/markers.py +++ b/.venv/lib/python3.10/site-packages/packaging/markers.py @@ -1,31 +1,37 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +from __future__ import annotations import operator import os import platform import sys -from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from typing import Any +from typing import Callable +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple +from typing import Union -from ._parser import ( - MarkerAtom, - MarkerList, - Op, - Value, - Variable, - parse_marker as _parse_marker, -) +from ._parser import MarkerAtom +from ._parser import MarkerList +from ._parser import Op +from ._parser import parse_marker as _parse_marker +from ._parser import Value +from ._parser import Variable from ._tokenizer import ParserSyntaxError -from .specifiers import InvalidSpecifier, Specifier +from .specifiers import InvalidSpecifier +from .specifiers import Specifier from .utils import canonicalize_name __all__ = [ - "InvalidMarker", - "UndefinedComparison", - "UndefinedEnvironmentName", - "Marker", - "default_environment", + 'InvalidMarker', + 'UndefinedComparison', + 'UndefinedEnvironmentName', + 'Marker', + 'default_environment', ] Operator = Callable[[str, str], bool] @@ -56,10 +62,10 @@ def _normalize_extra_values(results: Any) -> Any: """ if isinstance(results[0], tuple): lhs, op, rhs = results[0] - if isinstance(lhs, Variable) and lhs.value == "extra": + if isinstance(lhs, Variable) and lhs.value == 'extra': normalized_extra = canonicalize_name(rhs.value) rhs = Value(normalized_extra) - elif isinstance(rhs, Variable) and rhs.value == "extra": + elif isinstance(rhs, Variable) and rhs.value == 'extra': normalized_extra = canonicalize_name(lhs.value) lhs = Value(normalized_extra) results[0] = lhs, op, rhs @@ -67,7 +73,7 @@ def _normalize_extra_values(results: Any) -> Any: def _format_marker( - marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True + marker: list[str] | MarkerAtom | str, first: bool | None = True, ) -> str: assert isinstance(marker, (list, tuple, str)) @@ -77,65 +83,65 @@ def _format_marker( # the rest of this function so that we don't get extraneous () on the # outside. if ( - isinstance(marker, list) - and len(marker) == 1 - and isinstance(marker[0], (list, tuple)) + isinstance(marker, list) and + len(marker) == 1 and + isinstance(marker[0], (list, tuple)) ): return _format_marker(marker[0]) if isinstance(marker, list): inner = (_format_marker(m, first=False) for m in marker) if first: - return " ".join(inner) + return ' '.join(inner) else: - return "(" + " ".join(inner) + ")" + return '(' + ' '.join(inner) + ')' elif isinstance(marker, tuple): - return " ".join([m.serialize() for m in marker]) + return ' '.join([m.serialize() for m in marker]) else: return marker -_operators: Dict[str, Operator] = { - "in": lambda lhs, rhs: lhs in rhs, - "not in": lambda lhs, rhs: lhs not in rhs, - "<": operator.lt, - "<=": operator.le, - "==": operator.eq, - "!=": operator.ne, - ">=": operator.ge, - ">": operator.gt, +_operators: dict[str, Operator] = { + 'in': lambda lhs, rhs: lhs in rhs, + 'not in': lambda lhs, rhs: lhs not in rhs, + '<': operator.lt, + '<=': operator.le, + '==': operator.eq, + '!=': operator.ne, + '>=': operator.ge, + '>': operator.gt, } def _eval_op(lhs: str, op: Op, rhs: str) -> bool: try: - spec = Specifier("".join([op.serialize(), rhs])) + spec = Specifier(''.join([op.serialize(), rhs])) except InvalidSpecifier: pass else: return spec.contains(lhs, prereleases=True) - oper: Optional[Operator] = _operators.get(op.serialize()) + oper: Operator | None = _operators.get(op.serialize()) if oper is None: - raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") + raise UndefinedComparison(f'Undefined {op!r} on {lhs!r} and {rhs!r}.') return oper(lhs, rhs) -def _normalize(*values: str, key: str) -> Tuple[str, ...]: +def _normalize(*values: str, key: str) -> tuple[str, ...]: # PEP 685 – Comparison of extra names for optional distribution dependencies # https://peps.python.org/pep-0685/ # > When comparing extra names, tools MUST normalize the names being # > compared using the semantics outlined in PEP 503 for names - if key == "extra": + if key == 'extra': return tuple(canonicalize_name(v) for v in values) # other environment markers don't have such standards return values -def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool: - groups: List[List[bool]] = [[]] +def _evaluate_markers(markers: MarkerList, environment: dict[str, str]) -> bool: + groups: list[list[bool]] = [[]] for marker in markers: assert isinstance(marker, (list, tuple, str)) @@ -157,36 +163,36 @@ def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool: lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) groups[-1].append(_eval_op(lhs_value, op, rhs_value)) else: - assert marker in ["and", "or"] - if marker == "or": + assert marker in ['and', 'or'] + if marker == 'or': groups.append([]) return any(all(item) for item in groups) -def format_full_version(info: "sys._version_info") -> str: - version = "{0.major}.{0.minor}.{0.micro}".format(info) +def format_full_version(info: sys._version_info) -> str: + version = '{0.major}.{0.minor}.{0.micro}'.format(info) kind = info.releaselevel - if kind != "final": + if kind != 'final': version += kind[0] + str(info.serial) return version -def default_environment() -> Dict[str, str]: +def default_environment() -> dict[str, str]: iver = format_full_version(sys.implementation.version) implementation_name = sys.implementation.name return { - "implementation_name": implementation_name, - "implementation_version": iver, - "os_name": os.name, - "platform_machine": platform.machine(), - "platform_release": platform.release(), - "platform_system": platform.system(), - "platform_version": platform.version(), - "python_full_version": platform.python_version(), - "platform_python_implementation": platform.python_implementation(), - "python_version": ".".join(platform.python_version_tuple()[:2]), - "sys_platform": sys.platform, + 'implementation_name': implementation_name, + 'implementation_version': iver, + 'os_name': os.name, + 'platform_machine': platform.machine(), + 'platform_release': platform.release(), + 'platform_system': platform.system(), + 'platform_version': platform.version(), + 'python_full_version': platform.python_version(), + 'platform_python_implementation': platform.python_implementation(), + 'python_version': '.'.join(platform.python_version_tuple()[:2]), + 'sys_platform': sys.platform, } @@ -231,7 +237,7 @@ class Marker: return str(self) == str(other) - def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: + def evaluate(self, environment: dict[str, str] | None = None) -> bool: """Evaluate a marker. Return the boolean from evaluating the given marker against the @@ -241,12 +247,12 @@ class Marker: The environment is determined from the current Python process. """ current_environment = default_environment() - current_environment["extra"] = "" + current_environment['extra'] = '' if environment is not None: current_environment.update(environment) # The API used to allow setting extra to None. We need to handle this # case for backwards compatibility. - if current_environment["extra"] is None: - current_environment["extra"] = "" + if current_environment['extra'] is None: + current_environment['extra'] = '' return _evaluate_markers(self._markers, current_environment) diff --git a/.venv/lib/python3.10/site-packages/packaging/metadata.py b/.venv/lib/python3.10/site-packages/packaging/metadata.py index fb27493..57e0b3b 100644 --- a/.venv/lib/python3.10/site-packages/packaging/metadata.py +++ b/.venv/lib/python3.10/site-packages/packaging/metadata.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import email.feedparser import email.header import email.message @@ -5,22 +7,23 @@ import email.parser import email.policy import sys import typing -from typing import ( - Any, - Callable, - Dict, - Generic, - List, - Optional, - Tuple, - Type, - Union, - cast, -) +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import Generic +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import Union -from . import requirements, specifiers, utils, version as version_module +from . import requirements +from . import specifiers +from . import utils +from . import version as version_module -T = typing.TypeVar("T") +T = typing.TypeVar('T') if sys.version_info[:2] >= (3, 8): # pragma: no cover from typing import Literal, TypedDict else: # pragma: no cover @@ -52,14 +55,14 @@ except NameError: # pragma: no cover """ message: str - exceptions: List[Exception] + exceptions: list[Exception] - def __init__(self, message: str, exceptions: List[Exception]) -> None: + def __init__(self, message: str, exceptions: list[Exception]) -> None: self.message = message self.exceptions = exceptions def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" + return f'{self.__class__.__name__}({self.message!r}, {self.exceptions!r})' else: # pragma: no cover ExceptionGroup = ExceptionGroup @@ -100,32 +103,32 @@ class RawMetadata(TypedDict, total=False): metadata_version: str name: str version: str - platforms: List[str] + platforms: list[str] summary: str description: str - keywords: List[str] + keywords: list[str] home_page: str author: str author_email: str license: str # Metadata 1.1 - PEP 314 - supported_platforms: List[str] + supported_platforms: list[str] download_url: str - classifiers: List[str] - requires: List[str] - provides: List[str] - obsoletes: List[str] + classifiers: list[str] + requires: list[str] + provides: list[str] + obsoletes: list[str] # Metadata 1.2 - PEP 345 maintainer: str maintainer_email: str - requires_dist: List[str] - provides_dist: List[str] - obsoletes_dist: List[str] + requires_dist: list[str] + provides_dist: list[str] + obsoletes_dist: list[str] requires_python: str - requires_external: List[str] - project_urls: Dict[str, str] + requires_external: list[str] + project_urls: dict[str, str] # Metadata 2.0 # PEP 426 attempted to completely revamp the metadata format @@ -138,10 +141,10 @@ class RawMetadata(TypedDict, total=False): # Metadata 2.1 - PEP 566 description_content_type: str - provides_extra: List[str] + provides_extra: list[str] # Metadata 2.2 - PEP 643 - dynamic: List[str] + dynamic: list[str] # Metadata 2.3 - PEP 685 # No new fields were added in PEP 685, just some edge case were @@ -149,48 +152,48 @@ class RawMetadata(TypedDict, total=False): _STRING_FIELDS = { - "author", - "author_email", - "description", - "description_content_type", - "download_url", - "home_page", - "license", - "maintainer", - "maintainer_email", - "metadata_version", - "name", - "requires_python", - "summary", - "version", + 'author', + 'author_email', + 'description', + 'description_content_type', + 'download_url', + 'home_page', + 'license', + 'maintainer', + 'maintainer_email', + 'metadata_version', + 'name', + 'requires_python', + 'summary', + 'version', } _LIST_FIELDS = { - "classifiers", - "dynamic", - "obsoletes", - "obsoletes_dist", - "platforms", - "provides", - "provides_dist", - "provides_extra", - "requires", - "requires_dist", - "requires_external", - "supported_platforms", + 'classifiers', + 'dynamic', + 'obsoletes', + 'obsoletes_dist', + 'platforms', + 'provides', + 'provides_dist', + 'provides_extra', + 'requires', + 'requires_dist', + 'requires_external', + 'supported_platforms', } _DICT_FIELDS = { - "project_urls", + 'project_urls', } -def _parse_keywords(data: str) -> List[str]: +def _parse_keywords(data: str) -> list[str]: """Split a string of comma-separate keyboards into a list of keywords.""" - return [k.strip() for k in data.split(",")] + return [k.strip() for k in data.split(',')] -def _parse_project_urls(data: List[str]) -> Dict[str, str]: +def _parse_project_urls(data: list[str]) -> dict[str, str]: """Parse a list of label/URL string pairings separated by a comma.""" urls = {} for pair in data: @@ -211,8 +214,8 @@ def _parse_project_urls(data: List[str]) -> Dict[str, str]: # answer with what to do in that case. As such, we'll do the only # thing we can, which is treat the field as unparseable and add it # to our list of unparsed fields. - parts = [p.strip() for p in pair.split(",", 1)] - parts.extend([""] * (max(0, 2 - len(parts)))) # Ensure 2 items + parts = [p.strip() for p in pair.split(',', 1)] + parts.extend([''] * (max(0, 2 - len(parts)))) # Ensure 2 items # TODO: The spec doesn't say anything about if the keys should be # considered case sensitive or not... logically they should @@ -224,13 +227,13 @@ def _parse_project_urls(data: List[str]) -> Dict[str, str]: # The label already exists in our set of urls, so this field # is unparseable, and we can just add the whole thing to our # unparseable data and stop processing it. - raise KeyError("duplicate labels in project urls") + raise KeyError('duplicate labels in project urls') urls[label] = url return urls -def _get_payload(msg: email.message.Message, source: Union[bytes, str]) -> str: +def _get_payload(msg: email.message.Message, source: bytes | str) -> str: """Get the body of the message.""" # If our source is a str, then our caller has managed encodings for us, # and we don't need to deal with it. @@ -242,9 +245,9 @@ def _get_payload(msg: email.message.Message, source: Union[bytes, str]) -> str: else: bpayload: bytes = msg.get_payload(decode=True) try: - return bpayload.decode("utf8", "strict") + return bpayload.decode('utf8', 'strict') except UnicodeDecodeError: - raise ValueError("payload in an invalid encoding") + raise ValueError('payload in an invalid encoding') # The various parse_FORMAT functions here are intended to be as lenient as @@ -260,39 +263,39 @@ def _get_payload(msg: email.message.Message, source: Union[bytes, str]) -> str: # Map METADATA fields to RawMetadata. _EMAIL_TO_RAW_MAPPING = { - "author": "author", - "author-email": "author_email", - "classifier": "classifiers", - "description": "description", - "description-content-type": "description_content_type", - "download-url": "download_url", - "dynamic": "dynamic", - "home-page": "home_page", - "keywords": "keywords", - "license": "license", - "maintainer": "maintainer", - "maintainer-email": "maintainer_email", - "metadata-version": "metadata_version", - "name": "name", - "obsoletes": "obsoletes", - "obsoletes-dist": "obsoletes_dist", - "platform": "platforms", - "project-url": "project_urls", - "provides": "provides", - "provides-dist": "provides_dist", - "provides-extra": "provides_extra", - "requires": "requires", - "requires-dist": "requires_dist", - "requires-external": "requires_external", - "requires-python": "requires_python", - "summary": "summary", - "supported-platform": "supported_platforms", - "version": "version", + 'author': 'author', + 'author-email': 'author_email', + 'classifier': 'classifiers', + 'description': 'description', + 'description-content-type': 'description_content_type', + 'download-url': 'download_url', + 'dynamic': 'dynamic', + 'home-page': 'home_page', + 'keywords': 'keywords', + 'license': 'license', + 'maintainer': 'maintainer', + 'maintainer-email': 'maintainer_email', + 'metadata-version': 'metadata_version', + 'name': 'name', + 'obsoletes': 'obsoletes', + 'obsoletes-dist': 'obsoletes_dist', + 'platform': 'platforms', + 'project-url': 'project_urls', + 'provides': 'provides', + 'provides-dist': 'provides_dist', + 'provides-extra': 'provides_extra', + 'requires': 'requires', + 'requires-dist': 'requires_dist', + 'requires-external': 'requires_external', + 'requires-python': 'requires_python', + 'summary': 'summary', + 'supported-platform': 'supported_platforms', + 'version': 'version', } _RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()} -def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[str]]]: +def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]: """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``). This function returns a two-item tuple of dicts. The first dict is of @@ -308,8 +311,8 @@ def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[st included in this dict. """ - raw: Dict[str, Union[str, List[str], Dict[str, str]]] = {} - unparsed: Dict[str, List[str]] = {} + raw: dict[str, str | list[str] | dict[str, str]] = {} + unparsed: dict[str, list[str]] = {} if isinstance(data, str): parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data) @@ -357,16 +360,16 @@ def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[st # The Header object stores it's data as chunks, and each chunk # can be independently encoded, so we'll need to check each # of them. - chunks: List[Tuple[bytes, Optional[str]]] = [] + chunks: list[tuple[bytes, str | None]] = [] for bin, encoding in email.header.decode_header(h): try: - bin.decode("utf8", "strict") + bin.decode('utf8', 'strict') except UnicodeDecodeError: # Enable mojibake. - encoding = "latin1" + encoding = 'latin1' valid_encoding = False else: - encoding = "utf8" + encoding = 'utf8' chunks.append((bin, encoding)) # Turn our chunks back into a Header object, then let that @@ -416,7 +419,7 @@ def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[st # but it conceptually is a list of strings, and is serialized using # ", ".join(keywords), so we'll do some light data massaging to turn # this into what it logically is. - elif raw_name == "keywords" and len(value) == 1: + elif raw_name == 'keywords' and len(value) == 1: raw[raw_name] = _parse_keywords(value[0]) # Special Case: Project-URL # The project urls is implemented in the metadata spec as a list of @@ -427,7 +430,7 @@ def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[st # # We will do a little light data massaging to turn this into a map as # it logically should be. - elif raw_name == "project_urls": + elif raw_name == 'project_urls': try: raw[raw_name] = _parse_project_urls(value) except KeyError: @@ -444,22 +447,22 @@ def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[st try: payload = _get_payload(parsed, data) except ValueError: - unparsed.setdefault("description", []).append( - parsed.get_payload(decode=isinstance(data, bytes)) + unparsed.setdefault('description', []).append( + parsed.get_payload(decode=isinstance(data, bytes)), ) else: if payload: # Check to see if we've already got a description, if so then both # it, and this body move to unparseable. - if "description" in raw: - description_header = cast(str, raw.pop("description")) - unparsed.setdefault("description", []).extend( - [description_header, payload] + if 'description' in raw: + description_header = cast(str, raw.pop('description')) + unparsed.setdefault('description', []).extend( + [description_header, payload], ) - elif "description" in unparsed: - unparsed["description"].append(payload) + elif 'description' in unparsed: + unparsed['description'].append(payload) else: - raw["description"] = payload + raw['description'] = payload # We need to cast our `raw` to a metadata, because a TypedDict only support # literal key names, but we're computing our key names on purpose, but the @@ -472,10 +475,10 @@ _NOT_FOUND = object() # Keep the two values in sync. -_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] -_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] +_VALID_METADATA_VERSIONS = ['1.0', '1.1', '1.2', '2.1', '2.2', '2.3'] +_MetadataVersion = Literal['1.0', '1.1', '1.2', '2.1', '2.2', '2.3'] -_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"]) +_REQUIRED_ATTRS = frozenset(['metadata_version', 'name', 'version']) class _Validator(Generic[T]): @@ -495,15 +498,15 @@ class _Validator(Generic[T]): def __init__( self, *, - added: _MetadataVersion = "1.0", + added: _MetadataVersion = '1.0', ) -> None: self.added = added - def __set_name__(self, _owner: "Metadata", name: str) -> None: + def __set_name__(self, _owner: Metadata, name: str) -> None: self.name = name self.raw_name = _RAW_TO_EMAIL_MAPPING[name] - def __get__(self, instance: "Metadata", _owner: Type["Metadata"]) -> T: + def __get__(self, instance: Metadata, _owner: type[Metadata]) -> T: # With Python 3.8, the caching can be replaced with functools.cached_property(). # No need to check the cache as attribute lookup will resolve into the # instance's __dict__ before __get__ is called. @@ -516,7 +519,7 @@ class _Validator(Generic[T]): # converters never have to deal with the None union. if self.name in _REQUIRED_ATTRS or value is not None: try: - converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}") + converter: Callable[[Any], T] = getattr(self, f'_process_{self.name}') except AttributeError: pass else: @@ -531,10 +534,10 @@ class _Validator(Generic[T]): return cast(T, value) def _invalid_metadata( - self, msg: str, cause: Optional[Exception] = None + self, msg: str, cause: Exception | None = None, ) -> InvalidMetadata: exc = InvalidMetadata( - self.raw_name, msg.format_map({"field": repr(self.raw_name)}) + self.raw_name, msg.format_map({'field': repr(self.raw_name)}), ) exc.__cause__ = cause return exc @@ -542,91 +545,91 @@ class _Validator(Generic[T]): def _process_metadata_version(self, value: str) -> _MetadataVersion: # Implicitly makes Metadata-Version required. if value not in _VALID_METADATA_VERSIONS: - raise self._invalid_metadata(f"{value!r} is not a valid metadata version") + raise self._invalid_metadata(f'{value!r} is not a valid metadata version') return cast(_MetadataVersion, value) def _process_name(self, value: str) -> str: if not value: - raise self._invalid_metadata("{field} is a required field") + raise self._invalid_metadata('{field} is a required field') # Validate the name as a side-effect. try: utils.canonicalize_name(value, validate=True) except utils.InvalidName as exc: raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc + f'{value!r} is invalid for {{field}}', cause=exc, ) else: return value def _process_version(self, value: str) -> version_module.Version: if not value: - raise self._invalid_metadata("{field} is a required field") + raise self._invalid_metadata('{field} is a required field') try: return version_module.parse(value) except version_module.InvalidVersion as exc: raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc + f'{value!r} is invalid for {{field}}', cause=exc, ) def _process_summary(self, value: str) -> str: """Check the field contains no newlines.""" - if "\n" in value: - raise self._invalid_metadata("{field} must be a single line") + if '\n' in value: + raise self._invalid_metadata('{field} must be a single line') return value def _process_description_content_type(self, value: str) -> str: - content_types = {"text/plain", "text/x-rst", "text/markdown"} + content_types = {'text/plain', 'text/x-rst', 'text/markdown'} message = email.message.EmailMessage() - message["content-type"] = value + message['content-type'] = value content_type, parameters = ( # Defaults to `text/plain` if parsing failed. message.get_content_type().lower(), - message["content-type"].params, + message['content-type'].params, ) # Check if content-type is valid or defaulted to `text/plain` and thus was # not parseable. if content_type not in content_types or content_type not in value.lower(): raise self._invalid_metadata( - f"{{field}} must be one of {list(content_types)}, not {value!r}" + f'{{field}} must be one of {list(content_types)}, not {value!r}', ) - charset = parameters.get("charset", "UTF-8") - if charset != "UTF-8": + charset = parameters.get('charset', 'UTF-8') + if charset != 'UTF-8': raise self._invalid_metadata( - f"{{field}} can only specify the UTF-8 charset, not {list(charset)}" + f'{{field}} can only specify the UTF-8 charset, not {list(charset)}', ) - markdown_variants = {"GFM", "CommonMark"} - variant = parameters.get("variant", "GFM") # Use an acceptable default. - if content_type == "text/markdown" and variant not in markdown_variants: + markdown_variants = {'GFM', 'CommonMark'} + variant = parameters.get('variant', 'GFM') # Use an acceptable default. + if content_type == 'text/markdown' and variant not in markdown_variants: raise self._invalid_metadata( - f"valid Markdown variants for {{field}} are {list(markdown_variants)}, " - f"not {variant!r}", + f'valid Markdown variants for {{field}} are {list(markdown_variants)}, ' + f'not {variant!r}', ) return value - def _process_dynamic(self, value: List[str]) -> List[str]: + def _process_dynamic(self, value: list[str]) -> list[str]: for dynamic_field in map(str.lower, value): - if dynamic_field in {"name", "version", "metadata-version"}: + if dynamic_field in {'name', 'version', 'metadata-version'}: raise self._invalid_metadata( - f"{value!r} is not allowed as a dynamic field" + f'{value!r} is not allowed as a dynamic field', ) elif dynamic_field not in _EMAIL_TO_RAW_MAPPING: - raise self._invalid_metadata(f"{value!r} is not a valid dynamic field") + raise self._invalid_metadata(f'{value!r} is not a valid dynamic field') return list(map(str.lower, value)) def _process_provides_extra( self, - value: List[str], - ) -> List[utils.NormalizedName]: + value: list[str], + ) -> list[utils.NormalizedName]: normalized_names = [] try: for name in value: normalized_names.append(utils.canonicalize_name(name, validate=True)) except utils.InvalidName as exc: raise self._invalid_metadata( - f"{name!r} is invalid for {{field}}", cause=exc + f'{name!r} is invalid for {{field}}', cause=exc, ) else: return normalized_names @@ -636,19 +639,19 @@ class _Validator(Generic[T]): return specifiers.SpecifierSet(value) except specifiers.InvalidSpecifier as exc: raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc + f'{value!r} is invalid for {{field}}', cause=exc, ) def _process_requires_dist( self, - value: List[str], - ) -> List[requirements.Requirement]: + value: list[str], + ) -> list[requirements.Requirement]: reqs = [] try: for req in value: reqs.append(requirements.Requirement(req)) except requirements.InvalidRequirement as exc: - raise self._invalid_metadata(f"{req!r} is invalid for {{field}}", cause=exc) + raise self._invalid_metadata(f'{req!r} is invalid for {{field}}', cause=exc) else: return reqs @@ -665,7 +668,7 @@ class Metadata: _raw: RawMetadata @classmethod - def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> "Metadata": + def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata: """Create an instance from :class:`RawMetadata`. If *validate* is true, all metadata will be validated. All exceptions @@ -675,7 +678,7 @@ class Metadata: ins._raw = data.copy() # Mutations occur due to caching enriched values. if validate: - exceptions: List[Exception] = [] + exceptions: list[Exception] = [] try: metadata_version = ins.metadata_version metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version) @@ -687,7 +690,7 @@ class Metadata: # fields (so their absence can be reported). fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS # Remove fields that have already been checked. - fields_to_check -= {"metadata_version"} + fields_to_check -= {'metadata_version'} for key in fields_to_check: try: @@ -697,18 +700,18 @@ class Metadata: try: field_metadata_version = cls.__dict__[key].added except KeyError: - exc = InvalidMetadata(key, f"unrecognized field: {key!r}") + exc = InvalidMetadata(key, f'unrecognized field: {key!r}') exceptions.append(exc) continue field_age = _VALID_METADATA_VERSIONS.index( - field_metadata_version + field_metadata_version, ) if field_age > metadata_age: field = _RAW_TO_EMAIL_MAPPING[key] exc = InvalidMetadata( field, - "{field} introduced in metadata version " - "{field_metadata_version}, not {metadata_version}", + '{field} introduced in metadata version ' + '{field_metadata_version}, not {metadata_version}', ) exceptions.append(exc) continue @@ -717,14 +720,14 @@ class Metadata: exceptions.append(exc) if exceptions: - raise ExceptionGroup("invalid metadata", exceptions) + raise ExceptionGroup('invalid metadata', exceptions) return ins @classmethod def from_email( - cls, data: Union[bytes, str], *, validate: bool = True - ) -> "Metadata": + cls, data: bytes | str, *, validate: bool = True, + ) -> Metadata: """Parse metadata from email headers. If *validate* is true, the metadata will be validated. All exceptions @@ -736,19 +739,19 @@ class Metadata: exceptions: list[Exception] = [] for unparsed_key in unparsed: if unparsed_key in _EMAIL_TO_RAW_MAPPING: - message = f"{unparsed_key!r} has invalid data" + message = f'{unparsed_key!r} has invalid data' else: - message = f"unrecognized field: {unparsed_key!r}" + message = f'unrecognized field: {unparsed_key!r}' exceptions.append(InvalidMetadata(unparsed_key, message)) if exceptions: - raise ExceptionGroup("unparsed", exceptions) + raise ExceptionGroup('unparsed', exceptions) try: return cls.from_raw(raw, validate=validate) except ExceptionGroup as exc_group: raise ExceptionGroup( - "invalid or unparsed metadata", exc_group.exceptions + 'invalid or unparsed metadata', exc_group.exceptions, ) from None metadata_version: _Validator[_MetadataVersion] = _Validator() @@ -760,66 +763,66 @@ class Metadata: *validate* parameter)""" version: _Validator[version_module.Version] = _Validator() """:external:ref:`core-metadata-version` (required)""" - dynamic: _Validator[Optional[List[str]]] = _Validator( - added="2.2", + dynamic: _Validator[list[str] | None] = _Validator( + added='2.2', ) """:external:ref:`core-metadata-dynamic` (validated against core metadata field names and lowercased)""" - platforms: _Validator[Optional[List[str]]] = _Validator() + platforms: _Validator[list[str] | None] = _Validator() """:external:ref:`core-metadata-platform`""" - supported_platforms: _Validator[Optional[List[str]]] = _Validator(added="1.1") + supported_platforms: _Validator[list[str] | None] = _Validator(added='1.1') """:external:ref:`core-metadata-supported-platform`""" - summary: _Validator[Optional[str]] = _Validator() + summary: _Validator[str | None] = _Validator() """:external:ref:`core-metadata-summary` (validated to contain no newlines)""" - description: _Validator[Optional[str]] = _Validator() # TODO 2.1: can be in body + description: _Validator[str | None] = _Validator() # TODO 2.1: can be in body """:external:ref:`core-metadata-description`""" - description_content_type: _Validator[Optional[str]] = _Validator(added="2.1") + description_content_type: _Validator[str | None] = _Validator(added='2.1') """:external:ref:`core-metadata-description-content-type` (validated)""" - keywords: _Validator[Optional[List[str]]] = _Validator() + keywords: _Validator[list[str] | None] = _Validator() """:external:ref:`core-metadata-keywords`""" - home_page: _Validator[Optional[str]] = _Validator() + home_page: _Validator[str | None] = _Validator() """:external:ref:`core-metadata-home-page`""" - download_url: _Validator[Optional[str]] = _Validator(added="1.1") + download_url: _Validator[str | None] = _Validator(added='1.1') """:external:ref:`core-metadata-download-url`""" - author: _Validator[Optional[str]] = _Validator() + author: _Validator[str | None] = _Validator() """:external:ref:`core-metadata-author`""" - author_email: _Validator[Optional[str]] = _Validator() + author_email: _Validator[str | None] = _Validator() """:external:ref:`core-metadata-author-email`""" - maintainer: _Validator[Optional[str]] = _Validator(added="1.2") + maintainer: _Validator[str | None] = _Validator(added='1.2') """:external:ref:`core-metadata-maintainer`""" - maintainer_email: _Validator[Optional[str]] = _Validator(added="1.2") + maintainer_email: _Validator[str | None] = _Validator(added='1.2') """:external:ref:`core-metadata-maintainer-email`""" - license: _Validator[Optional[str]] = _Validator() + license: _Validator[str | None] = _Validator() """:external:ref:`core-metadata-license`""" - classifiers: _Validator[Optional[List[str]]] = _Validator(added="1.1") + classifiers: _Validator[list[str] | None] = _Validator(added='1.1') """:external:ref:`core-metadata-classifier`""" - requires_dist: _Validator[Optional[List[requirements.Requirement]]] = _Validator( - added="1.2" + requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator( + added='1.2', ) """:external:ref:`core-metadata-requires-dist`""" - requires_python: _Validator[Optional[specifiers.SpecifierSet]] = _Validator( - added="1.2" + requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator( + added='1.2', ) """:external:ref:`core-metadata-requires-python`""" # Because `Requires-External` allows for non-PEP 440 version specifiers, we # don't do any processing on the values. - requires_external: _Validator[Optional[List[str]]] = _Validator(added="1.2") + requires_external: _Validator[list[str] | None] = _Validator(added='1.2') """:external:ref:`core-metadata-requires-external`""" - project_urls: _Validator[Optional[Dict[str, str]]] = _Validator(added="1.2") + project_urls: _Validator[dict[str, str] | None] = _Validator(added='1.2') """:external:ref:`core-metadata-project-url`""" # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation # regardless of metadata version. - provides_extra: _Validator[Optional[List[utils.NormalizedName]]] = _Validator( - added="2.1", + provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator( + added='2.1', ) """:external:ref:`core-metadata-provides-extra`""" - provides_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2") + provides_dist: _Validator[list[str] | None] = _Validator(added='1.2') """:external:ref:`core-metadata-provides-dist`""" - obsoletes_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2") + obsoletes_dist: _Validator[list[str] | None] = _Validator(added='1.2') """:external:ref:`core-metadata-obsoletes-dist`""" - requires: _Validator[Optional[List[str]]] = _Validator(added="1.1") + requires: _Validator[list[str] | None] = _Validator(added='1.1') """``Requires`` (deprecated)""" - provides: _Validator[Optional[List[str]]] = _Validator(added="1.1") + provides: _Validator[list[str] | None] = _Validator(added='1.1') """``Provides`` (deprecated)""" - obsoletes: _Validator[Optional[List[str]]] = _Validator(added="1.1") + obsoletes: _Validator[list[str] | None] = _Validator(added='1.1') """``Obsoletes`` (deprecated)""" diff --git a/.venv/lib/python3.10/site-packages/packaging/requirements.py b/.venv/lib/python3.10/site-packages/packaging/requirements.py index bdc43a7..4f530cd 100644 --- a/.venv/lib/python3.10/site-packages/packaging/requirements.py +++ b/.venv/lib/python3.10/site-packages/packaging/requirements.py @@ -1,12 +1,17 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +from __future__ import annotations -from typing import Any, Iterator, Optional, Set +from typing import Any +from typing import Iterator +from typing import Optional +from typing import Set from ._parser import parse_requirement as _parse_requirement from ._tokenizer import ParserSyntaxError -from .markers import Marker, _normalize_extra_values +from .markers import _normalize_extra_values +from .markers import Marker from .specifiers import SpecifierSet from .utils import canonicalize_name @@ -37,10 +42,10 @@ class Requirement: raise InvalidRequirement(str(e)) from e self.name: str = parsed.name - self.url: Optional[str] = parsed.url or None - self.extras: Set[str] = set(parsed.extras or []) + self.url: str | None = parsed.url or None + self.extras: set[str] = set(parsed.extras or []) self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) - self.marker: Optional[Marker] = None + self.marker: Marker | None = None if parsed.marker is not None: self.marker = Marker.__new__(Marker) self.marker._markers = _normalize_extra_values(parsed.marker) @@ -49,22 +54,22 @@ class Requirement: yield name if self.extras: - formatted_extras = ",".join(sorted(self.extras)) - yield f"[{formatted_extras}]" + formatted_extras = ','.join(sorted(self.extras)) + yield f'[{formatted_extras}]' if self.specifier: yield str(self.specifier) if self.url: - yield f"@ {self.url}" + yield f'@ {self.url}' if self.marker: - yield " " + yield ' ' if self.marker: - yield f"; {self.marker}" + yield f'; {self.marker}' def __str__(self) -> str: - return "".join(self._iter_parts(self.name)) + return ''.join(self._iter_parts(self.name)) def __repr__(self) -> str: return f"" @@ -74,7 +79,7 @@ class Requirement: ( self.__class__.__name__, *self._iter_parts(canonicalize_name(self.name)), - ) + ), ) def __eq__(self, other: Any) -> bool: @@ -82,9 +87,9 @@ class Requirement: return NotImplemented return ( - canonicalize_name(self.name) == canonicalize_name(other.name) - and self.extras == other.extras - and self.specifier == other.specifier - and self.url == other.url - and self.marker == other.marker + canonicalize_name(self.name) == canonicalize_name(other.name) and + self.extras == other.extras and + self.specifier == other.specifier and + self.url == other.url and + self.marker == other.marker ) diff --git a/.venv/lib/python3.10/site-packages/packaging/specifiers.py b/.venv/lib/python3.10/site-packages/packaging/specifiers.py index 2d015ba..50b2b17 100644 --- a/.venv/lib/python3.10/site-packages/packaging/specifiers.py +++ b/.venv/lib/python3.10/site-packages/packaging/specifiers.py @@ -7,17 +7,25 @@ from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier from packaging.version import Version """ +from __future__ import annotations import abc import itertools import re -from typing import Callable, Iterable, Iterator, List, Optional, Tuple, TypeVar, Union +from typing import Callable +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Tuple +from typing import TypeVar +from typing import Union from .utils import canonicalize_version from .version import Version UnparsedVersion = Union[Version, str] -UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) +UnparsedVersionVar = TypeVar('UnparsedVersionVar', bound=UnparsedVersion) CallableOperator = Callable[[Version, str], bool] @@ -64,7 +72,7 @@ class BaseSpecifier(metaclass=abc.ABCMeta): @property @abc.abstractmethod - def prereleases(self) -> Optional[bool]: + def prereleases(self) -> bool | None: """Whether or not pre-releases as a whole are allowed. This can be set to either ``True`` or ``False`` to explicitly enable or disable @@ -79,14 +87,14 @@ class BaseSpecifier(metaclass=abc.ABCMeta): """ @abc.abstractmethod - def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: + def contains(self, item: str, prereleases: bool | None = None) -> bool: """ Determines if the given item is contained within this specifier. """ @abc.abstractmethod def filter( - self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None, ) -> Iterator[UnparsedVersionVar]: """ Takes an iterable of items and filters them so that only items which @@ -202,22 +210,22 @@ class Specifier(BaseSpecifier): """ _regex = re.compile( - r"^\s*" + _operator_regex_str + _version_regex_str + r"\s*$", + r'^\s*' + _operator_regex_str + _version_regex_str + r'\s*$', re.VERBOSE | re.IGNORECASE, ) _operators = { - "~=": "compatible", - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", + '~=': 'compatible', + '==': 'equal', + '!=': 'not_equal', + '<=': 'less_than_equal', + '>=': 'greater_than_equal', + '<': 'less_than', + '>': 'greater_than', + '===': 'arbitrary', } - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + def __init__(self, spec: str = '', prereleases: bool | None = None) -> None: """Initialize a Specifier instance. :param spec: @@ -234,9 +242,9 @@ class Specifier(BaseSpecifier): if not match: raise InvalidSpecifier(f"Invalid specifier: '{spec}'") - self._spec: Tuple[str, str] = ( - match.group("operator").strip(), - match.group("version").strip(), + self._spec: tuple[str, str] = ( + match.group('operator').strip(), + match.group('version').strip(), ) # Store whether or not this Specifier should accept prereleases @@ -254,10 +262,10 @@ class Specifier(BaseSpecifier): # operators, and if they are if they are including an explicit # prerelease. operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: + if operator in ['==', '>=', '<=', '~=', '===']: # The == specifier can include a trailing .*, if it does we # want to remove before parsing. - if operator == "==" and version.endswith(".*"): + if operator == '==' and version.endswith('.*'): version = version[:-2] # Parse the version, and if it is a pre-release than this @@ -300,12 +308,12 @@ class Specifier(BaseSpecifier): =1.0.0', prereleases=True)> """ pre = ( - f", prereleases={self.prereleases!r}" + f', prereleases={self.prereleases!r}' if self._prereleases is not None - else "" + else '' ) - return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + return f'<{self.__class__.__name__}({str(self)!r}{pre})>' def __str__(self) -> str: """A string representation of the Specifier that can be round-tripped. @@ -315,13 +323,13 @@ class Specifier(BaseSpecifier): >>> str(Specifier('>=1.0.0', prereleases=False)) '>=1.0.0' """ - return "{}{}".format(*self._spec) + return '{}{}'.format(*self._spec) @property - def _canonical_spec(self) -> Tuple[str, str]: + def _canonical_spec(self) -> tuple[str, str]: canonical_version = canonicalize_version( self._spec[1], - strip_trailing_zero=(self._spec[0] != "~="), + strip_trailing_zero=(self._spec[0] != '~='), ) return self._spec[0], canonical_version @@ -359,7 +367,7 @@ class Specifier(BaseSpecifier): def _get_operator(self, op: str) -> CallableOperator: operator_callable: CallableOperator = getattr( - self, f"_compare_{self._operators[op]}" + self, f'_compare_{self._operators[op]}', ) return operator_callable @@ -374,23 +382,23 @@ class Specifier(BaseSpecifier): # We want everything but the last item in the version, but we want to # ignore suffix segments. prefix = _version_join( - list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] + list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1], ) # Add the prefix notation to the end of our string - prefix += ".*" + prefix += '.*' - return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( - prospective, prefix + return self._get_operator('>=')(prospective, spec) and self._get_operator('==')( + prospective, prefix, ) def _compare_equal(self, prospective: Version, spec: str) -> bool: # We need special logic to handle prefix matching - if spec.endswith(".*"): + if spec.endswith('.*'): # In the case of prefix matching we want to ignore local segment. normalized_prospective = canonicalize_version( - prospective.public, strip_trailing_zero=False + prospective.public, strip_trailing_zero=False, ) # Get the normalized version string ignoring the trailing .* normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) @@ -501,7 +509,7 @@ class Specifier(BaseSpecifier): def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: return str(prospective).lower() == str(spec).lower() - def __contains__(self, item: Union[str, Version]) -> bool: + def __contains__(self, item: str | Version) -> bool: """Return whether or not the item is contained in this specifier. :param item: The item to check for. @@ -523,7 +531,7 @@ class Specifier(BaseSpecifier): return self.contains(item) def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None + self, item: UnparsedVersion, prereleases: bool | None = None, ) -> bool: """Return whether or not the item is contained in this specifier. @@ -569,7 +577,7 @@ class Specifier(BaseSpecifier): return operator_callable(normalized_item, self.version) def filter( - self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None, ) -> Iterator[UnparsedVersionVar]: """Filter items in the given iterable, that match the specifier. @@ -601,7 +609,7 @@ class Specifier(BaseSpecifier): yielded = False found_prereleases = [] - kw = {"prereleases": prereleases if prereleases is not None else True} + kw = {'prereleases': prereleases if prereleases is not None else True} # Attempt to iterate over all the values in the iterable and if any of # them match, yield them. @@ -630,10 +638,10 @@ class Specifier(BaseSpecifier): yield version -_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") +_prefix_regex = re.compile(r'^([0-9]+)((?:a|b|c|rc)[0-9]+)$') -def _version_split(version: str) -> List[str]: +def _version_split(version: str) -> list[str]: """Split version into components. The split components are intended for version comparison. The logic does @@ -641,12 +649,12 @@ def _version_split(version: str) -> List[str]: components back with :func:`_version_join` may not produce the original version string. """ - result: List[str] = [] + result: list[str] = [] - epoch, _, rest = version.rpartition("!") - result.append(epoch or "0") + epoch, _, rest = version.rpartition('!') + result.append(epoch or '0') - for item in rest.split("."): + for item in rest.split('.'): match = _prefix_regex.search(item) if match: result.extend(match.groups()) @@ -655,7 +663,7 @@ def _version_split(version: str) -> List[str]: return result -def _version_join(components: List[str]) -> str: +def _version_join(components: list[str]) -> str: """Join split version components into a version string. This function assumes the input came from :func:`_version_split`, where the @@ -668,11 +676,11 @@ def _version_join(components: List[str]) -> str: def _is_not_suffix(segment: str) -> bool: return not any( - segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + segment.startswith(prefix) for prefix in ('dev', 'a', 'b', 'rc', 'post') ) -def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: +def _pad_version(left: list[str], right: list[str]) -> tuple[list[str], list[str]]: left_split, right_split = [], [] # Get the release segment of our versions @@ -680,12 +688,12 @@ def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) # Get the rest of our versions - left_split.append(left[len(left_split[0]) :]) - right_split.append(right[len(right_split[0]) :]) + left_split.append(left[len(left_split[0]):]) + right_split.append(right[len(right_split[0]):]) # Insert our padding - left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) - right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) + left_split.insert(1, ['0'] * max(0, len(right_split[0]) - len(left_split[0]))) + right_split.insert(1, ['0'] * max(0, len(left_split[0]) - len(right_split[0]))) return ( list(itertools.chain.from_iterable(left_split)), @@ -701,7 +709,7 @@ class SpecifierSet(BaseSpecifier): """ def __init__( - self, specifiers: str = "", prereleases: Optional[bool] = None + self, specifiers: str = '', prereleases: bool | None = None, ) -> None: """Initialize a SpecifierSet instance. @@ -720,7 +728,7 @@ class SpecifierSet(BaseSpecifier): # Split on `,` to break each individual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. - split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + split_specifiers = [s.strip() for s in specifiers.split(',') if s.strip()] # Make each individual specifier a Specifier and save in a frozen set for later. self._specs = frozenset(map(Specifier, split_specifiers)) @@ -730,7 +738,7 @@ class SpecifierSet(BaseSpecifier): self._prereleases = prereleases @property - def prereleases(self) -> Optional[bool]: + def prereleases(self) -> bool | None: # If we have been given an explicit prerelease modifier, then we'll # pass that through here. if self._prereleases is not None: @@ -764,12 +772,12 @@ class SpecifierSet(BaseSpecifier): =1.0.0', prereleases=True)> """ pre = ( - f", prereleases={self.prereleases!r}" + f', prereleases={self.prereleases!r}' if self._prereleases is not None - else "" + else '' ) - return f"" + return f'' def __str__(self) -> str: """A string representation of the specifier set that can be round-tripped. @@ -782,12 +790,12 @@ class SpecifierSet(BaseSpecifier): >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) '!=1.0.1,>=1.0.0' """ - return ",".join(sorted(str(s) for s in self._specs)) + return ','.join(sorted(str(s) for s in self._specs)) def __hash__(self) -> int: return hash(self._specs) - def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + def __and__(self, other: SpecifierSet | str) -> SpecifierSet: """Return a SpecifierSet which is a combination of the two sets. :param other: The other object to combine with. @@ -813,8 +821,8 @@ class SpecifierSet(BaseSpecifier): specifier._prereleases = self._prereleases else: raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease " - "overrides." + 'Cannot combine SpecifierSets with True and False prerelease ' + 'overrides.', ) return specifier @@ -883,8 +891,8 @@ class SpecifierSet(BaseSpecifier): def contains( self, item: UnparsedVersion, - prereleases: Optional[bool] = None, - installed: Optional[bool] = None, + prereleases: bool | None = None, + installed: bool | None = None, ) -> bool: """Return whether or not the item is contained in this SpecifierSet. @@ -938,7 +946,7 @@ class SpecifierSet(BaseSpecifier): return all(s.contains(item, prereleases=prereleases) for s in self._specs) def filter( - self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None, ) -> Iterator[UnparsedVersionVar]: """Filter items in the given iterable, that match the specifiers in this set. @@ -995,8 +1003,8 @@ class SpecifierSet(BaseSpecifier): # which will filter out any pre-releases, unless there are no final # releases. else: - filtered: List[UnparsedVersionVar] = [] - found_prereleases: List[UnparsedVersionVar] = [] + filtered: list[UnparsedVersionVar] = [] + found_prereleases: list[UnparsedVersionVar] = [] for item in iterable: parsed_version = _coerce_version(item) diff --git a/.venv/lib/python3.10/site-packages/packaging/tags.py b/.venv/lib/python3.10/site-packages/packaging/tags.py index 89f1926..534326b 100644 --- a/.venv/lib/python3.10/site-packages/packaging/tags.py +++ b/.venv/lib/python3.10/site-packages/packaging/tags.py @@ -1,6 +1,7 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +from __future__ import annotations import logging import platform @@ -10,36 +11,35 @@ import subprocess import sys import sysconfig from importlib.machinery import EXTENSION_SUFFIXES -from typing import ( - Dict, - FrozenSet, - Iterable, - Iterator, - List, - Optional, - Sequence, - Tuple, - Union, - cast, -) +from typing import cast +from typing import Dict +from typing import FrozenSet +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import Union -from . import _manylinux, _musllinux +from . import _manylinux +from . import _musllinux logger = logging.getLogger(__name__) PythonVersion = Sequence[int] MacVersion = Tuple[int, int] -INTERPRETER_SHORT_NAMES: Dict[str, str] = { - "python": "py", # Generic. - "cpython": "cp", - "pypy": "pp", - "ironpython": "ip", - "jython": "jy", +INTERPRETER_SHORT_NAMES: dict[str, str] = { + 'python': 'py', # Generic. + 'cpython': 'cp', + 'pypy': 'pp', + 'ironpython': 'ip', + 'jython': 'jy', } -_32_BIT_INTERPRETER = struct.calcsize("P") == 4 +_32_BIT_INTERPRETER = struct.calcsize('P') == 4 class Tag: @@ -50,7 +50,7 @@ class Tag: is also supported. """ - __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] + __slots__ = ['_interpreter', '_abi', '_platform', '_hash'] def __init__(self, interpreter: str, abi: str, platform: str) -> None: self._interpreter = interpreter.lower() @@ -80,23 +80,23 @@ class Tag: return NotImplemented return ( - (self._hash == other._hash) # Short-circuit ASAP for perf reasons. - and (self._platform == other._platform) - and (self._abi == other._abi) - and (self._interpreter == other._interpreter) + (self._hash == other._hash) and # Short-circuit ASAP for perf reasons. + (self._platform == other._platform) and + (self._abi == other._abi) and + (self._interpreter == other._interpreter) ) def __hash__(self) -> int: return self._hash def __str__(self) -> str: - return f"{self._interpreter}-{self._abi}-{self._platform}" + return f'{self._interpreter}-{self._abi}-{self._platform}' def __repr__(self) -> str: - return f"<{self} @ {id(self)}>" + return f'<{self} @ {id(self)}>' -def parse_tag(tag: str) -> FrozenSet[Tag]: +def parse_tag(tag: str) -> frozenset[Tag]: """ Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. @@ -104,28 +104,28 @@ def parse_tag(tag: str) -> FrozenSet[Tag]: compressed tag set. """ tags = set() - interpreters, abis, platforms = tag.split("-") - for interpreter in interpreters.split("."): - for abi in abis.split("."): - for platform_ in platforms.split("."): + interpreters, abis, platforms = tag.split('-') + for interpreter in interpreters.split('.'): + for abi in abis.split('.'): + for platform_ in platforms.split('.'): tags.add(Tag(interpreter, abi, platform_)) return frozenset(tags) -def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: - value: Union[int, str, None] = sysconfig.get_config_var(name) +def _get_config_var(name: str, warn: bool = False) -> int | str | None: + value: int | str | None = sysconfig.get_config_var(name) if value is None and warn: logger.debug( - "Config variable '%s' is unset, Python ABI tag may be incorrect", name + "Config variable '%s' is unset, Python ABI tag may be incorrect", name, ) return value def _normalize_string(string: str) -> str: - return string.replace(".", "_").replace("-", "_").replace(" ", "_") + return string.replace('.', '_').replace('-', '_').replace(' ', '_') -def _is_threaded_cpython(abis: List[str]) -> bool: +def _is_threaded_cpython(abis: list[str]) -> bool: """ Determine if the ABI corresponds to a threaded (`--disable-gil`) build. @@ -134,11 +134,11 @@ def _is_threaded_cpython(abis: List[str]) -> bool: if len(abis) == 0: return False # expect e.g., cp313 - m = re.match(r"cp\d+(.*)", abis[0]) + m = re.match(r'cp\d+(.*)', abis[0]) if not m: return False abiflags = m.group(1) - return "t" in abiflags + return 't' in abiflags def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool: @@ -151,43 +151,43 @@ def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool: return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading -def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]: py_version = tuple(py_version) # To allow for version comparison. abis = [] version = _version_nodot(py_version[:2]) - threading = debug = pymalloc = ucs4 = "" - with_debug = _get_config_var("Py_DEBUG", warn) - has_refcount = hasattr(sys, "gettotalrefcount") + threading = debug = pymalloc = ucs4 = '' + with_debug = _get_config_var('Py_DEBUG', warn) + has_refcount = hasattr(sys, 'gettotalrefcount') # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled # extension modules is the best option. # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 - has_ext = "_d.pyd" in EXTENSION_SUFFIXES + has_ext = '_d.pyd' in EXTENSION_SUFFIXES if with_debug or (with_debug is None and (has_refcount or has_ext)): - debug = "d" - if py_version >= (3, 13) and _get_config_var("Py_GIL_DISABLED", warn): - threading = "t" + debug = 'd' + if py_version >= (3, 13) and _get_config_var('Py_GIL_DISABLED', warn): + threading = 't' if py_version < (3, 8): - with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) + with_pymalloc = _get_config_var('WITH_PYMALLOC', warn) if with_pymalloc or with_pymalloc is None: - pymalloc = "m" + pymalloc = 'm' if py_version < (3, 3): - unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) + unicode_size = _get_config_var('Py_UNICODE_SIZE', warn) if unicode_size == 4 or ( unicode_size is None and sys.maxunicode == 0x10FFFF ): - ucs4 = "u" + ucs4 = 'u' elif debug: # Debug builds can also load "normal" extension modules. # We can also assume no UCS-4 or pymalloc requirement. - abis.append(f"cp{version}{threading}") - abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}") + abis.append(f'cp{version}{threading}') + abis.insert(0, f'cp{version}{threading}{debug}{pymalloc}{ucs4}') return abis def cpython_tags( - python_version: Optional[PythonVersion] = None, - abis: Optional[Iterable[str]] = None, - platforms: Optional[Iterable[str]] = None, + python_version: PythonVersion | None = None, + abis: Iterable[str] | None = None, + platforms: Iterable[str] | None = None, *, warn: bool = False, ) -> Iterator[Tag]: @@ -209,7 +209,7 @@ def cpython_tags( if not python_version: python_version = sys.version_info[:2] - interpreter = f"cp{_version_nodot(python_version[:2])}" + interpreter = f'cp{_version_nodot(python_version[:2])}' if abis is None: if len(python_version) > 1: @@ -218,7 +218,7 @@ def cpython_tags( abis = [] abis = list(abis) # 'abi3' and 'none' are explicitly handled later. - for explicit_abi in ("abi3", "none"): + for explicit_abi in ('abi3', 'none'): try: abis.remove(explicit_abi) except ValueError: @@ -232,19 +232,19 @@ def cpython_tags( threading = _is_threaded_cpython(abis) use_abi3 = _abi3_applies(python_version, threading) if use_abi3: - yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) - yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + yield from (Tag(interpreter, 'abi3', platform_) for platform_ in platforms) + yield from (Tag(interpreter, 'none', platform_) for platform_ in platforms) if use_abi3: for minor_version in range(python_version[1] - 1, 1, -1): for platform_ in platforms: - interpreter = "cp{version}".format( - version=_version_nodot((python_version[0], minor_version)) + interpreter = 'cp{version}'.format( + version=_version_nodot((python_version[0], minor_version)), ) - yield Tag(interpreter, "abi3", platform_) + yield Tag(interpreter, 'abi3', platform_) -def _generic_abi() -> List[str]: +def _generic_abi() -> list[str]: """ Return the ABI tag based on EXT_SUFFIX. """ @@ -259,24 +259,24 @@ def _generic_abi() -> List[str]: # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' # => graalpy_38_native - ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) - if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": + ext_suffix = _get_config_var('EXT_SUFFIX', warn=True) + if not isinstance(ext_suffix, str) or ext_suffix[0] != '.': raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") - parts = ext_suffix.split(".") + parts = ext_suffix.split('.') if len(parts) < 3: # CPython3.7 and earlier uses ".pyd" on Windows. return _cpython_abis(sys.version_info[:2]) soabi = parts[1] - if soabi.startswith("cpython"): + if soabi.startswith('cpython'): # non-windows - abi = "cp" + soabi.split("-")[1] - elif soabi.startswith("cp"): + abi = 'cp' + soabi.split('-')[1] + elif soabi.startswith('cp'): # windows - abi = soabi.split("-")[0] - elif soabi.startswith("pypy"): - abi = "-".join(soabi.split("-")[:2]) - elif soabi.startswith("graalpy"): - abi = "-".join(soabi.split("-")[:3]) + abi = soabi.split('-')[0] + elif soabi.startswith('pypy'): + abi = '-'.join(soabi.split('-')[:2]) + elif soabi.startswith('graalpy'): + abi = '-'.join(soabi.split('-')[:3]) elif soabi: # pyston, ironpython, others? abi = soabi @@ -286,9 +286,9 @@ def _generic_abi() -> List[str]: def generic_tags( - interpreter: Optional[str] = None, - abis: Optional[Iterable[str]] = None, - platforms: Optional[Iterable[str]] = None, + interpreter: str | None = None, + abis: Iterable[str] | None = None, + platforms: Iterable[str] | None = None, *, warn: bool = False, ) -> Iterator[Tag]: @@ -303,14 +303,14 @@ def generic_tags( if not interpreter: interp_name = interpreter_name() interp_version = interpreter_version(warn=warn) - interpreter = "".join([interp_name, interp_version]) + interpreter = ''.join([interp_name, interp_version]) if abis is None: abis = _generic_abi() else: abis = list(abis) platforms = list(platforms or platform_tags()) - if "none" not in abis: - abis.append("none") + if 'none' not in abis: + abis.append('none') for abi in abis: for platform_ in platforms: yield Tag(interpreter, abi, platform_) @@ -324,17 +324,17 @@ def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: all previous versions of that major version. """ if len(py_version) > 1: - yield f"py{_version_nodot(py_version[:2])}" - yield f"py{py_version[0]}" + yield f'py{_version_nodot(py_version[:2])}' + yield f'py{py_version[0]}' if len(py_version) > 1: for minor in range(py_version[1] - 1, -1, -1): - yield f"py{_version_nodot((py_version[0], minor))}" + yield f'py{_version_nodot((py_version[0], minor))}' def compatible_tags( - python_version: Optional[PythonVersion] = None, - interpreter: Optional[str] = None, - platforms: Optional[Iterable[str]] = None, + python_version: PythonVersion | None = None, + interpreter: str | None = None, + platforms: Iterable[str] | None = None, ) -> Iterator[Tag]: """ Yields the sequence of tags that are compatible with a specific version of Python. @@ -349,57 +349,57 @@ def compatible_tags( platforms = list(platforms or platform_tags()) for version in _py_interpreter_range(python_version): for platform_ in platforms: - yield Tag(version, "none", platform_) + yield Tag(version, 'none', platform_) if interpreter: - yield Tag(interpreter, "none", "any") + yield Tag(interpreter, 'none', 'any') for version in _py_interpreter_range(python_version): - yield Tag(version, "none", "any") + yield Tag(version, 'none', 'any') def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: if not is_32bit: return arch - if arch.startswith("ppc"): - return "ppc" + if arch.startswith('ppc'): + return 'ppc' - return "i386" + return 'i386' -def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: +def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> list[str]: formats = [cpu_arch] - if cpu_arch == "x86_64": + if cpu_arch == 'x86_64': if version < (10, 4): return [] - formats.extend(["intel", "fat64", "fat32"]) + formats.extend(['intel', 'fat64', 'fat32']) - elif cpu_arch == "i386": + elif cpu_arch == 'i386': if version < (10, 4): return [] - formats.extend(["intel", "fat32", "fat"]) + formats.extend(['intel', 'fat32', 'fat']) - elif cpu_arch == "ppc64": + elif cpu_arch == 'ppc64': # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? if version > (10, 5) or version < (10, 4): return [] - formats.append("fat64") + formats.append('fat64') - elif cpu_arch == "ppc": + elif cpu_arch == 'ppc': if version > (10, 6): return [] - formats.extend(["fat32", "fat"]) + formats.extend(['fat32', 'fat']) - if cpu_arch in {"arm64", "x86_64"}: - formats.append("universal2") + if cpu_arch in {'arm64', 'x86_64'}: + formats.append('universal2') - if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: - formats.append("universal") + if cpu_arch in {'x86_64', 'i386', 'ppc64', 'ppc', 'intel'}: + formats.append('universal') return formats def mac_platforms( - version: Optional[MacVersion] = None, arch: Optional[str] = None + version: MacVersion | None = None, arch: str | None = None, ) -> Iterator[str]: """ Yields the platform tags for a macOS system. @@ -411,23 +411,23 @@ def mac_platforms( """ version_str, _, cpu_arch = platform.mac_ver() if version is None: - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + version = cast('MacVersion', tuple(map(int, version_str.split('.')[:2]))) if version == (10, 16): # When built against an older macOS SDK, Python will report macOS 10.16 # instead of the real version. version_str = subprocess.run( [ sys.executable, - "-sS", - "-c", - "import platform; print(platform.mac_ver()[0])", + '-sS', + '-c', + 'import platform; print(platform.mac_ver()[0])', ], check=True, - env={"SYSTEM_VERSION_COMPAT": "0"}, + env={'SYSTEM_VERSION_COMPAT': '0'}, stdout=subprocess.PIPE, text=True, ).stdout - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + version = cast('MacVersion', tuple(map(int, version_str.split('.')[:2]))) else: version = version if arch is None: @@ -442,8 +442,8 @@ def mac_platforms( compat_version = 10, minor_version binary_formats = _mac_binary_formats(compat_version, arch) for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=10, minor=minor_version, binary_format=binary_format + yield 'macosx_{major}_{minor}_{binary_format}'.format( + major=10, minor=minor_version, binary_format=binary_format, ) if version >= (11, 0): @@ -453,8 +453,8 @@ def mac_platforms( compat_version = major_version, 0 binary_formats = _mac_binary_formats(compat_version, arch) for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=major_version, minor=0, binary_format=binary_format + yield 'macosx_{major}_{minor}_{binary_format}'.format( + major=major_version, minor=0, binary_format=binary_format, ) if version >= (11, 0): @@ -465,12 +465,12 @@ def mac_platforms( # However, the "universal2" binary format can have a # macOS version earlier than 11.0 when the x86_64 part of the binary supports # that version of macOS. - if arch == "x86_64": + if arch == 'x86_64': for minor_version in range(16, 3, -1): compat_version = 10, minor_version binary_formats = _mac_binary_formats(compat_version, arch) for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( + yield 'macosx_{major}_{minor}_{binary_format}'.format( major=compat_version[0], minor=compat_version[1], binary_format=binary_format, @@ -478,8 +478,8 @@ def mac_platforms( else: for minor_version in range(16, 3, -1): compat_version = 10, minor_version - binary_format = "universal2" - yield "macosx_{major}_{minor}_{binary_format}".format( + binary_format = 'universal2' + yield 'macosx_{major}_{minor}_{binary_format}'.format( major=compat_version[0], minor=compat_version[1], binary_format=binary_format, @@ -488,21 +488,21 @@ def mac_platforms( def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: linux = _normalize_string(sysconfig.get_platform()) - if not linux.startswith("linux_"): + if not linux.startswith('linux_'): # we should never be here, just yield the sysconfig one and return yield linux return if is_32bit: - if linux == "linux_x86_64": - linux = "linux_i686" - elif linux == "linux_aarch64": - linux = "linux_armv8l" - _, arch = linux.split("_", 1) - archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch]) + if linux == 'linux_x86_64': + linux = 'linux_i686' + elif linux == 'linux_aarch64': + linux = 'linux_armv8l' + _, arch = linux.split('_', 1) + archs = {'armv8l': ['armv8l', 'armv7l']}.get(arch, [arch]) yield from _manylinux.platform_tags(archs) yield from _musllinux.platform_tags(archs) for arch in archs: - yield f"linux_{arch}" + yield f'linux_{arch}' def _generic_platforms() -> Iterator[str]: @@ -513,9 +513,9 @@ def platform_tags() -> Iterator[str]: """ Provides the platform tags for this installation. """ - if platform.system() == "Darwin": + if platform.system() == 'Darwin': return mac_platforms() - elif platform.system() == "Linux": + elif platform.system() == 'Linux': return _linux_platforms() else: return _generic_platforms() @@ -536,7 +536,7 @@ def interpreter_version(*, warn: bool = False) -> str: """ Returns the version of the running interpreter. """ - version = _get_config_var("py_version_nodot", warn=warn) + version = _get_config_var('py_version_nodot', warn=warn) if version: version = str(version) else: @@ -545,7 +545,7 @@ def interpreter_version(*, warn: bool = False) -> str: def _version_nodot(version: PythonVersion) -> str: - return "".join(map(str, version)) + return ''.join(map(str, version)) def sys_tags(*, warn: bool = False) -> Iterator[Tag]: @@ -557,15 +557,15 @@ def sys_tags(*, warn: bool = False) -> Iterator[Tag]: """ interp_name = interpreter_name() - if interp_name == "cp": + if interp_name == 'cp': yield from cpython_tags(warn=warn) else: yield from generic_tags() - if interp_name == "pp": - interp = "pp3" - elif interp_name == "cp": - interp = "cp" + interpreter_version(warn=warn) + if interp_name == 'pp': + interp = 'pp3' + elif interp_name == 'cp': + interp = 'cp' + interpreter_version(warn=warn) else: interp = None yield from compatible_tags(interpreter=interp) diff --git a/.venv/lib/python3.10/site-packages/packaging/utils.py b/.venv/lib/python3.10/site-packages/packaging/utils.py index c2c2f75..5053e7f 100644 --- a/.venv/lib/python3.10/site-packages/packaging/utils.py +++ b/.venv/lib/python3.10/site-packages/packaging/utils.py @@ -1,15 +1,22 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +from __future__ import annotations import re -from typing import FrozenSet, NewType, Tuple, Union, cast +from typing import cast +from typing import FrozenSet +from typing import NewType +from typing import Tuple +from typing import Union -from .tags import Tag, parse_tag -from .version import InvalidVersion, Version +from .tags import parse_tag +from .tags import Tag +from .version import InvalidVersion +from .version import Version BuildTag = Union[Tuple[()], Tuple[int, str]] -NormalizedName = NewType("NormalizedName", str) +NormalizedName = NewType('NormalizedName', str) class InvalidName(ValueError): @@ -32,19 +39,19 @@ class InvalidSdistFilename(ValueError): # Core metadata spec for `Name` _validate_regex = re.compile( - r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE + r'^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$', re.IGNORECASE, ) -_canonicalize_regex = re.compile(r"[-_.]+") -_normalized_regex = re.compile(r"^([a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9])$") +_canonicalize_regex = re.compile(r'[-_.]+') +_normalized_regex = re.compile(r'^([a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9])$') # PEP 427: The build number must start with a digit. -_build_tag_regex = re.compile(r"(\d+)(.*)") +_build_tag_regex = re.compile(r'(\d+)(.*)') def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName: if validate and not _validate_regex.match(name): - raise InvalidName(f"name is invalid: {name!r}") + raise InvalidName(f'name is invalid: {name!r}') # This is taken from PEP 503. - value = _canonicalize_regex.sub("-", name).lower() + value = _canonicalize_regex.sub('-', name).lower() return cast(NormalizedName, value) @@ -53,7 +60,7 @@ def is_normalized_name(name: str) -> bool: def canonicalize_version( - version: Union[Version, str], *, strip_trailing_zero: bool = True + version: Version | str, *, strip_trailing_zero: bool = True, ) -> str: """ This is very similar to Version.__str__, but has one subtle difference @@ -72,61 +79,61 @@ def canonicalize_version( # Epoch if parsed.epoch != 0: - parts.append(f"{parsed.epoch}!") + parts.append(f'{parsed.epoch}!') # Release segment - release_segment = ".".join(str(x) for x in parsed.release) + release_segment = '.'.join(str(x) for x in parsed.release) if strip_trailing_zero: # NB: This strips trailing '.0's to normalize - release_segment = re.sub(r"(\.0)+$", "", release_segment) + release_segment = re.sub(r'(\.0)+$', '', release_segment) parts.append(release_segment) # Pre-release if parsed.pre is not None: - parts.append("".join(str(x) for x in parsed.pre)) + parts.append(''.join(str(x) for x in parsed.pre)) # Post-release if parsed.post is not None: - parts.append(f".post{parsed.post}") + parts.append(f'.post{parsed.post}') # Development release if parsed.dev is not None: - parts.append(f".dev{parsed.dev}") + parts.append(f'.dev{parsed.dev}') # Local version segment if parsed.local is not None: - parts.append(f"+{parsed.local}") + parts.append(f'+{parsed.local}') - return "".join(parts) + return ''.join(parts) def parse_wheel_filename( filename: str, -) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: - if not filename.endswith(".whl"): +) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]: + if not filename.endswith('.whl'): raise InvalidWheelFilename( - f"Invalid wheel filename (extension must be '.whl'): {filename}" + f"Invalid wheel filename (extension must be '.whl'): {filename}", ) filename = filename[:-4] - dashes = filename.count("-") + dashes = filename.count('-') if dashes not in (4, 5): raise InvalidWheelFilename( - f"Invalid wheel filename (wrong number of parts): {filename}" + f'Invalid wheel filename (wrong number of parts): {filename}', ) - parts = filename.split("-", dashes - 2) + parts = filename.split('-', dashes - 2) name_part = parts[0] # See PEP 427 for the rules on escaping the project name. - if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: - raise InvalidWheelFilename(f"Invalid project name: {filename}") + if '__' in name_part or re.match(r'^[\w\d._]*$', name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f'Invalid project name: {filename}') name = canonicalize_name(name_part) try: version = Version(parts[1]) except InvalidVersion as e: raise InvalidWheelFilename( - f"Invalid wheel filename (invalid version): {filename}" + f'Invalid wheel filename (invalid version): {filename}', ) from e if dashes == 5: @@ -134,7 +141,7 @@ def parse_wheel_filename( build_match = _build_tag_regex.match(build_part) if build_match is None: raise InvalidWheelFilename( - f"Invalid build number: {build_part} in '{filename}'" + f"Invalid build number: {build_part} in '{filename}'", ) build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) else: @@ -143,22 +150,22 @@ def parse_wheel_filename( return (name, version, build, tags) -def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: - if filename.endswith(".tar.gz"): - file_stem = filename[: -len(".tar.gz")] - elif filename.endswith(".zip"): - file_stem = filename[: -len(".zip")] +def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]: + if filename.endswith('.tar.gz'): + file_stem = filename[: -len('.tar.gz')] + elif filename.endswith('.zip'): + file_stem = filename[: -len('.zip')] else: raise InvalidSdistFilename( f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" - f" {filename}" + f' {filename}', ) # We are requiring a PEP 440 version, which cannot contain dashes, # so we split on the last dash. - name_part, sep, version_part = file_stem.rpartition("-") + name_part, sep, version_part = file_stem.rpartition('-') if not sep: - raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + raise InvalidSdistFilename(f'Invalid sdist filename: {filename}') name = canonicalize_name(name_part) @@ -166,7 +173,7 @@ def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: version = Version(version_part) except InvalidVersion as e: raise InvalidSdistFilename( - f"Invalid sdist filename (invalid version): {filename}" + f'Invalid sdist filename (invalid version): {filename}', ) from e return (name, version) diff --git a/.venv/lib/python3.10/site-packages/packaging/version.py b/.venv/lib/python3.10/site-packages/packaging/version.py index 5faab9b..11fb174 100644 --- a/.venv/lib/python3.10/site-packages/packaging/version.py +++ b/.venv/lib/python3.10/site-packages/packaging/version.py @@ -6,14 +6,24 @@ from packaging.version import parse, Version """ +from __future__ import annotations import itertools import re -from typing import Any, Callable, NamedTuple, Optional, SupportsInt, Tuple, Union +from typing import Any +from typing import Callable +from typing import NamedTuple +from typing import Optional +from typing import SupportsInt +from typing import Tuple +from typing import Union -from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType +from ._structures import Infinity +from ._structures import InfinityType +from ._structures import NegativeInfinity +from ._structures import NegativeInfinityType -__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"] +__all__ = ['VERSION_PATTERN', 'parse', 'Version', 'InvalidVersion'] LocalType = Tuple[Union[int, str], ...] @@ -35,14 +45,14 @@ VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] class _Version(NamedTuple): epoch: int - release: Tuple[int, ...] - dev: Optional[Tuple[str, int]] - pre: Optional[Tuple[str, int]] - post: Optional[Tuple[str, int]] - local: Optional[LocalType] + release: tuple[int, ...] + dev: tuple[str, int] | None + pre: tuple[str, int] | None + post: tuple[str, int] | None + local: LocalType | None -def parse(version: str) -> "Version": +def parse(version: str) -> Version: """Parse the given version string. >>> parse('1.0.dev1') @@ -65,7 +75,7 @@ class InvalidVersion(ValueError): class _BaseVersion: - _key: Tuple[Any, ...] + _key: tuple[Any, ...] def __hash__(self) -> int: return hash(self._key) @@ -73,13 +83,13 @@ class _BaseVersion: # Please keep the duplicated `isinstance` check # in the six comparisons hereunder # unless you find a way to avoid adding overhead function calls. - def __lt__(self, other: "_BaseVersion") -> bool: + def __lt__(self, other: _BaseVersion) -> bool: if not isinstance(other, _BaseVersion): return NotImplemented return self._key < other._key - def __le__(self, other: "_BaseVersion") -> bool: + def __le__(self, other: _BaseVersion) -> bool: if not isinstance(other, _BaseVersion): return NotImplemented @@ -91,13 +101,13 @@ class _BaseVersion: return self._key == other._key - def __ge__(self, other: "_BaseVersion") -> bool: + def __ge__(self, other: _BaseVersion) -> bool: if not isinstance(other, _BaseVersion): return NotImplemented return self._key >= other._key - def __gt__(self, other: "_BaseVersion") -> bool: + def __gt__(self, other: _BaseVersion) -> bool: if not isinstance(other, _BaseVersion): return NotImplemented @@ -180,7 +190,7 @@ class Version(_BaseVersion): True """ - _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) + _regex = re.compile(r'^\s*' + VERSION_PATTERN + r'\s*$', re.VERBOSE | re.IGNORECASE) _key: CmpKey def __init__(self, version: str) -> None: @@ -201,14 +211,14 @@ class Version(_BaseVersion): # Store the parsed out pieces of the version self._version = _Version( - epoch=int(match.group("epoch")) if match.group("epoch") else 0, - release=tuple(int(i) for i in match.group("release").split(".")), - pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")), + epoch=int(match.group('epoch')) if match.group('epoch') else 0, + release=tuple(int(i) for i in match.group('release').split('.')), + pre=_parse_letter_version(match.group('pre_l'), match.group('pre_n')), post=_parse_letter_version( - match.group("post_l"), match.group("post_n1") or match.group("post_n2") + match.group('post_l'), match.group('post_n1') or match.group('post_n2'), ), - dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")), - local=_parse_local_version(match.group("local")), + dev=_parse_letter_version(match.group('dev_l'), match.group('dev_n')), + local=_parse_local_version(match.group('local')), ) # Generate a key which will be used for sorting @@ -239,28 +249,28 @@ class Version(_BaseVersion): # Epoch if self.epoch != 0: - parts.append(f"{self.epoch}!") + parts.append(f'{self.epoch}!') # Release segment - parts.append(".".join(str(x) for x in self.release)) + parts.append('.'.join(str(x) for x in self.release)) # Pre-release if self.pre is not None: - parts.append("".join(str(x) for x in self.pre)) + parts.append(''.join(str(x) for x in self.pre)) # Post-release if self.post is not None: - parts.append(f".post{self.post}") + parts.append(f'.post{self.post}') # Development release if self.dev is not None: - parts.append(f".dev{self.dev}") + parts.append(f'.dev{self.dev}') # Local version segment if self.local is not None: - parts.append(f"+{self.local}") + parts.append(f'+{self.local}') - return "".join(parts) + return ''.join(parts) @property def epoch(self) -> int: @@ -274,7 +284,7 @@ class Version(_BaseVersion): return self._version.epoch @property - def release(self) -> Tuple[int, ...]: + def release(self) -> tuple[int, ...]: """The components of the "release" segment of the version. >>> Version("1.2.3").release @@ -290,7 +300,7 @@ class Version(_BaseVersion): return self._version.release @property - def pre(self) -> Optional[Tuple[str, int]]: + def pre(self) -> tuple[str, int] | None: """The pre-release segment of the version. >>> print(Version("1.2.3").pre) @@ -305,7 +315,7 @@ class Version(_BaseVersion): return self._version.pre @property - def post(self) -> Optional[int]: + def post(self) -> int | None: """The post-release number of the version. >>> print(Version("1.2.3").post) @@ -316,7 +326,7 @@ class Version(_BaseVersion): return self._version.post[1] if self._version.post else None @property - def dev(self) -> Optional[int]: + def dev(self) -> int | None: """The development number of the version. >>> print(Version("1.2.3").dev) @@ -327,7 +337,7 @@ class Version(_BaseVersion): return self._version.dev[1] if self._version.dev else None @property - def local(self) -> Optional[str]: + def local(self) -> str | None: """The local version segment of the version. >>> print(Version("1.2.3").local) @@ -336,7 +346,7 @@ class Version(_BaseVersion): 'abc' """ if self._version.local: - return ".".join(str(x) for x in self._version.local) + return '.'.join(str(x) for x in self._version.local) else: return None @@ -351,7 +361,7 @@ class Version(_BaseVersion): >>> Version("1.2.3+abc.dev1").public '1.2.3' """ - return str(self).split("+", 1)[0] + return str(self).split('+', 1)[0] @property def base_version(self) -> str: @@ -371,12 +381,12 @@ class Version(_BaseVersion): # Epoch if self.epoch != 0: - parts.append(f"{self.epoch}!") + parts.append(f'{self.epoch}!') # Release segment - parts.append(".".join(str(x) for x in self.release)) + parts.append('.'.join(str(x) for x in self.release)) - return "".join(parts) + return ''.join(parts) @property def is_prerelease(self) -> bool: @@ -450,8 +460,8 @@ class Version(_BaseVersion): def _parse_letter_version( - letter: Optional[str], number: Union[str, bytes, SupportsInt, None] -) -> Optional[Tuple[str, int]]: + letter: str | None, number: str | bytes | SupportsInt | None, +) -> tuple[str, int] | None: if letter: # We consider there to be an implicit 0 in a pre-release if there is @@ -465,30 +475,30 @@ def _parse_letter_version( # We consider some words to be alternate spellings of other words and # in those cases we want to normalize the spellings to our preferred # spelling. - if letter == "alpha": - letter = "a" - elif letter == "beta": - letter = "b" - elif letter in ["c", "pre", "preview"]: - letter = "rc" - elif letter in ["rev", "r"]: - letter = "post" + if letter == 'alpha': + letter = 'a' + elif letter == 'beta': + letter = 'b' + elif letter in ['c', 'pre', 'preview']: + letter = 'rc' + elif letter in ['rev', 'r']: + letter = 'post' return letter, int(number) if not letter and number: # We assume if we are given a number, but we are not given a letter # then this is using the implicit post release syntax (e.g. 1.0-1) - letter = "post" + letter = 'post' return letter, int(number) return None -_local_version_separators = re.compile(r"[\._-]") +_local_version_separators = re.compile(r'[\._-]') -def _parse_local_version(local: Optional[str]) -> Optional[LocalType]: +def _parse_local_version(local: str | None) -> LocalType | None: """ Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). """ @@ -502,11 +512,11 @@ def _parse_local_version(local: Optional[str]) -> Optional[LocalType]: def _cmpkey( epoch: int, - release: Tuple[int, ...], - pre: Optional[Tuple[str, int]], - post: Optional[Tuple[str, int]], - dev: Optional[Tuple[str, int]], - local: Optional[LocalType], + release: tuple[int, ...], + pre: tuple[str, int] | None, + post: tuple[str, int] | None, + dev: tuple[str, int] | None, + local: LocalType | None, ) -> CmpKey: # When we compare a release version, we want to compare it with all of the @@ -515,7 +525,7 @@ def _cmpkey( # re-reverse it back into the correct order and make it a tuple and use # that for our sorting key. _release = tuple( - reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) + reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))), ) # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. @@ -557,7 +567,7 @@ def _cmpkey( # - Shorter versions sort before longer versions when the prefixes # match exactly _local = tuple( - (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local + (i, '') if isinstance(i, int) else (NegativeInfinity, i) for i in local ) return epoch, _release, _pre, _post, _dev, _local diff --git a/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/METADATA b/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/METADATA index 4e68399..3048e57 100644 --- a/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/METADATA +++ b/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/METADATA @@ -88,5 +88,3 @@ rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. .. _User IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa .. _Development IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev .. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md - - diff --git a/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/WHEEL index becc9a6..3cfad32 100644 --- a/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/WHEEL +++ b/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/WHEEL @@ -2,4 +2,3 @@ Wheel-Version: 1.0 Generator: bdist_wheel (0.37.1) Root-Is-Purelib: true Tag: py3-none-any - diff --git a/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/entry_points.txt b/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/entry_points.txt index 9609f72..ab909c9 100644 --- a/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/entry_points.txt +++ b/.venv/lib/python3.10/site-packages/pip-22.0.4.dist-info/entry_points.txt @@ -2,4 +2,3 @@ pip = pip._internal.cli.main:main pip3 = pip._internal.cli.main:main pip3.9 = pip._internal.cli.main:main - diff --git a/.venv/lib/python3.10/site-packages/pip/__init__.py b/.venv/lib/python3.10/site-packages/pip/__init__.py index 3a0d263..d4e6987 100644 --- a/.venv/lib/python3.10/site-packages/pip/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/__init__.py @@ -1,9 +1,12 @@ -from typing import List, Optional +from __future__ import annotations -__version__ = "22.0.4" +from typing import List +from typing import Optional + +__version__ = '22.0.4' -def main(args: Optional[List[str]] = None) -> int: +def main(args: list[str] | None = None) -> int: """This is an internal API only meant for use by pip's own console scripts. For additional details, see https://github.com/pypa/pip/issues/7498. diff --git a/.venv/lib/python3.10/site-packages/pip/__main__.py b/.venv/lib/python3.10/site-packages/pip/__main__.py index fe34a7b..f4ea14b 100644 --- a/.venv/lib/python3.10/site-packages/pip/__main__.py +++ b/.venv/lib/python3.10/site-packages/pip/__main__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import sys import warnings @@ -6,12 +8,12 @@ import warnings # of sys.path, if present to avoid using current directory # in pip commands check, freeze, install, list and show, # when invoked as python -m pip -if sys.path[0] in ("", os.getcwd()): +if sys.path[0] in ('', os.getcwd()): sys.path.pop(0) # If we are running from a wheel, add the wheel to sys.path # This allows the usage python pip-*.whl/pip install pip-*.whl -if __package__ == "": +if __package__ == '': # __file__ is pip-*.whl/pip/__main__.py # first dirname call strips of '/__main__.py', second strips off '/pip' # Resulting path is the name of the wheel itself @@ -19,12 +21,12 @@ if __package__ == "": path = os.path.dirname(os.path.dirname(__file__)) sys.path.insert(0, path) -if __name__ == "__main__": +if __name__ == '__main__': # Work around the error reported in #9540, pending a proper fix. # Note: It is essential the warning filter is set *before* importing # pip, as the deprecation happens at import time, not runtime. warnings.filterwarnings( - "ignore", category=DeprecationWarning, module=".*packaging\\.version" + 'ignore', category=DeprecationWarning, module='.*packaging\\.version', ) from pip._internal.cli.main import main as _main diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/__init__.py index 6afb5c6..4bfee90 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/__init__.py @@ -1,4 +1,7 @@ -from typing import List, Optional +from __future__ import annotations + +from typing import List +from typing import Optional import pip._internal.utils.inject_securetransport # noqa from pip._internal.utils import _log @@ -8,7 +11,7 @@ from pip._internal.utils import _log _log.init_logging() -def main(args: (Optional[List[str]]) = None) -> int: +def main(args: (list[str] | None) = None) -> int: """This is preserved for old console scripts that may still be referencing it. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/build_env.py b/.venv/lib/python3.10/site-packages/pip/_internal/build_env.py index daeb7fb..4d1b647 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/build_env.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/build_env.py @@ -1,5 +1,6 @@ """Build Environment used for isolation during sdist building """ +from __future__ import annotations import contextlib import logging @@ -11,18 +12,27 @@ import zipfile from collections import OrderedDict from sysconfig import get_paths from types import TracebackType -from typing import TYPE_CHECKING, Iterable, Iterator, List, Optional, Set, Tuple, Type - -from pip._vendor.certifi import where -from pip._vendor.packaging.requirements import Requirement -from pip._vendor.packaging.version import Version +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING from pip import __file__ as pip_location from pip._internal.cli.spinners import open_spinner -from pip._internal.locations import get_platlib, get_prefixed_libs, get_purelib +from pip._internal.locations import get_platlib +from pip._internal.locations import get_prefixed_libs +from pip._internal.locations import get_purelib from pip._internal.metadata import get_environment from pip._internal.utils.subprocess import call_subprocess -from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.temp_dir import tempdir_kinds +from pip._internal.utils.temp_dir import TempDirectory +from pip._vendor.certifi import where +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.version import Version if TYPE_CHECKING: from pip._internal.index.package_finder import PackageFinder @@ -35,9 +45,9 @@ class _Prefix: self.path = path self.setup = False self.bin_dir = get_paths( - "nt" if os.name == "nt" else "posix_prefix", - vars={"base": path, "platbase": path}, - )["scripts"] + 'nt' if os.name == 'nt' else 'posix_prefix', + vars={'base': path, 'platbase': path}, + )['scripts'] self.lib_dirs = get_prefixed_libs(path) @@ -56,15 +66,14 @@ def _create_standalone_pip() -> Iterator[str]: yield str(source) return - with TempDirectory(kind="standalone-pip") as tmp_dir: - pip_zip = os.path.join(tmp_dir.path, "__env_pip__.zip") + with TempDirectory(kind='standalone-pip') as tmp_dir: + pip_zip = os.path.join(tmp_dir.path, '__env_pip__.zip') kwargs = {} - if sys.version_info >= (3, 8): - kwargs["strict_timestamps"] = False - with zipfile.ZipFile(pip_zip, "w", **kwargs) as zf: - for child in source.rglob("*"): + kwargs['strict_timestamps'] = False + with zipfile.ZipFile(pip_zip, 'w', **kwargs) as zf: + for child in source.rglob('*'): zf.write(child, child.relative_to(source.parent).as_posix()) - yield os.path.join(pip_zip, "pip") + yield os.path.join(pip_zip, 'pip') class BuildEnvironment: @@ -75,11 +84,11 @@ class BuildEnvironment: self._prefixes = OrderedDict( (name, _Prefix(os.path.join(temp_dir.path, name))) - for name in ("normal", "overlay") + for name in ('normal', 'overlay') ) - self._bin_dirs: List[str] = [] - self._lib_dirs: List[str] = [] + self._bin_dirs: list[str] = [] + self._lib_dirs: list[str] = [] for prefix in reversed(list(self._prefixes.values())): self._bin_dirs.append(prefix.bin_dir) self._lib_dirs.extend(prefix.lib_dirs) @@ -90,11 +99,11 @@ class BuildEnvironment: system_sites = { os.path.normcase(site) for site in (get_purelib(), get_platlib()) } - self._site_dir = os.path.join(temp_dir.path, "site") + self._site_dir = os.path.join(temp_dir.path, 'site') if not os.path.exists(self._site_dir): os.mkdir(self._site_dir) with open( - os.path.join(self._site_dir, "sitecustomize.py"), "w", encoding="utf-8" + os.path.join(self._site_dir, 'sitecustomize.py'), 'w', encoding='utf-8', ) as fp: fp.write( textwrap.dedent( @@ -121,18 +130,18 @@ class BuildEnvironment: for path in {lib_dirs!r}: assert not path in sys.path site.addsitedir(path) - """ - ).format(system_sites=system_sites, lib_dirs=self._lib_dirs) + """, + ).format(system_sites=system_sites, lib_dirs=self._lib_dirs), ) def __enter__(self) -> None: self._save_env = { name: os.environ.get(name, None) - for name in ("PATH", "PYTHONNOUSERSITE", "PYTHONPATH") + for name in ('PATH', 'PYTHONNOUSERSITE', 'PYTHONPATH') } path = self._bin_dirs[:] - old_path = self._save_env["PATH"] + old_path = self._save_env['PATH'] if old_path: path.extend(old_path.split(os.pathsep)) @@ -140,17 +149,17 @@ class BuildEnvironment: os.environ.update( { - "PATH": os.pathsep.join(path), - "PYTHONNOUSERSITE": "1", - "PYTHONPATH": os.pathsep.join(pythonpath), - } + 'PATH': os.pathsep.join(path), + 'PYTHONNOUSERSITE': '1', + 'PYTHONPATH': os.pathsep.join(pythonpath), + }, ) def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: for varname, old_value in self._save_env.items(): if old_value is None: @@ -159,8 +168,8 @@ class BuildEnvironment: os.environ[varname] = old_value def check_requirements( - self, reqs: Iterable[str] - ) -> Tuple[Set[Tuple[str, str]], Set[str]]: + self, reqs: Iterable[str], + ) -> tuple[set[tuple[str, str]], set[str]]: """Return 2 sets: - conflicting requirements: set of (installed, wanted) reqs tuples - missing requirements: set of reqs @@ -176,9 +185,9 @@ class BuildEnvironment: missing.add(req_str) continue if isinstance(dist.version, Version): - installed_req_str = f"{req.name}=={dist.version}" + installed_req_str = f'{req.name}=={dist.version}' else: - installed_req_str = f"{req.name}==={dist.version}" + installed_req_str = f'{req.name}==={dist.version}' if dist.version not in req.specifier: conflicting.add((installed_req_str, req_str)) # FIXME: Consider direct URL? @@ -186,7 +195,7 @@ class BuildEnvironment: def install_requirements( self, - finder: "PackageFinder", + finder: PackageFinder, requirements: Iterable[str], prefix_as_string: str, *, @@ -210,56 +219,56 @@ class BuildEnvironment: @staticmethod def _install_requirements( pip_runnable: str, - finder: "PackageFinder", + finder: PackageFinder, requirements: Iterable[str], prefix: _Prefix, *, kind: str, ) -> None: - args: List[str] = [ + args: list[str] = [ sys.executable, pip_runnable, - "install", - "--ignore-installed", - "--no-user", - "--prefix", + 'install', + '--ignore-installed', + '--no-user', + '--prefix', prefix.path, - "--no-warn-script-location", + '--no-warn-script-location', ] if logger.getEffectiveLevel() <= logging.DEBUG: - args.append("-v") - for format_control in ("no_binary", "only_binary"): + args.append('-v') + for format_control in ('no_binary', 'only_binary'): formats = getattr(finder.format_control, format_control) args.extend( ( - "--" + format_control.replace("_", "-"), - ",".join(sorted(formats or {":none:"})), - ) + '--' + format_control.replace('_', '-'), + ','.join(sorted(formats or {':none:'})), + ), ) index_urls = finder.index_urls if index_urls: - args.extend(["-i", index_urls[0]]) + args.extend(['-i', index_urls[0]]) for extra_index in index_urls[1:]: - args.extend(["--extra-index-url", extra_index]) + args.extend(['--extra-index-url', extra_index]) else: - args.append("--no-index") + args.append('--no-index') for link in finder.find_links: - args.extend(["--find-links", link]) + args.extend(['--find-links', link]) for host in finder.trusted_hosts: - args.extend(["--trusted-host", host]) + args.extend(['--trusted-host', host]) if finder.allow_all_prereleases: - args.append("--pre") + args.append('--pre') if finder.prefer_binary: - args.append("--prefer-binary") - args.append("--") + args.append('--prefer-binary') + args.append('--') args.extend(requirements) - extra_environ = {"_PIP_STANDALONE_CERT": where()} - with open_spinner(f"Installing {kind}") as spinner: + extra_environ = {'_PIP_STANDALONE_CERT': where()} + with open_spinner(f'Installing {kind}') as spinner: call_subprocess( args, - command_desc=f"pip subprocess to install {kind}", + command_desc=f'pip subprocess to install {kind}', spinner=spinner, extra_environ=extra_environ, ) @@ -276,9 +285,9 @@ class NoOpBuildEnvironment(BuildEnvironment): def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: pass @@ -287,7 +296,7 @@ class NoOpBuildEnvironment(BuildEnvironment): def install_requirements( self, - finder: "PackageFinder", + finder: PackageFinder, requirements: Iterable[str], prefix_as_string: str, *, diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cache.py b/.venv/lib/python3.10/site-packages/pip/_internal/cache.py index 1d6df22..28c0029 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cache.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cache.py @@ -1,29 +1,36 @@ """Cache Management """ +from __future__ import annotations import hashlib import json import logging import os -from typing import Any, Dict, List, Optional, Set - -from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version -from pip._vendor.packaging.utils import canonicalize_name +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import Set from pip._internal.exceptions import InvalidWheelFilename from pip._internal.models.format_control import FormatControl from pip._internal.models.link import Link from pip._internal.models.wheel import Wheel -from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.temp_dir import tempdir_kinds +from pip._internal.utils.temp_dir import TempDirectory from pip._internal.utils.urls import path_to_url +from pip._vendor.packaging.tags import interpreter_name +from pip._vendor.packaging.tags import interpreter_version +from pip._vendor.packaging.tags import Tag +from pip._vendor.packaging.utils import canonicalize_name logger = logging.getLogger(__name__) -def _hash_dict(d: Dict[str, str]) -> str: +def _hash_dict(d: dict[str, str]) -> str: """Return a stable sha224 of a dictionary.""" - s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True) - return hashlib.sha224(s.encode("ascii")).hexdigest() + s = json.dumps(d, sort_keys=True, separators=(',', ':'), ensure_ascii=True) + return hashlib.sha224(s.encode('ascii')).hexdigest() class Cache: @@ -38,7 +45,7 @@ class Cache: """ def __init__( - self, cache_dir: str, format_control: FormatControl, allowed_formats: Set[str] + self, cache_dir: str, format_control: FormatControl, allowed_formats: set[str], ) -> None: super().__init__() assert not cache_dir or os.path.isabs(cache_dir) @@ -46,28 +53,28 @@ class Cache: self.format_control = format_control self.allowed_formats = allowed_formats - _valid_formats = {"source", "binary"} + _valid_formats = {'source', 'binary'} assert self.allowed_formats.union(_valid_formats) == _valid_formats - def _get_cache_path_parts(self, link: Link) -> List[str]: + def _get_cache_path_parts(self, link: Link) -> list[str]: """Get parts of part that must be os.path.joined with cache_dir""" # We want to generate an url to use as our cache key, we don't want to # just re-use the URL because it might have other items in the fragment # and we don't care about those. - key_parts = {"url": link.url_without_fragment} + key_parts = {'url': link.url_without_fragment} if link.hash_name is not None and link.hash is not None: key_parts[link.hash_name] = link.hash if link.subdirectory_fragment: - key_parts["subdirectory"] = link.subdirectory_fragment + key_parts['subdirectory'] = link.subdirectory_fragment # Include interpreter name, major and minor version in cache key # to cope with ill-behaved sdists that build a different wheel # depending on the python version their setup.py is being run on, # and don't encode the difference in compatibility tags. # https://github.com/pypa/pip/issues/7296 - key_parts["interpreter_name"] = interpreter_name() - key_parts["interpreter_version"] = interpreter_version() + key_parts['interpreter_name'] = interpreter_name() + key_parts['interpreter_version'] = interpreter_version() # Encode our key url with sha224, we'll use this because it has similar # security properties to sha256, but with a shorter total output (and @@ -82,7 +89,7 @@ class Cache: return parts - def _get_candidates(self, link: Link, canonical_package_name: str) -> List[Any]: + def _get_candidates(self, link: Link, canonical_package_name: str) -> list[Any]: can_not_cache = not self.cache_dir or not canonical_package_name or not link if can_not_cache: return [] @@ -105,8 +112,8 @@ class Cache: def get( self, link: Link, - package_name: Optional[str], - supported_tags: List[Tag], + package_name: str | None, + supported_tags: list[Tag], ) -> Link: """Returns a link to a cached item if it exists, otherwise returns the passed link. @@ -118,7 +125,7 @@ class SimpleWheelCache(Cache): """A cache of wheels for future installs.""" def __init__(self, cache_dir: str, format_control: FormatControl) -> None: - super().__init__(cache_dir, format_control, {"binary"}) + super().__init__(cache_dir, format_control, {'binary'}) def get_path_for_link(self, link: Link) -> str: """Return a directory to store cached wheels for link @@ -138,13 +145,13 @@ class SimpleWheelCache(Cache): parts = self._get_cache_path_parts(link) assert self.cache_dir # Store wheels within the root cache_dir - return os.path.join(self.cache_dir, "wheels", *parts) + return os.path.join(self.cache_dir, 'wheels', *parts) def get( self, link: Link, - package_name: Optional[str], - supported_tags: List[Tag], + package_name: str | None, + supported_tags: list[Tag], ) -> Link: candidates = [] @@ -159,8 +166,8 @@ class SimpleWheelCache(Cache): continue if canonicalize_name(wheel.name) != canonical_package_name: logger.debug( - "Ignoring cached wheel %s for %s as it " - "does not match the expected distribution name %s.", + 'Ignoring cached wheel %s for %s as it ' + 'does not match the expected distribution name %s.', wheel_name, link, package_name, @@ -174,7 +181,7 @@ class SimpleWheelCache(Cache): wheel.support_index_min(supported_tags), wheel_name, wheel_dir, - ) + ), ) if not candidates: @@ -214,7 +221,7 @@ class WheelCache(Cache): """ def __init__(self, cache_dir: str, format_control: FormatControl) -> None: - super().__init__(cache_dir, format_control, {"binary"}) + super().__init__(cache_dir, format_control, {'binary'}) self._wheel_cache = SimpleWheelCache(cache_dir, format_control) self._ephem_cache = EphemWheelCache(format_control) @@ -227,8 +234,8 @@ class WheelCache(Cache): def get( self, link: Link, - package_name: Optional[str], - supported_tags: List[Tag], + package_name: str | None, + supported_tags: list[Tag], ) -> Link: cache_entry = self.get_cache_entry(link, package_name, supported_tags) if cache_entry is None: @@ -238,9 +245,9 @@ class WheelCache(Cache): def get_cache_entry( self, link: Link, - package_name: Optional[str], - supported_tags: List[Tag], - ) -> Optional[CacheEntry]: + package_name: str | None, + supported_tags: list[Tag], + ) -> CacheEntry | None: """Returns a CacheEntry with a link to a cached item if it exists or None. The cache entry indicates if the item was found in the persistent or ephemeral cache. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/__init__.py index e589bb9..ed57ecf 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/__init__.py @@ -1,4 +1,4 @@ """Subpackage containing all of pip's command line interface related code """ - # This file intentionally does not import submodules +from __future__ import annotations diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/autocompletion.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/autocompletion.py index 226fe84..6182cff 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/autocompletion.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/autocompletion.py @@ -1,35 +1,40 @@ """Logic that powers autocompletion installed by ``pip completion``. """ +from __future__ import annotations import optparse import os import sys from itertools import chain -from typing import Any, Iterable, List, Optional +from typing import Any +from typing import Iterable +from typing import List +from typing import Optional from pip._internal.cli.main_parser import create_main_parser -from pip._internal.commands import commands_dict, create_command +from pip._internal.commands import commands_dict +from pip._internal.commands import create_command from pip._internal.metadata import get_default_environment def autocomplete() -> None: """Entry Point for completion of main and subcommand options.""" # Don't complete if user hasn't sourced bash_completion file. - if "PIP_AUTO_COMPLETE" not in os.environ: + if 'PIP_AUTO_COMPLETE' not in os.environ: return - cwords = os.environ["COMP_WORDS"].split()[1:] - cword = int(os.environ["COMP_CWORD"]) + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) try: current = cwords[cword - 1] except IndexError: - current = "" + current = '' parser = create_main_parser() subcommands = list(commands_dict) options = [] # subcommand - subcommand_name: Optional[str] = None + subcommand_name: str | None = None for word in cwords: if word in subcommands: subcommand_name = word @@ -37,12 +42,12 @@ def autocomplete() -> None: # subcommand options if subcommand_name is not None: # special case: 'help' subcommand has no options - if subcommand_name == "help": + if subcommand_name == 'help': sys.exit(1) # special case: list locally installed dists for show and uninstall - should_list_installed = not current.startswith("-") and subcommand_name in [ - "show", - "uninstall", + should_list_installed = not current.startswith('-') and subcommand_name in [ + 'show', + 'uninstall', ] if should_list_installed: env = get_default_environment() @@ -50,8 +55,8 @@ def autocomplete() -> None: installed = [ dist.canonical_name for dist in env.iter_installed_distributions(local_only=True) - if dist.canonical_name.startswith(lc) - and dist.canonical_name not in cwords[1:] + if dist.canonical_name.startswith(lc) and + dist.canonical_name not in cwords[1:] ] # if there are no dists installed, fall back to option completion if installed: @@ -60,10 +65,10 @@ def autocomplete() -> None: sys.exit(1) should_list_installables = ( - not current.startswith("-") and subcommand_name == "install" + not current.startswith('-') and subcommand_name == 'install' ) if should_list_installables: - for path in auto_complete_paths(current, "path"): + for path in auto_complete_paths(current, 'path'): print(path) sys.exit(1) @@ -75,7 +80,7 @@ def autocomplete() -> None: options.append((opt_str, opt.nargs)) # filter out previously specified options from available options - prev_opts = [x.split("=")[0] for x in cwords[1 : cword - 1]] + prev_opts = [x.split('=')[0] for x in cwords[1: cword - 1]] options = [(x, v) for (x, v) in options if x not in prev_opts] # filter options by current input options = [(k, v) for k, v in options if k.startswith(current)] @@ -93,8 +98,8 @@ def autocomplete() -> None: for option in options: opt_label = option[0] # append '=' to options which require args - if option[1] and option[0][:2] == "--": - opt_label += "=" + if option[1] and option[0][:2] == '--': + opt_label += '=' print(opt_label) else: # show main parser options only when necessary @@ -102,7 +107,7 @@ def autocomplete() -> None: opts = [i.option_list for i in parser.option_groups] opts.append(parser.option_list) flattened_opts = chain.from_iterable(opts) - if current.startswith("-"): + if current.startswith('-'): for opt in flattened_opts: if opt.help != optparse.SUPPRESS_HELP: subcommands += opt._long_opts + opt._short_opts @@ -112,13 +117,13 @@ def autocomplete() -> None: if completion_type: subcommands = list(auto_complete_paths(current, completion_type)) - print(" ".join([x for x in subcommands if x.startswith(current)])) + print(' '.join([x for x in subcommands if x.startswith(current)])) sys.exit(1) def get_path_completion_type( - cwords: List[str], cword: int, opts: Iterable[Any] -) -> Optional[str]: + cwords: list[str], cword: int, opts: Iterable[Any], +) -> str | None: """Get the type of path completion (``file``, ``dir``, ``path`` or None) :param cwords: same as the environmental variable ``COMP_WORDS`` @@ -126,15 +131,15 @@ def get_path_completion_type( :param opts: The available options to check :return: path completion type (``file``, ``dir``, ``path`` or None) """ - if cword < 2 or not cwords[cword - 2].startswith("-"): + if cword < 2 or not cwords[cword - 2].startswith('-'): return None for opt in opts: if opt.help == optparse.SUPPRESS_HELP: continue - for o in str(opt).split("/"): - if cwords[cword - 2].split("=")[0] == o: + for o in str(opt).split('/'): + if cwords[cword - 2].split('=')[0] == o: if not opt.metavar or any( - x in ("path", "file", "dir") for x in opt.metavar.split("/") + x in ('path', 'file', 'dir') for x in opt.metavar.split('/') ): return opt.metavar return None @@ -165,7 +170,7 @@ def auto_complete_paths(current: str, completion_type: str) -> Iterable[str]: # complete regular files when there is not ```` after option # complete directories when there is ````, ```` or # ````after option - if completion_type != "dir" and os.path.isfile(opt): + if completion_type != 'dir' and os.path.isfile(opt): yield comp_file elif os.path.isdir(opt): - yield os.path.join(comp_file, "") + yield os.path.join(comp_file, '') diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/base_command.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/base_command.py index 78b96bb..a66f2b2 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/base_command.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/base_command.py @@ -1,49 +1,52 @@ """Base Command class, and related routines""" +from __future__ import annotations import functools -import logging import logging.config import optparse import os import sys import traceback from optparse import Values -from typing import Any, Callable, List, Optional, Tuple - -from pip._vendor.rich import traceback as rich_traceback +from typing import Any +from typing import Callable +from typing import List +from typing import Optional +from typing import Tuple from pip._internal.cli import cmdoptions from pip._internal.cli.command_context import CommandContextMixIn -from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter -from pip._internal.cli.status_codes import ( - ERROR, - PREVIOUS_BUILD_DIR_ERROR, - UNKNOWN_ERROR, - VIRTUALENV_NOT_FOUND, -) -from pip._internal.exceptions import ( - BadCommand, - CommandError, - DiagnosticPipError, - InstallationError, - NetworkConnectionError, - PreviousBuildDirError, - UninstallationError, -) +from pip._internal.cli.parser import ConfigOptionParser +from pip._internal.cli.parser import UpdatingDefaultsHelpFormatter +from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import PREVIOUS_BUILD_DIR_ERROR +from pip._internal.cli.status_codes import UNKNOWN_ERROR +from pip._internal.cli.status_codes import VIRTUALENV_NOT_FOUND +from pip._internal.exceptions import BadCommand +from pip._internal.exceptions import CommandError +from pip._internal.exceptions import DiagnosticPipError +from pip._internal.exceptions import InstallationError +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.exceptions import PreviousBuildDirError +from pip._internal.exceptions import UninstallationError from pip._internal.utils.filesystem import check_path_owner -from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging -from pip._internal.utils.misc import get_prog, normalize_path +from pip._internal.utils.logging import BrokenStdoutLoggingError +from pip._internal.utils.logging import setup_logging +from pip._internal.utils.misc import get_prog +from pip._internal.utils.misc import normalize_path +from pip._internal.utils.temp_dir import global_tempdir_manager +from pip._internal.utils.temp_dir import tempdir_registry from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry as TempDirRegistry -from pip._internal.utils.temp_dir import global_tempdir_manager, tempdir_registry from pip._internal.utils.virtualenv import running_under_virtualenv +from pip._vendor.rich import traceback as rich_traceback -__all__ = ["Command"] +__all__ = ['Command'] logger = logging.getLogger(__name__) class Command(CommandContextMixIn): - usage: str = "" + usage: str = '' ignore_require_venv: bool = False def __init__(self, name: str, summary: str, isolated: bool = False) -> None: @@ -53,7 +56,7 @@ class Command(CommandContextMixIn): self.summary = summary self.parser = ConfigOptionParser( usage=self.usage, - prog=f"{get_prog()} {name}", + prog=f'{get_prog()} {name}', formatter=UpdatingDefaultsHelpFormatter(), add_help_option=False, name=name, @@ -61,10 +64,10 @@ class Command(CommandContextMixIn): isolated=isolated, ) - self.tempdir_registry: Optional[TempDirRegistry] = None + self.tempdir_registry: TempDirRegistry | None = None # Commands should add options to this option group - optgroup_name = f"{self.name.capitalize()} Options" + optgroup_name = f'{self.name.capitalize()} Options' self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) # Add the general options @@ -86,23 +89,23 @@ class Command(CommandContextMixIn): """ # Make sure we do the pip version check if the index_group options # are present. - assert not hasattr(options, "no_index") + assert not hasattr(options, 'no_index') - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: raise NotImplementedError - def parse_args(self, args: List[str]) -> Tuple[Values, List[str]]: + def parse_args(self, args: list[str]) -> tuple[Values, list[str]]: # factored out for testability return self.parser.parse_args(args) - def main(self, args: List[str]) -> int: + def main(self, args: list[str]) -> int: try: with self.main_context(): return self._main(args) finally: logging.shutdown() - def _main(self, args: List[str]) -> int: + def _main(self, args: list[str]) -> int: # We must initialize this before the tempdir manager, otherwise the # configuration would not be accessible by the time we clean up the # tempdir manager. @@ -127,15 +130,15 @@ class Command(CommandContextMixIn): # This also affects isolated builds and it should. if options.no_input: - os.environ["PIP_NO_INPUT"] = "1" + os.environ['PIP_NO_INPUT'] = '1' if options.exists_action: - os.environ["PIP_EXISTS_ACTION"] = " ".join(options.exists_action) + os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) if options.require_venv and not self.ignore_require_venv: # If a venv is required check if it can really be found if not running_under_virtualenv(): - logger.critical("Could not find an activated virtualenv (required).") + logger.critical('Could not find an activated virtualenv (required).') sys.exit(VIRTUALENV_NOT_FOUND) if options.cache_dir: @@ -143,23 +146,23 @@ class Command(CommandContextMixIn): if not check_path_owner(options.cache_dir): logger.warning( "The directory '%s' or its parent directory is not owned " - "or is not writable by the current user. The cache " - "has been disabled. Check the permissions and owner of " - "that directory. If executing pip with sudo, you should " + 'or is not writable by the current user. The cache ' + 'has been disabled. Check the permissions and owner of ' + 'that directory. If executing pip with sudo, you should ' "use sudo's -H flag.", options.cache_dir, ) options.cache_dir = None - if "2020-resolver" in options.features_enabled: + if '2020-resolver' in options.features_enabled: logger.warning( - "--use-feature=2020-resolver no longer has any effect, " - "since it is now the default dependency resolver in pip. " - "This will become an error in pip 21.0." + '--use-feature=2020-resolver no longer has any effect, ' + 'since it is now the default dependency resolver in pip. ' + 'This will become an error in pip 21.0.', ) def intercepts_unhandled_exc( - run_func: Callable[..., int] + run_func: Callable[..., int], ) -> Callable[..., int]: @functools.wraps(run_func) def exc_logging_wrapper(*args: Any) -> int: @@ -168,13 +171,13 @@ class Command(CommandContextMixIn): assert isinstance(status, int) return status except DiagnosticPipError as exc: - logger.error("[present-diagnostic] %s", exc) - logger.debug("Exception information:", exc_info=True) + logger.error('[present-diagnostic] %s', exc) + logger.debug('Exception information:', exc_info=True) return ERROR except PreviousBuildDirError as exc: logger.critical(str(exc)) - logger.debug("Exception information:", exc_info=True) + logger.debug('Exception information:', exc_info=True) return PREVIOUS_BUILD_DIR_ERROR except ( @@ -184,29 +187,29 @@ class Command(CommandContextMixIn): NetworkConnectionError, ) as exc: logger.critical(str(exc)) - logger.debug("Exception information:", exc_info=True) + logger.debug('Exception information:', exc_info=True) return ERROR except CommandError as exc: - logger.critical("%s", exc) - logger.debug("Exception information:", exc_info=True) + logger.critical('%s', exc) + logger.debug('Exception information:', exc_info=True) return ERROR except BrokenStdoutLoggingError: # Bypass our logger and write any remaining messages to # stderr because stdout no longer works. - print("ERROR: Pipe to stdout was broken", file=sys.stderr) + print('ERROR: Pipe to stdout was broken', file=sys.stderr) if level_number <= logging.DEBUG: traceback.print_exc(file=sys.stderr) return ERROR except KeyboardInterrupt: - logger.critical("Operation cancelled by user") - logger.debug("Exception information:", exc_info=True) + logger.critical('Operation cancelled by user') + logger.debug('Exception information:', exc_info=True) return ERROR except BaseException: - logger.critical("Exception:", exc_info=True) + logger.critical('Exception:', exc_info=True) return UNKNOWN_ERROR diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/cmdoptions.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/cmdoptions.py index 71b1d19..4e72e50 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/cmdoptions.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/cmdoptions.py @@ -6,29 +6,37 @@ globally. One reason being that options with action='append' can carry state between parses. pip parses general options twice internally, and shouldn't pass on state. To be consistent, all options will follow this design. """ - # The following comment should be removed at some point in the future. # mypy: strict-optional=False +from __future__ import annotations import logging import os import textwrap from functools import partial -from optparse import SUPPRESS_HELP, Option, OptionGroup, OptionParser, Values +from optparse import Option +from optparse import OptionGroup +from optparse import OptionParser +from optparse import SUPPRESS_HELP +from optparse import Values from textwrap import dedent -from typing import Any, Callable, Dict, Optional, Tuple - -from pip._vendor.packaging.utils import canonicalize_name +from typing import Any +from typing import Callable +from typing import Dict +from typing import Optional +from typing import Tuple from pip._internal.cli.parser import ConfigOptionParser from pip._internal.cli.progress_bars import BAR_TYPES from pip._internal.exceptions import CommandError -from pip._internal.locations import USER_CACHE_DIR, get_src_prefix +from pip._internal.locations import get_src_prefix +from pip._internal.locations import USER_CACHE_DIR from pip._internal.models.format_control import FormatControl from pip._internal.models.index import PyPI from pip._internal.models.target_python import TargetPython from pip._internal.utils.hashes import STRONG_HASHES from pip._internal.utils.misc import strtobool +from pip._vendor.packaging.utils import canonicalize_name logger = logging.getLogger(__name__) @@ -42,25 +50,25 @@ def raise_option_error(parser: OptionParser, option: Option, msg: str) -> None: option: an Option instance. msg: the error text. """ - msg = f"{option} error: {msg}" - msg = textwrap.fill(" ".join(msg.split())) + msg = f'{option} error: {msg}' + msg = textwrap.fill(' '.join(msg.split())) parser.error(msg) -def make_option_group(group: Dict[str, Any], parser: ConfigOptionParser) -> OptionGroup: +def make_option_group(group: dict[str, Any], parser: ConfigOptionParser) -> OptionGroup: """ Return an OptionGroup object group -- assumed to be dict with 'name' and 'options' keys parser -- an optparse Parser """ - option_group = OptionGroup(parser, group["name"]) - for option in group["options"]: + option_group = OptionGroup(parser, group['name']) + for option in group['options']: option_group.add_option(option()) return option_group def check_install_build_global( - options: Values, check_options: Optional[Values] = None + options: Values, check_options: Values | None = None, ) -> None: """Disable wheels if per-setup.py call options are set. @@ -71,16 +79,16 @@ def check_install_build_global( if check_options is None: check_options = options - def getname(n: str) -> Optional[Any]: + def getname(n: str) -> Any | None: return getattr(check_options, n, None) - names = ["build_options", "global_options", "install_options"] + names = ['build_options', 'global_options', 'install_options'] if any(map(getname, names)): control = options.format_control control.disallow_binaries() logger.warning( - "Disabling all use of wheels due to the use of --build-option " - "/ --global-option / --install-option.", + 'Disabling all use of wheels due to the use of --build-option ' + '/ --global-option / --install-option.', ) @@ -96,10 +104,10 @@ def check_dist_restriction(options: Values, check_target: bool = False) -> None: options.platforms, options.abis, options.implementation, - ] + ], ) - binary_only = FormatControl(set(), {":all:"}) + binary_only = FormatControl(set(), {':all:'}) sdist_dependencies_allowed = ( options.format_control != binary_only and not options.ignore_dependencies ) @@ -109,18 +117,18 @@ def check_dist_restriction(options: Values, check_target: bool = False) -> None: # guaranteed to be locally compatible. if dist_restriction_set and sdist_dependencies_allowed: raise CommandError( - "When restricting platform and interpreter constraints using " - "--python-version, --platform, --abi, or --implementation, " - "either --no-deps must be set, or --only-binary=:all: must be " - "set and --no-binary must not be set (or must be set to " - ":none:)." + 'When restricting platform and interpreter constraints using ' + '--python-version, --platform, --abi, or --implementation, ' + 'either --no-deps must be set, or --only-binary=:all: must be ' + 'set and --no-binary must not be set (or must be set to ' + ':none:).', ) if check_target: if dist_restriction_set and not options.target_dir: raise CommandError( - "Can not use any platform or abi specific options unless " - "installing via '--target'" + 'Can not use any platform or abi specific options unless ' + "installing via '--target'", ) @@ -133,10 +141,10 @@ def _package_name_option_check(option: Option, opt: str, value: str) -> str: class PipOption(Option): - TYPES = Option.TYPES + ("path", "package_name") + TYPES = Option.TYPES + ('path', 'package_name') TYPE_CHECKER = Option.TYPE_CHECKER.copy() - TYPE_CHECKER["package_name"] = _package_name_option_check - TYPE_CHECKER["path"] = _path_option_check + TYPE_CHECKER['package_name'] = _package_name_option_check + TYPE_CHECKER['path'] = _path_option_check ########### @@ -145,300 +153,300 @@ class PipOption(Option): help_: Callable[..., Option] = partial( Option, - "-h", - "--help", - dest="help", - action="help", - help="Show help.", + '-h', + '--help', + dest='help', + action='help', + help='Show help.', ) debug_mode: Callable[..., Option] = partial( Option, - "--debug", - dest="debug_mode", - action="store_true", + '--debug', + dest='debug_mode', + action='store_true', default=False, help=( - "Let unhandled exceptions propagate outside the main subroutine, " - "instead of logging them to stderr." + 'Let unhandled exceptions propagate outside the main subroutine, ' + 'instead of logging them to stderr.' ), ) isolated_mode: Callable[..., Option] = partial( Option, - "--isolated", - dest="isolated_mode", - action="store_true", + '--isolated', + dest='isolated_mode', + action='store_true', default=False, help=( - "Run pip in an isolated mode, ignoring environment variables and user " - "configuration." + 'Run pip in an isolated mode, ignoring environment variables and user ' + 'configuration.' ), ) require_virtualenv: Callable[..., Option] = partial( Option, - "--require-virtualenv", - "--require-venv", - dest="require_venv", - action="store_true", + '--require-virtualenv', + '--require-venv', + dest='require_venv', + action='store_true', default=False, help=( - "Allow pip to only run in a virtual environment; " - "exit with an error otherwise." + 'Allow pip to only run in a virtual environment; ' + 'exit with an error otherwise.' ), ) verbose: Callable[..., Option] = partial( Option, - "-v", - "--verbose", - dest="verbose", - action="count", + '-v', + '--verbose', + dest='verbose', + action='count', default=0, - help="Give more output. Option is additive, and can be used up to 3 times.", + help='Give more output. Option is additive, and can be used up to 3 times.', ) no_color: Callable[..., Option] = partial( Option, - "--no-color", - dest="no_color", - action="store_true", + '--no-color', + dest='no_color', + action='store_true', default=False, - help="Suppress colored output.", + help='Suppress colored output.', ) version: Callable[..., Option] = partial( Option, - "-V", - "--version", - dest="version", - action="store_true", - help="Show version and exit.", + '-V', + '--version', + dest='version', + action='store_true', + help='Show version and exit.', ) quiet: Callable[..., Option] = partial( Option, - "-q", - "--quiet", - dest="quiet", - action="count", + '-q', + '--quiet', + dest='quiet', + action='count', default=0, help=( - "Give less output. Option is additive, and can be used up to 3" - " times (corresponding to WARNING, ERROR, and CRITICAL logging" - " levels)." + 'Give less output. Option is additive, and can be used up to 3' + ' times (corresponding to WARNING, ERROR, and CRITICAL logging' + ' levels).' ), ) progress_bar: Callable[..., Option] = partial( Option, - "--progress-bar", - dest="progress_bar", - type="choice", + '--progress-bar', + dest='progress_bar', + type='choice', choices=list(BAR_TYPES.keys()), - default="on", + default='on', help=( - "Specify type of progress to be displayed [" - + "|".join(BAR_TYPES.keys()) - + "] (default: %default)" + 'Specify type of progress to be displayed [' + + '|'.join(BAR_TYPES.keys()) + + '] (default: %default)' ), ) log: Callable[..., Option] = partial( PipOption, - "--log", - "--log-file", - "--local-log", - dest="log", - metavar="path", - type="path", - help="Path to a verbose appending log.", + '--log', + '--log-file', + '--local-log', + dest='log', + metavar='path', + type='path', + help='Path to a verbose appending log.', ) no_input: Callable[..., Option] = partial( Option, # Don't ask for input - "--no-input", - dest="no_input", - action="store_true", + '--no-input', + dest='no_input', + action='store_true', default=False, - help="Disable prompting for input.", + help='Disable prompting for input.', ) proxy: Callable[..., Option] = partial( Option, - "--proxy", - dest="proxy", - type="str", - default="", - help="Specify a proxy in the form [user:passwd@]proxy.server:port.", + '--proxy', + dest='proxy', + type='str', + default='', + help='Specify a proxy in the form [user:passwd@]proxy.server:port.', ) retries: Callable[..., Option] = partial( Option, - "--retries", - dest="retries", - type="int", + '--retries', + dest='retries', + type='int', default=5, - help="Maximum number of retries each connection should attempt " - "(default %default times).", + help='Maximum number of retries each connection should attempt ' + '(default %default times).', ) timeout: Callable[..., Option] = partial( Option, - "--timeout", - "--default-timeout", - metavar="sec", - dest="timeout", - type="float", + '--timeout', + '--default-timeout', + metavar='sec', + dest='timeout', + type='float', default=15, - help="Set the socket timeout (default %default seconds).", + help='Set the socket timeout (default %default seconds).', ) def exists_action() -> Option: return Option( # Option when path already exist - "--exists-action", - dest="exists_action", - type="choice", - choices=["s", "i", "w", "b", "a"], + '--exists-action', + dest='exists_action', + type='choice', + choices=['s', 'i', 'w', 'b', 'a'], default=[], - action="append", - metavar="action", - help="Default action when a path already exists: " - "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.", + action='append', + metavar='action', + help='Default action when a path already exists: ' + '(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.', ) cert: Callable[..., Option] = partial( PipOption, - "--cert", - dest="cert", - type="path", - metavar="path", + '--cert', + dest='cert', + type='path', + metavar='path', help=( - "Path to PEM-encoded CA certificate bundle. " - "If provided, overrides the default. " + 'Path to PEM-encoded CA certificate bundle. ' + 'If provided, overrides the default. ' "See 'SSL Certificate Verification' in pip documentation " - "for more information." + 'for more information.' ), ) client_cert: Callable[..., Option] = partial( PipOption, - "--client-cert", - dest="client_cert", - type="path", + '--client-cert', + dest='client_cert', + type='path', default=None, - metavar="path", - help="Path to SSL client certificate, a single file containing the " - "private key and the certificate in PEM format.", + metavar='path', + help='Path to SSL client certificate, a single file containing the ' + 'private key and the certificate in PEM format.', ) index_url: Callable[..., Option] = partial( Option, - "-i", - "--index-url", - "--pypi-url", - dest="index_url", - metavar="URL", + '-i', + '--index-url', + '--pypi-url', + dest='index_url', + metavar='URL', default=PyPI.simple_url, - help="Base URL of the Python Package Index (default %default). " - "This should point to a repository compliant with PEP 503 " - "(the simple repository API) or a local directory laid out " - "in the same format.", + help='Base URL of the Python Package Index (default %default). ' + 'This should point to a repository compliant with PEP 503 ' + '(the simple repository API) or a local directory laid out ' + 'in the same format.', ) def extra_index_url() -> Option: return Option( - "--extra-index-url", - dest="extra_index_urls", - metavar="URL", - action="append", + '--extra-index-url', + dest='extra_index_urls', + metavar='URL', + action='append', default=[], - help="Extra URLs of package indexes to use in addition to " - "--index-url. Should follow the same rules as " - "--index-url.", + help='Extra URLs of package indexes to use in addition to ' + '--index-url. Should follow the same rules as ' + '--index-url.', ) no_index: Callable[..., Option] = partial( Option, - "--no-index", - dest="no_index", - action="store_true", + '--no-index', + dest='no_index', + action='store_true', default=False, - help="Ignore package index (only looking at --find-links URLs instead).", + help='Ignore package index (only looking at --find-links URLs instead).', ) def find_links() -> Option: return Option( - "-f", - "--find-links", - dest="find_links", - action="append", + '-f', + '--find-links', + dest='find_links', + action='append', default=[], - metavar="url", - help="If a URL or path to an html file, then parse for links to " - "archives such as sdist (.tar.gz) or wheel (.whl) files. " + metavar='url', + help='If a URL or path to an html file, then parse for links to ' + 'archives such as sdist (.tar.gz) or wheel (.whl) files. ' "If a local path or file:// URL that's a directory, " - "then look for archives in the directory listing. " - "Links to VCS project URLs are not supported.", + 'then look for archives in the directory listing. ' + 'Links to VCS project URLs are not supported.', ) def trusted_host() -> Option: return Option( - "--trusted-host", - dest="trusted_hosts", - action="append", - metavar="HOSTNAME", + '--trusted-host', + dest='trusted_hosts', + action='append', + metavar='HOSTNAME', default=[], - help="Mark this host or host:port pair as trusted, even though it " - "does not have valid or any HTTPS.", + help='Mark this host or host:port pair as trusted, even though it ' + 'does not have valid or any HTTPS.', ) def constraints() -> Option: return Option( - "-c", - "--constraint", - dest="constraints", - action="append", + '-c', + '--constraint', + dest='constraints', + action='append', default=[], - metavar="file", - help="Constrain versions using the given constraints file. " - "This option can be used multiple times.", + metavar='file', + help='Constrain versions using the given constraints file. ' + 'This option can be used multiple times.', ) def requirements() -> Option: return Option( - "-r", - "--requirement", - dest="requirements", - action="append", + '-r', + '--requirement', + dest='requirements', + action='append', default=[], - metavar="file", - help="Install from the given requirements file. " - "This option can be used multiple times.", + metavar='file', + help='Install from the given requirements file. ' + 'This option can be used multiple times.', ) def editable() -> Option: return Option( - "-e", - "--editable", - dest="editables", - action="append", + '-e', + '--editable', + dest='editables', + action='append', default=[], - metavar="path/url", + metavar='path/url', help=( - "Install a project in editable mode (i.e. setuptools " + 'Install a project in editable mode (i.e. setuptools ' '"develop mode") from a local project path or a VCS url.' ), ) @@ -451,17 +459,17 @@ def _handle_src(option: Option, opt_str: str, value: str, parser: OptionParser) src: Callable[..., Option] = partial( PipOption, - "--src", - "--source", - "--source-dir", - "--source-directory", - dest="src_dir", - type="path", - metavar="dir", + '--src', + '--source', + '--source-dir', + '--source-directory', + dest='src_dir', + type='path', + metavar='dir', default=get_src_prefix(), - action="callback", + action='callback', callback=_handle_src, - help="Directory to check out editable projects into. " + help='Directory to check out editable projects into. ' 'The default in a virtualenv is "/src". ' 'The default for global installs is "/src".', ) @@ -473,7 +481,7 @@ def _get_format_control(values: Values, option: Option) -> Any: def _handle_no_binary( - option: Option, opt_str: str, value: str, parser: OptionParser + option: Option, opt_str: str, value: str, parser: OptionParser, ) -> None: existing = _get_format_control(parser.values, option) FormatControl.handle_mutual_excludes( @@ -484,7 +492,7 @@ def _handle_no_binary( def _handle_only_binary( - option: Option, opt_str: str, value: str, parser: OptionParser + option: Option, opt_str: str, value: str, parser: OptionParser, ) -> None: existing = _get_format_control(parser.values, option) FormatControl.handle_mutual_excludes( @@ -497,56 +505,56 @@ def _handle_only_binary( def no_binary() -> Option: format_control = FormatControl(set(), set()) return Option( - "--no-binary", - dest="format_control", - action="callback", + '--no-binary', + dest='format_control', + action='callback', callback=_handle_no_binary, - type="str", + type='str', default=format_control, - help="Do not use binary packages. Can be supplied multiple times, and " + help='Do not use binary packages. Can be supplied multiple times, and ' 'each time adds to the existing value. Accepts either ":all:" to ' 'disable all binary packages, ":none:" to empty the set (notice ' - "the colons), or one or more package names with commas between " - "them (no colons). Note that some packages are tricky to compile " - "and may fail to install when this option is used on them.", + 'the colons), or one or more package names with commas between ' + 'them (no colons). Note that some packages are tricky to compile ' + 'and may fail to install when this option is used on them.', ) def only_binary() -> Option: format_control = FormatControl(set(), set()) return Option( - "--only-binary", - dest="format_control", - action="callback", + '--only-binary', + dest='format_control', + action='callback', callback=_handle_only_binary, - type="str", + type='str', default=format_control, - help="Do not use source packages. Can be supplied multiple times, and " + help='Do not use source packages. Can be supplied multiple times, and ' 'each time adds to the existing value. Accepts either ":all:" to ' 'disable all source packages, ":none:" to empty the set, or one ' - "or more package names with commas between them. Packages " - "without binary distributions will fail to install when this " - "option is used on them.", + 'or more package names with commas between them. Packages ' + 'without binary distributions will fail to install when this ' + 'option is used on them.', ) platforms: Callable[..., Option] = partial( Option, - "--platform", - dest="platforms", - metavar="platform", - action="append", + '--platform', + dest='platforms', + metavar='platform', + action='append', default=None, help=( - "Only use wheels compatible with . Defaults to the " - "platform of the running system. Use this option multiple times to " - "specify multiple platforms supported by the target interpreter." + 'Only use wheels compatible with . Defaults to the ' + 'platform of the running system. Use this option multiple times to ' + 'specify multiple platforms supported by the target interpreter.' ), ) # This was made a separate function for unit-testing purposes. -def _convert_python_version(value: str) -> Tuple[Tuple[int, ...], Optional[str]]: +def _convert_python_version(value: str) -> tuple[tuple[int, ...], str | None]: """ Convert a version string like "3", "37", or "3.7.3" into a tuple of ints. @@ -557,9 +565,9 @@ def _convert_python_version(value: str) -> Tuple[Tuple[int, ...], Optional[str]] # The empty string is the same as not providing a value. return (None, None) - parts = value.split(".") + parts = value.split('.') if len(parts) > 3: - return ((), "at most three version parts are allowed") + return ((), 'at most three version parts are allowed') if len(parts) == 1: # Then we are in the case of "3" or "37". @@ -570,20 +578,20 @@ def _convert_python_version(value: str) -> Tuple[Tuple[int, ...], Optional[str]] try: version_info = tuple(int(part) for part in parts) except ValueError: - return ((), "each version part must be an integer") + return ((), 'each version part must be an integer') return (version_info, None) def _handle_python_version( - option: Option, opt_str: str, value: str, parser: OptionParser + option: Option, opt_str: str, value: str, parser: OptionParser, ) -> None: """ Handle a provided --python-version value. """ version_info, error_msg = _convert_python_version(value) if error_msg is not None: - msg = "invalid --python-version value: {!r}: {}".format( + msg = 'invalid --python-version value: {!r}: {}'.format( value, error_msg, ) @@ -594,12 +602,12 @@ def _handle_python_version( python_version: Callable[..., Option] = partial( Option, - "--python-version", - dest="python_version", - metavar="python_version", - action="callback", + '--python-version', + dest='python_version', + metavar='python_version', + action='callback', callback=_handle_python_version, - type="str", + type='str', default=None, help=dedent( """\ @@ -608,41 +616,41 @@ python_version: Callable[..., Option] = partial( interpreter. The version can be specified using up to three dot-separated integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor version can also be given as a string without dots (e.g. "37" for 3.7.0). - """ + """, ), ) implementation: Callable[..., Option] = partial( Option, - "--implementation", - dest="implementation", - metavar="implementation", + '--implementation', + dest='implementation', + metavar='implementation', default=None, help=( - "Only use wheels compatible with Python " + 'Only use wheels compatible with Python ' "implementation , e.g. 'pp', 'jy', 'cp', " " or 'ip'. If not specified, then the current " "interpreter implementation is used. Use 'py' to force " - "implementation-agnostic wheels." + 'implementation-agnostic wheels.' ), ) abis: Callable[..., Option] = partial( Option, - "--abi", - dest="abis", - metavar="abi", - action="append", + '--abi', + dest='abis', + metavar='abi', + action='append', default=None, help=( "Only use wheels compatible with Python abi , e.g. 'pypy_41'. " - "If not specified, then the current interpreter abi tag is used. " - "Use this option multiple times to specify multiple abis supported " - "by the target interpreter. Generally you will need to specify " - "--implementation, --platform, and --python-version when using this " - "option." + 'If not specified, then the current interpreter abi tag is used. ' + 'Use this option multiple times to specify multiple abis supported ' + 'by the target interpreter. Generally you will need to specify ' + '--implementation, --platform, and --python-version when using this ' + 'option.' ), ) @@ -667,27 +675,27 @@ def make_target_python(options: Values) -> TargetPython: def prefer_binary() -> Option: return Option( - "--prefer-binary", - dest="prefer_binary", - action="store_true", + '--prefer-binary', + dest='prefer_binary', + action='store_true', default=False, - help="Prefer older binary packages over newer source packages.", + help='Prefer older binary packages over newer source packages.', ) cache_dir: Callable[..., Option] = partial( PipOption, - "--cache-dir", - dest="cache_dir", + '--cache-dir', + dest='cache_dir', default=USER_CACHE_DIR, - metavar="dir", - type="path", - help="Store the cache data in .", + metavar='dir', + type='path', + help='Store the cache data in .', ) def _handle_no_cache_dir( - option: Option, opt: str, value: str, parser: OptionParser + option: Option, opt: str, value: str, parser: OptionParser, ) -> None: """ Process a value provided for the --no-cache-dir option. @@ -717,45 +725,45 @@ def _handle_no_cache_dir( no_cache: Callable[..., Option] = partial( Option, - "--no-cache-dir", - dest="cache_dir", - action="callback", + '--no-cache-dir', + dest='cache_dir', + action='callback', callback=_handle_no_cache_dir, - help="Disable the cache.", + help='Disable the cache.', ) no_deps: Callable[..., Option] = partial( Option, - "--no-deps", - "--no-dependencies", - dest="ignore_dependencies", - action="store_true", + '--no-deps', + '--no-dependencies', + dest='ignore_dependencies', + action='store_true', default=False, help="Don't install package dependencies.", ) ignore_requires_python: Callable[..., Option] = partial( Option, - "--ignore-requires-python", - dest="ignore_requires_python", - action="store_true", - help="Ignore the Requires-Python information.", + '--ignore-requires-python', + dest='ignore_requires_python', + action='store_true', + help='Ignore the Requires-Python information.', ) no_build_isolation: Callable[..., Option] = partial( Option, - "--no-build-isolation", - dest="build_isolation", - action="store_false", + '--no-build-isolation', + dest='build_isolation', + action='store_false', default=True, - help="Disable isolation when building a modern source distribution. " - "Build dependencies specified by PEP 518 must be already installed " - "if this option is used.", + help='Disable isolation when building a modern source distribution. ' + 'Build dependencies specified by PEP 518 must be already installed ' + 'if this option is used.', ) def _handle_no_use_pep517( - option: Option, opt: str, value: str, parser: OptionParser + option: Option, opt: str, value: str, parser: OptionParser, ) -> None: """ Process a value provided for the --no-use-pep517 option. @@ -781,19 +789,19 @@ def _handle_no_use_pep517( use_pep517: Any = partial( Option, - "--use-pep517", - dest="use_pep517", - action="store_true", + '--use-pep517', + dest='use_pep517', + action='store_true', default=None, - help="Use PEP 517 for building source distributions " - "(use --no-use-pep517 to force legacy behaviour).", + help='Use PEP 517 for building source distributions ' + '(use --no-use-pep517 to force legacy behaviour).', ) no_use_pep517: Any = partial( Option, - "--no-use-pep517", - dest="use_pep517", - action="callback", + '--no-use-pep517', + dest='use_pep517', + action='callback', callback=_handle_no_use_pep517, default=None, help=SUPPRESS_HELP, @@ -801,122 +809,122 @@ no_use_pep517: Any = partial( install_options: Callable[..., Option] = partial( Option, - "--install-option", - dest="install_options", - action="append", - metavar="options", - help="Extra arguments to be supplied to the setup.py install " + '--install-option', + dest='install_options', + action='append', + metavar='options', + help='Extra arguments to be supplied to the setup.py install ' 'command (use like --install-option="--install-scripts=/usr/local/' 'bin"). Use multiple --install-option options to pass multiple ' - "options to setup.py install. If you are using an option with a " - "directory path, be sure to use absolute path.", + 'options to setup.py install. If you are using an option with a ' + 'directory path, be sure to use absolute path.', ) build_options: Callable[..., Option] = partial( Option, - "--build-option", - dest="build_options", - metavar="options", - action="append", + '--build-option', + dest='build_options', + metavar='options', + action='append', help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", ) global_options: Callable[..., Option] = partial( Option, - "--global-option", - dest="global_options", - action="append", - metavar="options", - help="Extra global options to be supplied to the setup.py " - "call before the install or bdist_wheel command.", + '--global-option', + dest='global_options', + action='append', + metavar='options', + help='Extra global options to be supplied to the setup.py ' + 'call before the install or bdist_wheel command.', ) no_clean: Callable[..., Option] = partial( Option, - "--no-clean", - action="store_true", + '--no-clean', + action='store_true', default=False, help="Don't clean up build directories.", ) pre: Callable[..., Option] = partial( Option, - "--pre", - action="store_true", + '--pre', + action='store_true', default=False, - help="Include pre-release and development versions. By default, " - "pip only finds stable versions.", + help='Include pre-release and development versions. By default, ' + 'pip only finds stable versions.', ) disable_pip_version_check: Callable[..., Option] = partial( Option, - "--disable-pip-version-check", - dest="disable_pip_version_check", - action="store_true", + '--disable-pip-version-check', + dest='disable_pip_version_check', + action='store_true', default=False, help="Don't periodically check PyPI to determine whether a new version " - "of pip is available for download. Implied with --no-index.", + 'of pip is available for download. Implied with --no-index.', ) def _handle_merge_hash( - option: Option, opt_str: str, value: str, parser: OptionParser + option: Option, opt_str: str, value: str, parser: OptionParser, ) -> None: """Given a value spelled "algo:digest", append the digest to a list pointed to in a dict by the algo name.""" if not parser.values.hashes: parser.values.hashes = {} try: - algo, digest = value.split(":", 1) + algo, digest = value.split(':', 1) except ValueError: parser.error( - "Arguments to {} must be a hash name " # noqa - "followed by a value, like --hash=sha256:" - "abcde...".format(opt_str) + 'Arguments to {} must be a hash name ' # noqa + 'followed by a value, like --hash=sha256:' + 'abcde...'.format(opt_str), ) if algo not in STRONG_HASHES: parser.error( - "Allowed hash algorithms for {} are {}.".format( # noqa - opt_str, ", ".join(STRONG_HASHES) - ) + 'Allowed hash algorithms for {} are {}.'.format( # noqa + opt_str, ', '.join(STRONG_HASHES), + ), ) parser.values.hashes.setdefault(algo, []).append(digest) hash: Callable[..., Option] = partial( Option, - "--hash", + '--hash', # Hash values eventually end up in InstallRequirement.hashes due to # __dict__ copying in process_line(). - dest="hashes", - action="callback", + dest='hashes', + action='callback', callback=_handle_merge_hash, - type="string", + type='string', help="Verify that the package's archive matches this " - "hash before installing. Example: --hash=sha256:abcdef...", + 'hash before installing. Example: --hash=sha256:abcdef...', ) require_hashes: Callable[..., Option] = partial( Option, - "--require-hashes", - dest="require_hashes", - action="store_true", + '--require-hashes', + dest='require_hashes', + action='store_true', default=False, - help="Require a hash to check each requirement against, for " - "repeatable installs. This option is implied when any package in a " - "requirements file has a --hash option.", + help='Require a hash to check each requirement against, for ' + 'repeatable installs. This option is implied when any package in a ' + 'requirements file has a --hash option.', ) list_path: Callable[..., Option] = partial( PipOption, - "--path", - dest="path", - type="path", - action="append", - help="Restrict to the specified installation path for listing " - "packages (can be used multiple times).", + '--path', + dest='path', + type='path', + action='append', + help='Restrict to the specified installation path for listing ' + 'packages (can be used multiple times).', ) @@ -927,50 +935,50 @@ def check_list_path_option(options: Values) -> None: list_exclude: Callable[..., Option] = partial( PipOption, - "--exclude", - dest="excludes", - action="append", - metavar="package", - type="package_name", - help="Exclude specified package from the output", + '--exclude', + dest='excludes', + action='append', + metavar='package', + type='package_name', + help='Exclude specified package from the output', ) no_python_version_warning: Callable[..., Option] = partial( Option, - "--no-python-version-warning", - dest="no_python_version_warning", - action="store_true", + '--no-python-version-warning', + dest='no_python_version_warning', + action='store_true', default=False, - help="Silence deprecation warnings for upcoming unsupported Pythons.", + help='Silence deprecation warnings for upcoming unsupported Pythons.', ) use_new_feature: Callable[..., Option] = partial( Option, - "--use-feature", - dest="features_enabled", - metavar="feature", - action="append", + '--use-feature', + dest='features_enabled', + metavar='feature', + action='append', default=[], - choices=["2020-resolver", "fast-deps", "in-tree-build"], - help="Enable new functionality, that may be backward incompatible.", + choices=['2020-resolver', 'fast-deps', 'in-tree-build'], + help='Enable new functionality, that may be backward incompatible.', ) use_deprecated_feature: Callable[..., Option] = partial( Option, - "--use-deprecated", - dest="deprecated_features_enabled", - metavar="feature", - action="append", + '--use-deprecated', + dest='deprecated_features_enabled', + metavar='feature', + action='append', default=[], choices=[ - "legacy-resolver", - "out-of-tree-build", - "backtrack-on-build-failures", - "html5lib", + 'legacy-resolver', + 'out-of-tree-build', + 'backtrack-on-build-failures', + 'html5lib', ], - help=("Enable deprecated functionality, that will be removed in the future."), + help=('Enable deprecated functionality, that will be removed in the future.'), ) @@ -978,9 +986,9 @@ use_deprecated_feature: Callable[..., Option] = partial( # groups # ########## -general_group: Dict[str, Any] = { - "name": "General Options", - "options": [ +general_group: dict[str, Any] = { + 'name': 'General Options', + 'options': [ help_, debug_mode, isolated_mode, @@ -1007,9 +1015,9 @@ general_group: Dict[str, Any] = { ], } -index_group: Dict[str, Any] = { - "name": "Package Index Options", - "options": [ +index_group: dict[str, Any] = { + 'name': 'Package Index Options', + 'options': [ index_url, extra_index_url, no_index, diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/command_context.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/command_context.py index ed68322..6455919 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/command_context.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/command_context.py @@ -1,7 +1,12 @@ -from contextlib import ExitStack, contextmanager -from typing import ContextManager, Iterator, TypeVar +from __future__ import annotations -_T = TypeVar("_T", covariant=True) +from contextlib import contextmanager +from contextlib import ExitStack +from typing import ContextManager +from typing import Iterator +from typing import TypeVar + +_T = TypeVar('_T', covariant=True) class CommandContextMixIn: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/main.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/main.py index 0e31221..b828555 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/main.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/main.py @@ -1,10 +1,13 @@ """Primary application entrypoint. """ +from __future__ import annotations + import locale import logging import os import sys -from typing import List, Optional +from typing import List +from typing import Optional from pip._internal.cli.autocompletion import autocomplete from pip._internal.cli.main_parser import parse_command @@ -42,7 +45,7 @@ logger = logging.getLogger(__name__) # main, this should not be an issue in practice. -def main(args: Optional[List[str]] = None) -> int: +def main(args: list[str] | None = None) -> int: if args is None: args = sys.argv[1:] @@ -54,17 +57,17 @@ def main(args: Optional[List[str]] = None) -> int: try: cmd_name, cmd_args = parse_command(args) except PipError as exc: - sys.stderr.write(f"ERROR: {exc}") + sys.stderr.write(f'ERROR: {exc}') sys.stderr.write(os.linesep) sys.exit(1) # Needed for locale.getpreferredencoding(False) to work # in pip._internal.utils.encoding.auto_decode try: - locale.setlocale(locale.LC_ALL, "") + locale.setlocale(locale.LC_ALL, '') except locale.Error as e: # setlocale can apparently crash if locale are uninitialized - logger.debug("Ignoring error %s when setting locale", e) - command = create_command(cmd_name, isolated=("--isolated" in cmd_args)) + logger.debug('Ignoring error %s when setting locale', e) + command = create_command(cmd_name, isolated=('--isolated' in cmd_args)) return command.main(cmd_args) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/main_parser.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/main_parser.py index 3666ab0..7b79c01 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/main_parser.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/main_parser.py @@ -1,27 +1,32 @@ """A single place for constructing and exposing the main parser """ +from __future__ import annotations import os import sys -from typing import List, Tuple +from typing import List +from typing import Tuple from pip._internal.cli import cmdoptions -from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter -from pip._internal.commands import commands_dict, get_similar_commands +from pip._internal.cli.parser import ConfigOptionParser +from pip._internal.cli.parser import UpdatingDefaultsHelpFormatter +from pip._internal.commands import commands_dict +from pip._internal.commands import get_similar_commands from pip._internal.exceptions import CommandError -from pip._internal.utils.misc import get_pip_version, get_prog +from pip._internal.utils.misc import get_pip_version +from pip._internal.utils.misc import get_prog -__all__ = ["create_main_parser", "parse_command"] +__all__ = ['create_main_parser', 'parse_command'] def create_main_parser() -> ConfigOptionParser: """Creates and returns the main parser for pip's CLI""" parser = ConfigOptionParser( - usage="\n%prog [options]", + usage='\n%prog [options]', add_help_option=False, formatter=UpdatingDefaultsHelpFormatter(), - name="global", + name='global', prog=get_prog(), ) parser.disable_interspersed_args() @@ -36,16 +41,16 @@ def create_main_parser() -> ConfigOptionParser: parser.main = True # type: ignore # create command listing for description - description = [""] + [ - f"{name:27} {command_info.summary}" + description = [''] + [ + f'{name:27} {command_info.summary}' for name, command_info in commands_dict.items() ] - parser.description = "\n".join(description) + parser.description = '\n'.join(description) return parser -def parse_command(args: List[str]) -> Tuple[str, List[str]]: +def parse_command(args: list[str]) -> tuple[str, list[str]]: parser = create_main_parser() # Note: parser calls disable_interspersed_args(), so the result of this @@ -64,7 +69,7 @@ def parse_command(args: List[str]) -> Tuple[str, List[str]]: sys.exit() # pip || pip help -> print_help() - if not args_else or (args_else[0] == "help" and len(args_else) == 1): + if not args_else or (args_else[0] == 'help' and len(args_else) == 1): parser.print_help() sys.exit() @@ -78,7 +83,7 @@ def parse_command(args: List[str]) -> Tuple[str, List[str]]: if guess: msg.append(f'maybe you meant "{guess}"') - raise CommandError(" - ".join(msg)) + raise CommandError(' - '.join(msg)) # all the args without the subcommand cmd_args = args[:] diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/parser.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/parser.py index a1c99a8..52ea601 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/parser.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/parser.py @@ -1,4 +1,5 @@ """Base option parser setup""" +from __future__ import annotations import logging import optparse @@ -6,11 +7,17 @@ import shutil import sys import textwrap from contextlib import suppress -from typing import Any, Dict, Iterator, List, Tuple +from typing import Any +from typing import Dict +from typing import Iterator +from typing import List +from typing import Tuple from pip._internal.cli.status_codes import UNKNOWN_ERROR -from pip._internal.configuration import Configuration, ConfigurationError -from pip._internal.utils.misc import redact_auth_from_url, strtobool +from pip._internal.configuration import Configuration +from pip._internal.configuration import ConfigurationError +from pip._internal.utils.misc import redact_auth_from_url +from pip._internal.utils.misc import strtobool logger = logging.getLogger(__name__) @@ -20,16 +27,16 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter): def __init__(self, *args: Any, **kwargs: Any) -> None: # help position must be aligned with __init__.parseopts.description - kwargs["max_help_position"] = 30 - kwargs["indent_increment"] = 1 - kwargs["width"] = shutil.get_terminal_size()[0] - 2 + kwargs['max_help_position'] = 30 + kwargs['indent_increment'] = 1 + kwargs['width'] = shutil.get_terminal_size()[0] - 2 super().__init__(*args, **kwargs) def format_option_strings(self, option: optparse.Option) -> str: return self._format_option_strings(option) def _format_option_strings( - self, option: optparse.Option, mvarfmt: str = " <{}>", optsep: str = ", " + self, option: optparse.Option, mvarfmt: str = ' <{}>', optsep: str = ', ', ) -> str: """ Return a comma-separated list of option strings and metavars. @@ -52,49 +59,49 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter): metavar = option.metavar or option.dest.lower() opts.append(mvarfmt.format(metavar.lower())) - return "".join(opts) + return ''.join(opts) def format_heading(self, heading: str) -> str: - if heading == "Options": - return "" - return heading + ":\n" + if heading == 'Options': + return '' + return heading + ':\n' def format_usage(self, usage: str) -> str: """ Ensure there is only one newline between usage and the first heading if there is no description. """ - msg = "\nUsage: {}\n".format(self.indent_lines(textwrap.dedent(usage), " ")) + msg = '\nUsage: {}\n'.format(self.indent_lines(textwrap.dedent(usage), ' ')) return msg def format_description(self, description: str) -> str: # leave full control over description to us if description: - if hasattr(self.parser, "main"): - label = "Commands" + if hasattr(self.parser, 'main'): + label = 'Commands' else: - label = "Description" + label = 'Description' # some doc strings have initial newlines, some don't - description = description.lstrip("\n") + description = description.lstrip('\n') # some doc strings have final newlines and spaces, some don't description = description.rstrip() # dedent, then reindent - description = self.indent_lines(textwrap.dedent(description), " ") - description = f"{label}:\n{description}\n" + description = self.indent_lines(textwrap.dedent(description), ' ') + description = f'{label}:\n{description}\n' return description else: - return "" + return '' def format_epilog(self, epilog: str) -> str: # leave full control over epilog to us if epilog: return epilog else: - return "" + return '' def indent_lines(self, text: str, indent: str) -> str: - new_lines = [indent + line for line in text.split("\n")] - return "\n".join(new_lines) + new_lines = [indent + line for line in text.split('\n')] + return '\n'.join(new_lines) class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): @@ -115,7 +122,7 @@ class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): default_values = self.parser.defaults.get(option.dest) help_text = super().expand_default(option) - if default_values and option.metavar == "URL": + if default_values and option.metavar == 'URL': if isinstance(default_values, str): default_values = [default_values] @@ -131,7 +138,7 @@ class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): class CustomOptionParser(optparse.OptionParser): def insert_option_group( - self, idx: int, *args: Any, **kwargs: Any + self, idx: int, *args: Any, **kwargs: Any, ) -> optparse.OptionGroup: """Insert an OptionGroup at a given position.""" group = self.add_option_group(*args, **kwargs) @@ -142,7 +149,7 @@ class CustomOptionParser(optparse.OptionParser): return group @property - def option_list_all(self) -> List[optparse.Option]: + def option_list_all(self) -> list[optparse.Option]: """Get a list of all options, including those in option groups.""" res = self.option_list[:] for i in self.option_groups: @@ -172,15 +179,15 @@ class ConfigOptionParser(CustomOptionParser): try: return option.check_value(key, val) except optparse.OptionValueError as exc: - print(f"An error occurred during configuration: {exc}") + print(f'An error occurred during configuration: {exc}') sys.exit(3) - def _get_ordered_configuration_items(self) -> Iterator[Tuple[str, Any]]: + def _get_ordered_configuration_items(self) -> Iterator[tuple[str, Any]]: # Configuration gives keys in an unordered manner. Order them. - override_order = ["global", self.name, ":env:"] + override_order = ['global', self.name, ':env:'] # Pool the options into different groups - section_items: Dict[str, List[Tuple[str, Any]]] = { + section_items: dict[str, list[tuple[str, Any]]] = { name: [] for name in override_order } for section_key, val in self.config.items(): @@ -192,7 +199,7 @@ class ConfigOptionParser(CustomOptionParser): ) continue - section, key = section_key.split(".", 1) + section, key = section_key.split('.', 1) if section in override_order: section_items[section].append((key, val)) @@ -201,7 +208,7 @@ class ConfigOptionParser(CustomOptionParser): for key, val in section_items[section]: yield key, val - def _update_defaults(self, defaults: Dict[str, Any]) -> Dict[str, Any]: + def _update_defaults(self, defaults: dict[str, Any]) -> dict[str, Any]: """Updates the given defaults with values from the config files and the environ. Does a little special handling for certain types of options (lists).""" @@ -212,7 +219,7 @@ class ConfigOptionParser(CustomOptionParser): # Then set the options with those values for key, val in self._get_ordered_configuration_items(): # '--' because configuration supports only long names - option = self.get_option("--" + key) + option = self.get_option('--' + key) # Ignore options not present in this parser. E.g. non-globals put # in [global] by users that want them to apply to all applicable @@ -222,31 +229,31 @@ class ConfigOptionParser(CustomOptionParser): assert option.dest is not None - if option.action in ("store_true", "store_false"): + if option.action in ('store_true', 'store_false'): try: val = strtobool(val) except ValueError: self.error( - "{} is not a valid value for {} option, " # noqa - "please specify a boolean value like yes/no, " - "true/false or 1/0 instead.".format(val, key) + '{} is not a valid value for {} option, ' # noqa + 'please specify a boolean value like yes/no, ' + 'true/false or 1/0 instead.'.format(val, key), ) - elif option.action == "count": + elif option.action == 'count': with suppress(ValueError): val = strtobool(val) with suppress(ValueError): val = int(val) if not isinstance(val, int) or val < 0: self.error( - "{} is not a valid value for {} option, " # noqa - "please instead specify either a non-negative integer " - "or a boolean value like yes/no or false/true " - "which is equivalent to 1/0.".format(val, key) + '{} is not a valid value for {} option, ' # noqa + 'please instead specify either a non-negative integer ' + 'or a boolean value like yes/no or false/true ' + 'which is equivalent to 1/0.'.format(val, key), ) - elif option.action == "append": + elif option.action == 'append': val = val.split() val = [self.check_default(option, key, v) for v in val] - elif option.action == "callback": + elif option.action == 'callback': assert option.callback is not None late_eval.add(option.dest) opt_str = option.get_opt_string() @@ -289,4 +296,4 @@ class ConfigOptionParser(CustomOptionParser): def error(self, msg: str) -> None: self.print_usage(sys.stderr) - self.exit(UNKNOWN_ERROR, f"{msg}\n") + self.exit(UNKNOWN_ERROR, f'{msg}\n') diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/progress_bars.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/progress_bars.py index ffa1964..200f5a9 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/progress_bars.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/progress_bars.py @@ -1,27 +1,34 @@ +from __future__ import annotations + import functools import itertools import sys -from signal import SIGINT, default_int_handler, signal -from typing import Any, Callable, Iterator, Optional, Tuple - -from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar -from pip._vendor.progress.spinner import Spinner -from pip._vendor.rich.progress import ( - BarColumn, - DownloadColumn, - FileSizeColumn, - Progress, - ProgressColumn, - SpinnerColumn, - TextColumn, - TimeElapsedColumn, - TimeRemainingColumn, - TransferSpeedColumn, -) +from signal import default_int_handler +from signal import SIGINT +from signal import signal +from typing import Any +from typing import Callable +from typing import Iterator +from typing import Optional +from typing import Tuple from pip._internal.utils.compat import WINDOWS from pip._internal.utils.logging import get_indentation from pip._internal.utils.misc import format_size +from pip._vendor.progress.bar import Bar +from pip._vendor.progress.bar import FillingCirclesBar +from pip._vendor.progress.bar import IncrementalBar +from pip._vendor.progress.spinner import Spinner +from pip._vendor.rich.progress import BarColumn +from pip._vendor.rich.progress import DownloadColumn +from pip._vendor.rich.progress import FileSizeColumn +from pip._vendor.rich.progress import Progress +from pip._vendor.rich.progress import ProgressColumn +from pip._vendor.rich.progress import SpinnerColumn +from pip._vendor.rich.progress import TextColumn +from pip._vendor.rich.progress import TimeElapsedColumn +from pip._vendor.rich.progress import TimeRemainingColumn +from pip._vendor.rich.progress import TransferSpeedColumn try: from pip._vendor import colorama @@ -34,7 +41,7 @@ DownloadProgressRenderer = Callable[[Iterator[bytes]], Iterator[bytes]] def _select_progress_class(preferred: Bar, fallback: Bar) -> Bar: - encoding = getattr(preferred.file, "encoding", None) + encoding = getattr(preferred.file, 'encoding', None) # If we don't know what encoding this file is in, then we'll just assume # that it doesn't support unicode and use the ASCII bar. @@ -44,16 +51,16 @@ def _select_progress_class(preferred: Bar, fallback: Bar) -> Bar: # Collect all of the possible characters we want to use with the preferred # bar. characters = [ - getattr(preferred, "empty_fill", ""), - getattr(preferred, "fill", ""), + getattr(preferred, 'empty_fill', ''), + getattr(preferred, 'fill', ''), ] - characters += list(getattr(preferred, "phases", [])) + characters += list(getattr(preferred, 'phases', [])) # Try to decode the characters we're using for the bar using the encoding # of the given file, if this works then we'll assume that we can use the # fancier bar and if not we'll fall back to the plaintext bar. try: - "".join(characters).encode(encoding) + ''.join(characters).encode(encoding) except UnicodeEncodeError: return fallback else: @@ -126,17 +133,17 @@ class SilentBar(Bar): class BlueEmojiBar(IncrementalBar): - suffix = "%(percent)d%%" - bar_prefix = " " - bar_suffix = " " - phases = ("\U0001F539", "\U0001F537", "\U0001F535") + suffix = '%(percent)d%%' + bar_prefix = ' ' + bar_suffix = ' ' + phases = ('\U0001F539', '\U0001F537', '\U0001F535') class DownloadProgressMixin: def __init__(self, *args: Any, **kwargs: Any) -> None: # https://github.com/python/mypy/issues/5887 super().__init__(*args, **kwargs) # type: ignore - self.message: str = (" " * (get_indentation() + 2)) + self.message + self.message: str = (' ' * (get_indentation() + 2)) + self.message @property def downloaded(self) -> str: @@ -146,14 +153,14 @@ class DownloadProgressMixin: def download_speed(self) -> str: # Avoid zero division errors... if self.avg == 0.0: # type: ignore - return "..." - return format_size(1 / self.avg) + "/s" # type: ignore + return '...' + return format_size(1 / self.avg) + '/s' # type: ignore @property def pretty_eta(self) -> str: if self.eta: # type: ignore - return f"eta {self.eta_td}" # type: ignore - return "" + return f'eta {self.eta_td}' # type: ignore + return '' def iter(self, it): # type: ignore for x in it: @@ -196,8 +203,8 @@ class WindowsMixin: class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, DownloadProgressMixin): file = sys.stdout - message = "%(percent)d%%" - suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" + message = '%(percent)d%%' + suffix = '%(downloaded)s %(download_speed)s %(pretty_eta)s' class DefaultDownloadProgressBar(BaseDownloadProgressBar, _BaseBar): @@ -221,14 +228,14 @@ class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, BlueEmojiBar): class DownloadProgressSpinner( - WindowsMixin, InterruptibleMixin, DownloadProgressMixin, Spinner + WindowsMixin, InterruptibleMixin, DownloadProgressMixin, Spinner, ): file = sys.stdout - suffix = "%(downloaded)s %(download_speed)s" + suffix = '%(downloaded)s %(download_speed)s' def next_phase(self) -> str: - if not hasattr(self, "_phaser"): + if not hasattr(self, '_phaser'): self._phaser = itertools.cycle(self.phases) return next(self._phaser) @@ -236,30 +243,30 @@ class DownloadProgressSpinner( message = self.message % self phase = self.next_phase() suffix = self.suffix % self - line = "".join( + line = ''.join( [ message, - " " if message else "", + ' ' if message else '', phase, - " " if suffix else "", + ' ' if suffix else '', suffix, - ] + ], ) self.writeln(line) BAR_TYPES = { - "off": (DownloadSilentBar, DownloadSilentBar), - "on": (DefaultDownloadProgressBar, DownloadProgressSpinner), - "ascii": (DownloadBar, DownloadProgressSpinner), - "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner), - "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner), + 'off': (DownloadSilentBar, DownloadSilentBar), + 'on': (DefaultDownloadProgressBar, DownloadProgressSpinner), + 'ascii': (DownloadBar, DownloadProgressSpinner), + 'pretty': (DownloadFillingCirclesBar, DownloadProgressSpinner), + 'emoji': (DownloadBlueEmojiProgressBar, DownloadProgressSpinner), } def _legacy_progress_bar( - progress_bar: str, max: Optional[int] + progress_bar: str, max: int | None, ) -> DownloadProgressRenderer: if max is None or max == 0: return BAR_TYPES[progress_bar][1]().iter # type: ignore @@ -276,13 +283,13 @@ def _rich_progress_bar( bar_type: str, size: int, ) -> Iterator[bytes]: - assert bar_type == "on", "This should only be used in the default mode." + assert bar_type == 'on', 'This should only be used in the default mode.' if not size: - total = float("inf") - columns: Tuple[ProgressColumn, ...] = ( - TextColumn("[progress.description]{task.description}"), - SpinnerColumn("line", speed=1.5), + total = float('inf') + columns: tuple[ProgressColumn, ...] = ( + TextColumn('[progress.description]{task.description}'), + SpinnerColumn('line', speed=1.5), FileSizeColumn(), TransferSpeedColumn(), TimeElapsedColumn(), @@ -290,16 +297,16 @@ def _rich_progress_bar( else: total = size columns = ( - TextColumn("[progress.description]{task.description}"), + TextColumn('[progress.description]{task.description}'), BarColumn(), DownloadColumn(), TransferSpeedColumn(), - TextColumn("eta"), + TextColumn('eta'), TimeRemainingColumn(), ) progress = Progress(*columns, refresh_per_second=30) - task_id = progress.add_task(" " * (get_indentation() + 2), total=total) + task_id = progress.add_task(' ' * (get_indentation() + 2), total=total) with progress: for chunk in iterable: yield chunk @@ -307,15 +314,15 @@ def _rich_progress_bar( def get_download_progress_renderer( - *, bar_type: str, size: Optional[int] = None + *, bar_type: str, size: int | None = None, ) -> DownloadProgressRenderer: """Get an object that can be used to render the download progress. Returns a callable, that takes an iterable to "wrap". """ - if bar_type == "on": + if bar_type == 'on': return functools.partial(_rich_progress_bar, bar_type=bar_type, size=size) - elif bar_type == "off": + elif bar_type == 'off': return iter # no-op, when passed an iterator else: return _legacy_progress_bar(bar_type, size) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/req_command.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/req_command.py index 5d4d1f0..e9e6551 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/req_command.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/req_command.py @@ -4,42 +4,43 @@ The classes in this module are in a separate module so the commands not needing download / PackageFinder capability don't unnecessarily import the PackageFinder machinery and all its vendored dependencies, etc. """ +from __future__ import annotations import logging import os import sys from functools import partial from optparse import Values -from typing import Any, List, Optional, Tuple +from typing import Any +from typing import List +from typing import Optional +from typing import Tuple from pip._internal.cache import WheelCache from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import Command from pip._internal.cli.command_context import CommandContextMixIn -from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.exceptions import CommandError +from pip._internal.exceptions import PreviousBuildDirError from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.models.target_python import TargetPython from pip._internal.network.session import PipSession from pip._internal.operations.prepare import RequirementPreparer -from pip._internal.req.constructors import ( - install_req_from_editable, - install_req_from_line, - install_req_from_parsed_requirement, - install_req_from_req_string, -) +from pip._internal.req.constructors import install_req_from_editable +from pip._internal.req.constructors import install_req_from_line +from pip._internal.req.constructors import install_req_from_parsed_requirement +from pip._internal.req.constructors import install_req_from_req_string from pip._internal.req.req_file import parse_requirements from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_tracker import RequirementTracker from pip._internal.resolution.base import BaseResolver from pip._internal.self_outdated_check import pip_self_version_check from pip._internal.utils.deprecation import deprecated -from pip._internal.utils.temp_dir import ( - TempDirectory, - TempDirectoryTypeRegistry, - tempdir_kinds, -) +from pip._internal.utils.temp_dir import tempdir_kinds +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry from pip._internal.utils.virtualenv import running_under_virtualenv logger = logging.getLogger(__name__) @@ -53,17 +54,17 @@ class SessionCommandMixin(CommandContextMixIn): def __init__(self) -> None: super().__init__() - self._session: Optional[PipSession] = None + self._session: PipSession | None = None @classmethod - def _get_index_urls(cls, options: Values) -> Optional[List[str]]: + def _get_index_urls(cls, options: Values) -> list[str] | None: """Return a list of index urls from user-provided options.""" index_urls = [] - if not getattr(options, "no_index", False): - url = getattr(options, "index_url", None) + if not getattr(options, 'no_index', False): + url = getattr(options, 'index_url', None) if url: index_urls.append(url) - urls = getattr(options, "extra_index_urls", None) + urls = getattr(options, 'extra_index_urls', None) if urls: index_urls.extend(urls) # Return None rather than an empty list @@ -82,13 +83,13 @@ class SessionCommandMixin(CommandContextMixIn): def _build_session( self, options: Values, - retries: Optional[int] = None, - timeout: Optional[int] = None, + retries: int | None = None, + timeout: int | None = None, ) -> PipSession: assert not options.cache_dir or os.path.isabs(options.cache_dir) session = PipSession( cache=( - os.path.join(options.cache_dir, "http") if options.cache_dir else None + os.path.join(options.cache_dir, 'http') if options.cache_dir else None ), retries=retries if retries is not None else options.retries, trusted_hosts=options.trusted_hosts, @@ -110,8 +111,8 @@ class SessionCommandMixin(CommandContextMixIn): # Handle configured proxies if options.proxy: session.proxies = { - "http": options.proxy, - "https": options.proxy, + 'http': options.proxy, + 'https': options.proxy, } # Determine if we can prompt the user for authentication or not @@ -135,14 +136,14 @@ class IndexGroupCommand(Command, SessionCommandMixin): This overrides the default behavior of not doing the check. """ # Make sure the index_group options are present. - assert hasattr(options, "no_index") + assert hasattr(options, 'no_index') if options.disable_pip_version_check or options.no_index: return # Otherwise, check if we're using the latest version of pip available. session = self._build_session( - options, retries=0, timeout=min(5, options.timeout) + options, retries=0, timeout=min(5, options.timeout), ) with session: pip_self_version_check(session, options) @@ -164,14 +165,14 @@ def warn_if_run_as_root() -> None: """ if running_under_virtualenv(): return - if not hasattr(os, "getuid"): + if not hasattr(os, 'getuid'): return # On Windows, there are no "system managed" Python packages. Installing as # Administrator via pip is the correct way of updating system environments. # # We choose sys.platform over utils.compat.WINDOWS here to enable Mypy platform # checks: https://mypy.readthedocs.io/en/stable/common_issues.html - if sys.platform == "win32" or sys.platform == "cygwin": + if sys.platform == 'win32' or sys.platform == 'cygwin': return if os.getuid() != 0: @@ -179,9 +180,9 @@ def warn_if_run_as_root() -> None: logger.warning( "Running pip as the 'root' user can result in broken permissions and " - "conflicting behaviour with the system package manager. " - "It is recommended to use a virtual environment instead: " - "https://pip.pypa.io/warnings/venv" + 'conflicting behaviour with the system package manager. ' + 'It is recommended to use a virtual environment instead: ' + 'https://pip.pypa.io/warnings/venv', ) @@ -195,8 +196,8 @@ def with_cleanup(func: Any) -> Any: registry.set_delete(t, False) def wrapper( - self: RequirementCommand, options: Values, args: List[Any] - ) -> Optional[int]: + self: RequirementCommand, options: Values, args: list[Any], + ) -> int | None: assert self.tempdir_registry is not None if options.no_clean: configure_tempdir_registry(self.tempdir_registry) @@ -222,30 +223,30 @@ class RequirementCommand(IndexGroupCommand): @staticmethod def determine_resolver_variant(options: Values) -> str: """Determines which resolver should be used, based on the given options.""" - if "legacy-resolver" in options.deprecated_features_enabled: - return "legacy" + if 'legacy-resolver' in options.deprecated_features_enabled: + return 'legacy' - return "2020-resolver" + return '2020-resolver' @staticmethod def determine_build_failure_suppression(options: Values) -> bool: """Determines whether build failures should be suppressed and backtracked on.""" - if "backtrack-on-build-failures" not in options.deprecated_features_enabled: + if 'backtrack-on-build-failures' not in options.deprecated_features_enabled: return False - if "legacy-resolver" in options.deprecated_features_enabled: - raise CommandError("Cannot backtrack with legacy resolver.") + if 'legacy-resolver' in options.deprecated_features_enabled: + raise CommandError('Cannot backtrack with legacy resolver.') deprecated( reason=( - "Backtracking on build failures can mask issues related to how " - "a package generates metadata or builds a wheel. This flag will " - "be removed in pip 22.2." + 'Backtracking on build failures can mask issues related to how ' + 'a package generates metadata or builds a wheel. This flag will ' + 'be removed in pip 22.2.' ), gone_in=None, replacement=( - "avoiding known-bad versions by explicitly telling pip to ignore them " - "(either directly as requirements, or via a constraints file)" + 'avoiding known-bad versions by explicitly telling pip to ignore them ' + '(either directly as requirements, or via a constraints file)' ), feature_flag=None, issue=10655, @@ -261,7 +262,7 @@ class RequirementCommand(IndexGroupCommand): session: PipSession, finder: PackageFinder, use_user_site: bool, - download_dir: Optional[str] = None, + download_dir: str | None = None, verbosity: int = 0, ) -> RequirementPreparer: """ @@ -271,42 +272,42 @@ class RequirementCommand(IndexGroupCommand): assert temp_build_dir_path is not None resolver_variant = cls.determine_resolver_variant(options) - if resolver_variant == "2020-resolver": - lazy_wheel = "fast-deps" in options.features_enabled + if resolver_variant == '2020-resolver': + lazy_wheel = 'fast-deps' in options.features_enabled if lazy_wheel: logger.warning( - "pip is using lazily downloaded wheels using HTTP " - "range requests to obtain dependency information. " - "This experimental feature is enabled through " - "--use-feature=fast-deps and it is not ready for " - "production." + 'pip is using lazily downloaded wheels using HTTP ' + 'range requests to obtain dependency information. ' + 'This experimental feature is enabled through ' + '--use-feature=fast-deps and it is not ready for ' + 'production.', ) else: lazy_wheel = False - if "fast-deps" in options.features_enabled: + if 'fast-deps' in options.features_enabled: logger.warning( - "fast-deps has no effect when used with the legacy resolver." + 'fast-deps has no effect when used with the legacy resolver.', ) - in_tree_build = "out-of-tree-build" not in options.deprecated_features_enabled - if "in-tree-build" in options.features_enabled: + in_tree_build = 'out-of-tree-build' not in options.deprecated_features_enabled + if 'in-tree-build' in options.features_enabled: deprecated( - reason="In-tree builds are now the default.", - replacement="to remove the --use-feature=in-tree-build flag", - gone_in="22.1", + reason='In-tree builds are now the default.', + replacement='to remove the --use-feature=in-tree-build flag', + gone_in='22.1', ) - if "out-of-tree-build" in options.deprecated_features_enabled: + if 'out-of-tree-build' in options.deprecated_features_enabled: deprecated( - reason="Out-of-tree builds are deprecated.", + reason='Out-of-tree builds are deprecated.', replacement=None, - gone_in="22.1", + gone_in='22.1', ) - if options.progress_bar not in {"on", "off"}: + if options.progress_bar not in {'on', 'off'}: deprecated( - reason="Custom progress bar styles are deprecated", - replacement="to use the default progress bar style.", - gone_in="22.1", + reason='Custom progress bar styles are deprecated', + replacement='to use the default progress bar style.', + gone_in='22.1', ) return RequirementPreparer( @@ -331,14 +332,14 @@ class RequirementCommand(IndexGroupCommand): preparer: RequirementPreparer, finder: PackageFinder, options: Values, - wheel_cache: Optional[WheelCache] = None, + wheel_cache: WheelCache | None = None, use_user_site: bool = False, ignore_installed: bool = True, ignore_requires_python: bool = False, force_reinstall: bool = False, - upgrade_strategy: str = "to-satisfy-only", - use_pep517: Optional[bool] = None, - py_version_info: Optional[Tuple[int, ...]] = None, + upgrade_strategy: str = 'to-satisfy-only', + use_pep517: bool | None = None, + py_version_info: tuple[int, ...] | None = None, ) -> BaseResolver: """ Create a Resolver instance for the given parameters. @@ -353,7 +354,7 @@ class RequirementCommand(IndexGroupCommand): # The long import name and duplicated invocation is needed to convince # Mypy into correctly typechecking. Otherwise it would complain the # "Resolver" class being redefined. - if resolver_variant == "2020-resolver": + if resolver_variant == '2020-resolver': import pip._internal.resolution.resolvelib.resolver return pip._internal.resolution.resolvelib.resolver.Resolver( @@ -388,15 +389,15 @@ class RequirementCommand(IndexGroupCommand): def get_requirements( self, - args: List[str], + args: list[str], options: Values, finder: PackageFinder, session: PipSession, - ) -> List[InstallRequirement]: + ) -> list[InstallRequirement]: """ Parse command-line arguments into the corresponding requirements. """ - requirements: List[InstallRequirement] = [] + requirements: list[InstallRequirement] = [] for filename in options.constraints: for parsed_req in parse_requirements( filename, @@ -434,7 +435,7 @@ class RequirementCommand(IndexGroupCommand): # NOTE: options.require_hashes may be set if --require-hashes is True for filename in options.requirements: for parsed_req in parse_requirements( - filename, finder=finder, options=options, session=session + filename, finder=finder, options=options, session=session, ): req_to_add = install_req_from_parsed_requirement( parsed_req, @@ -449,18 +450,18 @@ class RequirementCommand(IndexGroupCommand): options.require_hashes = True if not (args or options.editables or options.requirements): - opts = {"name": self.name} + opts = {'name': self.name} if options.find_links: raise CommandError( - "You must give at least one requirement to {name} " + 'You must give at least one requirement to {name} ' '(maybe you meant "pip {name} {links}"?)'.format( - **dict(opts, links=" ".join(options.find_links)) - ) + **dict(opts, links=' '.join(options.find_links)), + ), ) else: raise CommandError( - "You must give at least one requirement to {name} " - '(see "pip help {name}")'.format(**opts) + 'You must give at least one requirement to {name} ' + '(see "pip help {name}")'.format(**opts), ) return requirements @@ -480,8 +481,8 @@ class RequirementCommand(IndexGroupCommand): self, options: Values, session: PipSession, - target_python: Optional[TargetPython] = None, - ignore_requires_python: Optional[bool] = None, + target_python: TargetPython | None = None, + ignore_requires_python: bool | None = None, ) -> PackageFinder: """ Create a package finder appropriate to this requirement command. @@ -502,5 +503,5 @@ class RequirementCommand(IndexGroupCommand): link_collector=link_collector, selection_prefs=selection_prefs, target_python=target_python, - use_deprecated_html5lib="html5lib" in options.deprecated_features_enabled, + use_deprecated_html5lib='html5lib' in options.deprecated_features_enabled, ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/spinners.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/spinners.py index 1e313e1..3c38d50 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/spinners.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/spinners.py @@ -1,14 +1,17 @@ +from __future__ import annotations + import contextlib import itertools import logging import sys import time -from typing import IO, Iterator - -from pip._vendor.progress import HIDE_CURSOR, SHOW_CURSOR +from typing import IO +from typing import Iterator from pip._internal.utils.compat import WINDOWS from pip._internal.utils.logging import get_indentation +from pip._vendor.progress import HIDE_CURSOR +from pip._vendor.progress import SHOW_CURSOR logger = logging.getLogger(__name__) @@ -26,7 +29,7 @@ class InteractiveSpinner(SpinnerInterface): self, message: str, file: IO[str] = None, - spin_chars: str = "-\\|/", + spin_chars: str = '-\\|/', # Empirically, 8 updates/second looks nice min_update_interval_seconds: float = 0.125, ): @@ -39,15 +42,15 @@ class InteractiveSpinner(SpinnerInterface): self._spin_cycle = itertools.cycle(spin_chars) - self._file.write(" " * get_indentation() + self._message + " ... ") + self._file.write(' ' * get_indentation() + self._message + ' ... ') self._width = 0 def _write(self, status: str) -> None: assert not self._finished # Erase what we wrote before by backspacing to the beginning, writing # spaces to overwrite the old text, and then backspacing again - backup = "\b" * self._width - self._file.write(backup + " " * self._width + backup) + backup = '\b' * self._width + self._file.write(backup + ' ' * self._width + backup) # Now we have a blank slate to add our status self._file.write(status) self._width = len(status) @@ -65,7 +68,7 @@ class InteractiveSpinner(SpinnerInterface): if self._finished: return self._write(final_status) - self._file.write("\n") + self._file.write('\n') self._file.flush() self._finished = True @@ -79,19 +82,19 @@ class NonInteractiveSpinner(SpinnerInterface): self._message = message self._finished = False self._rate_limiter = RateLimiter(min_update_interval_seconds) - self._update("started") + self._update('started') def _update(self, status: str) -> None: assert not self._finished self._rate_limiter.reset() - logger.info("%s: %s", self._message, status) + logger.info('%s: %s', self._message, status) def spin(self) -> None: if self._finished: return if not self._rate_limiter.ready(): return - self._update("still running...") + self._update('still running...') def finish(self, final_status: str) -> None: if self._finished: @@ -129,13 +132,13 @@ def open_spinner(message: str) -> Iterator[SpinnerInterface]: with hidden_cursor(sys.stdout): yield spinner except KeyboardInterrupt: - spinner.finish("canceled") + spinner.finish('canceled') raise except Exception: - spinner.finish("error") + spinner.finish('error') raise else: - spinner.finish("done") + spinner.finish('done') @contextlib.contextmanager diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/cli/status_codes.py b/.venv/lib/python3.10/site-packages/pip/_internal/cli/status_codes.py index 5e29502..2eea687 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/cli/status_codes.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/cli/status_codes.py @@ -1,3 +1,4 @@ +from __future__ import annotations SUCCESS = 0 ERROR = 1 UNKNOWN_ERROR = 2 diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/__init__.py index c72f24f..135d5a9 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/__init__.py @@ -1,14 +1,17 @@ """ Package containing all pip commands """ +from __future__ import annotations import importlib from collections import namedtuple -from typing import Any, Dict, Optional +from typing import Any +from typing import Dict +from typing import Optional from pip._internal.cli.base_command import Command -CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary") +CommandInfo = namedtuple('CommandInfo', 'module_path, class_name, summary') # This dictionary does a bunch of heavy lifting for help output: # - Enables avoiding additional (costly) imports for presenting `--help`. @@ -17,86 +20,86 @@ CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary") # Even though the module path starts with the same "pip._internal.commands" # prefix, the full path makes testing easier (specifically when modifying # `commands_dict` in test setup / teardown). -commands_dict: Dict[str, CommandInfo] = { - "install": CommandInfo( - "pip._internal.commands.install", - "InstallCommand", - "Install packages.", +commands_dict: dict[str, CommandInfo] = { + 'install': CommandInfo( + 'pip._internal.commands.install', + 'InstallCommand', + 'Install packages.', ), - "download": CommandInfo( - "pip._internal.commands.download", - "DownloadCommand", - "Download packages.", + 'download': CommandInfo( + 'pip._internal.commands.download', + 'DownloadCommand', + 'Download packages.', ), - "uninstall": CommandInfo( - "pip._internal.commands.uninstall", - "UninstallCommand", - "Uninstall packages.", + 'uninstall': CommandInfo( + 'pip._internal.commands.uninstall', + 'UninstallCommand', + 'Uninstall packages.', ), - "freeze": CommandInfo( - "pip._internal.commands.freeze", - "FreezeCommand", - "Output installed packages in requirements format.", + 'freeze': CommandInfo( + 'pip._internal.commands.freeze', + 'FreezeCommand', + 'Output installed packages in requirements format.', ), - "list": CommandInfo( - "pip._internal.commands.list", - "ListCommand", - "List installed packages.", + 'list': CommandInfo( + 'pip._internal.commands.list', + 'ListCommand', + 'List installed packages.', ), - "show": CommandInfo( - "pip._internal.commands.show", - "ShowCommand", - "Show information about installed packages.", + 'show': CommandInfo( + 'pip._internal.commands.show', + 'ShowCommand', + 'Show information about installed packages.', ), - "check": CommandInfo( - "pip._internal.commands.check", - "CheckCommand", - "Verify installed packages have compatible dependencies.", + 'check': CommandInfo( + 'pip._internal.commands.check', + 'CheckCommand', + 'Verify installed packages have compatible dependencies.', ), - "config": CommandInfo( - "pip._internal.commands.configuration", - "ConfigurationCommand", - "Manage local and global configuration.", + 'config': CommandInfo( + 'pip._internal.commands.configuration', + 'ConfigurationCommand', + 'Manage local and global configuration.', ), - "search": CommandInfo( - "pip._internal.commands.search", - "SearchCommand", - "Search PyPI for packages.", + 'search': CommandInfo( + 'pip._internal.commands.search', + 'SearchCommand', + 'Search PyPI for packages.', ), - "cache": CommandInfo( - "pip._internal.commands.cache", - "CacheCommand", + 'cache': CommandInfo( + 'pip._internal.commands.cache', + 'CacheCommand', "Inspect and manage pip's wheel cache.", ), - "index": CommandInfo( - "pip._internal.commands.index", - "IndexCommand", - "Inspect information available from package indexes.", + 'index': CommandInfo( + 'pip._internal.commands.index', + 'IndexCommand', + 'Inspect information available from package indexes.', ), - "wheel": CommandInfo( - "pip._internal.commands.wheel", - "WheelCommand", - "Build wheels from your requirements.", + 'wheel': CommandInfo( + 'pip._internal.commands.wheel', + 'WheelCommand', + 'Build wheels from your requirements.', ), - "hash": CommandInfo( - "pip._internal.commands.hash", - "HashCommand", - "Compute hashes of package archives.", + 'hash': CommandInfo( + 'pip._internal.commands.hash', + 'HashCommand', + 'Compute hashes of package archives.', ), - "completion": CommandInfo( - "pip._internal.commands.completion", - "CompletionCommand", - "A helper command used for command completion.", + 'completion': CommandInfo( + 'pip._internal.commands.completion', + 'CompletionCommand', + 'A helper command used for command completion.', ), - "debug": CommandInfo( - "pip._internal.commands.debug", - "DebugCommand", - "Show information useful for debugging.", + 'debug': CommandInfo( + 'pip._internal.commands.debug', + 'DebugCommand', + 'Show information useful for debugging.', ), - "help": CommandInfo( - "pip._internal.commands.help", - "HelpCommand", - "Show help for commands.", + 'help': CommandInfo( + 'pip._internal.commands.help', + 'HelpCommand', + 'Show help for commands.', ), } @@ -113,7 +116,7 @@ def create_command(name: str, **kwargs: Any) -> Command: return command -def get_similar_commands(name: str) -> Optional[str]: +def get_similar_commands(name: str) -> str | None: """Command name auto-correct.""" from difflib import get_close_matches diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/cache.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/cache.py index f1a489d..05f7e6d 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/cache.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/cache.py @@ -1,12 +1,17 @@ +from __future__ import annotations + import os import textwrap from optparse import Values -from typing import Any, List +from typing import Any +from typing import List import pip._internal.utils.filesystem as filesystem from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR, SUCCESS -from pip._internal.exceptions import CommandError, PipError +from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.exceptions import PipError from pip._internal.utils.logging import getLogger logger = getLogger(__name__) @@ -39,34 +44,34 @@ class CacheCommand(Command): def add_options(self) -> None: self.cmd_opts.add_option( - "--format", - action="store", - dest="list_format", - default="human", - choices=("human", "abspath"), - help="Select the output format among: human (default) or abspath", + '--format', + action='store', + dest='list_format', + default='human', + choices=('human', 'abspath'), + help='Select the output format among: human (default) or abspath', ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: handlers = { - "dir": self.get_cache_dir, - "info": self.get_cache_info, - "list": self.list_cache_items, - "remove": self.remove_cache_items, - "purge": self.purge_cache, + 'dir': self.get_cache_dir, + 'info': self.get_cache_info, + 'list': self.list_cache_items, + 'remove': self.remove_cache_items, + 'purge': self.purge_cache, } if not options.cache_dir: - logger.error("pip cache commands can not function since cache is disabled.") + logger.error('pip cache commands can not function since cache is disabled.') return ERROR # Determine action if not args or args[0] not in handlers: logger.error( - "Need an action (%s) to perform.", - ", ".join(sorted(handlers)), + 'Need an action (%s) to perform.', + ', '.join(sorted(handlers)), ) return ERROR @@ -81,21 +86,21 @@ class CacheCommand(Command): return SUCCESS - def get_cache_dir(self, options: Values, args: List[Any]) -> None: + def get_cache_dir(self, options: Values, args: list[Any]) -> None: if args: - raise CommandError("Too many arguments") + raise CommandError('Too many arguments') logger.info(options.cache_dir) - def get_cache_info(self, options: Values, args: List[Any]) -> None: + def get_cache_info(self, options: Values, args: list[Any]) -> None: if args: - raise CommandError("Too many arguments") + raise CommandError('Too many arguments') num_http_files = len(self._find_http_files(options)) - num_packages = len(self._find_wheels(options, "*")) + num_packages = len(self._find_wheels(options, '*')) - http_cache_location = self._cache_dir(options, "http") - wheels_cache_location = self._cache_dir(options, "wheels") + http_cache_location = self._cache_dir(options, 'http') + wheels_cache_location = self._cache_dir(options, 'wheels') http_cache_size = filesystem.format_directory_size(http_cache_location) wheels_cache_size = filesystem.format_directory_size(wheels_cache_location) @@ -108,7 +113,7 @@ class CacheCommand(Command): Wheels location: {wheels_cache_location} Wheels size: {wheels_cache_size} Number of wheels: {package_count} - """ + """, ) .format( http_cache_location=http_cache_location, @@ -123,35 +128,35 @@ class CacheCommand(Command): logger.info(message) - def list_cache_items(self, options: Values, args: List[Any]) -> None: + def list_cache_items(self, options: Values, args: list[Any]) -> None: if len(args) > 1: - raise CommandError("Too many arguments") + raise CommandError('Too many arguments') if args: pattern = args[0] else: - pattern = "*" + pattern = '*' files = self._find_wheels(options, pattern) - if options.list_format == "human": + if options.list_format == 'human': self.format_for_human(files) else: self.format_for_abspath(files) - def format_for_human(self, files: List[str]) -> None: + def format_for_human(self, files: list[str]) -> None: if not files: - logger.info("Nothing cached.") + logger.info('Nothing cached.') return results = [] for filename in files: wheel = os.path.basename(filename) size = filesystem.format_file_size(filename) - results.append(f" - {wheel} ({size})") - logger.info("Cache contents:\n") - logger.info("\n".join(sorted(results))) + results.append(f' - {wheel} ({size})') + logger.info('Cache contents:\n') + logger.info('\n'.join(sorted(results))) - def format_for_abspath(self, files: List[str]) -> None: + def format_for_abspath(self, files: list[str]) -> None: if not files: return @@ -159,48 +164,48 @@ class CacheCommand(Command): for filename in files: results.append(filename) - logger.info("\n".join(sorted(results))) + logger.info('\n'.join(sorted(results))) - def remove_cache_items(self, options: Values, args: List[Any]) -> None: + def remove_cache_items(self, options: Values, args: list[Any]) -> None: if len(args) > 1: - raise CommandError("Too many arguments") + raise CommandError('Too many arguments') if not args: - raise CommandError("Please provide a pattern") + raise CommandError('Please provide a pattern') files = self._find_wheels(options, args[0]) - no_matching_msg = "No matching packages" - if args[0] == "*": + no_matching_msg = 'No matching packages' + if args[0] == '*': # Only fetch http files if no specific pattern given files += self._find_http_files(options) else: # Add the pattern to the log message - no_matching_msg += ' for pattern "{}"'.format(args[0]) + no_matching_msg += f' for pattern "{args[0]}"' if not files: logger.warning(no_matching_msg) for filename in files: os.unlink(filename) - logger.verbose("Removed %s", filename) - logger.info("Files removed: %s", len(files)) + logger.verbose('Removed %s', filename) + logger.info('Files removed: %s', len(files)) - def purge_cache(self, options: Values, args: List[Any]) -> None: + def purge_cache(self, options: Values, args: list[Any]) -> None: if args: - raise CommandError("Too many arguments") + raise CommandError('Too many arguments') - return self.remove_cache_items(options, ["*"]) + return self.remove_cache_items(options, ['*']) def _cache_dir(self, options: Values, subdir: str) -> str: return os.path.join(options.cache_dir, subdir) - def _find_http_files(self, options: Values) -> List[str]: - http_dir = self._cache_dir(options, "http") - return filesystem.find_files(http_dir, "*") + def _find_http_files(self, options: Values) -> list[str]: + http_dir = self._cache_dir(options, 'http') + return filesystem.find_files(http_dir, '*') - def _find_wheels(self, options: Values, pattern: str) -> List[str]: - wheel_dir = self._cache_dir(options, "wheels") + def _find_wheels(self, options: Values, pattern: str) -> list[str]: + wheel_dir = self._cache_dir(options, 'wheels') # The wheel filename format, as specified in PEP 427, is: # {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl @@ -218,6 +223,6 @@ class CacheCommand(Command): # match the hyphen before the version, followed by anything else. # # PEP 427: https://www.python.org/dev/peps/pep-0427/ - pattern = pattern + ("*.whl" if "-" in pattern else "-*.whl") + pattern = pattern + ('*.whl' if '-' in pattern else '-*.whl') return filesystem.find_files(wheel_dir, pattern) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/check.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/check.py index 3864220..dc3d763 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/check.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/check.py @@ -1,13 +1,14 @@ +from __future__ import annotations + import logging from optparse import Values from typing import List from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR, SUCCESS -from pip._internal.operations.check import ( - check_package_set, - create_package_set_from_installed, -) +from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.operations.check import check_package_set +from pip._internal.operations.check import create_package_set_from_installed from pip._internal.utils.misc import write_output logger = logging.getLogger(__name__) @@ -19,7 +20,7 @@ class CheckCommand(Command): usage = """ %prog [options]""" - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: package_set, parsing_probs = create_package_set_from_installed() missing, conflicting = check_package_set(package_set) @@ -28,7 +29,7 @@ class CheckCommand(Command): version = package_set[project_name].version for dependency in missing[project_name]: write_output( - "%s %s requires %s, which is not installed.", + '%s %s requires %s, which is not installed.', project_name, version, dependency[0], @@ -38,7 +39,7 @@ class CheckCommand(Command): version = package_set[project_name].version for dep_name, dep_version, req in conflicting[project_name]: write_output( - "%s %s has requirement %s, but you have %s %s.", + '%s %s has requirement %s, but you have %s %s.', project_name, version, req, @@ -49,5 +50,5 @@ class CheckCommand(Command): if missing or conflicting or parsing_probs: return ERROR else: - write_output("No broken requirements found.") + write_output('No broken requirements found.') return SUCCESS diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/completion.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/completion.py index c0fb4ca..117e723 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/completion.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/completion.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys import textwrap from optparse import Values @@ -12,7 +14,7 @@ BASE_COMPLETION = """ """ COMPLETION_SCRIPTS = { - "bash": """ + 'bash': """ _pip_completion() {{ COMPREPLY=( $( COMP_WORDS="${{COMP_WORDS[*]}}" \\ @@ -21,7 +23,7 @@ COMPLETION_SCRIPTS = { }} complete -o default -F _pip_completion {prog} """, - "zsh": """ + 'zsh': """ function _pip_completion {{ local words cword read -Ac words @@ -32,7 +34,7 @@ COMPLETION_SCRIPTS = { }} compctl -K _pip_completion {prog} """, - "fish": """ + 'fish': """ function __fish_complete_pip set -lx COMP_WORDS (commandline -o) "" set -lx COMP_CWORD ( \\ @@ -53,44 +55,44 @@ class CompletionCommand(Command): def add_options(self) -> None: self.cmd_opts.add_option( - "--bash", - "-b", - action="store_const", - const="bash", - dest="shell", - help="Emit completion code for bash", + '--bash', + '-b', + action='store_const', + const='bash', + dest='shell', + help='Emit completion code for bash', ) self.cmd_opts.add_option( - "--zsh", - "-z", - action="store_const", - const="zsh", - dest="shell", - help="Emit completion code for zsh", + '--zsh', + '-z', + action='store_const', + const='zsh', + dest='shell', + help='Emit completion code for zsh', ) self.cmd_opts.add_option( - "--fish", - "-f", - action="store_const", - const="fish", - dest="shell", - help="Emit completion code for fish", + '--fish', + '-f', + action='store_const', + const='fish', + dest='shell', + help='Emit completion code for fish', ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: """Prints the completion code of the given shell""" shells = COMPLETION_SCRIPTS.keys() - shell_options = ["--" + shell for shell in sorted(shells)] + shell_options = ['--' + shell for shell in sorted(shells)] if options.shell in shells: script = textwrap.dedent( - COMPLETION_SCRIPTS.get(options.shell, "").format(prog=get_prog()) + COMPLETION_SCRIPTS.get(options.shell, '').format(prog=get_prog()), ) print(BASE_COMPLETION.format(script=script, shell=options.shell)) return SUCCESS else: sys.stderr.write( - "ERROR: You must pass {}\n".format(" or ".join(shell_options)) + 'ERROR: You must pass {}\n'.format(' or '.join(shell_options)), ) return SUCCESS diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/configuration.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/configuration.py index c6c74ed..bbd7c9c 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/configuration.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/configuration.py @@ -1,20 +1,24 @@ +from __future__ import annotations + import logging import os import subprocess from optparse import Values -from typing import Any, List, Optional +from typing import Any +from typing import List +from typing import Optional from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR, SUCCESS -from pip._internal.configuration import ( - Configuration, - Kind, - get_configuration_files, - kinds, -) +from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.configuration import Configuration +from pip._internal.configuration import get_configuration_files +from pip._internal.configuration import Kind +from pip._internal.configuration import kinds from pip._internal.exceptions import PipError from pip._internal.utils.logging import indent_log -from pip._internal.utils.misc import get_prog, write_output +from pip._internal.utils.misc import get_prog +from pip._internal.utils.misc import write_output logger = logging.getLogger(__name__) @@ -51,57 +55,57 @@ class ConfigurationCommand(Command): def add_options(self) -> None: self.cmd_opts.add_option( - "--editor", - dest="editor", - action="store", + '--editor', + dest='editor', + action='store', default=None, help=( - "Editor to use to edit the file. Uses VISUAL or EDITOR " - "environment variables if not provided." + 'Editor to use to edit the file. Uses VISUAL or EDITOR ' + 'environment variables if not provided.' ), ) self.cmd_opts.add_option( - "--global", - dest="global_file", - action="store_true", + '--global', + dest='global_file', + action='store_true', default=False, - help="Use the system-wide configuration file only", + help='Use the system-wide configuration file only', ) self.cmd_opts.add_option( - "--user", - dest="user_file", - action="store_true", + '--user', + dest='user_file', + action='store_true', default=False, - help="Use the user configuration file only", + help='Use the user configuration file only', ) self.cmd_opts.add_option( - "--site", - dest="site_file", - action="store_true", + '--site', + dest='site_file', + action='store_true', default=False, - help="Use the current environment configuration file only", + help='Use the current environment configuration file only', ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: handlers = { - "list": self.list_values, - "edit": self.open_in_editor, - "get": self.get_name, - "set": self.set_name_value, - "unset": self.unset_name, - "debug": self.list_config_values, + 'list': self.list_values, + 'edit': self.open_in_editor, + 'get': self.get_name, + 'set': self.set_name_value, + 'unset': self.unset_name, + 'debug': self.list_config_values, } # Determine action if not args or args[0] not in handlers: logger.error( - "Need an action (%s) to perform.", - ", ".join(sorted(handlers)), + 'Need an action (%s) to perform.', + ', '.join(sorted(handlers)), ) return ERROR @@ -111,7 +115,7 @@ class ConfigurationCommand(Command): # Depends on whether the command is modifying. try: load_only = self._determine_file( - options, need_value=(action in ["get", "set", "unset", "edit"]) + options, need_value=(action in ['get', 'set', 'unset', 'edit']), ) except PipError as e: logger.error(e.args[0]) @@ -119,7 +123,7 @@ class ConfigurationCommand(Command): # Load a new configuration self.configuration = Configuration( - isolated=options.isolated_mode, load_only=load_only + isolated=options.isolated_mode, load_only=load_only, ) self.configuration.load() @@ -132,7 +136,7 @@ class ConfigurationCommand(Command): return SUCCESS - def _determine_file(self, options: Values, need_value: bool) -> Optional[Kind]: + def _determine_file(self, options: Values, need_value: bool) -> Kind | None: file_options = [ key for key, value in ( @@ -158,47 +162,47 @@ class ConfigurationCommand(Command): return file_options[0] raise PipError( - "Need exactly one file to operate upon " - "(--user, --site, --global) to perform." + 'Need exactly one file to operate upon ' + '(--user, --site, --global) to perform.', ) - def list_values(self, options: Values, args: List[str]) -> None: - self._get_n_args(args, "list", n=0) + def list_values(self, options: Values, args: list[str]) -> None: + self._get_n_args(args, 'list', n=0) for key, value in sorted(self.configuration.items()): - write_output("%s=%r", key, value) + write_output('%s=%r', key, value) - def get_name(self, options: Values, args: List[str]) -> None: - key = self._get_n_args(args, "get [name]", n=1) + def get_name(self, options: Values, args: list[str]) -> None: + key = self._get_n_args(args, 'get [name]', n=1) value = self.configuration.get_value(key) - write_output("%s", value) + write_output('%s', value) - def set_name_value(self, options: Values, args: List[str]) -> None: - key, value = self._get_n_args(args, "set [name] [value]", n=2) + def set_name_value(self, options: Values, args: list[str]) -> None: + key, value = self._get_n_args(args, 'set [name] [value]', n=2) self.configuration.set_value(key, value) self._save_configuration() - def unset_name(self, options: Values, args: List[str]) -> None: - key = self._get_n_args(args, "unset [name]", n=1) + def unset_name(self, options: Values, args: list[str]) -> None: + key = self._get_n_args(args, 'unset [name]', n=1) self.configuration.unset_value(key) self._save_configuration() - def list_config_values(self, options: Values, args: List[str]) -> None: + def list_config_values(self, options: Values, args: list[str]) -> None: """List config key-value pairs across different config files""" - self._get_n_args(args, "debug", n=0) + self._get_n_args(args, 'debug', n=0) self.print_env_var_values() # Iterate over config files and print if they exist, and the # key-value pairs present in them if they do for variant, files in sorted(self.configuration.iter_config_files()): - write_output("%s:", variant) + write_output('%s:', variant) for fname in files: with indent_log(): file_exists = os.path.exists(fname) - write_output("%s, exists: %r", fname, file_exists) + write_output('%s, exists: %r', fname, file_exists) if file_exists: self.print_config_file_values(variant) @@ -206,35 +210,35 @@ class ConfigurationCommand(Command): """Get key-value pairs from the file of a variant""" for name, value in self.configuration.get_values_in_config(variant).items(): with indent_log(): - write_output("%s: %s", name, value) + write_output('%s: %s', name, value) def print_env_var_values(self) -> None: """Get key-values pairs present as environment variables""" - write_output("%s:", "env_var") + write_output('%s:', 'env_var') with indent_log(): for key, value in sorted(self.configuration.get_environ_vars()): - env_var = f"PIP_{key.upper()}" - write_output("%s=%r", env_var, value) + env_var = f'PIP_{key.upper()}' + write_output('%s=%r', env_var, value) - def open_in_editor(self, options: Values, args: List[str]) -> None: + def open_in_editor(self, options: Values, args: list[str]) -> None: editor = self._determine_editor(options) fname = self.configuration.get_file_to_edit() if fname is None: - raise PipError("Could not determine appropriate file.") + raise PipError('Could not determine appropriate file.') try: subprocess.check_call([editor, fname]) except subprocess.CalledProcessError as e: raise PipError( - "Editor Subprocess exited with exit code {}".format(e.returncode) + f'Editor Subprocess exited with exit code {e.returncode}', ) - def _get_n_args(self, args: List[str], example: str, n: int) -> Any: + def _get_n_args(self, args: list[str], example: str, n: int) -> Any: """Helper to make sure the command got the right number of arguments""" if len(args) != n: msg = ( - "Got unexpected number of arguments, expected {}. " + 'Got unexpected number of arguments, expected {}. ' '(example: "{} config {}")' ).format(n, get_prog(), example) raise PipError(msg) @@ -251,16 +255,16 @@ class ConfigurationCommand(Command): self.configuration.save() except Exception: logger.exception( - "Unable to save configuration. Please report this as a bug." + 'Unable to save configuration. Please report this as a bug.', ) - raise PipError("Internal Error.") + raise PipError('Internal Error.') def _determine_editor(self, options: Values) -> str: if options.editor is not None: return options.editor - elif "VISUAL" in os.environ: - return os.environ["VISUAL"] - elif "EDITOR" in os.environ: - return os.environ["EDITOR"] + elif 'VISUAL' in os.environ: + return os.environ['VISUAL'] + elif 'EDITOR' in os.environ: + return os.environ['EDITOR'] else: - raise PipError("Could not determine editor to use.") + raise PipError('Could not determine editor to use.') diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/debug.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/debug.py index d3f1f28..1af60f3 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/debug.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/debug.py @@ -1,15 +1,17 @@ +from __future__ import annotations + import locale import logging import os import sys from optparse import Values from types import ModuleType -from typing import Any, Dict, List, Optional +from typing import Any +from typing import Dict +from typing import List +from typing import Optional import pip._vendor -from pip._vendor.certifi import where -from pip._vendor.packaging.version import parse as parse_version - from pip import __file__ as pip_location from pip._internal.cli import cmdoptions from pip._internal.cli.base_command import Command @@ -19,51 +21,53 @@ from pip._internal.configuration import Configuration from pip._internal.metadata import get_environment from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import get_pip_version +from pip._vendor.certifi import where +from pip._vendor.packaging.version import parse as parse_version logger = logging.getLogger(__name__) def show_value(name: str, value: Any) -> None: - logger.info("%s: %s", name, value) + logger.info('%s: %s', name, value) def show_sys_implementation() -> None: - logger.info("sys.implementation:") + logger.info('sys.implementation:') implementation_name = sys.implementation.name with indent_log(): - show_value("name", implementation_name) + show_value('name', implementation_name) -def create_vendor_txt_map() -> Dict[str, str]: +def create_vendor_txt_map() -> dict[str, str]: vendor_txt_path = os.path.join( - os.path.dirname(pip_location), "_vendor", "vendor.txt" + os.path.dirname(pip_location), '_vendor', 'vendor.txt', ) with open(vendor_txt_path) as f: # Purge non version specifying lines. # Also, remove any space prefix or suffixes (including comments). lines = [ - line.strip().split(" ", 1)[0] for line in f.readlines() if "==" in line + line.strip().split(' ', 1)[0] for line in f.readlines() if '==' in line ] # Transform into "module" -> version dict. - return dict(line.split("==", 1) for line in lines) # type: ignore + return dict(line.split('==', 1) for line in lines) # type: ignore def get_module_from_module_name(module_name: str) -> ModuleType: # Module name can be uppercase in vendor.txt for some reason... module_name = module_name.lower() # PATCH: setuptools is actually only pkg_resources. - if module_name == "setuptools": - module_name = "pkg_resources" + if module_name == 'setuptools': + module_name = 'pkg_resources' - __import__(f"pip._vendor.{module_name}", globals(), locals(), level=0) + __import__(f'pip._vendor.{module_name}', globals(), locals(), level=0) return getattr(pip._vendor, module_name) -def get_vendor_version_from_module(module_name: str) -> Optional[str]: +def get_vendor_version_from_module(module_name: str) -> str | None: module = get_module_from_module_name(module_name) - version = getattr(module, "__version__", None) + version = getattr(module, '__version__', None) if not version: # Try to find version in debundled module info. @@ -75,29 +79,29 @@ def get_vendor_version_from_module(module_name: str) -> Optional[str]: return version -def show_actual_vendor_versions(vendor_txt_versions: Dict[str, str]) -> None: +def show_actual_vendor_versions(vendor_txt_versions: dict[str, str]) -> None: """Log the actual version and print extra info if there is a conflict or if the actual version could not be imported. """ for module_name, expected_version in vendor_txt_versions.items(): - extra_message = "" + extra_message = '' actual_version = get_vendor_version_from_module(module_name) if not actual_version: extra_message = ( - " (Unable to locate actual module version, using" - " vendor.txt specified version)" + ' (Unable to locate actual module version, using' + ' vendor.txt specified version)' ) actual_version = expected_version elif parse_version(actual_version) != parse_version(expected_version): extra_message = ( - " (CONFLICT: vendor.txt suggests version should" - " be {})".format(expected_version) + ' (CONFLICT: vendor.txt suggests version should' + ' be {})'.format(expected_version) ) - logger.info("%s==%s%s", module_name, actual_version, extra_message) + logger.info('%s==%s%s', module_name, actual_version, extra_message) def show_vendor_versions() -> None: - logger.info("vendored library versions:") + logger.info('vendored library versions:') vendor_txt_versions = create_vendor_txt_map() with indent_log(): @@ -112,11 +116,11 @@ def show_tags(options: Values) -> None: # Display the target options that were explicitly provided. formatted_target = target_python.format_given() - suffix = "" + suffix = '' if formatted_target: - suffix = f" (target: {formatted_target})" + suffix = f' (target: {formatted_target})' - msg = "Compatible tags: {}{}".format(len(tags), suffix) + msg = f'Compatible tags: {len(tags)}{suffix}' logger.info(msg) if options.verbose < 1 and len(tags) > tag_limit: @@ -131,7 +135,7 @@ def show_tags(options: Values) -> None: if tags_limited: msg = ( - "...\n[First {tag_limit} tags shown. Pass --verbose to show all.]" + '...\n[First {tag_limit} tags shown. Pass --verbose to show all.]' ).format(tag_limit=tag_limit) logger.info(msg) @@ -139,21 +143,21 @@ def show_tags(options: Values) -> None: def ca_bundle_info(config: Configuration) -> str: levels = set() for key, _ in config.items(): - levels.add(key.split(".")[0]) + levels.add(key.split('.')[0]) if not levels: - return "Not specified" + return 'Not specified' - levels_that_override_global = ["install", "wheel", "download"] + levels_that_override_global = ['install', 'wheel', 'download'] global_overriding_level = [ level for level in levels if level in levels_that_override_global ] if not global_overriding_level: - return "global" + return 'global' - if "global" in levels: - levels.remove("global") - return ", ".join(levels) + if 'global' in levels: + levels.remove('global') + return ', '.join(levels) class DebugCommand(Command): @@ -170,30 +174,30 @@ class DebugCommand(Command): self.parser.insert_option_group(0, self.cmd_opts) self.parser.config.load() - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: logger.warning( - "This command is only meant for debugging. " - "Do not use this with automation for parsing and getting these " - "details, since the output and options of this command may " - "change without notice." + 'This command is only meant for debugging. ' + 'Do not use this with automation for parsing and getting these ' + 'details, since the output and options of this command may ' + 'change without notice.', ) - show_value("pip version", get_pip_version()) - show_value("sys.version", sys.version) - show_value("sys.executable", sys.executable) - show_value("sys.getdefaultencoding", sys.getdefaultencoding()) - show_value("sys.getfilesystemencoding", sys.getfilesystemencoding()) + show_value('pip version', get_pip_version()) + show_value('sys.version', sys.version) + show_value('sys.executable', sys.executable) + show_value('sys.getdefaultencoding', sys.getdefaultencoding()) + show_value('sys.getfilesystemencoding', sys.getfilesystemencoding()) show_value( - "locale.getpreferredencoding", + 'locale.getpreferredencoding', locale.getpreferredencoding(), ) - show_value("sys.platform", sys.platform) + show_value('sys.platform', sys.platform) show_sys_implementation() show_value("'cert' config value", ca_bundle_info(self.parser.config)) - show_value("REQUESTS_CA_BUNDLE", os.environ.get("REQUESTS_CA_BUNDLE")) - show_value("CURL_CA_BUNDLE", os.environ.get("CURL_CA_BUNDLE")) - show_value("pip._vendor.certifi.where()", where()) - show_value("pip._vendor.DEBUNDLED", pip._vendor.DEBUNDLED) + show_value('REQUESTS_CA_BUNDLE', os.environ.get('REQUESTS_CA_BUNDLE')) + show_value('CURL_CA_BUNDLE', os.environ.get('CURL_CA_BUNDLE')) + show_value('pip._vendor.certifi.where()', where()) + show_value('pip._vendor.DEBUNDLED', pip._vendor.DEBUNDLED) show_vendor_versions() diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/download.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/download.py index 233b7e9..36c82be 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/download.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/download.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os from optparse import Values @@ -5,10 +7,13 @@ from typing import List from pip._internal.cli import cmdoptions from pip._internal.cli.cmdoptions import make_target_python -from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.req_command import RequirementCommand +from pip._internal.cli.req_command import with_cleanup from pip._internal.cli.status_codes import SUCCESS from pip._internal.req.req_tracker import get_requirement_tracker -from pip._internal.utils.misc import ensure_dir, normalize_path, write_output +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.misc import normalize_path +from pip._internal.utils.misc import write_output from pip._internal.utils.temp_dir import TempDirectory logger = logging.getLogger(__name__) @@ -52,14 +57,14 @@ class DownloadCommand(RequirementCommand): self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) self.cmd_opts.add_option( - "-d", - "--dest", - "--destination-dir", - "--destination-directory", - dest="download_dir", - metavar="dir", + '-d', + '--dest', + '--destination-dir', + '--destination-directory', + dest='download_dir', + metavar='dir', default=os.curdir, - help="Download packages into .", + help='Download packages into .', ) cmdoptions.add_target_python_options(self.cmd_opts) @@ -73,7 +78,7 @@ class DownloadCommand(RequirementCommand): self.parser.insert_option_group(0, self.cmd_opts) @with_cleanup - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels @@ -99,7 +104,7 @@ class DownloadCommand(RequirementCommand): directory = TempDirectory( delete=not options.no_clean, - kind="download", + kind='download', globally_managed=True, ) @@ -128,13 +133,13 @@ class DownloadCommand(RequirementCommand): requirement_set = resolver.resolve(reqs, check_supported_wheels=True) - downloaded: List[str] = [] + downloaded: list[str] = [] for req in requirement_set.requirements.values(): if req.satisfied_by is None: assert req.name is not None preparer.save_linked_requirement(req) downloaded.append(req.name) if downloaded: - write_output("Successfully downloaded %s", " ".join(downloaded)) + write_output('Successfully downloaded %s', ' '.join(downloaded)) return SUCCESS diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/freeze.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/freeze.py index 5fa6d39..46df1d1 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/freeze.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/freeze.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from optparse import Values from typing import List @@ -8,7 +10,7 @@ from pip._internal.cli.status_codes import SUCCESS from pip._internal.operations.freeze import freeze from pip._internal.utils.compat import stdlib_pkgs -DEV_PKGS = {"pip", "setuptools", "distribute", "wheel"} +DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel'} class FreezeCommand(Command): @@ -20,61 +22,61 @@ class FreezeCommand(Command): usage = """ %prog [options]""" - log_streams = ("ext://sys.stderr", "ext://sys.stderr") + log_streams = ('ext://sys.stderr', 'ext://sys.stderr') def add_options(self) -> None: self.cmd_opts.add_option( - "-r", - "--requirement", - dest="requirements", - action="append", + '-r', + '--requirement', + dest='requirements', + action='append', default=[], - metavar="file", + metavar='file', help=( - "Use the order in the given requirements file and its " - "comments when generating output. This option can be " - "used multiple times." + 'Use the order in the given requirements file and its ' + 'comments when generating output. This option can be ' + 'used multiple times.' ), ) self.cmd_opts.add_option( - "-l", - "--local", - dest="local", - action="store_true", + '-l', + '--local', + dest='local', + action='store_true', default=False, help=( - "If in a virtualenv that has global access, do not output " - "globally-installed packages." + 'If in a virtualenv that has global access, do not output ' + 'globally-installed packages.' ), ) self.cmd_opts.add_option( - "--user", - dest="user", - action="store_true", + '--user', + dest='user', + action='store_true', default=False, - help="Only output packages installed in user-site.", + help='Only output packages installed in user-site.', ) self.cmd_opts.add_option(cmdoptions.list_path()) self.cmd_opts.add_option( - "--all", - dest="freeze_all", - action="store_true", + '--all', + dest='freeze_all', + action='store_true', help=( - "Do not skip these packages in the output:" - " {}".format(", ".join(DEV_PKGS)) + 'Do not skip these packages in the output:' + ' {}'.format(', '.join(DEV_PKGS)) ), ) self.cmd_opts.add_option( - "--exclude-editable", - dest="exclude_editable", - action="store_true", - help="Exclude editable package from output.", + '--exclude-editable', + dest='exclude_editable', + action='store_true', + help='Exclude editable package from output.', ) self.cmd_opts.add_option(cmdoptions.list_exclude()) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: skip = set(stdlib_pkgs) if not options.freeze_all: skip.update(DEV_PKGS) @@ -93,5 +95,5 @@ class FreezeCommand(Command): skip=skip, exclude_editable=options.exclude_editable, ): - sys.stdout.write(line + "\n") + sys.stdout.write(line + '\n') return SUCCESS diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/hash.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/hash.py index 042dac8..f7762f4 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/hash.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/hash.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import hashlib import logging import sys @@ -5,9 +7,12 @@ from optparse import Values from typing import List from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR, SUCCESS -from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES -from pip._internal.utils.misc import read_chunks, write_output +from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.utils.hashes import FAVORITE_HASH +from pip._internal.utils.hashes import STRONG_HASHES +from pip._internal.utils.misc import read_chunks +from pip._internal.utils.misc import write_output logger = logging.getLogger(__name__) @@ -20,24 +25,24 @@ class HashCommand(Command): installs. """ - usage = "%prog [options] ..." + usage = '%prog [options] ...' ignore_require_venv = True def add_options(self) -> None: self.cmd_opts.add_option( - "-a", - "--algorithm", - dest="algorithm", + '-a', + '--algorithm', + dest='algorithm', choices=STRONG_HASHES, - action="store", + action='store', default=FAVORITE_HASH, - help="The hash algorithm to use: one of {}".format( - ", ".join(STRONG_HASHES) + help='The hash algorithm to use: one of {}'.format( + ', '.join(STRONG_HASHES), ), ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: if not args: self.parser.print_usage(sys.stderr) return ERROR @@ -45,14 +50,14 @@ class HashCommand(Command): algorithm = options.algorithm for path in args: write_output( - "%s:\n--hash=%s:%s", path, algorithm, _hash_of_file(path, algorithm) + '%s:\n--hash=%s:%s', path, algorithm, _hash_of_file(path, algorithm), ) return SUCCESS def _hash_of_file(path: str, algorithm: str) -> str: """Return the hash digest of a file.""" - with open(path, "rb") as archive: + with open(path, 'rb') as archive: hash = hashlib.new(algorithm) for chunk in read_chunks(archive): hash.update(chunk) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/help.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/help.py index 6206631..094c1bc 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/help.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/help.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from optparse import Values from typing import List @@ -13,7 +15,7 @@ class HelpCommand(Command): %prog """ ignore_require_venv = True - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: from pip._internal.commands import ( commands_dict, create_command, @@ -33,7 +35,7 @@ class HelpCommand(Command): if guess: msg.append(f'maybe you meant "{guess}"') - raise CommandError(" - ".join(msg)) + raise CommandError(' - '.join(msg)) command = create_command(cmd_name) command.parser.print_help() diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/index.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/index.py index 9d8aae3..82382ed 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/index.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/index.py @@ -1,20 +1,29 @@ +from __future__ import annotations + import logging from optparse import Values -from typing import Any, Iterable, List, Optional, Union - -from pip._vendor.packaging.version import LegacyVersion, Version +from typing import Any +from typing import Iterable +from typing import List +from typing import Optional +from typing import Union from pip._internal.cli import cmdoptions from pip._internal.cli.req_command import IndexGroupCommand -from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import SUCCESS from pip._internal.commands.search import print_dist_installation_info -from pip._internal.exceptions import CommandError, DistributionNotFound, PipError +from pip._internal.exceptions import CommandError +from pip._internal.exceptions import DistributionNotFound +from pip._internal.exceptions import PipError from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.models.target_python import TargetPython from pip._internal.network.session import PipSession from pip._internal.utils.misc import write_output +from pip._vendor.packaging.version import LegacyVersion +from pip._vendor.packaging.version import Version logger = logging.getLogger(__name__) @@ -44,22 +53,22 @@ class IndexCommand(IndexGroupCommand): self.parser.insert_option_group(0, index_opts) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: handlers = { - "versions": self.get_available_package_versions, + 'versions': self.get_available_package_versions, } logger.warning( - "pip index is currently an experimental command. " - "It may be removed/changed in a future release " - "without prior warning." + 'pip index is currently an experimental command. ' + 'It may be removed/changed in a future release ' + 'without prior warning.', ) # Determine action if not args or args[0] not in handlers: logger.error( - "Need an action (%s) to perform.", - ", ".join(sorted(handlers)), + 'Need an action (%s) to perform.', + ', '.join(sorted(handlers)), ) return ERROR @@ -78,8 +87,8 @@ class IndexCommand(IndexGroupCommand): self, options: Values, session: PipSession, - target_python: Optional[TargetPython] = None, - ignore_requires_python: Optional[bool] = None, + target_python: TargetPython | None = None, + ignore_requires_python: bool | None = None, ) -> PackageFinder: """ Create a package finder appropriate to the index command. @@ -97,12 +106,12 @@ class IndexCommand(IndexGroupCommand): link_collector=link_collector, selection_prefs=selection_prefs, target_python=target_python, - use_deprecated_html5lib="html5lib" in options.deprecated_features_enabled, + use_deprecated_html5lib='html5lib' in options.deprecated_features_enabled, ) - def get_available_package_versions(self, options: Values, args: List[Any]) -> None: + def get_available_package_versions(self, options: Values, args: list[Any]) -> None: if len(args) != 1: - raise CommandError("You need to specify exactly one argument") + raise CommandError('You need to specify exactly one argument') target_python = cmdoptions.make_target_python(options) query = args[0] @@ -115,7 +124,7 @@ class IndexCommand(IndexGroupCommand): ignore_requires_python=options.ignore_requires_python, ) - versions: Iterable[Union[LegacyVersion, Version]] = ( + versions: Iterable[LegacyVersion | Version] = ( candidate.version for candidate in finder.find_all_candidates(query) ) @@ -128,12 +137,12 @@ class IndexCommand(IndexGroupCommand): if not versions: raise DistributionNotFound( - "No matching distribution found for {}".format(query) + f'No matching distribution found for {query}', ) formatted_versions = [str(ver) for ver in sorted(versions, reverse=True)] latest = formatted_versions[0] - write_output("{} ({})".format(query, latest)) - write_output("Available versions: {}".format(", ".join(formatted_versions))) + write_output(f'{query} ({latest})') + write_output('Available versions: {}'.format(', '.join(formatted_versions))) print_dist_installation_info(query, latest) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/install.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/install.py index 34e4c2f..0595053 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/install.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/install.py @@ -1,27 +1,31 @@ +from __future__ import annotations + import errno import operator import os import shutil import site -from optparse import SUPPRESS_HELP, Values -from typing import Iterable, List, Optional - -from pip._vendor.packaging.utils import canonicalize_name +from optparse import SUPPRESS_HELP +from optparse import Values +from typing import Iterable +from typing import List +from typing import Optional from pip._internal.cache import WheelCache from pip._internal.cli import cmdoptions from pip._internal.cli.cmdoptions import make_target_python -from pip._internal.cli.req_command import ( - RequirementCommand, - warn_if_run_as_root, - with_cleanup, -) -from pip._internal.cli.status_codes import ERROR, SUCCESS -from pip._internal.exceptions import CommandError, InstallationError +from pip._internal.cli.req_command import RequirementCommand +from pip._internal.cli.req_command import warn_if_run_as_root +from pip._internal.cli.req_command import with_cleanup +from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.exceptions import InstallationError from pip._internal.locations import get_scheme from pip._internal.metadata import get_environment from pip._internal.models.format_control import FormatControl -from pip._internal.operations.check import ConflictDetails, check_install_conflicts +from pip._internal.operations.check import check_install_conflicts +from pip._internal.operations.check import ConflictDetails from pip._internal.req import install_given_reqs from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_tracker import get_requirement_tracker @@ -29,31 +33,26 @@ from pip._internal.utils.compat import WINDOWS from pip._internal.utils.distutils_args import parse_distutils_args from pip._internal.utils.filesystem import test_writable_dir from pip._internal.utils.logging import getLogger -from pip._internal.utils.misc import ( - ensure_dir, - get_pip_version, - protect_pip_from_modification_on_windows, - write_output, -) +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.misc import get_pip_version +from pip._internal.utils.misc import protect_pip_from_modification_on_windows +from pip._internal.utils.misc import write_output from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.virtualenv import ( - running_under_virtualenv, - virtualenv_no_global, -) -from pip._internal.wheel_builder import ( - BinaryAllowedPredicate, - build, - should_build_for_install_command, -) +from pip._internal.utils.virtualenv import running_under_virtualenv +from pip._internal.utils.virtualenv import virtualenv_no_global +from pip._internal.wheel_builder import BinaryAllowedPredicate +from pip._internal.wheel_builder import build +from pip._internal.wheel_builder import should_build_for_install_command +from pip._vendor.packaging.utils import canonicalize_name logger = getLogger(__name__) def get_check_binary_allowed(format_control: FormatControl) -> BinaryAllowedPredicate: def check_binary_allowed(req: InstallRequirement) -> bool: - canonical_name = canonicalize_name(req.name or "") + canonical_name = canonicalize_name(req.name or '') allowed_formats = format_control.get_allowed_formats(canonical_name) - return "binary" in allowed_formats + return 'binary' in allowed_formats return check_binary_allowed @@ -86,102 +85,102 @@ class InstallCommand(RequirementCommand): self.cmd_opts.add_option(cmdoptions.editable()) self.cmd_opts.add_option( - "-t", - "--target", - dest="target_dir", - metavar="dir", + '-t', + '--target', + dest='target_dir', + metavar='dir', default=None, help=( - "Install packages into . " - "By default this will not replace existing files/folders in " - ". Use --upgrade to replace existing packages in " - "with new versions." + 'Install packages into . ' + 'By default this will not replace existing files/folders in ' + '. Use --upgrade to replace existing packages in ' + 'with new versions.' ), ) cmdoptions.add_target_python_options(self.cmd_opts) self.cmd_opts.add_option( - "--user", - dest="use_user_site", - action="store_true", + '--user', + dest='use_user_site', + action='store_true', help=( - "Install to the Python user install directory for your " - "platform. Typically ~/.local/, or %APPDATA%\\Python on " - "Windows. (See the Python documentation for site.USER_BASE " - "for full details.)" + 'Install to the Python user install directory for your ' + 'platform. Typically ~/.local/, or %APPDATA%\\Python on ' + 'Windows. (See the Python documentation for site.USER_BASE ' + 'for full details.)' ), ) self.cmd_opts.add_option( - "--no-user", - dest="use_user_site", - action="store_false", + '--no-user', + dest='use_user_site', + action='store_false', help=SUPPRESS_HELP, ) self.cmd_opts.add_option( - "--root", - dest="root_path", - metavar="dir", + '--root', + dest='root_path', + metavar='dir', default=None, - help="Install everything relative to this alternate root directory.", + help='Install everything relative to this alternate root directory.', ) self.cmd_opts.add_option( - "--prefix", - dest="prefix_path", - metavar="dir", + '--prefix', + dest='prefix_path', + metavar='dir', default=None, help=( - "Installation prefix where lib, bin and other top-level " - "folders are placed" + 'Installation prefix where lib, bin and other top-level ' + 'folders are placed' ), ) self.cmd_opts.add_option(cmdoptions.src()) self.cmd_opts.add_option( - "-U", - "--upgrade", - dest="upgrade", - action="store_true", + '-U', + '--upgrade', + dest='upgrade', + action='store_true', help=( - "Upgrade all specified packages to the newest available " - "version. The handling of dependencies depends on the " - "upgrade-strategy used." + 'Upgrade all specified packages to the newest available ' + 'version. The handling of dependencies depends on the ' + 'upgrade-strategy used.' ), ) self.cmd_opts.add_option( - "--upgrade-strategy", - dest="upgrade_strategy", - default="only-if-needed", - choices=["only-if-needed", "eager"], + '--upgrade-strategy', + dest='upgrade_strategy', + default='only-if-needed', + choices=['only-if-needed', 'eager'], help=( - "Determines how dependency upgrading should be handled " - "[default: %default]. " + 'Determines how dependency upgrading should be handled ' + '[default: %default]. ' '"eager" - dependencies are upgraded regardless of ' - "whether the currently installed version satisfies the " - "requirements of the upgraded package(s). " + 'whether the currently installed version satisfies the ' + 'requirements of the upgraded package(s). ' '"only-if-needed" - are upgraded only when they do not ' - "satisfy the requirements of the upgraded package(s)." + 'satisfy the requirements of the upgraded package(s).' ), ) self.cmd_opts.add_option( - "--force-reinstall", - dest="force_reinstall", - action="store_true", - help="Reinstall all packages even if they are already up-to-date.", + '--force-reinstall', + dest='force_reinstall', + action='store_true', + help='Reinstall all packages even if they are already up-to-date.', ) self.cmd_opts.add_option( - "-I", - "--ignore-installed", - dest="ignore_installed", - action="store_true", + '-I', + '--ignore-installed', + dest='ignore_installed', + action='store_true', help=( - "Ignore the installed packages, overwriting them. " - "This can break your system if the existing package " - "is of a different version or was installed " - "with a different package manager!" + 'Ignore the installed packages, overwriting them. ' + 'This can break your system if the existing package ' + 'is of a different version or was installed ' + 'with a different package manager!' ), ) @@ -194,33 +193,33 @@ class InstallCommand(RequirementCommand): self.cmd_opts.add_option(cmdoptions.global_options()) self.cmd_opts.add_option( - "--compile", - action="store_true", - dest="compile", + '--compile', + action='store_true', + dest='compile', default=True, - help="Compile Python source files to bytecode", + help='Compile Python source files to bytecode', ) self.cmd_opts.add_option( - "--no-compile", - action="store_false", - dest="compile", - help="Do not compile Python source files to bytecode", + '--no-compile', + action='store_false', + dest='compile', + help='Do not compile Python source files to bytecode', ) self.cmd_opts.add_option( - "--no-warn-script-location", - action="store_false", - dest="warn_script_location", + '--no-warn-script-location', + action='store_false', + dest='warn_script_location', default=True, - help="Do not warn when installing scripts outside PATH", + help='Do not warn when installing scripts outside PATH', ) self.cmd_opts.add_option( - "--no-warn-conflicts", - action="store_false", - dest="warn_about_conflicts", + '--no-warn-conflicts', + action='store_false', + dest='warn_about_conflicts', default=True, - help="Do not warn about broken dependencies", + help='Do not warn about broken dependencies', ) self.cmd_opts.add_option(cmdoptions.no_binary()) @@ -238,12 +237,12 @@ class InstallCommand(RequirementCommand): self.parser.insert_option_group(0, self.cmd_opts) @with_cleanup - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: if options.use_user_site and options.target_dir is not None: raise CommandError("Can not combine '--user' and '--target'") cmdoptions.check_install_build_global(options) - upgrade_strategy = "to-satisfy-only" + upgrade_strategy = 'to-satisfy-only' if options.upgrade: upgrade_strategy = options.upgrade_strategy @@ -251,7 +250,7 @@ class InstallCommand(RequirementCommand): install_options = options.install_options or [] - logger.verbose("Using %s", get_pip_version()) + logger.verbose('Using %s', get_pip_version()) options.use_user_site = decide_user_install( options.use_user_site, prefix_path=options.prefix_path, @@ -260,8 +259,8 @@ class InstallCommand(RequirementCommand): isolated_mode=options.isolated_mode, ) - target_temp_dir: Optional[TempDirectory] = None - target_temp_dir_path: Optional[str] = None + target_temp_dir: TempDirectory | None = None + target_temp_dir_path: str | None = None if options.target_dir: options.ignore_installed = True options.target_dir = os.path.abspath(options.target_dir) @@ -272,11 +271,11 @@ class InstallCommand(RequirementCommand): # fmt: on ): raise CommandError( - "Target path exists but is not a directory, will not continue." + 'Target path exists but is not a directory, will not continue.', ) # Create a target directory for using with the target option - target_temp_dir = TempDirectory(kind="target") + target_temp_dir = TempDirectory(kind='target') target_temp_dir_path = target_temp_dir.path self.enter_context(target_temp_dir) @@ -297,7 +296,7 @@ class InstallCommand(RequirementCommand): directory = TempDirectory( delete=not options.no_clean, - kind="install", + kind='install', globally_managed=True, ) @@ -337,11 +336,11 @@ class InstallCommand(RequirementCommand): self.trace_basic_info(finder) requirement_set = resolver.resolve( - reqs, check_supported_wheels=not options.target_dir + reqs, check_supported_wheels=not options.target_dir, ) try: - pip_req = requirement_set.get_requirement("pip") + pip_req = requirement_set.get_requirement('pip') except KeyError: modifying_pip = False else: @@ -368,15 +367,15 @@ class InstallCommand(RequirementCommand): # If we're using PEP 517, we cannot do a legacy setup.py install # so we fail here. - pep517_build_failure_names: List[str] = [ + pep517_build_failure_names: list[str] = [ r.name for r in build_failures if r.use_pep517 # type: ignore ] if pep517_build_failure_names: raise InstallationError( - "Could not build wheels for {}, which is required to " - "install pyproject.toml-based projects".format( - ", ".join(pep517_build_failure_names) - ) + 'Could not build wheels for {}, which is required to ' + 'install pyproject.toml-based projects'.format( + ', '.join(pep517_build_failure_names), + ), ) # For now, we just warn about failures building legacy @@ -389,7 +388,7 @@ class InstallCommand(RequirementCommand): to_install = resolver.get_installation_order(requirement_set) # Check for conflicts in the package set we're installing. - conflicts: Optional[ConflictDetails] = None + conflicts: ConflictDetails | None = None should_warn_about_conflicts = ( not options.ignore_dependencies and options.warn_about_conflicts ) @@ -423,14 +422,14 @@ class InstallCommand(RequirementCommand): ) env = get_environment(lib_locations) - installed.sort(key=operator.attrgetter("name")) + installed.sort(key=operator.attrgetter('name')) items = [] for result in installed: item = result.name try: installed_dist = env.get_distribution(item) if installed_dist is not None: - item = f"{item}-{installed_dist.version}" + item = f'{item}-{installed_dist.version}' except Exception: pass items.append(item) @@ -441,10 +440,10 @@ class InstallCommand(RequirementCommand): resolver_variant=self.determine_resolver_variant(options), ) - installed_desc = " ".join(items) + installed_desc = ' '.join(items) if installed_desc: write_output( - "Successfully installed %s", + 'Successfully installed %s', installed_desc, ) except OSError as error: @@ -462,14 +461,14 @@ class InstallCommand(RequirementCommand): if options.target_dir: assert target_temp_dir self._handle_target_dir( - options.target_dir, target_temp_dir, options.upgrade + options.target_dir, target_temp_dir, options.upgrade, ) warn_if_run_as_root() return SUCCESS def _handle_target_dir( - self, target_dir: str, target_temp_dir: TempDirectory, upgrade: bool + self, target_dir: str, target_temp_dir: TempDirectory, upgrade: bool, ) -> None: ensure_dir(target_dir) @@ -479,7 +478,7 @@ class InstallCommand(RequirementCommand): # Checking both purelib and platlib directories for installed # packages to be moved to target directory - scheme = get_scheme("", home=target_temp_dir.path) + scheme = get_scheme('', home=target_temp_dir.path) purelib_dir = scheme.purelib platlib_dir = scheme.platlib data_dir = scheme.data @@ -501,17 +500,17 @@ class InstallCommand(RequirementCommand): if os.path.exists(target_item_dir): if not upgrade: logger.warning( - "Target directory %s already exists. Specify " - "--upgrade to force replacement.", + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', target_item_dir, ) continue if os.path.islink(target_item_dir): logger.warning( - "Target directory %s already exists and is " - "a link. pip will not automatically replace " - "links, please remove if replacement is " - "desired.", + 'Target directory %s already exists and is ' + 'a link. pip will not automatically replace ' + 'links, please remove if replacement is ' + 'desired.', target_item_dir, ) continue @@ -523,37 +522,37 @@ class InstallCommand(RequirementCommand): shutil.move(os.path.join(lib_dir, item), target_item_dir) def _determine_conflicts( - self, to_install: List[InstallRequirement] - ) -> Optional[ConflictDetails]: + self, to_install: list[InstallRequirement], + ) -> ConflictDetails | None: try: return check_install_conflicts(to_install) except Exception: logger.exception( - "Error while checking for conflicts. Please file an issue on " - "pip's issue tracker: https://github.com/pypa/pip/issues/new" + 'Error while checking for conflicts. Please file an issue on ' + "pip's issue tracker: https://github.com/pypa/pip/issues/new", ) return None def _warn_about_conflicts( - self, conflict_details: ConflictDetails, resolver_variant: str + self, conflict_details: ConflictDetails, resolver_variant: str, ) -> None: package_set, (missing, conflicting) = conflict_details if not missing and not conflicting: return - parts: List[str] = [] - if resolver_variant == "legacy": + parts: list[str] = [] + if resolver_variant == 'legacy': parts.append( "pip's legacy dependency resolver does not consider dependency " - "conflicts when selecting packages. This behaviour is the " - "source of the following dependency conflicts." + 'conflicts when selecting packages. This behaviour is the ' + 'source of the following dependency conflicts.', ) else: - assert resolver_variant == "2020-resolver" + assert resolver_variant == '2020-resolver' parts.append( "pip's dependency resolver does not currently take into account " - "all the packages that are installed. This behaviour is the " - "source of the following dependency conflicts." + 'all the packages that are installed. This behaviour is the ' + 'source of the following dependency conflicts.', ) # NOTE: There is some duplication here, with commands/check.py @@ -561,8 +560,8 @@ class InstallCommand(RequirementCommand): version = package_set[project_name][0] for dependency in missing[project_name]: message = ( - "{name} {version} requires {requirement}, " - "which is not installed." + '{name} {version} requires {requirement}, ' + 'which is not installed.' ).format( name=project_name, version=version, @@ -574,30 +573,30 @@ class InstallCommand(RequirementCommand): version = package_set[project_name][0] for dep_name, dep_version, req in conflicting[project_name]: message = ( - "{name} {version} requires {requirement}, but {you} have " - "{dep_name} {dep_version} which is incompatible." + '{name} {version} requires {requirement}, but {you} have ' + '{dep_name} {dep_version} which is incompatible.' ).format( name=project_name, version=version, requirement=req, dep_name=dep_name, dep_version=dep_version, - you=("you" if resolver_variant == "2020-resolver" else "you'll"), + you=('you' if resolver_variant == '2020-resolver' else "you'll"), ) parts.append(message) - logger.critical("\n".join(parts)) + logger.critical('\n'.join(parts)) def get_lib_location_guesses( user: bool = False, - home: Optional[str] = None, - root: Optional[str] = None, + home: str | None = None, + root: str | None = None, isolated: bool = False, - prefix: Optional[str] = None, -) -> List[str]: + prefix: str | None = None, +) -> list[str]: scheme = get_scheme( - "", + '', user=user, home=home, root=root, @@ -607,7 +606,7 @@ def get_lib_location_guesses( return [scheme.purelib, scheme.platlib] -def site_packages_writable(root: Optional[str], isolated: bool) -> bool: +def site_packages_writable(root: str | None, isolated: bool) -> bool: return all( test_writable_dir(d) for d in set(get_lib_location_guesses(root=root, isolated=isolated)) @@ -615,10 +614,10 @@ def site_packages_writable(root: Optional[str], isolated: bool) -> bool: def decide_user_install( - use_user_site: Optional[bool], - prefix_path: Optional[str] = None, - target_dir: Optional[str] = None, - root_path: Optional[str] = None, + use_user_site: bool | None, + prefix_path: str | None = None, + target_dir: str | None = None, + root_path: str | None = None, isolated_mode: bool = False, ) -> bool: """Determine whether to do a user install based on the input options. @@ -632,21 +631,21 @@ def decide_user_install( # In some cases (config from tox), use_user_site can be set to an integer # rather than a bool, which 'use_user_site is False' wouldn't catch. if (use_user_site is not None) and (not use_user_site): - logger.debug("Non-user install by explicit request") + logger.debug('Non-user install by explicit request') return False if use_user_site: if prefix_path: raise CommandError( "Can not combine '--user' and '--prefix' as they imply " - "different installation locations" + 'different installation locations', ) if virtualenv_no_global(): raise InstallationError( "Can not perform a '--user' install. User site-packages " - "are not visible in this virtualenv." + 'are not visible in this virtualenv.', ) - logger.debug("User install by explicit request") + logger.debug('User install by explicit request') return True # If we are here, user installs have not been explicitly requested/avoided @@ -654,36 +653,36 @@ def decide_user_install( # user install incompatible with --prefix/--target if prefix_path or target_dir: - logger.debug("Non-user install due to --prefix or --target option") + logger.debug('Non-user install due to --prefix or --target option') return False # If user installs are not enabled, choose a non-user install if not site.ENABLE_USER_SITE: - logger.debug("Non-user install because user site-packages disabled") + logger.debug('Non-user install because user site-packages disabled') return False # If we have permission for a non-user install, do that, # otherwise do a user install. if site_packages_writable(root=root_path, isolated=isolated_mode): - logger.debug("Non-user install because site-packages writeable") + logger.debug('Non-user install because site-packages writeable') return False logger.info( - "Defaulting to user installation because normal site-packages " - "is not writeable" + 'Defaulting to user installation because normal site-packages ' + 'is not writeable', ) return True def reject_location_related_install_options( - requirements: List[InstallRequirement], options: Optional[List[str]] + requirements: list[InstallRequirement], options: list[str] | None, ) -> None: """If any location-changing --install-option arguments were passed for requirements or on the command-line, then show a deprecation warning. """ - def format_options(option_names: Iterable[str]) -> List[str]: - return ["--{}".format(name.replace("_", "-")) for name in option_names] + def format_options(option_names: Iterable[str]) -> list[str]: + return ['--{}'.format(name.replace('_', '-')) for name in option_names] offenders = [] @@ -692,30 +691,30 @@ def reject_location_related_install_options( location_options = parse_distutils_args(install_options) if location_options: offenders.append( - "{!r} from {}".format( - format_options(location_options.keys()), requirement - ) + '{!r} from {}'.format( + format_options(location_options.keys()), requirement, + ), ) if options: location_options = parse_distutils_args(options) if location_options: offenders.append( - "{!r} from command line".format(format_options(location_options.keys())) + f'{format_options(location_options.keys())!r} from command line', ) if not offenders: return raise CommandError( - "Location-changing options found in --install-option: {}." - " This is unsupported, use pip-level options like --user," - " --prefix, --root, and --target instead.".format("; ".join(offenders)) + 'Location-changing options found in --install-option: {}.' + ' This is unsupported, use pip-level options like --user,' + ' --prefix, --root, and --target instead.'.format('; '.join(offenders)), ) def create_os_error_message( - error: OSError, show_traceback: bool, using_user_site: bool + error: OSError, show_traceback: bool, using_user_site: bool, ) -> str: """Format an error message for an OSError @@ -724,48 +723,48 @@ def create_os_error_message( parts = [] # Mention the error if we are not going to show a traceback - parts.append("Could not install packages due to an OSError") + parts.append('Could not install packages due to an OSError') if not show_traceback: - parts.append(": ") + parts.append(': ') parts.append(str(error)) else: - parts.append(".") + parts.append('.') # Spilt the error indication from a helper message (if any) - parts[-1] += "\n" + parts[-1] += '\n' # Suggest useful actions to the user: # (1) using user site-packages or (2) verifying the permissions if error.errno == errno.EACCES: - user_option_part = "Consider using the `--user` option" - permissions_part = "Check the permissions" + user_option_part = 'Consider using the `--user` option' + permissions_part = 'Check the permissions' if not running_under_virtualenv() and not using_user_site: parts.extend( [ user_option_part, - " or ", + ' or ', permissions_part.lower(), - ] + ], ) else: parts.append(permissions_part) - parts.append(".\n") + parts.append('.\n') # Suggest the user to enable Long Paths if path length is # more than 260 if ( - WINDOWS - and error.errno == errno.ENOENT - and error.filename - and len(error.filename) > 260 + WINDOWS and + error.errno == errno.ENOENT and + error.filename and + len(error.filename) > 260 ): parts.append( - "HINT: This error might have occurred since " - "this system does not have Windows Long Path " - "support enabled. You can find information on " - "how to enable this at " - "https://pip.pypa.io/warnings/enable-long-paths\n" + 'HINT: This error might have occurred since ' + 'this system does not have Windows Long Path ' + 'support enabled. You can find information on ' + 'how to enable this at ' + 'https://pip.pypa.io/warnings/enable-long-paths\n', ) - return "".join(parts).strip() + "\n" + return ''.join(parts).strip() + '\n' diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/list.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/list.py index 57f05e0..ec2701f 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/list.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/list.py @@ -1,9 +1,15 @@ +from __future__ import annotations + import json import logging from optparse import Values -from typing import TYPE_CHECKING, Iterator, List, Optional, Sequence, Tuple, cast - -from pip._vendor.packaging.utils import canonicalize_name +from typing import cast +from typing import Iterator +from typing import List +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import TYPE_CHECKING from pip._internal.cli import cmdoptions from pip._internal.cli.req_command import IndexGroupCommand @@ -11,11 +17,14 @@ from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import CommandError from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder -from pip._internal.metadata import BaseDistribution, get_environment +from pip._internal.metadata import BaseDistribution +from pip._internal.metadata import get_environment from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.network.session import PipSession from pip._internal.utils.compat import stdlib_pkgs -from pip._internal.utils.misc import tabulate, write_output +from pip._internal.utils.misc import tabulate +from pip._internal.utils.misc import write_output +from pip._vendor.packaging.utils import canonicalize_name if TYPE_CHECKING: from pip._internal.metadata.base import DistributionVersion @@ -49,81 +58,81 @@ class ListCommand(IndexGroupCommand): def add_options(self) -> None: self.cmd_opts.add_option( - "-o", - "--outdated", - action="store_true", + '-o', + '--outdated', + action='store_true', default=False, - help="List outdated packages", + help='List outdated packages', ) self.cmd_opts.add_option( - "-u", - "--uptodate", - action="store_true", + '-u', + '--uptodate', + action='store_true', default=False, - help="List uptodate packages", + help='List uptodate packages', ) self.cmd_opts.add_option( - "-e", - "--editable", - action="store_true", + '-e', + '--editable', + action='store_true', default=False, - help="List editable projects.", + help='List editable projects.', ) self.cmd_opts.add_option( - "-l", - "--local", - action="store_true", + '-l', + '--local', + action='store_true', default=False, help=( - "If in a virtualenv that has global access, do not list " - "globally-installed packages." + 'If in a virtualenv that has global access, do not list ' + 'globally-installed packages.' ), ) self.cmd_opts.add_option( - "--user", - dest="user", - action="store_true", + '--user', + dest='user', + action='store_true', default=False, - help="Only output packages installed in user-site.", + help='Only output packages installed in user-site.', ) self.cmd_opts.add_option(cmdoptions.list_path()) self.cmd_opts.add_option( - "--pre", - action="store_true", + '--pre', + action='store_true', default=False, help=( - "Include pre-release and development versions. By default, " - "pip only finds stable versions." + 'Include pre-release and development versions. By default, ' + 'pip only finds stable versions.' ), ) self.cmd_opts.add_option( - "--format", - action="store", - dest="list_format", - default="columns", - choices=("columns", "freeze", "json"), - help="Select the output format among: columns (default), freeze, or json", + '--format', + action='store', + dest='list_format', + default='columns', + choices=('columns', 'freeze', 'json'), + help='Select the output format among: columns (default), freeze, or json', ) self.cmd_opts.add_option( - "--not-required", - action="store_true", - dest="not_required", - help="List packages that are not dependencies of installed packages.", + '--not-required', + action='store_true', + dest='not_required', + help='List packages that are not dependencies of installed packages.', ) self.cmd_opts.add_option( - "--exclude-editable", - action="store_false", - dest="include_editable", - help="Exclude editable package from output.", + '--exclude-editable', + action='store_false', + dest='include_editable', + help='Exclude editable package from output.', ) self.cmd_opts.add_option( - "--include-editable", - action="store_true", - dest="include_editable", - help="Include editable package from output.", + '--include-editable', + action='store_true', + dest='include_editable', + help='Include editable package from output.', default=True, ) self.cmd_opts.add_option(cmdoptions.list_exclude()) @@ -133,7 +142,7 @@ class ListCommand(IndexGroupCommand): self.parser.insert_option_group(0, self.cmd_opts) def _build_package_finder( - self, options: Values, session: PipSession + self, options: Values, session: PipSession, ) -> PackageFinder: """ Create a package finder appropriate to this list command. @@ -149,12 +158,12 @@ class ListCommand(IndexGroupCommand): return PackageFinder.create( link_collector=link_collector, selection_prefs=selection_prefs, - use_deprecated_html5lib="html5lib" in options.deprecated_features_enabled, + use_deprecated_html5lib='html5lib' in options.deprecated_features_enabled, ) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: if options.outdated and options.uptodate: - raise CommandError("Options --outdated and --uptodate cannot be combined.") + raise CommandError('Options --outdated and --uptodate cannot be combined.') cmdoptions.check_list_path_option(options) @@ -162,8 +171,8 @@ class ListCommand(IndexGroupCommand): if options.excludes: skip.update(canonicalize_name(n) for n in options.excludes) - packages: "_ProcessedDists" = [ - cast("_DistWithLatestInfo", d) + packages: _ProcessedDists = [ + cast('_DistWithLatestInfo', d) for d in get_environment(options.path).iter_installed_distributions( local_only=options.local, user_only=options.user, @@ -189,8 +198,8 @@ class ListCommand(IndexGroupCommand): return SUCCESS def get_outdated( - self, packages: "_ProcessedDists", options: Values - ) -> "_ProcessedDists": + self, packages: _ProcessedDists, options: Values, + ) -> _ProcessedDists: return [ dist for dist in self.iter_packages_latest_infos(packages, options) @@ -198,8 +207,8 @@ class ListCommand(IndexGroupCommand): ] def get_uptodate( - self, packages: "_ProcessedDists", options: Values - ) -> "_ProcessedDists": + self, packages: _ProcessedDists, options: Values, + ) -> _ProcessedDists: return [ dist for dist in self.iter_packages_latest_infos(packages, options) @@ -207,8 +216,8 @@ class ListCommand(IndexGroupCommand): ] def get_not_required( - self, packages: "_ProcessedDists", options: Values - ) -> "_ProcessedDists": + self, packages: _ProcessedDists, options: Values, + ) -> _ProcessedDists: dep_keys = { canonicalize_name(dep.name) for dist in packages @@ -221,14 +230,14 @@ class ListCommand(IndexGroupCommand): return list({pkg for pkg in packages if pkg.canonical_name not in dep_keys}) def iter_packages_latest_infos( - self, packages: "_ProcessedDists", options: Values - ) -> Iterator["_DistWithLatestInfo"]: + self, packages: _ProcessedDists, options: Values, + ) -> Iterator[_DistWithLatestInfo]: with self._build_session(options) as session: finder = self._build_package_finder(options, session) def latest_info( - dist: "_DistWithLatestInfo", - ) -> Optional["_DistWithLatestInfo"]: + dist: _DistWithLatestInfo, + ) -> _DistWithLatestInfo | None: all_candidates = finder.find_all_candidates(dist.canonical_name) if not options.pre: # Remove prereleases @@ -247,9 +256,9 @@ class ListCommand(IndexGroupCommand): remote_version = best_candidate.version if best_candidate.link.is_wheel: - typ = "wheel" + typ = 'wheel' else: - typ = "sdist" + typ = 'sdist' dist.latest_version = remote_version dist.latest_filetype = typ return dist @@ -259,28 +268,28 @@ class ListCommand(IndexGroupCommand): yield dist def output_package_listing( - self, packages: "_ProcessedDists", options: Values + self, packages: _ProcessedDists, options: Values, ) -> None: packages = sorted( packages, key=lambda dist: dist.canonical_name, ) - if options.list_format == "columns" and packages: + if options.list_format == 'columns' and packages: data, header = format_for_columns(packages, options) self.output_package_listing_columns(data, header) - elif options.list_format == "freeze": + elif options.list_format == 'freeze': for dist in packages: if options.verbose >= 1: write_output( - "%s==%s (%s)", dist.raw_name, dist.version, dist.location + '%s==%s (%s)', dist.raw_name, dist.version, dist.location, ) else: - write_output("%s==%s", dist.raw_name, dist.version) - elif options.list_format == "json": + write_output('%s==%s', dist.raw_name, dist.version) + elif options.list_format == 'json': write_output(format_for_json(packages, options)) def output_package_listing_columns( - self, data: List[List[str]], header: List[str] + self, data: list[list[str]], header: list[str], ) -> None: # insert the header first: we need to know the size of column names if len(data) > 0: @@ -290,33 +299,33 @@ class ListCommand(IndexGroupCommand): # Create and add a separator. if len(data) > 0: - pkg_strings.insert(1, " ".join(map(lambda x: "-" * x, sizes))) + pkg_strings.insert(1, ' '.join(map(lambda x: '-' * x, sizes))) for val in pkg_strings: write_output(val) def format_for_columns( - pkgs: "_ProcessedDists", options: Values -) -> Tuple[List[List[str]], List[str]]: + pkgs: _ProcessedDists, options: Values, +) -> tuple[list[list[str]], list[str]]: """ Convert the package data into something usable by output_package_listing_columns. """ - header = ["Package", "Version"] + header = ['Package', 'Version'] running_outdated = options.outdated if running_outdated: - header.extend(["Latest", "Type"]) + header.extend(['Latest', 'Type']) has_editables = any(x.editable for x in pkgs) if has_editables: - header.append("Editable project location") + header.append('Editable project location') if options.verbose >= 1: - header.append("Location") + header.append('Location') if options.verbose >= 1: - header.append("Installer") + header.append('Installer') data = [] for proj in pkgs: @@ -329,10 +338,10 @@ def format_for_columns( row.append(proj.latest_filetype) if has_editables: - row.append(proj.editable_project_location or "") + row.append(proj.editable_project_location or '') if options.verbose >= 1: - row.append(proj.location or "") + row.append(proj.location or '') if options.verbose >= 1: row.append(proj.installer) @@ -341,21 +350,21 @@ def format_for_columns( return data, header -def format_for_json(packages: "_ProcessedDists", options: Values) -> str: +def format_for_json(packages: _ProcessedDists, options: Values) -> str: data = [] for dist in packages: info = { - "name": dist.raw_name, - "version": str(dist.version), + 'name': dist.raw_name, + 'version': str(dist.version), } if options.verbose >= 1: - info["location"] = dist.location or "" - info["installer"] = dist.installer + info['location'] = dist.location or '' + info['installer'] = dist.installer if options.outdated: - info["latest_version"] = str(dist.latest_version) - info["latest_filetype"] = dist.latest_filetype + info['latest_version'] = str(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype editable_project_location = dist.editable_project_location if editable_project_location: - info["editable_project_location"] = editable_project_location + info['editable_project_location'] = editable_project_location data.append(info) return json.dumps(data) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/search.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/search.py index 03ed925..afc78be 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/search.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/search.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import shutil import sys @@ -5,19 +7,22 @@ import textwrap import xmlrpc.client from collections import OrderedDict from optparse import Values -from typing import TYPE_CHECKING, Dict, List, Optional - -from pip._vendor.packaging.version import parse as parse_version +from typing import Dict +from typing import List +from typing import Optional +from typing import TYPE_CHECKING from pip._internal.cli.base_command import Command from pip._internal.cli.req_command import SessionCommandMixin -from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS +from pip._internal.cli.status_codes import NO_MATCHES_FOUND +from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import CommandError from pip._internal.metadata import get_default_environment from pip._internal.models.index import PyPI from pip._internal.network.xmlrpc import PipXmlrpcTransport from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import write_output +from pip._vendor.packaging.version import parse as parse_version if TYPE_CHECKING: from typing import TypedDict @@ -25,7 +30,7 @@ if TYPE_CHECKING: class TransformedHit(TypedDict): name: str summary: str - versions: List[str] + versions: list[str] logger = logging.getLogger(__name__) @@ -40,19 +45,19 @@ class SearchCommand(Command, SessionCommandMixin): def add_options(self) -> None: self.cmd_opts.add_option( - "-i", - "--index", - dest="index", - metavar="URL", + '-i', + '--index', + dest='index', + metavar='URL', default=PyPI.pypi_url, - help="Base URL of Python Package Index (default %default)", + help='Base URL of Python Package Index (default %default)', ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: if not args: - raise CommandError("Missing required argument (search query).") + raise CommandError('Missing required argument (search query).') query = args pypi_hits = self.search(query, options) hits = transform_hits(pypi_hits) @@ -66,7 +71,7 @@ class SearchCommand(Command, SessionCommandMixin): return SUCCESS return NO_MATCHES_FOUND - def search(self, query: List[str], options: Values) -> List[Dict[str, str]]: + def search(self, query: list[str], options: Values) -> list[dict[str, str]]: index_url = options.index session = self.get_default_session(options) @@ -74,9 +79,9 @@ class SearchCommand(Command, SessionCommandMixin): transport = PipXmlrpcTransport(index_url, session) pypi = xmlrpc.client.ServerProxy(index_url, transport) try: - hits = pypi.search({"name": query, "summary": query}, "or") + hits = pypi.search({'name': query, 'summary': query}, 'or') except xmlrpc.client.Fault as fault: - message = "XMLRPC request failed [code: {code}]\n{string}".format( + message = 'XMLRPC request failed [code: {code}]\n{string}'.format( code=fault.faultCode, string=fault.faultString, ) @@ -85,30 +90,30 @@ class SearchCommand(Command, SessionCommandMixin): return hits -def transform_hits(hits: List[Dict[str, str]]) -> List["TransformedHit"]: +def transform_hits(hits: list[dict[str, str]]) -> list[TransformedHit]: """ The list from pypi is really a list of versions. We want a list of packages with the list of versions stored inline. This converts the list from pypi into one we can use. """ - packages: Dict[str, "TransformedHit"] = OrderedDict() + packages: dict[str, TransformedHit] = OrderedDict() for hit in hits: - name = hit["name"] - summary = hit["summary"] - version = hit["version"] + name = hit['name'] + summary = hit['summary'] + version = hit['version'] if name not in packages.keys(): packages[name] = { - "name": name, - "summary": summary, - "versions": [version], + 'name': name, + 'summary': summary, + 'versions': [version], } else: - packages[name]["versions"].append(version) + packages[name]['versions'].append(version) # if this is the highest version, replace summary and score - if version == highest_version(packages[name]["versions"]): - packages[name]["summary"] = summary + if version == highest_version(packages[name]['versions']): + packages[name]['summary'] = summary return list(packages.values()) @@ -119,23 +124,23 @@ def print_dist_installation_info(name: str, latest: str) -> None: if dist is not None: with indent_log(): if dist.version == latest: - write_output("INSTALLED: %s (latest)", dist.version) + write_output('INSTALLED: %s (latest)', dist.version) else: - write_output("INSTALLED: %s", dist.version) + write_output('INSTALLED: %s', dist.version) if parse_version(latest).pre: write_output( - "LATEST: %s (pre-release; install" - " with `pip install --pre`)", + 'LATEST: %s (pre-release; install' + ' with `pip install --pre`)', latest, ) else: - write_output("LATEST: %s", latest) + write_output('LATEST: %s', latest) def print_results( - hits: List["TransformedHit"], - name_column_width: Optional[int] = None, - terminal_width: Optional[int] = None, + hits: list[TransformedHit], + name_column_width: int | None = None, + terminal_width: int | None = None, ) -> None: if not hits: return @@ -143,26 +148,26 @@ def print_results( name_column_width = ( max( [ - len(hit["name"]) + len(highest_version(hit.get("versions", ["-"]))) + len(hit['name']) + len(highest_version(hit.get('versions', ['-']))) for hit in hits - ] - ) - + 4 + ], + ) + + 4 ) for hit in hits: - name = hit["name"] - summary = hit["summary"] or "" - latest = highest_version(hit.get("versions", ["-"])) + name = hit['name'] + summary = hit['summary'] or '' + latest = highest_version(hit.get('versions', ['-'])) if terminal_width is not None: target_width = terminal_width - name_column_width - 5 if target_width > 10: # wrap and indent summary to fit terminal summary_lines = textwrap.wrap(summary, target_width) - summary = ("\n" + " " * (name_column_width + 3)).join(summary_lines) + summary = ('\n' + ' ' * (name_column_width + 3)).join(summary_lines) - name_latest = f"{name} ({latest})" - line = f"{name_latest:{name_column_width}} - {summary}" + name_latest = f'{name} ({latest})' + line = f'{name_latest:{name_column_width}} - {summary}' try: write_output(line) print_dist_installation_info(name, latest) @@ -170,5 +175,5 @@ def print_results( pass -def highest_version(versions: List[str]) -> str: +def highest_version(versions: list[str]) -> str: return max(versions, key=parse_version) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/show.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/show.py index d5540d6..09266b5 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/show.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/show.py @@ -1,13 +1,19 @@ +from __future__ import annotations + import logging from optparse import Values -from typing import Iterator, List, NamedTuple, Optional - -from pip._vendor.packaging.utils import canonicalize_name +from typing import Iterator +from typing import List +from typing import NamedTuple +from typing import Optional from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR, SUCCESS -from pip._internal.metadata import BaseDistribution, get_default_environment +from pip._internal.cli.status_codes import ERROR +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.metadata import BaseDistribution +from pip._internal.metadata import get_default_environment from pip._internal.utils.misc import write_output +from pip._vendor.packaging.utils import canonicalize_name logger = logging.getLogger(__name__) @@ -25,25 +31,25 @@ class ShowCommand(Command): def add_options(self) -> None: self.cmd_opts.add_option( - "-f", - "--files", - dest="files", - action="store_true", + '-f', + '--files', + dest='files', + action='store_true', default=False, - help="Show the full list of installed files for each package.", + help='Show the full list of installed files for each package.', ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: if not args: - logger.warning("ERROR: Please provide a package name or names.") + logger.warning('ERROR: Please provide a package name or names.') return ERROR query = args results = search_packages_info(query) if not print_results( - results, list_files=options.files, verbose=options.verbose + results, list_files=options.files, verbose=options.verbose, ): return ERROR return SUCCESS @@ -53,21 +59,21 @@ class _PackageInfo(NamedTuple): name: str version: str location: str - requires: List[str] - required_by: List[str] + requires: list[str] + required_by: list[str] installer: str metadata_version: str - classifiers: List[str] + classifiers: list[str] summary: str homepage: str author: str author_email: str license: str - entry_points: List[str] - files: Optional[List[str]] + entry_points: list[str] + files: list[str] | None -def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]: +def search_packages_info(query: list[str]) -> Iterator[_PackageInfo]: """ Gather details from installed distributions. Print distribution name, version, location, and installed files. Installed files requires a @@ -79,14 +85,14 @@ def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]: installed = {dist.canonical_name: dist for dist in env.iter_distributions()} query_names = [canonicalize_name(name) for name in query] missing = sorted( - [name for name, pkg in zip(query, query_names) if pkg not in installed] + [name for name, pkg in zip(query, query_names) if pkg not in installed], ) if missing: - logger.warning("Package(s) not found: %s", ", ".join(missing)) + logger.warning('Package(s) not found: %s', ', '.join(missing)) def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]: return ( - dist.metadata["Name"] or "UNKNOWN" + dist.metadata['Name'] or 'UNKNOWN' for dist in installed.values() if current_dist.canonical_name in {canonicalize_name(d.name) for d in dist.iter_dependencies()} @@ -102,14 +108,14 @@ def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]: required_by = sorted(_get_requiring_packages(dist), key=str.lower) try: - entry_points_text = dist.read_text("entry_points.txt") + entry_points_text = dist.read_text('entry_points.txt') entry_points = entry_points_text.splitlines(keepends=False) except FileNotFoundError: entry_points = [] files_iter = dist.iter_declared_entries() if files_iter is None: - files: Optional[List[str]] = None + files: list[str] | None = None else: files = sorted(files_iter) @@ -118,17 +124,17 @@ def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]: yield _PackageInfo( name=dist.raw_name, version=str(dist.version), - location=dist.location or "", + location=dist.location or '', requires=requires, required_by=required_by, installer=dist.installer, - metadata_version=dist.metadata_version or "", - classifiers=metadata.get_all("Classifier", []), - summary=metadata.get("Summary", ""), - homepage=metadata.get("Home-page", ""), - author=metadata.get("Author", ""), - author_email=metadata.get("Author-email", ""), - license=metadata.get("License", ""), + metadata_version=dist.metadata_version or '', + classifiers=metadata.get_all('Classifier', []), + summary=metadata.get('Summary', ''), + homepage=metadata.get('Home-page', ''), + author=metadata.get('Author', ''), + author_email=metadata.get('Author-email', ''), + license=metadata.get('License', ''), entry_points=entry_points, files=files, ) @@ -146,33 +152,33 @@ def print_results( for i, dist in enumerate(distributions): results_printed = True if i > 0: - write_output("---") + write_output('---') - write_output("Name: %s", dist.name) - write_output("Version: %s", dist.version) - write_output("Summary: %s", dist.summary) - write_output("Home-page: %s", dist.homepage) - write_output("Author: %s", dist.author) - write_output("Author-email: %s", dist.author_email) - write_output("License: %s", dist.license) - write_output("Location: %s", dist.location) - write_output("Requires: %s", ", ".join(dist.requires)) - write_output("Required-by: %s", ", ".join(dist.required_by)) + write_output('Name: %s', dist.name) + write_output('Version: %s', dist.version) + write_output('Summary: %s', dist.summary) + write_output('Home-page: %s', dist.homepage) + write_output('Author: %s', dist.author) + write_output('Author-email: %s', dist.author_email) + write_output('License: %s', dist.license) + write_output('Location: %s', dist.location) + write_output('Requires: %s', ', '.join(dist.requires)) + write_output('Required-by: %s', ', '.join(dist.required_by)) if verbose: - write_output("Metadata-Version: %s", dist.metadata_version) - write_output("Installer: %s", dist.installer) - write_output("Classifiers:") + write_output('Metadata-Version: %s', dist.metadata_version) + write_output('Installer: %s', dist.installer) + write_output('Classifiers:') for classifier in dist.classifiers: - write_output(" %s", classifier) - write_output("Entry-points:") + write_output(' %s', classifier) + write_output('Entry-points:') for entry in dist.entry_points: - write_output(" %s", entry.strip()) + write_output(' %s', entry.strip()) if list_files: - write_output("Files:") + write_output('Files:') if dist.files is None: - write_output("Cannot locate RECORD or installed-files.txt") + write_output('Cannot locate RECORD or installed-files.txt') else: for line in dist.files: - write_output(" %s", line.strip()) + write_output(' %s', line.strip()) return results_printed diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/uninstall.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/uninstall.py index bb9e8e6..a8e4ea3 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/uninstall.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/uninstall.py @@ -1,19 +1,19 @@ +from __future__ import annotations + import logging from optparse import Values from typing import List -from pip._vendor.packaging.utils import canonicalize_name - from pip._internal.cli.base_command import Command -from pip._internal.cli.req_command import SessionCommandMixin, warn_if_run_as_root +from pip._internal.cli.req_command import SessionCommandMixin +from pip._internal.cli.req_command import warn_if_run_as_root from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import InstallationError from pip._internal.req import parse_requirements -from pip._internal.req.constructors import ( - install_req_from_line, - install_req_from_parsed_requirement, -) +from pip._internal.req.constructors import install_req_from_line +from pip._internal.req.constructors import install_req_from_parsed_requirement from pip._internal.utils.misc import protect_pip_from_modification_on_windows +from pip._vendor.packaging.utils import canonicalize_name logger = logging.getLogger(__name__) @@ -35,28 +35,28 @@ class UninstallCommand(Command, SessionCommandMixin): def add_options(self) -> None: self.cmd_opts.add_option( - "-r", - "--requirement", - dest="requirements", - action="append", + '-r', + '--requirement', + dest='requirements', + action='append', default=[], - metavar="file", + metavar='file', help=( - "Uninstall all the packages listed in the given requirements " - "file. This option can be used multiple times." + 'Uninstall all the packages listed in the given requirements ' + 'file. This option can be used multiple times.' ), ) self.cmd_opts.add_option( - "-y", - "--yes", - dest="yes", - action="store_true", + '-y', + '--yes', + dest='yes', + action='store_true', help="Don't ask for confirmation of uninstall deletions.", ) self.parser.insert_option_group(0, self.cmd_opts) - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: session = self.get_default_session(options) reqs_to_uninstall = {} @@ -69,28 +69,28 @@ class UninstallCommand(Command, SessionCommandMixin): reqs_to_uninstall[canonicalize_name(req.name)] = req else: logger.warning( - "Invalid requirement: %r ignored -" - " the uninstall command expects named" - " requirements.", + 'Invalid requirement: %r ignored -' + ' the uninstall command expects named' + ' requirements.', name, ) for filename in options.requirements: for parsed_req in parse_requirements( - filename, options=options, session=session + filename, options=options, session=session, ): req = install_req_from_parsed_requirement( - parsed_req, isolated=options.isolated_mode + parsed_req, isolated=options.isolated_mode, ) if req.name: reqs_to_uninstall[canonicalize_name(req.name)] = req if not reqs_to_uninstall: raise InstallationError( - f"You must give at least one requirement to {self.name} (see " - f'"pip help {self.name}")' + f'You must give at least one requirement to {self.name} (see ' + f'"pip help {self.name}")', ) protect_pip_from_modification_on_windows( - modifying_pip="pip" in reqs_to_uninstall + modifying_pip='pip' in reqs_to_uninstall, ) for req in reqs_to_uninstall.values(): diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/commands/wheel.py b/.venv/lib/python3.10/site-packages/pip/_internal/commands/wheel.py index d5b20dc..133ad72 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/commands/wheel.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/commands/wheel.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import shutil @@ -6,14 +8,17 @@ from typing import List from pip._internal.cache import WheelCache from pip._internal.cli import cmdoptions -from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.req_command import RequirementCommand +from pip._internal.cli.req_command import with_cleanup from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import CommandError from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_tracker import get_requirement_tracker -from pip._internal.utils.misc import ensure_dir, normalize_path +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.misc import normalize_path from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.wheel_builder import build, should_build_for_wheel_command +from pip._internal.wheel_builder import build +from pip._internal.wheel_builder import should_build_for_wheel_command logger = logging.getLogger(__name__) @@ -43,14 +48,14 @@ class WheelCommand(RequirementCommand): def add_options(self) -> None: self.cmd_opts.add_option( - "-w", - "--wheel-dir", - dest="wheel_dir", - metavar="dir", + '-w', + '--wheel-dir', + dest='wheel_dir', + metavar='dir', default=os.curdir, help=( - "Build wheels into , where the default is the " - "current working directory." + 'Build wheels into , where the default is the ' + 'current working directory.' ), ) self.cmd_opts.add_option(cmdoptions.no_binary()) @@ -68,9 +73,9 @@ class WheelCommand(RequirementCommand): self.cmd_opts.add_option(cmdoptions.progress_bar()) self.cmd_opts.add_option( - "--no-verify", - dest="no_verify", - action="store_true", + '--no-verify', + dest='no_verify', + action='store_true', default=False, help="Don't verify if built wheel is valid.", ) @@ -79,12 +84,12 @@ class WheelCommand(RequirementCommand): self.cmd_opts.add_option(cmdoptions.global_options()) self.cmd_opts.add_option( - "--pre", - action="store_true", + '--pre', + action='store_true', default=False, help=( - "Include pre-release and development versions. By default, " - "pip only finds stable versions." + 'Include pre-release and development versions. By default, ' + 'pip only finds stable versions.' ), ) @@ -99,7 +104,7 @@ class WheelCommand(RequirementCommand): self.parser.insert_option_group(0, self.cmd_opts) @with_cleanup - def run(self, options: Values, args: List[str]) -> int: + def run(self, options: Values, args: list[str]) -> int: cmdoptions.check_install_build_global(options) session = self.get_default_session(options) @@ -114,7 +119,7 @@ class WheelCommand(RequirementCommand): directory = TempDirectory( delete=not options.no_clean, - kind="wheel", + kind='wheel', globally_managed=True, ) @@ -144,7 +149,7 @@ class WheelCommand(RequirementCommand): requirement_set = resolver.resolve(reqs, check_supported_wheels=True) - reqs_to_build: List[InstallRequirement] = [] + reqs_to_build: list[InstallRequirement] = [] for req in requirement_set.requirements.values(): if req.is_wheel: preparer.save_linked_requirement(req) @@ -167,12 +172,12 @@ class WheelCommand(RequirementCommand): shutil.copy(req.local_file_path, options.wheel_dir) except OSError as e: logger.warning( - "Building wheel for %s failed: %s", + 'Building wheel for %s failed: %s', req.name, e, ) build_failures.append(req) if len(build_failures) != 0: - raise CommandError("Failed to build one or more wheels") + raise CommandError('Failed to build one or more wheels') return SUCCESS diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/configuration.py b/.venv/lib/python3.10/site-packages/pip/_internal/configuration.py index a8092d1..1814780 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/configuration.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/configuration.py @@ -10,35 +10,41 @@ Some terminology: - variant A single word describing where the configuration key-value pair came from """ +from __future__ import annotations import configparser import locale import os import sys -from typing import Any, Dict, Iterable, List, NewType, Optional, Tuple +from typing import Any +from typing import Dict +from typing import Iterable +from typing import List +from typing import NewType +from typing import Optional +from typing import Tuple -from pip._internal.exceptions import ( - ConfigurationError, - ConfigurationFileCouldNotBeLoaded, -) +from pip._internal.exceptions import ConfigurationError +from pip._internal.exceptions import ConfigurationFileCouldNotBeLoaded from pip._internal.utils import appdirs from pip._internal.utils.compat import WINDOWS from pip._internal.utils.logging import getLogger -from pip._internal.utils.misc import ensure_dir, enum +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.misc import enum RawConfigParser = configparser.RawConfigParser # Shorthand -Kind = NewType("Kind", str) +Kind = NewType('Kind', str) -CONFIG_BASENAME = "pip.ini" if WINDOWS else "pip.conf" -ENV_NAMES_IGNORED = "version", "help" +CONFIG_BASENAME = 'pip.ini' if WINDOWS else 'pip.conf' +ENV_NAMES_IGNORED = 'version', 'help' # The kinds of configurations there are. kinds = enum( - USER="user", # User Specific - GLOBAL="global", # System Wide - SITE="site", # [Virtual] Environment Specific - ENV="env", # from PIP_CONFIG_FILE - ENV_VAR="env-var", # from Environment Variables + USER='user', # User Specific + GLOBAL='global', # System Wide + SITE='site', # [Virtual] Environment Specific + ENV='env', # from PIP_CONFIG_FILE + ENV_VAR='env-var', # from Environment Variables ) OVERRIDE_ORDER = kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR VALID_LOAD_ONLY = kinds.USER, kinds.GLOBAL, kinds.SITE @@ -49,34 +55,34 @@ logger = getLogger(__name__) # NOTE: Maybe use the optionx attribute to normalize keynames. def _normalize_name(name: str) -> str: """Make a name consistent regardless of source (environment or file)""" - name = name.lower().replace("_", "-") - if name.startswith("--"): + name = name.lower().replace('_', '-') + if name.startswith('--'): name = name[2:] # only prefer long opts return name -def _disassemble_key(name: str) -> List[str]: - if "." not in name: +def _disassemble_key(name: str) -> list[str]: + if '.' not in name: error_message = ( - "Key does not contain dot separated section and key. " + 'Key does not contain dot separated section and key. ' "Perhaps you wanted to use 'global.{}' instead?" ).format(name) raise ConfigurationError(error_message) - return name.split(".", 1) + return name.split('.', 1) -def get_configuration_files() -> Dict[Kind, List[str]]: +def get_configuration_files() -> dict[Kind, list[str]]: global_config_files = [ - os.path.join(path, CONFIG_BASENAME) for path in appdirs.site_config_dirs("pip") + os.path.join(path, CONFIG_BASENAME) for path in appdirs.site_config_dirs('pip') ] site_config_file = os.path.join(sys.prefix, CONFIG_BASENAME) legacy_config_file = os.path.join( - os.path.expanduser("~"), - "pip" if WINDOWS else ".pip", + os.path.expanduser('~'), + 'pip' if WINDOWS else '.pip', CONFIG_BASENAME, ) - new_config_file = os.path.join(appdirs.user_config_dir("pip"), CONFIG_BASENAME) + new_config_file = os.path.join(appdirs.user_config_dir('pip'), CONFIG_BASENAME) return { kinds.GLOBAL: global_config_files, kinds.SITE: [site_config_file], @@ -98,26 +104,26 @@ class Configuration: and the data stored is also nice. """ - def __init__(self, isolated: bool, load_only: Optional[Kind] = None) -> None: + def __init__(self, isolated: bool, load_only: Kind | None = None) -> None: super().__init__() if load_only is not None and load_only not in VALID_LOAD_ONLY: raise ConfigurationError( - "Got invalid value for load_only - should be one of {}".format( - ", ".join(map(repr, VALID_LOAD_ONLY)) - ) + 'Got invalid value for load_only - should be one of {}'.format( + ', '.join(map(repr, VALID_LOAD_ONLY)), + ), ) self.isolated = isolated self.load_only = load_only # Because we keep track of where we got the data from - self._parsers: Dict[Kind, List[Tuple[str, RawConfigParser]]] = { + self._parsers: dict[Kind, list[tuple[str, RawConfigParser]]] = { variant: [] for variant in OVERRIDE_ORDER } - self._config: Dict[Kind, Dict[str, Any]] = { + self._config: dict[Kind, dict[str, Any]] = { variant: {} for variant in OVERRIDE_ORDER } - self._modified_parsers: List[Tuple[str, RawConfigParser]] = [] + self._modified_parsers: list[tuple[str, RawConfigParser]] = [] def load(self) -> None: """Loads configuration from configuration files and environment""" @@ -125,16 +131,16 @@ class Configuration: if not self.isolated: self._load_environment_vars() - def get_file_to_edit(self) -> Optional[str]: + def get_file_to_edit(self) -> str | None: """Returns the file with highest priority in configuration""" - assert self.load_only is not None, "Need to be specified a file to be editing" + assert self.load_only is not None, 'Need to be specified a file to be editing' try: return self._get_parser_to_modify()[0] except IndexError: return None - def items(self) -> Iterable[Tuple[str, Any]]: + def items(self) -> Iterable[tuple[str, Any]]: """Returns key-value pairs like dict.items() representing the loaded configuration """ @@ -145,7 +151,7 @@ class Configuration: try: return self._dictionary[key] except KeyError: - raise ConfigurationError(f"No such key - {key}") + raise ConfigurationError(f'No such key - {key}') def set_value(self, key: str, value: Any) -> None: """Modify a value in the configuration.""" @@ -171,7 +177,7 @@ class Configuration: assert self.load_only if key not in self._config[self.load_only]: - raise ConfigurationError(f"No such key - {key}") + raise ConfigurationError(f'No such key - {key}') fname, parser = self._get_parser_to_modify() @@ -182,7 +188,7 @@ class Configuration: ): # The option was not removed. raise ConfigurationError( - "Fatal Internal error [id=1]. Please report as a bug." + 'Fatal Internal error [id=1]. Please report as a bug.', ) # The section may be empty after the option was removed. @@ -197,12 +203,12 @@ class Configuration: self._ensure_have_load_only() for fname, parser in self._modified_parsers: - logger.info("Writing to %s", fname) + logger.info('Writing to %s', fname) # Ensure directory exists. ensure_dir(os.path.dirname(fname)) - with open(fname, "w") as f: + with open(fname, 'w') as f: parser.write(f) # @@ -211,11 +217,11 @@ class Configuration: def _ensure_have_load_only(self) -> None: if self.load_only is None: - raise ConfigurationError("Needed a specific file to be modifying.") - logger.debug("Will be working with %s variant only", self.load_only) + raise ConfigurationError('Needed a specific file to be modifying.') + logger.debug('Will be working with %s variant only', self.load_only) @property - def _dictionary(self) -> Dict[str, Any]: + def _dictionary(self) -> dict[str, Any]: """A dictionary representing the loaded configuration.""" # NOTE: Dictionaries are not populated if not loaded. So, conditionals # are not needed here. @@ -231,8 +237,8 @@ class Configuration: config_files = dict(self.iter_config_files()) if config_files[kinds.ENV][0:1] == [os.devnull]: logger.debug( - "Skipping loading configuration files due to " - "environment's PIP_CONFIG_FILE being os.devnull" + 'Skipping loading configuration files due to ' + "environment's PIP_CONFIG_FILE being os.devnull", ) return @@ -272,7 +278,7 @@ class Configuration: except UnicodeDecodeError: # See https://github.com/pypa/pip/issues/4963 raise ConfigurationFileCouldNotBeLoaded( - reason=f"contains invalid {locale_encoding} characters", + reason=f'contains invalid {locale_encoding} characters', fname=fname, ) except configparser.Error as error: @@ -283,12 +289,12 @@ class Configuration: def _load_environment_vars(self) -> None: """Loads configuration from environment variables""" self._config[kinds.ENV_VAR].update( - self._normalized_keys(":env:", self.get_environ_vars()) + self._normalized_keys(':env:', self.get_environ_vars()), ) def _normalized_keys( - self, section: str, items: Iterable[Tuple[str, Any]] - ) -> Dict[str, Any]: + self, section: str, items: Iterable[tuple[str, Any]], + ) -> dict[str, Any]: """Normalizes items to construct a dictionary with normalized keys. This routine is where the names become keys and are made the same @@ -296,20 +302,20 @@ class Configuration: """ normalized = {} for name, val in items: - key = section + "." + _normalize_name(name) + key = section + '.' + _normalize_name(name) normalized[key] = val return normalized - def get_environ_vars(self) -> Iterable[Tuple[str, str]]: + def get_environ_vars(self) -> Iterable[tuple[str, str]]: """Returns a generator with all environmental vars with prefix PIP_""" for key, val in os.environ.items(): - if key.startswith("PIP_"): + if key.startswith('PIP_'): name = key[4:].lower() if name not in ENV_NAMES_IGNORED: yield name, val # XXX: This is patched in the tests. - def iter_config_files(self) -> Iterable[Tuple[Kind, List[str]]]: + def iter_config_files(self) -> Iterable[tuple[Kind, list[str]]]: """Yields variant and configuration files associated with it. This should be treated like items of a dictionary. @@ -317,7 +323,7 @@ class Configuration: # SMELL: Move the conditions out of this function # environment variables have the lowest priority - config_file = os.environ.get("PIP_CONFIG_FILE", None) + config_file = os.environ.get('PIP_CONFIG_FILE', None) if config_file is not None: yield kinds.ENV, [config_file] else: @@ -339,18 +345,18 @@ class Configuration: # finally virtualenv configuration first trumping others yield kinds.SITE, config_files[kinds.SITE] - def get_values_in_config(self, variant: Kind) -> Dict[str, Any]: + def get_values_in_config(self, variant: Kind) -> dict[str, Any]: """Get values present in a config file""" return self._config[variant] - def _get_parser_to_modify(self) -> Tuple[str, RawConfigParser]: + def _get_parser_to_modify(self) -> tuple[str, RawConfigParser]: # Determine which parser to modify assert self.load_only parsers = self._parsers[self.load_only] if not parsers: # This should not happen if everything works correctly. raise ConfigurationError( - "Fatal Internal error [id=2]. Please report as a bug." + 'Fatal Internal error [id=2]. Please report as a bug.', ) # Use the highest priority parser. @@ -363,4 +369,4 @@ class Configuration: self._modified_parsers.append(file_parser_tuple) def __repr__(self) -> str: - return f"{self.__class__.__name__}({self._dictionary!r})" + return f'{self.__class__.__name__}({self._dictionary!r})' diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/__init__.py index 9a89a83..387cccd 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pip._internal.distributions.base import AbstractDistribution from pip._internal.distributions.sdist import SourceDistribution from pip._internal.distributions.wheel import WheelDistribution diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/base.py b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/base.py index 149fff5..9000910 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/base.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/base.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import abc from pip._internal.index.package_finder import PackageFinder @@ -31,6 +33,6 @@ class AbstractDistribution(metaclass=abc.ABCMeta): @abc.abstractmethod def prepare_distribution_metadata( - self, finder: PackageFinder, build_isolation: bool + self, finder: PackageFinder, build_isolation: bool, ) -> None: raise NotImplementedError() diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/installed.py b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/installed.py index be5962f..173ef43 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/installed.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/installed.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pip._internal.distributions.base import AbstractDistribution from pip._internal.index.package_finder import PackageFinder from pip._internal.metadata import BaseDistribution @@ -11,10 +13,10 @@ class InstalledDistribution(AbstractDistribution): """ def get_metadata_distribution(self) -> BaseDistribution: - assert self.req.satisfied_by is not None, "not actually installed" + assert self.req.satisfied_by is not None, 'not actually installed' return self.req.satisfied_by def prepare_distribution_metadata( - self, finder: PackageFinder, build_isolation: bool + self, finder: PackageFinder, build_isolation: bool, ) -> None: pass diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/sdist.py b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/sdist.py index bdaf403..0e5104b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/sdist.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/sdist.py @@ -1,5 +1,9 @@ +from __future__ import annotations + import logging -from typing import Iterable, Set, Tuple +from typing import Iterable +from typing import Set +from typing import Tuple from pip._internal.build_env import BuildEnvironment from pip._internal.distributions.base import AbstractDistribution @@ -22,7 +26,7 @@ class SourceDistribution(AbstractDistribution): return self.req.get_dist() def prepare_distribution_metadata( - self, finder: PackageFinder, build_isolation: bool + self, finder: PackageFinder, build_isolation: bool, ) -> None: # Load pyproject.toml, to determine whether PEP 517 is to be used self.req.load_pyproject_toml() @@ -54,27 +58,27 @@ class SourceDistribution(AbstractDistribution): self.req.build_env = BuildEnvironment() self.req.build_env.install_requirements( - finder, pyproject_requires, "overlay", kind="build dependencies" + finder, pyproject_requires, 'overlay', kind='build dependencies', ) conflicting, missing = self.req.build_env.check_requirements( - self.req.requirements_to_check + self.req.requirements_to_check, ) if conflicting: - self._raise_conflicts("PEP 517/518 supported requirements", conflicting) + self._raise_conflicts('PEP 517/518 supported requirements', conflicting) if missing: logger.warning( - "Missing build requirements in pyproject.toml for %s.", + 'Missing build requirements in pyproject.toml for %s.', self.req, ) logger.warning( - "The project does not specify a build backend, and " - "pip cannot fall back to setuptools without %s.", - " and ".join(map(repr, sorted(missing))), + 'The project does not specify a build backend, and ' + 'pip cannot fall back to setuptools without %s.', + ' and '.join(map(repr, sorted(missing))), ) def _get_build_requires_wheel(self) -> Iterable[str]: with self.req.build_env: - runner = runner_with_spinner_message("Getting requirements to build wheel") + runner = runner_with_spinner_message('Getting requirements to build wheel') backend = self.req.pep517_backend assert backend is not None with backend.subprocess_runner(runner): @@ -83,7 +87,7 @@ class SourceDistribution(AbstractDistribution): def _get_build_requires_editable(self) -> Iterable[str]: with self.req.build_env: runner = runner_with_spinner_message( - "Getting requirements to build editable" + 'Getting requirements to build editable', ) backend = self.req.pep517_backend assert backend is not None @@ -95,32 +99,32 @@ class SourceDistribution(AbstractDistribution): # This must be done in a second pass, as the pyproject.toml # dependencies must be installed before we can call the backend. if ( - self.req.editable - and self.req.permit_editable_wheels - and self.req.supports_pyproject_editable() + self.req.editable and + self.req.permit_editable_wheels and + self.req.supports_pyproject_editable() ): build_reqs = self._get_build_requires_editable() else: build_reqs = self._get_build_requires_wheel() conflicting, missing = self.req.build_env.check_requirements(build_reqs) if conflicting: - self._raise_conflicts("the backend dependencies", conflicting) + self._raise_conflicts('the backend dependencies', conflicting) self.req.build_env.install_requirements( - finder, missing, "normal", kind="backend dependencies" + finder, missing, 'normal', kind='backend dependencies', ) def _raise_conflicts( - self, conflicting_with: str, conflicting_reqs: Set[Tuple[str, str]] + self, conflicting_with: str, conflicting_reqs: set[tuple[str, str]], ) -> None: format_string = ( - "Some build dependencies for {requirement} " - "conflict with {conflicting_with}: {description}." + 'Some build dependencies for {requirement} ' + 'conflict with {conflicting_with}: {description}.' ) error_message = format_string.format( requirement=self.req, conflicting_with=conflicting_with, - description=", ".join( - f"{installed} is incompatible with {wanted}" + description=', '.join( + f'{installed} is incompatible with {wanted}' for installed, wanted in sorted(conflicting_reqs) ), ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/wheel.py b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/wheel.py index 340b0f3..7d06632 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/distributions/wheel.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/distributions/wheel.py @@ -1,12 +1,11 @@ -from pip._vendor.packaging.utils import canonicalize_name +from __future__ import annotations from pip._internal.distributions.base import AbstractDistribution from pip._internal.index.package_finder import PackageFinder -from pip._internal.metadata import ( - BaseDistribution, - FilesystemWheel, - get_wheel_distribution, -) +from pip._internal.metadata import BaseDistribution +from pip._internal.metadata import FilesystemWheel +from pip._internal.metadata import get_wheel_distribution +from pip._vendor.packaging.utils import canonicalize_name class WheelDistribution(AbstractDistribution): @@ -20,12 +19,12 @@ class WheelDistribution(AbstractDistribution): Distribution that uses it, not relying on the wheel file or requirement. """ - assert self.req.local_file_path, "Set as part of preparation during download" - assert self.req.name, "Wheels are never unnamed" + assert self.req.local_file_path, 'Set as part of preparation during download' + assert self.req.name, 'Wheels are never unnamed' wheel = FilesystemWheel(self.req.local_file_path) return get_wheel_distribution(wheel, canonicalize_name(self.req.name)) def prepare_distribution_metadata( - self, finder: PackageFinder, build_isolation: bool + self, finder: PackageFinder, build_isolation: bool, ) -> None: pass diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/exceptions.py b/.venv/lib/python3.10/site-packages/pip/_internal/exceptions.py index 97b9612..3460f40 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/exceptions.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/exceptions.py @@ -4,14 +4,24 @@ This module MUST NOT try to import from anything within `pip._internal` to operate. This is expected to be importable from any/all files within the subpackage and, thus, should not depend on them. """ +from __future__ import annotations import configparser import re -from itertools import chain, groupby, repeat -from typing import TYPE_CHECKING, Dict, List, Optional, Union +from itertools import chain +from itertools import groupby +from itertools import repeat +from typing import Dict +from typing import List +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union -from pip._vendor.requests.models import Request, Response -from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult +from pip._vendor.requests.models import Request +from pip._vendor.requests.models import Response +from pip._vendor.rich.console import Console +from pip._vendor.rich.console import ConsoleOptions +from pip._vendor.rich.console import RenderResult from pip._vendor.rich.markup import escape from pip._vendor.rich.text import Text @@ -27,11 +37,11 @@ if TYPE_CHECKING: # Scaffolding # def _is_kebab_case(s: str) -> bool: - return re.match(r"^[a-z]+(-[a-z]+)*$", s) is not None + return re.match(r'^[a-z]+(-[a-z]+)*$', s) is not None def _prefix_with_indent( - s: Union[Text, str], + s: Text | str, console: Console, *, prefix: str, @@ -42,8 +52,8 @@ def _prefix_with_indent( else: text = console.render_str(s) - return console.render_str(prefix, overflow="ignore") + console.render_str( - f"\n{indent}", overflow="ignore" + return console.render_str(prefix, overflow='ignore') + console.render_str( + f'\n{indent}', overflow='ignore', ).join(text.split(allow_blank=True)) @@ -67,19 +77,19 @@ class DiagnosticPipError(PipError): def __init__( self, *, - kind: 'Literal["error", "warning"]' = "error", - reference: Optional[str] = None, - message: Union[str, Text], - context: Optional[Union[str, Text]], - hint_stmt: Optional[Union[str, Text]], - note_stmt: Optional[Union[str, Text]] = None, - link: Optional[str] = None, + kind: Literal["error", "warning"] = 'error', + reference: str | None = None, + message: str | Text, + context: str | Text | None, + hint_stmt: str | Text | None, + note_stmt: str | Text | None = None, + link: str | None = None, ) -> None: # Ensure a proper reference is provided. if reference is None: - assert hasattr(self, "reference"), "error reference not provided!" + assert hasattr(self, 'reference'), 'error reference not provided!' reference = self.reference - assert _is_kebab_case(reference), "error reference must be kebab-case!" + assert _is_kebab_case(reference), 'error reference must be kebab-case!' self.kind = kind self.reference = reference @@ -92,17 +102,17 @@ class DiagnosticPipError(PipError): self.link = link - super().__init__(f"<{self.__class__.__name__}: {self.reference}>") + super().__init__(f'<{self.__class__.__name__}: {self.reference}>') def __repr__(self) -> str: return ( - f"<{self.__class__.__name__}(" - f"reference={self.reference!r}, " - f"message={self.message!r}, " - f"context={self.context!r}, " - f"note_stmt={self.note_stmt!r}, " - f"hint_stmt={self.hint_stmt!r}" - ")>" + f'<{self.__class__.__name__}(' + f'reference={self.reference!r}, ' + f'message={self.message!r}, ' + f'context={self.context!r}, ' + f'note_stmt={self.note_stmt!r}, ' + f'hint_stmt={self.hint_stmt!r}' + ')>' ) def __rich_console__( @@ -110,10 +120,10 @@ class DiagnosticPipError(PipError): console: Console, options: ConsoleOptions, ) -> RenderResult: - colour = "red" if self.kind == "error" else "yellow" + colour = 'red' if self.kind == 'error' else 'yellow' - yield f"[{colour} bold]{self.kind}[/]: [bold]{self.reference}[/]" - yield "" + yield f'[{colour} bold]{self.kind}[/]: [bold]{self.reference}[/]' + yield '' if not options.ascii_only: # Present the main message, with relevant context indented. @@ -121,49 +131,49 @@ class DiagnosticPipError(PipError): yield _prefix_with_indent( self.message, console, - prefix=f"[{colour}]×[/] ", - indent=f"[{colour}]│[/] ", + prefix=f'[{colour}]×[/] ', + indent=f'[{colour}]│[/] ', ) yield _prefix_with_indent( self.context, console, - prefix=f"[{colour}]╰─>[/] ", - indent=f"[{colour}] [/] ", + prefix=f'[{colour}]╰─>[/] ', + indent=f'[{colour}] [/] ', ) else: yield _prefix_with_indent( self.message, console, - prefix="[red]×[/] ", - indent=" ", + prefix='[red]×[/] ', + indent=' ', ) else: yield self.message if self.context is not None: - yield "" + yield '' yield self.context if self.note_stmt is not None or self.hint_stmt is not None: - yield "" + yield '' if self.note_stmt is not None: yield _prefix_with_indent( self.note_stmt, console, - prefix="[magenta bold]note[/]: ", - indent=" ", + prefix='[magenta bold]note[/]: ', + indent=' ', ) if self.hint_stmt is not None: yield _prefix_with_indent( self.hint_stmt, console, - prefix="[cyan bold]hint[/]: ", - indent=" ", + prefix='[cyan bold]hint[/]: ', + indent=' ', ) if self.link is not None: - yield "" - yield f"Link: {self.link}" + yield '' + yield f'Link: {self.link}' # @@ -184,34 +194,34 @@ class UninstallationError(PipError): class MissingPyProjectBuildRequires(DiagnosticPipError): """Raised when pyproject.toml has `build-system`, but no `build-system.requires`.""" - reference = "missing-pyproject-build-system-requires" + reference = 'missing-pyproject-build-system-requires' def __init__(self, *, package: str) -> None: super().__init__( - message=f"Can not process {escape(package)}", + message=f'Can not process {escape(package)}', context=Text( - "This package has an invalid pyproject.toml file.\n" - "The [build-system] table is missing the mandatory `requires` key." + 'This package has an invalid pyproject.toml file.\n' + 'The [build-system] table is missing the mandatory `requires` key.', ), - note_stmt="This is an issue with the package mentioned above, not pip.", - hint_stmt=Text("See PEP 518 for the detailed specification."), + note_stmt='This is an issue with the package mentioned above, not pip.', + hint_stmt=Text('See PEP 518 for the detailed specification.'), ) class InvalidPyProjectBuildRequires(DiagnosticPipError): """Raised when pyproject.toml an invalid `build-system.requires`.""" - reference = "invalid-pyproject-build-system-requires" + reference = 'invalid-pyproject-build-system-requires' def __init__(self, *, package: str, reason: str) -> None: super().__init__( - message=f"Can not process {escape(package)}", + message=f'Can not process {escape(package)}', context=Text( - "This package has an invalid `build-system.requires` key in " - f"pyproject.toml.\n{reason}" + 'This package has an invalid `build-system.requires` key in ' + f'pyproject.toml.\n{reason}', ), - note_stmt="This is an issue with the package mentioned above, not pip.", - hint_stmt=Text("See PEP 518 for the detailed specification."), + note_stmt='This is an issue with the package mentioned above, not pip.', + hint_stmt=Text('See PEP 518 for the detailed specification.'), ) @@ -226,7 +236,7 @@ class NoneMetadataError(PipError): def __init__( self, - dist: "BaseDistribution", + dist: BaseDistribution, metadata_name: str, ) -> None: """ @@ -240,7 +250,7 @@ class NoneMetadataError(PipError): def __str__(self) -> str: # Use `dist` in the error message because its stringification # includes more information, like the version and location. - return "None {} metadata found for distribution: {}".format( + return 'None {} metadata found for distribution: {}'.format( self.metadata_name, self.dist, ) @@ -250,13 +260,13 @@ class UserInstallationInvalid(InstallationError): """A --user install is requested on an environment without user site.""" def __str__(self) -> str: - return "User base directory is not specified" + return 'User base directory is not specified' class InvalidSchemeCombination(InstallationError): def __str__(self) -> str: - before = ", ".join(str(a) for a in self.args[:-1]) - return f"Cannot set {before} and {self.args[-1]} together" + before = ', '.join(str(a) for a in self.args[:-1]) + return f'Cannot set {before} and {self.args[-1]} together' class DistributionNotFound(InstallationError): @@ -288,7 +298,7 @@ class NetworkConnectionError(PipError): """HTTP connection error""" def __init__( - self, error_msg: str, response: Response = None, request: Request = None + self, error_msg: str, response: Response = None, request: Request = None, ) -> None: """ Initialize NetworkConnectionError with `request` and `response` @@ -298,9 +308,9 @@ class NetworkConnectionError(PipError): self.request = request self.error_msg = error_msg if ( - self.response is not None - and not self.request - and hasattr(response, "request") + self.response is not None and + not self.request and + hasattr(response, 'request') ): self.request = self.response.request super().__init__(error_msg, response, request) @@ -337,7 +347,7 @@ class MetadataInconsistent(InstallationError): """ def __init__( - self, ireq: "InstallRequirement", field: str, f_val: str, m_val: str + self, ireq: InstallRequirement, field: str, f_val: str, m_val: str, ) -> None: self.ireq = ireq self.field = field @@ -346,8 +356,8 @@ class MetadataInconsistent(InstallationError): def __str__(self) -> str: template = ( - "Requested {} has inconsistent {}: " - "filename has {!r}, but metadata has {!r}" + 'Requested {} has inconsistent {}: ' + 'filename has {!r}, but metadata has {!r}' ) return template.format(self.ireq, self.field, self.f_val, self.m_val) @@ -355,48 +365,48 @@ class MetadataInconsistent(InstallationError): class LegacyInstallFailure(DiagnosticPipError): """Error occurred while executing `setup.py install`""" - reference = "legacy-install-failure" + reference = 'legacy-install-failure' def __init__(self, package_details: str) -> None: super().__init__( - message="Encountered error while trying to install package.", + message='Encountered error while trying to install package.', context=package_details, - hint_stmt="See above for output from the failure.", - note_stmt="This is an issue with the package mentioned above, not pip.", + hint_stmt='See above for output from the failure.', + note_stmt='This is an issue with the package mentioned above, not pip.', ) class InstallationSubprocessError(DiagnosticPipError, InstallationError): """A subprocess call failed.""" - reference = "subprocess-exited-with-error" + reference = 'subprocess-exited-with-error' def __init__( self, *, command_description: str, exit_code: int, - output_lines: Optional[List[str]], + output_lines: list[str] | None, ) -> None: if output_lines is None: - output_prompt = Text("See above for output.") + output_prompt = Text('See above for output.') else: output_prompt = ( - Text.from_markup(f"[red][{len(output_lines)} lines of output][/]\n") - + Text("".join(output_lines)) - + Text.from_markup(R"[red]\[end of output][/]") + Text.from_markup(f'[red][{len(output_lines)} lines of output][/]\n') + + Text(''.join(output_lines)) + + Text.from_markup(R'[red]\[end of output][/]') ) super().__init__( message=( - f"[green]{escape(command_description)}[/] did not run successfully.\n" - f"exit code: {exit_code}" + f'[green]{escape(command_description)}[/] did not run successfully.\n' + f'exit code: {exit_code}' ), context=output_prompt, hint_stmt=None, note_stmt=( - "This error originates from a subprocess, and is likely not a " - "problem with pip." + 'This error originates from a subprocess, and is likely not a ' + 'problem with pip.' ), ) @@ -404,11 +414,11 @@ class InstallationSubprocessError(DiagnosticPipError, InstallationError): self.exit_code = exit_code def __str__(self) -> str: - return f"{self.command_description} exited with {self.exit_code}" + return f'{self.command_description} exited with {self.exit_code}' class MetadataGenerationFailed(InstallationSubprocessError, InstallationError): - reference = "metadata-generation-failed" + reference = 'metadata-generation-failed' def __init__( self, @@ -416,23 +426,23 @@ class MetadataGenerationFailed(InstallationSubprocessError, InstallationError): package_details: str, ) -> None: super(InstallationSubprocessError, self).__init__( - message="Encountered error while generating package metadata.", + message='Encountered error while generating package metadata.', context=escape(package_details), - hint_stmt="See above for details.", - note_stmt="This is an issue with the package mentioned above, not pip.", + hint_stmt='See above for details.', + note_stmt='This is an issue with the package mentioned above, not pip.', ) def __str__(self) -> str: - return "metadata generation failed" + return 'metadata generation failed' class HashErrors(InstallationError): """Multiple HashError instances rolled into one for reporting""" def __init__(self) -> None: - self.errors: List["HashError"] = [] + self.errors: list[HashError] = [] - def append(self, error: "HashError") -> None: + def append(self, error: HashError) -> None: self.errors.append(error) def __str__(self) -> str: @@ -442,8 +452,8 @@ class HashErrors(InstallationError): lines.append(cls.head) lines.extend(e.body() for e in errors_of_cls) if lines: - return "\n".join(lines) - return "" + return '\n'.join(lines) + return '' def __bool__(self) -> bool: return bool(self.errors) @@ -466,8 +476,8 @@ class HashError(InstallationError): """ - req: Optional["InstallRequirement"] = None - head = "" + req: InstallRequirement | None = None + head = '' order: int = -1 def body(self) -> str: @@ -480,10 +490,10 @@ class HashError(InstallationError): its link already populated by the resolver's _populate_link(). """ - return f" {self._requirement_name()}" + return f' {self._requirement_name()}' def __str__(self) -> str: - return f"{self.head}\n{self.body()}" + return f'{self.head}\n{self.body()}' def _requirement_name(self) -> str: """Return a description of the requirement that triggered me. @@ -492,7 +502,7 @@ class HashError(InstallationError): line numbers """ - return str(self.req) if self.req else "unknown package" + return str(self.req) if self.req else 'unknown package' class VcsHashUnsupported(HashError): @@ -502,7 +512,7 @@ class VcsHashUnsupported(HashError): order = 0 head = ( "Can't verify hashes for these requirements because we don't " - "have a way to hash version control repositories:" + 'have a way to hash version control repositories:' ) @@ -513,7 +523,7 @@ class DirectoryUrlHashUnsupported(HashError): order = 1 head = ( "Can't verify hashes for these file:// requirements because they " - "point to directories:" + 'point to directories:' ) @@ -522,13 +532,13 @@ class HashMissing(HashError): order = 2 head = ( - "Hashes are required in --require-hashes mode, but they are " - "missing from some requirements. Here is a list of those " - "requirements along with the hashes their downloaded archives " - "actually had. Add lines like these to your requirements files to " - "prevent tampering. (If you did not enable --require-hashes " - "manually, note that it turns on automatically when any package " - "has a hash.)" + 'Hashes are required in --require-hashes mode, but they are ' + 'missing from some requirements. Here is a list of those ' + 'requirements along with the hashes their downloaded archives ' + 'actually had. Add lines like these to your requirements files to ' + 'prevent tampering. (If you did not enable --require-hashes ' + 'manually, note that it turns on automatically when any package ' + 'has a hash.)' ) def __init__(self, gotten_hash: str) -> None: @@ -552,10 +562,10 @@ class HashMissing(HashError): if self.req.original_link # In case someone feeds something downright stupid # to InstallRequirement's constructor. - else getattr(self.req, "req", None) + else getattr(self.req, 'req', None) ) - return " {} --hash={}:{}".format( - package or "unknown package", FAVORITE_HASH, self.gotten_hash + return ' {} --hash={}:{}'.format( + package or 'unknown package', FAVORITE_HASH, self.gotten_hash, ) @@ -565,8 +575,8 @@ class HashUnpinned(HashError): order = 3 head = ( - "In --require-hashes mode, all requirements must have their " - "versions pinned with ==. These do not:" + 'In --require-hashes mode, all requirements must have their ' + 'versions pinned with ==. These do not:' ) @@ -582,13 +592,13 @@ class HashMismatch(HashError): order = 4 head = ( - "THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS " - "FILE. If you have updated the package versions, please update " - "the hashes. Otherwise, examine the package contents carefully; " - "someone may have tampered with them." + 'THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS ' + 'FILE. If you have updated the package versions, please update ' + 'the hashes. Otherwise, examine the package contents carefully; ' + 'someone may have tampered with them.' ) - def __init__(self, allowed: Dict[str, List[str]], gots: Dict[str, "_Hash"]) -> None: + def __init__(self, allowed: dict[str, list[str]], gots: dict[str, _Hash]) -> None: """ :param allowed: A dict of algorithm names pointing to lists of allowed hex digests @@ -599,7 +609,7 @@ class HashMismatch(HashError): self.gots = gots def body(self) -> str: - return " {}:\n{}".format(self._requirement_name(), self._hash_comparison()) + return f' {self._requirement_name()}:\n{self._hash_comparison()}' def _hash_comparison(self) -> str: """ @@ -613,21 +623,21 @@ class HashMismatch(HashError): """ - def hash_then_or(hash_name: str) -> "chain[str]": + def hash_then_or(hash_name: str) -> chain[str]: # For now, all the decent hashes have 6-char names, so we can get # away with hard-coding space literals. - return chain([hash_name], repeat(" or")) + return chain([hash_name], repeat(' or')) - lines: List[str] = [] + lines: list[str] = [] for hash_name, expecteds in self.allowed.items(): prefix = hash_then_or(hash_name) lines.extend( - (" Expected {} {}".format(next(prefix), e)) for e in expecteds + (f' Expected {next(prefix)} {e}') for e in expecteds ) lines.append( - " Got {}\n".format(self.gots[hash_name].hexdigest()) + f' Got {self.gots[hash_name].hexdigest()}\n', ) - return "\n".join(lines) + return '\n'.join(lines) class UnsupportedPythonVersion(InstallationError): @@ -640,9 +650,9 @@ class ConfigurationFileCouldNotBeLoaded(ConfigurationError): def __init__( self, - reason: str = "could not be loaded", - fname: Optional[str] = None, - error: Optional[configparser.Error] = None, + reason: str = 'could not be loaded', + fname: str | None = None, + error: configparser.Error | None = None, ) -> None: super().__init__(error) self.reason = reason @@ -651,8 +661,8 @@ class ConfigurationFileCouldNotBeLoaded(ConfigurationError): def __str__(self) -> str: if self.fname is not None: - message_part = f" in {self.fname}." + message_part = f' in {self.fname}.' else: assert self.error is not None - message_part = f".\n{self.error}\n" - return f"Configuration file {self.reason}{message_part}" + message_part = f'.\n{self.error}\n' + return f'Configuration file {self.reason}{message_part}' diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/index/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/index/__init__.py index 7a17b7b..4b7b323 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/index/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/index/__init__.py @@ -1,2 +1,3 @@ """Index interaction code """ +from __future__ import annotations diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/index/collector.py b/.venv/lib/python3.10/site-packages/pip/_internal/index/collector.py index e6e9469..3f1ee9b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/index/collector.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/index/collector.py @@ -1,6 +1,7 @@ """ The main purpose of this module is to expose LinkCollector.collect_sources(). """ +from __future__ import annotations import cgi import collections @@ -14,23 +15,17 @@ import urllib.request import xml.etree.ElementTree from html.parser import HTMLParser from optparse import Values -from typing import ( - TYPE_CHECKING, - Callable, - Dict, - Iterable, - List, - MutableMapping, - NamedTuple, - Optional, - Sequence, - Tuple, - Union, -) - -from pip._vendor import html5lib, requests -from pip._vendor.requests import Response -from pip._vendor.requests.exceptions import RetryError, SSLError +from typing import Callable +from typing import Dict +from typing import Iterable +from typing import List +from typing import MutableMapping +from typing import NamedTuple +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union from pip._internal.exceptions import NetworkConnectionError from pip._internal.models.link import Link @@ -38,10 +33,18 @@ from pip._internal.models.search_scope import SearchScope from pip._internal.network.session import PipSession from pip._internal.network.utils import raise_for_status from pip._internal.utils.filetypes import is_archive_file -from pip._internal.utils.misc import pairwise, redact_auth_from_url +from pip._internal.utils.misc import pairwise +from pip._internal.utils.misc import redact_auth_from_url from pip._internal.vcs import vcs +from pip._vendor import html5lib +from pip._vendor import requests +from pip._vendor.requests import Response +from pip._vendor.requests.exceptions import RetryError +from pip._vendor.requests.exceptions import SSLError -from .sources import CandidatesFromPage, LinkSource, build_source +from .sources import build_source +from .sources import CandidatesFromPage +from .sources import LinkSource if TYPE_CHECKING: from typing import Protocol @@ -54,13 +57,13 @@ HTMLElement = xml.etree.ElementTree.Element ResponseHeaders = MutableMapping[str, str] -def _match_vcs_scheme(url: str) -> Optional[str]: +def _match_vcs_scheme(url: str) -> str | None: """Look for VCS schemes in the URL. Returns the matched VCS scheme, or None if there's no match. """ for scheme in vcs.schemes: - if url.lower().startswith(scheme) and url[len(scheme)] in "+:": + if url.lower().startswith(scheme) and url[len(scheme)] in '+:': return scheme return None @@ -77,8 +80,8 @@ def _ensure_html_header(response: Response) -> None: Raises `_NotHTML` if the content type is not text/html. """ - content_type = response.headers.get("Content-Type", "") - if not content_type.lower().startswith("text/html"): + content_type = response.headers.get('Content-Type', '') + if not content_type.lower().startswith('text/html'): raise _NotHTML(content_type, response.request.method) @@ -93,7 +96,7 @@ def _ensure_html_response(url: str, session: PipSession) -> None: `_NotHTML` if the content type is not text/html. """ scheme, netloc, path, query, fragment = urllib.parse.urlsplit(url) - if scheme not in {"http", "https"}: + if scheme not in {'http', 'https'}: raise _NotHTTP() resp = session.head(url, allow_redirects=True) @@ -118,12 +121,12 @@ def _get_html_response(url: str, session: PipSession) -> Response: if is_archive_file(Link(url).filename): _ensure_html_response(url, session=session) - logger.debug("Getting page %s", redact_auth_from_url(url)) + logger.debug('Getting page %s', redact_auth_from_url(url)) resp = session.get( url, headers={ - "Accept": "text/html", + 'Accept': 'text/html', # We don't want to blindly returned cached data for # /simple/, because authors generally expecting that # twine upload && pip install will function, but if @@ -137,7 +140,7 @@ def _get_html_response(url: str, session: PipSession) -> Response: # trip for the conditional GET now instead of only # once per 10 minutes. # For more information, please see pypa/pip#5670. - "Cache-Control": "max-age=0", + 'Cache-Control': 'max-age=0', }, ) raise_for_status(resp) @@ -152,12 +155,12 @@ def _get_html_response(url: str, session: PipSession) -> Response: return resp -def _get_encoding_from_headers(headers: ResponseHeaders) -> Optional[str]: +def _get_encoding_from_headers(headers: ResponseHeaders) -> str | None: """Determine if we have any encoding information in our headers.""" - if headers and "Content-Type" in headers: - content_type, params = cgi.parse_header(headers["Content-Type"]) - if "charset" in params: - return params["charset"] + if headers and 'Content-Type' in headers: + content_type, params = cgi.parse_header(headers['Content-Type']) + if 'charset' in params: + return params['charset'] return None @@ -175,8 +178,8 @@ def _determine_base_url(document: HTMLElement, page_url: str) -> str: TODO: Remove when `html5lib` is dropped. """ - for base in document.findall(".//base"): - href = base.get("href") + for base in document.findall('.//base'): + href = base.get('href') if href is not None: return href return page_url @@ -204,7 +207,7 @@ def _clean_file_url_path(part: str) -> str: # percent-encoded: / -_reserved_chars_re = re.compile("(@|%2F)", re.IGNORECASE) +_reserved_chars_re = re.compile('(@|%2F)', re.IGNORECASE) def _clean_url_path(path: str, is_local_path: bool) -> str: @@ -221,12 +224,12 @@ def _clean_url_path(path: str, is_local_path: bool) -> str: parts = _reserved_chars_re.split(path) cleaned_parts = [] - for to_clean, reserved in pairwise(itertools.chain(parts, [""])): + for to_clean, reserved in pairwise(itertools.chain(parts, [''])): cleaned_parts.append(clean_func(to_clean)) # Normalize %xx escapes (e.g. %2f -> %2F) cleaned_parts.append(reserved.upper()) - return "".join(cleaned_parts) + return ''.join(cleaned_parts) def _clean_link(url: str) -> str: @@ -245,20 +248,20 @@ def _clean_link(url: str) -> str: def _create_link_from_element( - element_attribs: Dict[str, Optional[str]], + element_attribs: dict[str, str | None], page_url: str, base_url: str, -) -> Optional[Link]: +) -> Link | None: """ Convert an anchor element's attributes in a simple repository page to a Link. """ - href = element_attribs.get("href") + href = element_attribs.get('href') if not href: return None url = _clean_link(urllib.parse.urljoin(base_url, href)) - pyrequire = element_attribs.get("data-requires-python") - yanked_reason = element_attribs.get("data-yanked") + pyrequire = element_attribs.get('data-requires-python') + yanked_reason = element_attribs.get('data-yanked') link = Link( url, @@ -271,7 +274,7 @@ def _create_link_from_element( class CacheablePageContent: - def __init__(self, page: "HTMLPage") -> None: + def __init__(self, page: HTMLPage) -> None: assert page.cache_link_parsing self.page = page @@ -284,7 +287,7 @@ class CacheablePageContent: class ParseLinks(Protocol): def __call__( - self, page: "HTMLPage", use_deprecated_html5lib: bool + self, page: HTMLPage, use_deprecated_html5lib: bool, ) -> Iterable[Link]: ... @@ -298,12 +301,12 @@ def with_cached_html_pages(fn: ParseLinks) -> ParseLinks: @functools.lru_cache(maxsize=None) def wrapper( - cacheable_page: CacheablePageContent, use_deprecated_html5lib: bool - ) -> List[Link]: + cacheable_page: CacheablePageContent, use_deprecated_html5lib: bool, + ) -> list[Link]: return list(fn(cacheable_page.page, use_deprecated_html5lib)) @functools.wraps(fn) - def wrapper_wrapper(page: "HTMLPage", use_deprecated_html5lib: bool) -> List[Link]: + def wrapper_wrapper(page: HTMLPage, use_deprecated_html5lib: bool) -> list[Link]: if page.cache_link_parsing: return wrapper(CacheablePageContent(page), use_deprecated_html5lib) return list(fn(page, use_deprecated_html5lib)) @@ -311,7 +314,7 @@ def with_cached_html_pages(fn: ParseLinks) -> ParseLinks: return wrapper_wrapper -def _parse_links_html5lib(page: "HTMLPage") -> Iterable[Link]: +def _parse_links_html5lib(page: HTMLPage) -> Iterable[Link]: """ Parse an HTML document, and yield its anchor elements as Link objects. @@ -325,7 +328,7 @@ def _parse_links_html5lib(page: "HTMLPage") -> Iterable[Link]: url = page.url base_url = _determine_base_url(document, url) - for anchor in document.findall(".//a"): + for anchor in document.findall('.//a'): link = _create_link_from_element( anchor.attrib, page_url=url, @@ -337,7 +340,7 @@ def _parse_links_html5lib(page: "HTMLPage") -> Iterable[Link]: @with_cached_html_pages -def parse_links(page: "HTMLPage", use_deprecated_html5lib: bool) -> Iterable[Link]: +def parse_links(page: HTMLPage, use_deprecated_html5lib: bool) -> Iterable[Link]: """ Parse an HTML document, and yield its anchor elements as Link objects. """ @@ -347,7 +350,7 @@ def parse_links(page: "HTMLPage", use_deprecated_html5lib: bool) -> Iterable[Lin return parser = HTMLLinkParser(page.url) - encoding = page.encoding or "utf-8" + encoding = page.encoding or 'utf-8' parser.feed(page.content.decode(encoding)) url = page.url @@ -369,7 +372,7 @@ class HTMLPage: def __init__( self, content: bytes, - encoding: Optional[str], + encoding: str | None, url: str, cache_link_parsing: bool = True, ) -> None: @@ -399,32 +402,32 @@ class HTMLLinkParser(HTMLParser): super().__init__(convert_charrefs=True) self.url: str = url - self.base_url: Optional[str] = None - self.anchors: List[Dict[str, Optional[str]]] = [] + self.base_url: str | None = None + self.anchors: list[dict[str, str | None]] = [] - def handle_starttag(self, tag: str, attrs: List[Tuple[str, Optional[str]]]) -> None: - if tag == "base" and self.base_url is None: + def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: + if tag == 'base' and self.base_url is None: href = self.get_href(attrs) if href is not None: self.base_url = href - elif tag == "a": + elif tag == 'a': self.anchors.append(dict(attrs)) - def get_href(self, attrs: List[Tuple[str, Optional[str]]]) -> Optional[str]: + def get_href(self, attrs: list[tuple[str, str | None]]) -> str | None: for name, value in attrs: - if name == "href": + if name == 'href': return value return None def _handle_get_page_fail( link: Link, - reason: Union[str, Exception], - meth: Optional[Callable[..., None]] = None, + reason: str | Exception, + meth: Callable[..., None] | None = None, ) -> None: if meth is None: meth = logger.debug - meth("Could not fetch URL %s: %s - skipping", link, reason) + meth('Could not fetch URL %s: %s - skipping', link, reason) def _make_html_page(response: Response, cache_link_parsing: bool = True) -> HTMLPage: @@ -438,20 +441,20 @@ def _make_html_page(response: Response, cache_link_parsing: bool = True) -> HTML def _get_html_page( - link: Link, session: Optional[PipSession] = None -) -> Optional["HTMLPage"]: + link: Link, session: PipSession | None = None, +) -> HTMLPage | None: if session is None: raise TypeError( - "_get_html_page() missing 1 required keyword argument: 'session'" + "_get_html_page() missing 1 required keyword argument: 'session'", ) - url = link.url.split("#", 1)[0] + url = link.url.split('#', 1)[0] # Check for VCS schemes that do not support lookup as web pages. vcs_scheme = _match_vcs_scheme(url) if vcs_scheme: logger.warning( - "Cannot look at %s URL %s because it does not support lookup as web pages.", + 'Cannot look at %s URL %s because it does not support lookup as web pages.', vcs_scheme, link, ) @@ -459,26 +462,26 @@ def _get_html_page( # Tack index.html onto file:// URLs that point to directories scheme, _, path, _, _, _ = urllib.parse.urlparse(url) - if scheme == "file" and os.path.isdir(urllib.request.url2pathname(path)): + if scheme == 'file' and os.path.isdir(urllib.request.url2pathname(path)): # add trailing slash if not present so urljoin doesn't trim # final segment - if not url.endswith("/"): - url += "/" - url = urllib.parse.urljoin(url, "index.html") - logger.debug(" file: URL is directory, getting %s", url) + if not url.endswith('/'): + url += '/' + url = urllib.parse.urljoin(url, 'index.html') + logger.debug(' file: URL is directory, getting %s', url) try: resp = _get_html_response(url, session=session) except _NotHTTP: logger.warning( - "Skipping page %s because it looks like an archive, and cannot " - "be checked by a HTTP HEAD request.", + 'Skipping page %s because it looks like an archive, and cannot ' + 'be checked by a HTTP HEAD request.', link, ) except _NotHTML as exc: logger.warning( - "Skipping page %s because the %s request got Content-Type: %s." - "The only supported Content-Type is text/html", + 'Skipping page %s because the %s request got Content-Type: %s.' + 'The only supported Content-Type is text/html', link, exc.request_desc, exc.content_type, @@ -488,21 +491,21 @@ def _get_html_page( except RetryError as exc: _handle_get_page_fail(link, exc) except SSLError as exc: - reason = "There was a problem confirming the ssl certificate: " + reason = 'There was a problem confirming the ssl certificate: ' reason += str(exc) _handle_get_page_fail(link, reason, meth=logger.info) except requests.ConnectionError as exc: - _handle_get_page_fail(link, f"connection error: {exc}") + _handle_get_page_fail(link, f'connection error: {exc}') except requests.Timeout: - _handle_get_page_fail(link, "timed out") + _handle_get_page_fail(link, 'timed out') else: return _make_html_page(resp, cache_link_parsing=link.cache_link_parsing) return None class CollectedSources(NamedTuple): - find_links: Sequence[Optional[LinkSource]] - index_urls: Sequence[Optional[LinkSource]] + find_links: Sequence[LinkSource | None] + index_urls: Sequence[LinkSource | None] class LinkCollector: @@ -528,7 +531,7 @@ class LinkCollector: session: PipSession, options: Values, suppress_no_index: bool = False, - ) -> "LinkCollector": + ) -> LinkCollector: """ :param session: The Session to use to make requests. :param suppress_no_index: Whether to ignore the --no-index option @@ -537,8 +540,8 @@ class LinkCollector: index_urls = [options.index_url] + options.extra_index_urls if options.no_index and not suppress_no_index: logger.debug( - "Ignoring indexes: %s", - ",".join(redact_auth_from_url(url) for url in index_urls), + 'Ignoring indexes: %s', + ','.join(redact_auth_from_url(url) for url in index_urls), ) index_urls = [] @@ -556,10 +559,10 @@ class LinkCollector: return link_collector @property - def find_links(self) -> List[str]: + def find_links(self) -> list[str]: return self.search_scope.find_links - def fetch_page(self, location: Link) -> Optional[HTMLPage]: + def fetch_page(self, location: Link) -> HTMLPage | None: """ Fetch an HTML page containing package links. """ @@ -594,15 +597,15 @@ class LinkCollector: if logger.isEnabledFor(logging.DEBUG): lines = [ - f"* {s.link}" + f'* {s.link}' for s in itertools.chain(find_links_sources, index_url_sources) if s is not None and s.link is not None ] lines = [ - f"{len(lines)} location(s) to search " - f"for versions of {project_name}:" + f'{len(lines)} location(s) to search ' + f'for versions of {project_name}:', ] + lines - logger.debug("\n".join(lines)) + logger.debug('\n'.join(lines)) return CollectedSources( find_links=list(find_links_sources), diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/index/package_finder.py b/.venv/lib/python3.10/site-packages/pip/_internal/index/package_finder.py index 223d06d..19d3318 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/index/package_finder.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/index/package_finder.py @@ -1,27 +1,26 @@ """Routines related to PyPI, indexes""" - # The following comment should be removed at some point in the future. # mypy: strict-optional=False +from __future__ import annotations import functools import itertools import logging import re -from typing import FrozenSet, Iterable, List, Optional, Set, Tuple, Union +from typing import FrozenSet +from typing import Iterable +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple +from typing import Union -from pip._vendor.packaging import specifiers -from pip._vendor.packaging.tags import Tag -from pip._vendor.packaging.utils import canonicalize_name -from pip._vendor.packaging.version import _BaseVersion -from pip._vendor.packaging.version import parse as parse_version - -from pip._internal.exceptions import ( - BestVersionAlreadyInstalled, - DistributionNotFound, - InvalidWheelFilename, - UnsupportedWheel, -) -from pip._internal.index.collector import LinkCollector, parse_links +from pip._internal.exceptions import BestVersionAlreadyInstalled +from pip._internal.exceptions import DistributionNotFound +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.exceptions import UnsupportedWheel +from pip._internal.index.collector import LinkCollector +from pip._internal.index.collector import parse_links from pip._internal.models.candidate import InstallationCandidate from pip._internal.models.format_control import FormatControl from pip._internal.models.link import Link @@ -37,8 +36,13 @@ from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import build_netloc from pip._internal.utils.packaging import check_requires_python from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.tags import Tag +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import _BaseVersion +from pip._vendor.packaging.version import parse as parse_version -__all__ = ["FormatControl", "BestCandidateResult", "PackageFinder"] +__all__ = ['FormatControl', 'BestCandidateResult', 'PackageFinder'] logger = getLogger(__name__) @@ -49,7 +53,7 @@ CandidateSortingKey = Tuple[int, int, int, _BaseVersion, Optional[int], BuildTag def _check_link_requires_python( link: Link, - version_info: Tuple[int, int, int], + version_info: tuple[int, int, int], ignore_requires_python: bool = False, ) -> bool: """ @@ -68,16 +72,16 @@ def _check_link_requires_python( ) except specifiers.InvalidSpecifier: logger.debug( - "Ignoring invalid Requires-Python (%r) for link: %s", + 'Ignoring invalid Requires-Python (%r) for link: %s', link.requires_python, link, ) else: if not is_compatible: - version = ".".join(map(str, version_info)) + version = '.'.join(map(str, version_info)) if not ignore_requires_python: logger.verbose( - "Link requires a different Python (%s not in: %r): %s", + 'Link requires a different Python (%s not in: %r): %s', version, link.requires_python, link, @@ -85,7 +89,7 @@ def _check_link_requires_python( return False logger.debug( - "Ignoring failed Requires-Python check (%s not in: %r) for link: %s", + 'Ignoring failed Requires-Python check (%s not in: %r) for link: %s', version, link.requires_python, link, @@ -100,7 +104,7 @@ class LinkEvaluator: Responsible for evaluating links for a particular project. """ - _py_version_re = re.compile(r"-py([123]\.?[0-9]?)$") + _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') # Don't include an allow_yanked default value to make sure each call # site considers whether yanked releases are allowed. This also causes @@ -110,10 +114,10 @@ class LinkEvaluator: self, project_name: str, canonical_name: str, - formats: FrozenSet[str], + formats: frozenset[str], target_python: TargetPython, allow_yanked: bool, - ignore_requires_python: Optional[bool] = None, + ignore_requires_python: bool | None = None, ) -> None: """ :param project_name: The user supplied package name. @@ -143,7 +147,7 @@ class LinkEvaluator: self.project_name = project_name - def evaluate_link(self, link: Link) -> Tuple[bool, Optional[str]]: + def evaluate_link(self, link: Link) -> tuple[bool, str | None]: """ Determine whether a link is a candidate for installation. @@ -154,8 +158,8 @@ class LinkEvaluator: """ version = None if link.is_yanked and not self._allow_yanked: - reason = link.yanked_reason or "" - return (False, f"yanked for reason: {reason}") + reason = link.yanked_reason or '' + return (False, f'yanked for reason: {reason}') if link.egg_fragment: egg_info = link.egg_fragment @@ -163,21 +167,21 @@ class LinkEvaluator: else: egg_info, ext = link.splitext() if not ext: - return (False, "not a file") + return (False, 'not a file') if ext not in SUPPORTED_EXTENSIONS: - return (False, f"unsupported archive format: {ext}") - if "binary" not in self._formats and ext == WHEEL_EXTENSION: - reason = "No binaries permitted for {}".format(self.project_name) + return (False, f'unsupported archive format: {ext}') + if 'binary' not in self._formats and ext == WHEEL_EXTENSION: + reason = f'No binaries permitted for {self.project_name}' return (False, reason) - if "macosx10" in link.path and ext == ".zip": - return (False, "macosx10 one") + if 'macosx10' in link.path and ext == '.zip': + return (False, 'macosx10 one') if ext == WHEEL_EXTENSION: try: wheel = Wheel(link.filename) except InvalidWheelFilename: - return (False, "invalid wheel filename") + return (False, 'invalid wheel filename') if canonicalize_name(wheel.name) != self._canonical_name: - reason = "wrong project name (not {})".format(self.project_name) + reason = f'wrong project name (not {self.project_name})' return (False, reason) supported_tags = self._target_python.get_tags() @@ -187,8 +191,8 @@ class LinkEvaluator: file_tags = wheel.get_formatted_file_tags() reason = ( "none of the wheel's tags ({}) are compatible " - "(run pip debug --verbose to show compatible tags)".format( - ", ".join(file_tags) + '(run pip debug --verbose to show compatible tags)'.format( + ', '.join(file_tags), ) ) return (False, reason) @@ -196,8 +200,8 @@ class LinkEvaluator: version = wheel.version # This should be up by the self.ok_binary check, but see issue 2700. - if "source" not in self._formats and ext != WHEEL_EXTENSION: - reason = f"No sources permitted for {self.project_name}" + if 'source' not in self._formats and ext != WHEEL_EXTENSION: + reason = f'No sources permitted for {self.project_name}' return (False, reason) if not version: @@ -206,7 +210,7 @@ class LinkEvaluator: self._canonical_name, ) if not version: - reason = f"Missing project version for {self.project_name}" + reason = f'Missing project version for {self.project_name}' return (False, reason) match = self._py_version_re.search(version) @@ -214,7 +218,7 @@ class LinkEvaluator: version = version[: match.start()] py_version = match.group(1) if py_version != self._target_python.py_version: - return (False, "Python version is incorrect") + return (False, 'Python version is incorrect') supports_python = _check_link_requires_python( link, @@ -226,16 +230,16 @@ class LinkEvaluator: # _log_skipped_link(). return (False, None) - logger.debug("Found link %s, version: %s", link, version) + logger.debug('Found link %s, version: %s', link, version) return (True, version) def filter_unallowed_hashes( - candidates: List[InstallationCandidate], + candidates: list[InstallationCandidate], hashes: Hashes, project_name: str, -) -> List[InstallationCandidate]: +) -> list[InstallationCandidate]: """ Filter out candidates whose hashes aren't allowed, and return a new list of candidates. @@ -253,8 +257,8 @@ def filter_unallowed_hashes( """ if not hashes: logger.debug( - "Given no hashes to check %s links for project %r: " - "discarding no candidates", + 'Given no hashes to check %s links for project %r: ' + 'discarding no candidates', len(candidates), project_name, ) @@ -284,16 +288,16 @@ def filter_unallowed_hashes( filtered = list(candidates) if len(filtered) == len(candidates): - discard_message = "discarding no candidates" + discard_message = 'discarding no candidates' else: - discard_message = "discarding {} non-matches:\n {}".format( + discard_message = 'discarding {} non-matches:\n {}'.format( len(non_matches), - "\n ".join(str(candidate.link) for candidate in non_matches), + '\n '.join(str(candidate.link) for candidate in non_matches), ) logger.debug( - "Checked %s links for project %r against %s hashes " - "(%s matches, %s no digest): %s", + 'Checked %s links for project %r against %s hashes ' + '(%s matches, %s no digest): %s', len(candidates), project_name, hashes.digest_count, @@ -333,9 +337,9 @@ class BestCandidateResult: def __init__( self, - candidates: List[InstallationCandidate], - applicable_candidates: List[InstallationCandidate], - best_candidate: Optional[InstallationCandidate], + candidates: list[InstallationCandidate], + applicable_candidates: list[InstallationCandidate], + best_candidate: InstallationCandidate | None, ) -> None: """ :param candidates: A sequence of all available candidates found. @@ -375,12 +379,12 @@ class CandidateEvaluator: def create( cls, project_name: str, - target_python: Optional[TargetPython] = None, + target_python: TargetPython | None = None, prefer_binary: bool = False, allow_all_prereleases: bool = False, - specifier: Optional[specifiers.BaseSpecifier] = None, - hashes: Optional[Hashes] = None, - ) -> "CandidateEvaluator": + specifier: specifiers.BaseSpecifier | None = None, + hashes: Hashes | None = None, + ) -> CandidateEvaluator: """Create a CandidateEvaluator object. :param target_python: The target Python interpreter to use when @@ -410,11 +414,11 @@ class CandidateEvaluator: def __init__( self, project_name: str, - supported_tags: List[Tag], + supported_tags: list[Tag], specifier: specifiers.BaseSpecifier, prefer_binary: bool = False, allow_all_prereleases: bool = False, - hashes: Optional[Hashes] = None, + hashes: Hashes | None = None, ) -> None: """ :param supported_tags: The PEP 425 tags supported by the target @@ -435,8 +439,8 @@ class CandidateEvaluator: def get_applicable_candidates( self, - candidates: List[InstallationCandidate], - ) -> List[InstallationCandidate]: + candidates: list[InstallationCandidate], + ) -> list[InstallationCandidate]: """ Return the applicable candidates from a list of candidates. """ @@ -510,18 +514,18 @@ class CandidateEvaluator: try: pri = -( wheel.find_most_preferred_tag( - valid_tags, self._wheel_tag_preferences + valid_tags, self._wheel_tag_preferences, ) ) except ValueError: raise UnsupportedWheel( - "{} is not a supported wheel for this platform. It " - "can't be sorted.".format(wheel.filename) + '{} is not a supported wheel for this platform. It ' + "can't be sorted.".format(wheel.filename), ) if self._prefer_binary: binary_preference = 1 if wheel.build_tag is not None: - match = re.match(r"^(\d+)(.*)$", wheel.build_tag) + match = re.match(r'^(\d+)(.*)$', wheel.build_tag) build_tag_groups = match.groups() build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) else: # sdist @@ -539,8 +543,8 @@ class CandidateEvaluator: def sort_best_candidate( self, - candidates: List[InstallationCandidate], - ) -> Optional[InstallationCandidate]: + candidates: list[InstallationCandidate], + ) -> InstallationCandidate | None: """ Return the best candidate per the instance's sort order, or None if no candidate is acceptable. @@ -552,7 +556,7 @@ class CandidateEvaluator: def compute_best_candidate( self, - candidates: List[InstallationCandidate], + candidates: list[InstallationCandidate], ) -> BestCandidateResult: """ Compute and return a `BestCandidateResult` instance. @@ -581,9 +585,9 @@ class PackageFinder: target_python: TargetPython, allow_yanked: bool, use_deprecated_html5lib: bool, - format_control: Optional[FormatControl] = None, - candidate_prefs: Optional[CandidatePreferences] = None, - ignore_requires_python: Optional[bool] = None, + format_control: FormatControl | None = None, + candidate_prefs: CandidatePreferences | None = None, + ignore_requires_python: bool | None = None, ) -> None: """ This constructor is primarily meant to be used by the create() class @@ -610,7 +614,7 @@ class PackageFinder: self.format_control = format_control # These are boring links that have already been logged somehow. - self._logged_links: Set[Link] = set() + self._logged_links: set[Link] = set() # Don't include an allow_yanked default value to make sure each call # site considers whether yanked releases are allowed. This also causes @@ -621,10 +625,10 @@ class PackageFinder: cls, link_collector: LinkCollector, selection_prefs: SelectionPreferences, - target_python: Optional[TargetPython] = None, + target_python: TargetPython | None = None, *, use_deprecated_html5lib: bool, - ) -> "PackageFinder": + ) -> PackageFinder: """Create a PackageFinder. :param selection_prefs: The candidate selection preferences, as a @@ -664,11 +668,11 @@ class PackageFinder: self._link_collector.search_scope = search_scope @property - def find_links(self) -> List[str]: + def find_links(self) -> list[str]: return self._link_collector.find_links @property - def index_urls(self) -> List[str]: + def index_urls(self) -> list[str]: return self.search_scope.index_urls @property @@ -703,13 +707,13 @@ class PackageFinder: ignore_requires_python=self._ignore_requires_python, ) - def _sort_links(self, links: Iterable[Link]) -> List[Link]: + def _sort_links(self, links: Iterable[Link]) -> list[Link]: """ Returns elements of links in order, non-egg links first, egg links second, while eliminating duplicates """ eggs, no_eggs = [], [] - seen: Set[Link] = set() + seen: set[Link] = set() for link in links: if link not in seen: seen.add(link) @@ -723,12 +727,12 @@ class PackageFinder: if link not in self._logged_links: # Put the link at the end so the reason is more visible and because # the link string is usually very long. - logger.debug("Skipping link: %s: %s", reason, link) + logger.debug('Skipping link: %s: %s', reason, link) self._logged_links.add(link) def get_install_candidate( - self, link_evaluator: LinkEvaluator, link: Link - ) -> Optional[InstallationCandidate]: + self, link_evaluator: LinkEvaluator, link: Link, + ) -> InstallationCandidate | None: """ If the link is a candidate for install, convert it to an InstallationCandidate and return it. Otherwise, return None. @@ -746,8 +750,8 @@ class PackageFinder: ) def evaluate_links( - self, link_evaluator: LinkEvaluator, links: Iterable[Link] - ) -> List[InstallationCandidate]: + self, link_evaluator: LinkEvaluator, links: Iterable[Link], + ) -> list[InstallationCandidate]: """ Convert links that are candidates to InstallationCandidate objects. """ @@ -760,10 +764,10 @@ class PackageFinder: return candidates def process_project_url( - self, project_url: Link, link_evaluator: LinkEvaluator - ) -> List[InstallationCandidate]: + self, project_url: Link, link_evaluator: LinkEvaluator, + ) -> list[InstallationCandidate]: logger.debug( - "Fetching project page and analyzing links: %s", + 'Fetching project page and analyzing links: %s', project_url, ) html_page = self._link_collector.fetch_page(project_url) @@ -781,7 +785,7 @@ class PackageFinder: return package_links @functools.lru_cache(maxsize=None) - def find_all_candidates(self, project_name: str) -> List[InstallationCandidate]: + def find_all_candidates(self, project_name: str) -> list[InstallationCandidate]: """Find all available InstallationCandidate for project_name This checks index_urls and find_links. @@ -828,7 +832,7 @@ class PackageFinder: except Exception: paths.append(candidate.link.url) # it's not a local file - logger.debug("Local files found: %s", ", ".join(paths)) + logger.debug('Local files found: %s', ', '.join(paths)) # This is an intentional priority ordering return file_candidates + page_candidates @@ -836,8 +840,8 @@ class PackageFinder: def make_candidate_evaluator( self, project_name: str, - specifier: Optional[specifiers.BaseSpecifier] = None, - hashes: Optional[Hashes] = None, + specifier: specifiers.BaseSpecifier | None = None, + hashes: Hashes | None = None, ) -> CandidateEvaluator: """Create a CandidateEvaluator object to use.""" candidate_prefs = self._candidate_prefs @@ -854,8 +858,8 @@ class PackageFinder: def find_best_candidate( self, project_name: str, - specifier: Optional[specifiers.BaseSpecifier] = None, - hashes: Optional[Hashes] = None, + specifier: specifiers.BaseSpecifier | None = None, + hashes: Hashes | None = None, ) -> BestCandidateResult: """Find matches for the given project and specifier. @@ -874,8 +878,8 @@ class PackageFinder: return candidate_evaluator.compute_best_candidate(candidates) def find_requirement( - self, req: InstallRequirement, upgrade: bool - ) -> Optional[InstallationCandidate]: + self, req: InstallRequirement, upgrade: bool, + ) -> InstallationCandidate | None: """Try to find a Link matching req Expects req, an InstallRequirement and upgrade, a boolean @@ -890,7 +894,7 @@ class PackageFinder: ) best_candidate = best_candidate_result.best_candidate - installed_version: Optional[_BaseVersion] = None + installed_version: _BaseVersion | None = None if req.satisfied_by is not None: installed_version = req.satisfied_by.version @@ -900,25 +904,25 @@ class PackageFinder: # If we stop using the pkg_resources provided specifier and start # using our own, we can drop the cast to str(). return ( - ", ".join( + ', '.join( sorted( {str(c.version) for c in cand_iter}, key=parse_version, - ) - ) - or "none" + ), + ) or + 'none' ) if installed_version is None and best_candidate is None: logger.critical( - "Could not find a version that satisfies the requirement %s " - "(from versions: %s)", + 'Could not find a version that satisfies the requirement %s ' + '(from versions: %s)', req, _format_versions(best_candidate_result.iter_all()), ) raise DistributionNotFound( - "No matching distribution found for {}".format(req) + f'No matching distribution found for {req}', ) best_installed = False @@ -930,14 +934,14 @@ class PackageFinder: if not upgrade and installed_version is not None: if best_installed: logger.debug( - "Existing installed version (%s) is most up-to-date and " - "satisfies requirement", + 'Existing installed version (%s) is most up-to-date and ' + 'satisfies requirement', installed_version, ) else: logger.debug( - "Existing installed version (%s) satisfies requirement " - "(most up-to-date version is %s)", + 'Existing installed version (%s) satisfies requirement ' + '(most up-to-date version is %s)', installed_version, best_candidate.version, ) @@ -946,14 +950,14 @@ class PackageFinder: if best_installed: # We have an existing version, and its the best version logger.debug( - "Installed version (%s) is most up-to-date (past versions: %s)", + 'Installed version (%s) is most up-to-date (past versions: %s)', installed_version, _format_versions(best_candidate_result.iter_applicable()), ) raise BestVersionAlreadyInstalled logger.debug( - "Using version %s (newest of versions: %s)", + 'Using version %s (newest of versions: %s)', best_candidate.version, _format_versions(best_candidate_result.iter_applicable()), ) @@ -979,14 +983,14 @@ def _find_name_version_sep(fragment: str, canonical_name: str) -> int: # occurrences of dashes; if the string in front of it matches the canonical # name, this is the one separating the name and version parts. for i, c in enumerate(fragment): - if c != "-": + if c != '-': continue if canonicalize_name(fragment[:i]) == canonical_name: return i - raise ValueError(f"{fragment} does not match {canonical_name}") + raise ValueError(f'{fragment} does not match {canonical_name}') -def _extract_version_from_fragment(fragment: str, canonical_name: str) -> Optional[str]: +def _extract_version_from_fragment(fragment: str, canonical_name: str) -> str | None: """Parse the version string from a + filename "fragment" (stem) or egg fragment. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/index/sources.py b/.venv/lib/python3.10/site-packages/pip/_internal/index/sources.py index eec3f12..96166e0 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/index/sources.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/index/sources.py @@ -1,12 +1,18 @@ +from __future__ import annotations + import logging import mimetypes import os import pathlib -from typing import Callable, Iterable, Optional, Tuple +from typing import Callable +from typing import Iterable +from typing import Optional +from typing import Tuple from pip._internal.models.candidate import InstallationCandidate from pip._internal.models.link import Link -from pip._internal.utils.urls import path_to_url, url_to_path +from pip._internal.utils.urls import path_to_url +from pip._internal.utils.urls import url_to_path from pip._internal.vcs import is_url logger = logging.getLogger(__name__) @@ -19,7 +25,7 @@ PageValidator = Callable[[Link], bool] class LinkSource: @property - def link(self) -> Optional[Link]: + def link(self) -> Link | None: """Returns the underlying link, if there's one.""" raise NotImplementedError() @@ -33,7 +39,7 @@ class LinkSource: def _is_html_file(file_url: str) -> bool: - return mimetypes.guess_type(file_url, strict=False)[0] == "text/html" + return mimetypes.guess_type(file_url, strict=False)[0] == 'text/html' class _FlatDirectorySource(LinkSource): @@ -54,7 +60,7 @@ class _FlatDirectorySource(LinkSource): self._path = pathlib.Path(os.path.realpath(path)) @property - def link(self) -> Optional[Link]: + def link(self) -> Link | None: return None def page_candidates(self) -> FoundCandidates: @@ -91,7 +97,7 @@ class _LocalFileSource(LinkSource): self._link = link @property - def link(self) -> Optional[Link]: + def link(self) -> Link | None: return self._link def page_candidates(self) -> FoundCandidates: @@ -125,7 +131,7 @@ class _RemoteFileSource(LinkSource): self._link = link @property - def link(self) -> Optional[Link]: + def link(self) -> Link | None: return self._link def page_candidates(self) -> FoundCandidates: @@ -153,7 +159,7 @@ class _IndexDirectorySource(LinkSource): self._link = link @property - def link(self) -> Optional[Link]: + def link(self) -> Link | None: return self._link def page_candidates(self) -> FoundCandidates: @@ -170,14 +176,14 @@ def build_source( page_validator: PageValidator, expand_dir: bool, cache_link_parsing: bool, -) -> Tuple[Optional[str], Optional[LinkSource]]: +) -> tuple[str | None, LinkSource | None]: - path: Optional[str] = None - url: Optional[str] = None + path: str | None = None + url: str | None = None if os.path.exists(location): # Is a local path. url = path_to_url(location) path = location - elif location.startswith("file:"): # A file: URL. + elif location.startswith('file:'): # A file: URL. url = location path = url_to_path(location) elif is_url(location): @@ -186,7 +192,7 @@ def build_source( if url is None: msg = ( "Location '%s' is ignored: " - "it is either a non-existing path or lacks a specific scheme." + 'it is either a non-existing path or lacks a specific scheme.' ) logger.warning(msg, location) return (None, None) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/locations/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/locations/__init__.py index ac0c166..ec9eeb4 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/locations/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/locations/__init__.py @@ -1,45 +1,52 @@ +from __future__ import annotations + import functools import logging import os import pathlib import sys import sysconfig -from typing import Any, Dict, Iterator, List, Optional, Tuple +from typing import Any +from typing import Dict +from typing import Iterator +from typing import List +from typing import Optional +from typing import Tuple -from pip._internal.models.scheme import SCHEME_KEYS, Scheme +from pip._internal.models.scheme import Scheme +from pip._internal.models.scheme import SCHEME_KEYS from pip._internal.utils.compat import WINDOWS from pip._internal.utils.deprecation import deprecated from pip._internal.utils.virtualenv import running_under_virtualenv -from . import _distutils, _sysconfig -from .base import ( - USER_CACHE_DIR, - get_major_minor_version, - get_src_prefix, - is_osx_framework, - site_packages, - user_site, -) +from . import _distutils +from . import _sysconfig +from .base import get_major_minor_version +from .base import get_src_prefix +from .base import is_osx_framework +from .base import site_packages +from .base import USER_CACHE_DIR +from .base import user_site __all__ = [ - "USER_CACHE_DIR", - "get_bin_prefix", - "get_bin_user", - "get_major_minor_version", - "get_platlib", - "get_prefixed_libs", - "get_purelib", - "get_scheme", - "get_src_prefix", - "site_packages", - "user_site", + 'USER_CACHE_DIR', + 'get_bin_prefix', + 'get_bin_user', + 'get_major_minor_version', + 'get_platlib', + 'get_prefixed_libs', + 'get_purelib', + 'get_scheme', + 'get_src_prefix', + 'site_packages', + 'user_site', ] logger = logging.getLogger(__name__) -_PLATLIBDIR: str = getattr(sys, "platlibdir", "lib") +_PLATLIBDIR: str = getattr(sys, 'platlibdir', 'lib') _USE_SYSCONFIG_DEFAULT = sys.version_info >= (3, 10) @@ -55,7 +62,7 @@ def _should_use_sysconfig() -> bool: This is a function for testability, but should be constant during any one run. """ - return bool(getattr(sysconfig, "_PIP_USE_SYSCONFIG", _USE_SYSCONFIG_DEFAULT)) + return bool(getattr(sysconfig, '_PIP_USE_SYSCONFIG', _USE_SYSCONFIG_DEFAULT)) _USE_SYSCONFIG = _should_use_sysconfig() @@ -76,20 +83,20 @@ def _looks_like_bpo_44860() -> bool: from distutils.command.install import INSTALL_SCHEMES # type: ignore try: - unix_user_platlib = INSTALL_SCHEMES["unix_user"]["platlib"] + unix_user_platlib = INSTALL_SCHEMES['unix_user']['platlib'] except KeyError: return False - return unix_user_platlib == "$usersite" + return unix_user_platlib == '$usersite' -def _looks_like_red_hat_patched_platlib_purelib(scheme: Dict[str, str]) -> bool: - platlib = scheme["platlib"] - if "/$platlibdir/" in platlib: - platlib = platlib.replace("/$platlibdir/", f"/{_PLATLIBDIR}/") - if "/lib64/" not in platlib: +def _looks_like_red_hat_patched_platlib_purelib(scheme: dict[str, str]) -> bool: + platlib = scheme['platlib'] + if '/$platlibdir/' in platlib: + platlib = platlib.replace('/$platlibdir/', f'/{_PLATLIBDIR}/') + if '/lib64/' not in platlib: return False - unpatched = platlib.replace("/lib64/", "/lib/") - return unpatched.replace("$platbase/", "$base/") == scheme["purelib"] + unpatched = platlib.replace('/lib64/', '/lib/') + return unpatched.replace('$platbase/', '$base/') == scheme['purelib'] @functools.lru_cache(maxsize=None) @@ -101,9 +108,9 @@ def _looks_like_red_hat_lib() -> bool: from distutils.command.install import INSTALL_SCHEMES # type: ignore return all( - k in INSTALL_SCHEMES - and _looks_like_red_hat_patched_platlib_purelib(INSTALL_SCHEMES[k]) - for k in ("unix_prefix", "unix_home") + k in INSTALL_SCHEMES and + _looks_like_red_hat_patched_platlib_purelib(INSTALL_SCHEMES[k]) + for k in ('unix_prefix', 'unix_home') ) @@ -112,7 +119,7 @@ def _looks_like_debian_scheme() -> bool: """Debian adds two additional schemes.""" from distutils.command.install import INSTALL_SCHEMES # type: ignore - return "deb_system" in INSTALL_SCHEMES and "unix_local" in INSTALL_SCHEMES + return 'deb_system' in INSTALL_SCHEMES and 'unix_local' in INSTALL_SCHEMES @functools.lru_cache(maxsize=None) @@ -130,8 +137,8 @@ def _looks_like_red_hat_scheme() -> bool: cmd: Any = install(Distribution()) cmd.finalize_options() return ( - cmd.exec_prefix == f"{os.path.normpath(sys.exec_prefix)}/local" - and cmd.prefix == f"{os.path.normpath(sys.prefix)}/local" + cmd.exec_prefix == f'{os.path.normpath(sys.exec_prefix)}/local' and + cmd.prefix == f'{os.path.normpath(sys.prefix)}/local' ) @@ -145,10 +152,10 @@ def _looks_like_slackware_scheme() -> bool: if user_site is None: # User-site not available. return False try: - paths = sysconfig.get_paths(scheme="posix_user", expand=False) + paths = sysconfig.get_paths(scheme='posix_user', expand=False) except KeyError: # User-site not available. return False - return "/lib64/" in paths["purelib"] and "/lib64/" not in user_site + return '/lib64/' in paths['purelib'] and '/lib64/' not in user_site @functools.lru_cache(maxsize=None) @@ -162,16 +169,16 @@ def _looks_like_msys2_mingw_scheme() -> bool: MSYS2 MINGW's patch uses lowercase ``"lib"`` instead of the usual uppercase, and is missing the final ``"site-packages"``. """ - paths = sysconfig.get_paths("nt", expand=False) + paths = sysconfig.get_paths('nt', expand=False) return all( - "Lib" not in p and "lib" in p and not p.endswith("site-packages") - for p in (paths[key] for key in ("platlib", "purelib")) + 'Lib' not in p and 'lib' in p and not p.endswith('site-packages') + for p in (paths[key] for key in ('platlib', 'purelib')) ) -def _fix_abiflags(parts: Tuple[str]) -> Iterator[str]: - ldversion = sysconfig.get_config_var("LDVERSION") - abiflags: str = getattr(sys, "abiflags", None) +def _fix_abiflags(parts: tuple[str]) -> Iterator[str]: + ldversion = sysconfig.get_config_var('LDVERSION') + abiflags: str = getattr(sys, 'abiflags', None) # LDVERSION does not end with sys.abiflags. Just return the path unchanged. if not ldversion or not abiflags or not ldversion.endswith(abiflags): @@ -187,11 +194,11 @@ def _fix_abiflags(parts: Tuple[str]) -> Iterator[str]: @functools.lru_cache(maxsize=None) def _warn_mismatched(old: pathlib.Path, new: pathlib.Path, *, key: str) -> None: - issue_url = "https://github.com/pypa/pip/issues/10151" + issue_url = 'https://github.com/pypa/pip/issues/10151' message = ( - "Value for %s does not match. Please report this to <%s>" - "\ndistutils: %s" - "\nsysconfig: %s" + 'Value for %s does not match. Please report this to <%s>' + '\ndistutils: %s' + '\nsysconfig: %s' ) logger.log(_MISMATCH_LEVEL, message, key, issue_url, old, new) @@ -207,28 +214,28 @@ def _warn_if_mismatch(old: pathlib.Path, new: pathlib.Path, *, key: str) -> bool def _log_context( *, user: bool = False, - home: Optional[str] = None, - root: Optional[str] = None, - prefix: Optional[str] = None, + home: str | None = None, + root: str | None = None, + prefix: str | None = None, ) -> None: parts = [ - "Additional context:", - "user = %r", - "home = %r", - "root = %r", - "prefix = %r", + 'Additional context:', + 'user = %r', + 'home = %r', + 'root = %r', + 'prefix = %r', ] - logger.log(_MISMATCH_LEVEL, "\n".join(parts), user, home, root, prefix) + logger.log(_MISMATCH_LEVEL, '\n'.join(parts), user, home, root, prefix) def get_scheme( dist_name: str, user: bool = False, - home: Optional[str] = None, - root: Optional[str] = None, + home: str | None = None, + root: str | None = None, isolated: bool = False, - prefix: Optional[str] = None, + prefix: str | None = None, ) -> Scheme: new = _sysconfig.get_scheme( dist_name, @@ -263,12 +270,12 @@ def get_scheme( # directory name to be ``pypy`` instead. So we treat this as a bug fix # and not warn about it. See bpo-43307 and python/cpython#24628. skip_pypy_special_case = ( - sys.implementation.name == "pypy" - and home is not None - and k in ("platlib", "purelib") - and old_v.parent == new_v.parent - and old_v.name.startswith("python") - and new_v.name.startswith("pypy") + sys.implementation.name == 'pypy' and + home is not None and + k in ('platlib', 'purelib') and + old_v.parent == new_v.parent and + old_v.name.startswith('python') and + new_v.name.startswith('pypy') ) if skip_pypy_special_case: continue @@ -277,18 +284,18 @@ def get_scheme( # the ``include`` value, but distutils's ``headers`` does. We'll let # CPython decide whether this is a bug or feature. See bpo-43948. skip_osx_framework_user_special_case = ( - user - and is_osx_framework() - and k == "headers" - and old_v.parent.parent == new_v.parent - and old_v.parent.name.startswith("python") + user and + is_osx_framework() and + k == 'headers' and + old_v.parent.parent == new_v.parent and + old_v.parent.name.startswith('python') ) if skip_osx_framework_user_special_case: continue # On Red Hat and derived Linux distributions, distutils is patched to # use "lib64" instead of "lib" for platlib. - if k == "platlib" and _looks_like_red_hat_lib(): + if k == 'platlib' and _looks_like_red_hat_lib(): continue # On Python 3.9+, sysconfig's posix_user scheme sets platlib against @@ -296,12 +303,12 @@ def get_scheme( # using the same $usersite for both platlib and purelib. This creates a # mismatch when sys.platlibdir is not "lib". skip_bpo_44860 = ( - user - and k == "platlib" - and not WINDOWS - and sys.version_info >= (3, 9) - and _PLATLIBDIR != "lib" - and _looks_like_bpo_44860() + user and + k == 'platlib' and + not WINDOWS and + sys.version_info >= (3, 9) and + _PLATLIBDIR != 'lib' and + _looks_like_bpo_44860() ) if skip_bpo_44860: continue @@ -309,10 +316,10 @@ def get_scheme( # Slackware incorrectly patches posix_user to use lib64 instead of lib, # but not usersite to match the location. skip_slackware_user_scheme = ( - user - and k in ("platlib", "purelib") - and not WINDOWS - and _looks_like_slackware_scheme() + user and + k in ('platlib', 'purelib') and + not WINDOWS and + _looks_like_slackware_scheme() ) if skip_slackware_user_scheme: continue @@ -321,12 +328,12 @@ def get_scheme( # /usr/local instead of /usr. Debian also places lib in dist-packages # instead of site-packages, but the /usr/local check should cover it. skip_linux_system_special_case = ( - not (user or home or prefix or running_under_virtualenv()) - and old_v.parts[1:3] == ("usr", "local") - and len(new_v.parts) > 1 - and new_v.parts[1] == "usr" - and (len(new_v.parts) < 3 or new_v.parts[2] != "local") - and (_looks_like_red_hat_scheme() or _looks_like_debian_scheme()) + not (user or home or prefix or running_under_virtualenv()) and + old_v.parts[1:3] == ('usr', 'local') and + len(new_v.parts) > 1 and + new_v.parts[1] == 'usr' and + (len(new_v.parts) < 3 or new_v.parts[2] != 'local') and + (_looks_like_red_hat_scheme() or _looks_like_debian_scheme()) ) if skip_linux_system_special_case: continue @@ -334,10 +341,10 @@ def get_scheme( # On Python 3.7 and earlier, sysconfig does not include sys.abiflags in # the "pythonX.Y" part of the path, but distutils does. skip_sysconfig_abiflag_bug = ( - sys.version_info < (3, 8) - and not WINDOWS - and k in ("headers", "platlib", "purelib") - and tuple(_fix_abiflags(old_v.parts)) == new_v.parts + sys.version_info < (3, 8) and + not WINDOWS and + k in ('headers', 'platlib', 'purelib') and + tuple(_fix_abiflags(old_v.parts)) == new_v.parts ) if skip_sysconfig_abiflag_bug: continue @@ -345,7 +352,7 @@ def get_scheme( # MSYS2 MINGW's sysconfig patch does not include the "site-packages" # part of the path. This is incorrect and will be fixed in MSYS. skip_msys2_mingw_bug = ( - WINDOWS and k in ("platlib", "purelib") and _looks_like_msys2_mingw_scheme() + WINDOWS and k in ('platlib', 'purelib') and _looks_like_msys2_mingw_scheme() ) if skip_msys2_mingw_bug: continue @@ -355,14 +362,14 @@ def get_scheme( # triggers special logic in sysconfig that's not present in distutils. # https://github.com/python/cpython/blob/8c21941ddaf/Lib/sysconfig.py#L178-L194 skip_cpython_build = ( - sysconfig.is_python_build(check_home=True) - and not WINDOWS - and k in ("headers", "include", "platinclude") + sysconfig.is_python_build(check_home=True) and + not WINDOWS and + k in ('headers', 'include', 'platinclude') ) if skip_cpython_build: continue - warning_contexts.append((old_v, new_v, f"scheme.{k}")) + warning_contexts.append((old_v, new_v, f'scheme.{k}')) if not warning_contexts: return old @@ -382,10 +389,10 @@ def get_scheme( if any(default_old[k] != getattr(old, k) for k in SCHEME_KEYS): deprecated( reason=( - "Configuring installation scheme with distutils config files " - "is deprecated and will no longer work in the near future. If you " - "are using a Homebrew or Linuxbrew Python, please see discussion " - "at https://github.com/Homebrew/homebrew-core/issues/76621" + 'Configuring installation scheme with distutils config files ' + 'is deprecated and will no longer work in the near future. If you ' + 'are using a Homebrew or Linuxbrew Python, please see discussion ' + 'at https://github.com/Homebrew/homebrew-core/issues/76621' ), replacement=None, gone_in=None, @@ -406,13 +413,13 @@ def get_bin_prefix() -> str: return new old = _distutils.get_bin_prefix() - if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="bin_prefix"): + if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key='bin_prefix'): _log_context() return old def get_bin_user() -> str: - return _sysconfig.get_scheme("", user=True).scripts + return _sysconfig.get_scheme('', user=True).scripts def _looks_like_deb_system_dist_packages(value: str) -> bool: @@ -427,7 +434,7 @@ def _looks_like_deb_system_dist_packages(value: str) -> bool: """ if not _looks_like_debian_scheme(): return False - if value == "/usr/lib/python3/dist-packages": + if value == '/usr/lib/python3/dist-packages': return True return False @@ -441,7 +448,7 @@ def get_purelib() -> str: old = _distutils.get_purelib() if _looks_like_deb_system_dist_packages(old): return old - if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="purelib"): + if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key='purelib'): _log_context() return old @@ -455,12 +462,12 @@ def get_platlib() -> str: old = _distutils.get_platlib() if _looks_like_deb_system_dist_packages(old): return old - if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="platlib"): + if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key='platlib'): _log_context() return old -def _deduplicated(v1: str, v2: str) -> List[str]: +def _deduplicated(v1: str, v2: str) -> list[str]: """Deduplicate values from a list.""" if v1 == v2: return [v1] @@ -469,12 +476,12 @@ def _deduplicated(v1: str, v2: str) -> List[str]: def _looks_like_apple_library(path: str) -> bool: """Apple patches sysconfig to *always* look under */Library/Python*.""" - if sys.platform[:6] != "darwin": + if sys.platform[:6] != 'darwin': return False - return path == f"/Library/Python/{get_major_minor_version()}/site-packages" + return path == f'/Library/Python/{get_major_minor_version()}/site-packages' -def get_prefixed_libs(prefix: str) -> List[str]: +def get_prefixed_libs(prefix: str) -> list[str]: """Return the lib locations under ``prefix``.""" new_pure, new_plat = _sysconfig.get_prefixed_libs(prefix) if _USE_SYSCONFIG: @@ -493,9 +500,9 @@ def get_prefixed_libs(prefix: str) -> List[str]: reason=( "Python distributed by Apple's Command Line Tools incorrectly " "patches sysconfig to always point to '/Library/Python'. This " - "will cause build isolation to operate incorrectly on Python " - "3.10 or later. Please help report this to Apple so they can " - "fix this. https://developer.apple.com/bug-reporting/" + 'will cause build isolation to operate incorrectly on Python ' + '3.10 or later. Please help report this to Apple so they can ' + 'fix this. https://developer.apple.com/bug-reporting/' ), replacement=None, gone_in=None, @@ -506,12 +513,12 @@ def get_prefixed_libs(prefix: str) -> List[str]: _warn_if_mismatch( pathlib.Path(old_pure), pathlib.Path(new_pure), - key="prefixed-purelib", + key='prefixed-purelib', ), _warn_if_mismatch( pathlib.Path(old_plat), pathlib.Path(new_plat), - key="prefixed-platlib", + key='prefixed-platlib', ), ] if any(warned): diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/locations/_distutils.py b/.venv/lib/python3.10/site-packages/pip/_internal/locations/_distutils.py index 2ec79e6..5fdd0e1 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/locations/_distutils.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/locations/_distutils.py @@ -1,17 +1,22 @@ """Locations where we look for configs, install stuff, etc""" - # The following comment should be removed at some point in the future. # mypy: strict-optional=False +from __future__ import annotations import logging import os import sys -from distutils.cmd import Command as DistutilsCommand -from distutils.command.install import SCHEME_KEYS -from distutils.command.install import install as distutils_install_command -from distutils.sysconfig import get_python_lib -from typing import Dict, List, Optional, Tuple, Union, cast +from typing import cast +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple +from typing import Union +from distutils.cmd import Command as DistutilsCommand +from distutils.command.install import install as distutils_install_command +from distutils.command.install import SCHEME_KEYS +from distutils.sysconfig import get_python_lib from pip._internal.models.scheme import Scheme from pip._internal.utils.compat import WINDOWS from pip._internal.utils.virtualenv import running_under_virtualenv @@ -30,15 +35,15 @@ def distutils_scheme( prefix: str = None, *, ignore_config_files: bool = False, -) -> Dict[str, str]: +) -> dict[str, str]: """ Return a distutils install scheme """ from distutils.dist import Distribution - dist_args: Dict[str, Union[str, List[str]]] = {"name": dist_name} + dist_args: dict[str, str | list[str]] = {'name': dist_name} if isolated: - dist_args["script_args"] = ["--no-user-cfg"] + dist_args['script_args'] = ['--no-user-cfg'] d = Distribution(dist_args) if not ignore_config_files: @@ -48,21 +53,21 @@ def distutils_scheme( # Typeshed does not include find_config_files() for some reason. paths = d.find_config_files() # type: ignore logger.warning( - "Ignore distutils configs in %s due to encoding errors.", - ", ".join(os.path.basename(p) for p in paths), + 'Ignore distutils configs in %s due to encoding errors.', + ', '.join(os.path.basename(p) for p in paths), ) - obj: Optional[DistutilsCommand] = None - obj = d.get_command_obj("install", create=True) + obj: DistutilsCommand | None = None + obj = d.get_command_obj('install', create=True) assert obj is not None i = cast(distutils_install_command, obj) # NOTE: setting user or home has the side-effect of creating the home dir # or user base for installations during finalize_options() # ideally, we'd prefer a scheme class that has no side-effects. - assert not (user and prefix), f"user={user} prefix={prefix}" - assert not (home and prefix), f"home={home} prefix={prefix}" + assert not (user and prefix), f'user={user} prefix={prefix}' + assert not (home and prefix), f'home={home} prefix={prefix}' i.user = user or i.user if user or home: - i.prefix = "" + i.prefix = '' i.prefix = prefix or i.prefix i.home = home or i.home i.root = root or i.root @@ -70,14 +75,14 @@ def distutils_scheme( scheme = {} for key in SCHEME_KEYS: - scheme[key] = getattr(i, "install_" + key) + scheme[key] = getattr(i, 'install_' + key) # install_lib specified in setup.cfg should install *everything* # into there (i.e. it takes precedence over both purelib and # platlib). Note, i.install_lib is *always* set after # finalize_options(); we only want to override here if the user # has explicitly requested it hence going back to the config - if "install_lib" in d.get_option_dict("install"): + if 'install_lib' in d.get_option_dict('install'): scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) if running_under_virtualenv(): @@ -87,17 +92,17 @@ def distutils_scheme( prefix = i.install_userbase # type: ignore else: prefix = i.prefix - scheme["headers"] = os.path.join( + scheme['headers'] = os.path.join( prefix, - "include", - "site", - f"python{get_major_minor_version()}", + 'include', + 'site', + f'python{get_major_minor_version()}', dist_name, ) if root is not None: - path_no_drive = os.path.splitdrive(os.path.abspath(scheme["headers"]))[1] - scheme["headers"] = os.path.join(root, path_no_drive[1:]) + path_no_drive = os.path.splitdrive(os.path.abspath(scheme['headers']))[1] + scheme['headers'] = os.path.join(root, path_no_drive[1:]) return scheme @@ -105,10 +110,10 @@ def distutils_scheme( def get_scheme( dist_name: str, user: bool = False, - home: Optional[str] = None, - root: Optional[str] = None, + home: str | None = None, + root: str | None = None, isolated: bool = False, - prefix: Optional[str] = None, + prefix: str | None = None, ) -> Scheme: """ Get the "scheme" corresponding to the input parameters. The distutils @@ -129,11 +134,11 @@ def get_scheme( """ scheme = distutils_scheme(dist_name, user, home, root, isolated, prefix) return Scheme( - platlib=scheme["platlib"], - purelib=scheme["purelib"], - headers=scheme["headers"], - scripts=scheme["scripts"], - data=scheme["data"], + platlib=scheme['platlib'], + purelib=scheme['purelib'], + headers=scheme['headers'], + scripts=scheme['scripts'], + data=scheme['data'], ) @@ -142,16 +147,16 @@ def get_bin_prefix() -> str: # so we need to call normpath to eliminate them. prefix = os.path.normpath(sys.prefix) if WINDOWS: - bin_py = os.path.join(prefix, "Scripts") + bin_py = os.path.join(prefix, 'Scripts') # buildout uses 'bin' on Windows too? if not os.path.exists(bin_py): - bin_py = os.path.join(prefix, "bin") + bin_py = os.path.join(prefix, 'bin') return bin_py # Forcing to use /usr/local/bin for standard macOS framework installs # Also log to ~/Library/Logs/ for use with the Console.app log viewer - if sys.platform[:6] == "darwin" and prefix[:16] == "/System/Library/": - return "/usr/local/bin" - return os.path.join(prefix, "bin") + if sys.platform[:6] == 'darwin' and prefix[:16] == '/System/Library/': + return '/usr/local/bin' + return os.path.join(prefix, 'bin') def get_purelib() -> str: @@ -162,7 +167,7 @@ def get_platlib() -> str: return get_python_lib(plat_specific=True) -def get_prefixed_libs(prefix: str) -> Tuple[str, str]: +def get_prefixed_libs(prefix: str) -> tuple[str, str]: return ( get_python_lib(plat_specific=False, prefix=prefix), get_python_lib(plat_specific=True, prefix=prefix), diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/locations/_sysconfig.py b/.venv/lib/python3.10/site-packages/pip/_internal/locations/_sysconfig.py index 5e141aa..a5fdf2c 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/locations/_sysconfig.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/locations/_sysconfig.py @@ -1,15 +1,20 @@ -import distutils.util # FIXME: For change_root. +from __future__ import annotations + import logging import os import sys import sysconfig import typing -from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationInvalid -from pip._internal.models.scheme import SCHEME_KEYS, Scheme +import distutils.util # FIXME: For change_root. +from pip._internal.exceptions import InvalidSchemeCombination +from pip._internal.exceptions import UserInstallationInvalid +from pip._internal.models.scheme import Scheme +from pip._internal.models.scheme import SCHEME_KEYS from pip._internal.utils.virtualenv import running_under_virtualenv -from .base import get_major_minor_version, is_osx_framework +from .base import get_major_minor_version +from .base import is_osx_framework logger = logging.getLogger(__name__) @@ -24,7 +29,7 @@ logger = logging.getLogger(__name__) _AVAILABLE_SCHEMES = set(sysconfig.get_scheme_names()) -_PREFERRED_SCHEME_API = getattr(sysconfig, "get_preferred_scheme", None) +_PREFERRED_SCHEME_API = getattr(sysconfig, 'get_preferred_scheme', None) def _should_use_osx_framework_prefix() -> bool: @@ -47,9 +52,9 @@ def _should_use_osx_framework_prefix() -> bool: or our own, and we deal with this special case in ``get_scheme()`` instead. """ return ( - "osx_framework_library" in _AVAILABLE_SCHEMES - and not running_under_virtualenv() - and is_osx_framework() + 'osx_framework_library' in _AVAILABLE_SCHEMES and + not running_under_virtualenv() and + is_osx_framework() ) @@ -68,67 +73,67 @@ def _infer_prefix() -> str: If none of the above works, fall back to ``posix_prefix``. """ if _PREFERRED_SCHEME_API: - return _PREFERRED_SCHEME_API("prefix") + return _PREFERRED_SCHEME_API('prefix') if _should_use_osx_framework_prefix(): - return "osx_framework_library" - implementation_suffixed = f"{sys.implementation.name}_{os.name}" + return 'osx_framework_library' + implementation_suffixed = f'{sys.implementation.name}_{os.name}' if implementation_suffixed in _AVAILABLE_SCHEMES: return implementation_suffixed if sys.implementation.name in _AVAILABLE_SCHEMES: return sys.implementation.name - suffixed = f"{os.name}_prefix" + suffixed = f'{os.name}_prefix' if suffixed in _AVAILABLE_SCHEMES: return suffixed if os.name in _AVAILABLE_SCHEMES: # On Windows, prefx is just called "nt". return os.name - return "posix_prefix" + return 'posix_prefix' def _infer_user() -> str: """Try to find a user scheme for the current platform.""" if _PREFERRED_SCHEME_API: - return _PREFERRED_SCHEME_API("user") + return _PREFERRED_SCHEME_API('user') if is_osx_framework() and not running_under_virtualenv(): - suffixed = "osx_framework_user" + suffixed = 'osx_framework_user' else: - suffixed = f"{os.name}_user" + suffixed = f'{os.name}_user' if suffixed in _AVAILABLE_SCHEMES: return suffixed - if "posix_user" not in _AVAILABLE_SCHEMES: # User scheme unavailable. + if 'posix_user' not in _AVAILABLE_SCHEMES: # User scheme unavailable. raise UserInstallationInvalid() - return "posix_user" + return 'posix_user' def _infer_home() -> str: """Try to find a home for the current platform.""" if _PREFERRED_SCHEME_API: - return _PREFERRED_SCHEME_API("home") - suffixed = f"{os.name}_home" + return _PREFERRED_SCHEME_API('home') + suffixed = f'{os.name}_home' if suffixed in _AVAILABLE_SCHEMES: return suffixed - return "posix_home" + return 'posix_home' # Update these keys if the user sets a custom home. _HOME_KEYS = [ - "installed_base", - "base", - "installed_platbase", - "platbase", - "prefix", - "exec_prefix", + 'installed_base', + 'base', + 'installed_platbase', + 'platbase', + 'prefix', + 'exec_prefix', ] -if sysconfig.get_config_var("userbase") is not None: - _HOME_KEYS.append("userbase") +if sysconfig.get_config_var('userbase') is not None: + _HOME_KEYS.append('userbase') def get_scheme( dist_name: str, user: bool = False, - home: typing.Optional[str] = None, - root: typing.Optional[str] = None, + home: str | None = None, + root: str | None = None, isolated: bool = False, - prefix: typing.Optional[str] = None, + prefix: str | None = None, ) -> Scheme: """ Get the "scheme" corresponding to the input parameters. @@ -144,9 +149,9 @@ def get_scheme( base directory for the same """ if user and prefix: - raise InvalidSchemeCombination("--user", "--prefix") + raise InvalidSchemeCombination('--user', '--prefix') if home and prefix: - raise InvalidSchemeCombination("--home", "--prefix") + raise InvalidSchemeCombination('--home', '--prefix') if home is not None: scheme_name = _infer_home() @@ -158,8 +163,8 @@ def get_scheme( # Special case: When installing into a custom prefix, use posix_prefix # instead of osx_framework_library. See _should_use_osx_framework_prefix() # docstring for details. - if prefix is not None and scheme_name == "osx_framework_library": - scheme_name = "posix_prefix" + if prefix is not None and scheme_name == 'osx_framework_library': + scheme_name = 'posix_prefix' if home is not None: variables = {k: home for k in _HOME_KEYS} @@ -177,20 +182,20 @@ def get_scheme( # pip's historical header path logic (see point 1) did not do this. if running_under_virtualenv(): if user: - base = variables.get("userbase", sys.prefix) + base = variables.get('userbase', sys.prefix) else: - base = variables.get("base", sys.prefix) - python_xy = f"python{get_major_minor_version()}" - paths["include"] = os.path.join(base, "include", "site", python_xy) + base = variables.get('base', sys.prefix) + python_xy = f'python{get_major_minor_version()}' + paths['include'] = os.path.join(base, 'include', 'site', python_xy) elif not dist_name: - dist_name = "UNKNOWN" + dist_name = 'UNKNOWN' scheme = Scheme( - platlib=paths["platlib"], - purelib=paths["purelib"], - headers=os.path.join(paths["include"], dist_name), - scripts=paths["scripts"], - data=paths["data"], + platlib=paths['platlib'], + purelib=paths['purelib'], + headers=os.path.join(paths['include'], dist_name), + scripts=paths['scripts'], + data=paths['data'], ) if root is not None: for key in SCHEME_KEYS: @@ -201,19 +206,19 @@ def get_scheme( def get_bin_prefix() -> str: # Forcing to use /usr/local/bin for standard macOS framework installs. - if sys.platform[:6] == "darwin" and sys.prefix[:16] == "/System/Library/": - return "/usr/local/bin" - return sysconfig.get_paths()["scripts"] + if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': + return '/usr/local/bin' + return sysconfig.get_paths()['scripts'] def get_purelib() -> str: - return sysconfig.get_paths()["purelib"] + return sysconfig.get_paths()['purelib'] def get_platlib() -> str: - return sysconfig.get_paths()["platlib"] + return sysconfig.get_paths()['platlib'] -def get_prefixed_libs(prefix: str) -> typing.Tuple[str, str]: - paths = sysconfig.get_paths(vars={"base": prefix, "platbase": prefix}) - return (paths["purelib"], paths["platlib"]) +def get_prefixed_libs(prefix: str) -> tuple[str, str]: + paths = sysconfig.get_paths(vars={'base': prefix, 'platbase': prefix}) + return (paths['purelib'], paths['platlib']) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/locations/base.py b/.venv/lib/python3.10/site-packages/pip/_internal/locations/base.py index 86dad4a..acbf4e6 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/locations/base.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/locations/base.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import functools import os import site @@ -9,10 +11,10 @@ from pip._internal.utils import appdirs from pip._internal.utils.virtualenv import running_under_virtualenv # Application Directories -USER_CACHE_DIR = appdirs.user_cache_dir("pip") +USER_CACHE_DIR = appdirs.user_cache_dir('pip') # FIXME doesn't account for venv linked to global site-packages -site_packages: typing.Optional[str] = sysconfig.get_path("purelib") +site_packages: str | None = sysconfig.get_path('purelib') def get_major_minor_version() -> str: @@ -20,19 +22,19 @@ def get_major_minor_version() -> str: Return the major-minor version of the current Python as a string, e.g. "3.7" or "3.10". """ - return "{}.{}".format(*sys.version_info) + return '{}.{}'.format(*sys.version_info) def get_src_prefix() -> str: if running_under_virtualenv(): - src_prefix = os.path.join(sys.prefix, "src") + src_prefix = os.path.join(sys.prefix, 'src') else: # FIXME: keep src in cwd for now (it is not a temporary folder) try: - src_prefix = os.path.join(os.getcwd(), "src") + src_prefix = os.path.join(os.getcwd(), 'src') except OSError: # In case the current working directory has been renamed or deleted - sys.exit("The folder you are executing pip from can no longer be found.") + sys.exit('The folder you are executing pip from can no longer be found.') # under macOS + virtualenv sys.prefix is not properly resolved # it is something like /path/to/python/bin/.. @@ -42,11 +44,11 @@ def get_src_prefix() -> str: try: # Use getusersitepackages if this is present, as it ensures that the # value is initialised properly. - user_site: typing.Optional[str] = site.getusersitepackages() + user_site: str | None = site.getusersitepackages() except AttributeError: user_site = site.USER_SITE @functools.lru_cache(maxsize=None) def is_osx_framework() -> bool: - return bool(sysconfig.get_config_var("PYTHONFRAMEWORK")) + return bool(sysconfig.get_config_var('PYTHONFRAMEWORK')) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/main.py b/.venv/lib/python3.10/site-packages/pip/_internal/main.py index 33c6d24..dcc393b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/main.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/main.py @@ -1,7 +1,10 @@ -from typing import List, Optional +from __future__ import annotations + +from typing import List +from typing import Optional -def main(args: Optional[List[str]] = None) -> int: +def main(args: list[str] | None = None) -> int: """This is preserved for old console scripts that may still be referencing it. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/metadata/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/metadata/__init__.py index cc037c1..3b96f17 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/metadata/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/metadata/__init__.py @@ -1,16 +1,23 @@ -from typing import List, Optional +from __future__ import annotations -from .base import BaseDistribution, BaseEnvironment, FilesystemWheel, MemoryWheel, Wheel +from typing import List +from typing import Optional + +from .base import BaseDistribution +from .base import BaseEnvironment +from .base import FilesystemWheel +from .base import MemoryWheel +from .base import Wheel __all__ = [ - "BaseDistribution", - "BaseEnvironment", - "FilesystemWheel", - "MemoryWheel", - "Wheel", - "get_default_environment", - "get_environment", - "get_wheel_distribution", + 'BaseDistribution', + 'BaseEnvironment', + 'FilesystemWheel', + 'MemoryWheel', + 'Wheel', + 'get_default_environment', + 'get_environment', + 'get_wheel_distribution', ] @@ -26,7 +33,7 @@ def get_default_environment() -> BaseEnvironment: return Environment.default() -def get_environment(paths: Optional[List[str]]) -> BaseEnvironment: +def get_environment(paths: list[str] | None) -> BaseEnvironment: """Get a representation of the environment specified by ``paths``. This returns an Environment instance from the chosen backend based on the diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/metadata/base.py b/.venv/lib/python3.10/site-packages/pip/_internal/metadata/base.py index 1a5a781..687fb60 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/metadata/base.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/metadata/base.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import csv import email.message import json @@ -5,38 +7,35 @@ import logging import pathlib import re import zipfile -from typing import ( - IO, - TYPE_CHECKING, - Collection, - Container, - Iterable, - Iterator, - List, - Optional, - Tuple, - Union, -) - -from pip._vendor.packaging.requirements import Requirement -from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet -from pip._vendor.packaging.utils import NormalizedName -from pip._vendor.packaging.version import LegacyVersion, Version +from typing import Collection +from typing import Container +from typing import IO +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union from pip._internal.exceptions import NoneMetadataError -from pip._internal.locations import site_packages, user_site -from pip._internal.models.direct_url import ( - DIRECT_URL_METADATA_NAME, - DirectUrl, - DirectUrlValidationError, -) +from pip._internal.locations import site_packages +from pip._internal.locations import user_site +from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME +from pip._internal.models.direct_url import DirectUrl +from pip._internal.models.direct_url import DirectUrlValidationError from pip._internal.utils.compat import stdlib_pkgs # TODO: Move definition here. -from pip._internal.utils.egg_link import ( - egg_link_path_from_location, - egg_link_path_from_sys_path, -) -from pip._internal.utils.misc import is_local, normalize_path +from pip._internal.utils.egg_link import egg_link_path_from_location +from pip._internal.utils.egg_link import egg_link_path_from_sys_path +from pip._internal.utils.misc import is_local +from pip._internal.utils.misc import normalize_path from pip._internal.utils.urls import url_to_path +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.specifiers import InvalidSpecifier +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import NormalizedName +from pip._vendor.packaging.version import LegacyVersion +from pip._vendor.packaging.version import Version if TYPE_CHECKING: from typing import Protocol @@ -65,8 +64,8 @@ class BaseEntryPoint(Protocol): def _convert_installed_files_path( - entry: Tuple[str, ...], - info: Tuple[str, ...], + entry: tuple[str, ...], + info: tuple[str, ...], ) -> str: """Convert a legacy installed-files.txt path into modern RECORD path. @@ -85,9 +84,9 @@ def _convert_installed_files_path( from ``info``; if ``info`` is empty, start appending ``..`` instead. 2. Join the two directly. """ - while entry and entry[0] == "..": - if not info or info[-1] == "..": - info += ("..",) + while entry and entry[0] == '..': + if not info or info[-1] == '..': + info += ('..',) else: info = info[:-1] entry = entry[1:] @@ -96,13 +95,13 @@ def _convert_installed_files_path( class BaseDistribution(Protocol): def __repr__(self) -> str: - return f"{self.raw_name} {self.version} ({self.location})" + return f'{self.raw_name} {self.version} ({self.location})' def __str__(self) -> str: - return f"{self.raw_name} {self.version}" + return f'{self.raw_name} {self.version}' @property - def location(self) -> Optional[str]: + def location(self) -> str | None: """Where the distribution is loaded from. A string value is not necessarily a filesystem path, since distributions @@ -116,7 +115,7 @@ class BaseDistribution(Protocol): raise NotImplementedError() @property - def editable_project_location(self) -> Optional[str]: + def editable_project_location(self) -> str | None: """The project location for editable distributions. This is the directory where pyproject.toml or setup.py is located. @@ -138,7 +137,7 @@ class BaseDistribution(Protocol): return None @property - def installed_location(self) -> Optional[str]: + def installed_location(self) -> str | None: """The distribution's "installed" location. This should generally be a ``site-packages`` directory. This is @@ -158,7 +157,7 @@ class BaseDistribution(Protocol): return normalize_path(location) @property - def info_location(self) -> Optional[str]: + def info_location(self) -> str | None: """Location of the .[egg|dist]-info directory or file. Similarly to ``location``, a string value is not necessarily a @@ -196,7 +195,7 @@ class BaseDistribution(Protocol): location = self.location if not location: return False - return location.endswith(".egg") + return location.endswith('.egg') @property def installed_with_setuptools_egg_info(self) -> bool: @@ -212,7 +211,7 @@ class BaseDistribution(Protocol): info_location = self.info_location if not info_location: return False - if not info_location.endswith(".egg-info"): + if not info_location.endswith('.egg-info'): return False return pathlib.Path(info_location).is_dir() @@ -228,7 +227,7 @@ class BaseDistribution(Protocol): info_location = self.info_location if not info_location: return False - if not info_location.endswith(".dist-info"): + if not info_location.endswith('.dist-info'): return False return pathlib.Path(info_location).is_dir() @@ -246,10 +245,10 @@ class BaseDistribution(Protocol): This is a copy of ``pkg_resources.to_filename()`` for compatibility. """ - return self.raw_name.replace("-", "_") + return self.raw_name.replace('-', '_') @property - def direct_url(self) -> Optional[DirectUrl]: + def direct_url(self) -> DirectUrl | None: """Obtain a DirectUrl from this distribution. Returns None if the distribution has no `direct_url.json` metadata, @@ -267,7 +266,7 @@ class BaseDistribution(Protocol): DirectUrlValidationError, ) as e: logger.warning( - "Error parsing %s for %s: %s", + 'Error parsing %s for %s: %s', DIRECT_URL_METADATA_NAME, self.canonical_name, e, @@ -277,14 +276,14 @@ class BaseDistribution(Protocol): @property def installer(self) -> str: try: - installer_text = self.read_text("INSTALLER") + installer_text = self.read_text('INSTALLER') except (OSError, ValueError, NoneMetadataError): - return "" # Fail silently if the installer file cannot be read. + return '' # Fail silently if the installer file cannot be read. for line in installer_text.splitlines(): cleaned_line = line.strip() if cleaned_line: return cleaned_line - return "" + return '' @property def editable(self) -> bool: @@ -350,16 +349,16 @@ class BaseDistribution(Protocol): raise NotImplementedError() @property - def metadata_version(self) -> Optional[str]: + def metadata_version(self) -> str | None: """Value of "Metadata-Version:" in distribution metadata, if available.""" - return self.metadata.get("Metadata-Version") + return self.metadata.get('Metadata-Version') @property def raw_name(self) -> str: """Value of "Name:" in distribution metadata.""" # The metadata should NEVER be missing the Name: key, but if it somehow # does, fall back to the known canonical name. - return self.metadata.get("Name", self.canonical_name) + return self.metadata.get('Name', self.canonical_name) @property def requires_python(self) -> SpecifierSet: @@ -368,14 +367,14 @@ class BaseDistribution(Protocol): If the key does not exist or contains an invalid value, an empty SpecifierSet should be returned. """ - value = self.metadata.get("Requires-Python") + value = self.metadata.get('Requires-Python') if value is None: return SpecifierSet() try: # Convert to str to satisfy the type checker; this can be a Header object. spec = SpecifierSet(str(value)) except InvalidSpecifier as e: - message = "Package %r has an invalid Requires-Python: %s" + message = 'Package %r has an invalid Requires-Python: %s' logger.warning(message, self.raw_name, e) return SpecifierSet() return spec @@ -396,17 +395,17 @@ class BaseDistribution(Protocol): """ raise NotImplementedError() - def _iter_declared_entries_from_record(self) -> Optional[Iterator[str]]: + def _iter_declared_entries_from_record(self) -> Iterator[str] | None: try: - text = self.read_text("RECORD") + text = self.read_text('RECORD') except FileNotFoundError: return None # This extra Path-str cast normalizes entries. return (str(pathlib.Path(row[0])) for row in csv.reader(text.splitlines())) - def _iter_declared_entries_from_legacy(self) -> Optional[Iterator[str]]: + def _iter_declared_entries_from_legacy(self) -> Iterator[str] | None: try: - text = self.read_text("installed-files.txt") + text = self.read_text('installed-files.txt') except FileNotFoundError: return None paths = (p for p in text.splitlines(keepends=False) if p) @@ -425,7 +424,7 @@ class BaseDistribution(Protocol): for p in paths ) - def iter_declared_entries(self) -> Optional[Iterator[str]]: + def iter_declared_entries(self) -> Iterator[str] | None: """Iterate through file entires declared in this distribution. For modern .dist-info distributions, this is the files listed in the @@ -437,8 +436,8 @@ class BaseDistribution(Protocol): contains neither ``RECORD`` nor ``installed-files.txt``. """ return ( - self._iter_declared_entries_from_record() - or self._iter_declared_entries_from_legacy() + self._iter_declared_entries_from_record() or + self._iter_declared_entries_from_legacy() ) @@ -446,14 +445,14 @@ class BaseEnvironment: """An environment containing distributions to introspect.""" @classmethod - def default(cls) -> "BaseEnvironment": + def default(cls) -> BaseEnvironment: raise NotImplementedError() @classmethod - def from_paths(cls, paths: Optional[List[str]]) -> "BaseEnvironment": + def from_paths(cls, paths: list[str] | None) -> BaseEnvironment: raise NotImplementedError() - def get_distribution(self, name: str) -> Optional["BaseDistribution"]: + def get_distribution(self, name: str) -> BaseDistribution | None: """Given a requirement name, return the installed distributions. The name may not be normalized. The implementation must canonicalize @@ -461,7 +460,7 @@ class BaseEnvironment: """ raise NotImplementedError() - def _iter_distributions(self) -> Iterator["BaseDistribution"]: + def _iter_distributions(self) -> Iterator[BaseDistribution]: """Iterate through installed distributions. This function should be implemented by subclass, but never called @@ -470,7 +469,7 @@ class BaseEnvironment: """ raise NotImplementedError() - def iter_distributions(self) -> Iterator["BaseDistribution"]: + def iter_distributions(self) -> Iterator[BaseDistribution]: """Iterate through installed distributions.""" for dist in self._iter_distributions(): # Make sure the distribution actually comes from a valid Python @@ -478,13 +477,13 @@ class BaseEnvironment: # e.g. ``~atplotlib.dist-info`` if cleanup was interrupted. The # valid project name pattern is taken from PEP 508. project_name_valid = re.match( - r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", + r'^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$', dist.canonical_name, flags=re.IGNORECASE, ) if not project_name_valid: logger.warning( - "Ignoring invalid distribution %s (%s)", + 'Ignoring invalid distribution %s (%s)', dist.canonical_name, dist.location, ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/metadata/pkg_resources.py b/.venv/lib/python3.10/site-packages/pip/_internal/metadata/pkg_resources.py index d39f0ba..49e40a3 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/metadata/pkg_resources.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/metadata/pkg_resources.py @@ -1,28 +1,37 @@ +from __future__ import annotations + import email.message import email.parser import logging import os import pathlib import zipfile -from typing import Collection, Iterable, Iterator, List, Mapping, NamedTuple, Optional +from typing import Collection +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Mapping +from typing import NamedTuple +from typing import Optional +from pip._internal.exceptions import InvalidWheel +from pip._internal.exceptions import NoneMetadataError +from pip._internal.exceptions import UnsupportedWheel +from pip._internal.utils.misc import display_path +from pip._internal.utils.wheel import parse_wheel +from pip._internal.utils.wheel import read_wheel_metadata_file from pip._vendor import pkg_resources from pip._vendor.packaging.requirements import Requirement -from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.utils import NormalizedName from pip._vendor.packaging.version import parse as parse_version -from pip._internal.exceptions import InvalidWheel, NoneMetadataError, UnsupportedWheel -from pip._internal.utils.misc import display_path -from pip._internal.utils.wheel import parse_wheel, read_wheel_metadata_file - -from .base import ( - BaseDistribution, - BaseEntryPoint, - BaseEnvironment, - DistributionVersion, - InfoPath, - Wheel, -) +from .base import BaseDistribution +from .base import BaseEntryPoint +from .base import BaseEnvironment +from .base import DistributionVersion +from .base import InfoPath +from .base import Wheel logger = logging.getLogger(__name__) @@ -52,7 +61,7 @@ class WheelMetadata: except UnicodeDecodeError as e: # Augment the default error with the origin of the file. raise UnsupportedWheel( - f"Error decoding metadata for {self._wheel_name}: {e} in {name} file" + f'Error decoding metadata for {self._wheel_name}: {e} in {name} file', ) def get_metadata_lines(self, name: str) -> Iterable[str]: @@ -61,7 +70,7 @@ class WheelMetadata: def metadata_isdir(self, name: str) -> bool: return False - def metadata_listdir(self, name: str) -> List[str]: + def metadata_listdir(self, name: str) -> list[str]: return [] def run_script(self, script_name: str, namespace: str) -> None: @@ -73,7 +82,7 @@ class Distribution(BaseDistribution): self._dist = dist @classmethod - def from_directory(cls, directory: str) -> "Distribution": + def from_directory(cls, directory: str) -> Distribution: dist_dir = directory.rstrip(os.sep) # Build a PathMetadata object, from path to metadata. :wink: @@ -81,19 +90,19 @@ class Distribution(BaseDistribution): metadata = pkg_resources.PathMetadata(base_dir, dist_dir) # Determine the correct Distribution object type. - if dist_dir.endswith(".egg-info"): + if dist_dir.endswith('.egg-info'): dist_cls = pkg_resources.Distribution dist_name = os.path.splitext(dist_dir_name)[0] else: - assert dist_dir.endswith(".dist-info") + assert dist_dir.endswith('.dist-info') dist_cls = pkg_resources.DistInfoDistribution - dist_name = os.path.splitext(dist_dir_name)[0].split("-")[0] + dist_name = os.path.splitext(dist_dir_name)[0].split('-')[0] dist = dist_cls(base_dir, project_name=dist_name, metadata=metadata) return cls(dist) @classmethod - def from_wheel(cls, wheel: Wheel, name: str) -> "Distribution": + def from_wheel(cls, wheel: Wheel, name: str) -> Distribution: """Load the distribution from a given wheel. :raises InvalidWheel: Whenever loading of the wheel causes a @@ -105,14 +114,14 @@ class Distribution(BaseDistribution): with wheel.as_zipfile() as zf: info_dir, _ = parse_wheel(zf, name) metadata_text = { - path.split("/", 1)[-1]: read_wheel_metadata_file(zf, path) + path.split('/', 1)[-1]: read_wheel_metadata_file(zf, path) for path in zf.namelist() - if path.startswith(f"{info_dir}/") + if path.startswith(f'{info_dir}/') } except zipfile.BadZipFile as e: raise InvalidWheel(wheel.location, name) from e except UnsupportedWheel as e: - raise UnsupportedWheel(f"{name} has an invalid wheel, {e}") + raise UnsupportedWheel(f'{name} has an invalid wheel, {e}') dist = pkg_resources.DistInfoDistribution( location=wheel.location, metadata=WheelMetadata(metadata_text, wheel.location), @@ -121,11 +130,11 @@ class Distribution(BaseDistribution): return cls(dist) @property - def location(self) -> Optional[str]: + def location(self) -> str | None: return self._dist.location @property - def info_location(self) -> Optional[str]: + def info_location(self) -> str | None: return self._dist.egg_info @property @@ -170,7 +179,7 @@ class Distribution(BaseDistribution): def iter_entry_points(self) -> Iterable[BaseEntryPoint]: for group, entries in self._dist.get_entry_map().items(): for name, entry_point in entries.items(): - name, _, value = str(entry_point).partition("=") + name, _, value = str(entry_point).partition('=') yield EntryPoint(name=name.strip(), value=value.strip(), group=group) @property @@ -180,9 +189,9 @@ class Distribution(BaseDistribution): True but `get_metadata()` returns None. """ if isinstance(self._dist, pkg_resources.DistInfoDistribution): - metadata_name = "METADATA" + metadata_name = 'METADATA' else: - metadata_name = "PKG-INFO" + metadata_name = 'PKG-INFO' try: metadata = self.read_text(metadata_name) except FileNotFoundError: @@ -190,8 +199,8 @@ class Distribution(BaseDistribution): displaying_path = display_path(self.location) else: displaying_path = repr(self.location) - logger.warning("No metadata found in %s", displaying_path) - metadata = "" + logger.warning('No metadata found in %s', displaying_path) + metadata = '' feed_parser = email.parser.FeedParser() feed_parser.feed(metadata) return feed_parser.close() @@ -214,10 +223,10 @@ class Environment(BaseEnvironment): return cls(pkg_resources.working_set) @classmethod - def from_paths(cls, paths: Optional[List[str]]) -> BaseEnvironment: + def from_paths(cls, paths: list[str] | None) -> BaseEnvironment: return cls(pkg_resources.WorkingSet(paths)) - def _search_distribution(self, name: str) -> Optional[BaseDistribution]: + def _search_distribution(self, name: str) -> BaseDistribution | None: """Find a distribution matching the ``name`` in the environment. This searches from *all* distributions available in the environment, to @@ -229,7 +238,7 @@ class Environment(BaseEnvironment): return dist return None - def get_distribution(self, name: str) -> Optional[BaseDistribution]: + def get_distribution(self, name: str) -> BaseDistribution | None: # Search the distribution by looking through the working set. dist = self._search_distribution(name) if dist: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/__init__.py index 7855226..0411c3c 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/__init__.py @@ -1,2 +1,3 @@ """A package that contains models that represent entities. """ +from __future__ import annotations diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/candidate.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/candidate.py index a4963ae..28e6d71 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/candidate.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/candidate.py @@ -1,13 +1,14 @@ -from pip._vendor.packaging.version import parse as parse_version +from __future__ import annotations from pip._internal.models.link import Link from pip._internal.utils.models import KeyBasedCompareMixin +from pip._vendor.packaging.version import parse as parse_version class InstallationCandidate(KeyBasedCompareMixin): """Represents a potential "candidate" for installation.""" - __slots__ = ["name", "version", "link"] + __slots__ = ['name', 'version', 'link'] def __init__(self, name: str, version: str, link: Link) -> None: self.name = name @@ -20,14 +21,14 @@ class InstallationCandidate(KeyBasedCompareMixin): ) def __repr__(self) -> str: - return "".format( + return ''.format( self.name, self.version, self.link, ) def __str__(self) -> str: - return "{!r} candidate (version {} at {})".format( + return '{!r} candidate (version {} at {})'.format( self.name, self.version, self.link, diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/direct_url.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/direct_url.py index 92060d4..907742a 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/direct_url.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/direct_url.py @@ -1,21 +1,29 @@ """ PEP 610 """ +from __future__ import annotations + import json import re import urllib.parse -from typing import Any, Dict, Iterable, Optional, Type, TypeVar, Union +from typing import Any +from typing import Dict +from typing import Iterable +from typing import Optional +from typing import Type +from typing import TypeVar +from typing import Union __all__ = [ - "DirectUrl", - "DirectUrlValidationError", - "DirInfo", - "ArchiveInfo", - "VcsInfo", + 'DirectUrl', + 'DirectUrlValidationError', + 'DirInfo', + 'ArchiveInfo', + 'VcsInfo', ] -T = TypeVar("T") +T = TypeVar('T') -DIRECT_URL_METADATA_NAME = "direct_url.json" -ENV_VAR_RE = re.compile(r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$") +DIRECT_URL_METADATA_NAME = 'direct_url.json' +ENV_VAR_RE = re.compile(r'^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$') class DirectUrlValidationError(Exception): @@ -23,59 +31,59 @@ class DirectUrlValidationError(Exception): def _get( - d: Dict[str, Any], expected_type: Type[T], key: str, default: Optional[T] = None -) -> Optional[T]: + d: dict[str, Any], expected_type: type[T], key: str, default: T | None = None, +) -> T | None: """Get value from dictionary and verify expected type.""" if key not in d: return default value = d[key] if not isinstance(value, expected_type): raise DirectUrlValidationError( - "{!r} has unexpected type for {} (expected {})".format( - value, key, expected_type - ) + '{!r} has unexpected type for {} (expected {})'.format( + value, key, expected_type, + ), ) return value def _get_required( - d: Dict[str, Any], expected_type: Type[T], key: str, default: Optional[T] = None + d: dict[str, Any], expected_type: type[T], key: str, default: T | None = None, ) -> T: value = _get(d, expected_type, key, default) if value is None: - raise DirectUrlValidationError(f"{key} must have a value") + raise DirectUrlValidationError(f'{key} must have a value') return value -def _exactly_one_of(infos: Iterable[Optional["InfoType"]]) -> "InfoType": +def _exactly_one_of(infos: Iterable[InfoType | None]) -> InfoType: infos = [info for info in infos if info is not None] if not infos: raise DirectUrlValidationError( - "missing one of archive_info, dir_info, vcs_info" + 'missing one of archive_info, dir_info, vcs_info', ) if len(infos) > 1: raise DirectUrlValidationError( - "more than one of archive_info, dir_info, vcs_info" + 'more than one of archive_info, dir_info, vcs_info', ) assert infos[0] is not None return infos[0] -def _filter_none(**kwargs: Any) -> Dict[str, Any]: +def _filter_none(**kwargs: Any) -> dict[str, Any]: """Make dict excluding None values.""" return {k: v for k, v in kwargs.items() if v is not None} class VcsInfo: - name = "vcs_info" + name = 'vcs_info' def __init__( self, vcs: str, commit_id: str, - requested_revision: Optional[str] = None, - resolved_revision: Optional[str] = None, - resolved_revision_type: Optional[str] = None, + requested_revision: str | None = None, + resolved_revision: str | None = None, + resolved_revision_type: str | None = None, ) -> None: self.vcs = vcs self.requested_revision = requested_revision @@ -84,18 +92,18 @@ class VcsInfo: self.resolved_revision_type = resolved_revision_type @classmethod - def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["VcsInfo"]: + def _from_dict(cls, d: dict[str, Any] | None) -> VcsInfo | None: if d is None: return None return cls( - vcs=_get_required(d, str, "vcs"), - commit_id=_get_required(d, str, "commit_id"), - requested_revision=_get(d, str, "requested_revision"), - resolved_revision=_get(d, str, "resolved_revision"), - resolved_revision_type=_get(d, str, "resolved_revision_type"), + vcs=_get_required(d, str, 'vcs'), + commit_id=_get_required(d, str, 'commit_id'), + requested_revision=_get(d, str, 'requested_revision'), + resolved_revision=_get(d, str, 'resolved_revision'), + resolved_revision_type=_get(d, str, 'resolved_revision_type'), ) - def _to_dict(self) -> Dict[str, Any]: + def _to_dict(self) -> dict[str, Any]: return _filter_none( vcs=self.vcs, requested_revision=self.requested_revision, @@ -106,26 +114,26 @@ class VcsInfo: class ArchiveInfo: - name = "archive_info" + name = 'archive_info' def __init__( self, - hash: Optional[str] = None, + hash: str | None = None, ) -> None: self.hash = hash @classmethod - def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["ArchiveInfo"]: + def _from_dict(cls, d: dict[str, Any] | None) -> ArchiveInfo | None: if d is None: return None - return cls(hash=_get(d, str, "hash")) + return cls(hash=_get(d, str, 'hash')) - def _to_dict(self) -> Dict[str, Any]: + def _to_dict(self) -> dict[str, Any]: return _filter_none(hash=self.hash) class DirInfo: - name = "dir_info" + name = 'dir_info' def __init__( self, @@ -134,12 +142,12 @@ class DirInfo: self.editable = editable @classmethod - def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["DirInfo"]: + def _from_dict(cls, d: dict[str, Any] | None) -> DirInfo | None: if d is None: return None - return cls(editable=_get_required(d, bool, "editable", default=False)) + return cls(editable=_get_required(d, bool, 'editable', default=False)) - def _to_dict(self) -> Dict[str, Any]: + def _to_dict(self) -> dict[str, Any]: return _filter_none(editable=self.editable or None) @@ -151,20 +159,20 @@ class DirectUrl: self, url: str, info: InfoType, - subdirectory: Optional[str] = None, + subdirectory: str | None = None, ) -> None: self.url = url self.info = info self.subdirectory = subdirectory def _remove_auth_from_netloc(self, netloc: str) -> str: - if "@" not in netloc: + if '@' not in netloc: return netloc - user_pass, netloc_no_user_pass = netloc.split("@", 1) + user_pass, netloc_no_user_pass = netloc.split('@', 1) if ( - isinstance(self.info, VcsInfo) - and self.info.vcs == "git" - and user_pass == "git" + isinstance(self.info, VcsInfo) and + self.info.vcs == 'git' and + user_pass == 'git' ): return netloc if ENV_VAR_RE.match(user_pass): @@ -180,7 +188,7 @@ class DirectUrl: purl = urllib.parse.urlsplit(self.url) netloc = self._remove_auth_from_netloc(purl.netloc) surl = urllib.parse.urlunsplit( - (purl.scheme, netloc, purl.path, purl.query, purl.fragment) + (purl.scheme, netloc, purl.path, purl.query, purl.fragment), ) return surl @@ -188,20 +196,20 @@ class DirectUrl: self.from_dict(self.to_dict()) @classmethod - def from_dict(cls, d: Dict[str, Any]) -> "DirectUrl": + def from_dict(cls, d: dict[str, Any]) -> DirectUrl: return DirectUrl( - url=_get_required(d, str, "url"), - subdirectory=_get(d, str, "subdirectory"), + url=_get_required(d, str, 'url'), + subdirectory=_get(d, str, 'subdirectory'), info=_exactly_one_of( [ - ArchiveInfo._from_dict(_get(d, dict, "archive_info")), - DirInfo._from_dict(_get(d, dict, "dir_info")), - VcsInfo._from_dict(_get(d, dict, "vcs_info")), - ] + ArchiveInfo._from_dict(_get(d, dict, 'archive_info')), + DirInfo._from_dict(_get(d, dict, 'dir_info')), + VcsInfo._from_dict(_get(d, dict, 'vcs_info')), + ], ), ) - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: res = _filter_none( url=self.redacted_url, subdirectory=self.subdirectory, @@ -210,7 +218,7 @@ class DirectUrl: return res @classmethod - def from_json(cls, s: str) -> "DirectUrl": + def from_json(cls, s: str) -> DirectUrl: return cls.from_dict(json.loads(s)) def to_json(self) -> str: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/format_control.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/format_control.py index db3995e..cbc4794 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/format_control.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/format_control.py @@ -1,19 +1,22 @@ -from typing import FrozenSet, Optional, Set +from __future__ import annotations -from pip._vendor.packaging.utils import canonicalize_name +from typing import FrozenSet +from typing import Optional +from typing import Set from pip._internal.exceptions import CommandError +from pip._vendor.packaging.utils import canonicalize_name class FormatControl: """Helper for managing formats from which a package can be installed.""" - __slots__ = ["no_binary", "only_binary"] + __slots__ = ['no_binary', 'only_binary'] def __init__( self, - no_binary: Optional[Set[str]] = None, - only_binary: Optional[Set[str]] = None, + no_binary: set[str] | None = None, + only_binary: set[str] | None = None, ) -> None: if no_binary is None: no_binary = set() @@ -33,48 +36,48 @@ class FormatControl: return all(getattr(self, k) == getattr(other, k) for k in self.__slots__) def __repr__(self) -> str: - return "{}({}, {})".format( - self.__class__.__name__, self.no_binary, self.only_binary + return '{}({}, {})'.format( + self.__class__.__name__, self.no_binary, self.only_binary, ) @staticmethod - def handle_mutual_excludes(value: str, target: Set[str], other: Set[str]) -> None: - if value.startswith("-"): + def handle_mutual_excludes(value: str, target: set[str], other: set[str]) -> None: + if value.startswith('-'): raise CommandError( - "--no-binary / --only-binary option requires 1 argument." + '--no-binary / --only-binary option requires 1 argument.', ) - new = value.split(",") - while ":all:" in new: + new = value.split(',') + while ':all:' in new: other.clear() target.clear() - target.add(":all:") - del new[: new.index(":all:") + 1] + target.add(':all:') + del new[: new.index(':all:') + 1] # Without a none, we want to discard everything as :all: covers it - if ":none:" not in new: + if ':none:' not in new: return for name in new: - if name == ":none:": + if name == ':none:': target.clear() continue name = canonicalize_name(name) other.discard(name) target.add(name) - def get_allowed_formats(self, canonical_name: str) -> FrozenSet[str]: - result = {"binary", "source"} + def get_allowed_formats(self, canonical_name: str) -> frozenset[str]: + result = {'binary', 'source'} if canonical_name in self.only_binary: - result.discard("source") + result.discard('source') elif canonical_name in self.no_binary: - result.discard("binary") - elif ":all:" in self.only_binary: - result.discard("source") - elif ":all:" in self.no_binary: - result.discard("binary") + result.discard('binary') + elif ':all:' in self.only_binary: + result.discard('source') + elif ':all:' in self.no_binary: + result.discard('binary') return frozenset(result) def disallow_binaries(self) -> None: self.handle_mutual_excludes( - ":all:", + ':all:', self.no_binary, self.only_binary, ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/index.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/index.py index b94c325..577800f 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/index.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/index.py @@ -1,17 +1,19 @@ +from __future__ import annotations + import urllib.parse class PackageIndex: """Represents a Package Index and provides easier access to endpoints""" - __slots__ = ["url", "netloc", "simple_url", "pypi_url", "file_storage_domain"] + __slots__ = ['url', 'netloc', 'simple_url', 'pypi_url', 'file_storage_domain'] def __init__(self, url: str, file_storage_domain: str) -> None: super().__init__() self.url = url self.netloc = urllib.parse.urlsplit(url).netloc - self.simple_url = self._url_for_path("simple") - self.pypi_url = self._url_for_path("pypi") + self.simple_url = self._url_for_path('simple') + self.pypi_url = self._url_for_path('pypi') # This is part of a temporary hack used to block installs of PyPI # packages which depend on external urls only necessary until PyPI can @@ -22,7 +24,7 @@ class PackageIndex: return urllib.parse.urljoin(self.url, path) -PyPI = PackageIndex("https://pypi.org/", file_storage_domain="files.pythonhosted.org") +PyPI = PackageIndex('https://pypi.org/', file_storage_domain='files.pythonhosted.org') TestPyPI = PackageIndex( - "https://test.pypi.org/", file_storage_domain="test-files.pythonhosted.org" + 'https://test.pypi.org/', file_storage_domain='test-files.pythonhosted.org', ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/link.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/link.py index 6069b27..54c13a0 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/link.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/link.py @@ -1,20 +1,27 @@ +from __future__ import annotations + import functools import logging import os import posixpath import re import urllib.parse -from typing import TYPE_CHECKING, Dict, List, NamedTuple, Optional, Tuple, Union +from typing import Dict +from typing import List +from typing import NamedTuple +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union from pip._internal.utils.filetypes import WHEEL_EXTENSION from pip._internal.utils.hashes import Hashes -from pip._internal.utils.misc import ( - redact_auth_from_url, - split_auth_from_netloc, - splitext, -) +from pip._internal.utils.misc import redact_auth_from_url +from pip._internal.utils.misc import split_auth_from_netloc +from pip._internal.utils.misc import splitext from pip._internal.utils.models import KeyBasedCompareMixin -from pip._internal.utils.urls import path_to_url, url_to_path +from pip._internal.utils.urls import path_to_url +from pip._internal.utils.urls import url_to_path if TYPE_CHECKING: from pip._internal.index.collector import HTMLPage @@ -22,27 +29,27 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) -_SUPPORTED_HASHES = ("sha1", "sha224", "sha384", "sha256", "sha512", "md5") +_SUPPORTED_HASHES = ('sha1', 'sha224', 'sha384', 'sha256', 'sha512', 'md5') class Link(KeyBasedCompareMixin): """Represents a parsed link from a Package Index's simple URL""" __slots__ = [ - "_parsed_url", - "_url", - "comes_from", - "requires_python", - "yanked_reason", - "cache_link_parsing", + '_parsed_url', + '_url', + 'comes_from', + 'requires_python', + 'yanked_reason', + 'cache_link_parsing', ] def __init__( self, url: str, - comes_from: Optional[Union[str, "HTMLPage"]] = None, - requires_python: Optional[str] = None, - yanked_reason: Optional[str] = None, + comes_from: str | HTMLPage | None = None, + requires_python: str | None = None, + yanked_reason: str | None = None, cache_link_parsing: bool = True, ) -> None: """ @@ -67,7 +74,7 @@ class Link(KeyBasedCompareMixin): """ # url can be a UNC windows share - if url.startswith("\\\\"): + if url.startswith('\\\\'): url = path_to_url(url) self._parsed_url = urllib.parse.urlsplit(url) @@ -85,18 +92,18 @@ class Link(KeyBasedCompareMixin): def __str__(self) -> str: if self.requires_python: - rp = f" (requires-python:{self.requires_python})" + rp = f' (requires-python:{self.requires_python})' else: - rp = "" + rp = '' if self.comes_from: - return "{} (from {}){}".format( - redact_auth_from_url(self._url), self.comes_from, rp + return '{} (from {}){}'.format( + redact_auth_from_url(self._url), self.comes_from, rp, ) else: return redact_auth_from_url(str(self._url)) def __repr__(self) -> str: - return f"" + return f'' @property def url(self) -> str: @@ -104,7 +111,7 @@ class Link(KeyBasedCompareMixin): @property def filename(self) -> str: - path = self.path.rstrip("/") + path = self.path.rstrip('/') name = posixpath.basename(path) if not name: # Make sure we don't leak auth information if the netloc @@ -113,7 +120,7 @@ class Link(KeyBasedCompareMixin): return netloc name = urllib.parse.unquote(name) - assert name, f"URL {self._url!r} produced no filename" + assert name, f'URL {self._url!r} produced no filename' return name @property @@ -135,8 +142,8 @@ class Link(KeyBasedCompareMixin): def path(self) -> str: return urllib.parse.unquote(self._parsed_url.path) - def splitext(self) -> Tuple[str, str]: - return splitext(posixpath.basename(self.path.rstrip("/"))) + def splitext(self) -> tuple[str, str]: + return splitext(posixpath.basename(self.path.rstrip('/'))) @property def ext(self) -> str: @@ -145,39 +152,39 @@ class Link(KeyBasedCompareMixin): @property def url_without_fragment(self) -> str: scheme, netloc, path, query, fragment = self._parsed_url - return urllib.parse.urlunsplit((scheme, netloc, path, query, "")) + return urllib.parse.urlunsplit((scheme, netloc, path, query, '')) - _egg_fragment_re = re.compile(r"[#&]egg=([^&]*)") + _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)') @property - def egg_fragment(self) -> Optional[str]: + def egg_fragment(self) -> str | None: match = self._egg_fragment_re.search(self._url) if not match: return None return match.group(1) - _subdirectory_fragment_re = re.compile(r"[#&]subdirectory=([^&]*)") + _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)') @property - def subdirectory_fragment(self) -> Optional[str]: + def subdirectory_fragment(self) -> str | None: match = self._subdirectory_fragment_re.search(self._url) if not match: return None return match.group(1) _hash_re = re.compile( - r"({choices})=([a-f0-9]+)".format(choices="|".join(_SUPPORTED_HASHES)) + r'({choices})=([a-f0-9]+)'.format(choices='|'.join(_SUPPORTED_HASHES)), ) @property - def hash(self) -> Optional[str]: + def hash(self) -> str | None: match = self._hash_re.search(self._url) if match: return match.group(2) return None @property - def hash_name(self) -> Optional[str]: + def hash_name(self) -> str | None: match = self._hash_re.search(self._url) if match: return match.group(1) @@ -185,11 +192,11 @@ class Link(KeyBasedCompareMixin): @property def show_url(self) -> str: - return posixpath.basename(self._url.split("#", 1)[0].split("?", 1)[0]) + return posixpath.basename(self._url.split('#', 1)[0].split('?', 1)[0]) @property def is_file(self) -> bool: - return self.scheme == "file" + return self.scheme == 'file' def is_existing_dir(self) -> bool: return self.is_file and os.path.isdir(self.file_path) @@ -212,7 +219,7 @@ class Link(KeyBasedCompareMixin): def has_hash(self) -> bool: return self.hash_name is not None - def is_hash_allowed(self, hashes: Optional[Hashes]) -> bool: + def is_hash_allowed(self, hashes: Hashes | None) -> bool: """ Return True if the link has a hash and it is allowed. """ @@ -252,31 +259,31 @@ class _CleanResult(NamedTuple): """ parsed: urllib.parse.SplitResult - query: Dict[str, List[str]] + query: dict[str, list[str]] subdirectory: str - hashes: Dict[str, str] + hashes: dict[str, str] def _clean_link(link: Link) -> _CleanResult: parsed = link._parsed_url - netloc = parsed.netloc.rsplit("@", 1)[-1] + netloc = parsed.netloc.rsplit('@', 1)[-1] # According to RFC 8089, an empty host in file: means localhost. - if parsed.scheme == "file" and not netloc: - netloc = "localhost" + if parsed.scheme == 'file' and not netloc: + netloc = 'localhost' fragment = urllib.parse.parse_qs(parsed.fragment) - if "egg" in fragment: - logger.debug("Ignoring egg= fragment in %s", link) + if 'egg' in fragment: + logger.debug('Ignoring egg= fragment in %s', link) try: # If there are multiple subdirectory values, use the first one. # This matches the behavior of Link.subdirectory_fragment. - subdirectory = fragment["subdirectory"][0] + subdirectory = fragment['subdirectory'][0] except (IndexError, KeyError): - subdirectory = "" + subdirectory = '' # If there are multiple hash values under the same algorithm, use the # first one. This matches the behavior of Link.hash_value. hashes = {k: fragment[k][0] for k in _SUPPORTED_HASHES if k in fragment} return _CleanResult( - parsed=parsed._replace(netloc=netloc, query="", fragment=""), + parsed=parsed._replace(netloc=netloc, query='', fragment=''), query=urllib.parse.parse_qs(parsed.query), subdirectory=subdirectory, hashes=hashes, diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/scheme.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/scheme.py index f51190a..da78d4d 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/scheme.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/scheme.py @@ -4,9 +4,10 @@ For types associated with installation schemes. For a general overview of available schemes and their context, see https://docs.python.org/3/install/index.html#alternate-installation. """ +from __future__ import annotations -SCHEME_KEYS = ["platlib", "purelib", "headers", "scripts", "data"] +SCHEME_KEYS = ['platlib', 'purelib', 'headers', 'scripts', 'data'] class Scheme: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/search_scope.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/search_scope.py index e4e54c2..3dab48a 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/search_scope.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/search_scope.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import itertools import logging import os @@ -5,11 +7,11 @@ import posixpath import urllib.parse from typing import List -from pip._vendor.packaging.utils import canonicalize_name - from pip._internal.models.index import PyPI from pip._internal.utils.compat import has_tls -from pip._internal.utils.misc import normalize_path, redact_auth_from_url +from pip._internal.utils.misc import normalize_path +from pip._internal.utils.misc import redact_auth_from_url +from pip._vendor.packaging.utils import canonicalize_name logger = logging.getLogger(__name__) @@ -20,14 +22,14 @@ class SearchScope: Encapsulates the locations that pip is configured to search. """ - __slots__ = ["find_links", "index_urls"] + __slots__ = ['find_links', 'index_urls'] @classmethod def create( cls, - find_links: List[str], - index_urls: List[str], - ) -> "SearchScope": + find_links: list[str], + index_urls: list[str], + ) -> SearchScope: """ Create a SearchScope object after normalizing the `find_links`. """ @@ -36,9 +38,9 @@ class SearchScope: # it and if it exists, use the normalized version. # This is deliberately conservative - it might be fine just to # blindly normalize anything starting with a ~... - built_find_links: List[str] = [] + built_find_links: list[str] = [] for link in find_links: - if link.startswith("~"): + if link.startswith('~'): new_link = normalize_path(link) if os.path.exists(new_link): link = new_link @@ -49,11 +51,11 @@ class SearchScope: if not has_tls(): for link in itertools.chain(index_urls, built_find_links): parsed = urllib.parse.urlparse(link) - if parsed.scheme == "https": + if parsed.scheme == 'https': logger.warning( - "pip is configured with locations that require " - "TLS/SSL, however the ssl module in Python is not " - "available." + 'pip is configured with locations that require ' + 'TLS/SSL, however the ssl module in Python is not ' + 'available.', ) break @@ -64,8 +66,8 @@ class SearchScope: def __init__( self, - find_links: List[str], - index_urls: List[str], + find_links: list[str], + index_urls: list[str], ) -> None: self.find_links = find_links self.index_urls = index_urls @@ -95,18 +97,18 @@ class SearchScope: redacted_index_urls.append(redacted_index_url) lines.append( - "Looking in indexes: {}".format(", ".join(redacted_index_urls)) + 'Looking in indexes: {}'.format(', '.join(redacted_index_urls)), ) if self.find_links: lines.append( - "Looking in links: {}".format( - ", ".join(redact_auth_from_url(url) for url in self.find_links) - ) + 'Looking in links: {}'.format( + ', '.join(redact_auth_from_url(url) for url in self.find_links), + ), ) - return "\n".join(lines) + return '\n'.join(lines) - def get_index_urls_locations(self, project_name: str) -> List[str]: + def get_index_urls_locations(self, project_name: str) -> list[str]: """Returns the locations found via self.index_urls Checks the url_name on the main (first in the list) index and @@ -115,15 +117,15 @@ class SearchScope: def mkurl_pypi_url(url: str) -> str: loc = posixpath.join( - url, urllib.parse.quote(canonicalize_name(project_name)) + url, urllib.parse.quote(canonicalize_name(project_name)), ) # For maximum compatibility with easy_install, ensure the path # ends in a trailing slash. Although this isn't in the spec # (and PyPI can handle it without the slash) some other index # implementations might break if they relied on easy_install's # behavior. - if not loc.endswith("/"): - loc = loc + "/" + if not loc.endswith('/'): + loc = loc + '/' return loc return [mkurl_pypi_url(url) for url in self.index_urls] diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/selection_prefs.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/selection_prefs.py index 977bc4c..7900d75 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/selection_prefs.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/selection_prefs.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import Optional from pip._internal.models.format_control import FormatControl @@ -10,11 +12,11 @@ class SelectionPreferences: """ __slots__ = [ - "allow_yanked", - "allow_all_prereleases", - "format_control", - "prefer_binary", - "ignore_requires_python", + 'allow_yanked', + 'allow_all_prereleases', + 'format_control', + 'prefer_binary', + 'ignore_requires_python', ] # Don't include an allow_yanked default value to make sure each call @@ -25,9 +27,9 @@ class SelectionPreferences: self, allow_yanked: bool, allow_all_prereleases: bool = False, - format_control: Optional[FormatControl] = None, + format_control: FormatControl | None = None, prefer_binary: bool = False, - ignore_requires_python: Optional[bool] = None, + ignore_requires_python: bool | None = None, ) -> None: """Create a SelectionPreferences object. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/target_python.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/target_python.py index 744bd7e..c2b56ea 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/target_python.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/target_python.py @@ -1,10 +1,14 @@ +from __future__ import annotations + import sys -from typing import List, Optional, Tuple +from typing import List +from typing import Optional +from typing import Tuple -from pip._vendor.packaging.tags import Tag - -from pip._internal.utils.compatibility_tags import get_supported, version_info_to_nodot +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.compatibility_tags import version_info_to_nodot from pip._internal.utils.misc import normalize_version_info +from pip._vendor.packaging.tags import Tag class TargetPython: @@ -15,21 +19,21 @@ class TargetPython: """ __slots__ = [ - "_given_py_version_info", - "abis", - "implementation", - "platforms", - "py_version", - "py_version_info", - "_valid_tags", + '_given_py_version_info', + 'abis', + 'implementation', + 'platforms', + 'py_version', + 'py_version_info', + '_valid_tags', ] def __init__( self, - platforms: Optional[List[str]] = None, - py_version_info: Optional[Tuple[int, ...]] = None, - abis: Optional[List[str]] = None, - implementation: Optional[str] = None, + platforms: list[str] | None = None, + py_version_info: tuple[int, ...] | None = None, + abis: list[str] | None = None, + implementation: str | None = None, ) -> None: """ :param platforms: A list of strings or None. If None, searches for @@ -53,7 +57,7 @@ class TargetPython: else: py_version_info = normalize_version_info(py_version_info) - py_version = ".".join(map(str, py_version_info[:2])) + py_version = '.'.join(map(str, py_version_info[:2])) self.abis = abis self.implementation = implementation @@ -62,7 +66,7 @@ class TargetPython: self.py_version_info = py_version_info # This is used to cache the return value of get_tags(). - self._valid_tags: Optional[List[Tag]] = None + self._valid_tags: list[Tag] | None = None def format_given(self) -> str: """ @@ -70,21 +74,21 @@ class TargetPython: """ display_version = None if self._given_py_version_info is not None: - display_version = ".".join( + display_version = '.'.join( str(part) for part in self._given_py_version_info ) key_values = [ - ("platforms", self.platforms), - ("version_info", display_version), - ("abis", self.abis), - ("implementation", self.implementation), + ('platforms', self.platforms), + ('version_info', display_version), + ('abis', self.abis), + ('implementation', self.implementation), ] - return " ".join( - f"{key}={value!r}" for key, value in key_values if value is not None + return ' '.join( + f'{key}={value!r}' for key, value in key_values if value is not None ) - def get_tags(self) -> List[Tag]: + def get_tags(self) -> list[Tag]: """ Return the supported PEP 425 tags to check wheel candidates against. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/models/wheel.py b/.venv/lib/python3.10/site-packages/pip/_internal/models/wheel.py index e091612..fd91054 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/models/wheel.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/models/wheel.py @@ -1,12 +1,15 @@ """Represents a wheel file and provides access to the various parts of the name that have meaning. """ -import re -from typing import Dict, Iterable, List +from __future__ import annotations -from pip._vendor.packaging.tags import Tag +import re +from typing import Dict +from typing import Iterable +from typing import List from pip._internal.exceptions import InvalidWheelFilename +from pip._vendor.packaging.tags import Tag class Wheel: @@ -25,27 +28,27 @@ class Wheel: """ wheel_info = self.wheel_file_re.match(filename) if not wheel_info: - raise InvalidWheelFilename(f"{filename} is not a valid wheel filename.") + raise InvalidWheelFilename(f'{filename} is not a valid wheel filename.') self.filename = filename - self.name = wheel_info.group("name").replace("_", "-") + self.name = wheel_info.group('name').replace('_', '-') # we'll assume "_" means "-" due to wheel naming scheme # (https://github.com/pypa/pip/issues/1150) - self.version = wheel_info.group("ver").replace("_", "-") - self.build_tag = wheel_info.group("build") - self.pyversions = wheel_info.group("pyver").split(".") - self.abis = wheel_info.group("abi").split(".") - self.plats = wheel_info.group("plat").split(".") + self.version = wheel_info.group('ver').replace('_', '-') + self.build_tag = wheel_info.group('build') + self.pyversions = wheel_info.group('pyver').split('.') + self.abis = wheel_info.group('abi').split('.') + self.plats = wheel_info.group('plat').split('.') # All the tag combinations from this file self.file_tags = { Tag(x, y, z) for x in self.pyversions for y in self.abis for z in self.plats } - def get_formatted_file_tags(self) -> List[str]: + def get_formatted_file_tags(self) -> list[str]: """Return the wheel's tags as a sorted list of strings.""" return sorted(str(tag) for tag in self.file_tags) - def support_index_min(self, tags: List[Tag]) -> int: + def support_index_min(self, tags: list[Tag]) -> int: """Return the lowest index that one of the wheel's file_tag combinations achieves in the given list of supported tags. @@ -61,7 +64,7 @@ class Wheel: return min(tags.index(tag) for tag in self.file_tags if tag in tags) def find_most_preferred_tag( - self, tags: List[Tag], tag_to_priority: Dict[Tag, int] + self, tags: list[Tag], tag_to_priority: dict[Tag, int], ) -> int: """Return the priority of the most preferred tag that one of the wheel's file tag combinations achieves in the given list of supported tags using the given diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/network/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/network/__init__.py index b51bde9..c99c1ad 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/network/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/network/__init__.py @@ -1,2 +1,3 @@ """Contains purely network-related utilities. """ +from __future__ import annotations diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/network/auth.py b/.venv/lib/python3.10/site-packages/pip/_internal/network/auth.py index ca42798..06d31d1 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/network/auth.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/network/auth.py @@ -3,23 +3,27 @@ Contains interface (MultiDomainBasicAuth) and associated glue code for providing credentials in the context of network requests. """ +from __future__ import annotations import urllib.parse -from typing import Any, Dict, List, Optional, Tuple - -from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth -from pip._vendor.requests.models import Request, Response -from pip._vendor.requests.utils import get_netrc_auth +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple from pip._internal.utils.logging import getLogger -from pip._internal.utils.misc import ( - ask, - ask_input, - ask_password, - remove_auth_from_url, - split_auth_netloc_from_url, -) +from pip._internal.utils.misc import ask +from pip._internal.utils.misc import ask_input +from pip._internal.utils.misc import ask_password +from pip._internal.utils.misc import remove_auth_from_url +from pip._internal.utils.misc import split_auth_netloc_from_url from pip._internal.vcs.versioncontrol import AuthInfo +from pip._vendor.requests.auth import AuthBase +from pip._vendor.requests.auth import HTTPBasicAuth +from pip._vendor.requests.models import Request +from pip._vendor.requests.models import Response +from pip._vendor.requests.utils import get_netrc_auth logger = getLogger(__name__) @@ -31,13 +35,13 @@ except ImportError: keyring = None # type: ignore[assignment] except Exception as exc: logger.warning( - "Keyring is skipped due to an exception: %s", + 'Keyring is skipped due to an exception: %s', str(exc), ) keyring = None # type: ignore[assignment] -def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[AuthInfo]: +def get_keyring_auth(url: str | None, username: str | None) -> AuthInfo | None: """Return the tuple auth for a given url from keyring.""" global keyring if not url or not keyring: @@ -49,21 +53,21 @@ def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[Au except AttributeError: pass else: - logger.debug("Getting credentials from keyring for %s", url) + logger.debug('Getting credentials from keyring for %s', url) cred = get_credential(url, username) if cred is not None: return cred.username, cred.password return None if username: - logger.debug("Getting password from keyring for %s", url) + logger.debug('Getting password from keyring for %s', url) password = keyring.get_password(url, username) if password: return username, password except Exception as exc: logger.warning( - "Keyring is skipped due to an exception: %s", + 'Keyring is skipped due to an exception: %s', str(exc), ) keyring = None # type: ignore[assignment] @@ -72,19 +76,19 @@ def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[Au class MultiDomainBasicAuth(AuthBase): def __init__( - self, prompting: bool = True, index_urls: Optional[List[str]] = None + self, prompting: bool = True, index_urls: list[str] | None = None, ) -> None: self.prompting = prompting self.index_urls = index_urls - self.passwords: Dict[str, AuthInfo] = {} + self.passwords: dict[str, AuthInfo] = {} # When the user is prompted to enter credentials and keyring is # available, we will offer to save them. If the user accepts, # this value is set to the credentials they entered. After the # request authenticates, the caller should call # ``save_credentials`` to save these. - self._credentials_to_save: Optional[Credentials] = None + self._credentials_to_save: Credentials | None = None - def _get_index_url(self, url: str) -> Optional[str]: + def _get_index_url(self, url: str) -> str | None: """Return the original index URL matching the requested URL. Cached or dynamically generated credentials may work against @@ -101,7 +105,7 @@ class MultiDomainBasicAuth(AuthBase): return None for u in self.index_urls: - prefix = remove_auth_from_url(u).rstrip("/") + "/" + prefix = remove_auth_from_url(u).rstrip('/') + '/' if url.startswith(prefix): return u return None @@ -121,7 +125,7 @@ class MultiDomainBasicAuth(AuthBase): # Start with the credentials embedded in the url username, password = url_user_password if username is not None and password is not None: - logger.debug("Found credentials in url for %s", netloc) + logger.debug('Found credentials in url for %s', netloc) return url_user_password # Find a matching index url for this request @@ -131,20 +135,20 @@ class MultiDomainBasicAuth(AuthBase): index_info = split_auth_netloc_from_url(index_url) if index_info: index_url, _, index_url_user_password = index_info - logger.debug("Found index url %s", index_url) + logger.debug('Found index url %s', index_url) # If an index URL was found, try its embedded credentials if index_url and index_url_user_password[0] is not None: username, password = index_url_user_password if username is not None and password is not None: - logger.debug("Found credentials in index url for %s", netloc) + logger.debug('Found credentials in index url for %s', netloc) return index_url_user_password # Get creds from netrc if we still don't have them if allow_netrc: netrc_auth = get_netrc_auth(original_url) if netrc_auth: - logger.debug("Found credentials in netrc for %s", netloc) + logger.debug('Found credentials in netrc for %s', netloc) return netrc_auth # If we don't have a password and keyring is available, use it. @@ -157,14 +161,14 @@ class MultiDomainBasicAuth(AuthBase): ) # fmt: on if kr_auth: - logger.debug("Found credentials in keyring for %s", netloc) + logger.debug('Found credentials in keyring for %s', netloc) return kr_auth return username, password def _get_url_and_credentials( - self, original_url: str - ) -> Tuple[str, Optional[str], Optional[str]]: + self, original_url: str, + ) -> tuple[str, str | None, str | None]: """Return the credentials to use for the provided URL. If allowed, netrc and keyring may be used to obtain the @@ -195,18 +199,18 @@ class MultiDomainBasicAuth(AuthBase): # this netloc will show up as "cached" in the conditional above. # Further, HTTPBasicAuth doesn't accept None, so it makes sense to # cache the value that is going to be used. - username = username or "" - password = password or "" + username = username or '' + password = password or '' # Store any acquired credentials. self.passwords[netloc] = (username, password) assert ( # Credentials were found - (username is not None and password is not None) + (username is not None and password is not None) or # Credentials were not found - or (username is None and password is None) - ), f"Could not load credentials from url: {original_url}" + (username is None and password is None) + ), f'Could not load credentials from url: {original_url}' return url, username, password @@ -222,28 +226,28 @@ class MultiDomainBasicAuth(AuthBase): req = HTTPBasicAuth(username, password)(req) # Attach a hook to handle 401 responses - req.register_hook("response", self.handle_401) + req.register_hook('response', self.handle_401) return req # Factored out to allow for easy patching in tests def _prompt_for_password( - self, netloc: str - ) -> Tuple[Optional[str], Optional[str], bool]: - username = ask_input(f"User for {netloc}: ") + self, netloc: str, + ) -> tuple[str | None, str | None, bool]: + username = ask_input(f'User for {netloc}: ') if not username: return None, None, False auth = get_keyring_auth(netloc, username) if auth and auth[0] is not None and auth[1] is not None: return auth[0], auth[1], False - password = ask_password("Password: ") + password = ask_password('Password: ') return username, password, True # Factored out to allow for easy patching in tests def _should_save_password_to_keyring(self) -> bool: if not keyring: return False - return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" + return ask('Save credentials to keyring [y/N]: ', ['y', 'n']) == 'y' def handle_401(self, resp: Response, **kwargs: Any) -> Response: # We only care about 401 responses, anything else we want to just @@ -284,14 +288,14 @@ class MultiDomainBasicAuth(AuthBase): resp.raw.release_conn() # Add our new username and password to the request - req = HTTPBasicAuth(username or "", password or "")(resp.request) - req.register_hook("response", self.warn_on_401) + req = HTTPBasicAuth(username or '', password or '')(resp.request) + req.register_hook('response', self.warn_on_401) # On successful request, save the credentials that were used to # keyring. (Note that if the user responded "no" above, this member # is not set and nothing will be saved.) if self._credentials_to_save: - req.register_hook("response", self.save_credentials) + req.register_hook('response', self.save_credentials) # Send our new request new_resp = resp.connection.send(req, **kwargs) @@ -303,13 +307,13 @@ class MultiDomainBasicAuth(AuthBase): """Response callback to warn about incorrect credentials.""" if resp.status_code == 401: logger.warning( - "401 Error, Credentials not correct for %s", + '401 Error, Credentials not correct for %s', resp.request.url, ) def save_credentials(self, resp: Response, **kwargs: Any) -> None: """Response callback to save credentials on success.""" - assert keyring is not None, "should never reach here without keyring" + assert keyring is not None, 'should never reach here without keyring' if not keyring: return @@ -317,7 +321,7 @@ class MultiDomainBasicAuth(AuthBase): self._credentials_to_save = None if creds and resp.status_code < 400: try: - logger.info("Saving credentials to keyring") + logger.info('Saving credentials to keyring') keyring.set_password(*creds) except Exception: - logger.exception("Failed to save credentials") + logger.exception('Failed to save credentials') diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/network/cache.py b/.venv/lib/python3.10/site-packages/pip/_internal/network/cache.py index 9dba7ed..ad72654 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/network/cache.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/network/cache.py @@ -1,20 +1,22 @@ """HTTP cache implementation. """ +from __future__ import annotations import os from contextlib import contextmanager -from typing import Iterator, Optional +from typing import Iterator +from typing import Optional +from pip._internal.utils.filesystem import adjacent_tmp_file +from pip._internal.utils.filesystem import replace +from pip._internal.utils.misc import ensure_dir from pip._vendor.cachecontrol.cache import BaseCache from pip._vendor.cachecontrol.caches import FileCache from pip._vendor.requests.models import Response -from pip._internal.utils.filesystem import adjacent_tmp_file, replace -from pip._internal.utils.misc import ensure_dir - def is_from_cache(response: Response) -> bool: - return getattr(response, "from_cache", False) + return getattr(response, 'from_cache', False) @contextmanager @@ -35,7 +37,7 @@ class SafeFileCache(BaseCache): """ def __init__(self, directory: str) -> None: - assert directory is not None, "Cache directory must not be None." + assert directory is not None, 'Cache directory must not be None.' super().__init__() self.directory = directory @@ -47,13 +49,13 @@ class SafeFileCache(BaseCache): parts = list(hashed[:5]) + [hashed] return os.path.join(self.directory, *parts) - def get(self, key: str) -> Optional[bytes]: + def get(self, key: str) -> bytes | None: path = self._get_cache_path(key) with suppressed_cache_errors(): - with open(path, "rb") as f: + with open(path, 'rb') as f: return f.read() - def set(self, key: str, value: bytes, expires: Optional[int] = None) -> None: + def set(self, key: str, value: bytes, expires: int | None = None) -> None: path = self._get_cache_path(key) with suppressed_cache_errors(): ensure_dir(os.path.dirname(path)) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/network/download.py b/.venv/lib/python3.10/site-packages/pip/_internal/network/download.py index 35bc970..a770f3e 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/network/download.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/network/download.py @@ -1,12 +1,14 @@ """Download files with progress indicators. """ +from __future__ import annotations + import cgi import logging import mimetypes import os -from typing import Iterable, Optional, Tuple - -from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response +from typing import Iterable +from typing import Optional +from typing import Tuple from pip._internal.cli.progress_bars import get_download_progress_renderer from pip._internal.exceptions import NetworkConnectionError @@ -14,15 +16,21 @@ from pip._internal.models.index import PyPI from pip._internal.models.link import Link from pip._internal.network.cache import is_from_cache from pip._internal.network.session import PipSession -from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks -from pip._internal.utils.misc import format_size, redact_auth_from_url, splitext +from pip._internal.network.utils import HEADERS +from pip._internal.network.utils import raise_for_status +from pip._internal.network.utils import response_chunks +from pip._internal.utils.misc import format_size +from pip._internal.utils.misc import redact_auth_from_url +from pip._internal.utils.misc import splitext +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE +from pip._vendor.requests.models import Response logger = logging.getLogger(__name__) -def _get_http_response_size(resp: Response) -> Optional[int]: +def _get_http_response_size(resp: Response) -> int | None: try: - return int(resp.headers["content-length"]) + return int(resp.headers['content-length']) except (ValueError, KeyError, TypeError): return None @@ -42,12 +50,12 @@ def _prepare_download( logged_url = redact_auth_from_url(url) if total_length: - logged_url = "{} ({})".format(logged_url, format_size(total_length)) + logged_url = f'{logged_url} ({format_size(total_length)})' if is_from_cache(resp): - logger.info("Using cached %s", logged_url) + logger.info('Using cached %s', logged_url) else: - logger.info("Downloading %s", logged_url) + logger.info('Downloading %s', logged_url) if logger.getEffectiveLevel() > logging.INFO: show_progress = False @@ -82,7 +90,7 @@ def parse_content_disposition(content_disposition: str, default_filename: str) - return the default filename if the result is empty. """ _type, params = cgi.parse_header(content_disposition) - filename = params.get("filename") + filename = params.get('filename') if filename: # We need to sanitize the filename to prevent directory traversal # in case the filename contains ".." path parts. @@ -96,12 +104,12 @@ def _get_http_response_filename(resp: Response, link: Link) -> str: """ filename = link.filename # fallback # Have a look at the Content-Disposition header for a better guess - content_disposition = resp.headers.get("content-disposition") + content_disposition = resp.headers.get('content-disposition') if content_disposition: filename = parse_content_disposition(content_disposition, filename) - ext: Optional[str] = splitext(filename)[1] + ext: str | None = splitext(filename)[1] if not ext: - ext = mimetypes.guess_extension(resp.headers.get("content-type", "")) + ext = mimetypes.guess_extension(resp.headers.get('content-type', '')) if ext: filename += ext if not ext and link.url != resp.url: @@ -112,7 +120,7 @@ def _get_http_response_filename(resp: Response, link: Link) -> str: def _http_get_download(session: PipSession, link: Link) -> Response: - target_url = link.url.split("#", 1)[0] + target_url = link.url.split('#', 1)[0] resp = session.get(target_url, headers=HEADERS, stream=True) raise_for_status(resp) return resp @@ -127,14 +135,14 @@ class Downloader: self._session = session self._progress_bar = progress_bar - def __call__(self, link: Link, location: str) -> Tuple[str, str]: + def __call__(self, link: Link, location: str) -> tuple[str, str]: """Download the file given by link into location.""" try: resp = _http_get_download(self._session, link) except NetworkConnectionError as e: assert e.response is not None logger.critical( - "HTTP error %s while getting %s", e.response.status_code, link + 'HTTP error %s while getting %s', e.response.status_code, link, ) raise @@ -142,10 +150,10 @@ class Downloader: filepath = os.path.join(location, filename) chunks = _prepare_download(resp, link, self._progress_bar) - with open(filepath, "wb") as content_file: + with open(filepath, 'wb') as content_file: for chunk in chunks: content_file.write(chunk) - content_type = resp.headers.get("Content-Type", "") + content_type = resp.headers.get('Content-Type', '') return filepath, content_type @@ -159,8 +167,8 @@ class BatchDownloader: self._progress_bar = progress_bar def __call__( - self, links: Iterable[Link], location: str - ) -> Iterable[Tuple[Link, Tuple[str, str]]]: + self, links: Iterable[Link], location: str, + ) -> Iterable[tuple[Link, tuple[str, str]]]: """Download the files given by links into location.""" for link in links: try: @@ -168,7 +176,7 @@ class BatchDownloader: except NetworkConnectionError as e: assert e.response is not None logger.critical( - "HTTP error %s while getting %s", + 'HTTP error %s while getting %s', e.response.status_code, link, ) @@ -178,8 +186,8 @@ class BatchDownloader: filepath = os.path.join(location, filename) chunks = _prepare_download(resp, link, self._progress_bar) - with open(filepath, "wb") as content_file: + with open(filepath, 'wb') as content_file: for chunk in chunks: content_file.write(chunk) - content_type = resp.headers.get("Content-Type", "") + content_type = resp.headers.get('Content-Type', '') yield link, (filepath, content_type) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/network/lazy_wheel.py b/.venv/lib/python3.10/site-packages/pip/_internal/network/lazy_wheel.py index c9e44d5..c75b3ff 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/network/lazy_wheel.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/network/lazy_wheel.py @@ -1,6 +1,7 @@ """Lazy ZIP over HTTP""" +from __future__ import annotations -__all__ = ["HTTPRangeRequestUnsupported", "dist_from_wheel_url"] +__all__ = ['HTTPRangeRequestUnsupported', 'dist_from_wheel_url'] from bisect import bisect_left, bisect_right from contextlib import contextmanager @@ -47,25 +48,25 @@ class LazyZipOverHTTP: """ def __init__( - self, url: str, session: PipSession, chunk_size: int = CONTENT_CHUNK_SIZE + self, url: str, session: PipSession, chunk_size: int = CONTENT_CHUNK_SIZE, ) -> None: head = session.head(url, headers=HEADERS) raise_for_status(head) assert head.status_code == 200 self._session, self._url, self._chunk_size = session, url, chunk_size - self._length = int(head.headers["Content-Length"]) + self._length = int(head.headers['Content-Length']) self._file = NamedTemporaryFile() self.truncate(self._length) - self._left: List[int] = [] - self._right: List[int] = [] - if "bytes" not in head.headers.get("Accept-Ranges", "none"): - raise HTTPRangeRequestUnsupported("range request is not supported") + self._left: list[int] = [] + self._right: list[int] = [] + if 'bytes' not in head.headers.get('Accept-Ranges', 'none'): + raise HTTPRangeRequestUnsupported('range request is not supported') self._check_zip() @property def mode(self) -> str: """Opening mode, which is always rb.""" - return "rb" + return 'rb' @property def name(self) -> str: @@ -117,7 +118,7 @@ class LazyZipOverHTTP: """Return the current position.""" return self._file.tell() - def truncate(self, size: Optional[int] = None) -> int: + def truncate(self, size: int | None = None) -> int: """Resize the stream to the given size in bytes. If size is unspecified resize to the current position. @@ -131,11 +132,11 @@ class LazyZipOverHTTP: """Return False.""" return False - def __enter__(self) -> "LazyZipOverHTTP": + def __enter__(self) -> LazyZipOverHTTP: self._file.__enter__() return self - def __exit__(self, *exc: Any) -> Optional[bool]: + def __exit__(self, *exc: Any) -> bool | None: return self._file.__exit__(*exc) @contextmanager @@ -166,18 +167,18 @@ class LazyZipOverHTTP: break def _stream_response( - self, start: int, end: int, base_headers: Dict[str, str] = HEADERS + self, start: int, end: int, base_headers: dict[str, str] = HEADERS, ) -> Response: """Return HTTP response to a range request from start to end.""" headers = base_headers.copy() - headers["Range"] = f"bytes={start}-{end}" + headers['Range'] = f'bytes={start}-{end}' # TODO: Get range requests to be correctly cached - headers["Cache-Control"] = "no-cache" + headers['Cache-Control'] = 'no-cache' return self._session.get(self._url, headers=headers, stream=True) def _merge( - self, start: int, end: int, left: int, right: int - ) -> Iterator[Tuple[int, int]]: + self, start: int, end: int, left: int, right: int, + ) -> Iterator[tuple[int, int]]: """Return an iterator of intervals to be fetched. Args: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/network/session.py b/.venv/lib/python3.10/site-packages/pip/_internal/network/session.py index cbe743b..a7944e4 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/network/session.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/network/session.py @@ -1,6 +1,7 @@ """PipSession and supporting code, containing all pip-specific network request configuration and behavior. """ +from __future__ import annotations import email.utils import io @@ -15,27 +16,37 @@ import subprocess import sys import urllib.parse import warnings -from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Tuple, Union - -from pip._vendor import requests, urllib3 -from pip._vendor.cachecontrol import CacheControlAdapter -from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter -from pip._vendor.requests.models import PreparedRequest, Response -from pip._vendor.requests.structures import CaseInsensitiveDict -from pip._vendor.urllib3.connectionpool import ConnectionPool -from pip._vendor.urllib3.exceptions import InsecureRequestWarning +from typing import Any +from typing import Dict +from typing import Iterator +from typing import List +from typing import Mapping +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import Union from pip import __version__ from pip._internal.metadata import get_default_environment from pip._internal.models.link import Link from pip._internal.network.auth import MultiDomainBasicAuth from pip._internal.network.cache import SafeFileCache - -# Import ssl from compat so the initial import occurs in only one place. from pip._internal.utils.compat import has_tls from pip._internal.utils.glibc import libc_ver -from pip._internal.utils.misc import build_url_from_netloc, parse_netloc +from pip._internal.utils.misc import build_url_from_netloc +from pip._internal.utils.misc import parse_netloc from pip._internal.utils.urls import url_to_path +from pip._vendor import requests +from pip._vendor import urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.requests.adapters import BaseAdapter +from pip._vendor.requests.adapters import HTTPAdapter +from pip._vendor.requests.models import PreparedRequest +from pip._vendor.requests.models import Response +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.urllib3.connectionpool import ConnectionPool +from pip._vendor.urllib3.exceptions import InsecureRequestWarning +# Import ssl from compat so the initial import occurs in only one place. logger = logging.getLogger(__name__) @@ -43,19 +54,19 @@ SecureOrigin = Tuple[str, str, Optional[Union[int, str]]] # Ignore warning raised when using --trusted-host. -warnings.filterwarnings("ignore", category=InsecureRequestWarning) +warnings.filterwarnings('ignore', category=InsecureRequestWarning) -SECURE_ORIGINS: List[SecureOrigin] = [ +SECURE_ORIGINS: list[SecureOrigin] = [ # protocol, hostname, port # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) - ("https", "*", "*"), - ("*", "localhost", "*"), - ("*", "127.0.0.0/8", "*"), - ("*", "::1/128", "*"), - ("file", "*", None), + ('https', '*', '*'), + ('*', 'localhost', '*'), + ('*', '127.0.0.0/8', '*'), + ('*', '::1/128', '*'), + ('file', '*', None), # ssh is always secure. - ("ssh", "*", "*"), + ('ssh', '*', '*'), ] @@ -68,13 +79,13 @@ SECURE_ORIGINS: List[SecureOrigin] = [ # For more background, see: https://github.com/pypa/pip/issues/5499 CI_ENVIRONMENT_VARIABLES = ( # Azure Pipelines - "BUILD_BUILDID", + 'BUILD_BUILDID', # Jenkins - "BUILD_ID", + 'BUILD_ID', # AppVeyor, CircleCI, Codeship, Gitlab CI, Shippable, Travis CI - "CI", + 'CI', # Explicit environment variable. - "PIP_IS_CI", + 'PIP_IS_CI', ) @@ -92,100 +103,100 @@ def user_agent() -> str: """ Return a string representing the user agent. """ - data: Dict[str, Any] = { - "installer": {"name": "pip", "version": __version__}, - "python": platform.python_version(), - "implementation": { - "name": platform.python_implementation(), + data: dict[str, Any] = { + 'installer': {'name': 'pip', 'version': __version__}, + 'python': platform.python_version(), + 'implementation': { + 'name': platform.python_implementation(), }, } - if data["implementation"]["name"] == "CPython": - data["implementation"]["version"] = platform.python_version() - elif data["implementation"]["name"] == "PyPy": + if data['implementation']['name'] == 'CPython': + data['implementation']['version'] = platform.python_version() + elif data['implementation']['name'] == 'PyPy': pypy_version_info = sys.pypy_version_info # type: ignore - if pypy_version_info.releaselevel == "final": + if pypy_version_info.releaselevel == 'final': pypy_version_info = pypy_version_info[:3] - data["implementation"]["version"] = ".".join( - [str(x) for x in pypy_version_info] + data['implementation']['version'] = '.'.join( + [str(x) for x in pypy_version_info], ) - elif data["implementation"]["name"] == "Jython": + elif data['implementation']['name'] == 'Jython': # Complete Guess - data["implementation"]["version"] = platform.python_version() - elif data["implementation"]["name"] == "IronPython": + data['implementation']['version'] = platform.python_version() + elif data['implementation']['name'] == 'IronPython': # Complete Guess - data["implementation"]["version"] = platform.python_version() + data['implementation']['version'] = platform.python_version() - if sys.platform.startswith("linux"): + if sys.platform.startswith('linux'): from pip._vendor import distro linux_distribution = distro.name(), distro.version(), distro.codename() - distro_infos: Dict[str, Any] = dict( + distro_infos: dict[str, Any] = dict( filter( lambda x: x[1], - zip(["name", "version", "id"], linux_distribution), - ) + zip(['name', 'version', 'id'], linux_distribution), + ), ) libc = dict( filter( lambda x: x[1], - zip(["lib", "version"], libc_ver()), - ) + zip(['lib', 'version'], libc_ver()), + ), ) if libc: - distro_infos["libc"] = libc + distro_infos['libc'] = libc if distro_infos: - data["distro"] = distro_infos + data['distro'] = distro_infos - if sys.platform.startswith("darwin") and platform.mac_ver()[0]: - data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]} + if sys.platform.startswith('darwin') and platform.mac_ver()[0]: + data['distro'] = {'name': 'macOS', 'version': platform.mac_ver()[0]} if platform.system(): - data.setdefault("system", {})["name"] = platform.system() + data.setdefault('system', {})['name'] = platform.system() if platform.release(): - data.setdefault("system", {})["release"] = platform.release() + data.setdefault('system', {})['release'] = platform.release() if platform.machine(): - data["cpu"] = platform.machine() + data['cpu'] = platform.machine() if has_tls(): import _ssl as ssl - data["openssl_version"] = ssl.OPENSSL_VERSION + data['openssl_version'] = ssl.OPENSSL_VERSION - setuptools_dist = get_default_environment().get_distribution("setuptools") + setuptools_dist = get_default_environment().get_distribution('setuptools') if setuptools_dist is not None: - data["setuptools_version"] = str(setuptools_dist.version) + data['setuptools_version'] = str(setuptools_dist.version) - if shutil.which("rustc") is not None: + if shutil.which('rustc') is not None: # If for any reason `rustc --version` fails, silently ignore it try: rustc_output = subprocess.check_output( - ["rustc", "--version"], stderr=subprocess.STDOUT, timeout=0.5 + ['rustc', '--version'], stderr=subprocess.STDOUT, timeout=0.5, ) except Exception: pass else: - if rustc_output.startswith(b"rustc "): + if rustc_output.startswith(b'rustc '): # The format of `rustc --version` is: # `b'rustc 1.52.1 (9bc8c42bb 2021-05-09)\n'` # We extract just the middle (1.52.1) part - data["rustc_version"] = rustc_output.split(b" ")[1].decode() + data['rustc_version'] = rustc_output.split(b' ')[1].decode() # Use None rather than False so as not to give the impression that # pip knows it is not being run under CI. Rather, it is a null or # inconclusive result. Also, we include some value rather than no # value to make it easier to know that the check has been run. - data["ci"] = True if looks_like_ci() else None + data['ci'] = True if looks_like_ci() else None - user_data = os.environ.get("PIP_USER_AGENT_USER_DATA") + user_data = os.environ.get('PIP_USER_AGENT_USER_DATA') if user_data is not None: - data["user_data"] = user_data + data['user_data'] = user_data - return "{data[installer][name]}/{data[installer][version]} {json}".format( + return '{data[installer][name]}/{data[installer][version]} {json}'.format( data=data, - json=json.dumps(data, separators=(",", ":"), sort_keys=True), + json=json.dumps(data, separators=(',', ':'), sort_keys=True), ) @@ -194,10 +205,10 @@ class LocalFSAdapter(BaseAdapter): self, request: PreparedRequest, stream: bool = False, - timeout: Optional[Union[float, Tuple[float, float]]] = None, - verify: Union[bool, str] = True, - cert: Optional[Union[str, Tuple[str, str]]] = None, - proxies: Optional[Mapping[str, str]] = None, + timeout: float | tuple[float, float] | None = None, + verify: bool | str = True, + cert: str | tuple[str, str] | None = None, + proxies: Mapping[str, str] | None = None, ) -> Response: pathname = url_to_path(request.url) @@ -212,19 +223,19 @@ class LocalFSAdapter(BaseAdapter): # to return a better error message: resp.status_code = 404 resp.reason = type(exc).__name__ - resp.raw = io.BytesIO(f"{resp.reason}: {exc}".encode("utf8")) + resp.raw = io.BytesIO(f'{resp.reason}: {exc}'.encode()) else: modified = email.utils.formatdate(stats.st_mtime, usegmt=True) - content_type = mimetypes.guess_type(pathname)[0] or "text/plain" + content_type = mimetypes.guess_type(pathname)[0] or 'text/plain' resp.headers = CaseInsensitiveDict( { - "Content-Type": content_type, - "Content-Length": stats.st_size, - "Last-Modified": modified, - } + 'Content-Type': content_type, + 'Content-Length': stats.st_size, + 'Last-Modified': modified, + }, ) - resp.raw = open(pathname, "rb") + resp.raw = open(pathname, 'rb') resp.close = resp.raw.close return resp @@ -238,8 +249,8 @@ class InsecureHTTPAdapter(HTTPAdapter): self, conn: ConnectionPool, url: str, - verify: Union[bool, str], - cert: Optional[Union[str, Tuple[str, str]]], + verify: bool | str, + cert: str | tuple[str, str] | None, ) -> None: super().cert_verify(conn=conn, url=url, verify=False, cert=cert) @@ -249,23 +260,23 @@ class InsecureCacheControlAdapter(CacheControlAdapter): self, conn: ConnectionPool, url: str, - verify: Union[bool, str], - cert: Optional[Union[str, Tuple[str, str]]], + verify: bool | str, + cert: str | tuple[str, str] | None, ) -> None: super().cert_verify(conn=conn, url=url, verify=False, cert=cert) class PipSession(requests.Session): - timeout: Optional[int] = None + timeout: int | None = None def __init__( self, *args: Any, retries: int = 0, - cache: Optional[str] = None, + cache: str | None = None, trusted_hosts: Sequence[str] = (), - index_urls: Optional[List[str]] = None, + index_urls: list[str] | None = None, **kwargs: Any, ) -> None: """ @@ -276,10 +287,10 @@ class PipSession(requests.Session): # Namespace the attribute with "pip_" just in case to prevent # possible conflicts with the base class. - self.pip_trusted_origins: List[Tuple[str, Optional[int]]] = [] + self.pip_trusted_origins: list[tuple[str, int | None]] = [] # Attach our User Agent to the request - self.headers["User-Agent"] = user_agent() + self.headers['User-Agent'] = user_agent() # Attach our Authentication handler to the session self.auth = MultiDomainBasicAuth(index_urls=index_urls) @@ -327,16 +338,16 @@ class PipSession(requests.Session): secure_adapter = HTTPAdapter(max_retries=retries) self._trusted_host_adapter = insecure_adapter - self.mount("https://", secure_adapter) - self.mount("http://", insecure_adapter) + self.mount('https://', secure_adapter) + self.mount('http://', insecure_adapter) # Enable file:// urls - self.mount("file://", LocalFSAdapter()) + self.mount('file://', LocalFSAdapter()) for host in trusted_hosts: self.add_trusted_host(host, suppress_logging=True) - def update_index_urls(self, new_index_urls: List[str]) -> None: + def update_index_urls(self, new_index_urls: list[str]) -> None: """ :param new_index_urls: New index urls to update the authentication handler with. @@ -344,7 +355,7 @@ class PipSession(requests.Session): self.auth.index_urls = new_index_urls def add_trusted_host( - self, host: str, source: Optional[str] = None, suppress_logging: bool = False + self, host: str, source: str | None = None, suppress_logging: bool = False, ) -> None: """ :param host: It is okay to provide a host that has previously been @@ -353,9 +364,9 @@ class PipSession(requests.Session): string came from. """ if not suppress_logging: - msg = f"adding trusted host: {host!r}" + msg = f'adding trusted host: {host!r}' if source is not None: - msg += f" (from {source})" + msg += f' (from {source})' logger.info(msg) host_port = parse_netloc(host) @@ -363,21 +374,21 @@ class PipSession(requests.Session): self.pip_trusted_origins.append(host_port) self.mount( - build_url_from_netloc(host, scheme="http") + "/", self._trusted_host_adapter + build_url_from_netloc(host, scheme='http') + '/', self._trusted_host_adapter, ) - self.mount(build_url_from_netloc(host) + "/", self._trusted_host_adapter) + self.mount(build_url_from_netloc(host) + '/', self._trusted_host_adapter) if not host_port[1]: self.mount( - build_url_from_netloc(host, scheme="http") + ":", + build_url_from_netloc(host, scheme='http') + ':', self._trusted_host_adapter, ) # Mount wildcard ports for the same host. - self.mount(build_url_from_netloc(host) + ":", self._trusted_host_adapter) + self.mount(build_url_from_netloc(host) + ':', self._trusted_host_adapter) def iter_secure_origins(self) -> Iterator[SecureOrigin]: yield from SECURE_ORIGINS for host, port in self.pip_trusted_origins: - yield ("*", host, "*" if port is None else port) + yield ('*', host, '*' if port is None else port) def is_secure_origin(self, location: Link) -> bool: # Determine if this url used a secure transport mechanism @@ -392,14 +403,14 @@ class PipSession(requests.Session): # Don't count the repository type as part of the protocol: in # cases such as "git+ssh", only use "ssh". (I.e., Only verify against # the last scheme.) - origin_protocol = origin_protocol.rsplit("+", 1)[-1] + origin_protocol = origin_protocol.rsplit('+', 1)[-1] # Determine if our origin is a secure origin by looking through our # hardcoded list of secure origins, as well as any additional ones # configured on this PackageFinder instance. for secure_origin in self.iter_secure_origins(): secure_protocol, secure_host, secure_port = secure_origin - if origin_protocol != secure_protocol and secure_protocol != "*": + if origin_protocol != secure_protocol and secure_protocol != '*': continue try: @@ -409,9 +420,9 @@ class PipSession(requests.Session): # We don't have both a valid address or a valid network, so # we'll check this origin against hostnames. if ( - origin_host - and origin_host.lower() != secure_host.lower() - and secure_host != "*" + origin_host and + origin_host.lower() != secure_host.lower() and + secure_host != '*' ): continue else: @@ -422,9 +433,9 @@ class PipSession(requests.Session): # Check to see if the port matches. if ( - origin_port != secure_port - and secure_port != "*" - and secure_port is not None + origin_port != secure_port and + secure_port != '*' and + secure_port is not None ): continue @@ -436,9 +447,9 @@ class PipSession(requests.Session): # will not accept it as a valid location to search. We will however # log a warning that we are ignoring it. logger.warning( - "The repository located at %s is not a trusted or secure host and " - "is being ignored. If this repository is available via HTTPS we " - "recommend you use HTTPS instead, otherwise you may silence " + 'The repository located at %s is not a trusted or secure host and ' + 'is being ignored. If this repository is available via HTTPS we ' + 'recommend you use HTTPS instead, otherwise you may silence ' "this warning and allow it anyway with '--trusted-host %s'.", origin_host, origin_host, @@ -448,7 +459,7 @@ class PipSession(requests.Session): def request(self, method: str, url: str, *args: Any, **kwargs: Any) -> Response: # Allow setting a default timeout on a session - kwargs.setdefault("timeout", self.timeout) + kwargs.setdefault('timeout', self.timeout) # Dispatch the actual request return super().request(method, url, *args, **kwargs) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/network/utils.py b/.venv/lib/python3.10/site-packages/pip/_internal/network/utils.py index 094cf1b..8ef7bfc 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/network/utils.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/network/utils.py @@ -1,8 +1,11 @@ -from typing import Dict, Iterator +from __future__ import annotations -from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response +from typing import Dict +from typing import Iterator from pip._internal.exceptions import NetworkConnectionError +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE +from pip._vendor.requests.models import Response # The following comments and HTTP headers were originally added by # Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03. @@ -23,31 +26,31 @@ from pip._internal.exceptions import NetworkConnectionError # you're not asking for a compressed file and will then decompress it # before sending because if that's the case I don't think it'll ever be # possible to make this work. -HEADERS: Dict[str, str] = {"Accept-Encoding": "identity"} +HEADERS: dict[str, str] = {'Accept-Encoding': 'identity'} def raise_for_status(resp: Response) -> None: - http_error_msg = "" + http_error_msg = '' if isinstance(resp.reason, bytes): # We attempt to decode utf-8 first because some servers # choose to localize their reason strings. If the string # isn't utf-8, we fall back to iso-8859-1 for all other # encodings. try: - reason = resp.reason.decode("utf-8") + reason = resp.reason.decode('utf-8') except UnicodeDecodeError: - reason = resp.reason.decode("iso-8859-1") + reason = resp.reason.decode('iso-8859-1') else: reason = resp.reason if 400 <= resp.status_code < 500: http_error_msg = ( - f"{resp.status_code} Client Error: {reason} for url: {resp.url}" + f'{resp.status_code} Client Error: {reason} for url: {resp.url}' ) elif 500 <= resp.status_code < 600: http_error_msg = ( - f"{resp.status_code} Server Error: {reason} for url: {resp.url}" + f'{resp.status_code} Server Error: {reason} for url: {resp.url}' ) if http_error_msg: @@ -55,7 +58,7 @@ def raise_for_status(resp: Response) -> None: def response_chunks( - response: Response, chunk_size: int = CONTENT_CHUNK_SIZE + response: Response, chunk_size: int = CONTENT_CHUNK_SIZE, ) -> Iterator[bytes]: """Given a requests Response, provide the data chunks.""" try: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/network/xmlrpc.py b/.venv/lib/python3.10/site-packages/pip/_internal/network/xmlrpc.py index 4a7d55d..8c6cad8 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/network/xmlrpc.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/network/xmlrpc.py @@ -1,10 +1,12 @@ """xmlrpclib.Transport implementation """ +from __future__ import annotations import logging import urllib.parse import xmlrpc.client -from typing import TYPE_CHECKING, Tuple +from typing import Tuple +from typing import TYPE_CHECKING from pip._internal.exceptions import NetworkConnectionError from pip._internal.network.session import PipSession @@ -22,7 +24,7 @@ class PipXmlrpcTransport(xmlrpc.client.Transport): """ def __init__( - self, index_url: str, session: PipSession, use_datetime: bool = False + self, index_url: str, session: PipSession, use_datetime: bool = False, ) -> None: super().__init__(use_datetime) index_parts = urllib.parse.urlparse(index_url) @@ -31,16 +33,16 @@ class PipXmlrpcTransport(xmlrpc.client.Transport): def request( self, - host: "_HostType", + host: _HostType, handler: str, request_body: bytes, verbose: bool = False, - ) -> Tuple["_Marshallable", ...]: + ) -> tuple[_Marshallable, ...]: assert isinstance(host, str) parts = (self._scheme, host, handler, None, None, None) url = urllib.parse.urlunparse(parts) try: - headers = {"Content-Type": "text/xml"} + headers = {'Content-Type': 'text/xml'} response = self._session.post( url, data=request_body, @@ -53,7 +55,7 @@ class PipXmlrpcTransport(xmlrpc.client.Transport): except NetworkConnectionError as exc: assert exc.response logger.critical( - "HTTP error %s while getting %s", + 'HTTP error %s while getting %s', exc.response.status_code, url, ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata.py index e2b7b44..997ea16 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata.py @@ -1,27 +1,25 @@ """Metadata generation logic for source distributions. """ +from __future__ import annotations import os -from pip._vendor.pep517.wrappers import Pep517HookCaller - from pip._internal.build_env import BuildEnvironment -from pip._internal.exceptions import ( - InstallationSubprocessError, - MetadataGenerationFailed, -) +from pip._internal.exceptions import InstallationSubprocessError +from pip._internal.exceptions import MetadataGenerationFailed from pip._internal.utils.subprocess import runner_with_spinner_message from pip._internal.utils.temp_dir import TempDirectory +from pip._vendor.pep517.wrappers import Pep517HookCaller def generate_metadata( - build_env: BuildEnvironment, backend: Pep517HookCaller, details: str + build_env: BuildEnvironment, backend: Pep517HookCaller, details: str, ) -> str: """Generate metadata using mechanisms described in PEP 517. Returns the generated metadata directory. """ - metadata_tmpdir = TempDirectory(kind="modern-metadata", globally_managed=True) + metadata_tmpdir = TempDirectory(kind='modern-metadata', globally_managed=True) metadata_dir = metadata_tmpdir.path @@ -29,7 +27,7 @@ def generate_metadata( # Note that Pep517HookCaller implements a fallback for # prepare_metadata_for_build_wheel, so we don't have to # consider the possibility that this hook doesn't exist. - runner = runner_with_spinner_message("Preparing metadata (pyproject.toml)") + runner = runner_with_spinner_message('Preparing metadata (pyproject.toml)') with backend.subprocess_runner(runner): try: distinfo_dir = backend.prepare_metadata_for_build_wheel(metadata_dir) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_editable.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_editable.py index 4c3f48b..72b3a4a 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_editable.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_editable.py @@ -1,27 +1,25 @@ """Metadata generation logic for source distributions. """ +from __future__ import annotations import os -from pip._vendor.pep517.wrappers import Pep517HookCaller - from pip._internal.build_env import BuildEnvironment -from pip._internal.exceptions import ( - InstallationSubprocessError, - MetadataGenerationFailed, -) +from pip._internal.exceptions import InstallationSubprocessError +from pip._internal.exceptions import MetadataGenerationFailed from pip._internal.utils.subprocess import runner_with_spinner_message from pip._internal.utils.temp_dir import TempDirectory +from pip._vendor.pep517.wrappers import Pep517HookCaller def generate_editable_metadata( - build_env: BuildEnvironment, backend: Pep517HookCaller, details: str + build_env: BuildEnvironment, backend: Pep517HookCaller, details: str, ) -> str: """Generate metadata using mechanisms described in PEP 660. Returns the generated metadata directory. """ - metadata_tmpdir = TempDirectory(kind="modern-metadata", globally_managed=True) + metadata_tmpdir = TempDirectory(kind='modern-metadata', globally_managed=True) metadata_dir = metadata_tmpdir.path @@ -30,7 +28,7 @@ def generate_editable_metadata( # prepare_metadata_for_build_wheel/editable, so we don't have to # consider the possibility that this hook doesn't exist. runner = runner_with_spinner_message( - "Preparing editable metadata (pyproject.toml)" + 'Preparing editable metadata (pyproject.toml)', ) with backend.subprocess_runner(runner): try: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_legacy.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_legacy.py index e60988d..a4faf35 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_legacy.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_legacy.py @@ -1,16 +1,15 @@ """Metadata generation logic for legacy source distributions. """ +from __future__ import annotations import logging import os from pip._internal.build_env import BuildEnvironment from pip._internal.cli.spinners import open_spinner -from pip._internal.exceptions import ( - InstallationError, - InstallationSubprocessError, - MetadataGenerationFailed, -) +from pip._internal.exceptions import InstallationError +from pip._internal.exceptions import InstallationSubprocessError +from pip._internal.exceptions import MetadataGenerationFailed from pip._internal.utils.setuptools_build import make_setuptools_egg_info_args from pip._internal.utils.subprocess import call_subprocess from pip._internal.utils.temp_dir import TempDirectory @@ -20,14 +19,14 @@ logger = logging.getLogger(__name__) def _find_egg_info(directory: str) -> str: """Find an .egg-info subdirectory in `directory`.""" - filenames = [f for f in os.listdir(directory) if f.endswith(".egg-info")] + filenames = [f for f in os.listdir(directory) if f.endswith('.egg-info')] if not filenames: - raise InstallationError(f"No .egg-info directory found in {directory}") + raise InstallationError(f'No .egg-info directory found in {directory}') if len(filenames) > 1: raise InstallationError( - "More than one .egg-info directory found in {}".format(directory) + f'More than one .egg-info directory found in {directory}', ) return os.path.join(directory, filenames[0]) @@ -45,12 +44,12 @@ def generate_metadata( Returns the generated metadata directory. """ logger.debug( - "Running setup.py (path:%s) egg_info for package %s", + 'Running setup.py (path:%s) egg_info for package %s', setup_py_path, details, ) - egg_info_dir = TempDirectory(kind="pip-egg-info", globally_managed=True).path + egg_info_dir = TempDirectory(kind='pip-egg-info', globally_managed=True).path args = make_setuptools_egg_info_args( setup_py_path, @@ -59,12 +58,12 @@ def generate_metadata( ) with build_env: - with open_spinner("Preparing metadata (setup.py)") as spinner: + with open_spinner('Preparing metadata (setup.py)') as spinner: try: call_subprocess( args, cwd=source_dir, - command_desc="python setup.py egg_info", + command_desc='python setup.py egg_info', spinner=spinner, ) except InstallationSubprocessError as error: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel.py index b0d2fc9..c10a8c1 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel.py @@ -1,10 +1,11 @@ +from __future__ import annotations + import logging import os from typing import Optional -from pip._vendor.pep517.wrappers import Pep517HookCaller - from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._vendor.pep517.wrappers import Pep517HookCaller logger = logging.getLogger(__name__) @@ -14,17 +15,17 @@ def build_wheel_pep517( backend: Pep517HookCaller, metadata_directory: str, tempd: str, -) -> Optional[str]: +) -> str | None: """Build one InstallRequirement using the PEP 517 build process. Returns path to wheel if successfully built. Otherwise, returns None. """ assert metadata_directory is not None try: - logger.debug("Destination directory: %s", tempd) + logger.debug('Destination directory: %s', tempd) runner = runner_with_spinner_message( - f"Building wheel for {name} (pyproject.toml)" + f'Building wheel for {name} (pyproject.toml)', ) with backend.subprocess_runner(runner): wheel_name = backend.build_wheel( @@ -32,6 +33,6 @@ def build_wheel_pep517( metadata_directory=metadata_directory, ) except Exception: - logger.error("Failed building wheel for %s", name) + logger.error('Failed building wheel for %s', name) return None return os.path.join(tempd, wheel_name) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_editable.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_editable.py index cf7b01a..ef330bc 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_editable.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_editable.py @@ -1,10 +1,12 @@ +from __future__ import annotations + import logging import os from typing import Optional -from pip._vendor.pep517.wrappers import HookMissing, Pep517HookCaller - from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._vendor.pep517.wrappers import HookMissing +from pip._vendor.pep517.wrappers import Pep517HookCaller logger = logging.getLogger(__name__) @@ -14,17 +16,17 @@ def build_wheel_editable( backend: Pep517HookCaller, metadata_directory: str, tempd: str, -) -> Optional[str]: +) -> str | None: """Build one InstallRequirement using the PEP 660 build process. Returns path to wheel if successfully built. Otherwise, returns None. """ assert metadata_directory is not None try: - logger.debug("Destination directory: %s", tempd) + logger.debug('Destination directory: %s', tempd) runner = runner_with_spinner_message( - f"Building editable for {name} (pyproject.toml)" + f'Building editable for {name} (pyproject.toml)', ) with backend.subprocess_runner(runner): try: @@ -34,13 +36,13 @@ def build_wheel_editable( ) except HookMissing as e: logger.error( - "Cannot build editable %s because the build " - "backend does not have the %s hook", + 'Cannot build editable %s because the build ' + 'backend does not have the %s hook', name, e, ) return None except Exception: - logger.error("Failed building editable for %s", name) + logger.error('Failed building editable for %s', name) return None return os.path.join(tempd, wheel_name) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_legacy.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_legacy.py index c5f0492..1d3a99f 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_legacy.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_legacy.py @@ -1,54 +1,58 @@ +from __future__ import annotations + import logging import os.path -from typing import List, Optional +from typing import List +from typing import Optional from pip._internal.cli.spinners import open_spinner from pip._internal.utils.setuptools_build import make_setuptools_bdist_wheel_args -from pip._internal.utils.subprocess import call_subprocess, format_command_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.subprocess import format_command_args logger = logging.getLogger(__name__) def format_command_result( - command_args: List[str], + command_args: list[str], command_output: str, ) -> str: """Format command information for logging.""" command_desc = format_command_args(command_args) - text = f"Command arguments: {command_desc}\n" + text = f'Command arguments: {command_desc}\n' if not command_output: - text += "Command output: None" + text += 'Command output: None' elif logger.getEffectiveLevel() > logging.DEBUG: - text += "Command output: [use --verbose to show]" + text += 'Command output: [use --verbose to show]' else: - if not command_output.endswith("\n"): - command_output += "\n" - text += f"Command output:\n{command_output}" + if not command_output.endswith('\n'): + command_output += '\n' + text += f'Command output:\n{command_output}' return text def get_legacy_build_wheel_path( - names: List[str], + names: list[str], temp_dir: str, name: str, - command_args: List[str], + command_args: list[str], command_output: str, -) -> Optional[str]: +) -> str | None: """Return the path to the wheel in the temporary build directory.""" # Sort for determinism. names = sorted(names) if not names: - msg = ("Legacy build of wheel for {!r} created no files.\n").format(name) + msg = ('Legacy build of wheel for {!r} created no files.\n').format(name) msg += format_command_result(command_args, command_output) logger.warning(msg) return None if len(names) > 1: msg = ( - "Legacy build of wheel for {!r} created more than one file.\n" - "Filenames (choosing first): {}\n" + 'Legacy build of wheel for {!r} created more than one file.\n' + 'Filenames (choosing first): {}\n' ).format(name, names) msg += format_command_result(command_args, command_output) logger.warning(msg) @@ -60,10 +64,10 @@ def build_wheel_legacy( name: str, setup_py_path: str, source_dir: str, - global_options: List[str], - build_options: List[str], + global_options: list[str], + build_options: list[str], tempd: str, -) -> Optional[str]: +) -> str | None: """Build one unpacked package using the "legacy" build process. Returns path to wheel if successfully built. Otherwise, returns None. @@ -75,20 +79,20 @@ def build_wheel_legacy( destination_dir=tempd, ) - spin_message = f"Building wheel for {name} (setup.py)" + spin_message = f'Building wheel for {name} (setup.py)' with open_spinner(spin_message) as spinner: - logger.debug("Destination directory: %s", tempd) + logger.debug('Destination directory: %s', tempd) try: output = call_subprocess( wheel_args, - command_desc="python setup.py bdist_wheel", + command_desc='python setup.py bdist_wheel', cwd=source_dir, spinner=spinner, ) except Exception: - spinner.finish("error") - logger.error("Failed building wheel for %s", name) + spinner.finish('error') + logger.error('Failed building wheel for %s', name) return None names = os.listdir(tempd) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/check.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/check.py index fb3ac8b..c5c5129 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/check.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/check.py @@ -1,23 +1,30 @@ """Validation of dependencies of packages """ +from __future__ import annotations import logging -from typing import Callable, Dict, List, NamedTuple, Optional, Set, Tuple - -from pip._vendor.packaging.requirements import Requirement -from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from typing import Callable +from typing import Dict +from typing import List +from typing import NamedTuple +from typing import Optional +from typing import Set +from typing import Tuple from pip._internal.distributions import make_distribution_for_install_requirement from pip._internal.metadata import get_default_environment from pip._internal.metadata.base import DistributionVersion from pip._internal.req.req_install import InstallRequirement +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.utils import NormalizedName logger = logging.getLogger(__name__) class PackageDetails(NamedTuple): version: DistributionVersion - dependencies: List[Requirement] + dependencies: list[Requirement] # Shorthands @@ -31,7 +38,7 @@ CheckResult = Tuple[MissingDict, ConflictingDict] ConflictDetails = Tuple[PackageSet, CheckResult] -def create_package_set_from_installed() -> Tuple[PackageSet, bool]: +def create_package_set_from_installed() -> tuple[PackageSet, bool]: """Converts a list of distributions into a PackageSet.""" package_set = {} problems = False @@ -43,13 +50,13 @@ def create_package_set_from_installed() -> Tuple[PackageSet, bool]: package_set[name] = PackageDetails(dist.version, dependencies) except (OSError, ValueError) as e: # Don't crash on unreadable or broken metadata. - logger.warning("Error parsing requirements for %s: %s", name, e) + logger.warning('Error parsing requirements for %s: %s', name, e) problems = True return package_set, problems def check_package_set( - package_set: PackageSet, should_ignore: Optional[Callable[[str], bool]] = None + package_set: PackageSet, should_ignore: Callable[[str], bool] | None = None, ) -> CheckResult: """Check if a package set is consistent @@ -62,8 +69,8 @@ def check_package_set( for package_name, package_detail in package_set.items(): # Info about dependencies of package_name - missing_deps: Set[Missing] = set() - conflicting_deps: Set[Conflicting] = set() + missing_deps: set[Missing] = set() + conflicting_deps: set[Conflicting] = set() if should_ignore and should_ignore(package_name): continue @@ -93,7 +100,7 @@ def check_package_set( return missing, conflicting -def check_install_conflicts(to_install: List[InstallRequirement]) -> ConflictDetails: +def check_install_conflicts(to_install: list[InstallRequirement]) -> ConflictDetails: """For checking if the dependency graph would be consistent after \ installing given requirements """ @@ -108,14 +115,14 @@ def check_install_conflicts(to_install: List[InstallRequirement]) -> ConflictDet return ( package_set, check_package_set( - package_set, should_ignore=lambda name: name not in whitelist + package_set, should_ignore=lambda name: name not in whitelist, ), ) def _simulate_installation_of( - to_install: List[InstallRequirement], package_set: PackageSet -) -> Set[NormalizedName]: + to_install: list[InstallRequirement], package_set: PackageSet, +) -> set[NormalizedName]: """Computes the version of packages after installing to_install.""" # Keep track of packages that were installed installed = set() @@ -133,8 +140,8 @@ def _simulate_installation_of( def _create_whitelist( - would_be_installed: Set[NormalizedName], package_set: PackageSet -) -> Set[NormalizedName]: + would_be_installed: set[NormalizedName], package_set: PackageSet, +) -> set[NormalizedName]: packages_affected = set(would_be_installed) for package_name in package_set: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/freeze.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/freeze.py index 4565540..81244ee 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/freeze.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/freeze.py @@ -1,38 +1,46 @@ +from __future__ import annotations + import collections import logging import os -from typing import Container, Dict, Iterable, Iterator, List, NamedTuple, Optional, Set +from typing import Container +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import List +from typing import NamedTuple +from typing import Optional +from typing import Set -from pip._vendor.packaging.utils import canonicalize_name -from pip._vendor.packaging.version import Version - -from pip._internal.exceptions import BadCommand, InstallationError -from pip._internal.metadata import BaseDistribution, get_environment -from pip._internal.req.constructors import ( - install_req_from_editable, - install_req_from_line, -) +from pip._internal.exceptions import BadCommand +from pip._internal.exceptions import InstallationError +from pip._internal.metadata import BaseDistribution +from pip._internal.metadata import get_environment +from pip._internal.req.constructors import install_req_from_editable +from pip._internal.req.constructors import install_req_from_line from pip._internal.req.req_file import COMMENT_RE from pip._internal.utils.direct_url_helpers import direct_url_as_pep440_direct_reference +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version logger = logging.getLogger(__name__) class _EditableInfo(NamedTuple): requirement: str - comments: List[str] + comments: list[str] def freeze( - requirement: Optional[List[str]] = None, + requirement: list[str] | None = None, local_only: bool = False, user_only: bool = False, - paths: Optional[List[str]] = None, + paths: list[str] | None = None, isolated: bool = False, exclude_editable: bool = False, skip: Container[str] = (), ) -> Iterator[str]: - installations: Dict[str, FrozenRequirement] = {} + installations: dict[str, FrozenRequirement] = {} dists = get_environment(paths).iter_installed_distributions( local_only=local_only, @@ -50,30 +58,30 @@ def freeze( # should only be emitted once, even if the same option is in multiple # requirements files, so we need to keep track of what has been emitted # so that we don't emit it again if it's seen again - emitted_options: Set[str] = set() + emitted_options: set[str] = set() # keep track of which files a requirement is in so that we can # give an accurate warning if a requirement appears multiple times. - req_files: Dict[str, List[str]] = collections.defaultdict(list) + req_files: dict[str, list[str]] = collections.defaultdict(list) for req_file_path in requirement: with open(req_file_path) as req_file: for line in req_file: if ( - not line.strip() - or line.strip().startswith("#") - or line.startswith( + not line.strip() or + line.strip().startswith('#') or + line.startswith( ( - "-r", - "--requirement", - "-f", - "--find-links", - "-i", - "--index-url", - "--pre", - "--trusted-host", - "--process-dependency-links", - "--extra-index-url", - "--use-feature", - ) + '-r', + '--requirement', + '-f', + '--find-links', + '-i', + '--index-url', + '--pre', + '--trusted-host', + '--process-dependency-links', + '--extra-index-url', + '--use-feature', + ), ) ): line = line.rstrip() @@ -82,31 +90,31 @@ def freeze( yield line continue - if line.startswith("-e") or line.startswith("--editable"): - if line.startswith("-e"): + if line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): line = line[2:].strip() else: - line = line[len("--editable") :].strip().lstrip("=") + line = line[len('--editable'):].strip().lstrip('=') line_req = install_req_from_editable( line, isolated=isolated, ) else: line_req = install_req_from_line( - COMMENT_RE.sub("", line).strip(), + COMMENT_RE.sub('', line).strip(), isolated=isolated, ) if not line_req.name: logger.info( - "Skipping line in requirement file [%s] because " + 'Skipping line in requirement file [%s] because ' "it's not clear what it would install: %s", req_file_path, line.strip(), ) logger.info( - " (add #egg=PackageName to the URL to avoid" - " this warning)" + ' (add #egg=PackageName to the URL to avoid' + ' this warning)', ) else: line_req_canonical_name = canonicalize_name(line_req.name) @@ -115,10 +123,10 @@ def freeze( # but has been processed already if not req_files[line_req.name]: logger.warning( - "Requirement file [%s] contains %s, but " - "package %r is not installed", + 'Requirement file [%s] contains %s, but ' + 'package %r is not installed', req_file_path, - COMMENT_RE.sub("", line).strip(), + COMMENT_RE.sub('', line).strip(), line_req.name, ) else: @@ -133,12 +141,12 @@ def freeze( for name, files in req_files.items(): if len(files) > 1: logger.warning( - "Requirement %s included multiple times [%s]", + 'Requirement %s included multiple times [%s]', name, - ", ".join(sorted(set(files))), + ', '.join(sorted(set(files))), ) - yield ("## The following requirements were added by pip freeze:") + yield ('## The following requirements were added by pip freeze:') for installation in sorted(installations.values(), key=lambda x: x.name.lower()): if installation.canonical_name not in skip: yield str(installation).rstrip() @@ -146,8 +154,8 @@ def freeze( def _format_as_name_version(dist: BaseDistribution) -> str: if isinstance(dist.version, Version): - return f"{dist.raw_name}=={dist.version}" - return f"{dist.raw_name}==={dist.version}" + return f'{dist.raw_name}=={dist.version}' + return f'{dist.raw_name}==={dist.version}' def _get_editable_info(dist: BaseDistribution) -> _EditableInfo: @@ -172,7 +180,7 @@ def _get_editable_info(dist: BaseDistribution) -> _EditableInfo: ) return _EditableInfo( requirement=location, - comments=[f"# Editable install with no version control ({display})"], + comments=[f'# Editable install with no version control ({display})'], ) vcs_name = type(vcs_backend).__name__ @@ -183,36 +191,36 @@ def _get_editable_info(dist: BaseDistribution) -> _EditableInfo: display = _format_as_name_version(dist) return _EditableInfo( requirement=location, - comments=[f"# Editable {vcs_name} install with no remote ({display})"], + comments=[f'# Editable {vcs_name} install with no remote ({display})'], ) except RemoteNotValidError as ex: display = _format_as_name_version(dist) return _EditableInfo( requirement=location, comments=[ - f"# Editable {vcs_name} install ({display}) with either a deleted " - f"local remote or invalid URI:", + f'# Editable {vcs_name} install ({display}) with either a deleted ' + f'local remote or invalid URI:', f"# '{ex.url}'", ], ) except BadCommand: logger.warning( - "cannot determine version of editable source in %s " - "(%s command not found in path)", + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', location, vcs_backend.name, ) return _EditableInfo(requirement=location, comments=[]) except InstallationError as exc: - logger.warning("Error when trying to get requirement for VCS system %s", exc) + logger.warning('Error when trying to get requirement for VCS system %s', exc) else: return _EditableInfo(requirement=req, comments=[]) - logger.warning("Could not determine repository location of %s", location) + logger.warning('Could not determine repository location of %s', location) return _EditableInfo( requirement=location, - comments=["## !! Could not determine repository location"], + comments=['## !! Could not determine repository location'], ) @@ -231,7 +239,7 @@ class FrozenRequirement: self.comments = comments @classmethod - def from_dist(cls, dist: BaseDistribution) -> "FrozenRequirement": + def from_dist(cls, dist: BaseDistribution) -> FrozenRequirement: editable = dist.editable if editable: req, comments = _get_editable_info(dist) @@ -250,5 +258,5 @@ class FrozenRequirement: def __str__(self) -> str: req = self.req if self.editable: - req = f"-e {req}" - return "\n".join(list(self.comments) + [str(req)]) + "\n" + req = f'-e {req}' + return '\n'.join(list(self.comments) + [str(req)]) + '\n' diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/__init__.py index 24d6a5d..e04cd3f 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/__init__.py @@ -1,2 +1,3 @@ """For modules related to installing packages. """ +from __future__ import annotations diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/editable_legacy.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/editable_legacy.py index bb548cd..110934e 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/editable_legacy.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/editable_legacy.py @@ -1,7 +1,11 @@ """Legacy editable installation process, i.e. `setup.py develop`. """ +from __future__ import annotations + import logging -from typing import List, Optional, Sequence +from typing import List +from typing import Optional +from typing import Sequence from pip._internal.build_env import BuildEnvironment from pip._internal.utils.logging import indent_log @@ -12,10 +16,10 @@ logger = logging.getLogger(__name__) def install_editable( - install_options: List[str], + install_options: list[str], global_options: Sequence[str], - prefix: Optional[str], - home: Optional[str], + prefix: str | None, + home: str | None, use_user_site: bool, name: str, setup_py_path: str, @@ -26,7 +30,7 @@ def install_editable( """Install a package in editable mode. Most arguments are pass-through to setuptools. """ - logger.info("Running setup.py develop for %s", name) + logger.info('Running setup.py develop for %s', name) args = make_setuptools_develop_args( setup_py_path, @@ -42,6 +46,6 @@ def install_editable( with build_env: call_subprocess( args, - command_desc="python setup.py develop", + command_desc='python setup.py develop', cwd=unpacked_source_directory, ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/legacy.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/legacy.py index 5b7ef90..b46eb0d 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/legacy.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/legacy.py @@ -1,13 +1,17 @@ """Legacy installation process, i.e. `setup.py install`. """ +from __future__ import annotations import logging import os -from distutils.util import change_root -from typing import List, Optional, Sequence +from typing import List +from typing import Optional +from typing import Sequence +from distutils.util import change_root from pip._internal.build_env import BuildEnvironment -from pip._internal.exceptions import InstallationError, LegacyInstallFailure +from pip._internal.exceptions import InstallationError +from pip._internal.exceptions import LegacyInstallFailure from pip._internal.models.scheme import Scheme from pip._internal.utils.misc import ensure_dir from pip._internal.utils.setuptools_build import make_setuptools_install_args @@ -18,8 +22,8 @@ logger = logging.getLogger(__name__) def write_installed_files_from_setuptools_record( - record_lines: List[str], - root: Optional[str], + record_lines: list[str], + root: str | None, req_description: str, ) -> None: def prepend_root(path: str) -> str: @@ -30,14 +34,14 @@ def write_installed_files_from_setuptools_record( for line in record_lines: directory = os.path.dirname(line) - if directory.endswith(".egg-info"): + if directory.endswith('.egg-info'): egg_info_dir = prepend_root(directory) break else: message = ( - "{} did not indicate that it installed an " - ".egg-info directory. Only setup.py projects " - "generating .egg-info directories are supported." + '{} did not indicate that it installed an ' + '.egg-info directory. Only setup.py projects ' + 'generating .egg-info directories are supported.' ).format(req_description) raise InstallationError(message) @@ -49,17 +53,17 @@ def write_installed_files_from_setuptools_record( new_lines.append(os.path.relpath(prepend_root(filename), egg_info_dir)) new_lines.sort() ensure_dir(egg_info_dir) - inst_files_path = os.path.join(egg_info_dir, "installed-files.txt") - with open(inst_files_path, "w") as f: - f.write("\n".join(new_lines) + "\n") + inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') + with open(inst_files_path, 'w') as f: + f.write('\n'.join(new_lines) + '\n') def install( - install_options: List[str], + install_options: list[str], global_options: Sequence[str], - root: Optional[str], - home: Optional[str], - prefix: Optional[str], + root: str | None, + home: str | None, + prefix: str | None, use_user_site: bool, pycompile: bool, scheme: Scheme, @@ -73,9 +77,9 @@ def install( header_dir = scheme.headers - with TempDirectory(kind="record") as temp_dir: + with TempDirectory(kind='record') as temp_dir: try: - record_filename = os.path.join(temp_dir.path, "install-record.txt") + record_filename = os.path.join(temp_dir.path, 'install-record.txt') install_args = make_setuptools_install_args( setup_py_path, global_options=global_options, @@ -91,7 +95,7 @@ def install( ) runner = runner_with_spinner_message( - f"Running setup.py install for {req_name}" + f'Running setup.py install for {req_name}', ) with build_env: runner( @@ -100,7 +104,7 @@ def install( ) if not os.path.exists(record_filename): - logger.debug("Record file %s not found", record_filename) + logger.debug('Record file %s not found', record_filename) # Signal to the caller that we didn't install the new package return False diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/wheel.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/wheel.py index e191b13..fce1c15 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/wheel.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/install/wheel.py @@ -1,5 +1,6 @@ """Support for installing and building the "wheel" binary package format. """ +from __future__ import annotations import collections import compileall @@ -14,55 +15,57 @@ import sys import warnings from base64 import urlsafe_b64encode from email.message import Message -from itertools import chain, filterfalse, starmap -from typing import ( - IO, - TYPE_CHECKING, - Any, - BinaryIO, - Callable, - Dict, - Iterable, - Iterator, - List, - NewType, - Optional, - Sequence, - Set, - Tuple, - Union, - cast, -) -from zipfile import ZipFile, ZipInfo - -from pip._vendor.distlib.scripts import ScriptMaker -from pip._vendor.distlib.util import get_export_entry -from pip._vendor.packaging.utils import canonicalize_name +from itertools import chain +from itertools import filterfalse +from itertools import starmap +from typing import Any +from typing import BinaryIO +from typing import Callable +from typing import cast +from typing import Dict +from typing import IO +from typing import Iterable +from typing import Iterator +from typing import List +from typing import NewType +from typing import Optional +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union +from zipfile import ZipFile +from zipfile import ZipInfo from pip._internal.exceptions import InstallationError from pip._internal.locations import get_major_minor_version -from pip._internal.metadata import ( - BaseDistribution, - FilesystemWheel, - get_wheel_distribution, -) -from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl -from pip._internal.models.scheme import SCHEME_KEYS, Scheme -from pip._internal.utils.filesystem import adjacent_tmp_file, replace -from pip._internal.utils.misc import captured_stdout, ensure_dir, hash_file, partition -from pip._internal.utils.unpacking import ( - current_umask, - is_within_directory, - set_extracted_file_to_default_mode_plus_executable, - zip_item_is_executable, -) +from pip._internal.metadata import BaseDistribution +from pip._internal.metadata import FilesystemWheel +from pip._internal.metadata import get_wheel_distribution +from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME +from pip._internal.models.direct_url import DirectUrl +from pip._internal.models.scheme import Scheme +from pip._internal.models.scheme import SCHEME_KEYS +from pip._internal.utils.filesystem import adjacent_tmp_file +from pip._internal.utils.filesystem import replace +from pip._internal.utils.misc import captured_stdout +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.misc import hash_file +from pip._internal.utils.misc import partition +from pip._internal.utils.unpacking import current_umask +from pip._internal.utils.unpacking import is_within_directory +from pip._internal.utils.unpacking import set_extracted_file_to_default_mode_plus_executable +from pip._internal.utils.unpacking import zip_item_is_executable from pip._internal.utils.wheel import parse_wheel +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor.distlib.util import get_export_entry +from pip._vendor.packaging.utils import canonicalize_name if TYPE_CHECKING: from typing import Protocol class File(Protocol): - src_record_path: "RecordPath" + src_record_path: RecordPath dest_path: str changed: bool @@ -72,22 +75,22 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) -RecordPath = NewType("RecordPath", str) +RecordPath = NewType('RecordPath', str) InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] -def rehash(path: str, blocksize: int = 1 << 20) -> Tuple[str, str]: +def rehash(path: str, blocksize: int = 1 << 20) -> tuple[str, str]: """Return (encoded_digest, length) for path using hashlib.sha256()""" h, length = hash_file(path, blocksize) - digest = "sha256=" + urlsafe_b64encode(h.digest()).decode("latin1").rstrip("=") + digest = 'sha256=' + urlsafe_b64encode(h.digest()).decode('latin1').rstrip('=') return (digest, str(length)) -def csv_io_kwargs(mode: str) -> Dict[str, Any]: +def csv_io_kwargs(mode: str) -> dict[str, Any]: """Return keyword arguments to properly open a CSV file in the given mode. """ - return {"mode": mode, "newline": "", "encoding": "utf-8"} + return {'mode': mode, 'newline': '', 'encoding': 'utf-8'} def fix_script(path: str) -> bool: @@ -97,35 +100,35 @@ def fix_script(path: str) -> bool: # XXX RECORD hashes will need to be updated assert os.path.isfile(path) - with open(path, "rb") as script: + with open(path, 'rb') as script: firstline = script.readline() - if not firstline.startswith(b"#!python"): + if not firstline.startswith(b'#!python'): return False exename = sys.executable.encode(sys.getfilesystemencoding()) - firstline = b"#!" + exename + os.linesep.encode("ascii") + firstline = b'#!' + exename + os.linesep.encode('ascii') rest = script.read() - with open(path, "wb") as script: + with open(path, 'wb') as script: script.write(firstline) script.write(rest) return True def wheel_root_is_purelib(metadata: Message) -> bool: - return metadata.get("Root-Is-Purelib", "").lower() == "true" + return metadata.get('Root-Is-Purelib', '').lower() == 'true' -def get_entrypoints(dist: BaseDistribution) -> Tuple[Dict[str, str], Dict[str, str]]: +def get_entrypoints(dist: BaseDistribution) -> tuple[dict[str, str], dict[str, str]]: console_scripts = {} gui_scripts = {} for entry_point in dist.iter_entry_points(): - if entry_point.group == "console_scripts": + if entry_point.group == 'console_scripts': console_scripts[entry_point.name] = entry_point.value - elif entry_point.group == "gui_scripts": + elif entry_point.group == 'gui_scripts': gui_scripts[entry_point.name] = entry_point.value return console_scripts, gui_scripts -def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]: +def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> str | None: """Determine if any scripts are not on PATH and format a warning. Returns a warning message if one or more scripts are not on PATH, otherwise None. @@ -134,7 +137,7 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]: return None # Group scripts by the path they were installed in - grouped_by_dir: Dict[str, Set[str]] = collections.defaultdict(set) + grouped_by_dir: dict[str, set[str]] = collections.defaultdict(set) for destfile in scripts: parent_dir = os.path.dirname(destfile) script_name = os.path.basename(destfile) @@ -143,12 +146,12 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]: # We don't want to warn for directories that are on PATH. not_warn_dirs = [ os.path.normcase(i).rstrip(os.sep) - for i in os.environ.get("PATH", "").split(os.pathsep) + for i in os.environ.get('PATH', '').split(os.pathsep) ] # If an executable sits with sys.executable, we don't warn for it. # This covers the case of venv invocations without activating the venv. not_warn_dirs.append(os.path.normcase(os.path.dirname(sys.executable))) - warn_for: Dict[str, Set[str]] = { + warn_for: dict[str, set[str]] = { parent_dir: scripts for parent_dir, scripts in grouped_by_dir.items() if os.path.normcase(parent_dir) not in not_warn_dirs @@ -159,47 +162,47 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]: # Format a message msg_lines = [] for parent_dir, dir_scripts in warn_for.items(): - sorted_scripts: List[str] = sorted(dir_scripts) + sorted_scripts: list[str] = sorted(dir_scripts) if len(sorted_scripts) == 1: - start_text = "script {} is".format(sorted_scripts[0]) + start_text = f'script {sorted_scripts[0]} is' else: - start_text = "scripts {} are".format( - ", ".join(sorted_scripts[:-1]) + " and " + sorted_scripts[-1] + start_text = 'scripts {} are'.format( + ', '.join(sorted_scripts[:-1]) + ' and ' + sorted_scripts[-1], ) msg_lines.append( "The {} installed in '{}' which is not on PATH.".format( - start_text, parent_dir - ) + start_text, parent_dir, + ), ) last_line_fmt = ( - "Consider adding {} to PATH or, if you prefer " - "to suppress this warning, use --no-warn-script-location." + 'Consider adding {} to PATH or, if you prefer ' + 'to suppress this warning, use --no-warn-script-location.' ) if len(msg_lines) == 1: - msg_lines.append(last_line_fmt.format("this directory")) + msg_lines.append(last_line_fmt.format('this directory')) else: - msg_lines.append(last_line_fmt.format("these directories")) + msg_lines.append(last_line_fmt.format('these directories')) # Add a note if any directory starts with ~ warn_for_tilde = any( - i[0] == "~" for i in os.environ.get("PATH", "").split(os.pathsep) if i + i[0] == '~' for i in os.environ.get('PATH', '').split(os.pathsep) if i ) if warn_for_tilde: tilde_warning_msg = ( - "NOTE: The current PATH contains path(s) starting with `~`, " - "which may not be expanded by all applications." + 'NOTE: The current PATH contains path(s) starting with `~`, ' + 'which may not be expanded by all applications.' ) msg_lines.append(tilde_warning_msg) # Returns the formatted multiline message - return "\n".join(msg_lines) + return '\n'.join(msg_lines) def _normalized_outrows( outrows: Iterable[InstalledCSVRow], -) -> List[Tuple[str, str, str]]: +) -> list[tuple[str, str, str]]: """Normalize the given rows of a RECORD file. Items in each row are converted into str. Rows are then sorted to make @@ -227,52 +230,52 @@ def _record_to_fs_path(record_path: RecordPath) -> str: return record_path -def _fs_to_record_path(path: str, relative_to: Optional[str] = None) -> RecordPath: +def _fs_to_record_path(path: str, relative_to: str | None = None) -> RecordPath: if relative_to is not None: # On Windows, do not handle relative paths if they belong to different # logical disks if ( - os.path.splitdrive(path)[0].lower() - == os.path.splitdrive(relative_to)[0].lower() + os.path.splitdrive(path)[0].lower() == + os.path.splitdrive(relative_to)[0].lower() ): path = os.path.relpath(path, relative_to) - path = path.replace(os.path.sep, "/") - return cast("RecordPath", path) + path = path.replace(os.path.sep, '/') + return cast('RecordPath', path) def get_csv_rows_for_installed( - old_csv_rows: List[List[str]], - installed: Dict[RecordPath, RecordPath], - changed: Set[RecordPath], - generated: List[str], + old_csv_rows: list[list[str]], + installed: dict[RecordPath, RecordPath], + changed: set[RecordPath], + generated: list[str], lib_dir: str, -) -> List[InstalledCSVRow]: +) -> list[InstalledCSVRow]: """ :param installed: A map from archive RECORD path to installation RECORD path. """ - installed_rows: List[InstalledCSVRow] = [] + installed_rows: list[InstalledCSVRow] = [] for row in old_csv_rows: if len(row) > 3: - logger.warning("RECORD line has more than three elements: %s", row) - old_record_path = cast("RecordPath", row[0]) + logger.warning('RECORD line has more than three elements: %s', row) + old_record_path = cast('RecordPath', row[0]) new_record_path = installed.pop(old_record_path, old_record_path) if new_record_path in changed: digest, length = rehash(_record_to_fs_path(new_record_path)) else: - digest = row[1] if len(row) > 1 else "" - length = row[2] if len(row) > 2 else "" + digest = row[1] if len(row) > 1 else '' + length = row[2] if len(row) > 2 else '' installed_rows.append((new_record_path, digest, length)) for f in generated: path = _fs_to_record_path(f, lib_dir) digest, length = rehash(f) installed_rows.append((path, digest, length)) for installed_record_path in installed.values(): - installed_rows.append((installed_record_path, "", "")) + installed_rows.append((installed_record_path, '', '')) return installed_rows -def get_console_script_specs(console: Dict[str, str]) -> List[str]: +def get_console_script_specs(console: dict[str, str]) -> list[str]: """ Given the mapping from entrypoint name to callable, return the relevant console script specs. @@ -315,47 +318,47 @@ def get_console_script_specs(console: Dict[str, str]) -> List[str]: # DEFAULT # - The default behavior is to install pip, pipX, pipX.Y, easy_install # and easy_install-X.Y. - pip_script = console.pop("pip", None) + pip_script = console.pop('pip', None) if pip_script: - if "ENSUREPIP_OPTIONS" not in os.environ: - scripts_to_generate.append("pip = " + pip_script) + if 'ENSUREPIP_OPTIONS' not in os.environ: + scripts_to_generate.append('pip = ' + pip_script) - if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + if os.environ.get('ENSUREPIP_OPTIONS', '') != 'altinstall': scripts_to_generate.append( - "pip{} = {}".format(sys.version_info[0], pip_script) + f'pip{sys.version_info[0]} = {pip_script}', ) - scripts_to_generate.append(f"pip{get_major_minor_version()} = {pip_script}") + scripts_to_generate.append(f'pip{get_major_minor_version()} = {pip_script}') # Delete any other versioned pip entry points - pip_ep = [k for k in console if re.match(r"pip(\d(\.\d)?)?$", k)] + pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)] for k in pip_ep: del console[k] - easy_install_script = console.pop("easy_install", None) + easy_install_script = console.pop('easy_install', None) if easy_install_script: - if "ENSUREPIP_OPTIONS" not in os.environ: - scripts_to_generate.append("easy_install = " + easy_install_script) + if 'ENSUREPIP_OPTIONS' not in os.environ: + scripts_to_generate.append('easy_install = ' + easy_install_script) scripts_to_generate.append( - "easy_install-{} = {}".format( - get_major_minor_version(), easy_install_script - ) + 'easy_install-{} = {}'.format( + get_major_minor_version(), easy_install_script, + ), ) # Delete any other versioned easy_install entry points easy_install_ep = [ - k for k in console if re.match(r"easy_install(-\d\.\d)?$", k) + k for k in console if re.match(r'easy_install(-\d\.\d)?$', k) ] for k in easy_install_ep: del console[k] # Generate the console entry points specified in the wheel - scripts_to_generate.extend(starmap("{} = {}".format, console.items())) + scripts_to_generate.extend(starmap('{} = {}'.format, console.items())) return scripts_to_generate class ZipBackedFile: def __init__( - self, src_record_path: RecordPath, dest_path: str, zip_file: ZipFile + self, src_record_path: RecordPath, dest_path: str, zip_file: ZipFile, ) -> None: self.src_record_path = src_record_path self.dest_path = dest_path @@ -386,7 +389,7 @@ class ZipBackedFile: zipinfo = self._getinfo() with self._zip_file.open(zipinfo) as f: - with open(self.dest_path, "wb") as dest: + with open(self.dest_path, 'wb') as dest: shutil.copyfileobj(f, dest) if zip_item_is_executable(zipinfo): @@ -394,7 +397,7 @@ class ZipBackedFile: class ScriptFile: - def __init__(self, file: "File") -> None: + def __init__(self, file: File) -> None: self._file = file self.src_record_path = self._file.src_record_path self.dest_path = self._file.dest_path @@ -408,10 +411,10 @@ class ScriptFile: class MissingCallableSuffix(InstallationError): def __init__(self, entry_point: str) -> None: super().__init__( - "Invalid script entry point: {} - A callable " - "suffix is required. Cf https://packaging.python.org/" - "specifications/entry-points/#use-for-scripts for more " - "information.".format(entry_point) + 'Invalid script entry point: {} - A callable ' + 'suffix is required. Cf https://packaging.python.org/' + 'specifications/entry-points/#use-for-scripts for more ' + 'information.'.format(entry_point), ) @@ -422,7 +425,7 @@ def _raise_for_invalid_entrypoint(specification: str) -> None: class PipScriptMaker(ScriptMaker): - def make(self, specification: str, options: Dict[str, Any] = None) -> List[str]: + def make(self, specification: str, options: dict[str, Any] = None) -> list[str]: _raise_for_invalid_entrypoint(specification) return super().make(specification, options) @@ -434,7 +437,7 @@ def _install_wheel( scheme: Scheme, pycompile: bool = True, warn_script_location: bool = True, - direct_url: Optional[DirectUrl] = None, + direct_url: DirectUrl | None = None, requested: bool = False, ) -> None: """Install a wheel. @@ -463,12 +466,12 @@ def _install_wheel( # installed = files copied from the wheel to the destination # changed = files changed while installing (scripts #! line typically) # generated = files newly generated during the install (script wrappers) - installed: Dict[RecordPath, RecordPath] = {} - changed: Set[RecordPath] = set() - generated: List[str] = [] + installed: dict[RecordPath, RecordPath] = {} + changed: set[RecordPath] = set() + generated: list[str] = [] def record_installed( - srcfile: RecordPath, destfile: str, modified: bool = False + srcfile: RecordPath, destfile: str, modified: bool = False, ) -> None: """Map archive RECORD paths to installation RECORD paths.""" newpath = _fs_to_record_path(destfile, lib_dir) @@ -477,22 +480,22 @@ def _install_wheel( changed.add(_fs_to_record_path(destfile)) def is_dir_path(path: RecordPath) -> bool: - return path.endswith("/") + return path.endswith('/') def assert_no_path_traversal(dest_dir_path: str, target_path: str) -> None: if not is_within_directory(dest_dir_path, target_path): message = ( - "The wheel {!r} has a file {!r} trying to install" - " outside the target directory {!r}" + 'The wheel {!r} has a file {!r} trying to install' + ' outside the target directory {!r}' ) raise InstallationError( - message.format(wheel_path, target_path, dest_dir_path) + message.format(wheel_path, target_path, dest_dir_path), ) def root_scheme_file_maker( - zip_file: ZipFile, dest: str - ) -> Callable[[RecordPath], "File"]: - def make_root_scheme_file(record_path: RecordPath) -> "File": + zip_file: ZipFile, dest: str, + ) -> Callable[[RecordPath], File]: + def make_root_scheme_file(record_path: RecordPath) -> File: normed_path = os.path.normpath(record_path) dest_path = os.path.join(dest, normed_path) assert_no_path_traversal(dest, dest_path) @@ -501,17 +504,17 @@ def _install_wheel( return make_root_scheme_file def data_scheme_file_maker( - zip_file: ZipFile, scheme: Scheme - ) -> Callable[[RecordPath], "File"]: + zip_file: ZipFile, scheme: Scheme, + ) -> Callable[[RecordPath], File]: scheme_paths = {key: getattr(scheme, key) for key in SCHEME_KEYS} - def make_data_scheme_file(record_path: RecordPath) -> "File": + def make_data_scheme_file(record_path: RecordPath) -> File: normed_path = os.path.normpath(record_path) try: _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2) except ValueError: message = ( - "Unexpected file in {}: {!r}. .data directory contents" + 'Unexpected file in {}: {!r}. .data directory contents' " should be named like: '/'." ).format(wheel_path, record_path) raise InstallationError(message) @@ -519,11 +522,11 @@ def _install_wheel( try: scheme_path = scheme_paths[scheme_key] except KeyError: - valid_scheme_keys = ", ".join(sorted(scheme_paths)) + valid_scheme_keys = ', '.join(sorted(scheme_paths)) message = ( - "Unknown scheme key used in {}: {} (for file {!r}). .data" - " directory contents should be in subdirectories named" - " with a valid scheme key ({})" + 'Unknown scheme key used in {}: {} (for file {!r}). .data' + ' directory contents should be in subdirectories named' + ' with a valid scheme key ({})' ).format(wheel_path, scheme_key, record_path, valid_scheme_keys) raise InstallationError(message) @@ -534,7 +537,7 @@ def _install_wheel( return make_data_scheme_file def is_data_scheme_path(path: RecordPath) -> bool: - return path.split("/", 1)[0].endswith(".data") + return path.split('/', 1)[0].endswith('.data') paths = cast(List[RecordPath], wheel_zip.namelist()) file_paths = filterfalse(is_dir_path, paths) @@ -544,11 +547,11 @@ def _install_wheel( files: Iterator[File] = map(make_root_scheme_file, root_scheme_paths) def is_script_scheme_path(path: RecordPath) -> bool: - parts = path.split("/", 2) - return len(parts) > 2 and parts[0].endswith(".data") and parts[1] == "scripts" + parts = path.split('/', 2) + return len(parts) > 2 and parts[0].endswith('.data') and parts[1] == 'scripts' other_scheme_paths, script_scheme_paths = partition( - is_script_scheme_path, data_scheme_paths + is_script_scheme_path, data_scheme_paths, ) make_data_scheme_file = data_scheme_file_maker(wheel_zip, scheme) @@ -562,16 +565,16 @@ def _install_wheel( ) console, gui = get_entrypoints(distribution) - def is_entrypoint_wrapper(file: "File") -> bool: + def is_entrypoint_wrapper(file: File) -> bool: # EP, EP.exe and EP-script.py are scripts generated for # entry point EP by setuptools path = file.dest_path name = os.path.basename(path) - if name.lower().endswith(".exe"): + if name.lower().endswith('.exe'): matchname = name[:-4] - elif name.lower().endswith("-script.py"): + elif name.lower().endswith('-script.py'): matchname = name[:-10] - elif name.lower().endswith(".pya"): + elif name.lower().endswith('.pya'): matchname = name[:-4] else: matchname = name @@ -579,7 +582,7 @@ def _install_wheel( return matchname in console or matchname in gui script_scheme_files: Iterator[File] = map( - make_data_scheme_file, script_scheme_paths + make_data_scheme_file, script_scheme_paths, ) script_scheme_files = filterfalse(is_entrypoint_wrapper, script_scheme_files) script_scheme_files = map(ScriptFile, script_scheme_files) @@ -598,7 +601,7 @@ def _install_wheel( full_installed_path = os.path.join(lib_dir, installed_path) if not os.path.isfile(full_installed_path): continue - if not full_installed_path.endswith(".py"): + if not full_installed_path.endswith('.py'): continue yield full_installed_path @@ -610,14 +613,14 @@ def _install_wheel( if pycompile: with captured_stdout() as stdout: with warnings.catch_warnings(): - warnings.filterwarnings("ignore") + warnings.filterwarnings('ignore') for path in pyc_source_file_paths(): success = compileall.compile_file(path, force=True, quiet=True) if success: pyc_path = pyc_output_path(path) assert os.path.exists(pyc_path) pyc_record_path = cast( - "RecordPath", pyc_path.replace(os.path.sep, "/") + 'RecordPath', pyc_path.replace(os.path.sep, '/'), ) record_installed(pyc_record_path, pyc_path) logger.debug(stdout.getvalue()) @@ -631,7 +634,7 @@ def _install_wheel( # Ensure we don't generate any variants for scripts because this is almost # never what somebody wants. # See https://bitbucket.org/pypa/distlib/issue/35/ - maker.variants = {""} + maker.variants = {''} # This is required because otherwise distlib creates scripts that are not # executable. @@ -641,12 +644,12 @@ def _install_wheel( # Generate the console and GUI entry points specified in the wheel scripts_to_generate = get_console_script_specs(console) - gui_scripts_to_generate = list(starmap("{} = {}".format, gui.items())) + gui_scripts_to_generate = list(starmap('{} = {}'.format, gui.items())) generated_console_scripts = maker.make_multiple(scripts_to_generate) generated.extend(generated_console_scripts) - generated.extend(maker.make_multiple(gui_scripts_to_generate, {"gui": True})) + generated.extend(maker.make_multiple(gui_scripts_to_generate, {'gui': True})) if warn_script_location: msg = message_about_scripts_not_on_PATH(generated_console_scripts) @@ -665,26 +668,26 @@ def _install_wheel( dest_info_dir = os.path.join(lib_dir, info_dir) # Record pip as the installer - installer_path = os.path.join(dest_info_dir, "INSTALLER") + installer_path = os.path.join(dest_info_dir, 'INSTALLER') with _generate_file(installer_path) as installer_file: - installer_file.write(b"pip\n") + installer_file.write(b'pip\n') generated.append(installer_path) # Record the PEP 610 direct URL reference if direct_url is not None: direct_url_path = os.path.join(dest_info_dir, DIRECT_URL_METADATA_NAME) with _generate_file(direct_url_path) as direct_url_file: - direct_url_file.write(direct_url.to_json().encode("utf-8")) + direct_url_file.write(direct_url.to_json().encode('utf-8')) generated.append(direct_url_path) # Record the REQUESTED file if requested: - requested_path = os.path.join(dest_info_dir, "REQUESTED") - with open(requested_path, "wb"): + requested_path = os.path.join(dest_info_dir, 'REQUESTED') + with open(requested_path, 'wb'): pass generated.append(requested_path) - record_text = distribution.read_text("RECORD") + record_text = distribution.read_text('RECORD') record_rows = list(csv.reader(record_text.splitlines())) rows = get_csv_rows_for_installed( @@ -696,12 +699,12 @@ def _install_wheel( ) # Record details of all files installed - record_path = os.path.join(dest_info_dir, "RECORD") + record_path = os.path.join(dest_info_dir, 'RECORD') - with _generate_file(record_path, **csv_io_kwargs("w")) as record_file: + with _generate_file(record_path, **csv_io_kwargs('w')) as record_file: # Explicitly cast to typing.IO[str] as a workaround for the mypy error: # "writer" has incompatible type "BinaryIO"; expected "_Writer" - writer = csv.writer(cast("IO[str]", record_file)) + writer = csv.writer(cast('IO[str]', record_file)) writer.writerows(_normalized_outrows(rows)) @@ -710,7 +713,7 @@ def req_error_context(req_description: str) -> Iterator[None]: try: yield except InstallationError as e: - message = "For req: {}. {}".format(req_description, e.args[0]) + message = f'For req: {req_description}. {e.args[0]}' raise InstallationError(message) from e @@ -721,7 +724,7 @@ def install_wheel( req_description: str, pycompile: bool = True, warn_script_location: bool = True, - direct_url: Optional[DirectUrl] = None, + direct_url: DirectUrl | None = None, requested: bool = False, ) -> None: with ZipFile(wheel_path, allowZip64=True) as z: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/operations/prepare.py b/.venv/lib/python3.10/site-packages/pip/_internal/operations/prepare.py index a726f03..6fac751 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/operations/prepare.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/operations/prepare.py @@ -1,47 +1,50 @@ """Prepares a distribution for installation """ - # The following comment should be removed at some point in the future. # mypy: strict-optional=False +from __future__ import annotations import logging import mimetypes import os import shutil -from typing import Dict, Iterable, List, Optional - -from pip._vendor.packaging.utils import canonicalize_name +from typing import Dict +from typing import Iterable +from typing import List +from typing import Optional from pip._internal.distributions import make_distribution_for_install_requirement from pip._internal.distributions.installed import InstalledDistribution -from pip._internal.exceptions import ( - DirectoryUrlHashUnsupported, - HashMismatch, - HashUnpinned, - InstallationError, - NetworkConnectionError, - PreviousBuildDirError, - VcsHashUnsupported, -) +from pip._internal.exceptions import DirectoryUrlHashUnsupported +from pip._internal.exceptions import HashMismatch +from pip._internal.exceptions import HashUnpinned +from pip._internal.exceptions import InstallationError +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.exceptions import PreviousBuildDirError +from pip._internal.exceptions import VcsHashUnsupported from pip._internal.index.package_finder import PackageFinder from pip._internal.metadata import BaseDistribution from pip._internal.models.link import Link from pip._internal.models.wheel import Wheel -from pip._internal.network.download import BatchDownloader, Downloader -from pip._internal.network.lazy_wheel import ( - HTTPRangeRequestUnsupported, - dist_from_wheel_url, -) +from pip._internal.network.download import BatchDownloader +from pip._internal.network.download import Downloader +from pip._internal.network.lazy_wheel import dist_from_wheel_url +from pip._internal.network.lazy_wheel import HTTPRangeRequestUnsupported from pip._internal.network.session import PipSession from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_tracker import RequirementTracker from pip._internal.utils.filesystem import copy2_fixed -from pip._internal.utils.hashes import Hashes, MissingHashes +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.hashes import MissingHashes from pip._internal.utils.logging import indent_log -from pip._internal.utils.misc import display_path, hide_url, is_installable_dir, rmtree +from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import hide_url +from pip._internal.utils.misc import is_installable_dir +from pip._internal.utils.misc import rmtree from pip._internal.utils.temp_dir import TempDirectory from pip._internal.utils.unpacking import unpack_file from pip._internal.vcs import vcs +from pip._vendor.packaging.utils import canonicalize_name logger = logging.getLogger(__name__) @@ -66,7 +69,7 @@ def unpack_vcs_link(link: Link, location: str, verbosity: int) -> None: class File: - def __init__(self, path: str, content_type: Optional[str]) -> None: + def __init__(self, path: str, content_type: str | None) -> None: self.path = path if content_type is None: self.content_type = mimetypes.guess_type(path)[0] @@ -77,10 +80,10 @@ class File: def get_http_url( link: Link, download: Downloader, - download_dir: Optional[str] = None, - hashes: Optional[Hashes] = None, + download_dir: str | None = None, + hashes: Hashes | None = None, ) -> File: - temp_dir = TempDirectory(kind="unpack", globally_managed=True) + temp_dir = TempDirectory(kind='unpack', globally_managed=True) # If a download dir is specified, is the file already downloaded there? already_downloaded_path = None if download_dir: @@ -123,14 +126,14 @@ def _copy_source_tree(source: str, target: str) -> None: target_basename = os.path.basename(target_abspath) target_dirname = os.path.dirname(target_abspath) - def ignore(d: str, names: List[str]) -> List[str]: - skipped: List[str] = [] + def ignore(d: str, names: list[str]) -> list[str]: + skipped: list[str] = [] if d == source: # Pulling in those directories can potentially be very slow, # exclude the following directories if they appear in the top # level dir (and only it). # See discussion at https://github.com/pypa/pip/pull/6770 - skipped += [".tox", ".nox"] + skipped += ['.tox', '.nox'] if os.path.abspath(d) == target_dirname: # Prevent an infinite recursion if the target is in source. # This can happen when TMPDIR is set to ${PWD}/... @@ -148,7 +151,7 @@ def _copy_source_tree(source: str, target: str) -> None: def get_file_url( - link: Link, download_dir: Optional[str] = None, hashes: Optional[Hashes] = None + link: Link, download_dir: str | None = None, hashes: Hashes | None = None, ) -> File: """Get file and optionally check its hash.""" # If a download dir is specified, is the file already there and valid? @@ -176,9 +179,9 @@ def unpack_url( location: str, download: Downloader, verbosity: int, - download_dir: Optional[str] = None, - hashes: Optional[Hashes] = None, -) -> Optional[File]: + download_dir: str | None = None, + hashes: Hashes | None = None, +) -> File | None: """Unpack link into location, downloading if required. :param hashes: A Hashes object, one of whose embedded hashes must match, @@ -227,8 +230,8 @@ def unpack_url( def _check_download_dir( - link: Link, download_dir: str, hashes: Optional[Hashes] -) -> Optional[str]: + link: Link, download_dir: str, hashes: Hashes | None, +) -> str | None: """Check download_dir for previously downloaded file with correct hash If a correct file is found return its path else None """ @@ -238,13 +241,13 @@ def _check_download_dir( return None # If already downloaded, does its hash match? - logger.info("File was already downloaded %s", download_path) + logger.info('File was already downloaded %s', download_path) if hashes: try: hashes.check_against_path(download_path) except HashMismatch: logger.warning( - "Previously-downloaded file %s has bad hash. Re-downloading.", + 'Previously-downloaded file %s has bad hash. Re-downloading.', download_path, ) os.unlink(download_path) @@ -258,7 +261,7 @@ class RequirementPreparer: def __init__( self, build_dir: str, - download_dir: Optional[str], + download_dir: str | None, src_dir: str, build_isolation: bool, req_tracker: RequirementTracker, @@ -304,18 +307,18 @@ class RequirementPreparer: self.in_tree_build = in_tree_build # Memoized downloaded files, as mapping of url: path. - self._downloaded: Dict[str, str] = {} + self._downloaded: dict[str, str] = {} # Previous "header" printed for a link-based InstallRequirement - self._previous_requirement_header = ("", "") + self._previous_requirement_header = ('', '') def _log_preparing_link(self, req: InstallRequirement) -> None: """Provide context for the requirement being prepared.""" if req.link.is_file and not req.original_link_is_in_wheel_cache: - message = "Processing %s" + message = 'Processing %s' information = str(display_path(req.link.file_path)) else: - message = "Collecting %s" + message = 'Collecting %s' information = str(req.req or req) if (message, information) != self._previous_requirement_header: @@ -324,10 +327,10 @@ class RequirementPreparer: if req.original_link_is_in_wheel_cache: with indent_log(): - logger.info("Using cached %s", req.link.filename) + logger.info('Using cached %s', req.link.filename) def _ensure_link_req_src_dir( - self, req: InstallRequirement, parallel_builds: bool + self, req: InstallRequirement, parallel_builds: bool, ) -> None: """Ensure source_dir of a linked InstallRequirement.""" # Since source_dir is only set for editable requirements. @@ -357,10 +360,10 @@ class RequirementPreparer: if is_installable_dir(req.source_dir): raise PreviousBuildDirError( "pip can't proceed with requirements '{}' due to a" - "pre-existing build directory ({}). This is likely " - "due to a previous installation that failed . pip is " - "being responsible and not assuming it can delete this. " - "Please delete it and try again.".format(req, req.source_dir) + 'pre-existing build directory ({}). This is likely ' + 'due to a previous installation that failed . pip is ' + 'being responsible and not assuming it can delete this. ' + 'Please delete it and try again.'.format(req, req.source_dir), ) def _get_linked_req_hashes(self, req: InstallRequirement) -> Hashes: @@ -398,16 +401,16 @@ class RequirementPreparer: def _fetch_metadata_using_lazy_wheel( self, link: Link, - ) -> Optional[BaseDistribution]: + ) -> BaseDistribution | None: """Fetch metadata using lazy wheel, if possible.""" if not self.use_lazy_wheel: return None if self.require_hashes: - logger.debug("Lazy wheel is not used as hash checking is required") + logger.debug('Lazy wheel is not used as hash checking is required') return None if link.is_file or not link.is_wheel: logger.debug( - "Lazy wheel is not used as %r does not points to a remote wheel", + 'Lazy wheel is not used as %r does not points to a remote wheel', link, ) return None @@ -415,15 +418,15 @@ class RequirementPreparer: wheel = Wheel(link.filename) name = canonicalize_name(wheel.name) logger.info( - "Obtaining dependency information from %s %s", + 'Obtaining dependency information from %s %s', name, wheel.version, ) - url = link.url.split("#", 1)[0] + url = link.url.split('#', 1)[0] try: return dist_from_wheel_url(name, url, self._session) except HTTPRangeRequestUnsupported: - logger.debug("%s does not support range requests", url) + logger.debug('%s does not support range requests', url) return None def _complete_partial_requirements( @@ -434,12 +437,12 @@ class RequirementPreparer: """Download any requirements which were only fetched by metadata.""" # Download to a temporary directory. These will be copied over as # needed for downstream 'download', 'wheel', and 'install' commands. - temp_dir = TempDirectory(kind="unpack", globally_managed=True).path + temp_dir = TempDirectory(kind='unpack', globally_managed=True).path # Map each link to the requirement that owns it. This allows us to set # `req.local_file_path` on the appropriate requirement after passing # all the links at once into BatchDownloader. - links_to_fully_download: Dict[Link, InstallRequirement] = {} + links_to_fully_download: dict[Link, InstallRequirement] = {} for req in partially_downloaded_reqs: assert req.link links_to_fully_download[req.link] = req @@ -449,7 +452,7 @@ class RequirementPreparer: temp_dir, ) for link, (filepath, _) in batch_download: - logger.debug("Downloading link %s to %s", link, filepath) + logger.debug('Downloading link %s to %s', link, filepath) req = links_to_fully_download[link] req.local_file_path = filepath @@ -459,7 +462,7 @@ class RequirementPreparer: self._prepare_linked_requirement(req, parallel_builds) def prepare_linked_requirement( - self, req: InstallRequirement, parallel_builds: bool = False + self, req: InstallRequirement, parallel_builds: bool = False, ) -> BaseDistribution: """Prepare a requirement to be obtained from req.link.""" assert req.link @@ -487,7 +490,7 @@ class RequirementPreparer: return self._prepare_linked_requirement(req, parallel_builds) def prepare_linked_requirements_more( - self, reqs: Iterable[InstallRequirement], parallel_builds: bool = False + self, reqs: Iterable[InstallRequirement], parallel_builds: bool = False, ) -> None: """Prepare linked requirements more, if needed.""" reqs = [req for req in reqs if req.needs_more_preparation] @@ -502,7 +505,7 @@ class RequirementPreparer: # Prepare requirements we found were already downloaded for some # reason. The other downloads will be completed separately. - partially_downloaded_reqs: List[InstallRequirement] = [] + partially_downloaded_reqs: list[InstallRequirement] = [] for req in reqs: if req.needs_more_preparation: partially_downloaded_reqs.append(req) @@ -517,7 +520,7 @@ class RequirementPreparer: ) def _prepare_linked_requirement( - self, req: InstallRequirement, parallel_builds: bool + self, req: InstallRequirement, parallel_builds: bool, ) -> BaseDistribution: assert req.link link = req.link @@ -539,8 +542,8 @@ class RequirementPreparer: ) except NetworkConnectionError as exc: raise InstallationError( - "Could not install requirement {} because of HTTP " - "error {} for URL {}".format(req, exc, link) + 'Could not install requirement {} because of HTTP ' + 'error {} for URL {}'.format(req, exc, link), ) else: file_path = self._downloaded[link.url] @@ -572,8 +575,8 @@ class RequirementPreparer: if link.is_existing_dir(): logger.debug( - "Not copying link to destination directory " - "since it is a directory: %s", + 'Not copying link to destination directory ' + 'since it is a directory: %s', link, ) return @@ -585,23 +588,23 @@ class RequirementPreparer: if not os.path.exists(download_location): shutil.copy(req.local_file_path, download_location) download_path = display_path(download_location) - logger.info("Saved %s", download_path) + logger.info('Saved %s', download_path) def prepare_editable_requirement( self, req: InstallRequirement, ) -> BaseDistribution: """Prepare an editable requirement.""" - assert req.editable, "cannot prepare a non-editable req as editable" + assert req.editable, 'cannot prepare a non-editable req as editable' - logger.info("Obtaining %s", req) + logger.info('Obtaining %s', req) with indent_log(): if self.require_hashes: raise InstallationError( - "The editable requirement {} cannot be installed when " - "requiring hashes, because there is no single file to " - "hash.".format(req) + 'The editable requirement {} cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.'.format(req), ) req.ensure_has_source_dir(self.src_dir) req.update_editable() @@ -625,18 +628,18 @@ class RequirementPreparer: """Prepare an already-installed requirement.""" assert req.satisfied_by, "req should have been satisfied but isn't" assert skip_reason is not None, ( - "did not get skip reason skipped but req.satisfied_by " - "is set to {}".format(req.satisfied_by) + 'did not get skip reason skipped but req.satisfied_by ' + 'is set to {}'.format(req.satisfied_by) ) logger.info( - "Requirement %s: %s (%s)", skip_reason, req, req.satisfied_by.version + 'Requirement %s: %s (%s)', skip_reason, req, req.satisfied_by.version, ) with indent_log(): if self.require_hashes: logger.debug( - "Since it is already installed, we are trusting this " - "package without checking its hash. To ensure a " - "completely repeatable environment, install into an " - "empty virtualenv." + 'Since it is already installed, we are trusting this ' + 'package without checking its hash. To ensure a ' + 'completely repeatable environment, install into an ' + 'empty virtualenv.', ) return InstalledDistribution(req).get_metadata_distribution() diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/pyproject.py b/.venv/lib/python3.10/site-packages/pip/_internal/pyproject.py index e183eaf..b574c55 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/pyproject.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/pyproject.py @@ -1,15 +1,17 @@ +from __future__ import annotations + import os from collections import namedtuple -from typing import Any, List, Optional +from typing import Any +from typing import List +from typing import Optional +from pip._internal.exceptions import InstallationError +from pip._internal.exceptions import InvalidPyProjectBuildRequires +from pip._internal.exceptions import MissingPyProjectBuildRequires from pip._vendor import tomli -from pip._vendor.packaging.requirements import InvalidRequirement, Requirement - -from pip._internal.exceptions import ( - InstallationError, - InvalidPyProjectBuildRequires, - MissingPyProjectBuildRequires, -) +from pip._vendor.packaging.requirements import InvalidRequirement +from pip._vendor.packaging.requirements import Requirement def _is_list_of_str(obj: Any) -> bool: @@ -17,17 +19,17 @@ def _is_list_of_str(obj: Any) -> bool: def make_pyproject_path(unpacked_source_directory: str) -> str: - return os.path.join(unpacked_source_directory, "pyproject.toml") + return os.path.join(unpacked_source_directory, 'pyproject.toml') BuildSystemDetails = namedtuple( - "BuildSystemDetails", ["requires", "backend", "check", "backend_path"] + 'BuildSystemDetails', ['requires', 'backend', 'check', 'backend_path'], ) def load_pyproject_toml( - use_pep517: Optional[bool], pyproject_toml: str, setup_py: str, req_name: str -) -> Optional[BuildSystemDetails]: + use_pep517: bool | None, pyproject_toml: str, setup_py: str, req_name: str, +) -> BuildSystemDetails | None: """Load the pyproject.toml file. Parameters: @@ -54,14 +56,14 @@ def load_pyproject_toml( if not has_pyproject and not has_setup: raise InstallationError( - f"{req_name} does not appear to be a Python project: " - f"neither 'setup.py' nor 'pyproject.toml' found." + f'{req_name} does not appear to be a Python project: ' + f"neither 'setup.py' nor 'pyproject.toml' found.", ) if has_pyproject: - with open(pyproject_toml, encoding="utf-8") as f: + with open(pyproject_toml, encoding='utf-8') as f: pp_toml = tomli.loads(f.read()) - build_system = pp_toml.get("build-system") + build_system = pp_toml.get('build-system') else: build_system = None @@ -74,16 +76,16 @@ def load_pyproject_toml( if has_pyproject and not has_setup: if use_pep517 is not None and not use_pep517: raise InstallationError( - "Disabling PEP 517 processing is invalid: " - "project does not have a setup.py" + 'Disabling PEP 517 processing is invalid: ' + 'project does not have a setup.py', ) use_pep517 = True - elif build_system and "build-backend" in build_system: + elif build_system and 'build-backend' in build_system: if use_pep517 is not None and not use_pep517: raise InstallationError( - "Disabling PEP 517 processing is invalid: " - "project specifies a build backend of {} " - "in pyproject.toml".format(build_system["build-backend"]) + 'Disabling PEP 517 processing is invalid: ' + 'project specifies a build backend of {} ' + 'in pyproject.toml'.format(build_system['build-backend']), ) use_pep517 = True @@ -111,8 +113,8 @@ def load_pyproject_toml( # a version of setuptools that supports that backend. build_system = { - "requires": ["setuptools>=40.8.0", "wheel"], - "build-backend": "setuptools.build_meta:__legacy__", + 'requires': ['setuptools>=40.8.0', 'wheel'], + 'build-backend': 'setuptools.build_meta:__legacy__', } # If we're using PEP 517, we have build system information (either @@ -125,15 +127,15 @@ def load_pyproject_toml( # to PEP 518. # Specifying the build-system table but not the requires key is invalid - if "requires" not in build_system: + if 'requires' not in build_system: raise MissingPyProjectBuildRequires(package=req_name) # Error out if requires is not a list of strings - requires = build_system["requires"] + requires = build_system['requires'] if not _is_list_of_str(requires): raise InvalidPyProjectBuildRequires( package=req_name, - reason="It is not a list of strings.", + reason='It is not a list of strings.', ) # Each requirement must be valid as per PEP 508 @@ -143,12 +145,12 @@ def load_pyproject_toml( except InvalidRequirement as error: raise InvalidPyProjectBuildRequires( package=req_name, - reason=f"It contains an invalid requirement: {requirement!r}", + reason=f'It contains an invalid requirement: {requirement!r}', ) from error - backend = build_system.get("build-backend") - backend_path = build_system.get("backend-path", []) - check: List[str] = [] + backend = build_system.get('build-backend') + backend_path = build_system.get('backend-path', []) + check: list[str] = [] if backend is None: # If the user didn't specify a backend, we assume they want to use # the setuptools backend. But we can't be sure they have included @@ -162,7 +164,7 @@ def load_pyproject_toml( # execute setup.py, but never considered needing to mention the build # tools themselves. The original PEP 518 code had a similar check (but # implemented in a different way). - backend = "setuptools.build_meta:__legacy__" - check = ["setuptools>=40.8.0", "wheel"] + backend = 'setuptools.build_meta:__legacy__' + check = ['setuptools>=40.8.0', 'wheel'] return BuildSystemDetails(requires, backend, check, backend_path) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/req/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/req/__init__.py index 70dea27..87ed5b5 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/req/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/req/__init__.py @@ -1,6 +1,12 @@ +from __future__ import annotations + import collections import logging -from typing import Iterator, List, Optional, Sequence, Tuple +from typing import Iterator +from typing import List +from typing import Optional +from typing import Sequence +from typing import Tuple from pip._internal.utils.logging import indent_log @@ -9,10 +15,10 @@ from .req_install import InstallRequirement from .req_set import RequirementSet __all__ = [ - "RequirementSet", - "InstallRequirement", - "parse_requirements", - "install_given_reqs", + 'RequirementSet', + 'InstallRequirement', + 'parse_requirements', + 'install_given_reqs', ] logger = logging.getLogger(__name__) @@ -23,28 +29,28 @@ class InstallationResult: self.name = name def __repr__(self) -> str: - return f"InstallationResult(name={self.name!r})" + return f'InstallationResult(name={self.name!r})' def _validate_requirements( - requirements: List[InstallRequirement], -) -> Iterator[Tuple[str, InstallRequirement]]: + requirements: list[InstallRequirement], +) -> Iterator[tuple[str, InstallRequirement]]: for req in requirements: - assert req.name, f"invalid to-be-installed requirement: {req}" + assert req.name, f'invalid to-be-installed requirement: {req}' yield req.name, req def install_given_reqs( - requirements: List[InstallRequirement], - install_options: List[str], + requirements: list[InstallRequirement], + install_options: list[str], global_options: Sequence[str], - root: Optional[str], - home: Optional[str], - prefix: Optional[str], + root: str | None, + home: str | None, + prefix: str | None, warn_script_location: bool, use_user_site: bool, pycompile: bool, -) -> List[InstallationResult]: +) -> list[InstallationResult]: """ Install everything in the given list. @@ -54,8 +60,8 @@ def install_given_reqs( if to_install: logger.info( - "Installing collected packages: %s", - ", ".join(to_install.keys()), + 'Installing collected packages: %s', + ', '.join(to_install.keys()), ) installed = [] @@ -63,7 +69,7 @@ def install_given_reqs( with indent_log(): for req_name, requirement in to_install.items(): if requirement.should_reinstall: - logger.info("Attempting uninstall: %s", req_name) + logger.info('Attempting uninstall: %s', req_name) with indent_log(): uninstalled_pathset = requirement.uninstall(auto_confirm=True) else: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/req/constructors.py b/.venv/lib/python3.10/site-packages/pip/_internal/req/constructors.py index 25bfb39..e293c4b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/req/constructors.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/req/constructors.py @@ -7,18 +7,21 @@ helps creates for better understandability for the rest of the code. These are meant to be used elsewhere within pip to create instances of InstallRequirement. """ +from __future__ import annotations import logging import os import re -from typing import Any, Dict, Optional, Set, Tuple, Union - -from pip._vendor.packaging.markers import Marker -from pip._vendor.packaging.requirements import InvalidRequirement, Requirement -from pip._vendor.packaging.specifiers import Specifier +from typing import Any +from typing import Dict +from typing import Optional +from typing import Set +from typing import Tuple +from typing import Union from pip._internal.exceptions import InstallationError -from pip._internal.models.index import PyPI, TestPyPI +from pip._internal.models.index import PyPI +from pip._internal.models.index import TestPyPI from pip._internal.models.link import Link from pip._internal.models.wheel import Wheel from pip._internal.req.req_file import ParsedRequirement @@ -27,20 +30,25 @@ from pip._internal.utils.filetypes import is_archive_file from pip._internal.utils.misc import is_installable_dir from pip._internal.utils.packaging import get_requirement from pip._internal.utils.urls import path_to_url -from pip._internal.vcs import is_url, vcs +from pip._internal.vcs import is_url +from pip._internal.vcs import vcs +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import InvalidRequirement +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.specifiers import Specifier __all__ = [ - "install_req_from_editable", - "install_req_from_line", - "parse_editable", + 'install_req_from_editable', + 'install_req_from_line', + 'parse_editable', ] logger = logging.getLogger(__name__) operators = Specifier._operators.keys() -def _strip_extras(path: str) -> Tuple[str, Optional[str]]: - m = re.match(r"^(.+)(\[[^\]]+\])$", path) +def _strip_extras(path: str) -> tuple[str, str | None]: + m = re.match(r'^(.+)(\[[^\]]+\])$', path) extras = None if m: path_no_extras = m.group(1) @@ -51,13 +59,13 @@ def _strip_extras(path: str) -> Tuple[str, Optional[str]]: return path_no_extras, extras -def convert_extras(extras: Optional[str]) -> Set[str]: +def convert_extras(extras: str | None) -> set[str]: if not extras: return set() - return get_requirement("placeholder" + extras.lower()).extras + return get_requirement('placeholder' + extras.lower()).extras -def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]: +def parse_editable(editable_req: str) -> tuple[str | None, str, set[str]]: """Parses an editable requirement into: - a requirement name - an URL @@ -77,37 +85,37 @@ def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]: # Treating it as code that has already been checked out url_no_extras = path_to_url(url_no_extras) - if url_no_extras.lower().startswith("file:"): + if url_no_extras.lower().startswith('file:'): package_name = Link(url_no_extras).egg_fragment if extras: return ( package_name, url_no_extras, - get_requirement("placeholder" + extras.lower()).extras, + get_requirement('placeholder' + extras.lower()).extras, ) else: return package_name, url_no_extras, set() for version_control in vcs: - if url.lower().startswith(f"{version_control}:"): - url = f"{version_control}+{url}" + if url.lower().startswith(f'{version_control}:'): + url = f'{version_control}+{url}' break link = Link(url) if not link.is_vcs: - backends = ", ".join(vcs.all_schemes) + backends = ', '.join(vcs.all_schemes) raise InstallationError( - f"{editable_req} is not a valid editable requirement. " - f"It should either be a path to a local project or a VCS URL " - f"(beginning with {backends})." + f'{editable_req} is not a valid editable requirement. ' + f'It should either be a path to a local project or a VCS URL ' + f'(beginning with {backends}).', ) package_name = link.egg_fragment if not package_name: raise InstallationError( "Could not detect requirement name for '{}', please specify one " - "with #egg=your_package_name".format(editable_req) + 'with #egg=your_package_name'.format(editable_req), ) return package_name, url, set() @@ -121,21 +129,21 @@ def check_first_requirement_in_file(filename: str) -> None: :raises InvalidRequirement: If the first meaningful line cannot be parsed as an requirement. """ - with open(filename, encoding="utf-8", errors="ignore") as f: + with open(filename, encoding='utf-8', errors='ignore') as f: # Create a steppable iterator, so we can handle \-continuations. lines = ( line for line in (line.strip() for line in f) - if line and not line.startswith("#") # Skip blank lines/comments. + if line and not line.startswith('#') # Skip blank lines/comments. ) for line in lines: # Drop comments -- a hash without a space may be in a URL. - if " #" in line: - line = line[: line.find(" #")] + if ' #' in line: + line = line[: line.find(' #')] # If there is a line continuation, drop it, and append the next line. - if line.endswith("\\"): - line = line[:-2].strip() + next(lines, "") + if line.endswith('\\'): + line = line[:-2].strip() + next(lines, '') Requirement(line) return @@ -148,7 +156,7 @@ def deduce_helpful_msg(req: str) -> str: """ if not os.path.exists(req): return f" File '{req}' does not exist." - msg = " The path does exist. " + msg = ' The path does exist. ' # Try to parse and check if it is a requirements file. try: check_first_requirement_in_file(req) @@ -156,11 +164,11 @@ def deduce_helpful_msg(req: str) -> str: logger.debug("Cannot parse '%s' as requirements file", req) else: msg += ( - f"The argument you provided " - f"({req}) appears to be a" - f" requirements file. If that is the" + f'The argument you provided ' + f'({req}) appears to be a' + f' requirements file. If that is the' f" case, use the '-r' flag to install" - f" the packages specified within it." + f' the packages specified within it.' ) return msg @@ -168,10 +176,10 @@ def deduce_helpful_msg(req: str) -> str: class RequirementParts: def __init__( self, - requirement: Optional[Requirement], - link: Optional[Link], - markers: Optional[Marker], - extras: Set[str], + requirement: Requirement | None, + link: Link | None, + markers: Marker | None, + extras: set[str], ): self.requirement = requirement self.link = link @@ -184,7 +192,7 @@ def parse_req_from_editable(editable_req: str) -> RequirementParts: if name is not None: try: - req: Optional[Requirement] = Requirement(name) + req: Requirement | None = Requirement(name) except InvalidRequirement: raise InstallationError(f"Invalid requirement: '{name}'") else: @@ -200,10 +208,10 @@ def parse_req_from_editable(editable_req: str) -> RequirementParts: def install_req_from_editable( editable_req: str, - comes_from: Optional[Union[InstallRequirement, str]] = None, - use_pep517: Optional[bool] = None, + comes_from: InstallRequirement | str | None = None, + use_pep517: bool | None = None, isolated: bool = False, - options: Optional[Dict[str, Any]] = None, + options: dict[str, Any] | None = None, constraint: bool = False, user_supplied: bool = False, permit_editable_wheels: bool = False, @@ -221,9 +229,9 @@ def install_req_from_editable( constraint=constraint, use_pep517=use_pep517, isolated=isolated, - install_options=options.get("install_options", []) if options else [], - global_options=options.get("global_options", []) if options else [], - hash_options=options.get("hashes", {}) if options else {}, + install_options=options.get('install_options', []) if options else [], + global_options=options.get('global_options', []) if options else [], + hash_options=options.get('hashes', {}) if options else {}, extras=parts.extras, ) @@ -242,12 +250,12 @@ def _looks_like_path(name: str) -> bool: return True if os.path.altsep is not None and os.path.altsep in name: return True - if name.startswith("."): + if name.startswith('.'): return True return False -def _get_url_from_path(path: str, name: str) -> Optional[str]: +def _get_url_from_path(path: str, name: str) -> str | None: """ First, it checks whether a provided path is an installable directory. If it is, returns the path. @@ -263,29 +271,29 @@ def _get_url_from_path(path: str, name: str) -> Optional[str]: # now that it is done in load_pyproject_toml too. raise InstallationError( f"Directory {name!r} is not installable. Neither 'setup.py' " - "nor 'pyproject.toml' found." + "nor 'pyproject.toml' found.", ) if not is_archive_file(path): return None if os.path.isfile(path): return path_to_url(path) - urlreq_parts = name.split("@", 1) + urlreq_parts = name.split('@', 1) if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]): # If the path contains '@' and the part before it does not look # like a path, try to treat it as a PEP 440 URL req instead. return None logger.warning( - "Requirement %r looks like a filename, but the file does not exist", + 'Requirement %r looks like a filename, but the file does not exist', name, ) return path_to_url(path) -def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementParts: +def parse_req_from_line(name: str, line_source: str | None) -> RequirementParts: if is_url(name): - marker_sep = "; " + marker_sep = '; ' else: - marker_sep = ";" + marker_sep = ';' if marker_sep in name: name, markers_as_string = name.split(marker_sep, 1) markers_as_string = markers_as_string.strip() @@ -312,12 +320,12 @@ def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementPar # it's a local file, dir, or url if link: # Handle relative file URLs - if link.scheme == "file" and re.search(r"\.\./", link.url): + if link.scheme == 'file' and re.search(r'\.\./', link.url): link = Link(path_to_url(os.path.normpath(os.path.abspath(link.path)))) # wheel file if link.is_wheel: wheel = Wheel(link.filename) # can raise InvalidWheelFilename - req_as_string = f"{wheel.name}=={wheel.version}" + req_as_string = f'{wheel.name}=={wheel.version}' else: # set the req to the egg fragment. when it's not there, this # will become an 'unnamed' requirement @@ -332,24 +340,24 @@ def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementPar def with_source(text: str) -> str: if not line_source: return text - return f"{text} (from {line_source})" + return f'{text} (from {line_source})' def _parse_req_string(req_as_string: str) -> Requirement: try: req = get_requirement(req_as_string) except InvalidRequirement: if os.path.sep in req_as_string: - add_msg = "It looks like a path." + add_msg = 'It looks like a path.' add_msg += deduce_helpful_msg(req_as_string) - elif "=" in req_as_string and not any( + elif '=' in req_as_string and not any( op in req_as_string for op in operators ): - add_msg = "= is not a valid operator. Did you mean == ?" + add_msg = '= is not a valid operator. Did you mean == ?' else: - add_msg = "" - msg = with_source(f"Invalid requirement: {req_as_string!r}") + add_msg = '' + msg = with_source(f'Invalid requirement: {req_as_string!r}') if add_msg: - msg += f"\nHint: {add_msg}" + msg += f'\nHint: {add_msg}' raise InstallationError(msg) else: # Deprecate extras after specifiers: "name>=1.0[extras]" @@ -358,13 +366,13 @@ def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementPar # RequirementParts for spec in req.specifier: spec_str = str(spec) - if spec_str.endswith("]"): + if spec_str.endswith(']'): msg = f"Extras after version '{spec_str}'." raise InstallationError(msg) return req if req_as_string is not None: - req: Optional[Requirement] = _parse_req_string(req_as_string) + req: Requirement | None = _parse_req_string(req_as_string) else: req = None @@ -373,12 +381,12 @@ def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementPar def install_req_from_line( name: str, - comes_from: Optional[Union[str, InstallRequirement]] = None, - use_pep517: Optional[bool] = None, + comes_from: str | InstallRequirement | None = None, + use_pep517: bool | None = None, isolated: bool = False, - options: Optional[Dict[str, Any]] = None, + options: dict[str, Any] | None = None, constraint: bool = False, - line_source: Optional[str] = None, + line_source: str | None = None, user_supplied: bool = False, ) -> InstallRequirement: """Creates an InstallRequirement from a name, which might be a @@ -396,9 +404,9 @@ def install_req_from_line( markers=parts.markers, use_pep517=use_pep517, isolated=isolated, - install_options=options.get("install_options", []) if options else [], - global_options=options.get("global_options", []) if options else [], - hash_options=options.get("hashes", {}) if options else {}, + install_options=options.get('install_options', []) if options else [], + global_options=options.get('global_options', []) if options else [], + hash_options=options.get('hashes', {}) if options else {}, constraint=constraint, extras=parts.extras, user_supplied=user_supplied, @@ -407,9 +415,9 @@ def install_req_from_line( def install_req_from_req_string( req_string: str, - comes_from: Optional[InstallRequirement] = None, + comes_from: InstallRequirement | None = None, isolated: bool = False, - use_pep517: Optional[bool] = None, + use_pep517: bool | None = None, user_supplied: bool = False, ) -> InstallRequirement: try: @@ -422,16 +430,16 @@ def install_req_from_req_string( TestPyPI.file_storage_domain, ] if ( - req.url - and comes_from - and comes_from.link - and comes_from.link.netloc in domains_not_allowed + req.url and + comes_from and + comes_from.link and + comes_from.link.netloc in domains_not_allowed ): # Explicitly disallow pypi packages that depend on external urls raise InstallationError( - "Packages installed from PyPI cannot depend on packages " - "which are not also hosted on PyPI.\n" - "{} depends on {} ".format(comes_from.name, req) + 'Packages installed from PyPI cannot depend on packages ' + 'which are not also hosted on PyPI.\n' + '{} depends on {} '.format(comes_from.name, req), ) return InstallRequirement( @@ -446,7 +454,7 @@ def install_req_from_req_string( def install_req_from_parsed_requirement( parsed_req: ParsedRequirement, isolated: bool = False, - use_pep517: Optional[bool] = None, + use_pep517: bool | None = None, user_supplied: bool = False, ) -> InstallRequirement: if parsed_req.is_editable: @@ -474,7 +482,7 @@ def install_req_from_parsed_requirement( def install_req_from_link_and_ireq( - link: Link, ireq: InstallRequirement + link: Link, ireq: InstallRequirement, ) -> InstallRequirement: return InstallRequirement( req=ireq.req, diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_file.py b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_file.py index 03ae504..e67711e 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_file.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_file.py @@ -1,6 +1,7 @@ """ Requirements file parsing """ +from __future__ import annotations import optparse import os @@ -8,20 +9,19 @@ import re import shlex import urllib.parse from optparse import Values -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - Iterable, - Iterator, - List, - Optional, - Tuple, -) +from typing import Any +from typing import Callable +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING from pip._internal.cli import cmdoptions -from pip._internal.exceptions import InstallationError, RequirementsFileParseError +from pip._internal.exceptions import InstallationError +from pip._internal.exceptions import RequirementsFileParseError from pip._internal.models.search_scope import SearchScope from pip._internal.network.session import PipSession from pip._internal.network.utils import raise_for_status @@ -35,22 +35,22 @@ if TYPE_CHECKING: from pip._internal.index.package_finder import PackageFinder -__all__ = ["parse_requirements"] +__all__ = ['parse_requirements'] ReqFileLines = Iterable[Tuple[int, str]] LineParser = Callable[[str], Tuple[str, Values]] -SCHEME_RE = re.compile(r"^(http|https|file):", re.I) -COMMENT_RE = re.compile(r"(^|\s+)#.*$") +SCHEME_RE = re.compile(r'^(http|https|file):', re.I) +COMMENT_RE = re.compile(r'(^|\s+)#.*$') # Matches environment variable-style values in '${MY_VARIABLE_1}' with the # variable name consisting of only uppercase letters, digits or the '_' # (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, # 2013 Edition. -ENV_VAR_RE = re.compile(r"(?P\$\{(?P[A-Z0-9_]+)\})") +ENV_VAR_RE = re.compile(r'(?P\$\{(?P[A-Z0-9_]+)\})') -SUPPORTED_OPTIONS: List[Callable[..., optparse.Option]] = [ +SUPPORTED_OPTIONS: list[Callable[..., optparse.Option]] = [ cmdoptions.index_url, cmdoptions.extra_index_url, cmdoptions.no_index, @@ -68,7 +68,7 @@ SUPPORTED_OPTIONS: List[Callable[..., optparse.Option]] = [ ] # options to be passed to requirements -SUPPORTED_OPTIONS_REQ: List[Callable[..., optparse.Option]] = [ +SUPPORTED_OPTIONS_REQ: list[Callable[..., optparse.Option]] = [ cmdoptions.install_options, cmdoptions.global_options, cmdoptions.hash, @@ -85,8 +85,8 @@ class ParsedRequirement: is_editable: bool, comes_from: str, constraint: bool, - options: Optional[Dict[str, Any]] = None, - line_source: Optional[str] = None, + options: dict[str, Any] | None = None, + line_source: str | None = None, ) -> None: self.requirement = requirement self.is_editable = is_editable @@ -126,8 +126,8 @@ class ParsedLine: def parse_requirements( filename: str, session: PipSession, - finder: Optional["PackageFinder"] = None, - options: Optional[optparse.Values] = None, + finder: PackageFinder | None = None, + options: optparse.Values | None = None, constraint: bool = False, ) -> Iterator[ParsedRequirement]: """Parse a requirements file and yield ParsedRequirement instances. @@ -144,7 +144,7 @@ def parse_requirements( for parsed_line in parser.parse(filename, constraint): parsed_req = handle_line( - parsed_line, options=options, finder=finder, session=session + parsed_line, options=options, finder=finder, session=session, ) if parsed_req is not None: yield parsed_req @@ -164,12 +164,12 @@ def preprocess(content: str) -> ReqFileLines: def handle_requirement_line( line: ParsedLine, - options: Optional[optparse.Values] = None, + options: optparse.Values | None = None, ) -> ParsedRequirement: # preserve for the nested code path - line_comes_from = "{} {} (line {})".format( - "-c" if line.constraint else "-r", + line_comes_from = '{} {} (line {})'.format( + '-c' if line.constraint else '-r', line.filename, line.lineno, ) @@ -196,7 +196,7 @@ def handle_requirement_line( if dest in line.opts.__dict__ and line.opts.__dict__[dest]: req_options[dest] = line.opts.__dict__[dest] - line_source = f"line {line.lineno} of {line.filename}" + line_source = f'line {line.lineno} of {line.filename}' return ParsedRequirement( requirement=line.requirement, is_editable=line.is_editable, @@ -211,9 +211,9 @@ def handle_option_line( opts: Values, filename: str, lineno: int, - finder: Optional["PackageFinder"] = None, - options: Optional[optparse.Values] = None, - session: Optional[PipSession] = None, + finder: PackageFinder | None = None, + options: optparse.Values | None = None, + session: PipSession | None = None, ) -> None: if options: @@ -264,16 +264,16 @@ def handle_option_line( if session: for host in opts.trusted_hosts or []: - source = f"line {lineno} of {filename}" + source = f'line {lineno} of {filename}' session.add_trusted_host(host, source=source) def handle_line( line: ParsedLine, - options: Optional[optparse.Values] = None, - finder: Optional["PackageFinder"] = None, - session: Optional[PipSession] = None, -) -> Optional[ParsedRequirement]: + options: optparse.Values | None = None, + finder: PackageFinder | None = None, + session: PipSession | None = None, +) -> ParsedRequirement | None: """Handle a single parsed requirements line; This can result in creating/yielding requirements, or updating the finder. @@ -326,7 +326,7 @@ class RequirementsFileParser: yield from self._parse_and_recurse(filename, constraint) def _parse_and_recurse( - self, filename: str, constraint: bool + self, filename: str, constraint: bool, ) -> Iterator[ParsedLine]: for line in self._parse_file(filename, constraint): if not line.is_requirement and ( @@ -366,7 +366,7 @@ class RequirementsFileParser: args_str, opts = self._line_parser(line) except OptionParsingError as e: # add offending line - msg = f"Invalid requirement: {line}\n{e.msg}" + msg = f'Invalid requirement: {line}\n{e.msg}' raise RequirementsFileParseError(msg) yield ParsedLine( @@ -378,8 +378,8 @@ class RequirementsFileParser: ) -def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser: - def parse_line(line: str) -> Tuple[str, Values]: +def get_line_parser(finder: PackageFinder | None) -> LineParser: + def parse_line(line: str) -> tuple[str, Values]: # Build new parser for each line since it accumulates appendable # options. parser = build_parser() @@ -397,21 +397,21 @@ def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser: return parse_line -def break_args_options(line: str) -> Tuple[str, str]: +def break_args_options(line: str) -> tuple[str, str]: """Break up the line into an args and options string. We only want to shlex (and then optparse) the options, not the args. args can contain markers which are corrupted by shlex. """ - tokens = line.split(" ") + tokens = line.split(' ') args = [] options = tokens[:] for token in tokens: - if token.startswith("-") or token.startswith("--"): + if token.startswith('-') or token.startswith('--'): break else: args.append(token) options.pop(0) - return " ".join(args), " ".join(options) + return ' '.join(args), ' '.join(options) class OptionParsingError(Exception): @@ -432,7 +432,7 @@ def build_parser() -> optparse.OptionParser: # By default optparse sys.exits on parsing errors. We want to wrap # that in our own exception. - def parser_exit(self: Any, msg: str) -> "NoReturn": + def parser_exit(self: Any, msg: str) -> NoReturn: raise OptionParsingError(msg) # NOTE: mypy disallows assigning to a method @@ -447,28 +447,28 @@ def join_lines(lines_enum: ReqFileLines) -> ReqFileLines: comments). The joined line takes on the index of the first line. """ primary_line_number = None - new_line: List[str] = [] + new_line: list[str] = [] for line_number, line in lines_enum: - if not line.endswith("\\") or COMMENT_RE.match(line): + if not line.endswith('\\') or COMMENT_RE.match(line): if COMMENT_RE.match(line): # this ensures comments are always matched later - line = " " + line + line = ' ' + line if new_line: new_line.append(line) assert primary_line_number is not None - yield primary_line_number, "".join(new_line) + yield primary_line_number, ''.join(new_line) new_line = [] else: yield line_number, line else: if not new_line: primary_line_number = line_number - new_line.append(line.strip("\\")) + new_line.append(line.strip('\\')) # last line contains \ if new_line: assert primary_line_number is not None - yield primary_line_number, "".join(new_line) + yield primary_line_number, ''.join(new_line) # TODO: handle space after '\'. @@ -478,7 +478,7 @@ def ignore_comments(lines_enum: ReqFileLines) -> ReqFileLines: Strips comments and filter empty lines. """ for line_number, line in lines_enum: - line = COMMENT_RE.sub("", line) + line = COMMENT_RE.sub('', line) line = line.strip() if line: yield line_number, line @@ -511,7 +511,7 @@ def expand_env_variables(lines_enum: ReqFileLines) -> ReqFileLines: yield line_number, line -def get_file_content(url: str, session: PipSession) -> Tuple[str, str]: +def get_file_content(url: str, session: PipSession) -> tuple[str, str]: """Gets the content of a file; it may be a filename, file: URL, or http: URL. Returns (location, content). Content is unicode. Respects # -*- coding: declarations on the retrieved files. @@ -522,15 +522,15 @@ def get_file_content(url: str, session: PipSession) -> Tuple[str, str]: scheme = get_url_scheme(url) # Pip has special support for file:// URLs (LocalFSAdapter). - if scheme in ["http", "https", "file"]: + if scheme in ['http', 'https', 'file']: resp = session.get(url) raise_for_status(resp) return resp.url, resp.text # Assume this is a bare path. try: - with open(url, "rb") as f: + with open(url, 'rb') as f: content = auto_decode(f.read()) except OSError as exc: - raise InstallationError(f"Could not open requirements file: {exc}") + raise InstallationError(f'Could not open requirements file: {exc}') return url, content diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_install.py b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_install.py index 02dbda1..e0bca01 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_install.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_install.py @@ -1,5 +1,6 @@ # The following comment should be removed at some point in the future. # mypy: strict-optional=False +from __future__ import annotations import functools import logging @@ -8,24 +9,23 @@ import shutil import sys import uuid import zipfile -from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union +from typing import Any +from typing import Collection +from typing import Dict +from typing import Iterable +from typing import List +from typing import Optional +from typing import Sequence +from typing import Union -from pip._vendor.packaging.markers import Marker -from pip._vendor.packaging.requirements import Requirement -from pip._vendor.packaging.specifiers import SpecifierSet -from pip._vendor.packaging.utils import canonicalize_name -from pip._vendor.packaging.version import Version -from pip._vendor.packaging.version import parse as parse_version -from pip._vendor.pep517.wrappers import Pep517HookCaller - -from pip._internal.build_env import BuildEnvironment, NoOpBuildEnvironment -from pip._internal.exceptions import InstallationError, LegacyInstallFailure +from pip._internal.build_env import BuildEnvironment +from pip._internal.build_env import NoOpBuildEnvironment +from pip._internal.exceptions import InstallationError +from pip._internal.exceptions import LegacyInstallFailure from pip._internal.locations import get_scheme -from pip._internal.metadata import ( - BaseDistribution, - get_default_environment, - get_directory_distribution, -) +from pip._internal.metadata import BaseDistribution +from pip._internal.metadata import get_default_environment +from pip._internal.metadata import get_directory_distribution from pip._internal.models.link import Link from pip._internal.operations.build.metadata import generate_metadata from pip._internal.operations.build.metadata_editable import generate_editable_metadata @@ -37,26 +37,31 @@ from pip._internal.operations.install.editable_legacy import ( ) from pip._internal.operations.install.legacy import install as install_legacy from pip._internal.operations.install.wheel import install_wheel -from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path +from pip._internal.pyproject import load_pyproject_toml +from pip._internal.pyproject import make_pyproject_path from pip._internal.req.req_uninstall import UninstallPathSet from pip._internal.utils.deprecation import deprecated -from pip._internal.utils.direct_url_helpers import ( - direct_url_for_editable, - direct_url_from_link, -) +from pip._internal.utils.direct_url_helpers import direct_url_for_editable +from pip._internal.utils.direct_url_helpers import direct_url_from_link from pip._internal.utils.hashes import Hashes -from pip._internal.utils.misc import ( - ask_path_exists, - backup_dir, - display_path, - hide_url, - redact_auth_from_url, -) +from pip._internal.utils.misc import ask_path_exists +from pip._internal.utils.misc import backup_dir +from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import hide_url +from pip._internal.utils.misc import redact_auth_from_url from pip._internal.utils.packaging import safe_extra from pip._internal.utils.subprocess import runner_with_spinner_message -from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.temp_dir import tempdir_kinds +from pip._internal.utils.temp_dir import TempDirectory from pip._internal.utils.virtualenv import running_under_virtualenv from pip._internal.vcs import vcs +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.packaging.version import Version +from pip._vendor.pep517.wrappers import Pep517HookCaller logger = logging.getLogger(__name__) @@ -70,16 +75,16 @@ class InstallRequirement: def __init__( self, - req: Optional[Requirement], - comes_from: Optional[Union[str, "InstallRequirement"]], + req: Requirement | None, + comes_from: str | InstallRequirement | None, editable: bool = False, - link: Optional[Link] = None, - markers: Optional[Marker] = None, - use_pep517: Optional[bool] = None, + link: Link | None = None, + markers: Marker | None = None, + use_pep517: bool | None = None, isolated: bool = False, - install_options: Optional[List[str]] = None, - global_options: Optional[List[str]] = None, - hash_options: Optional[Dict[str, List[str]]] = None, + install_options: list[str] | None = None, + global_options: list[str] | None = None, + hash_options: dict[str, list[str]] | None = None, constraint: bool = False, extras: Collection[str] = (), user_supplied: bool = False, @@ -91,14 +96,14 @@ class InstallRequirement: self.constraint = constraint self.editable = editable self.permit_editable_wheels = permit_editable_wheels - self.legacy_install_reason: Optional[int] = None + self.legacy_install_reason: int | None = None # source_dir is the local directory where the linked requirement is # located, or unpacked. In case unpacking is needed, creating and # populating source_dir is done by the RequirementPreparer. Note this # is not necessarily the directory where pyproject.toml or setup.py is # located - that one is obtained via unpacked_source_directory. - self.source_dir: Optional[str] = None + self.source_dir: str | None = None if self.editable: assert link if link.is_file: @@ -111,7 +116,7 @@ class InstallRequirement: self.original_link_is_in_wheel_cache = False # Path to any downloaded or already-existing package. - self.local_file_path: Optional[str] = None + self.local_file_path: str | None = None if self.link and self.link.is_file: self.local_file_path = self.link.file_path @@ -126,14 +131,14 @@ class InstallRequirement: self.markers = markers # This holds the Distribution object if this requirement is already installed. - self.satisfied_by: Optional[BaseDistribution] = None + self.satisfied_by: BaseDistribution | None = None # Whether the installation process should try to uninstall an existing # distribution before installing this requirement. self.should_reinstall = False # Temporary build location - self._temp_build_dir: Optional[TempDirectory] = None + self._temp_build_dir: TempDirectory | None = None # Set to True after successful installation - self.install_succeeded: Optional[bool] = None + self.install_succeeded: bool | None = None # Supplied options self.install_options = install_options if install_options else [] self.global_options = global_options if global_options else [] @@ -152,16 +157,16 @@ class InstallRequirement: # gets stored. We need this to pass to build_wheel, so the backend # can ensure that the wheel matches the metadata (see the PEP for # details). - self.metadata_directory: Optional[str] = None + self.metadata_directory: str | None = None # The static build requirements (from pyproject.toml) - self.pyproject_requires: Optional[List[str]] = None + self.pyproject_requires: list[str] | None = None # Build requirements that we will check are available - self.requirements_to_check: List[str] = [] + self.requirements_to_check: list[str] = [] # The PEP 517 backend we should use to build the project - self.pep517_backend: Optional[Pep517HookCaller] = None + self.pep517_backend: Pep517HookCaller | None = None # Are we using PEP 517 for this requirement? # After pyproject.toml has been loaded, the only valid values are True @@ -177,25 +182,25 @@ class InstallRequirement: if self.req: s = str(self.req) if self.link: - s += " from {}".format(redact_auth_from_url(self.link.url)) + s += f' from {redact_auth_from_url(self.link.url)}' elif self.link: s = redact_auth_from_url(self.link.url) else: - s = "" + s = '' if self.satisfied_by is not None: - s += " in {}".format(display_path(self.satisfied_by.location)) + s += f' in {display_path(self.satisfied_by.location)}' if self.comes_from: if isinstance(self.comes_from, str): - comes_from: Optional[str] = self.comes_from + comes_from: str | None = self.comes_from else: comes_from = self.comes_from.from_path() if comes_from: - s += f" (from {comes_from})" + s += f' (from {comes_from})' return s def __repr__(self) -> str: - return "<{} object: {} editable={!r}>".format( - self.__class__.__name__, str(self), self.editable + return '<{} object: {} editable={!r}>'.format( + self.__class__.__name__, str(self), self.editable, ) def format_debug(self) -> str: @@ -203,30 +208,30 @@ class InstallRequirement: attributes = vars(self) names = sorted(attributes) - state = ("{}={!r}".format(attr, attributes[attr]) for attr in sorted(names)) - return "<{name} object: {{{state}}}>".format( + state = (f'{attr}={attributes[attr]!r}' for attr in sorted(names)) + return '<{name} object: {{{state}}}>'.format( name=self.__class__.__name__, - state=", ".join(state), + state=', '.join(state), ) # Things that are valid for all kinds of requirements? @property - def name(self) -> Optional[str]: + def name(self) -> str | None: if self.req is None: return None return self.req.name - @functools.lru_cache() # use cached_property in python 3.8+ + @functools.lru_cache # use cached_property in python 3.8+ def supports_pyproject_editable(self) -> bool: if not self.use_pep517: return False assert self.pep517_backend with self.build_env: runner = runner_with_spinner_message( - "Checking if build backend supports build_editable" + 'Checking if build backend supports build_editable', ) with self.pep517_backend.subprocess_runner(runner): - return "build_editable" in self.pep517_backend._supported_features() + return 'build_editable' in self.pep517_backend._supported_features() @property def specifier(self) -> SpecifierSet: @@ -239,16 +244,16 @@ class InstallRequirement: For example, some-package==1.2 is pinned; some-package>1.2 is not. """ specifiers = self.specifier - return len(specifiers) == 1 and next(iter(specifiers)).operator in {"==", "==="} + return len(specifiers) == 1 and next(iter(specifiers)).operator in {'==', '==='} - def match_markers(self, extras_requested: Optional[Iterable[str]] = None) -> bool: + def match_markers(self, extras_requested: Iterable[str] | None = None) -> bool: if not extras_requested: # Provide an extra to safely evaluate the markers # without matching any extra - extras_requested = ("",) + extras_requested = ('',) if self.markers is not None: return any( - self.markers.evaluate({"extra": extra}) for extra in extras_requested + self.markers.evaluate({'extra': extra}) for extra in extras_requested ) else: return True @@ -284,7 +289,7 @@ class InstallRequirement: good_hashes.setdefault(link.hash_name, []).append(link.hash) return Hashes(good_hashes) - def from_path(self) -> Optional[str]: + def from_path(self) -> str | None: """Format a nice indicator to show where this "comes from" """ if self.req is None: return None @@ -295,11 +300,11 @@ class InstallRequirement: else: comes_from = self.comes_from.from_path() if comes_from: - s += "->" + comes_from + s += '->' + comes_from return s def ensure_build_location( - self, build_dir: str, autodelete: bool, parallel_builds: bool + self, build_dir: str, autodelete: bool, parallel_builds: bool, ) -> str: assert build_dir is not None if self._temp_build_dir is not None: @@ -310,7 +315,7 @@ class InstallRequirement: # builds (such as numpy). Thus, we ensure that the real path # is returned. self._temp_build_dir = TempDirectory( - kind=tempdir_kinds.REQ_BUILD, globally_managed=True + kind=tempdir_kinds.REQ_BUILD, globally_managed=True, ) return self._temp_build_dir.path @@ -323,12 +328,12 @@ class InstallRequirement: # name so multiple builds do not interfere with each other. dir_name: str = canonicalize_name(self.name) if parallel_builds: - dir_name = f"{dir_name}_{uuid.uuid4().hex}" + dir_name = f'{dir_name}_{uuid.uuid4().hex}' # FIXME: Is there a better place to create the build_dir? (hg and bzr # need this) if not os.path.exists(build_dir): - logger.debug("Creating directory %s", build_dir) + logger.debug('Creating directory %s', build_dir) os.makedirs(build_dir) actual_build_dir = os.path.join(build_dir, dir_name) # `None` indicates that we respect the globally-configured deletion @@ -348,32 +353,32 @@ class InstallRequirement: assert self.source_dir is not None # Construct a Requirement object from the generated metadata - if isinstance(parse_version(self.metadata["Version"]), Version): - op = "==" + if isinstance(parse_version(self.metadata['Version']), Version): + op = '==' else: - op = "===" + op = '===' self.req = Requirement( - "".join( + ''.join( [ - self.metadata["Name"], + self.metadata['Name'], op, - self.metadata["Version"], - ] - ) + self.metadata['Version'], + ], + ), ) def warn_on_mismatching_name(self) -> None: - metadata_name = canonicalize_name(self.metadata["Name"]) + metadata_name = canonicalize_name(self.metadata['Name']) if canonicalize_name(self.req.name) == metadata_name: # Everything is fine. return # If we're here, there's a mismatch. Log a warning about it. logger.warning( - "Generating metadata for package %s " - "produced metadata for project name %s. Fix your " - "#egg=%s fragments.", + 'Generating metadata for package %s ' + 'produced metadata for project name %s. Fix your ' + '#egg=%s fragments.', self.name, metadata_name, self.name, @@ -402,9 +407,9 @@ class InstallRequirement: self.should_reinstall = True elif running_under_virtualenv() and existing_dist.in_site_packages: raise InstallationError( - f"Will not install to the user site because it will " - f"lack sys.path precedence to {existing_dist.raw_name} " - f"in {existing_dist.location}" + f'Will not install to the user site because it will ' + f'lack sys.path precedence to {existing_dist.raw_name} ' + f'in {existing_dist.location}', ) else: self.should_reinstall = True @@ -428,26 +433,26 @@ class InstallRequirement: @property def unpacked_source_directory(self) -> str: return os.path.join( - self.source_dir, self.link and self.link.subdirectory_fragment or "" + self.source_dir, self.link and self.link.subdirectory_fragment or '', ) @property def setup_py_path(self) -> str: - assert self.source_dir, f"No source dir for {self}" - setup_py = os.path.join(self.unpacked_source_directory, "setup.py") + assert self.source_dir, f'No source dir for {self}' + setup_py = os.path.join(self.unpacked_source_directory, 'setup.py') return setup_py @property def setup_cfg_path(self) -> str: - assert self.source_dir, f"No source dir for {self}" - setup_cfg = os.path.join(self.unpacked_source_directory, "setup.cfg") + assert self.source_dir, f'No source dir for {self}' + setup_cfg = os.path.join(self.unpacked_source_directory, 'setup.cfg') return setup_cfg @property def pyproject_toml_path(self) -> str: - assert self.source_dir, f"No source dir for {self}" + assert self.source_dir, f'No source dir for {self}' return make_pyproject_path(self.unpacked_source_directory) def load_pyproject_toml(self) -> None: @@ -459,7 +464,7 @@ class InstallRequirement: follow the PEP 517 or legacy (setup.py) code path. """ pyproject_toml_data = load_pyproject_toml( - self.use_pep517, self.pyproject_toml_path, self.setup_py_path, str(self) + self.use_pep517, self.pyproject_toml_path, self.setup_py_path, str(self), ) if pyproject_toml_data is None: @@ -483,18 +488,18 @@ class InstallRequirement: or as a setup.py or a setup.cfg """ if ( - self.editable - and self.use_pep517 - and not self.supports_pyproject_editable() - and not os.path.isfile(self.setup_py_path) - and not os.path.isfile(self.setup_cfg_path) + self.editable and + self.use_pep517 and + not self.supports_pyproject_editable() and + not os.path.isfile(self.setup_py_path) and + not os.path.isfile(self.setup_cfg_path) ): raise InstallationError( f"Project {self} has a 'pyproject.toml' and its build " f"backend is missing the 'build_editable' hook. Since it does not " f"have a 'setup.py' nor a 'setup.cfg', " - f"it cannot be installed in editable mode. " - f"Consider using a build backend that supports PEP 660." + f'it cannot be installed in editable mode. ' + f'Consider using a build backend that supports PEP 660.', ) def prepare_metadata(self) -> None: @@ -504,14 +509,14 @@ class InstallRequirement: Under legacy processing, call setup.py egg-info. """ assert self.source_dir - details = self.name or f"from {self.link}" + details = self.name or f'from {self.link}' if self.use_pep517: assert self.pep517_backend is not None if ( - self.editable - and self.permit_editable_wheels - and self.supports_pyproject_editable() + self.editable and + self.permit_editable_wheels and + self.supports_pyproject_editable() ): self.metadata_directory = generate_editable_metadata( build_env=self.build_env, @@ -543,7 +548,7 @@ class InstallRequirement: @property def metadata(self) -> Any: - if not hasattr(self, "_metadata"): + if not hasattr(self, '_metadata'): self._metadata = self.get_dist().metadata return self._metadata @@ -553,16 +558,16 @@ class InstallRequirement: def assert_source_matches_version(self) -> None: assert self.source_dir - version = self.metadata["version"] + version = self.metadata['version'] if self.req.specifier and version not in self.req.specifier: logger.warning( - "Requested %s, but installing version %s", + 'Requested %s, but installing version %s', self, version, ) else: logger.debug( - "Source in %s has version %s, which satisfies requirement %s", + 'Source in %s has version %s, which satisfies requirement %s', display_path(self.source_dir), version, self, @@ -595,26 +600,26 @@ class InstallRequirement: def update_editable(self) -> None: if not self.link: logger.debug( - "Cannot update repository at %s; repository location is unknown", + 'Cannot update repository at %s; repository location is unknown', self.source_dir, ) return assert self.editable assert self.source_dir - if self.link.scheme == "file": + if self.link.scheme == 'file': # Static paths don't get updated return vcs_backend = vcs.get_backend_for_scheme(self.link.scheme) # Editable requirements are validated in Requirement constructors. # So here, if it's neither a path nor a valid VCS URL, it's a bug. - assert vcs_backend, f"Unsupported VCS URL {self.link.url}" + assert vcs_backend, f'Unsupported VCS URL {self.link.url}' hidden_url = hide_url(self.link.url) vcs_backend.obtain(self.source_dir, url=hidden_url, verbosity=0) # Top-level Actions def uninstall( - self, auto_confirm: bool = False, verbose: bool = False - ) -> Optional[UninstallPathSet]: + self, auto_confirm: bool = False, verbose: bool = False, + ) -> UninstallPathSet | None: """ Uninstall the distribution currently satisfying this requirement. @@ -630,9 +635,9 @@ class InstallRequirement: assert self.req dist = get_default_environment().get_distribution(self.req.name) if not dist: - logger.warning("Skipping %s as it is not installed.", self.name) + logger.warning('Skipping %s as it is not installed.', self.name) return None - logger.info("Found existing installation: %s", dist) + logger.info('Found existing installation: %s', dist) uninstalled_pathset = UninstallPathSet.from_dist(dist) uninstalled_pathset.remove(auto_confirm, verbose) @@ -641,17 +646,17 @@ class InstallRequirement: def _get_archive_name(self, path: str, parentdir: str, rootdir: str) -> str: def _clean_zip_name(name: str, prefix: str) -> str: assert name.startswith( - prefix + os.path.sep + prefix + os.path.sep, ), f"name {name!r} doesn't start with prefix {prefix!r}" - name = name[len(prefix) + 1 :] - name = name.replace(os.path.sep, "/") + name = name[len(prefix) + 1:] + name = name.replace(os.path.sep, '/') return name path = os.path.join(parentdir, path) name = _clean_zip_name(path, rootdir) - return self.name + "/" + name + return self.name + '/' + name - def archive(self, build_dir: Optional[str]) -> None: + def archive(self, build_dir: str | None) -> None: """Saves archive to provided build_dir. Used for saving downloaded VCS requirements as part of `pip download`. @@ -661,29 +666,29 @@ class InstallRequirement: return create_archive = True - archive_name = "{}-{}.zip".format(self.name, self.metadata["version"]) + archive_name = '{}-{}.zip'.format(self.name, self.metadata['version']) archive_path = os.path.join(build_dir, archive_name) if os.path.exists(archive_path): response = ask_path_exists( - "The file {} exists. (i)gnore, (w)ipe, " - "(b)ackup, (a)bort ".format(display_path(archive_path)), - ("i", "w", "b", "a"), + 'The file {} exists. (i)gnore, (w)ipe, ' + '(b)ackup, (a)bort '.format(display_path(archive_path)), + ('i', 'w', 'b', 'a'), ) - if response == "i": + if response == 'i': create_archive = False - elif response == "w": - logger.warning("Deleting %s", display_path(archive_path)) + elif response == 'w': + logger.warning('Deleting %s', display_path(archive_path)) os.remove(archive_path) - elif response == "b": + elif response == 'b': dest_file = backup_dir(archive_path) logger.warning( - "Backing up %s to %s", + 'Backing up %s to %s', display_path(archive_path), display_path(dest_file), ) shutil.move(archive_path, dest_file) - elif response == "a": + elif response == 'a': sys.exit(-1) if not create_archive: @@ -691,7 +696,7 @@ class InstallRequirement: zip_output = zipfile.ZipFile( archive_path, - "w", + 'w', zipfile.ZIP_DEFLATED, allowZip64=True, ) @@ -704,9 +709,9 @@ class InstallRequirement: parentdir=dirpath, rootdir=dir, ) - zipdir = zipfile.ZipInfo(dir_arcname + "/") + zipdir = zipfile.ZipInfo(dir_arcname + '/') zipdir.external_attr = 0x1ED << 16 # 0o755 - zip_output.writestr(zipdir, "") + zip_output.writestr(zipdir, '') for filename in filenames: file_arcname = self._get_archive_name( filename, @@ -716,15 +721,15 @@ class InstallRequirement: filename = os.path.join(dirpath, filename) zip_output.write(filename, file_arcname) - logger.info("Saved %s", display_path(archive_path)) + logger.info('Saved %s', display_path(archive_path)) def install( self, - install_options: List[str], - global_options: Optional[Sequence[str]] = None, - root: Optional[str] = None, - home: Optional[str] = None, - prefix: Optional[str] = None, + install_options: list[str], + global_options: Sequence[str] | None = None, + root: str | None = None, + home: str | None = None, + prefix: str | None = None, warn_script_location: bool = True, use_user_site: bool = False, pycompile: bool = True, @@ -819,11 +824,11 @@ class InstallRequirement: deprecated( reason=( "{} was installed using the legacy 'setup.py install' " - "method, because a wheel could not be built for it.".format( - self.name + 'method, because a wheel could not be built for it.'.format( + self.name, ) ), - replacement="to fix the wheel build issue reported above", + replacement='to fix the wheel build issue reported above', gone_in=None, issue=8368, ) @@ -832,24 +837,24 @@ class InstallRequirement: def check_invalid_constraint_type(req: InstallRequirement) -> str: # Check for unsupported forms - problem = "" + problem = '' if not req.name: - problem = "Unnamed requirements are not allowed as constraints" + problem = 'Unnamed requirements are not allowed as constraints' elif req.editable: - problem = "Editable requirements are not allowed as constraints" + problem = 'Editable requirements are not allowed as constraints' elif req.extras: - problem = "Constraints cannot have extras" + problem = 'Constraints cannot have extras' if problem: deprecated( reason=( - "Constraints are only allowed to take the form of a package " - "name and a version specifier. Other forms were originally " - "permitted as an accident of the implementation, but were " - "undocumented. The new implementation of the resolver no " - "longer supports these forms." + 'Constraints are only allowed to take the form of a package ' + 'name and a version specifier. Other forms were originally ' + 'permitted as an accident of the implementation, but were ' + 'undocumented. The new implementation of the resolver no ' + 'longer supports these forms.' ), - replacement="replacing the constraint with a requirement", + replacement='replacing the constraint with a requirement', # No plan yet for when the new resolver becomes default gone_in=None, issue=8210, diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_set.py b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_set.py index 6626c37..25d3a29 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_set.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_set.py @@ -1,13 +1,18 @@ +from __future__ import annotations + import logging from collections import OrderedDict -from typing import Dict, Iterable, List, Optional, Tuple - -from pip._vendor.packaging.utils import canonicalize_name +from typing import Dict +from typing import Iterable +from typing import List +from typing import Optional +from typing import Tuple from pip._internal.exceptions import InstallationError from pip._internal.models.wheel import Wheel from pip._internal.req.req_install import InstallRequirement from pip._internal.utils import compatibility_tags +from pip._vendor.packaging.utils import canonicalize_name logger = logging.getLogger(__name__) @@ -16,29 +21,29 @@ class RequirementSet: def __init__(self, check_supported_wheels: bool = True) -> None: """Create a RequirementSet.""" - self.requirements: Dict[str, InstallRequirement] = OrderedDict() + self.requirements: dict[str, InstallRequirement] = OrderedDict() self.check_supported_wheels = check_supported_wheels - self.unnamed_requirements: List[InstallRequirement] = [] + self.unnamed_requirements: list[InstallRequirement] = [] def __str__(self) -> str: requirements = sorted( (req for req in self.requirements.values() if not req.comes_from), - key=lambda req: canonicalize_name(req.name or ""), + key=lambda req: canonicalize_name(req.name or ''), ) - return " ".join(str(req.req) for req in requirements) + return ' '.join(str(req.req) for req in requirements) def __repr__(self) -> str: requirements = sorted( self.requirements.values(), - key=lambda req: canonicalize_name(req.name or ""), + key=lambda req: canonicalize_name(req.name or ''), ) - format_string = "<{classname} object; {count} requirement(s): {reqs}>" + format_string = '<{classname} object; {count} requirement(s): {reqs}>' return format_string.format( classname=self.__class__.__name__, count=len(requirements), - reqs=", ".join(str(req.req) for req in requirements), + reqs=', '.join(str(req.req) for req in requirements), ) def add_unnamed_requirement(self, install_req: InstallRequirement) -> None: @@ -54,9 +59,9 @@ class RequirementSet: def add_requirement( self, install_req: InstallRequirement, - parent_req_name: Optional[str] = None, - extras_requested: Optional[Iterable[str]] = None, - ) -> Tuple[List[InstallRequirement], Optional[InstallRequirement]]: + parent_req_name: str | None = None, + extras_requested: Iterable[str] | None = None, + ) -> tuple[list[InstallRequirement], InstallRequirement | None]: """Add install_req as a requirement to install. :param parent_req_name: The name of the requirement that needed this @@ -89,9 +94,9 @@ class RequirementSet: tags = compatibility_tags.get_supported() if self.check_supported_wheels and not wheel.supported(tags): raise InstallationError( - "{} is not a supported wheel on this platform.".format( - wheel.filename - ) + '{} is not a supported wheel on this platform.'.format( + wheel.filename, + ), ) # This next bit is really a sanity check. @@ -106,26 +111,26 @@ class RequirementSet: return [install_req], None try: - existing_req: Optional[InstallRequirement] = self.get_requirement( - install_req.name + existing_req: InstallRequirement | None = self.get_requirement( + install_req.name, ) except KeyError: existing_req = None has_conflicting_requirement = ( - parent_req_name is None - and existing_req - and not existing_req.constraint - and existing_req.extras == install_req.extras - and existing_req.req - and install_req.req - and existing_req.req.specifier != install_req.req.specifier + parent_req_name is None and + existing_req and + not existing_req.constraint and + existing_req.extras == install_req.extras and + existing_req.req and + install_req.req and + existing_req.req.specifier != install_req.req.specifier ) if has_conflicting_requirement: raise InstallationError( - "Double requirement given: {} (already in {}, name={!r})".format( - install_req, existing_req, install_req.name - ) + 'Double requirement given: {} (already in {}, name={!r})'.format( + install_req, existing_req, install_req.name, + ), ) # When no existing requirement exists, add the requirement as a @@ -146,8 +151,8 @@ class RequirementSet: if does_not_satisfy_constraint: raise InstallationError( "Could not satisfy constraints for '{}': " - "installation from path or url cannot be " - "constrained to a version".format(install_req.name) + 'installation from path or url cannot be ' + 'constrained to a version'.format(install_req.name), ) # If we're now installing a constraint, mark the existing # object for real installation. @@ -157,10 +162,10 @@ class RequirementSet: if install_req.user_supplied: existing_req.user_supplied = True existing_req.extras = tuple( - sorted(set(existing_req.extras) | set(install_req.extras)) + sorted(set(existing_req.extras) | set(install_req.extras)), ) logger.debug( - "Setting %s extras to: %s", + 'Setting %s extras to: %s', existing_req, existing_req.extras, ) @@ -172,8 +177,8 @@ class RequirementSet: project_name = canonicalize_name(name) return ( - project_name in self.requirements - and not self.requirements[project_name].constraint + project_name in self.requirements and + not self.requirements[project_name].constraint ) def get_requirement(self, name: str) -> InstallRequirement: @@ -182,8 +187,8 @@ class RequirementSet: if project_name in self.requirements: return self.requirements[project_name] - raise KeyError(f"No project with the name {name!r}") + raise KeyError(f'No project with the name {name!r}') @property - def all_requirements(self) -> List[InstallRequirement]: + def all_requirements(self) -> list[InstallRequirement]: return self.unnamed_requirements + list(self.requirements.values()) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_tracker.py b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_tracker.py index 24d3c53..529f60e 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_tracker.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_tracker.py @@ -1,9 +1,16 @@ +from __future__ import annotations + import contextlib import hashlib import logging import os from types import TracebackType -from typing import Dict, Iterator, Optional, Set, Type, Union +from typing import Dict +from typing import Iterator +from typing import Optional +from typing import Set +from typing import Type +from typing import Union from pip._internal.models.link import Link from pip._internal.req.req_install import InstallRequirement @@ -18,7 +25,7 @@ def update_env_context_manager(**changes: str) -> Iterator[None]: # Save values from the target and change them. non_existent_marker = object() - saved_values: Dict[str, Union[object, str]] = {} + saved_values: dict[str, object | str] = {} for name, new_value in changes.items(): try: saved_values[name] = target[name] @@ -39,13 +46,13 @@ def update_env_context_manager(**changes: str) -> Iterator[None]: @contextlib.contextmanager -def get_requirement_tracker() -> Iterator["RequirementTracker"]: - root = os.environ.get("PIP_REQ_TRACKER") +def get_requirement_tracker() -> Iterator[RequirementTracker]: + root = os.environ.get('PIP_REQ_TRACKER') with contextlib.ExitStack() as ctx: if root is None: - root = ctx.enter_context(TempDirectory(kind="req-tracker")).path + root = ctx.enter_context(TempDirectory(kind='req-tracker')).path ctx.enter_context(update_env_context_manager(PIP_REQ_TRACKER=root)) - logger.debug("Initialized build tracking at %s", root) + logger.debug('Initialized build tracking at %s', root) with RequirementTracker(root) as tracker: yield tracker @@ -54,18 +61,18 @@ def get_requirement_tracker() -> Iterator["RequirementTracker"]: class RequirementTracker: def __init__(self, root: str) -> None: self._root = root - self._entries: Set[InstallRequirement] = set() - logger.debug("Created build tracker: %s", self._root) + self._entries: set[InstallRequirement] = set() + logger.debug('Created build tracker: %s', self._root) - def __enter__(self) -> "RequirementTracker": - logger.debug("Entered build tracker: %s", self._root) + def __enter__(self) -> RequirementTracker: + logger.debug('Entered build tracker: %s', self._root) return self def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: self.cleanup() @@ -88,18 +95,18 @@ class RequirementTracker: except FileNotFoundError: pass else: - message = "{} is already being built: {}".format(req.link, contents) + message = f'{req.link} is already being built: {contents}' raise LookupError(message) # If we're here, req should really not be building already. assert req not in self._entries # Start tracking this requirement. - with open(entry_path, "w", encoding="utf-8") as fp: + with open(entry_path, 'w', encoding='utf-8') as fp: fp.write(str(req)) self._entries.add(req) - logger.debug("Added %s to build tracker %r", req, self._root) + logger.debug('Added %s to build tracker %r', req, self._root) def remove(self, req: InstallRequirement) -> None: """Remove an InstallRequirement from build tracking.""" @@ -109,13 +116,13 @@ class RequirementTracker: os.unlink(self._entry_path(req.link)) self._entries.remove(req) - logger.debug("Removed %s from build tracker %r", req, self._root) + logger.debug('Removed %s from build tracker %r', req, self._root) def cleanup(self) -> None: for req in set(self._entries): self.remove(req) - logger.debug("Removed build tracker: %r", self._root) + logger.debug('Removed build tracker: %r', self._root) @contextlib.contextmanager def track(self, req: InstallRequirement) -> Iterator[None]: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_uninstall.py b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_uninstall.py index 472090a..e876951 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/req/req_uninstall.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/req/req_uninstall.py @@ -1,18 +1,35 @@ +from __future__ import annotations + import functools import os import sys import sysconfig from importlib.util import cache_from_source -from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Set, Tuple +from typing import Any +from typing import Callable +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple from pip._internal.exceptions import UninstallationError -from pip._internal.locations import get_bin_prefix, get_bin_user +from pip._internal.locations import get_bin_prefix +from pip._internal.locations import get_bin_user from pip._internal.metadata import BaseDistribution from pip._internal.utils.compat import WINDOWS from pip._internal.utils.egg_link import egg_link_path_from_location -from pip._internal.utils.logging import getLogger, indent_log -from pip._internal.utils.misc import ask, is_local, normalize_path, renames, rmtree -from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory +from pip._internal.utils.logging import getLogger +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ask +from pip._internal.utils.misc import is_local +from pip._internal.utils.misc import normalize_path +from pip._internal.utils.misc import renames +from pip._internal.utils.misc import rmtree +from pip._internal.utils.temp_dir import AdjacentTempDirectory +from pip._internal.utils.temp_dir import TempDirectory logger = getLogger(__name__) @@ -26,18 +43,18 @@ def _script_names(bin_dir: str, script_name: str, is_gui: bool) -> Iterator[str] yield exe_name if not WINDOWS: return - yield f"{exe_name}.exe" - yield f"{exe_name}.exe.manifest" + yield f'{exe_name}.exe' + yield f'{exe_name}.exe.manifest' if is_gui: - yield f"{exe_name}-script.pyw" + yield f'{exe_name}-script.pyw' else: - yield f"{exe_name}-script.py" + yield f'{exe_name}-script.py' def _unique(fn: Callable[..., Iterator[Any]]) -> Callable[..., Iterator[Any]]: @functools.wraps(fn) def unique(*args: Any, **kw: Any) -> Iterator[Any]: - seen: Set[Any] = set() + seen: set[Any] = set() for item in fn(*args, **kw): if item not in seen: seen.add(item) @@ -62,46 +79,46 @@ def uninstallation_paths(dist: BaseDistribution) -> Iterator[str]: https://packaging.python.org/specifications/recording-installed-packages/ """ location = dist.location - assert location is not None, "not installed" + assert location is not None, 'not installed' entries = dist.iter_declared_entries() if entries is None: - msg = "Cannot uninstall {dist}, RECORD file not found.".format(dist=dist) + msg = f'Cannot uninstall {dist}, RECORD file not found.' installer = dist.installer - if not installer or installer == "pip": - dep = "{}=={}".format(dist.raw_name, dist.version) + if not installer or installer == 'pip': + dep = f'{dist.raw_name}=={dist.version}' msg += ( - " You might be able to recover from this via: " + ' You might be able to recover from this via: ' "'pip install --force-reinstall --no-deps {}'.".format(dep) ) else: - msg += " Hint: The package was installed by {}.".format(installer) + msg += f' Hint: The package was installed by {installer}.' raise UninstallationError(msg) for entry in entries: path = os.path.join(location, entry) yield path - if path.endswith(".py"): + if path.endswith('.py'): dn, fn = os.path.split(path) base = fn[:-3] - path = os.path.join(dn, base + ".pyc") + path = os.path.join(dn, base + '.pyc') yield path - path = os.path.join(dn, base + ".pyo") + path = os.path.join(dn, base + '.pyo') yield path -def compact(paths: Iterable[str]) -> Set[str]: +def compact(paths: Iterable[str]) -> set[str]: """Compact a path set to contain the minimal number of paths necessary to contain all paths in the set. If /a/path/ and /a/path/to/a/file.txt are both in the set, leave only the shorter path.""" sep = os.path.sep - short_paths: Set[str] = set() + short_paths: set[str] = set() for path in sorted(paths, key=len): should_skip = any( - path.startswith(shortpath.rstrip("*")) - and path[len(shortpath.rstrip("*").rstrip(sep))] == sep + path.startswith(shortpath.rstrip('*')) and + path[len(shortpath.rstrip('*').rstrip(sep))] == sep for shortpath in short_paths ) if not should_skip: @@ -109,7 +126,7 @@ def compact(paths: Iterable[str]) -> Set[str]: return short_paths -def compress_for_rename(paths: Iterable[str]) -> Set[str]: +def compress_for_rename(paths: Iterable[str]) -> set[str]: """Returns a set containing the paths that need to be renamed. This set may include directories when the original sequence of paths @@ -118,7 +135,7 @@ def compress_for_rename(paths: Iterable[str]) -> Set[str]: case_map = {os.path.normcase(p): p for p in paths} remaining = set(case_map) unchecked = sorted({os.path.split(p)[0] for p in case_map.values()}, key=len) - wildcards: Set[str] = set() + wildcards: set[str] = set() def norm_join(*a: str) -> str: return os.path.normcase(os.path.join(*a)) @@ -128,8 +145,8 @@ def compress_for_rename(paths: Iterable[str]) -> Set[str]: # This directory has already been handled. continue - all_files: Set[str] = set() - all_subdirs: Set[str] = set() + all_files: set[str] = set() + all_subdirs: set[str] = set() for dirname, subdirs, files in os.walk(root): all_subdirs.update(norm_join(root, dirname, d) for d in subdirs) all_files.update(norm_join(root, dirname, f) for f in files) @@ -143,7 +160,7 @@ def compress_for_rename(paths: Iterable[str]) -> Set[str]: return set(map(case_map.__getitem__, remaining)) | wildcards -def compress_for_output_listing(paths: Iterable[str]) -> Tuple[Set[str], Set[str]]: +def compress_for_output_listing(paths: Iterable[str]) -> tuple[set[str], set[str]]: """Returns a tuple of 2 sets of which paths to display to user The first set contains paths that would be deleted. Files of a package @@ -161,9 +178,9 @@ def compress_for_output_listing(paths: Iterable[str]) -> Tuple[Set[str], Set[str folders = set() files = set() for path in will_remove: - if path.endswith(".pyc"): + if path.endswith('.pyc'): continue - if path.endswith("__init__.py") or ".dist-info" in path: + if path.endswith('__init__.py') or '.dist-info' in path: folders.add(os.path.dirname(path)) files.add(path) @@ -177,18 +194,18 @@ def compress_for_output_listing(paths: Iterable[str]) -> Tuple[Set[str], Set[str for folder in folders: for dirpath, _, dirfiles in os.walk(folder): for fname in dirfiles: - if fname.endswith(".pyc"): + if fname.endswith('.pyc'): continue file_ = os.path.join(dirpath, fname) if ( - os.path.isfile(file_) - and os.path.normcase(file_) not in _normcased_files + os.path.isfile(file_) and + os.path.normcase(file_) not in _normcased_files ): # We are skipping this file. Add it to the set. will_skip.add(file_) - will_remove = files | {os.path.join(folder, "*") for folder in folders} + will_remove = files | {os.path.join(folder, '*') for folder in folders} return will_remove, will_skip @@ -200,10 +217,10 @@ class StashedUninstallPathSet: def __init__(self) -> None: # Mapping from source file root to [Adjacent]TempDirectory # for files under that directory. - self._save_dirs: Dict[str, TempDirectory] = {} + self._save_dirs: dict[str, TempDirectory] = {} # (old path, new path) tuples for each move that may need # to be undone. - self._moves: List[Tuple[str, str]] = [] + self._moves: list[tuple[str, str]] = [] def _get_directory_stash(self, path: str) -> str: """Stashes a directory. @@ -214,7 +231,7 @@ class StashedUninstallPathSet: try: save_dir: TempDirectory = AdjacentTempDirectory(path) except OSError: - save_dir = TempDirectory(kind="uninstall") + save_dir = TempDirectory(kind='uninstall') self._save_dirs[os.path.normcase(path)] = save_dir return save_dir.path @@ -238,7 +255,7 @@ class StashedUninstallPathSet: else: # Did not find any suitable root head = os.path.dirname(path) - save_dir = TempDirectory(kind="uninstall") + save_dir = TempDirectory(kind='uninstall') self._save_dirs[head] = save_dir relpath = os.path.relpath(path, head) @@ -277,19 +294,19 @@ class StashedUninstallPathSet: def rollback(self) -> None: """Undoes the uninstall by moving stashed files back.""" for p in self._moves: - logger.info("Moving to %s\n from %s", *p) + logger.info('Moving to %s\n from %s', *p) for new_path, path in self._moves: try: - logger.debug("Replacing %s from %s", new_path, path) + logger.debug('Replacing %s from %s', new_path, path) if os.path.isfile(new_path) or os.path.islink(new_path): os.unlink(new_path) elif os.path.isdir(new_path): rmtree(new_path) renames(path, new_path) except OSError as ex: - logger.error("Failed to restore %s", new_path) - logger.debug("Exception: %s", ex) + logger.error('Failed to restore %s', new_path) + logger.debug('Exception: %s', ex) self.commit() @@ -303,9 +320,9 @@ class UninstallPathSet: requirement.""" def __init__(self, dist: BaseDistribution) -> None: - self._paths: Set[str] = set() - self._refuse: Set[str] = set() - self._pth: Dict[str, UninstallPthEntries] = {} + self._paths: set[str] = set() + self._refuse: set[str] = set() + self._pth: dict[str, UninstallPthEntries] = {} self._dist = dist self._moved_paths = StashedUninstallPathSet() @@ -333,7 +350,7 @@ class UninstallPathSet: # __pycache__ files can show up after 'installed-files.txt' is created, # due to imports - if os.path.splitext(path)[1] == ".py": + if os.path.splitext(path)[1] == '.py': self.add(cache_from_source(path)) def add_pth(self, pth_file: str, entry: str) -> None: @@ -356,8 +373,8 @@ class UninstallPathSet: ) return - dist_name_version = f"{self._dist.raw_name}-{self._dist.version}" - logger.info("Uninstalling %s:", dist_name_version) + dist_name_version = f'{self._dist.raw_name}-{self._dist.version}' + logger.info('Uninstalling %s:', dist_name_version) with indent_log(): if auto_confirm or self._allowed_to_proceed(verbose): @@ -367,12 +384,12 @@ class UninstallPathSet: for path in sorted(compact(for_rename)): moved.stash(path) - logger.verbose("Removing file or directory %s", path) + logger.verbose('Removing file or directory %s', path) for pth in self._pth.values(): pth.remove() - logger.info("Successfully uninstalled %s", dist_name_version) + logger.info('Successfully uninstalled %s', dist_name_version) def _allowed_to_proceed(self, verbose: bool) -> bool: """Display which files would be deleted and prompt for confirmation""" @@ -394,13 +411,13 @@ class UninstallPathSet: will_remove = set(self._paths) will_skip = set() - _display("Would remove:", will_remove) - _display("Would not remove (might be manually added):", will_skip) - _display("Would not remove (outside of prefix):", self._refuse) + _display('Would remove:', will_remove) + _display('Would not remove (might be manually added):', will_skip) + _display('Would not remove (outside of prefix):', self._refuse) if verbose: - _display("Will actually move:", compress_for_rename(self._paths)) + _display('Will actually move:', compress_for_rename(self._paths)) - return ask("Proceed (Y/n)? ", ("y", "n", "")) != "n" + return ask('Proceed (Y/n)? ', ('y', 'n', '')) != 'n' def rollback(self) -> None: """Rollback the changes previously made by remove().""" @@ -410,7 +427,7 @@ class UninstallPathSet: self._dist.raw_name, ) return - logger.info("Rolling back uninstall of %s", self._dist.raw_name) + logger.info('Rolling back uninstall of %s', self._dist.raw_name) self._moved_paths.rollback() for pth in self._pth.values(): pth.rollback() @@ -420,12 +437,12 @@ class UninstallPathSet: self._moved_paths.commit() @classmethod - def from_dist(cls, dist: BaseDistribution) -> "UninstallPathSet": + def from_dist(cls, dist: BaseDistribution) -> UninstallPathSet: dist_location = dist.location info_location = dist.info_location if dist_location is None: logger.info( - "Not uninstalling %s since it is not installed", + 'Not uninstalling %s since it is not installed', dist.canonical_name, ) return cls(dist) @@ -433,7 +450,7 @@ class UninstallPathSet: normalized_dist_location = normalize_path(dist_location) if not dist.local: logger.info( - "Not uninstalling %s at %s, outside environment %s", + 'Not uninstalling %s at %s, outside environment %s', dist.canonical_name, normalized_dist_location, sys.prefix, @@ -442,11 +459,11 @@ class UninstallPathSet: if normalized_dist_location in { p - for p in {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")} + for p in {sysconfig.get_path('stdlib'), sysconfig.get_path('platstdlib')} if p }: logger.info( - "Not uninstalling %s at %s, as it is in the standard library.", + 'Not uninstalling %s at %s, as it is in the standard library.', dist.canonical_name, normalized_dist_location, ) @@ -459,12 +476,12 @@ class UninstallPathSet: # directory. This means it is not a modern .dist-info installation, an # egg, or legacy editable. setuptools_flat_installation = ( - dist.installed_with_setuptools_egg_info - and info_location is not None - and os.path.exists(info_location) + dist.installed_with_setuptools_egg_info and + info_location is not None and + os.path.exists(info_location) and # If dist is editable and the location points to a ``.egg-info``, # we are in fact in the legacy editable case. - and not info_location.endswith(f"{dist.setuptools_filename}.egg-info") + not info_location.endswith(f'{dist.setuptools_filename}.egg-info') ) # Uninstall cases order do matter as in the case of 2 installs of the @@ -479,31 +496,31 @@ class UninstallPathSet: # FIXME: need a test for this elif block # occurs with --single-version-externally-managed/--record outside # of pip - elif dist.is_file("top_level.txt"): + elif dist.is_file('top_level.txt'): try: - namespace_packages = dist.read_text("namespace_packages.txt") + namespace_packages = dist.read_text('namespace_packages.txt') except FileNotFoundError: namespaces = [] else: namespaces = namespace_packages.splitlines(keepends=False) for top_level_pkg in [ p - for p in dist.read_text("top_level.txt").splitlines() + for p in dist.read_text('top_level.txt').splitlines() if p and p not in namespaces ]: path = os.path.join(dist_location, top_level_pkg) paths_to_remove.add(path) - paths_to_remove.add(f"{path}.py") - paths_to_remove.add(f"{path}.pyc") - paths_to_remove.add(f"{path}.pyo") + paths_to_remove.add(f'{path}.py') + paths_to_remove.add(f'{path}.pyc') + paths_to_remove.add(f'{path}.pyo') elif dist.installed_by_distutils: raise UninstallationError( - "Cannot uninstall {!r}. It is a distutils installed project " - "and thus we cannot accurately determine which files belong " - "to it which would lead to only a partial uninstall.".format( + 'Cannot uninstall {!r}. It is a distutils installed project ' + 'and thus we cannot accurately determine which files belong ' + 'to it which would lead to only a partial uninstall.'.format( dist.raw_name, - ) + ), ) elif dist.installed_as_egg: @@ -514,9 +531,9 @@ class UninstallPathSet: easy_install_egg = os.path.split(dist_location)[1] easy_install_pth = os.path.join( os.path.dirname(dist_location), - "easy-install.pth", + 'easy-install.pth', ) - paths_to_remove.add_pth(easy_install_pth, "./" + easy_install_egg) + paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) elif dist.installed_with_dist_info: for path in uninstallation_paths(dist): @@ -528,18 +545,18 @@ class UninstallPathSet: with open(develop_egg_link) as fh: link_pointer = os.path.normcase(fh.readline().strip()) assert link_pointer == dist_location, ( - f"Egg-link {link_pointer} does not match installed location of " - f"{dist.raw_name} (at {dist_location})" + f'Egg-link {link_pointer} does not match installed location of ' + f'{dist.raw_name} (at {dist_location})' ) paths_to_remove.add(develop_egg_link) easy_install_pth = os.path.join( - os.path.dirname(develop_egg_link), "easy-install.pth" + os.path.dirname(develop_egg_link), 'easy-install.pth', ) paths_to_remove.add_pth(easy_install_pth, dist_location) else: logger.debug( - "Not sure how to uninstall: %s - Check: %s", + 'Not sure how to uninstall: %s - Check: %s', dist, dist_location, ) @@ -551,10 +568,10 @@ class UninstallPathSet: # find distutils scripts= scripts try: - for script in dist.iterdir("scripts"): + for script in dist.iterdir('scripts'): paths_to_remove.add(os.path.join(bin_dir, script.name)) if WINDOWS: - paths_to_remove.add(os.path.join(bin_dir, f"{script.name}.bat")) + paths_to_remove.add(os.path.join(bin_dir, f'{script.name}.bat')) except (FileNotFoundError, NotADirectoryError): pass @@ -564,9 +581,9 @@ class UninstallPathSet: bin_dir: str, ) -> Iterator[str]: for entry_point in dist.iter_entry_points(): - if entry_point.group == "console_scripts": + if entry_point.group == 'console_scripts': yield from _script_names(bin_dir, entry_point.name, False) - elif entry_point.group == "gui_scripts": + elif entry_point.group == 'gui_scripts': yield from _script_names(bin_dir, entry_point.name, True) for s in iter_scripts_to_remove(dist, bin_dir): @@ -578,8 +595,8 @@ class UninstallPathSet: class UninstallPthEntries: def __init__(self, pth_file: str) -> None: self.file = pth_file - self.entries: Set[str] = set() - self._saved_lines: Optional[List[bytes]] = None + self.entries: set[str] = set() + self._saved_lines: list[bytes] | None = None def add(self, entry: str) -> None: entry = os.path.normcase(entry) @@ -593,41 +610,41 @@ class UninstallPthEntries: # have more than "\\sever\share". Valid examples: "\\server\share\" or # "\\server\share\folder". if WINDOWS and not os.path.splitdrive(entry)[0]: - entry = entry.replace("\\", "/") + entry = entry.replace('\\', '/') self.entries.add(entry) def remove(self) -> None: - logger.verbose("Removing pth entries from %s:", self.file) + logger.verbose('Removing pth entries from %s:', self.file) # If the file doesn't exist, log a warning and return if not os.path.isfile(self.file): - logger.warning("Cannot remove entries from nonexistent file %s", self.file) + logger.warning('Cannot remove entries from nonexistent file %s', self.file) return - with open(self.file, "rb") as fh: + with open(self.file, 'rb') as fh: # windows uses '\r\n' with py3k, but uses '\n' with py2.x lines = fh.readlines() self._saved_lines = lines - if any(b"\r\n" in line for line in lines): - endline = "\r\n" + if any(b'\r\n' in line for line in lines): + endline = '\r\n' else: - endline = "\n" + endline = '\n' # handle missing trailing newline - if lines and not lines[-1].endswith(endline.encode("utf-8")): - lines[-1] = lines[-1] + endline.encode("utf-8") + if lines and not lines[-1].endswith(endline.encode('utf-8')): + lines[-1] = lines[-1] + endline.encode('utf-8') for entry in self.entries: try: - logger.verbose("Removing entry: %s", entry) - lines.remove((entry + endline).encode("utf-8")) + logger.verbose('Removing entry: %s', entry) + lines.remove((entry + endline).encode('utf-8')) except ValueError: pass - with open(self.file, "wb") as fh: + with open(self.file, 'wb') as fh: fh.writelines(lines) def rollback(self) -> bool: if self._saved_lines is None: - logger.error("Cannot roll back changes to %s, none were made", self.file) + logger.error('Cannot roll back changes to %s, none were made', self.file) return False - logger.debug("Rolling %s back to previous state", self.file) - with open(self.file, "wb") as fh: + logger.debug('Rolling %s back to previous state', self.file) + with open(self.file, 'wb') as fh: fh.writelines(self._saved_lines) return True diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/base.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/base.py index 42dade1..64467e4 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/base.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/base.py @@ -1,20 +1,24 @@ -from typing import Callable, List, Optional +from __future__ import annotations + +from typing import Callable +from typing import List +from typing import Optional from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_set import RequirementSet InstallRequirementProvider = Callable[ - [str, Optional[InstallRequirement]], InstallRequirement + [str, Optional[InstallRequirement]], InstallRequirement, ] class BaseResolver: def resolve( - self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + self, root_reqs: list[InstallRequirement], check_supported_wheels: bool, ) -> RequirementSet: raise NotImplementedError() def get_installation_order( - self, req_set: RequirementSet - ) -> List[InstallRequirement]: + self, req_set: RequirementSet, + ) -> list[InstallRequirement]: raise NotImplementedError() diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/resolver.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/resolver.py index 8c149d4..cb59ce7 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/resolver.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/resolver.py @@ -9,42 +9,43 @@ for top-level requirements: for sub-dependencies a. "first found, wins" (where the order is breadth first) """ - # The following comment should be removed at some point in the future. # mypy: strict-optional=False +from __future__ import annotations import logging import sys from collections import defaultdict from itertools import chain -from typing import DefaultDict, Iterable, List, Optional, Set, Tuple - -from pip._vendor.packaging import specifiers -from pip._vendor.packaging.requirements import Requirement +from typing import DefaultDict +from typing import Iterable +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple from pip._internal.cache import WheelCache -from pip._internal.exceptions import ( - BestVersionAlreadyInstalled, - DistributionNotFound, - HashError, - HashErrors, - NoneMetadataError, - UnsupportedPythonVersion, -) +from pip._internal.exceptions import BestVersionAlreadyInstalled +from pip._internal.exceptions import DistributionNotFound +from pip._internal.exceptions import HashError +from pip._internal.exceptions import HashErrors +from pip._internal.exceptions import NoneMetadataError +from pip._internal.exceptions import UnsupportedPythonVersion from pip._internal.index.package_finder import PackageFinder from pip._internal.metadata import BaseDistribution from pip._internal.models.link import Link from pip._internal.operations.prepare import RequirementPreparer -from pip._internal.req.req_install import ( - InstallRequirement, - check_invalid_constraint_type, -) +from pip._internal.req.req_install import check_invalid_constraint_type +from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_set import RequirementSet -from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider +from pip._internal.resolution.base import BaseResolver +from pip._internal.resolution.base import InstallRequirementProvider from pip._internal.utils.compatibility_tags import get_supported from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import normalize_version_info from pip._internal.utils.packaging import check_requires_python +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.requirements import Requirement logger = logging.getLogger(__name__) @@ -53,7 +54,7 @@ DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]] def _check_dist_requires_python( dist: BaseDistribution, - version_info: Tuple[int, int, int], + version_info: tuple[int, int, int], ignore_requires_python: bool = False, ) -> None: """ @@ -82,17 +83,17 @@ def _check_dist_requires_python( ) except specifiers.InvalidSpecifier as exc: logger.warning( - "Package %r has an invalid Requires-Python: %s", dist.raw_name, exc + 'Package %r has an invalid Requires-Python: %s', dist.raw_name, exc, ) return if is_compatible: return - version = ".".join(map(str, version_info)) + version = '.'.join(map(str, version_info)) if ignore_requires_python: logger.debug( - "Ignoring failed Requires-Python check for package %r: %s not in %r", + 'Ignoring failed Requires-Python check for package %r: %s not in %r', dist.raw_name, version, requires_python, @@ -100,9 +101,9 @@ def _check_dist_requires_python( return raise UnsupportedPythonVersion( - "Package {!r} requires a different Python: {} not in {!r}".format( - dist.raw_name, version, requires_python - ) + 'Package {!r} requires a different Python: {} not in {!r}'.format( + dist.raw_name, version, requires_python, + ), ) @@ -111,13 +112,13 @@ class Resolver(BaseResolver): the requested operation without breaking the requirements of any package. """ - _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + _allowed_strategies = {'eager', 'only-if-needed', 'to-satisfy-only'} def __init__( self, preparer: RequirementPreparer, finder: PackageFinder, - wheel_cache: Optional[WheelCache], + wheel_cache: WheelCache | None, make_install_req: InstallRequirementProvider, use_user_site: bool, ignore_dependencies: bool, @@ -125,7 +126,7 @@ class Resolver(BaseResolver): ignore_requires_python: bool, force_reinstall: bool, upgrade_strategy: str, - py_version_info: Optional[Tuple[int, ...]] = None, + py_version_info: tuple[int, ...] | None = None, ) -> None: super().__init__() assert upgrade_strategy in self._allowed_strategies @@ -152,7 +153,7 @@ class Resolver(BaseResolver): self._discovered_dependencies: DiscoveredDependencies = defaultdict(list) def resolve( - self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + self, root_reqs: list[InstallRequirement], check_supported_wheels: bool, ) -> RequirementSet: """Resolve what operations need to be done @@ -174,7 +175,7 @@ class Resolver(BaseResolver): # exceptions cannot be checked ahead of time, because # _populate_link() needs to be called before we can make decisions # based on link type. - discovered_reqs: List[InstallRequirement] = [] + discovered_reqs: list[InstallRequirement] = [] hash_errors = HashErrors() for req in chain(requirement_set.all_requirements, discovered_reqs): try: @@ -189,12 +190,12 @@ class Resolver(BaseResolver): return requirement_set def _is_upgrade_allowed(self, req: InstallRequirement) -> bool: - if self.upgrade_strategy == "to-satisfy-only": + if self.upgrade_strategy == 'to-satisfy-only': return False - elif self.upgrade_strategy == "eager": + elif self.upgrade_strategy == 'eager': return True else: - assert self.upgrade_strategy == "only-if-needed" + assert self.upgrade_strategy == 'only-if-needed' return req.user_supplied or req.constraint def _set_req_to_reinstall(self, req: InstallRequirement) -> None: @@ -208,8 +209,8 @@ class Resolver(BaseResolver): req.satisfied_by = None def _check_skip_installed( - self, req_to_install: InstallRequirement - ) -> Optional[str]: + self, req_to_install: InstallRequirement, + ) -> str | None: """Check if req_to_install should be skipped. This will check if the req is installed, and whether we should upgrade @@ -239,9 +240,9 @@ class Resolver(BaseResolver): return None if not self._is_upgrade_allowed(req_to_install): - if self.upgrade_strategy == "only-if-needed": - return "already satisfied, skipping upgrade" - return "already satisfied" + if self.upgrade_strategy == 'only-if-needed': + return 'already satisfied, skipping upgrade' + return 'already satisfied' # Check for the possibility of an upgrade. For link-based # requirements we have to pull the tree down and inspect to assess @@ -251,7 +252,7 @@ class Resolver(BaseResolver): self.finder.find_requirement(req_to_install, upgrade=True) except BestVersionAlreadyInstalled: # Then the best version is installed. - return "already up-to-date" + return 'already up-to-date' except DistributionNotFound: # No distribution found, so we squash the error. It will # be raised later when we re-try later to do the install. @@ -261,7 +262,7 @@ class Resolver(BaseResolver): self._set_req_to_reinstall(req_to_install) return None - def _find_requirement_link(self, req: InstallRequirement) -> Optional[Link]: + def _find_requirement_link(self, req: InstallRequirement) -> Link | None: upgrade = self._is_upgrade_allowed(req) best_candidate = self.finder.find_requirement(req, upgrade) if not best_candidate: @@ -270,14 +271,14 @@ class Resolver(BaseResolver): # Log a warning per PEP 592 if necessary before returning. link = best_candidate.link if link.is_yanked: - reason = link.yanked_reason or "" + reason = link.yanked_reason or '' msg = ( # Mark this as a unicode string to prevent # "UnicodeEncodeError: 'ascii' codec can't encode character" # in Python 2 when the reason contains non-ascii characters. - "The candidate selected for download or install is a " - "yanked version: {candidate}\n" - "Reason for being yanked: {reason}" + 'The candidate selected for download or install is a ' + 'yanked version: {candidate}\n' + 'Reason for being yanked: {reason}' ).format(candidate=best_candidate, reason=reason) logger.warning(msg) @@ -307,7 +308,7 @@ class Resolver(BaseResolver): supported_tags=get_supported(), ) if cache_entry is not None: - logger.debug("Using cached wheel link: %s", cache_entry.link) + logger.debug('Using cached wheel link: %s', cache_entry.link) if req.link is req.original_link and cache_entry.persistent: req.original_link_is_in_wheel_cache = True req.link = cache_entry.link @@ -344,16 +345,16 @@ class Resolver(BaseResolver): if req.satisfied_by: should_modify = ( - self.upgrade_strategy != "to-satisfy-only" - or self.force_reinstall - or self.ignore_installed - or req.link.scheme == "file" + self.upgrade_strategy != 'to-satisfy-only' or + self.force_reinstall or + self.ignore_installed or + req.link.scheme == 'file' ) if should_modify: self._set_req_to_reinstall(req) else: logger.info( - "Requirement already satisfied (use --upgrade to upgrade): %s", + 'Requirement already satisfied (use --upgrade to upgrade): %s', req, ) return dist @@ -362,7 +363,7 @@ class Resolver(BaseResolver): self, requirement_set: RequirementSet, req_to_install: InstallRequirement, - ) -> List[InstallRequirement]: + ) -> list[InstallRequirement]: """Prepare a single requirements file. :return: A list of additional InstallRequirements to also install. @@ -385,7 +386,7 @@ class Resolver(BaseResolver): ignore_requires_python=self.ignore_requires_python, ) - more_reqs: List[InstallRequirement] = [] + more_reqs: list[InstallRequirement] = [] def add_req(subreq: Requirement, extras_requested: Iterable[str]) -> None: # This idiosyncratically converts the Requirement to str and let @@ -415,11 +416,11 @@ class Resolver(BaseResolver): if not self.ignore_dependencies: if req_to_install.extras: logger.debug( - "Installing extra requirements: %r", - ",".join(req_to_install.extras), + 'Installing extra requirements: %r', + ','.join(req_to_install.extras), ) missing_requested = sorted( - set(req_to_install.extras) - set(dist.iter_provided_extras()) + set(req_to_install.extras) - set(dist.iter_provided_extras()), ) for missing in missing_requested: logger.warning( @@ -430,7 +431,7 @@ class Resolver(BaseResolver): ) available_requested = sorted( - set(dist.iter_provided_extras()) & set(req_to_install.extras) + set(dist.iter_provided_extras()) & set(req_to_install.extras), ) for subreq in dist.iter_dependencies(available_requested): add_req(subreq, extras_requested=available_requested) @@ -438,8 +439,8 @@ class Resolver(BaseResolver): return more_reqs def get_installation_order( - self, req_set: RequirementSet - ) -> List[InstallRequirement]: + self, req_set: RequirementSet, + ) -> list[InstallRequirement]: """Create the installation order. The installation order is topological - requirements are installed @@ -450,7 +451,7 @@ class Resolver(BaseResolver): # installs the user specified things in the order given, except when # dependencies must come earlier to achieve topological order. order = [] - ordered_reqs: Set[InstallRequirement] = set() + ordered_reqs: set[InstallRequirement] = set() def schedule(req: InstallRequirement) -> None: if req.satisfied_by or req in ordered_reqs: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/base.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/base.py index b206692..53f3064 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/base.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/base.py @@ -1,45 +1,53 @@ -from typing import FrozenSet, Iterable, Optional, Tuple, Union +from __future__ import annotations -from pip._vendor.packaging.specifiers import SpecifierSet -from pip._vendor.packaging.utils import NormalizedName, canonicalize_name -from pip._vendor.packaging.version import LegacyVersion, Version +from typing import FrozenSet +from typing import Iterable +from typing import Optional +from typing import Tuple +from typing import Union -from pip._internal.models.link import Link, links_equivalent +from pip._internal.models.link import Link +from pip._internal.models.link import links_equivalent from pip._internal.req.req_install import InstallRequirement from pip._internal.utils.hashes import Hashes +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.utils import NormalizedName +from pip._vendor.packaging.version import LegacyVersion +from pip._vendor.packaging.version import Version -CandidateLookup = Tuple[Optional["Candidate"], Optional[InstallRequirement]] +CandidateLookup = Tuple[Optional['Candidate'], Optional[InstallRequirement]] CandidateVersion = Union[LegacyVersion, Version] -def format_name(project: str, extras: FrozenSet[str]) -> str: +def format_name(project: str, extras: frozenset[str]) -> str: if not extras: return project canonical_extras = sorted(canonicalize_name(e) for e in extras) - return "{}[{}]".format(project, ",".join(canonical_extras)) + return '{}[{}]'.format(project, ','.join(canonical_extras)) class Constraint: def __init__( - self, specifier: SpecifierSet, hashes: Hashes, links: FrozenSet[Link] + self, specifier: SpecifierSet, hashes: Hashes, links: frozenset[Link], ) -> None: self.specifier = specifier self.hashes = hashes self.links = links @classmethod - def empty(cls) -> "Constraint": + def empty(cls) -> Constraint: return Constraint(SpecifierSet(), Hashes(), frozenset()) @classmethod - def from_ireq(cls, ireq: InstallRequirement) -> "Constraint": + def from_ireq(cls, ireq: InstallRequirement) -> Constraint: links = frozenset([ireq.link]) if ireq.link else frozenset() return Constraint(ireq.specifier, ireq.hashes(trust_internet=False), links) def __bool__(self) -> bool: return bool(self.specifier) or bool(self.hashes) or bool(self.links) - def __and__(self, other: InstallRequirement) -> "Constraint": + def __and__(self, other: InstallRequirement) -> Constraint: if not isinstance(other, InstallRequirement): return NotImplemented specifier = self.specifier & other.specifier @@ -49,7 +57,7 @@ class Constraint: links = links.union([other.link]) return Constraint(specifier, hashes, links) - def is_satisfied_by(self, candidate: "Candidate") -> bool: + def is_satisfied_by(self, candidate: Candidate) -> bool: # Reject if there are any mismatched URL constraints on this package. if self.links and not all(_match_link(link, candidate) for link in self.links): return False @@ -68,7 +76,7 @@ class Requirement: in which case ``name`` would contain the ``[...]`` part, while this refers to the name of the project. """ - raise NotImplementedError("Subclass should override") + raise NotImplementedError('Subclass should override') @property def name(self) -> str: @@ -77,19 +85,19 @@ class Requirement: This is different from ``project_name`` if this requirement contains extras, where ``project_name`` would not contain the ``[...]`` part. """ - raise NotImplementedError("Subclass should override") + raise NotImplementedError('Subclass should override') - def is_satisfied_by(self, candidate: "Candidate") -> bool: + def is_satisfied_by(self, candidate: Candidate) -> bool: return False def get_candidate_lookup(self) -> CandidateLookup: - raise NotImplementedError("Subclass should override") + raise NotImplementedError('Subclass should override') def format_for_error(self) -> str: - raise NotImplementedError("Subclass should override") + raise NotImplementedError('Subclass should override') -def _match_link(link: Link, candidate: "Candidate") -> bool: +def _match_link(link: Link, candidate: Candidate) -> bool: if candidate.source_link: return links_equivalent(link, candidate.source_link) return False @@ -104,7 +112,7 @@ class Candidate: in which case ``name`` would contain the ``[...]`` part, while this refers to the name of the project. """ - raise NotImplementedError("Override in subclass") + raise NotImplementedError('Override in subclass') @property def name(self) -> str: @@ -113,29 +121,29 @@ class Candidate: This is different from ``project_name`` if this candidate contains extras, where ``project_name`` would not contain the ``[...]`` part. """ - raise NotImplementedError("Override in subclass") + raise NotImplementedError('Override in subclass') @property def version(self) -> CandidateVersion: - raise NotImplementedError("Override in subclass") + raise NotImplementedError('Override in subclass') @property def is_installed(self) -> bool: - raise NotImplementedError("Override in subclass") + raise NotImplementedError('Override in subclass') @property def is_editable(self) -> bool: - raise NotImplementedError("Override in subclass") + raise NotImplementedError('Override in subclass') @property - def source_link(self) -> Optional[Link]: - raise NotImplementedError("Override in subclass") + def source_link(self) -> Link | None: + raise NotImplementedError('Override in subclass') - def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: - raise NotImplementedError("Override in subclass") + def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]: + raise NotImplementedError('Override in subclass') - def get_install_requirement(self) -> Optional[InstallRequirement]: - raise NotImplementedError("Override in subclass") + def get_install_requirement(self) -> InstallRequirement | None: + raise NotImplementedError('Override in subclass') def format_for_error(self) -> str: - raise NotImplementedError("Subclass should override") + raise NotImplementedError('Subclass should override') diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/candidates.py index 9b8450e..7a18098 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/candidates.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/candidates.py @@ -1,26 +1,35 @@ +from __future__ import annotations + import logging import sys -from typing import TYPE_CHECKING, Any, FrozenSet, Iterable, Optional, Tuple, Union, cast +from typing import Any +from typing import cast +from typing import FrozenSet +from typing import Iterable +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union -from pip._vendor.packaging.utils import NormalizedName, canonicalize_name -from pip._vendor.packaging.version import Version - -from pip._internal.exceptions import ( - HashError, - InstallationSubprocessError, - MetadataInconsistent, -) +from pip._internal.exceptions import HashError +from pip._internal.exceptions import InstallationSubprocessError +from pip._internal.exceptions import MetadataInconsistent from pip._internal.metadata import BaseDistribution -from pip._internal.models.link import Link, links_equivalent +from pip._internal.models.link import Link +from pip._internal.models.link import links_equivalent from pip._internal.models.wheel import Wheel -from pip._internal.req.constructors import ( - install_req_from_editable, - install_req_from_line, -) +from pip._internal.req.constructors import install_req_from_editable +from pip._internal.req.constructors import install_req_from_line from pip._internal.req.req_install import InstallRequirement from pip._internal.utils.misc import normalize_version_info +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.utils import NormalizedName +from pip._vendor.packaging.version import Version -from .base import Candidate, CandidateVersion, Requirement, format_name +from .base import Candidate +from .base import CandidateVersion +from .base import format_name +from .base import Requirement if TYPE_CHECKING: from .factory import Factory @@ -28,16 +37,16 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) BaseCandidate = Union[ - "AlreadyInstalledCandidate", - "EditableCandidate", - "LinkCandidate", + 'AlreadyInstalledCandidate', + 'EditableCandidate', + 'LinkCandidate', ] # Avoid conflicting with the PyPI package "Python". -REQUIRES_PYTHON_IDENTIFIER = cast(NormalizedName, "") +REQUIRES_PYTHON_IDENTIFIER = cast(NormalizedName, '') -def as_base_candidate(candidate: Candidate) -> Optional[BaseCandidate]: +def as_base_candidate(candidate: Candidate) -> BaseCandidate | None: """The runtime version of BaseCandidate.""" base_candidate_classes = ( AlreadyInstalledCandidate, @@ -50,9 +59,9 @@ def as_base_candidate(candidate: Candidate) -> Optional[BaseCandidate]: def make_install_req_from_link( - link: Link, template: InstallRequirement + link: Link, template: InstallRequirement, ) -> InstallRequirement: - assert not template.editable, "template is editable" + assert not template.editable, 'template is editable' if template.req: line = str(template.req) else: @@ -76,9 +85,9 @@ def make_install_req_from_link( def make_install_req_from_editable( - link: Link, template: InstallRequirement + link: Link, template: InstallRequirement, ) -> InstallRequirement: - assert template.editable, "template not editable" + assert template.editable, 'template not editable' return install_req_from_editable( link.url, user_supplied=template.user_supplied, @@ -96,14 +105,14 @@ def make_install_req_from_editable( def _make_install_req_from_dist( - dist: BaseDistribution, template: InstallRequirement + dist: BaseDistribution, template: InstallRequirement, ) -> InstallRequirement: if template.req: line = str(template.req) elif template.link: - line = f"{dist.canonical_name} @ {template.link.url}" + line = f'{dist.canonical_name} @ {template.link.url}' else: - line = f"{dist.canonical_name}=={dist.version}" + line = f'{dist.canonical_name}=={dist.version}' ireq = install_req_from_line( line, user_supplied=template.user_supplied, @@ -145,9 +154,9 @@ class _InstallRequirementBackedCandidate(Candidate): link: Link, source_link: Link, ireq: InstallRequirement, - factory: "Factory", - name: Optional[NormalizedName] = None, - version: Optional[CandidateVersion] = None, + factory: Factory, + name: NormalizedName | None = None, + version: CandidateVersion | None = None, ) -> None: self._link = link self._source_link = source_link @@ -158,10 +167,10 @@ class _InstallRequirementBackedCandidate(Candidate): self.dist = self._prepare() def __str__(self) -> str: - return f"{self.name} {self.version}" + return f'{self.name} {self.version}' def __repr__(self) -> str: - return "{class_name}({link!r})".format( + return '{class_name}({link!r})'.format( class_name=self.__class__.__name__, link=str(self._link), ) @@ -175,7 +184,7 @@ class _InstallRequirementBackedCandidate(Candidate): return False @property - def source_link(self) -> Optional[Link]: + def source_link(self) -> Link | None: return self._source_link @property @@ -196,28 +205,28 @@ class _InstallRequirementBackedCandidate(Candidate): return self._version def format_for_error(self) -> str: - return "{} {} (from {})".format( + return '{} {} (from {})'.format( self.name, self.version, self._link.file_path if self._link.is_file else self._link, ) def _prepare_distribution(self) -> BaseDistribution: - raise NotImplementedError("Override in subclass") + raise NotImplementedError('Override in subclass') def _check_metadata_consistency(self, dist: BaseDistribution) -> None: """Check for consistency of project name and version of dist.""" if self._name is not None and self._name != dist.canonical_name: raise MetadataInconsistent( self._ireq, - "name", + 'name', self._name, dist.canonical_name, ) if self._version is not None and self._version != dist.version: raise MetadataInconsistent( self._ireq, - "version", + 'version', str(self._version), str(dist.version), ) @@ -233,19 +242,19 @@ class _InstallRequirementBackedCandidate(Candidate): raise except InstallationSubprocessError as exc: # The output has been presented already, so don't duplicate it. - exc.context = "See above for output." + exc.context = 'See above for output.' raise self._check_metadata_consistency(dist) return dist - def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]: requires = self.dist.iter_dependencies() if with_requires else () for r in requires: yield self._factory.make_requirement_from_spec(str(r), self._ireq) yield self._factory.make_requires_python_requirement(self.dist.requires_python) - def get_install_requirement(self) -> Optional[InstallRequirement]: + def get_install_requirement(self) -> InstallRequirement | None: return self._ireq @@ -256,32 +265,32 @@ class LinkCandidate(_InstallRequirementBackedCandidate): self, link: Link, template: InstallRequirement, - factory: "Factory", - name: Optional[NormalizedName] = None, - version: Optional[CandidateVersion] = None, + factory: Factory, + name: NormalizedName | None = None, + version: CandidateVersion | None = None, ) -> None: source_link = link cache_entry = factory.get_wheel_cache_entry(link, name) if cache_entry is not None: - logger.debug("Using cached wheel link: %s", cache_entry.link) + logger.debug('Using cached wheel link: %s', cache_entry.link) link = cache_entry.link ireq = make_install_req_from_link(link, template) assert ireq.link == link if ireq.link.is_wheel and not ireq.link.is_file: wheel = Wheel(ireq.link.filename) wheel_name = canonicalize_name(wheel.name) - assert name == wheel_name, f"{name!r} != {wheel_name!r} for wheel" + assert name == wheel_name, f'{name!r} != {wheel_name!r} for wheel' # Version may not be present for PEP 508 direct URLs if version is not None: wheel_version = Version(wheel.version) - assert version == wheel_version, "{!r} != {!r} for wheel {}".format( - version, wheel_version, name + assert version == wheel_version, '{!r} != {!r} for wheel {}'.format( + version, wheel_version, name, ) if ( - cache_entry is not None - and cache_entry.persistent - and template.link is template.original_link + cache_entry is not None and + cache_entry.persistent and + template.link is template.original_link ): ireq.original_link_is_in_wheel_cache = True @@ -306,9 +315,9 @@ class EditableCandidate(_InstallRequirementBackedCandidate): self, link: Link, template: InstallRequirement, - factory: "Factory", - name: Optional[NormalizedName] = None, - version: Optional[CandidateVersion] = None, + factory: Factory, + name: NormalizedName | None = None, + version: CandidateVersion | None = None, ) -> None: super().__init__( link=link, @@ -331,7 +340,7 @@ class AlreadyInstalledCandidate(Candidate): self, dist: BaseDistribution, template: InstallRequirement, - factory: "Factory", + factory: Factory, ) -> None: self.dist = dist self._ireq = _make_install_req_from_dist(dist, template) @@ -341,14 +350,14 @@ class AlreadyInstalledCandidate(Candidate): # The returned dist would be exactly the same as self.dist because we # set satisfied_by in _make_install_req_from_dist. # TODO: Supply reason based on force_reinstall and upgrade_strategy. - skip_reason = "already satisfied" + skip_reason = 'already satisfied' factory.preparer.prepare_installed_requirement(self._ireq, skip_reason) def __str__(self) -> str: return str(self.dist) def __repr__(self) -> str: - return "{class_name}({distribution!r})".format( + return '{class_name}({distribution!r})'.format( class_name=self.__class__.__name__, distribution=self.dist, ) @@ -378,15 +387,15 @@ class AlreadyInstalledCandidate(Candidate): return self.dist.editable def format_for_error(self) -> str: - return f"{self.name} {self.version} (Installed)" + return f'{self.name} {self.version} (Installed)' - def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]: if not with_requires: return for r in self.dist.iter_dependencies(): yield self._factory.make_requirement_from_spec(str(r), self._ireq) - def get_install_requirement(self) -> Optional[InstallRequirement]: + def get_install_requirement(self) -> InstallRequirement | None: return None @@ -418,17 +427,17 @@ class ExtrasCandidate(Candidate): def __init__( self, base: BaseCandidate, - extras: FrozenSet[str], + extras: frozenset[str], ) -> None: self.base = base self.extras = extras def __str__(self) -> str: - name, rest = str(self.base).split(" ", 1) - return "{}[{}] {}".format(name, ",".join(self.extras), rest) + name, rest = str(self.base).split(' ', 1) + return '{}[{}] {}'.format(name, ','.join(self.extras), rest) def __repr__(self) -> str: - return "{class_name}(base={base!r}, extras={extras!r})".format( + return '{class_name}(base={base!r}, extras={extras!r})'.format( class_name=self.__class__.__name__, base=self.base, extras=self.extras, @@ -456,8 +465,8 @@ class ExtrasCandidate(Candidate): return self.base.version def format_for_error(self) -> str: - return "{} [{}]".format( - self.base.format_for_error(), ", ".join(sorted(self.extras)) + return '{} [{}]'.format( + self.base.format_for_error(), ', '.join(sorted(self.extras)), ) @property @@ -469,10 +478,10 @@ class ExtrasCandidate(Candidate): return self.base.is_editable @property - def source_link(self) -> Optional[Link]: + def source_link(self) -> Link | None: return self.base.source_link - def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]: factory = self.base._factory # Add a dependency on the exact base @@ -495,12 +504,12 @@ class ExtrasCandidate(Candidate): for r in self.base.dist.iter_dependencies(valid_extras): requirement = factory.make_requirement_from_spec( - str(r), self.base._ireq, valid_extras + str(r), self.base._ireq, valid_extras, ) if requirement: yield requirement - def get_install_requirement(self) -> Optional[InstallRequirement]: + def get_install_requirement(self) -> InstallRequirement | None: # We don't return anything here, because we always # depend on the base candidate, and we'll get the # install requirement from that. @@ -511,19 +520,19 @@ class RequiresPythonCandidate(Candidate): is_installed = False source_link = None - def __init__(self, py_version_info: Optional[Tuple[int, ...]]) -> None: + def __init__(self, py_version_info: tuple[int, ...] | None) -> None: if py_version_info is not None: version_info = normalize_version_info(py_version_info) else: version_info = sys.version_info[:3] - self._version = Version(".".join(str(c) for c in version_info)) + self._version = Version('.'.join(str(c) for c in version_info)) # We don't need to implement __eq__() and __ne__() since there is always # only one RequiresPythonCandidate in a resolution, i.e. the host Python. # The built-in object.__eq__() and object.__ne__() do exactly what we want. def __str__(self) -> str: - return f"Python {self._version}" + return f'Python {self._version}' @property def project_name(self) -> NormalizedName: @@ -538,10 +547,10 @@ class RequiresPythonCandidate(Candidate): return self._version def format_for_error(self) -> str: - return f"Python {self.version}" + return f'Python {self.version}' - def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]: return () - def get_install_requirement(self) -> Optional[InstallRequirement]: + def get_install_requirement(self) -> InstallRequirement | None: return None diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/factory.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/factory.py index 261d8d5..3bfb4a1 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/factory.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/factory.py @@ -1,70 +1,68 @@ +from __future__ import annotations + import contextlib import functools import logging -from typing import ( - TYPE_CHECKING, - Dict, - FrozenSet, - Iterable, - Iterator, - List, - Mapping, - NamedTuple, - Optional, - Sequence, - Set, - Tuple, - TypeVar, - cast, -) +from typing import cast +from typing import Dict +from typing import FrozenSet +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Mapping +from typing import NamedTuple +from typing import Optional +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING +from typing import TypeVar -from pip._vendor.packaging.requirements import InvalidRequirement -from pip._vendor.packaging.specifiers import SpecifierSet -from pip._vendor.packaging.utils import NormalizedName, canonicalize_name -from pip._vendor.resolvelib import ResolutionImpossible - -from pip._internal.cache import CacheEntry, WheelCache -from pip._internal.exceptions import ( - DistributionNotFound, - InstallationError, - InstallationSubprocessError, - MetadataInconsistent, - UnsupportedPythonVersion, - UnsupportedWheel, -) +from pip._internal.cache import CacheEntry +from pip._internal.cache import WheelCache +from pip._internal.exceptions import DistributionNotFound +from pip._internal.exceptions import InstallationError +from pip._internal.exceptions import InstallationSubprocessError +from pip._internal.exceptions import MetadataInconsistent +from pip._internal.exceptions import UnsupportedPythonVersion +from pip._internal.exceptions import UnsupportedWheel from pip._internal.index.package_finder import PackageFinder -from pip._internal.metadata import BaseDistribution, get_default_environment +from pip._internal.metadata import BaseDistribution +from pip._internal.metadata import get_default_environment from pip._internal.models.link import Link from pip._internal.models.wheel import Wheel from pip._internal.operations.prepare import RequirementPreparer from pip._internal.req.constructors import install_req_from_link_and_ireq -from pip._internal.req.req_install import ( - InstallRequirement, - check_invalid_constraint_type, -) +from pip._internal.req.req_install import check_invalid_constraint_type +from pip._internal.req.req_install import InstallRequirement from pip._internal.resolution.base import InstallRequirementProvider from pip._internal.utils.compatibility_tags import get_supported from pip._internal.utils.hashes import Hashes from pip._internal.utils.packaging import get_requirement from pip._internal.utils.virtualenv import running_under_virtualenv +from pip._vendor.packaging.requirements import InvalidRequirement +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.utils import NormalizedName +from pip._vendor.resolvelib import ResolutionImpossible -from .base import Candidate, CandidateVersion, Constraint, Requirement -from .candidates import ( - AlreadyInstalledCandidate, - BaseCandidate, - EditableCandidate, - ExtrasCandidate, - LinkCandidate, - RequiresPythonCandidate, - as_base_candidate, -) -from .found_candidates import FoundCandidates, IndexCandidateInfo -from .requirements import ( - ExplicitRequirement, - RequiresPythonRequirement, - SpecifierRequirement, - UnsatisfiableRequirement, -) +from .base import Candidate +from .base import CandidateVersion +from .base import Constraint +from .base import Requirement +from .candidates import AlreadyInstalledCandidate +from .candidates import as_base_candidate +from .candidates import BaseCandidate +from .candidates import EditableCandidate +from .candidates import ExtrasCandidate +from .candidates import LinkCandidate +from .candidates import RequiresPythonCandidate +from .found_candidates import FoundCandidates +from .found_candidates import IndexCandidateInfo +from .requirements import ExplicitRequirement +from .requirements import RequiresPythonRequirement +from .requirements import SpecifierRequirement +from .requirements import UnsatisfiableRequirement if TYPE_CHECKING: from typing import Protocol @@ -76,14 +74,14 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) -C = TypeVar("C") +C = TypeVar('C') Cache = Dict[Link, C] class CollectedRootRequirements(NamedTuple): - requirements: List[Requirement] - constraints: Dict[str, Constraint] - user_requested: Dict[str, int] + requirements: list[Requirement] + constraints: dict[str, Constraint] + user_requested: dict[str, int] class Factory: @@ -92,13 +90,13 @@ class Factory: finder: PackageFinder, preparer: RequirementPreparer, make_install_req: InstallRequirementProvider, - wheel_cache: Optional[WheelCache], + wheel_cache: WheelCache | None, use_user_site: bool, force_reinstall: bool, ignore_installed: bool, ignore_requires_python: bool, suppress_build_failures: bool, - py_version_info: Optional[Tuple[int, ...]] = None, + py_version_info: tuple[int, ...] | None = None, ) -> None: self._finder = finder self.preparer = preparer @@ -113,9 +111,9 @@ class Factory: self._build_failures: Cache[InstallationError] = {} self._link_candidate_cache: Cache[LinkCandidate] = {} self._editable_candidate_cache: Cache[EditableCandidate] = {} - self._installed_candidate_cache: Dict[str, AlreadyInstalledCandidate] = {} - self._extras_candidate_cache: Dict[ - Tuple[int, FrozenSet[str]], ExtrasCandidate + self._installed_candidate_cache: dict[str, AlreadyInstalledCandidate] = {} + self._extras_candidate_cache: dict[ + tuple[int, frozenset[str]], ExtrasCandidate, ] = {} if not ignore_installed: @@ -137,11 +135,11 @@ class Factory: wheel = Wheel(link.filename) if wheel.supported(self._finder.target_python.get_tags()): return - msg = f"{link.filename} is not a supported wheel on this platform." + msg = f'{link.filename} is not a supported wheel on this platform.' raise UnsupportedWheel(msg) def _make_extras_candidate( - self, base: BaseCandidate, extras: FrozenSet[str] + self, base: BaseCandidate, extras: frozenset[str], ) -> ExtrasCandidate: cache_key = (id(base), extras) try: @@ -154,7 +152,7 @@ class Factory: def _make_candidate_from_dist( self, dist: BaseDistribution, - extras: FrozenSet[str], + extras: frozenset[str], template: InstallRequirement, ) -> Candidate: try: @@ -169,11 +167,11 @@ class Factory: def _make_candidate_from_link( self, link: Link, - extras: FrozenSet[str], + extras: frozenset[str], template: InstallRequirement, - name: Optional[NormalizedName], - version: Optional[CandidateVersion], - ) -> Optional[Candidate]: + name: NormalizedName | None, + version: CandidateVersion | None, + ) -> Candidate | None: # TODO: Check already installed candidate, and use it if the link and # editable flag match. @@ -194,17 +192,17 @@ class Factory: ) except MetadataInconsistent as e: logger.info( - "Discarding [blue underline]%s[/]: [yellow]%s[reset]", + 'Discarding [blue underline]%s[/]: [yellow]%s[reset]', link, e, - extra={"markup": True}, + extra={'markup': True}, ) self._build_failures[link] = e return None except InstallationSubprocessError as e: if not self._suppress_build_failures: raise - logger.warning("Discarding %s due to build failure: %s", link, e) + logger.warning('Discarding %s due to build failure: %s', link, e) self._build_failures[link] = e return None @@ -221,17 +219,17 @@ class Factory: ) except MetadataInconsistent as e: logger.info( - "Discarding [blue underline]%s[/]: [yellow]%s[reset]", + 'Discarding [blue underline]%s[/]: [yellow]%s[reset]', link, e, - extra={"markup": True}, + extra={'markup': True}, ) self._build_failures[link] = e return None except InstallationSubprocessError as e: if not self._suppress_build_failures: raise - logger.warning("Discarding %s due to build failure: %s", link, e) + logger.warning('Discarding %s due to build failure: %s', link, e) self._build_failures[link] = e return None base = self._link_candidate_cache[link] @@ -246,7 +244,7 @@ class Factory: specifier: SpecifierSet, hashes: Hashes, prefers_installed: bool, - incompatible_ids: Set[int], + incompatible_ids: set[int], ) -> Iterable[Candidate]: if not ireqs: return () @@ -256,17 +254,17 @@ class Factory: # all of them. # Hopefully the Project model can correct this mismatch in the future. template = ireqs[0] - assert template.req, "Candidates found on index must be PEP 508" + assert template.req, 'Candidates found on index must be PEP 508' name = canonicalize_name(template.req.name) - extras: FrozenSet[str] = frozenset() + extras: frozenset[str] = frozenset() for ireq in ireqs: - assert ireq.req, "Candidates found on index must be PEP 508" + assert ireq.req, 'Candidates found on index must be PEP 508' specifier &= ireq.req.specifier hashes &= ireq.hashes(trust_internet=False) extras |= frozenset(ireq.extras) - def _get_installed_candidate() -> Optional[Candidate]: + def _get_installed_candidate() -> Candidate | None: """Get the candidate for the currently-installed version.""" # If --force-reinstall is set, we want the version from the index # instead, so we "pretend" there is nothing installed. @@ -305,11 +303,11 @@ class Factory: def is_pinned(specifier: SpecifierSet) -> bool: for sp in specifier: - if sp.operator == "===": + if sp.operator == '===': return True - if sp.operator != "==": + if sp.operator != '==': continue - if sp.version.endswith(".*"): + if sp.version.endswith('.*'): continue return True return False @@ -340,7 +338,7 @@ class Factory: def _iter_explicit_candidates_from_base( self, base_requirements: Iterable[Requirement], - extras: FrozenSet[str], + extras: frozenset[str], ) -> Iterator[Candidate]: """Produce explicit candidates from the base given an extra-ed package. @@ -356,7 +354,7 @@ class Factory: # We've stripped extras from the identifier, and should always # get a BaseCandidate here, unless there's a bug elsewhere. base_cand = as_base_candidate(lookup_cand) - assert base_cand is not None, "no extras here" + assert base_cand is not None, 'no extras here' yield self._make_extras_candidate(base_cand, extras) def _iter_candidates_from_constraints( @@ -391,8 +389,8 @@ class Factory: prefers_installed: bool, ) -> Iterable[Candidate]: # Collect basic lookup information from the requirements. - explicit_candidates: Set[Candidate] = set() - ireqs: List[InstallRequirement] = [] + explicit_candidates: set[Candidate] = set() + ireqs: list[InstallRequirement] = [] for req in requirements[identifier]: cand, ireq = req.get_candidate_lookup() if cand is not None: @@ -447,14 +445,14 @@ class Factory: return ( c for c in explicit_candidates - if id(c) not in incompat_ids - and constraint.is_satisfied_by(c) - and all(req.is_satisfied_by(c) for req in requirements[identifier]) + if id(c) not in incompat_ids and + constraint.is_satisfied_by(c) and + all(req.is_satisfied_by(c) for req in requirements[identifier]) ) def _make_requirement_from_install_req( - self, ireq: InstallRequirement, requested_extras: Iterable[str] - ) -> Optional[Requirement]: + self, ireq: InstallRequirement, requested_extras: Iterable[str], + ) -> Requirement | None: if not ireq.match_markers(requested_extras): logger.info( "Ignoring %s: markers '%s' don't match your environment", @@ -485,7 +483,7 @@ class Factory: return self.make_requirement_from_candidate(cand) def collect_root_requirements( - self, root_ireqs: List[InstallRequirement] + self, root_ireqs: list[InstallRequirement], ) -> CollectedRootRequirements: collected = CollectedRootRequirements([], {}, {}) for i, ireq in enumerate(root_ireqs): @@ -496,7 +494,7 @@ class Factory: raise InstallationError(problem) if not ireq.match_markers(): continue - assert ireq.name, "Constraint must be named" + assert ireq.name, 'Constraint must be named' name = canonicalize_name(ireq.name) if name in collected.constraints: collected.constraints[name] &= ireq @@ -515,23 +513,23 @@ class Factory: return collected def make_requirement_from_candidate( - self, candidate: Candidate + self, candidate: Candidate, ) -> ExplicitRequirement: return ExplicitRequirement(candidate) def make_requirement_from_spec( self, specifier: str, - comes_from: Optional[InstallRequirement], + comes_from: InstallRequirement | None, requested_extras: Iterable[str] = (), - ) -> Optional[Requirement]: + ) -> Requirement | None: ireq = self._make_install_req_from_spec(specifier, comes_from) return self._make_requirement_from_install_req(ireq, requested_extras) def make_requires_python_requirement( self, specifier: SpecifierSet, - ) -> Optional[Requirement]: + ) -> Requirement | None: if self._ignore_requires_python: return None # Don't bother creating a dependency for an empty Requires-Python. @@ -540,8 +538,8 @@ class Factory: return RequiresPythonRequirement(specifier, self._python_candidate) def get_wheel_cache_entry( - self, link: Link, name: Optional[str] - ) -> Optional[CacheEntry]: + self, link: Link, name: str | None, + ) -> CacheEntry | None: """Look up the link in the wheel cache. If ``preparer.require_hashes`` is True, don't use the wheel cache, @@ -558,7 +556,7 @@ class Factory: supported_tags=get_supported(), ) - def get_dist_to_uninstall(self, candidate: Candidate) -> Optional[BaseDistribution]: + def get_dist_to_uninstall(self, candidate: Candidate) -> BaseDistribution | None: # TODO: Are there more cases this needs to return True? Editable? dist = self._installed_dists.get(candidate.project_name) if dist is None: # Not installed, no uninstallation required. @@ -580,82 +578,82 @@ class Factory: # in virtual environments, so we error out. if running_under_virtualenv() and dist.in_site_packages: message = ( - f"Will not install to the user site because it will lack " - f"sys.path precedence to {dist.raw_name} in {dist.location}" + f'Will not install to the user site because it will lack ' + f'sys.path precedence to {dist.raw_name} in {dist.location}' ) raise InstallationError(message) return None def _report_requires_python_error( - self, causes: Sequence["ConflictCause"] + self, causes: Sequence[ConflictCause], ) -> UnsupportedPythonVersion: - assert causes, "Requires-Python error reported with no cause" + assert causes, 'Requires-Python error reported with no cause' version = self._python_candidate.version if len(causes) == 1: specifier = str(causes[0].requirement.specifier) message = ( - f"Package {causes[0].parent.name!r} requires a different " - f"Python: {version} not in {specifier!r}" + f'Package {causes[0].parent.name!r} requires a different ' + f'Python: {version} not in {specifier!r}' ) return UnsupportedPythonVersion(message) - message = f"Packages require a different Python. {version} not in:" + message = f'Packages require a different Python. {version} not in:' for cause in causes: package = cause.parent.format_for_error() specifier = str(cause.requirement.specifier) - message += f"\n{specifier!r} (required by {package})" + message += f'\n{specifier!r} (required by {package})' return UnsupportedPythonVersion(message) def _report_single_requirement_conflict( - self, req: Requirement, parent: Optional[Candidate] + self, req: Requirement, parent: Candidate | None, ) -> DistributionNotFound: if parent is None: req_disp = str(req) else: - req_disp = f"{req} (from {parent.name})" + req_disp = f'{req} (from {parent.name})' cands = self._finder.find_all_candidates(req.project_name) versions = [str(v) for v in sorted({c.version for c in cands})] logger.critical( - "Could not find a version that satisfies the requirement %s " - "(from versions: %s)", + 'Could not find a version that satisfies the requirement %s ' + '(from versions: %s)', req_disp, - ", ".join(versions) or "none", + ', '.join(versions) or 'none', ) - if str(req) == "requirements.txt": + if str(req) == 'requirements.txt': logger.info( - "HINT: You are attempting to install a package literally " + 'HINT: You are attempting to install a package literally ' 'named "requirements.txt" (which cannot exist). Consider ' "using the '-r' flag to install the packages listed in " - "requirements.txt" + 'requirements.txt', ) - return DistributionNotFound(f"No matching distribution found for {req}") + return DistributionNotFound(f'No matching distribution found for {req}') def get_installation_error( self, - e: "ResolutionImpossible[Requirement, Candidate]", - constraints: Dict[str, Constraint], + e: ResolutionImpossible[Requirement, Candidate], + constraints: dict[str, Constraint], ) -> InstallationError: - assert e.causes, "Installation error reported with no cause" + assert e.causes, 'Installation error reported with no cause' # If one of the things we can't solve is "we need Python X.Y", # that is what we report. requires_python_causes = [ cause for cause in e.causes - if isinstance(cause.requirement, RequiresPythonRequirement) - and not cause.requirement.is_satisfied_by(self._python_candidate) + if isinstance(cause.requirement, RequiresPythonRequirement) and + not cause.requirement.is_satisfied_by(self._python_candidate) ] if requires_python_causes: # The comprehension above makes sure all Requirement instances are # RequiresPythonRequirement, so let's cast for convenience. return self._report_requires_python_error( - cast("Sequence[ConflictCause]", requires_python_causes), + cast('Sequence[ConflictCause]', requires_python_causes), ) # Otherwise, we have a set of causes which can't all be satisfied @@ -672,16 +670,16 @@ class Factory: # satisfied at once. # A couple of formatting helpers - def text_join(parts: List[str]) -> str: + def text_join(parts: list[str]) -> str: if len(parts) == 1: return parts[0] - return ", ".join(parts[:-1]) + " and " + parts[-1] + return ', '.join(parts[:-1]) + ' and ' + parts[-1] def describe_trigger(parent: Candidate) -> str: ireq = parent.get_install_requirement() if not ireq or not ireq.comes_from: - return f"{parent.name}=={parent.version}" + return f'{parent.name}=={parent.version}' if isinstance(ireq.comes_from, InstallRequirement): return str(ireq.comes_from.name) return str(ireq.comes_from) @@ -698,42 +696,42 @@ class Factory: if triggers: info = text_join(sorted(triggers)) else: - info = "the requested packages" + info = 'the requested packages' msg = ( - "Cannot install {} because these package versions " - "have conflicting dependencies.".format(info) + 'Cannot install {} because these package versions ' + 'have conflicting dependencies.'.format(info) ) logger.critical(msg) - msg = "\nThe conflict is caused by:" + msg = '\nThe conflict is caused by:' relevant_constraints = set() for req, parent in e.causes: if req.name in constraints: relevant_constraints.add(req.name) - msg = msg + "\n " + msg = msg + '\n ' if parent: - msg = msg + f"{parent.name} {parent.version} depends on " + msg = msg + f'{parent.name} {parent.version} depends on ' else: - msg = msg + "The user requested " + msg = msg + 'The user requested ' msg = msg + req.format_for_error() for key in relevant_constraints: spec = constraints[key].specifier - msg += f"\n The user requested (constraint) {key}{spec}" + msg += f'\n The user requested (constraint) {key}{spec}' msg = ( - msg - + "\n\n" - + "To fix this you could try to:\n" - + "1. loosen the range of package versions you've specified\n" - + "2. remove package versions to allow pip attempt to solve " - + "the dependency conflict\n" + msg + + '\n\n' + + 'To fix this you could try to:\n' + + "1. loosen the range of package versions you've specified\n" + + '2. remove package versions to allow pip attempt to solve ' + + 'the dependency conflict\n' ) logger.info(msg) return DistributionNotFound( - "ResolutionImpossible: for help visit " - "https://pip.pypa.io/en/latest/topics/dependency-resolution/" - "#dealing-with-dependency-conflicts" + 'ResolutionImpossible: for help visit ' + 'https://pip.pypa.io/en/latest/topics/dependency-resolution/' + '#dealing-with-dependency-conflicts', ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py index 8663097..353ddbe 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py @@ -7,10 +7,17 @@ everything here lazy all the way down, so we only touch candidates that we absolutely need, and not "download the world" when we only need one version of something. """ +from __future__ import annotations import functools from collections.abc import Sequence -from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, Set, Tuple +from typing import Any +from typing import Callable +from typing import Iterator +from typing import Optional +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING from pip._vendor.packaging.version import _BaseVersion @@ -40,7 +47,7 @@ def _iter_built(infos: Iterator[IndexCandidateInfo]) -> Iterator[Candidate]: This iterator is used when the package is not already installed. Candidates from index come later in their normal ordering. """ - versions_found: Set[_BaseVersion] = set() + versions_found: set[_BaseVersion] = set() for version, func in infos: if version in versions_found: continue @@ -52,7 +59,7 @@ def _iter_built(infos: Iterator[IndexCandidateInfo]) -> Iterator[Candidate]: def _iter_built_with_prepended( - installed: Candidate, infos: Iterator[IndexCandidateInfo] + installed: Candidate, infos: Iterator[IndexCandidateInfo], ) -> Iterator[Candidate]: """Iterator for ``FoundCandidates``. @@ -62,7 +69,7 @@ def _iter_built_with_prepended( normal ordering, except skipped when the version is already installed. """ yield installed - versions_found: Set[_BaseVersion] = {installed.version} + versions_found: set[_BaseVersion] = {installed.version} for version, func in infos: if version in versions_found: continue @@ -74,7 +81,7 @@ def _iter_built_with_prepended( def _iter_built_with_inserted( - installed: Candidate, infos: Iterator[IndexCandidateInfo] + installed: Candidate, infos: Iterator[IndexCandidateInfo], ) -> Iterator[Candidate]: """Iterator for ``FoundCandidates``. @@ -86,7 +93,7 @@ def _iter_built_with_inserted( the installed candidate exactly once before we start yielding older or equivalent candidates, or after all other candidates if they are all newer. """ - versions_found: Set[_BaseVersion] = set() + versions_found: set[_BaseVersion] = set() for version, func in infos: if version in versions_found: continue @@ -117,9 +124,9 @@ class FoundCandidates(SequenceCandidate): def __init__( self, get_infos: Callable[[], Iterator[IndexCandidateInfo]], - installed: Optional[Candidate], + installed: Candidate | None, prefers_installed: bool, - incompatible_ids: Set[int], + incompatible_ids: set[int], ): self._get_infos = get_infos self._installed = installed diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/provider.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/provider.py index e6ec959..bb90c08 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/provider.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/provider.py @@ -1,19 +1,21 @@ +from __future__ import annotations + import collections import math -from typing import ( - TYPE_CHECKING, - Dict, - Iterable, - Iterator, - Mapping, - Sequence, - TypeVar, - Union, -) +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import Mapping +from typing import Sequence +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union from pip._vendor.resolvelib.providers import AbstractProvider -from .base import Candidate, Constraint, Requirement +from .base import Candidate +from .base import Constraint +from .base import Requirement from .candidates import REQUIRES_PYTHON_IDENTIFIER from .factory import Factory @@ -46,15 +48,15 @@ else: # services to those objects (access to pip's finder and preparer). -D = TypeVar("D") -V = TypeVar("V") +D = TypeVar('D') +V = TypeVar('V') def _get_with_identifier( mapping: Mapping[str, V], identifier: str, default: D, -) -> Union[D, V]: +) -> D | V: """Get item from a package name lookup mapping with a resolver identifier. This extra logic is needed when the target mapping is keyed by package @@ -69,7 +71,7 @@ def _get_with_identifier( # some regular expression. But since pip's resolver only spits out three # kinds of identifiers: normalized PEP 503 names, normalized names plus # extras, and Requires-Python, we can cheat a bit here. - name, open_bracket, _ = identifier.partition("[") + name, open_bracket, _ = identifier.partition('[') if open_bracket and name in mapping: return mapping[name] return default @@ -89,19 +91,19 @@ class PipProvider(_ProviderBase): def __init__( self, factory: Factory, - constraints: Dict[str, Constraint], + constraints: dict[str, Constraint], ignore_dependencies: bool, upgrade_strategy: str, - user_requested: Dict[str, int], + user_requested: dict[str, int], ) -> None: self._factory = factory self._constraints = constraints self._ignore_dependencies = ignore_dependencies self._upgrade_strategy = upgrade_strategy self._user_requested = user_requested - self._known_depths: Dict[str, float] = collections.defaultdict(lambda: math.inf) + self._known_depths: dict[str, float] = collections.defaultdict(lambda: math.inf) - def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str: + def identify(self, requirement_or_candidate: Requirement | Candidate) -> str: return requirement_or_candidate.name def get_preference( # type: ignore @@ -109,9 +111,9 @@ class PipProvider(_ProviderBase): identifier: str, resolutions: Mapping[str, Candidate], candidates: Mapping[str, Iterator[Candidate]], - information: Mapping[str, Iterable["PreferenceInformation"]], - backtrack_causes: Sequence["PreferenceInformation"], - ) -> "Preference": + information: Mapping[str, Iterable[PreferenceInformation]], + backtrack_causes: Sequence[PreferenceInformation], + ) -> Preference: """Produce a sort key for given requirement based on preference. The lower the return value is, the more preferred this group of @@ -139,11 +141,11 @@ class PipProvider(_ProviderBase): ] direct = candidate is not None - pinned = any(op[:2] == "==" for op in operators) + pinned = any(op[:2] == '==' for op in operators) unfree = bool(operators) try: - requested_order: Union[int, float] = self._user_requested[identifier] + requested_order: int | float = self._user_requested[identifier] except KeyError: requested_order = math.inf parent_depths = ( @@ -169,7 +171,7 @@ class PipProvider(_ProviderBase): # delaying Setuptools helps reduce branches the resolver has to check. # This serves as a temporary fix for issues like "apache-airflow[all]" # while we work on "proper" branch pruning techniques. - delay_this = identifier == "setuptools" + delay_this = identifier == 'setuptools' # Prefer the causes of backtracking on the assumption that the problem # resolving the dependency tree is related to the failures that caused @@ -205,9 +207,9 @@ class PipProvider(_ProviderBase): an upgrade strategy of "to-satisfy-only" means that `--upgrade` was not specified). """ - if self._upgrade_strategy == "eager": + if self._upgrade_strategy == 'eager': return True - elif self._upgrade_strategy == "only-if-needed": + elif self._upgrade_strategy == 'only-if-needed': user_order = _get_with_identifier( self._user_requested, identifier, @@ -238,7 +240,7 @@ class PipProvider(_ProviderBase): @staticmethod def is_backtrack_cause( - identifier: str, backtrack_causes: Sequence["PreferenceInformation"] + identifier: str, backtrack_causes: Sequence[PreferenceInformation], ) -> bool: for backtrack_cause in backtrack_causes: if identifier == backtrack_cause.requirement.name: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/reporter.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/reporter.py index 6ced532..5e61d52 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/reporter.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/reporter.py @@ -1,10 +1,14 @@ +from __future__ import annotations + from collections import defaultdict from logging import getLogger -from typing import Any, DefaultDict +from typing import Any +from typing import DefaultDict from pip._vendor.resolvelib.reporters import BaseReporter -from .base import Candidate, Requirement +from .base import Candidate +from .base import Requirement logger = getLogger(__name__) @@ -15,20 +19,20 @@ class PipReporter(BaseReporter): self._messages_at_backtrack = { 1: ( - "pip is looking at multiple versions of {package_name} to " - "determine which version is compatible with other " - "requirements. This could take a while." + 'pip is looking at multiple versions of {package_name} to ' + 'determine which version is compatible with other ' + 'requirements. This could take a while.' ), 8: ( - "pip is looking at multiple versions of {package_name} to " - "determine which version is compatible with other " - "requirements. This could take a while." + 'pip is looking at multiple versions of {package_name} to ' + 'determine which version is compatible with other ' + 'requirements. This could take a while.' ), 13: ( - "This is taking longer than usual. You might need to provide " - "the dependency resolver with stricter constraints to reduce " - "runtime. See https://pip.pypa.io/warnings/backtracking for " - "guidance. If you want to abort this run, press Ctrl + C." + 'This is taking longer than usual. You might need to provide ' + 'the dependency resolver with stricter constraints to reduce ' + 'runtime. See https://pip.pypa.io/warnings/backtracking for ' + 'guidance. If you want to abort this run, press Ctrl + C.' ), } @@ -40,29 +44,29 @@ class PipReporter(BaseReporter): return message = self._messages_at_backtrack[count] - logger.info("INFO: %s", message.format(package_name=candidate.name)) + logger.info('INFO: %s', message.format(package_name=candidate.name)) class PipDebuggingReporter(BaseReporter): """A reporter that does an info log for every event it sees.""" def starting(self) -> None: - logger.info("Reporter.starting()") + logger.info('Reporter.starting()') def starting_round(self, index: int) -> None: - logger.info("Reporter.starting_round(%r)", index) + logger.info('Reporter.starting_round(%r)', index) def ending_round(self, index: int, state: Any) -> None: - logger.info("Reporter.ending_round(%r, state)", index) + logger.info('Reporter.ending_round(%r, state)', index) def ending(self, state: Any) -> None: - logger.info("Reporter.ending(%r)", state) + logger.info('Reporter.ending(%r)', state) def adding_requirement(self, requirement: Requirement, parent: Candidate) -> None: - logger.info("Reporter.adding_requirement(%r, %r)", requirement, parent) + logger.info('Reporter.adding_requirement(%r, %r)', requirement, parent) def backtracking(self, candidate: Candidate) -> None: - logger.info("Reporter.backtracking(%r)", candidate) + logger.info('Reporter.backtracking(%r)', candidate) def pinning(self, candidate: Candidate) -> None: - logger.info("Reporter.pinning(%r)", candidate) + logger.info('Reporter.pinning(%r)', candidate) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/requirements.py index f561f1f..311c26b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/requirements.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/requirements.py @@ -1,9 +1,14 @@ -from pip._vendor.packaging.specifiers import SpecifierSet -from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from __future__ import annotations from pip._internal.req.req_install import InstallRequirement +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.utils import NormalizedName -from .base import Candidate, CandidateLookup, Requirement, format_name +from .base import Candidate +from .base import CandidateLookup +from .base import format_name +from .base import Requirement class ExplicitRequirement(Requirement): @@ -14,7 +19,7 @@ class ExplicitRequirement(Requirement): return str(self.candidate) def __repr__(self) -> str: - return "{class_name}({candidate!r})".format( + return '{class_name}({candidate!r})'.format( class_name=self.__class__.__name__, candidate=self.candidate, ) @@ -41,7 +46,7 @@ class ExplicitRequirement(Requirement): class SpecifierRequirement(Requirement): def __init__(self, ireq: InstallRequirement) -> None: - assert ireq.link is None, "This is a link, not a specifier" + assert ireq.link is None, 'This is a link, not a specifier' self._ireq = ireq self._extras = frozenset(ireq.extras) @@ -49,14 +54,14 @@ class SpecifierRequirement(Requirement): return str(self._ireq.req) def __repr__(self) -> str: - return "{class_name}({requirement!r})".format( + return '{class_name}({requirement!r})'.format( class_name=self.__class__.__name__, requirement=str(self._ireq.req), ) @property def project_name(self) -> NormalizedName: - assert self._ireq.req, "Specifier-backed ireq is always PEP 508" + assert self._ireq.req, 'Specifier-backed ireq is always PEP 508' return canonicalize_name(self._ireq.req.name) @property @@ -69,26 +74,26 @@ class SpecifierRequirement(Requirement): # This makes the specifier a bit more "human readable", without # risking a change in meaning. (Hopefully! Not all edge cases have # been checked) - parts = [s.strip() for s in str(self).split(",")] + parts = [s.strip() for s in str(self).split(',')] if len(parts) == 0: - return "" + return '' elif len(parts) == 1: return parts[0] - return ", ".join(parts[:-1]) + " and " + parts[-1] + return ', '.join(parts[:-1]) + ' and ' + parts[-1] def get_candidate_lookup(self) -> CandidateLookup: return None, self._ireq def is_satisfied_by(self, candidate: Candidate) -> bool: assert candidate.name == self.name, ( - f"Internal issue: Candidate is not for this requirement " - f"{candidate.name} vs {self.name}" + f'Internal issue: Candidate is not for this requirement ' + f'{candidate.name} vs {self.name}' ) # We can safely always allow prereleases here since PackageFinder # already implements the prerelease logic, and would have filtered out # prerelease candidates if the user does not expect them. - assert self._ireq.req, "Specifier-backed ireq is always PEP 508" + assert self._ireq.req, 'Specifier-backed ireq is always PEP 508' spec = self._ireq.req.specifier return spec.contains(candidate.version, prereleases=True) @@ -101,10 +106,10 @@ class RequiresPythonRequirement(Requirement): self._candidate = match def __str__(self) -> str: - return f"Python {self.specifier}" + return f'Python {self.specifier}' def __repr__(self) -> str: - return "{class_name}({specifier!r})".format( + return '{class_name}({specifier!r})'.format( class_name=self.__class__.__name__, specifier=str(self.specifier), ) @@ -126,7 +131,7 @@ class RequiresPythonRequirement(Requirement): return None, None def is_satisfied_by(self, candidate: Candidate) -> bool: - assert candidate.name == self._candidate.name, "Not Python candidate" + assert candidate.name == self._candidate.name, 'Not Python candidate' # We can safely always allow prereleases here since PackageFinder # already implements the prerelease logic, and would have filtered out # prerelease candidates if the user does not expect them. @@ -140,10 +145,10 @@ class UnsatisfiableRequirement(Requirement): self._name = name def __str__(self) -> str: - return f"{self._name} (unavailable)" + return f'{self._name} (unavailable)' def __repr__(self) -> str: - return "{class_name}({name!r})".format( + return '{class_name}({name!r})'.format( class_name=self.__class__.__name__, name=str(self._name), ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/resolver.py index 32ef789..63aceb1 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/resolver.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/resolver.py @@ -1,26 +1,34 @@ +from __future__ import annotations + import functools import logging import os -from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, cast - -from pip._vendor.packaging.utils import canonicalize_name -from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible -from pip._vendor.resolvelib import Resolver as RLResolver -from pip._vendor.resolvelib.structs import DirectedGraph +from typing import cast +from typing import Dict +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING from pip._internal.cache import WheelCache from pip._internal.index.package_finder import PackageFinder from pip._internal.operations.prepare import RequirementPreparer from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_set import RequirementSet -from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider +from pip._internal.resolution.base import BaseResolver +from pip._internal.resolution.base import InstallRequirementProvider from pip._internal.resolution.resolvelib.provider import PipProvider -from pip._internal.resolution.resolvelib.reporter import ( - PipDebuggingReporter, - PipReporter, -) +from pip._internal.resolution.resolvelib.reporter import PipDebuggingReporter +from pip._internal.resolution.resolvelib.reporter import PipReporter +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.resolvelib import BaseReporter +from pip._vendor.resolvelib import ResolutionImpossible +from pip._vendor.resolvelib import Resolver as RLResolver +from pip._vendor.resolvelib.structs import DirectedGraph -from .base import Candidate, Requirement +from .base import Candidate +from .base import Requirement from .factory import Factory if TYPE_CHECKING: @@ -33,13 +41,13 @@ logger = logging.getLogger(__name__) class Resolver(BaseResolver): - _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + _allowed_strategies = {'eager', 'only-if-needed', 'to-satisfy-only'} def __init__( self, preparer: RequirementPreparer, finder: PackageFinder, - wheel_cache: Optional[WheelCache], + wheel_cache: WheelCache | None, make_install_req: InstallRequirementProvider, use_user_site: bool, ignore_dependencies: bool, @@ -48,7 +56,7 @@ class Resolver(BaseResolver): force_reinstall: bool, upgrade_strategy: str, suppress_build_failures: bool, - py_version_info: Optional[Tuple[int, ...]] = None, + py_version_info: tuple[int, ...] | None = None, ): super().__init__() assert upgrade_strategy in self._allowed_strategies @@ -67,10 +75,10 @@ class Resolver(BaseResolver): ) self.ignore_dependencies = ignore_dependencies self.upgrade_strategy = upgrade_strategy - self._result: Optional[Result] = None + self._result: Result | None = None def resolve( - self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + self, root_reqs: list[InstallRequirement], check_supported_wheels: bool, ) -> RequirementSet: collected = self.factory.collect_root_requirements(root_reqs) provider = PipProvider( @@ -80,7 +88,7 @@ class Resolver(BaseResolver): upgrade_strategy=self.upgrade_strategy, user_requested=collected.user_requested, ) - if "PIP_RESOLVER_DEBUG" in os.environ: + if 'PIP_RESOLVER_DEBUG' in os.environ: reporter: BaseReporter = PipDebuggingReporter() else: reporter = PipReporter() @@ -92,12 +100,12 @@ class Resolver(BaseResolver): try: try_to_avoid_resolution_too_deep = 2000000 result = self._result = resolver.resolve( - collected.requirements, max_rounds=try_to_avoid_resolution_too_deep + collected.requirements, max_rounds=try_to_avoid_resolution_too_deep, ) except ResolutionImpossible as e: error = self.factory.get_installation_error( - cast("ResolutionImpossible[Requirement, Candidate]", e), + cast('ResolutionImpossible[Requirement, Candidate]', e), collected.constraints, ) raise error from e @@ -129,9 +137,9 @@ class Resolver(BaseResolver): if candidate.source_link.is_wheel: # is a local wheel -- do nothing. logger.info( - "%s is already installed with the same version as the " - "provided wheel. Use --force-reinstall to force an " - "installation of the wheel.", + '%s is already installed with the same version as the ' + 'provided wheel. Use --force-reinstall to force an ' + 'installation of the wheel.', ireq.name, ) continue @@ -146,14 +154,14 @@ class Resolver(BaseResolver): # The reason can contain non-ASCII characters, Unicode # is required for Python 2. msg = ( - "The candidate selected for download or install is a " - "yanked version: {name!r} candidate (version {version} " - "at {link})\nReason for being yanked: {reason}" + 'The candidate selected for download or install is a ' + 'yanked version: {name!r} candidate (version {version} ' + 'at {link})\nReason for being yanked: {reason}' ).format( name=candidate.name, version=candidate.version, link=link, - reason=link.yanked_reason or "", + reason=link.yanked_reason or '', ) logger.warning(msg) @@ -164,8 +172,8 @@ class Resolver(BaseResolver): return req_set def get_installation_order( - self, req_set: RequirementSet - ) -> List[InstallRequirement]: + self, req_set: RequirementSet, + ) -> list[InstallRequirement]: """Get order for installation of requirements in RequirementSet. The returned list contains a requirement before another that depends on @@ -178,7 +186,7 @@ class Resolver(BaseResolver): arbitrary points. We make no guarantees about where the cycle would be broken, other than it *would* be broken. """ - assert self._result is not None, "must call resolve() first" + assert self._result is not None, 'must call resolve() first' if not req_set.requirements: # Nothing is left to install, so we do not need an order. @@ -196,8 +204,8 @@ class Resolver(BaseResolver): def get_topological_weights( - graph: "DirectedGraph[Optional[str]]", requirement_keys: Set[str] -) -> Dict[Optional[str], int]: + graph: DirectedGraph[Optional[str]], requirement_keys: set[str], +) -> dict[str | None, int]: """Assign weights to each node based on how "deep" they are. This implementation may change at any point in the future without prior @@ -223,10 +231,10 @@ def get_topological_weights( We are only interested in the weights of packages that are in the requirement_keys. """ - path: Set[Optional[str]] = set() - weights: Dict[Optional[str], int] = {} + path: set[str | None] = set() + weights: dict[str | None, int] = {} - def visit(node: Optional[str]) -> None: + def visit(node: str | None) -> None: if node in path: # We hit a cycle, so we'll break it here. return @@ -285,9 +293,9 @@ def get_topological_weights( def _req_set_item_sorter( - item: Tuple[str, InstallRequirement], - weights: Dict[Optional[str], int], -) -> Tuple[int, str]: + item: tuple[str, InstallRequirement], + weights: dict[str | None, int], +) -> tuple[int, str]: """Key function used to sort install requirements for installation. Based on the "weight" mapping calculated in ``get_installation_order()``. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/self_outdated_check.py b/.venv/lib/python3.10/site-packages/pip/_internal/self_outdated_check.py index 7300e0e..df60418 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/self_outdated_check.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/self_outdated_check.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import datetime import hashlib import json @@ -5,19 +7,21 @@ import logging import optparse import os.path import sys -from typing import Any, Dict - -from pip._vendor.packaging.version import parse as parse_version +from typing import Any +from typing import Dict from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder from pip._internal.metadata import get_default_environment from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.network.session import PipSession -from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace +from pip._internal.utils.filesystem import adjacent_tmp_file +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.filesystem import replace from pip._internal.utils.misc import ensure_dir +from pip._vendor.packaging.version import parse as parse_version -SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" +SELFCHECK_DATE_FMT = '%Y-%m-%dT%H:%M:%SZ' logger = logging.getLogger(__name__) @@ -31,16 +35,16 @@ def _get_statefile_name(key: str) -> str: class SelfCheckState: def __init__(self, cache_dir: str) -> None: - self.state: Dict[str, Any] = {} + self.state: dict[str, Any] = {} self.statefile_path = None # Try to load the existing state if cache_dir: self.statefile_path = os.path.join( - cache_dir, "selfcheck", _get_statefile_name(self.key) + cache_dir, 'selfcheck', _get_statefile_name(self.key), ) try: - with open(self.statefile_path, encoding="utf-8") as statefile: + with open(self.statefile_path, encoding='utf-8') as statefile: self.state = json.load(statefile) except (OSError, ValueError, KeyError): # Explicitly suppressing exceptions, since we don't want to @@ -67,12 +71,12 @@ class SelfCheckState: state = { # Include the key so it's easy to tell which pip wrote the # file. - "key": self.key, - "last_check": current_time.strftime(SELFCHECK_DATE_FMT), - "pypi_version": pypi_version, + 'key': self.key, + 'last_check': current_time.strftime(SELFCHECK_DATE_FMT), + 'pypi_version': pypi_version, } - text = json.dumps(state, sort_keys=True, separators=(",", ":")) + text = json.dumps(state, sort_keys=True, separators=(',', ':')) with adjacent_tmp_file(self.statefile_path) as f: f.write(text.encode()) @@ -93,7 +97,7 @@ def was_installed_by_pip(pkg: str) -> bool: installed by system package manager, such as dnf on Fedora. """ dist = get_default_environment().get_distribution(pkg) - return dist is not None and "pip" == dist.installer + return dist is not None and 'pip' == dist.installer def pip_self_version_check(session: PipSession, options: optparse.Values) -> None: @@ -103,7 +107,7 @@ def pip_self_version_check(session: PipSession, options: optparse.Values) -> Non the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix of the pip script path. """ - installed_dist = get_default_environment().get_distribution("pip") + installed_dist = get_default_environment().get_distribution('pip') if not installed_dist: return @@ -115,12 +119,12 @@ def pip_self_version_check(session: PipSession, options: optparse.Values) -> Non current_time = datetime.datetime.utcnow() # Determine if we need to refresh the state - if "last_check" in state.state and "pypi_version" in state.state: + if 'last_check' in state.state and 'pypi_version' in state.state: last_check = datetime.datetime.strptime( - state.state["last_check"], SELFCHECK_DATE_FMT + state.state['last_check'], SELFCHECK_DATE_FMT, ) if (current_time - last_check).total_seconds() < 7 * 24 * 60 * 60: - pypi_version = state.state["pypi_version"] + pypi_version = state.state['pypi_version'] # Refresh the version if we need to or just see if we need to warn if pypi_version is None: @@ -142,10 +146,10 @@ def pip_self_version_check(session: PipSession, options: optparse.Values) -> Non link_collector=link_collector, selection_prefs=selection_prefs, use_deprecated_html5lib=( - "html5lib" in options.deprecated_features_enabled + 'html5lib' in options.deprecated_features_enabled ), ) - best_candidate = finder.find_best_candidate("pip").best_candidate + best_candidate = finder.find_best_candidate('pip').best_candidate if best_candidate is None: return pypi_version = str(best_candidate.version) @@ -156,9 +160,9 @@ def pip_self_version_check(session: PipSession, options: optparse.Values) -> Non remote_version = parse_version(pypi_version) local_version_is_older = ( - pip_version < remote_version - and pip_version.base_version != remote_version.base_version - and was_installed_by_pip("pip") + pip_version < remote_version and + pip_version.base_version != remote_version.base_version and + was_installed_by_pip('pip') ) # Determine if our pypi_version is older @@ -173,10 +177,10 @@ def pip_self_version_check(session: PipSession, options: optparse.Values) -> Non # it won't be done until possible through the standard library. # Do not be tempted to use the undocumented subprocess.list2cmdline. # It is considered an internal implementation detail for a reason. - pip_cmd = f"{sys.executable} -m pip" + pip_cmd = f'{sys.executable} -m pip' logger.warning( - "You are using pip version %s; however, version %s is " - "available.\nYou should consider upgrading via the " + 'You are using pip version %s; however, version %s is ' + 'available.\nYou should consider upgrading via the ' "'%s install --upgrade pip' command.", pip_version, pypi_version, @@ -184,6 +188,6 @@ def pip_self_version_check(session: PipSession, options: optparse.Values) -> Non ) except Exception: logger.debug( - "There was an error checking the latest version of pip", + 'There was an error checking the latest version of pip', exc_info=True, ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/_log.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/_log.py index 92c4c6a..09659da 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/_log.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/_log.py @@ -4,9 +4,11 @@ Defines custom logger class for the `logger.verbose(...)` method. init_logging() must be called before any other modules that call logging.getLogger. """ +from __future__ import annotations import logging -from typing import Any, cast +from typing import Any +from typing import cast # custom log level for `--verbose` output # between DEBUG and INFO @@ -35,4 +37,4 @@ def init_logging() -> None: i.e. in pip._internal.__init__ """ logging.setLoggerClass(VerboseLogger) - logging.addLevelName(VERBOSE, "VERBOSE") + logging.addLevelName(VERBOSE, 'VERBOSE') diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/appdirs.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/appdirs.py index 16933bf..9445ef0 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/appdirs.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/appdirs.py @@ -5,6 +5,7 @@ compatible for the current pip code base. The intention is to rewrite current usages gradually, keeping the tests pass, and eventually drop this after all usages are changed. """ +from __future__ import annotations import os import sys @@ -24,7 +25,7 @@ def _macos_user_config_dir(appname: str, roaming: bool = True) -> str: return path # Use a Linux-like ~/.config/pip, by default. - linux_like_path = "~/.config/" + linux_like_path = '~/.config/' if appname: linux_like_path = os.path.join(linux_like_path, appname) @@ -32,7 +33,7 @@ def _macos_user_config_dir(appname: str, roaming: bool = True) -> str: def user_config_dir(appname: str, roaming: bool = True) -> str: - if sys.platform == "darwin": + if sys.platform == 'darwin': return _macos_user_config_dir(appname, roaming) return _appdirs.user_config_dir(appname, appauthor=False, roaming=roaming) @@ -40,13 +41,13 @@ def user_config_dir(appname: str, roaming: bool = True) -> str: # for the discussion regarding site_config_dir locations # see -def site_config_dirs(appname: str) -> List[str]: - if sys.platform == "darwin": +def site_config_dirs(appname: str) -> list[str]: + if sys.platform == 'darwin': return [_appdirs.site_data_dir(appname, appauthor=False, multipath=True)] dirval = _appdirs.site_config_dir(appname, appauthor=False, multipath=True) - if sys.platform == "win32": + if sys.platform == 'win32': return [dirval] # Unix-y system. Look in /etc as well. - return dirval.split(os.pathsep) + ["/etc"] + return dirval.split(os.pathsep) + ['/etc'] diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/compat.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/compat.py index 3f4d300..fef3d9d 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/compat.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/compat.py @@ -1,11 +1,12 @@ """Stuff that differs in different Python versions and platform distributions.""" +from __future__ import annotations import logging import os import sys -__all__ = ["get_path_uid", "stdlib_pkgs", "WINDOWS"] +__all__ = ['get_path_uid', 'stdlib_pkgs', 'WINDOWS'] logger = logging.getLogger(__name__) @@ -36,7 +37,7 @@ def get_path_uid(path: str) -> int: :raises OSError: When path is a symlink or can't be read. """ - if hasattr(os, "O_NOFOLLOW"): + if hasattr(os, 'O_NOFOLLOW'): fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) file_uid = os.fstat(fd).st_uid os.close(fd) @@ -47,7 +48,7 @@ def get_path_uid(path: str) -> int: file_uid = os.stat(path).st_uid else: # raise OSError for parity with os.O_NOFOLLOW above - raise OSError(f"{path} is a symlink; Will not return uid for symlinks") + raise OSError(f'{path} is a symlink; Will not return uid for symlinks') return file_uid @@ -56,8 +57,8 @@ def get_path_uid(path: str) -> int: # dist.location (py27:`sysconfig.get_paths()['stdlib']`, # py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may # make this ineffective, so hard-coding -stdlib_pkgs = {"python", "wsgiref", "argparse"} +stdlib_pkgs = {'python', 'wsgiref', 'argparse'} # windows detection, covers cpython and ironpython -WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") +WINDOWS = sys.platform.startswith('win') or (sys.platform == 'cli' and os.name == 'nt') diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/compatibility_tags.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/compatibility_tags.py index b6ed9a7..274a756 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/compatibility_tags.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/compatibility_tags.py @@ -1,29 +1,30 @@ """Generate and work with PEP 425 Compatibility Tags. """ +from __future__ import annotations import re -from typing import List, Optional, Tuple +from typing import List +from typing import Optional +from typing import Tuple -from pip._vendor.packaging.tags import ( - PythonVersion, - Tag, - compatible_tags, - cpython_tags, - generic_tags, - interpreter_name, - interpreter_version, - mac_platforms, -) +from pip._vendor.packaging.tags import compatible_tags +from pip._vendor.packaging.tags import cpython_tags +from pip._vendor.packaging.tags import generic_tags +from pip._vendor.packaging.tags import interpreter_name +from pip._vendor.packaging.tags import interpreter_version +from pip._vendor.packaging.tags import mac_platforms +from pip._vendor.packaging.tags import PythonVersion +from pip._vendor.packaging.tags import Tag -_osx_arch_pat = re.compile(r"(.+)_(\d+)_(\d+)_(.+)") +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') -def version_info_to_nodot(version_info: Tuple[int, ...]) -> str: +def version_info_to_nodot(version_info: tuple[int, ...]) -> str: # Only use up to the first two numbers. - return "".join(map(str, version_info[:2])) + return ''.join(map(str, version_info[:2])) -def _mac_platforms(arch: str) -> List[str]: +def _mac_platforms(arch: str) -> list[str]: match = _osx_arch_pat.match(arch) if match: name, major, minor, actual_arch = match.groups() @@ -34,7 +35,7 @@ def _mac_platforms(arch: str) -> List[str]: # actual prefix provided by the user in case they provided # something like "macosxcustom_". It may be good to remove # this as undocumented or deprecate it in the future. - "{}_{}".format(name, arch[len("macosx_") :]) + '{}_{}'.format(name, arch[len('macosx_'):]) for arch in mac_platforms(mac_version, actual_arch) ] else: @@ -43,39 +44,39 @@ def _mac_platforms(arch: str) -> List[str]: return arches -def _custom_manylinux_platforms(arch: str) -> List[str]: +def _custom_manylinux_platforms(arch: str) -> list[str]: arches = [arch] - arch_prefix, arch_sep, arch_suffix = arch.partition("_") - if arch_prefix == "manylinux2014": + arch_prefix, arch_sep, arch_suffix = arch.partition('_') + if arch_prefix == 'manylinux2014': # manylinux1/manylinux2010 wheels run on most manylinux2014 systems # with the exception of wheels depending on ncurses. PEP 599 states # manylinux1/manylinux2010 wheels should be considered # manylinux2014 wheels: # https://www.python.org/dev/peps/pep-0599/#backwards-compatibility-with-manylinux2010-wheels - if arch_suffix in {"i686", "x86_64"}: - arches.append("manylinux2010" + arch_sep + arch_suffix) - arches.append("manylinux1" + arch_sep + arch_suffix) - elif arch_prefix == "manylinux2010": + if arch_suffix in {'i686', 'x86_64'}: + arches.append('manylinux2010' + arch_sep + arch_suffix) + arches.append('manylinux1' + arch_sep + arch_suffix) + elif arch_prefix == 'manylinux2010': # manylinux1 wheels run on most manylinux2010 systems with the # exception of wheels depending on ncurses. PEP 571 states # manylinux1 wheels should be considered manylinux2010 wheels: # https://www.python.org/dev/peps/pep-0571/#backwards-compatibility-with-manylinux1-wheels - arches.append("manylinux1" + arch_sep + arch_suffix) + arches.append('manylinux1' + arch_sep + arch_suffix) return arches -def _get_custom_platforms(arch: str) -> List[str]: - arch_prefix, arch_sep, arch_suffix = arch.partition("_") - if arch.startswith("macosx"): +def _get_custom_platforms(arch: str) -> list[str]: + arch_prefix, arch_sep, arch_suffix = arch.partition('_') + if arch.startswith('macosx'): arches = _mac_platforms(arch) - elif arch_prefix in ["manylinux2014", "manylinux2010"]: + elif arch_prefix in ['manylinux2014', 'manylinux2010']: arches = _custom_manylinux_platforms(arch) else: arches = [arch] return arches -def _expand_allowed_platforms(platforms: Optional[List[str]]) -> Optional[List[str]]: +def _expand_allowed_platforms(platforms: list[str] | None) -> list[str] | None: if not platforms: return None @@ -100,21 +101,21 @@ def _get_python_version(version: str) -> PythonVersion: def _get_custom_interpreter( - implementation: Optional[str] = None, version: Optional[str] = None + implementation: str | None = None, version: str | None = None, ) -> str: if implementation is None: implementation = interpreter_name() if version is None: version = interpreter_version() - return f"{implementation}{version}" + return f'{implementation}{version}' def get_supported( - version: Optional[str] = None, - platforms: Optional[List[str]] = None, - impl: Optional[str] = None, - abis: Optional[List[str]] = None, -) -> List[Tag]: + version: str | None = None, + platforms: list[str] | None = None, + impl: str | None = None, + abis: list[str] | None = None, +) -> list[Tag]: """Return a list of supported tags for each version specified in `versions`. @@ -127,9 +128,9 @@ def get_supported( :param abis: specify a list of abis you want valid tags for, or None. If None, use the local interpreter abi. """ - supported: List[Tag] = [] + supported: list[Tag] = [] - python_version: Optional[PythonVersion] = None + python_version: PythonVersion | None = None if version is not None: python_version = _get_python_version(version) @@ -137,14 +138,14 @@ def get_supported( platforms = _expand_allowed_platforms(platforms) - is_cpython = (impl or interpreter_name()) == "cp" + is_cpython = (impl or interpreter_name()) == 'cp' if is_cpython: supported.extend( cpython_tags( python_version=python_version, abis=abis, platforms=platforms, - ) + ), ) else: supported.extend( @@ -152,14 +153,14 @@ def get_supported( interpreter=interpreter, abis=abis, platforms=platforms, - ) + ), ) supported.extend( compatible_tags( python_version=python_version, interpreter=interpreter, platforms=platforms, - ) + ), ) return supported diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/datetime.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/datetime.py index 8668b3b..150c093 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/datetime.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/datetime.py @@ -1,5 +1,6 @@ """For when pip wants to check the date or time. """ +from __future__ import annotations import datetime diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/deprecation.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/deprecation.py index 72bd6f2..b624aa8 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/deprecation.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/deprecation.py @@ -1,16 +1,20 @@ """ A module that implements tooling to enable easy warnings about deprecations. """ +from __future__ import annotations import logging import warnings -from typing import Any, Optional, TextIO, Type, Union - -from pip._vendor.packaging.version import parse +from typing import Any +from typing import Optional +from typing import TextIO +from typing import Type +from typing import Union from pip import __version__ as current_version # NOTE: tests patch this name. +from pip._vendor.packaging.version import parse -DEPRECATION_MSG_PREFIX = "DEPRECATION: " +DEPRECATION_MSG_PREFIX = 'DEPRECATION: ' class PipDeprecationWarning(Warning): @@ -22,12 +26,12 @@ _original_showwarning: Any = None # Warnings <-> Logging Integration def _showwarning( - message: Union[Warning, str], - category: Type[Warning], + message: Warning | str, + category: type[Warning], filename: str, lineno: int, - file: Optional[TextIO] = None, - line: Optional[str] = None, + file: TextIO | None = None, + line: str | None = None, ) -> None: if file is not None: if _original_showwarning is not None: @@ -35,7 +39,7 @@ def _showwarning( elif issubclass(category, PipDeprecationWarning): # We use a specially named logger which will handle all of the # deprecation messages for pip. - logger = logging.getLogger("pip._internal.deprecations") + logger = logging.getLogger('pip._internal.deprecations') logger.warning(message) else: _original_showwarning(message, category, filename, lineno, file, line) @@ -43,7 +47,7 @@ def _showwarning( def install_warning_logger() -> None: # Enable our Deprecation Warnings - warnings.simplefilter("default", PipDeprecationWarning, append=True) + warnings.simplefilter('default', PipDeprecationWarning, append=True) global _original_showwarning @@ -55,10 +59,10 @@ def install_warning_logger() -> None: def deprecated( *, reason: str, - replacement: Optional[str], - gone_in: Optional[str], - feature_flag: Optional[str] = None, - issue: Optional[int] = None, + replacement: str | None, + gone_in: str | None, + feature_flag: str | None = None, + issue: int | None = None, ) -> None: """Helper to deprecate existing functionality. @@ -84,30 +88,30 @@ def deprecated( is_gone = gone_in is not None and parse(current_version) >= parse(gone_in) message_parts = [ - (reason, f"{DEPRECATION_MSG_PREFIX}{{}}"), + (reason, f'{DEPRECATION_MSG_PREFIX}{{}}'), ( gone_in, - "pip {} will enforce this behaviour change." + 'pip {} will enforce this behaviour change.' if not is_gone - else "Since pip {}, this is no longer supported.", + else 'Since pip {}, this is no longer supported.', ), ( replacement, - "A possible replacement is {}.", + 'A possible replacement is {}.', ), ( feature_flag, - "You can use the flag --use-feature={} to test the upcoming behaviour." + 'You can use the flag --use-feature={} to test the upcoming behaviour.' if not is_gone else None, ), ( issue, - "Discussion can be found at https://github.com/pypa/pip/issues/{}", + 'Discussion can be found at https://github.com/pypa/pip/issues/{}', ), ] - message = " ".join( + message = ' '.join( format_str.format(value) for value, format_str in message_parts if format_str is not None and value is not None diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/direct_url_helpers.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/direct_url_helpers.py index 0e8e5e1..810be7a 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/direct_url_helpers.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/direct_url_helpers.py @@ -1,6 +1,11 @@ +from __future__ import annotations + from typing import Optional -from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo +from pip._internal.models.direct_url import ArchiveInfo +from pip._internal.models.direct_url import DirectUrl +from pip._internal.models.direct_url import DirInfo +from pip._internal.models.direct_url import VcsInfo from pip._internal.models.link import Link from pip._internal.utils.urls import path_to_url from pip._internal.vcs import vcs @@ -9,11 +14,11 @@ from pip._internal.vcs import vcs def direct_url_as_pep440_direct_reference(direct_url: DirectUrl, name: str) -> str: """Convert a DirectUrl to a pip requirement string.""" direct_url.validate() # if invalid, this is a pip bug - requirement = name + " @ " + requirement = name + ' @ ' fragments = [] if isinstance(direct_url.info, VcsInfo): - requirement += "{}+{}@{}".format( - direct_url.info.vcs, direct_url.url, direct_url.info.commit_id + requirement += '{}+{}@{}'.format( + direct_url.info.vcs, direct_url.url, direct_url.info.commit_id, ) elif isinstance(direct_url.info, ArchiveInfo): requirement += direct_url.url @@ -23,9 +28,9 @@ def direct_url_as_pep440_direct_reference(direct_url: DirectUrl, name: str) -> s assert isinstance(direct_url.info, DirInfo) requirement += direct_url.url if direct_url.subdirectory: - fragments.append("subdirectory=" + direct_url.subdirectory) + fragments.append('subdirectory=' + direct_url.subdirectory) if fragments: - requirement += "#" + "&".join(fragments) + requirement += '#' + '&'.join(fragments) return requirement @@ -37,13 +42,13 @@ def direct_url_for_editable(source_dir: str) -> DirectUrl: def direct_url_from_link( - link: Link, source_dir: Optional[str] = None, link_is_in_wheel_cache: bool = False + link: Link, source_dir: str | None = None, link_is_in_wheel_cache: bool = False, ) -> DirectUrl: if link.is_vcs: vcs_backend = vcs.get_backend_for_scheme(link.scheme) assert vcs_backend url, requested_revision, _ = vcs_backend.get_url_rev_and_auth( - link.url_without_fragment + link.url_without_fragment, ) # For VCS links, we need to find out and add commit_id. if link_is_in_wheel_cache: @@ -79,7 +84,7 @@ def direct_url_from_link( hash = None hash_name = link.hash_name if hash_name: - hash = f"{hash_name}={link.hash}" + hash = f'{hash_name}={link.hash}' return DirectUrl( url=link.url_without_fragment, info=ArchiveInfo(hash=hash), diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/distutils_args.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/distutils_args.py index e4aa5b8..4a6d65c 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/distutils_args.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/distutils_args.py @@ -1,20 +1,24 @@ +from __future__ import annotations + +from typing import Dict +from typing import List + from distutils.errors import DistutilsArgError from distutils.fancy_getopt import FancyGetopt -from typing import Dict, List _options = [ - ("exec-prefix=", None, ""), - ("home=", None, ""), - ("install-base=", None, ""), - ("install-data=", None, ""), - ("install-headers=", None, ""), - ("install-lib=", None, ""), - ("install-platlib=", None, ""), - ("install-purelib=", None, ""), - ("install-scripts=", None, ""), - ("prefix=", None, ""), - ("root=", None, ""), - ("user", None, ""), + ('exec-prefix=', None, ''), + ('home=', None, ''), + ('install-base=', None, ''), + ('install-data=', None, ''), + ('install-headers=', None, ''), + ('install-lib=', None, ''), + ('install-platlib=', None, ''), + ('install-purelib=', None, ''), + ('install-scripts=', None, ''), + ('prefix=', None, ''), + ('root=', None, ''), + ('user', None, ''), ] @@ -22,7 +26,7 @@ _options = [ _distutils_getopt = FancyGetopt(_options) # type: ignore -def parse_distutils_args(args: List[str]) -> Dict[str, str]: +def parse_distutils_args(args: list[str]) -> dict[str, str]: """Parse provided arguments, returning an object that has the matched arguments. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/egg_link.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/egg_link.py index 9e0da8d..7fe3b02 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/egg_link.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/egg_link.py @@ -1,20 +1,20 @@ # The following comment should be removed at some point in the future. # mypy: strict-optional=False +from __future__ import annotations import os import re import sys from typing import Optional -from pip._internal.locations import site_packages, user_site -from pip._internal.utils.virtualenv import ( - running_under_virtualenv, - virtualenv_no_global, -) +from pip._internal.locations import site_packages +from pip._internal.locations import user_site +from pip._internal.utils.virtualenv import running_under_virtualenv +from pip._internal.utils.virtualenv import virtualenv_no_global __all__ = [ - "egg_link_path_from_sys_path", - "egg_link_path_from_location", + 'egg_link_path_from_sys_path', + 'egg_link_path_from_location', ] @@ -24,10 +24,10 @@ def _egg_link_name(raw_name: str) -> str: the same substitution as pkg_resources's safe_name function. Note: we cannot use canonicalize_name because it has a different logic. """ - return re.sub("[^A-Za-z0-9.]+", "-", raw_name) + ".egg-link" + return re.sub('[^A-Za-z0-9.]+', '-', raw_name) + '.egg-link' -def egg_link_path_from_sys_path(raw_name: str) -> Optional[str]: +def egg_link_path_from_sys_path(raw_name: str) -> str | None: """ Look for a .egg-link file for project name, by walking sys.path. """ @@ -39,7 +39,7 @@ def egg_link_path_from_sys_path(raw_name: str) -> Optional[str]: return None -def egg_link_path_from_location(raw_name: str) -> Optional[str]: +def egg_link_path_from_location(raw_name: str) -> str | None: """ Return the path for the .egg-link file if it exists, otherwise, None. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/encoding.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/encoding.py index 1c73f6c..cd89689 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/encoding.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/encoding.py @@ -1,20 +1,23 @@ +from __future__ import annotations + import codecs import locale import re import sys -from typing import List, Tuple +from typing import List +from typing import Tuple -BOMS: List[Tuple[bytes, str]] = [ - (codecs.BOM_UTF8, "utf-8"), - (codecs.BOM_UTF16, "utf-16"), - (codecs.BOM_UTF16_BE, "utf-16-be"), - (codecs.BOM_UTF16_LE, "utf-16-le"), - (codecs.BOM_UTF32, "utf-32"), - (codecs.BOM_UTF32_BE, "utf-32-be"), - (codecs.BOM_UTF32_LE, "utf-32-le"), +BOMS: list[tuple[bytes, str]] = [ + (codecs.BOM_UTF8, 'utf-8'), + (codecs.BOM_UTF16, 'utf-16'), + (codecs.BOM_UTF16_BE, 'utf-16-be'), + (codecs.BOM_UTF16_LE, 'utf-16-le'), + (codecs.BOM_UTF32, 'utf-32'), + (codecs.BOM_UTF32_BE, 'utf-32-be'), + (codecs.BOM_UTF32_LE, 'utf-32-le'), ] -ENCODING_RE = re.compile(br"coding[:=]\s*([-\w.]+)") +ENCODING_RE = re.compile(br'coding[:=]\s*([-\w.]+)') def auto_decode(data: bytes) -> str: @@ -23,13 +26,13 @@ def auto_decode(data: bytes) -> str: Fallback to locale.getpreferredencoding(False) like open() on Python3""" for bom, encoding in BOMS: if data.startswith(bom): - return data[len(bom) :].decode(encoding) + return data[len(bom):].decode(encoding) # Lets check the first two lines as in PEP263 - for line in data.split(b"\n")[:2]: - if line[0:1] == b"#" and ENCODING_RE.search(line): + for line in data.split(b'\n')[:2]: + if line[0:1] == b'#' and ENCODING_RE.search(line): result = ENCODING_RE.search(line) assert result is not None - encoding = result.groups()[0].decode("ascii") + encoding = result.groups()[0].decode('ascii') return data.decode(encoding) return data.decode( locale.getpreferredencoding(False) or sys.getdefaultencoding(), diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/entrypoints.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/entrypoints.py index 1504a12..3bccf75 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/entrypoints.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/entrypoints.py @@ -1,10 +1,13 @@ +from __future__ import annotations + import sys -from typing import List, Optional +from typing import List +from typing import Optional from pip._internal.cli.main import main -def _wrapper(args: Optional[List[str]] = None) -> int: +def _wrapper(args: list[str] | None = None) -> int: """Central wrapper for all old entrypoints. Historically pip has had several entrypoints defined. Because of issues @@ -17,11 +20,11 @@ def _wrapper(args: Optional[List[str]] = None) -> int: our old entrypoints as wrappers for the current one. """ sys.stderr.write( - "WARNING: pip is being invoked by an old script wrapper. This will " - "fail in a future version of pip.\n" - "Please see https://github.com/pypa/pip/issues/5599 for advice on " - "fixing the underlying issue.\n" + 'WARNING: pip is being invoked by an old script wrapper. This will ' + 'fail in a future version of pip.\n' + 'Please see https://github.com/pypa/pip/issues/5599 for advice on ' + 'fixing the underlying issue.\n' "To avoid this problem you can invoke Python with '-m pip' instead of " - "running pip directly.\n" + 'running pip directly.\n', ) return main(args) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/filesystem.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/filesystem.py index b7e6191..63f3cf7 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/filesystem.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/filesystem.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import fnmatch -import os import os.path import random import shutil @@ -7,18 +8,24 @@ import stat import sys from contextlib import contextmanager from tempfile import NamedTemporaryFile -from typing import Any, BinaryIO, Iterator, List, Union, cast - -from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed +from typing import Any +from typing import BinaryIO +from typing import cast +from typing import Iterator +from typing import List +from typing import Union from pip._internal.utils.compat import get_path_uid from pip._internal.utils.misc import format_size +from pip._vendor.tenacity import retry +from pip._vendor.tenacity import stop_after_delay +from pip._vendor.tenacity import wait_fixed def check_path_owner(path: str) -> bool: # If we don't have a way to check the effective uid of this process, then # we'll just assume that we own the directory. - if sys.platform == "win32" or not hasattr(os, "geteuid"): + if sys.platform == 'win32' or not hasattr(os, 'geteuid'): return True assert os.path.isabs(path) @@ -60,7 +67,7 @@ def copy2_fixed(src: str, dest: str) -> None: pass else: if is_socket_file: - raise shutil.SpecialFileError(f"`{f}` is a socket") + raise shutil.SpecialFileError(f'`{f}` is a socket') raise @@ -83,7 +90,7 @@ def adjacent_tmp_file(path: str, **kwargs: Any) -> Iterator[BinaryIO]: delete=False, dir=os.path.dirname(path), prefix=os.path.basename(path), - suffix=".tmp", + suffix='.tmp', **kwargs, ) as f: result = cast(BinaryIO, f) @@ -114,7 +121,7 @@ def test_writable_dir(path: str) -> bool: break # Should never get here, but infinite loops are bad path = parent - if os.name == "posix": + if os.name == 'posix': return os.access(path, os.W_OK) return _test_writable_dir_win(path) @@ -123,10 +130,10 @@ def test_writable_dir(path: str) -> bool: def _test_writable_dir_win(path: str) -> bool: # os.access doesn't work on Windows: http://bugs.python.org/issue2528 # and we can't use tempfile: http://bugs.python.org/issue22107 - basename = "accesstest_deleteme_fishfingers_custard_" - alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" + basename = 'accesstest_deleteme_fishfingers_custard_' + alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789' for _ in range(10): - name = basename + "".join(random.choice(alphabet) for _ in range(6)) + name = basename + ''.join(random.choice(alphabet) for _ in range(6)) file = os.path.join(path, name) try: fd = os.open(file, os.O_RDWR | os.O_CREAT | os.O_EXCL) @@ -145,20 +152,20 @@ def _test_writable_dir_win(path: str) -> bool: return True # This should never be reached - raise OSError("Unexpected condition testing for writable directory") + raise OSError('Unexpected condition testing for writable directory') -def find_files(path: str, pattern: str) -> List[str]: +def find_files(path: str, pattern: str) -> list[str]: """Returns a list of absolute paths of files beneath path, recursively, with filenames which match the UNIX-style shell glob pattern.""" - result: List[str] = [] + result: list[str] = [] for root, _, files in os.walk(path): matches = fnmatch.filter(files, pattern) result.extend(os.path.join(root, f) for f in matches) return result -def file_size(path: str) -> Union[int, float]: +def file_size(path: str) -> int | float: # If it's a symlink, return 0. if os.path.islink(path): return 0 @@ -169,7 +176,7 @@ def format_file_size(path: str) -> str: return format_size(file_size(path)) -def directory_size(path: str) -> Union[int, float]: +def directory_size(path: str) -> int | float: size = 0.0 for root, _dirs, files in os.walk(path): for filename in files: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/filetypes.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/filetypes.py index 5948570..e793420 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/filetypes.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/filetypes.py @@ -1,21 +1,22 @@ """Filetype information. """ +from __future__ import annotations from typing import Tuple from pip._internal.utils.misc import splitext -WHEEL_EXTENSION = ".whl" -BZ2_EXTENSIONS: Tuple[str, ...] = (".tar.bz2", ".tbz") -XZ_EXTENSIONS: Tuple[str, ...] = ( - ".tar.xz", - ".txz", - ".tlz", - ".tar.lz", - ".tar.lzma", +WHEEL_EXTENSION = '.whl' +BZ2_EXTENSIONS: tuple[str, ...] = ('.tar.bz2', '.tbz') +XZ_EXTENSIONS: tuple[str, ...] = ( + '.tar.xz', + '.txz', + '.tlz', + '.tar.lz', + '.tar.lzma', ) -ZIP_EXTENSIONS: Tuple[str, ...] = (".zip", WHEEL_EXTENSION) -TAR_EXTENSIONS: Tuple[str, ...] = (".tar.gz", ".tgz", ".tar") +ZIP_EXTENSIONS: tuple[str, ...] = ('.zip', WHEEL_EXTENSION) +TAR_EXTENSIONS: tuple[str, ...] = ('.tar.gz', '.tgz', '.tar') ARCHIVE_EXTENSIONS = ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/glibc.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/glibc.py index 7bd3c20..4370242 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/glibc.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/glibc.py @@ -1,35 +1,37 @@ # The following comment should be removed at some point in the future. # mypy: strict-optional=False +from __future__ import annotations import os import sys -from typing import Optional, Tuple +from typing import Optional +from typing import Tuple -def glibc_version_string() -> Optional[str]: - "Returns glibc version string, or None if not using glibc." +def glibc_version_string() -> str | None: + 'Returns glibc version string, or None if not using glibc.' return glibc_version_string_confstr() or glibc_version_string_ctypes() -def glibc_version_string_confstr() -> Optional[str]: - "Primary implementation of glibc_version_string using os.confstr." +def glibc_version_string_confstr() -> str | None: + 'Primary implementation of glibc_version_string using os.confstr.' # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely # to be broken or missing. This strategy is used in the standard library # platform module: # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 - if sys.platform == "win32": + if sys.platform == 'win32': return None try: # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17": - _, version = os.confstr("CS_GNU_LIBC_VERSION").split() + _, version = os.confstr('CS_GNU_LIBC_VERSION').split() except (AttributeError, OSError, ValueError): # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... return None return version -def glibc_version_string_ctypes() -> Optional[str]: - "Fallback implementation of glibc_version_string using ctypes." +def glibc_version_string_ctypes() -> str | None: + 'Fallback implementation of glibc_version_string using ctypes.' try: import ctypes @@ -53,7 +55,7 @@ def glibc_version_string_ctypes() -> Optional[str]: version_str = gnu_get_libc_version() # py2 / py3 compatibility: if not isinstance(version_str, str): - version_str = version_str.decode("ascii") + version_str = version_str.decode('ascii') return version_str @@ -75,7 +77,7 @@ def glibc_version_string_ctypes() -> Optional[str]: # versions that was generated by pip 8.1.2 and earlier is useless and # misleading. Solution: instead of using platform, use our code that actually # works. -def libc_ver() -> Tuple[str, str]: +def libc_ver() -> tuple[str, str]: """Try to determine the glibc version Returns a tuple of strings (lib, version) which default to empty strings @@ -83,6 +85,6 @@ def libc_ver() -> Tuple[str, str]: """ glibc_version = glibc_version_string() if glibc_version is None: - return ("", "") + return ('', '') else: - return ("glibc", glibc_version) + return ('glibc', glibc_version) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/hashes.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/hashes.py index 82eb035..697d796 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/hashes.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/hashes.py @@ -1,7 +1,15 @@ -import hashlib -from typing import TYPE_CHECKING, BinaryIO, Dict, Iterator, List +from __future__ import annotations -from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError +import hashlib +from typing import BinaryIO +from typing import Dict +from typing import Iterator +from typing import List +from typing import TYPE_CHECKING + +from pip._internal.exceptions import HashMismatch +from pip._internal.exceptions import HashMissing +from pip._internal.exceptions import InstallationError from pip._internal.utils.misc import read_chunks if TYPE_CHECKING: @@ -14,12 +22,12 @@ if TYPE_CHECKING: # The recommended hash algo of the moment. Change this whenever the state of # the art changes; it won't hurt backward compatibility. -FAVORITE_HASH = "sha256" +FAVORITE_HASH = 'sha256' # Names of hashlib algorithms allowed by the --hash option and ``pip hash`` # Currently, those are the ones at least as collision-resistant as sha256. -STRONG_HASHES = ["sha256", "sha384", "sha512"] +STRONG_HASHES = ['sha256', 'sha384', 'sha512'] class Hashes: @@ -28,7 +36,7 @@ class Hashes: """ - def __init__(self, hashes: Dict[str, List[str]] = None) -> None: + def __init__(self, hashes: dict[str, list[str]] = None) -> None: """ :param hashes: A dict of algorithm names pointing to lists of allowed hex digests @@ -40,7 +48,7 @@ class Hashes: allowed[alg] = sorted(keys) self._allowed = allowed - def __and__(self, other: "Hashes") -> "Hashes": + def __and__(self, other: Hashes) -> Hashes: if not isinstance(other, Hashes): return NotImplemented @@ -79,7 +87,7 @@ class Hashes: try: gots[hash_name] = hashlib.new(hash_name) except (ValueError, TypeError): - raise InstallationError(f"Unknown hash name: {hash_name}") + raise InstallationError(f'Unknown hash name: {hash_name}') for chunk in chunks: for hash in gots.values(): @@ -90,7 +98,7 @@ class Hashes: return self._raise(gots) - def _raise(self, gots: Dict[str, "_Hash"]) -> "NoReturn": + def _raise(self, gots: dict[str, _Hash]) -> NoReturn: raise HashMismatch(self._allowed, gots) def check_against_file(self, file: BinaryIO) -> None: @@ -102,7 +110,7 @@ class Hashes: return self.check_against_chunks(read_chunks(file)) def check_against_path(self, path: str) -> None: - with open(path, "rb") as file: + with open(path, 'rb') as file: return self.check_against_file(file) def __bool__(self) -> bool: @@ -116,13 +124,13 @@ class Hashes: def __hash__(self) -> int: return hash( - ",".join( + ','.join( sorted( - ":".join((alg, digest)) + ':'.join((alg, digest)) for alg, digest_list in self._allowed.items() for digest in digest_list - ) - ) + ), + ), ) @@ -140,5 +148,5 @@ class MissingHashes(Hashes): # empty list, it will never match, so an error will always raise. super().__init__(hashes={FAVORITE_HASH: []}) - def _raise(self, gots: Dict[str, "_Hash"]) -> "NoReturn": + def _raise(self, gots: dict[str, _Hash]) -> NoReturn: raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/inject_securetransport.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/inject_securetransport.py index 276aa79..aa4d325 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/inject_securetransport.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/inject_securetransport.py @@ -6,13 +6,14 @@ sessions (or whatever) are created after injecting SecureTransport. Note that we only do the injection on macOS, when the linked OpenSSL is too old to handle TLSv1.2. """ +from __future__ import annotations import sys def inject_securetransport() -> None: # Only relevant on macOS - if sys.platform != "darwin": + if sys.platform != 'darwin': return try: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/logging.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/logging.py index 6e001c5..a583c90 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/logging.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/logging.py @@ -1,33 +1,39 @@ +from __future__ import annotations + import contextlib import errno -import logging import logging.handlers import os import sys import threading from dataclasses import dataclass from logging import Filter -from typing import IO, Any, ClassVar, Iterator, List, Optional, TextIO, Type +from typing import Any +from typing import ClassVar +from typing import IO +from typing import Iterator +from typing import List +from typing import Optional +from typing import TextIO +from typing import Type -from pip._vendor.rich.console import ( - Console, - ConsoleOptions, - ConsoleRenderable, - RenderResult, -) +from pip._internal.exceptions import DiagnosticPipError +from pip._internal.utils._log import getLogger +from pip._internal.utils._log import VERBOSE +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX +from pip._internal.utils.misc import ensure_dir +from pip._vendor.rich.console import Console +from pip._vendor.rich.console import ConsoleOptions +from pip._vendor.rich.console import ConsoleRenderable +from pip._vendor.rich.console import RenderResult from pip._vendor.rich.highlighter import NullHighlighter from pip._vendor.rich.logging import RichHandler from pip._vendor.rich.segment import Segment from pip._vendor.rich.style import Style -from pip._internal.exceptions import DiagnosticPipError -from pip._internal.utils._log import VERBOSE, getLogger -from pip._internal.utils.compat import WINDOWS -from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX -from pip._internal.utils.misc import ensure_dir - _log_state = threading.local() -subprocess_logger = getLogger("pip.subprocessor") +subprocess_logger = getLogger('pip.subprocessor') class BrokenStdoutLoggingError(Exception): @@ -36,7 +42,7 @@ class BrokenStdoutLoggingError(Exception): """ -def _is_broken_pipe_error(exc_class: Type[BaseException], exc: BaseException) -> bool: +def _is_broken_pipe_error(exc_class: type[BaseException], exc: BaseException) -> bool: if exc_class is BrokenPipeError: return True @@ -65,11 +71,11 @@ def indent_log(num: int = 2) -> Iterator[None]: def get_indentation() -> int: - return getattr(_log_state, "indentation", 0) + return getattr(_log_state, 'indentation', 0) class IndentingFormatter(logging.Formatter): - default_time_format = "%Y-%m-%dT%H:%M:%S" + default_time_format = '%Y-%m-%dT%H:%M:%S' def __init__( self, @@ -92,15 +98,15 @@ class IndentingFormatter(logging.Formatter): prefix to add to each line). """ if levelno < logging.WARNING: - return "" + return '' if formatted.startswith(DEPRECATION_MSG_PREFIX): # Then the message already has a prefix. We don't want it to # look like "WARNING: DEPRECATION: ...." - return "" + return '' if levelno < logging.ERROR: - return "WARNING: " + return 'WARNING: ' - return "ERROR: " + return 'ERROR: ' def format(self, record: logging.LogRecord) -> str: """ @@ -111,11 +117,11 @@ class IndentingFormatter(logging.Formatter): message_start = self.get_message_start(formatted, record.levelno) formatted = message_start + formatted - prefix = "" + prefix = '' if self.add_timestamp: - prefix = f"{self.formatTime(record)} " - prefix += " " * get_indentation() - formatted = "".join([prefix + line for line in formatted.splitlines(True)]) + prefix = f'{self.formatTime(record)} ' + prefix += ' ' * get_indentation() + formatted = ''.join([prefix + line for line in formatted.splitlines(True)]) return formatted @@ -125,20 +131,20 @@ class IndentedRenderable: indent: int def __rich_console__( - self, console: Console, options: ConsoleOptions + self, console: Console, options: ConsoleOptions, ) -> RenderResult: segments = console.render(self.renderable, options) lines = Segment.split_lines(segments) for line in lines: - yield Segment(" " * self.indent) + yield Segment(' ' * self.indent) yield from line - yield Segment("\n") + yield Segment('\n') class RichPipStreamHandler(RichHandler): - KEYWORDS: ClassVar[Optional[List[str]]] = [] + KEYWORDS: ClassVar[list[str] | None] = [] - def __init__(self, stream: Optional[TextIO], no_color: bool) -> None: + def __init__(self, stream: TextIO | None, no_color: bool) -> None: super().__init__( console=Console(file=stream, no_color=no_color, soft_wrap=True), show_time=False, @@ -149,27 +155,27 @@ class RichPipStreamHandler(RichHandler): # Our custom override on Rich's logger, to make things work as we need them to. def emit(self, record: logging.LogRecord) -> None: - style: Optional[Style] = None + style: Style | None = None # If we are given a diagnostic error to present, present it with indentation. - if record.msg == "[present-diagnostic] %s" and len(record.args) == 1: + if record.msg == '[present-diagnostic] %s' and len(record.args) == 1: diagnostic_error: DiagnosticPipError = record.args[0] # type: ignore[index] assert isinstance(diagnostic_error, DiagnosticPipError) renderable: ConsoleRenderable = IndentedRenderable( - diagnostic_error, indent=get_indentation() + diagnostic_error, indent=get_indentation(), ) else: message = self.format(record) renderable = self.render_message(record, message) if record.levelno is not None: if record.levelno >= logging.ERROR: - style = Style(color="red") + style = Style(color='red') elif record.levelno >= logging.WARNING: - style = Style(color="yellow") + style = Style(color='yellow') try: - self.console.print(renderable, overflow="ignore", crop=False, style=style) + self.console.print(renderable, overflow='ignore', crop=False, style=style) except Exception: self.handleError(record) @@ -182,10 +188,10 @@ class RichPipStreamHandler(RichHandler): # exception so we can handle it in main() instead of logging the # broken pipe error and continuing. if ( - exc_class - and exc - and self.console.file is sys.stdout - and _is_broken_pipe_error(exc_class, exc) + exc_class and + exc and + self.console.file is sys.stdout and + _is_broken_pipe_error(exc_class, exc) ): raise BrokenStdoutLoggingError() @@ -218,7 +224,7 @@ class ExcludeLoggerFilter(Filter): return not super().filter(record) -def setup_logging(verbosity: int, no_color: bool, user_log_file: Optional[str]) -> int: +def setup_logging(verbosity: int, no_color: bool, user_log_file: str | None) -> int: """Configures and sets up all of the logging Returns the requested logging level, as its integer value. @@ -245,99 +251,99 @@ def setup_logging(verbosity: int, no_color: bool, user_log_file: Optional[str]) include_user_log = user_log_file is not None if include_user_log: additional_log_file = user_log_file - root_level = "DEBUG" + root_level = 'DEBUG' else: - additional_log_file = "/dev/null" + additional_log_file = '/dev/null' root_level = level # Disable any logging besides WARNING unless we have DEBUG level logging # enabled for vendored libraries. - vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG" + vendored_log_level = 'WARNING' if level in ['INFO', 'ERROR'] else 'DEBUG' # Shorthands for clarity log_streams = { - "stdout": "ext://sys.stdout", - "stderr": "ext://sys.stderr", + 'stdout': 'ext://sys.stdout', + 'stderr': 'ext://sys.stderr', } handler_classes = { - "stream": "pip._internal.utils.logging.RichPipStreamHandler", - "file": "pip._internal.utils.logging.BetterRotatingFileHandler", + 'stream': 'pip._internal.utils.logging.RichPipStreamHandler', + 'file': 'pip._internal.utils.logging.BetterRotatingFileHandler', } - handlers = ["console", "console_errors", "console_subprocess"] + ( - ["user_log"] if include_user_log else [] + handlers = ['console', 'console_errors', 'console_subprocess'] + ( + ['user_log'] if include_user_log else [] ) logging.config.dictConfig( { - "version": 1, - "disable_existing_loggers": False, - "filters": { - "exclude_warnings": { - "()": "pip._internal.utils.logging.MaxLevelFilter", - "level": logging.WARNING, + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'exclude_warnings': { + '()': 'pip._internal.utils.logging.MaxLevelFilter', + 'level': logging.WARNING, }, - "restrict_to_subprocess": { - "()": "logging.Filter", - "name": subprocess_logger.name, + 'restrict_to_subprocess': { + '()': 'logging.Filter', + 'name': subprocess_logger.name, }, - "exclude_subprocess": { - "()": "pip._internal.utils.logging.ExcludeLoggerFilter", - "name": subprocess_logger.name, + 'exclude_subprocess': { + '()': 'pip._internal.utils.logging.ExcludeLoggerFilter', + 'name': subprocess_logger.name, }, }, - "formatters": { - "indent": { - "()": IndentingFormatter, - "format": "%(message)s", + 'formatters': { + 'indent': { + '()': IndentingFormatter, + 'format': '%(message)s', }, - "indent_with_timestamp": { - "()": IndentingFormatter, - "format": "%(message)s", - "add_timestamp": True, + 'indent_with_timestamp': { + '()': IndentingFormatter, + 'format': '%(message)s', + 'add_timestamp': True, }, }, - "handlers": { - "console": { - "level": level, - "class": handler_classes["stream"], - "no_color": no_color, - "stream": log_streams["stdout"], - "filters": ["exclude_subprocess", "exclude_warnings"], - "formatter": "indent", + 'handlers': { + 'console': { + 'level': level, + 'class': handler_classes['stream'], + 'no_color': no_color, + 'stream': log_streams['stdout'], + 'filters': ['exclude_subprocess', 'exclude_warnings'], + 'formatter': 'indent', }, - "console_errors": { - "level": "WARNING", - "class": handler_classes["stream"], - "no_color": no_color, - "stream": log_streams["stderr"], - "filters": ["exclude_subprocess"], - "formatter": "indent", + 'console_errors': { + 'level': 'WARNING', + 'class': handler_classes['stream'], + 'no_color': no_color, + 'stream': log_streams['stderr'], + 'filters': ['exclude_subprocess'], + 'formatter': 'indent', }, # A handler responsible for logging to the console messages # from the "subprocessor" logger. - "console_subprocess": { - "level": level, - "class": handler_classes["stream"], - "stream": log_streams["stderr"], - "no_color": no_color, - "filters": ["restrict_to_subprocess"], - "formatter": "indent", + 'console_subprocess': { + 'level': level, + 'class': handler_classes['stream'], + 'stream': log_streams['stderr'], + 'no_color': no_color, + 'filters': ['restrict_to_subprocess'], + 'formatter': 'indent', }, - "user_log": { - "level": "DEBUG", - "class": handler_classes["file"], - "filename": additional_log_file, - "encoding": "utf-8", - "delay": True, - "formatter": "indent_with_timestamp", + 'user_log': { + 'level': 'DEBUG', + 'class': handler_classes['file'], + 'filename': additional_log_file, + 'encoding': 'utf-8', + 'delay': True, + 'formatter': 'indent_with_timestamp', }, }, - "root": { - "level": root_level, - "handlers": handlers, + 'root': { + 'level': root_level, + 'handlers': handlers, }, - "loggers": {"pip._vendor": {"level": vendored_log_level}}, - } + 'loggers': {'pip._vendor': {'level': vendored_log_level}}, + }, ) return level_number diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/misc.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/misc.py index b07e56f..6615223 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/misc.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/misc.py @@ -1,5 +1,6 @@ # The following comment should be removed at some point in the future. # mypy: strict-optional=False +from __future__ import annotations import contextlib import errno @@ -14,69 +15,70 @@ import stat import sys import urllib.parse from io import StringIO -from itertools import filterfalse, tee, zip_longest +from itertools import filterfalse +from itertools import tee +from itertools import zip_longest from types import TracebackType -from typing import ( - Any, - BinaryIO, - Callable, - ContextManager, - Iterable, - Iterator, - List, - Optional, - TextIO, - Tuple, - Type, - TypeVar, - cast, -) - -from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed +from typing import Any +from typing import BinaryIO +from typing import Callable +from typing import cast +from typing import ContextManager +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import TextIO +from typing import Tuple +from typing import Type +from typing import TypeVar from pip import __version__ from pip._internal.exceptions import CommandError from pip._internal.locations import get_major_minor_version from pip._internal.utils.compat import WINDOWS from pip._internal.utils.virtualenv import running_under_virtualenv +from pip._vendor.tenacity import retry +from pip._vendor.tenacity import stop_after_delay +from pip._vendor.tenacity import wait_fixed __all__ = [ - "rmtree", - "display_path", - "backup_dir", - "ask", - "splitext", - "format_size", - "is_installable_dir", - "normalize_path", - "renames", - "get_prog", - "captured_stdout", - "ensure_dir", - "remove_auth_from_url", + 'rmtree', + 'display_path', + 'backup_dir', + 'ask', + 'splitext', + 'format_size', + 'is_installable_dir', + 'normalize_path', + 'renames', + 'get_prog', + 'captured_stdout', + 'ensure_dir', + 'remove_auth_from_url', ] logger = logging.getLogger(__name__) -T = TypeVar("T") +T = TypeVar('T') ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType] VersionInfo = Tuple[int, int, int] NetlocTuple = Tuple[str, Tuple[Optional[str], Optional[str]]] def get_pip_version() -> str: - pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..") + pip_pkg_dir = os.path.join(os.path.dirname(__file__), '..', '..') pip_pkg_dir = os.path.abspath(pip_pkg_dir) - return "pip {} from {} (python {})".format( + return 'pip {} from {} (python {})'.format( __version__, pip_pkg_dir, get_major_minor_version(), ) -def normalize_version_info(py_version_info: Tuple[int, ...]) -> Tuple[int, int, int]: +def normalize_version_info(py_version_info: tuple[int, ...]) -> tuple[int, int, int]: """ Convert a tuple of ints representing a Python version to one of length three. @@ -92,7 +94,7 @@ def normalize_version_info(py_version_info: Tuple[int, ...]) -> Tuple[int, int, elif len(py_version_info) > 3: py_version_info = py_version_info[:3] - return cast("VersionInfo", py_version_info) + return cast('VersionInfo', py_version_info) def ensure_dir(path: str) -> None: @@ -108,13 +110,13 @@ def ensure_dir(path: str) -> None: def get_prog() -> str: try: prog = os.path.basename(sys.argv[0]) - if prog in ("__main__.py", "-c"): - return f"{sys.executable} -m pip" + if prog in ('__main__.py', '-c'): + return f'{sys.executable} -m pip' else: return prog except (AttributeError, TypeError, IndexError): pass - return "pip" + return 'pip' # Retry every half second for up to 3 seconds @@ -149,11 +151,11 @@ def display_path(path: str) -> str: if possible.""" path = os.path.normcase(os.path.abspath(path)) if path.startswith(os.getcwd() + os.path.sep): - path = "." + path[len(os.getcwd()) :] + path = '.' + path[len(os.getcwd()):] return path -def backup_dir(dir: str, ext: str = ".bak") -> str: +def backup_dir(dir: str, ext: str = '.bak') -> str: """Figure out the name of a directory to back up the given dir to (adding .bak, .bak2, etc)""" n = 1 @@ -165,7 +167,7 @@ def backup_dir(dir: str, ext: str = ".bak") -> str: def ask_path_exists(message: str, options: Iterable[str]) -> str: - for action in os.environ.get("PIP_EXISTS_ACTION", "").split(): + for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): if action in options: return action return ask(message, options) @@ -173,9 +175,9 @@ def ask_path_exists(message: str, options: Iterable[str]) -> str: def _check_no_input(message: str) -> None: """Raise an error if no input is allowed.""" - if os.environ.get("PIP_NO_INPUT"): + if os.environ.get('PIP_NO_INPUT'): raise Exception( - f"No input was expected ($PIP_NO_INPUT set); question: {message}" + f'No input was expected ($PIP_NO_INPUT set); question: {message}', ) @@ -187,8 +189,8 @@ def ask(message: str, options: Iterable[str]) -> str: response = response.strip().lower() if response not in options: print( - "Your response ({!r}) was not one of the expected responses: " - "{}".format(response, ", ".join(options)) + 'Your response ({!r}) was not one of the expected responses: ' + '{}'.format(response, ', '.join(options)), ) else: return response @@ -214,26 +216,26 @@ def strtobool(val: str) -> int: 'val' is anything else. """ val = val.lower() - if val in ("y", "yes", "t", "true", "on", "1"): + if val in ('y', 'yes', 't', 'true', 'on', '1'): return 1 - elif val in ("n", "no", "f", "false", "off", "0"): + elif val in ('n', 'no', 'f', 'false', 'off', '0'): return 0 else: - raise ValueError(f"invalid truth value {val!r}") + raise ValueError(f'invalid truth value {val!r}') def format_size(bytes: float) -> str: if bytes > 1000 * 1000: - return "{:.1f} MB".format(bytes / 1000.0 / 1000) + return f'{bytes / 1000.0 / 1000:.1f} MB' elif bytes > 10 * 1000: - return "{} kB".format(int(bytes / 1000)) + return f'{int(bytes / 1000)} kB' elif bytes > 1000: - return "{:.1f} kB".format(bytes / 1000.0) + return f'{bytes / 1000.0:.1f} kB' else: - return "{} bytes".format(int(bytes)) + return f'{int(bytes)} bytes' -def tabulate(rows: Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]]: +def tabulate(rows: Iterable[Iterable[Any]]) -> tuple[list[str], list[int]]: """Return a list of formatted rows and a list of column sizes. For example:: @@ -242,8 +244,8 @@ def tabulate(rows: Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]]: (['foobar 2000', '3735928559'], [10, 4]) """ rows = [tuple(map(str, row)) for row in rows] - sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue="")] - table = [" ".join(map(str.ljust, row, sizes)).rstrip() for row in rows] + sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue='')] + table = [' '.join(map(str.ljust, row, sizes)).rstrip() for row in rows] return table, sizes @@ -257,9 +259,9 @@ def is_installable_dir(path: str) -> bool: """ if not os.path.isdir(path): return False - if os.path.isfile(os.path.join(path, "pyproject.toml")): + if os.path.isfile(os.path.join(path, 'pyproject.toml')): return True - if os.path.isfile(os.path.join(path, "setup.py")): + if os.path.isfile(os.path.join(path, 'setup.py')): return True return False @@ -286,10 +288,10 @@ def normalize_path(path: str, resolve_symlinks: bool = True) -> str: return os.path.normcase(path) -def splitext(path: str) -> Tuple[str, str]: +def splitext(path: str) -> tuple[str, str]: """Like os.path.splitext, but take off .tar too""" base, ext = posixpath.splitext(path) - if base.lower().endswith(".tar"): + if base.lower().endswith('.tar'): ext = base[-4:] + ext base = base[:-4] return base, ext @@ -334,7 +336,7 @@ class StreamWrapper(StringIO): orig_stream: TextIO = None @classmethod - def from_stream(cls, orig_stream: TextIO) -> "StreamWrapper": + def from_stream(cls, orig_stream: TextIO) -> StreamWrapper: cls.orig_stream = orig_stream return cls() @@ -369,47 +371,47 @@ def captured_stdout() -> ContextManager[StreamWrapper]: Taken from Lib/support/__init__.py in the CPython repo. """ - return captured_output("stdout") + return captured_output('stdout') def captured_stderr() -> ContextManager[StreamWrapper]: """ See captured_stdout(). """ - return captured_output("stderr") + return captured_output('stderr') # Simulates an enum -def enum(*sequential: Any, **named: Any) -> Type[Any]: +def enum(*sequential: Any, **named: Any) -> type[Any]: enums = dict(zip(sequential, range(len(sequential))), **named) reverse = {value: key for key, value in enums.items()} - enums["reverse_mapping"] = reverse - return type("Enum", (), enums) + enums['reverse_mapping'] = reverse + return type('Enum', (), enums) -def build_netloc(host: str, port: Optional[int]) -> str: +def build_netloc(host: str, port: int | None) -> str: """ Build a netloc from a host-port pair """ if port is None: return host - if ":" in host: + if ':' in host: # Only wrap host with square brackets when it is IPv6 - host = f"[{host}]" - return f"{host}:{port}" + host = f'[{host}]' + return f'{host}:{port}' -def build_url_from_netloc(netloc: str, scheme: str = "https") -> str: +def build_url_from_netloc(netloc: str, scheme: str = 'https') -> str: """ Build a full URL from a netloc. """ - if netloc.count(":") >= 2 and "@" not in netloc and "[" not in netloc: + if netloc.count(':') >= 2 and '@' not in netloc and '[' not in netloc: # It must be a bare IPv6 address, so wrap it with brackets. - netloc = f"[{netloc}]" - return f"{scheme}://{netloc}" + netloc = f'[{netloc}]' + return f'{scheme}://{netloc}' -def parse_netloc(netloc: str) -> Tuple[str, Optional[int]]: +def parse_netloc(netloc: str) -> tuple[str, int | None]: """ Return the host-port pair from a netloc. """ @@ -424,19 +426,19 @@ def split_auth_from_netloc(netloc: str) -> NetlocTuple: Returns: (netloc, (username, password)). """ - if "@" not in netloc: + if '@' not in netloc: return netloc, (None, None) # Split from the right because that's how urllib.parse.urlsplit() # behaves if more than one @ is present (which can be checked using # the password attribute of urlsplit()'s return value). - auth, netloc = netloc.rsplit("@", 1) - pw: Optional[str] = None - if ":" in auth: + auth, netloc = netloc.rsplit('@', 1) + pw: str | None = None + if ':' in auth: # Split from the left because that's how urllib.parse.urlsplit() # behaves if more than one : is present (which again can be checked # using the password attribute of the return value) - user, pw = auth.split(":", 1) + user, pw = auth.split(':', 1) else: user, pw = auth, None @@ -459,19 +461,19 @@ def redact_netloc(netloc: str) -> str: if user is None: return netloc if password is None: - user = "****" - password = "" + user = '****' + password = '' else: user = urllib.parse.quote(user) - password = ":****" - return "{user}{password}@{netloc}".format( - user=user, password=password, netloc=netloc + password = ':****' + return '{user}{password}@{netloc}'.format( + user=user, password=password, netloc=netloc, ) def _transform_url( - url: str, transform_netloc: Callable[[str], Tuple[Any, ...]] -) -> Tuple[str, NetlocTuple]: + url: str, transform_netloc: Callable[[str], tuple[Any, ...]], +) -> tuple[str, NetlocTuple]: """Transform and replace netloc in a url. transform_netloc is a function taking the netloc and returning a @@ -486,18 +488,18 @@ def _transform_url( # stripped url url_pieces = (purl.scheme, netloc_tuple[0], purl.path, purl.query, purl.fragment) surl = urllib.parse.urlunsplit(url_pieces) - return surl, cast("NetlocTuple", netloc_tuple) + return surl, cast('NetlocTuple', netloc_tuple) def _get_netloc(netloc: str) -> NetlocTuple: return split_auth_from_netloc(netloc) -def _redact_netloc(netloc: str) -> Tuple[str]: +def _redact_netloc(netloc: str) -> tuple[str]: return (redact_netloc(netloc),) -def split_auth_netloc_from_url(url: str) -> Tuple[str, str, Tuple[str, str]]: +def split_auth_netloc_from_url(url: str) -> tuple[str, str, tuple[str, str]]: """ Parse a url into separate netloc, auth, and url with no auth. @@ -525,7 +527,7 @@ class HiddenText: self.redacted = redacted def __repr__(self) -> str: - return "".format(str(self)) + return f'' def __str__(self) -> str: return self.redacted @@ -541,7 +543,7 @@ class HiddenText: def hide_value(value: str) -> HiddenText: - return HiddenText(value, redacted="****") + return HiddenText(value, redacted='****') def hide_url(url: str) -> HiddenText: @@ -556,9 +558,9 @@ def protect_pip_from_modification_on_windows(modifying_pip: bool) -> None: python -m pip ... """ pip_names = [ - "pip.exe", - "pip{}.exe".format(sys.version_info[0]), - "pip{}.{}.exe".format(*sys.version_info[:2]), + 'pip.exe', + f'pip{sys.version_info[0]}.exe', + 'pip{}.{}.exe'.format(*sys.version_info[:2]), ] # See https://github.com/pypa/pip/issues/1299 for more discussion @@ -567,11 +569,11 @@ def protect_pip_from_modification_on_windows(modifying_pip: bool) -> None: ) if should_show_use_python_msg: - new_command = [sys.executable, "-m", "pip"] + sys.argv[1:] + new_command = [sys.executable, '-m', 'pip'] + sys.argv[1:] raise CommandError( - "To modify pip, please run the following command:\n{}".format( - " ".join(new_command) - ) + 'To modify pip, please run the following command:\n{}'.format( + ' '.join(new_command), + ), ) @@ -580,12 +582,12 @@ def is_console_interactive() -> bool: return sys.stdin is not None and sys.stdin.isatty() -def hash_file(path: str, blocksize: int = 1 << 20) -> Tuple[Any, int]: +def hash_file(path: str, blocksize: int = 1 << 20) -> tuple[Any, int]: """Return (hash, length) for path using hashlib.sha256()""" h = hashlib.sha256() length = 0 - with open(path, "rb") as f: + with open(path, 'rb') as f: for block in read_chunks(f, size=blocksize): length += len(block) h.update(block) @@ -604,7 +606,7 @@ def is_wheel_installed() -> bool: return True -def pairwise(iterable: Iterable[Any]) -> Iterator[Tuple[Any, Any]]: +def pairwise(iterable: Iterable[Any]) -> Iterator[tuple[Any, Any]]: """ Return paired elements. @@ -618,7 +620,7 @@ def pairwise(iterable: Iterable[Any]) -> Iterator[Tuple[Any, Any]]: def partition( pred: Callable[[T], bool], iterable: Iterable[T], -) -> Tuple[Iterable[T], Iterable[T]]: +) -> tuple[Iterable[T], Iterable[T]]: """ Use a predicate to partition entries into false entries and true entries, like diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/models.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/models.py index b6bb21a..6a40371 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/models.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/models.py @@ -1,16 +1,19 @@ """Utilities for defining models """ +from __future__ import annotations import operator -from typing import Any, Callable, Type +from typing import Any +from typing import Callable +from typing import Type class KeyBasedCompareMixin: """Provides comparison capabilities that is based on a key""" - __slots__ = ["_compare_key", "_defining_class"] + __slots__ = ['_compare_key', '_defining_class'] - def __init__(self, key: Any, defining_class: Type["KeyBasedCompareMixin"]) -> None: + def __init__(self, key: Any, defining_class: type[KeyBasedCompareMixin]) -> None: self._compare_key = key self._defining_class = defining_class diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/packaging.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/packaging.py index b9f6af4..8a2e3fb 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/packaging.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/packaging.py @@ -1,18 +1,24 @@ +from __future__ import annotations + import functools import logging import re -from typing import NewType, Optional, Tuple, cast +from typing import cast +from typing import NewType +from typing import Optional +from typing import Tuple -from pip._vendor.packaging import specifiers, version +from pip._vendor.packaging import specifiers +from pip._vendor.packaging import version from pip._vendor.packaging.requirements import Requirement -NormalizedExtra = NewType("NormalizedExtra", str) +NormalizedExtra = NewType('NormalizedExtra', str) logger = logging.getLogger(__name__) def check_requires_python( - requires_python: Optional[str], version_info: Tuple[int, ...] + requires_python: str | None, version_info: tuple[int, ...], ) -> bool: """ Check if the given Python version matches a "Requires-Python" specifier. @@ -30,7 +36,7 @@ def check_requires_python( return True requires_python_specifier = specifiers.SpecifierSet(requires_python) - python_version = version.parse(".".join(map(str, version_info))) + python_version = version.parse('.'.join(map(str, version_info))) return python_version in requires_python_specifier @@ -54,4 +60,4 @@ def safe_extra(extra: str) -> NormalizedExtra: This function is duplicated from ``pkg_resources``. Note that this is not the same to either ``canonicalize_name`` or ``_egg_link_name``. """ - return cast(NormalizedExtra, re.sub("[^A-Za-z0-9.-]+", "_", extra).lower()) + return cast(NormalizedExtra, re.sub('[^A-Za-z0-9.-]+', '_', extra).lower()) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/setuptools_build.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/setuptools_build.py index f460c40..fe4a6df 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/setuptools_build.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/setuptools_build.py @@ -1,6 +1,10 @@ +from __future__ import annotations + import sys import textwrap -from typing import List, Optional, Sequence +from typing import List +from typing import Optional +from typing import Sequence # Shim to wrap setup.py invocation with setuptools # Note that __file__ is handled via two {!r} *and* %r, to ensure that paths on @@ -42,7 +46,7 @@ _SETUPTOOLS_SHIM = textwrap.dedent( exec(compile(setup_py_code, filename, "exec")) ''' % ({!r},), "", "exec")) - """ + """, ).rstrip() @@ -51,7 +55,7 @@ def make_setuptools_shim_args( global_options: Sequence[str] = None, no_user_config: bool = False, unbuffered_output: bool = False, -) -> List[str]: +) -> list[str]: """ Get setuptools command arguments with shim wrapped setup file invocation. @@ -63,12 +67,12 @@ def make_setuptools_shim_args( """ args = [sys.executable] if unbuffered_output: - args += ["-u"] - args += ["-c", _SETUPTOOLS_SHIM.format(setup_py_path)] + args += ['-u'] + args += ['-c', _SETUPTOOLS_SHIM.format(setup_py_path)] if global_options: args += global_options if no_user_config: - args += ["--no-user-cfg"] + args += ['--no-user-cfg'] return args @@ -77,15 +81,15 @@ def make_setuptools_bdist_wheel_args( global_options: Sequence[str], build_options: Sequence[str], destination_dir: str, -) -> List[str]: +) -> list[str]: # NOTE: Eventually, we'd want to also -S to the flags here, when we're # isolating. Currently, it breaks Python in virtualenvs, because it # relies on site.py to find parts of the standard library outside the # virtualenv. args = make_setuptools_shim_args( - setup_py_path, global_options=global_options, unbuffered_output=True + setup_py_path, global_options=global_options, unbuffered_output=True, ) - args += ["bdist_wheel", "-d", destination_dir] + args += ['bdist_wheel', '-d', destination_dir] args += build_options return args @@ -93,11 +97,11 @@ def make_setuptools_bdist_wheel_args( def make_setuptools_clean_args( setup_py_path: str, global_options: Sequence[str], -) -> List[str]: +) -> list[str]: args = make_setuptools_shim_args( - setup_py_path, global_options=global_options, unbuffered_output=True + setup_py_path, global_options=global_options, unbuffered_output=True, ) - args += ["clean", "--all"] + args += ['clean', '--all'] return args @@ -106,10 +110,10 @@ def make_setuptools_develop_args( global_options: Sequence[str], install_options: Sequence[str], no_user_config: bool, - prefix: Optional[str], - home: Optional[str], + prefix: str | None, + home: str | None, use_user_site: bool, -) -> List[str]: +) -> list[str]: assert not (use_user_site and prefix) args = make_setuptools_shim_args( @@ -118,32 +122,32 @@ def make_setuptools_develop_args( no_user_config=no_user_config, ) - args += ["develop", "--no-deps"] + args += ['develop', '--no-deps'] args += install_options if prefix: - args += ["--prefix", prefix] + args += ['--prefix', prefix] if home is not None: - args += ["--install-dir", home] + args += ['--install-dir', home] if use_user_site: - args += ["--user", "--prefix="] + args += ['--user', '--prefix='] return args def make_setuptools_egg_info_args( setup_py_path: str, - egg_info_dir: Optional[str], + egg_info_dir: str | None, no_user_config: bool, -) -> List[str]: +) -> list[str]: args = make_setuptools_shim_args(setup_py_path, no_user_config=no_user_config) - args += ["egg_info"] + args += ['egg_info'] if egg_info_dir: - args += ["--egg-base", egg_info_dir] + args += ['--egg-base', egg_info_dir] return args @@ -153,14 +157,14 @@ def make_setuptools_install_args( global_options: Sequence[str], install_options: Sequence[str], record_filename: str, - root: Optional[str], - prefix: Optional[str], - header_dir: Optional[str], - home: Optional[str], + root: str | None, + prefix: str | None, + header_dir: str | None, + home: str | None, use_user_site: bool, no_user_config: bool, pycompile: bool, -) -> List[str]: +) -> list[str]: assert not (use_user_site and prefix) assert not (use_user_site and root) @@ -170,25 +174,25 @@ def make_setuptools_install_args( no_user_config=no_user_config, unbuffered_output=True, ) - args += ["install", "--record", record_filename] - args += ["--single-version-externally-managed"] + args += ['install', '--record', record_filename] + args += ['--single-version-externally-managed'] if root is not None: - args += ["--root", root] + args += ['--root', root] if prefix is not None: - args += ["--prefix", prefix] + args += ['--prefix', prefix] if home is not None: - args += ["--home", home] + args += ['--home', home] if use_user_site: - args += ["--user", "--prefix="] + args += ['--user', '--prefix='] if pycompile: - args += ["--compile"] + args += ['--compile'] else: - args += ["--no-compile"] + args += ['--no-compile'] if header_dir: - args += ["--install-headers", header_dir] + args += ['--install-headers', header_dir] args += install_options diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/subprocess.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/subprocess.py index b5b7624..869d9c4 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/subprocess.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/subprocess.py @@ -1,24 +1,25 @@ +from __future__ import annotations + import logging import os import shlex import subprocess -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Iterable, - List, - Mapping, - Optional, - Union, -) +from typing import Any +from typing import Callable +from typing import Iterable +from typing import List +from typing import Mapping +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union -from pip._vendor.rich.markup import escape - -from pip._internal.cli.spinners import SpinnerInterface, open_spinner +from pip._internal.cli.spinners import open_spinner +from pip._internal.cli.spinners import SpinnerInterface from pip._internal.exceptions import InstallationSubprocessError -from pip._internal.utils.logging import VERBOSE, subprocess_logger +from pip._internal.utils.logging import subprocess_logger +from pip._internal.utils.logging import VERBOSE from pip._internal.utils.misc import HiddenText +from pip._vendor.rich.markup import escape if TYPE_CHECKING: # Literal was introduced in Python 3.8. @@ -29,7 +30,7 @@ if TYPE_CHECKING: CommandArgs = List[Union[str, HiddenText]] -def make_command(*args: Union[str, HiddenText, CommandArgs]) -> CommandArgs: +def make_command(*args: str | HiddenText | CommandArgs) -> CommandArgs: """ Create a CommandArgs object. """ @@ -46,7 +47,7 @@ def make_command(*args: Union[str, HiddenText, CommandArgs]) -> CommandArgs: return command_args -def format_command_args(args: Union[List[str], CommandArgs]) -> str: +def format_command_args(args: list[str] | CommandArgs) -> str: """ Format command arguments for display. """ @@ -55,13 +56,13 @@ def format_command_args(args: Union[List[str], CommandArgs]) -> str: # this can trigger a UnicodeDecodeError in Python 2 if the argument # has type unicode and includes a non-ascii character. (The type # checker doesn't ensure the annotations are correct in all cases.) - return " ".join( + return ' '.join( shlex.quote(str(arg)) if isinstance(arg, HiddenText) else shlex.quote(arg) for arg in args ) -def reveal_command_args(args: Union[List[str], CommandArgs]) -> List[str]: +def reveal_command_args(args: list[str] | CommandArgs) -> list[str]: """ Return the arguments in their raw, unredacted form. """ @@ -69,16 +70,16 @@ def reveal_command_args(args: Union[List[str], CommandArgs]) -> List[str]: def call_subprocess( - cmd: Union[List[str], CommandArgs], + cmd: list[str] | CommandArgs, show_stdout: bool = False, - cwd: Optional[str] = None, - on_returncode: 'Literal["raise", "warn", "ignore"]' = "raise", - extra_ok_returncodes: Optional[Iterable[int]] = None, - extra_environ: Optional[Mapping[str, Any]] = None, - unset_environ: Optional[Iterable[str]] = None, - spinner: Optional[SpinnerInterface] = None, - log_failed_cmd: Optional[bool] = True, - stdout_only: Optional[bool] = False, + cwd: str | None = None, + on_returncode: Literal["raise", "warn", "ignore"] = 'raise', + extra_ok_returncodes: Iterable[int] | None = None, + extra_environ: Mapping[str, Any] | None = None, + unset_environ: Iterable[str] | None = None, + spinner: SpinnerInterface | None = None, + log_failed_cmd: bool | None = True, + stdout_only: bool | None = False, *, command_desc: str, ) -> str: @@ -131,7 +132,7 @@ def call_subprocess( # and we have a spinner. use_spinner = not showing_subprocess and spinner is not None - log_subprocess("Running command %s", command_desc) + log_subprocess('Running command %s', command_desc) env = os.environ.copy() if extra_environ: env.update(extra_environ) @@ -146,12 +147,12 @@ def call_subprocess( stderr=subprocess.STDOUT if not stdout_only else subprocess.PIPE, cwd=cwd, env=env, - errors="backslashreplace", + errors='backslashreplace', ) except Exception as exc: if log_failed_cmd: subprocess_logger.critical( - "Error %s while executing command %s", + 'Error %s while executing command %s', exc, command_desc, ) @@ -167,7 +168,7 @@ def call_subprocess( if not line: break line = line.rstrip() - all_output.append(line + "\n") + all_output.append(line + '\n') # Show the line immediately. log_subprocess(line) @@ -180,7 +181,7 @@ def call_subprocess( finally: if proc.stdout: proc.stdout.close() - output = "".join(all_output) + output = ''.join(all_output) else: # In this mode, stdout and stderr are in different pipes. # We must use communicate() which is the only safe way to read both. @@ -198,41 +199,41 @@ def call_subprocess( if use_spinner: assert spinner if proc_had_error: - spinner.finish("error") + spinner.finish('error') else: - spinner.finish("done") + spinner.finish('done') if proc_had_error: - if on_returncode == "raise": + if on_returncode == 'raise': error = InstallationSubprocessError( command_description=command_desc, exit_code=proc.returncode, output_lines=all_output if not showing_subprocess else None, ) if log_failed_cmd: - subprocess_logger.error("[present-diagnostic] %s", error) + subprocess_logger.error('[present-diagnostic] %s', error) subprocess_logger.verbose( - "[bold magenta]full command[/]: [blue]%s[/]", + '[bold magenta]full command[/]: [blue]%s[/]', escape(format_command_args(cmd)), - extra={"markup": True}, + extra={'markup': True}, ) subprocess_logger.verbose( - "[bold magenta]cwd[/]: %s", - escape(cwd or "[inherit]"), - extra={"markup": True}, + '[bold magenta]cwd[/]: %s', + escape(cwd or '[inherit]'), + extra={'markup': True}, ) raise error - elif on_returncode == "warn": + elif on_returncode == 'warn': subprocess_logger.warning( 'Command "%s" had error code %s in %s', command_desc, proc.returncode, cwd, ) - elif on_returncode == "ignore": + elif on_returncode == 'ignore': pass else: - raise ValueError(f"Invalid value: on_returncode={on_returncode!r}") + raise ValueError(f'Invalid value: on_returncode={on_returncode!r}') return output @@ -244,9 +245,9 @@ def runner_with_spinner_message(message: str) -> Callable[..., None]: """ def runner( - cmd: List[str], - cwd: Optional[str] = None, - extra_environ: Optional[Mapping[str, Any]] = None, + cmd: list[str], + cwd: str | None = None, + extra_environ: Mapping[str, Any] | None = None, ) -> None: with open_spinner(message) as spinner: call_subprocess( diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/temp_dir.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/temp_dir.py index 442679a..8df917e 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/temp_dir.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/temp_dir.py @@ -1,28 +1,37 @@ +from __future__ import annotations + import errno import itertools import logging import os.path import tempfile -from contextlib import ExitStack, contextmanager -from typing import Any, Dict, Iterator, Optional, TypeVar, Union +from contextlib import contextmanager +from contextlib import ExitStack +from typing import Any +from typing import Dict +from typing import Iterator +from typing import Optional +from typing import TypeVar +from typing import Union -from pip._internal.utils.misc import enum, rmtree +from pip._internal.utils.misc import enum +from pip._internal.utils.misc import rmtree logger = logging.getLogger(__name__) -_T = TypeVar("_T", bound="TempDirectory") +_T = TypeVar('_T', bound='TempDirectory') # Kinds of temporary directories. Only needed for ones that are # globally-managed. tempdir_kinds = enum( - BUILD_ENV="build-env", - EPHEM_WHEEL_CACHE="ephem-wheel-cache", - REQ_BUILD="req-build", + BUILD_ENV='build-env', + EPHEM_WHEEL_CACHE='ephem-wheel-cache', + REQ_BUILD='req-build', ) -_tempdir_manager: Optional[ExitStack] = None +_tempdir_manager: ExitStack | None = None @contextmanager @@ -40,7 +49,7 @@ class TempDirectoryTypeRegistry: """Manages temp directory behavior""" def __init__(self) -> None: - self._should_delete: Dict[str, bool] = {} + self._should_delete: dict[str, bool] = {} def set_delete(self, kind: str, value: bool) -> None: """Indicate whether a TempDirectory of the given kind should be @@ -55,7 +64,7 @@ class TempDirectoryTypeRegistry: return self._should_delete.get(kind, True) -_tempdir_registry: Optional[TempDirectoryTypeRegistry] = None +_tempdir_registry: TempDirectoryTypeRegistry | None = None @contextmanager @@ -102,9 +111,9 @@ class TempDirectory: def __init__( self, - path: Optional[str] = None, - delete: Union[bool, None, _Default] = _default, - kind: str = "temp", + path: str | None = None, + delete: bool | None | _Default = _default, + kind: str = 'temp', globally_managed: bool = False, ): super().__init__() @@ -135,11 +144,11 @@ class TempDirectory: @property def path(self) -> str: - assert not self._deleted, f"Attempted to access deleted path: {self._path}" + assert not self._deleted, f'Attempted to access deleted path: {self._path}' return self._path def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.path!r}>" + return f'<{self.__class__.__name__} {self.path!r}>' def __enter__(self: _T) -> _T: return self @@ -161,8 +170,8 @@ class TempDirectory: # symlinked to another directory. This tends to confuse build # scripts, so we canonicalize the path by traversing potential # symlinks here. - path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-")) - logger.debug("Created temporary directory: %s", path) + path = os.path.realpath(tempfile.mkdtemp(prefix=f'pip-{kind}-')) + logger.debug('Created temporary directory: %s', path) return path def cleanup(self) -> None: @@ -193,10 +202,10 @@ class AdjacentTempDirectory(TempDirectory): # a usable name is found. # pkg_resources raises a different error for .dist-info folder # with leading '-' and invalid metadata - LEADING_CHARS = "-~.=%0123456789" + LEADING_CHARS = '-~.=%0123456789' - def __init__(self, original: str, delete: Optional[bool] = None) -> None: - self.original = original.rstrip("/\\") + def __init__(self, original: str, delete: bool | None = None) -> None: + self.original = original.rstrip('/\\') super().__init__(delete=delete) @classmethod @@ -210,18 +219,18 @@ class AdjacentTempDirectory(TempDirectory): """ for i in range(1, len(name)): for candidate in itertools.combinations_with_replacement( - cls.LEADING_CHARS, i - 1 + cls.LEADING_CHARS, i - 1, ): - new_name = "~" + "".join(candidate) + name[i:] + new_name = '~' + ''.join(candidate) + name[i:] if new_name != name: yield new_name # If we make it this far, we will have to make a longer name for i in range(len(cls.LEADING_CHARS)): for candidate in itertools.combinations_with_replacement( - cls.LEADING_CHARS, i + cls.LEADING_CHARS, i, ): - new_name = "~" + "".join(candidate) + name + new_name = '~' + ''.join(candidate) + name if new_name != name: yield new_name @@ -240,7 +249,7 @@ class AdjacentTempDirectory(TempDirectory): break else: # Final fallback on the default behavior. - path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-")) + path = os.path.realpath(tempfile.mkdtemp(prefix=f'pip-{kind}-')) - logger.debug("Created temporary directory: %s", path) + logger.debug('Created temporary directory: %s', path) return path diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/unpacking.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/unpacking.py index 5f63f97..b48aee9 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/unpacking.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/unpacking.py @@ -1,5 +1,6 @@ """Utilities related archives. """ +from __future__ import annotations import logging import os @@ -7,16 +8,16 @@ import shutil import stat import tarfile import zipfile -from typing import Iterable, List, Optional +from typing import Iterable +from typing import List +from typing import Optional from zipfile import ZipInfo from pip._internal.exceptions import InstallationError -from pip._internal.utils.filetypes import ( - BZ2_EXTENSIONS, - TAR_EXTENSIONS, - XZ_EXTENSIONS, - ZIP_EXTENSIONS, -) +from pip._internal.utils.filetypes import BZ2_EXTENSIONS +from pip._internal.utils.filetypes import TAR_EXTENSIONS +from pip._internal.utils.filetypes import XZ_EXTENSIONS +from pip._internal.utils.filetypes import ZIP_EXTENSIONS from pip._internal.utils.misc import ensure_dir logger = logging.getLogger(__name__) @@ -29,7 +30,7 @@ try: SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS except ImportError: - logger.debug("bz2 module is not available") + logger.debug('bz2 module is not available') try: # Only for Python 3.3+ @@ -37,7 +38,7 @@ try: SUPPORTED_EXTENSIONS += XZ_EXTENSIONS except ImportError: - logger.debug("lzma module is not available") + logger.debug('lzma module is not available') def current_umask() -> int: @@ -47,16 +48,16 @@ def current_umask() -> int: return mask -def split_leading_dir(path: str) -> List[str]: - path = path.lstrip("/").lstrip("\\") - if "/" in path and ( - ("\\" in path and path.find("/") < path.find("\\")) or "\\" not in path +def split_leading_dir(path: str) -> list[str]: + path = path.lstrip('/').lstrip('\\') + if '/' in path and ( + ('\\' in path and path.find('/') < path.find('\\')) or '\\' not in path ): - return path.split("/", 1) - elif "\\" in path: - return path.split("\\", 1) + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) else: - return [path, ""] + return [path, ''] def has_leading_dir(paths: Iterable[str]) -> bool: @@ -110,7 +111,7 @@ def unzip_file(filename: str, location: str, flatten: bool = True) -> None: no-ops per the python docs. """ ensure_dir(location) - zipfp = open(filename, "rb") + zipfp = open(filename, 'rb') try: zip = zipfile.ZipFile(zipfp, allowZip64=True) leading = has_leading_dir(zip.namelist()) and flatten @@ -123,11 +124,11 @@ def unzip_file(filename: str, location: str, flatten: bool = True) -> None: dir = os.path.dirname(fn) if not is_within_directory(location, fn): message = ( - "The zip file ({}) has a file ({}) trying to install " - "outside target directory ({})" + 'The zip file ({}) has a file ({}) trying to install ' + 'outside target directory ({})' ) raise InstallationError(message.format(filename, fn, location)) - if fn.endswith("/") or fn.endswith("\\"): + if fn.endswith('/') or fn.endswith('\\'): # A directory ensure_dir(fn) else: @@ -136,7 +137,7 @@ def unzip_file(filename: str, location: str, flatten: bool = True) -> None: # chunk of memory for the file's content fp = zip.open(name) try: - with open(fn, "wb") as destfp: + with open(fn, 'wb') as destfp: shutil.copyfileobj(fp, destfp) finally: fp.close() @@ -156,21 +157,21 @@ def untar_file(filename: str, location: str) -> None: no-ops per the python docs. """ ensure_dir(location) - if filename.lower().endswith(".gz") or filename.lower().endswith(".tgz"): - mode = "r:gz" + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' elif filename.lower().endswith(BZ2_EXTENSIONS): - mode = "r:bz2" + mode = 'r:bz2' elif filename.lower().endswith(XZ_EXTENSIONS): - mode = "r:xz" - elif filename.lower().endswith(".tar"): - mode = "r" + mode = 'r:xz' + elif filename.lower().endswith('.tar'): + mode = 'r' else: logger.warning( - "Cannot determine compression type for file %s", + 'Cannot determine compression type for file %s', filename, ) - mode = "r:*" - tar = tarfile.open(filename, mode, encoding="utf-8") + mode = 'r:*' + tar = tarfile.open(filename, mode, encoding='utf-8') try: leading = has_leading_dir([member.name for member in tar.getmembers()]) for member in tar.getmembers(): @@ -180,8 +181,8 @@ def untar_file(filename: str, location: str) -> None: path = os.path.join(location, fn) if not is_within_directory(location, path): message = ( - "The tar file ({}) has a file ({}) trying to install " - "outside target directory ({})" + 'The tar file ({}) has a file ({}) trying to install ' + 'outside target directory ({})' ) raise InstallationError(message.format(filename, path, location)) if member.isdir(): @@ -194,7 +195,7 @@ def untar_file(filename: str, location: str) -> None: # Some corrupt tar files seem to produce this # (specifically bad symlinks) logger.warning( - "In the tar file %s the member %s is invalid: %s", + 'In the tar file %s the member %s is invalid: %s', filename, member.name, exc, @@ -207,7 +208,7 @@ def untar_file(filename: str, location: str) -> None: # Some corrupt tar files seem to produce this # (specifically bad symlinks) logger.warning( - "In the tar file %s the member %s is invalid: %s", + 'In the tar file %s the member %s is invalid: %s', filename, member.name, exc, @@ -215,7 +216,7 @@ def untar_file(filename: str, location: str) -> None: continue ensure_dir(os.path.dirname(path)) assert fp is not None - with open(path, "wb") as destfp: + with open(path, 'wb') as destfp: shutil.copyfileobj(fp, destfp) fp.close() # Update the timestamp (useful for cython compiled files) @@ -230,29 +231,29 @@ def untar_file(filename: str, location: str) -> None: def unpack_file( filename: str, location: str, - content_type: Optional[str] = None, + content_type: str | None = None, ) -> None: filename = os.path.realpath(filename) if ( - content_type == "application/zip" - or filename.lower().endswith(ZIP_EXTENSIONS) - or zipfile.is_zipfile(filename) + content_type == 'application/zip' or + filename.lower().endswith(ZIP_EXTENSIONS) or + zipfile.is_zipfile(filename) ): - unzip_file(filename, location, flatten=not filename.endswith(".whl")) + unzip_file(filename, location, flatten=not filename.endswith('.whl')) elif ( - content_type == "application/x-gzip" - or tarfile.is_tarfile(filename) - or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS) + content_type == 'application/x-gzip' or + tarfile.is_tarfile(filename) or + filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS) ): untar_file(filename, location) else: # FIXME: handle? # FIXME: magic signatures? logger.critical( - "Cannot unpack file %s (downloaded from %s, content-type: %s); " - "cannot detect archive format", + 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' + 'cannot detect archive format', filename, location, content_type, ) - raise InstallationError(f"Cannot determine archive format of {location}") + raise InstallationError(f'Cannot determine archive format of {location}') diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/urls.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/urls.py index 6ba2e04..412ac4d 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/urls.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/urls.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import string import urllib.parse @@ -7,10 +9,10 @@ from typing import Optional from .compat import WINDOWS -def get_url_scheme(url: str) -> Optional[str]: - if ":" not in url: +def get_url_scheme(url: str) -> str | None: + if ':' not in url: return None - return url.split(":", 1)[0].lower() + return url.split(':', 1)[0].lower() def path_to_url(path: str) -> str: @@ -19,7 +21,7 @@ def path_to_url(path: str) -> str: quoted path parts. """ path = os.path.normpath(os.path.abspath(path)) - url = urllib.parse.urljoin("file:", urllib.request.pathname2url(path)) + url = urllib.parse.urljoin('file:', urllib.request.pathname2url(path)) return url @@ -28,20 +30,20 @@ def url_to_path(url: str) -> str: Convert a file: URL to a path. """ assert url.startswith( - "file:" - ), f"You can only turn file: urls into filenames (not {url!r})" + 'file:', + ), f'You can only turn file: urls into filenames (not {url!r})' _, netloc, path, _, _ = urllib.parse.urlsplit(url) - if not netloc or netloc == "localhost": + if not netloc or netloc == 'localhost': # According to RFC 8089, same as empty authority. - netloc = "" + netloc = '' elif WINDOWS: # If we have a UNC path, prepend UNC share notation. - netloc = "\\\\" + netloc + netloc = '\\\\' + netloc else: raise ValueError( - f"non-local file URIs are not supported on this platform: {url!r}" + f'non-local file URIs are not supported on this platform: {url!r}', ) path = urllib.request.url2pathname(netloc + path) @@ -50,12 +52,12 @@ def url_to_path(url: str) -> str: # This creates issues for path-related functions like io.open(), so we try # to detect and strip the leading slash. if ( - WINDOWS - and not netloc # Not UNC. - and len(path) >= 3 - and path[0] == "/" # Leading slash to strip. - and path[1] in string.ascii_letters # Drive letter. - and path[2:4] in (":", ":/") # Colon + end of string, or colon + absolute path. + WINDOWS and + not netloc and # Not UNC. + len(path) >= 3 and + path[0] == '/' and # Leading slash to strip. + path[1] in string.ascii_letters and # Drive letter. + path[2:4] in (':', ':/') # Colon + end of string, or colon + absolute path. ): path = path[1:] diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/virtualenv.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/virtualenv.py index c926db4..6e24f74 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/virtualenv.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/virtualenv.py @@ -1,13 +1,16 @@ +from __future__ import annotations + import logging import os import re import site import sys -from typing import List, Optional +from typing import List +from typing import Optional logger = logging.getLogger(__name__) _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile( - r"include-system-site-packages\s*=\s*(?Ptrue|false)" + r'include-system-site-packages\s*=\s*(?Ptrue|false)', ) @@ -16,7 +19,7 @@ def _running_under_venv() -> bool: This handles PEP 405 compliant virtual environments. """ - return sys.prefix != getattr(sys, "base_prefix", sys.prefix) + return sys.prefix != getattr(sys, 'base_prefix', sys.prefix) def _running_under_regular_virtualenv() -> bool: @@ -25,7 +28,7 @@ def _running_under_regular_virtualenv() -> bool: This handles virtual environments created with pypa's virtualenv. """ # pypa/virtualenv case - return hasattr(sys, "real_prefix") + return hasattr(sys, 'real_prefix') def running_under_virtualenv() -> bool: @@ -33,16 +36,16 @@ def running_under_virtualenv() -> bool: return _running_under_venv() or _running_under_regular_virtualenv() -def _get_pyvenv_cfg_lines() -> Optional[List[str]]: +def _get_pyvenv_cfg_lines() -> list[str] | None: """Reads {sys.prefix}/pyvenv.cfg and returns its contents as list of lines Returns None, if it could not read/access the file. """ - pyvenv_cfg_file = os.path.join(sys.prefix, "pyvenv.cfg") + pyvenv_cfg_file = os.path.join(sys.prefix, 'pyvenv.cfg') try: # Although PEP 405 does not specify, the built-in venv module always # writes with UTF-8. (pypa/pip#8717) - with open(pyvenv_cfg_file, encoding="utf-8") as f: + with open(pyvenv_cfg_file, encoding='utf-8') as f: return f.read().splitlines() # avoids trailing newlines except OSError: return None @@ -65,14 +68,14 @@ def _no_global_under_venv() -> bool: # site-packages access (since that's PEP 405's default state). logger.warning( "Could not access 'pyvenv.cfg' despite a virtual environment " - "being active. Assuming global site-packages is not accessible " - "in this environment." + 'being active. Assuming global site-packages is not accessible ' + 'in this environment.', ) return True for line in cfg_lines: match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line) - if match is not None and match.group("value") == "false": + if match is not None and match.group('value') == 'false': return True return False @@ -86,7 +89,7 @@ def _no_global_under_regular_virtualenv() -> bool: site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) no_global_site_packages_file = os.path.join( site_mod_dir, - "no-global-site-packages.txt", + 'no-global-site-packages.txt', ) return os.path.exists(no_global_site_packages_file) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/utils/wheel.py b/.venv/lib/python3.10/site-packages/pip/_internal/utils/wheel.py index e5e3f34..2f1973b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/utils/wheel.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/utils/wheel.py @@ -1,15 +1,16 @@ """Support functions for working with wheel files. """ +from __future__ import annotations import logging from email.message import Message from email.parser import Parser from typing import Tuple -from zipfile import BadZipFile, ZipFile - -from pip._vendor.packaging.utils import canonicalize_name +from zipfile import BadZipFile +from zipfile import ZipFile from pip._internal.exceptions import UnsupportedWheel +from pip._vendor.packaging.utils import canonicalize_name VERSION_COMPATIBLE = (1, 0) @@ -17,7 +18,7 @@ VERSION_COMPATIBLE = (1, 0) logger = logging.getLogger(__name__) -def parse_wheel(wheel_zip: ZipFile, name: str) -> Tuple[str, Message]: +def parse_wheel(wheel_zip: ZipFile, name: str) -> tuple[str, Message]: """Extract information from the provided wheel, ensuring it meets basic standards. @@ -28,7 +29,7 @@ def parse_wheel(wheel_zip: ZipFile, name: str) -> Tuple[str, Message]: metadata = wheel_metadata(wheel_zip, info_dir) version = wheel_version(metadata) except UnsupportedWheel as e: - raise UnsupportedWheel("{} has an invalid wheel, {}".format(name, str(e))) + raise UnsupportedWheel(f'{name} has an invalid wheel, {str(e)}') check_compatibility(version, name) @@ -42,16 +43,16 @@ def wheel_dist_info_dir(source: ZipFile, name: str) -> str: it doesn't match the provided name. """ # Zip file path separators must be / - subdirs = {p.split("/", 1)[0] for p in source.namelist()} + subdirs = {p.split('/', 1)[0] for p in source.namelist()} - info_dirs = [s for s in subdirs if s.endswith(".dist-info")] + info_dirs = [s for s in subdirs if s.endswith('.dist-info')] if not info_dirs: - raise UnsupportedWheel(".dist-info directory not found") + raise UnsupportedWheel('.dist-info directory not found') if len(info_dirs) > 1: raise UnsupportedWheel( - "multiple .dist-info directories found: {}".format(", ".join(info_dirs)) + 'multiple .dist-info directories found: {}'.format(', '.join(info_dirs)), ) info_dir = info_dirs[0] @@ -60,9 +61,9 @@ def wheel_dist_info_dir(source: ZipFile, name: str) -> str: canonical_name = canonicalize_name(name) if not info_dir_name.startswith(canonical_name): raise UnsupportedWheel( - ".dist-info directory {!r} does not start with {!r}".format( - info_dir, canonical_name - ) + '.dist-info directory {!r} does not start with {!r}'.format( + info_dir, canonical_name, + ), ) return info_dir @@ -74,21 +75,21 @@ def read_wheel_metadata_file(source: ZipFile, path: str) -> bytes: # BadZipFile for general corruption, KeyError for missing entry, # and RuntimeError for password-protected files except (BadZipFile, KeyError, RuntimeError) as e: - raise UnsupportedWheel(f"could not read {path!r} file: {e!r}") + raise UnsupportedWheel(f'could not read {path!r} file: {e!r}') def wheel_metadata(source: ZipFile, dist_info_dir: str) -> Message: """Return the WHEEL metadata of an extracted wheel, if possible. Otherwise, raise UnsupportedWheel. """ - path = f"{dist_info_dir}/WHEEL" + path = f'{dist_info_dir}/WHEEL' # Zip file path separators must be / wheel_contents = read_wheel_metadata_file(source, path) try: wheel_text = wheel_contents.decode() except UnicodeDecodeError as e: - raise UnsupportedWheel(f"error decoding {path!r}: {e!r}") + raise UnsupportedWheel(f'error decoding {path!r}: {e!r}') # FeedParser (used by Parser) does not raise any exceptions. The returned # message may have .defects populated, but for backwards-compatibility we @@ -96,23 +97,23 @@ def wheel_metadata(source: ZipFile, dist_info_dir: str) -> Message: return Parser().parsestr(wheel_text) -def wheel_version(wheel_data: Message) -> Tuple[int, ...]: +def wheel_version(wheel_data: Message) -> tuple[int, ...]: """Given WHEEL metadata, return the parsed Wheel-Version. Otherwise, raise UnsupportedWheel. """ - version_text = wheel_data["Wheel-Version"] + version_text = wheel_data['Wheel-Version'] if version_text is None: - raise UnsupportedWheel("WHEEL is missing Wheel-Version") + raise UnsupportedWheel('WHEEL is missing Wheel-Version') version = version_text.strip() try: - return tuple(map(int, version.split("."))) + return tuple(map(int, version.split('.'))) except ValueError: - raise UnsupportedWheel(f"invalid Wheel-Version: {version!r}") + raise UnsupportedWheel(f'invalid Wheel-Version: {version!r}') -def check_compatibility(version: Tuple[int, ...], name: str) -> None: +def check_compatibility(version: tuple[int, ...], name: str) -> None: """Raises errors or warns if called with an incompatible Wheel-Version. pip should refuse to install a Wheel-Version that's a major series @@ -127,10 +128,10 @@ def check_compatibility(version: Tuple[int, ...], name: str) -> None: if version[0] > VERSION_COMPATIBLE[0]: raise UnsupportedWheel( "{}'s Wheel-Version ({}) is not compatible with this version " - "of pip".format(name, ".".join(map(str, version))) + 'of pip'.format(name, '.'.join(map(str, version))), ) elif version > VERSION_COMPATIBLE: logger.warning( - "Installing from a newer Wheel-Version (%s)", - ".".join(map(str, version)), + 'Installing from a newer Wheel-Version (%s)', + '.'.join(map(str, version)), ) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/__init__.py b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/__init__.py index b6beddb..063e0f1 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/__init__.py @@ -2,14 +2,14 @@ # the vcs package don't need to import deeper than `pip._internal.vcs`. # (The test directory may still need to import from a vcs sub-package.) # Import all vcs modules to register each VCS in the VcsSupport object. +from __future__ import annotations + import pip._internal.vcs.bazaar import pip._internal.vcs.git import pip._internal.vcs.mercurial import pip._internal.vcs.subversion # noqa: F401 -from pip._internal.vcs.versioncontrol import ( # noqa: F401 - RemoteNotFoundError, - RemoteNotValidError, - is_url, - make_vcs_requirement_url, - vcs, -) +from pip._internal.vcs.versioncontrol import is_url +from pip._internal.vcs.versioncontrol import make_vcs_requirement_url +from pip._internal.vcs.versioncontrol import RemoteNotFoundError +from pip._internal.vcs.versioncontrol import RemoteNotValidError +from pip._internal.vcs.versioncontrol import vcs diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/bazaar.py b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/bazaar.py index a7b16e2..e886fa8 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/bazaar.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/bazaar.py @@ -1,80 +1,83 @@ -import logging -from typing import List, Optional, Tuple +from __future__ import annotations -from pip._internal.utils.misc import HiddenText, display_path +import logging +from typing import List +from typing import Optional +from typing import Tuple + +from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import HiddenText from pip._internal.utils.subprocess import make_command from pip._internal.utils.urls import path_to_url -from pip._internal.vcs.versioncontrol import ( - AuthInfo, - RemoteNotFoundError, - RevOptions, - VersionControl, - vcs, -) +from pip._internal.vcs.versioncontrol import AuthInfo +from pip._internal.vcs.versioncontrol import RemoteNotFoundError +from pip._internal.vcs.versioncontrol import RevOptions +from pip._internal.vcs.versioncontrol import vcs +from pip._internal.vcs.versioncontrol import VersionControl logger = logging.getLogger(__name__) class Bazaar(VersionControl): - name = "bzr" - dirname = ".bzr" - repo_name = "branch" + name = 'bzr' + dirname = '.bzr' + repo_name = 'branch' schemes = ( - "bzr+http", - "bzr+https", - "bzr+ssh", - "bzr+sftp", - "bzr+ftp", - "bzr+lp", - "bzr+file", + 'bzr+http', + 'bzr+https', + 'bzr+ssh', + 'bzr+sftp', + 'bzr+ftp', + 'bzr+lp', + 'bzr+file', ) @staticmethod - def get_base_rev_args(rev: str) -> List[str]: - return ["-r", rev] + def get_base_rev_args(rev: str) -> list[str]: + return ['-r', rev] def fetch_new( - self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int, ) -> None: rev_display = rev_options.to_display() logger.info( - "Checking out %s%s to %s", + 'Checking out %s%s to %s', url, rev_display, display_path(dest), ) if verbosity <= 0: - flag = "--quiet" + flag = '--quiet' elif verbosity == 1: - flag = "" + flag = '' else: flag = f"-{'v'*verbosity}" - cmd_args = make_command("branch", flag, rev_options.to_args(), url, dest) + cmd_args = make_command('branch', flag, rev_options.to_args(), url, dest) self.run_command(cmd_args) def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: - self.run_command(make_command("switch", url), cwd=dest) + self.run_command(make_command('switch', url), cwd=dest) def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: - cmd_args = make_command("pull", "-q", rev_options.to_args()) + cmd_args = make_command('pull', '-q', rev_options.to_args()) self.run_command(cmd_args, cwd=dest) @classmethod - def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]: # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it url, rev, user_pass = super().get_url_rev_and_auth(url) - if url.startswith("ssh://"): - url = "bzr+" + url + if url.startswith('ssh://'): + url = 'bzr+' + url return url, rev, user_pass @classmethod def get_remote_url(cls, location: str) -> str: urls = cls.run_command( - ["info"], show_stdout=False, stdout_only=True, cwd=location + ['info'], show_stdout=False, stdout_only=True, cwd=location, ) for line in urls.splitlines(): line = line.strip() - for x in ("checkout of branch: ", "parent branch: "): + for x in ('checkout of branch: ', 'parent branch: '): if line.startswith(x): repo = line.split(x)[1] if cls._is_local_repository(repo): @@ -85,7 +88,7 @@ class Bazaar(VersionControl): @classmethod def get_revision(cls, location: str) -> str: revision = cls.run_command( - ["revno"], + ['revno'], show_stdout=False, stdout_only=True, cwd=location, @@ -93,7 +96,7 @@ class Bazaar(VersionControl): return revision.splitlines()[-1] @classmethod - def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + def is_commit_id_equal(cls, dest: str, name: str | None) -> bool: """Always assume the versions don't match""" return False diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/git.py b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/git.py index 8d1d499..96dca04 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/git.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/git.py @@ -1,23 +1,28 @@ +from __future__ import annotations + import logging import os.path import pathlib import re import urllib.parse import urllib.request -from typing import List, Optional, Tuple +from typing import List +from typing import Optional +from typing import Tuple -from pip._internal.exceptions import BadCommand, InstallationError -from pip._internal.utils.misc import HiddenText, display_path, hide_url +from pip._internal.exceptions import BadCommand +from pip._internal.exceptions import InstallationError +from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import HiddenText +from pip._internal.utils.misc import hide_url from pip._internal.utils.subprocess import make_command -from pip._internal.vcs.versioncontrol import ( - AuthInfo, - RemoteNotFoundError, - RemoteNotValidError, - RevOptions, - VersionControl, - find_path_to_project_root_from_repo_root, - vcs, -) +from pip._internal.vcs.versioncontrol import AuthInfo +from pip._internal.vcs.versioncontrol import find_path_to_project_root_from_repo_root +from pip._internal.vcs.versioncontrol import RemoteNotFoundError +from pip._internal.vcs.versioncontrol import RemoteNotValidError +from pip._internal.vcs.versioncontrol import RevOptions +from pip._internal.vcs.versioncontrol import vcs +from pip._internal.vcs.versioncontrol import VersionControl urlsplit = urllib.parse.urlsplit urlunsplit = urllib.parse.urlunsplit @@ -27,14 +32,14 @@ logger = logging.getLogger(__name__) GIT_VERSION_REGEX = re.compile( - r"^git version " # Prefix. - r"(\d+)" # Major. - r"\.(\d+)" # Dot, minor. - r"(?:\.(\d+))?" # Optional dot, patch. - r".*$" # Suffix, including any pre- and post-release segments we don't care about. + r'^git version ' # Prefix. + r'(\d+)' # Major. + r'\.(\d+)' # Dot, minor. + r'(?:\.(\d+))?' # Optional dot, patch. + r'.*$', # Suffix, including any pre- and post-release segments we don't care about. ) -HASH_REGEX = re.compile("^[a-fA-F0-9]{40}$") +HASH_REGEX = re.compile('^[a-fA-F0-9]{40}$') # SCP (Secure copy protocol) shorthand. e.g. 'git@example.com:foo/bar.git' SCP_REGEX = re.compile( @@ -57,23 +62,23 @@ def looks_like_hash(sha: str) -> bool: class Git(VersionControl): - name = "git" - dirname = ".git" - repo_name = "clone" + name = 'git' + dirname = '.git' + repo_name = 'clone' schemes = ( - "git+http", - "git+https", - "git+ssh", - "git+git", - "git+file", + 'git+http', + 'git+https', + 'git+ssh', + 'git+git', + 'git+file', ) # Prevent the user's environment variables from interfering with pip: # https://github.com/pypa/pip/issues/1130 - unset_environ = ("GIT_DIR", "GIT_WORK_TREE") - default_arg_rev = "HEAD" + unset_environ = ('GIT_DIR', 'GIT_WORK_TREE') + default_arg_rev = 'HEAD' @staticmethod - def get_base_rev_args(rev: str) -> List[str]: + def get_base_rev_args(rev: str) -> list[str]: return [rev] def is_immutable_rev_checkout(self, url: str, dest: str) -> bool: @@ -90,10 +95,10 @@ class Git(VersionControl): is_tag_or_branch = bool(self.get_revision_sha(dest, rev_options.rev)[0]) return not is_tag_or_branch - def get_git_version(self) -> Tuple[int, ...]: + def get_git_version(self) -> tuple[int, ...]: version = self.run_command( - ["version"], - command_desc="git version", + ['version'], + command_desc='git version', show_stdout=False, stdout_only=True, ) @@ -104,7 +109,7 @@ class Git(VersionControl): return tuple(int(c) for c in match.groups()) @classmethod - def get_current_branch(cls, location: str) -> Optional[str]: + def get_current_branch(cls, location: str) -> str | None: """ Return the current branch, or None if HEAD isn't at a branch (e.g. detached HEAD). @@ -113,7 +118,7 @@ class Git(VersionControl): # HEAD rather than a symbolic ref. In addition, the -q causes the # command to exit with status code 1 instead of 128 in this case # and to suppress the message to stderr. - args = ["symbolic-ref", "-q", "HEAD"] + args = ['symbolic-ref', '-q', 'HEAD'] output = cls.run_command( args, extra_ok_returncodes=(1,), @@ -123,13 +128,13 @@ class Git(VersionControl): ) ref = output.strip() - if ref.startswith("refs/heads/"): - return ref[len("refs/heads/") :] + if ref.startswith('refs/heads/'): + return ref[len('refs/heads/'):] return None @classmethod - def get_revision_sha(cls, dest: str, rev: str) -> Tuple[Optional[str], bool]: + def get_revision_sha(cls, dest: str, rev: str) -> tuple[str | None, bool]: """ Return (sha_or_none, is_branch), where sha_or_none is a commit hash if the revision names a remote branch or tag, otherwise None. @@ -140,31 +145,31 @@ class Git(VersionControl): """ # Pass rev to pre-filter the list. output = cls.run_command( - ["show-ref", rev], + ['show-ref', rev], cwd=dest, show_stdout=False, stdout_only=True, - on_returncode="ignore", + on_returncode='ignore', ) refs = {} # NOTE: We do not use splitlines here since that would split on other # unicode separators, which can be maliciously used to install a # different revision. - for line in output.strip().split("\n"): - line = line.rstrip("\r") + for line in output.strip().split('\n'): + line = line.rstrip('\r') if not line: continue try: - ref_sha, ref_name = line.split(" ", maxsplit=2) + ref_sha, ref_name = line.split(' ', maxsplit=2) except ValueError: # Include the offending line to simplify troubleshooting if # this error ever occurs. - raise ValueError(f"unexpected show-ref line: {line!r}") + raise ValueError(f'unexpected show-ref line: {line!r}') refs[ref_name] = ref_sha - branch_ref = f"refs/remotes/origin/{rev}" - tag_ref = f"refs/tags/{rev}" + branch_ref = f'refs/remotes/origin/{rev}' + tag_ref = f'refs/tags/{rev}' sha = refs.get(branch_ref) if sha is not None: @@ -183,7 +188,7 @@ class Git(VersionControl): assumed to be always available locally (which is a normal outcome of ``git clone`` and ``git fetch --tags``). """ - if rev.startswith("refs/"): + if rev.startswith('refs/'): # Always fetch remote refs. return True @@ -199,7 +204,7 @@ class Git(VersionControl): @classmethod def resolve_revision( - cls, dest: str, url: HiddenText, rev_options: RevOptions + cls, dest: str, url: HiddenText, rev_options: RevOptions, ) -> RevOptions: """ Resolve a revision to a new RevOptions object with the SHA1 of the @@ -234,17 +239,17 @@ class Git(VersionControl): # fetch the requested revision cls.run_command( - make_command("fetch", "-q", url, rev_options.to_args()), + make_command('fetch', '-q', url, rev_options.to_args()), cwd=dest, ) # Change the revision to the SHA of the ref we fetched - sha = cls.get_revision(dest, rev="FETCH_HEAD") + sha = cls.get_revision(dest, rev='FETCH_HEAD') rev_options = rev_options.make_new(sha) return rev_options @classmethod - def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + def is_commit_id_equal(cls, dest: str, name: str | None) -> bool: """ Return whether the current commit hash equals the given name. @@ -259,56 +264,56 @@ class Git(VersionControl): return cls.get_revision(dest) == name def fetch_new( - self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int, ) -> None: rev_display = rev_options.to_display() - logger.info("Cloning %s%s to %s", url, rev_display, display_path(dest)) + logger.info('Cloning %s%s to %s', url, rev_display, display_path(dest)) if verbosity <= 0: - flags: Tuple[str, ...] = ("--quiet",) + flags: tuple[str, ...] = ('--quiet',) elif verbosity == 1: flags = () else: - flags = ("--verbose", "--progress") + flags = ('--verbose', '--progress') if self.get_git_version() >= (2, 17): # Git added support for partial clone in 2.17 # https://git-scm.com/docs/partial-clone # Speeds up cloning by functioning without a complete copy of repository self.run_command( make_command( - "clone", - "--filter=blob:none", + 'clone', + '--filter=blob:none', *flags, url, dest, - ) + ), ) else: - self.run_command(make_command("clone", *flags, url, dest)) + self.run_command(make_command('clone', *flags, url, dest)) if rev_options.rev: # Then a specific revision was requested. rev_options = self.resolve_revision(dest, url, rev_options) - branch_name = getattr(rev_options, "branch_name", None) - logger.debug("Rev options %s, branch_name %s", rev_options, branch_name) + branch_name = getattr(rev_options, 'branch_name', None) + logger.debug('Rev options %s, branch_name %s', rev_options, branch_name) if branch_name is None: # Only do a checkout if the current commit id doesn't match # the requested revision. if not self.is_commit_id_equal(dest, rev_options.rev): cmd_args = make_command( - "checkout", - "-q", + 'checkout', + '-q', rev_options.to_args(), ) self.run_command(cmd_args, cwd=dest) elif self.get_current_branch(dest) != branch_name: # Then a specific branch was requested, and that branch # is not yet checked out. - track_branch = f"origin/{branch_name}" + track_branch = f'origin/{branch_name}' cmd_args = [ - "checkout", - "-b", + 'checkout', + '-b', branch_name, - "--track", + '--track', track_branch, ] self.run_command(cmd_args, cwd=dest) @@ -316,17 +321,17 @@ class Git(VersionControl): sha = self.get_revision(dest) rev_options = rev_options.make_new(sha) - logger.info("Resolved %s to commit %s", url, rev_options.rev) + logger.info('Resolved %s to commit %s', url, rev_options.rev) #: repo may contain submodules self.update_submodules(dest) def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: self.run_command( - make_command("config", "remote.origin.url", url), + make_command('config', 'remote.origin.url', url), cwd=dest, ) - cmd_args = make_command("checkout", "-q", rev_options.to_args()) + cmd_args = make_command('checkout', '-q', rev_options.to_args()) self.run_command(cmd_args, cwd=dest) self.update_submodules(dest) @@ -335,12 +340,12 @@ class Git(VersionControl): # First fetch changes from the default remote if self.get_git_version() >= (1, 9): # fetch tags in addition to everything else - self.run_command(["fetch", "-q", "--tags"], cwd=dest) + self.run_command(['fetch', '-q', '--tags'], cwd=dest) else: - self.run_command(["fetch", "-q"], cwd=dest) + self.run_command(['fetch', '-q'], cwd=dest) # Then reset to wanted revision (maybe even origin/master) rev_options = self.resolve_revision(dest, url, rev_options) - cmd_args = make_command("reset", "--hard", "-q", rev_options.to_args()) + cmd_args = make_command('reset', '--hard', '-q', rev_options.to_args()) self.run_command(cmd_args, cwd=dest) #: update submodules self.update_submodules(dest) @@ -356,7 +361,7 @@ class Git(VersionControl): # We need to pass 1 for extra_ok_returncodes since the command # exits with return code 1 if there are no matching lines. stdout = cls.run_command( - ["config", "--get-regexp", r"remote\..*\.url"], + ['config', '--get-regexp', r'remote\..*\.url'], extra_ok_returncodes=(1,), show_stdout=False, stdout_only=True, @@ -369,10 +374,10 @@ class Git(VersionControl): raise RemoteNotFoundError for remote in remotes: - if remote.startswith("remote.origin.url "): + if remote.startswith('remote.origin.url '): found_remote = remote break - url = found_remote.split(" ")[1] + url = found_remote.split(' ')[1] return cls._git_remote_to_pip_url(url.strip()) @staticmethod @@ -392,7 +397,7 @@ class Git(VersionControl): See the corresponding test test_git_remote_url_to_pip() for examples of sample inputs/outputs. """ - if re.match(r"\w+://", url): + if re.match(r'\w+://', url): # This is already valid. Pass it though as-is. return url if os.path.exists(url): @@ -402,7 +407,7 @@ class Git(VersionControl): scp_match = SCP_REGEX.match(url) if scp_match: # Add an ssh:// prefix and replace the ':' with a '/'. - return scp_match.expand(r"ssh://\1\2/\3") + return scp_match.expand(r'ssh://\1\2/\3') # Otherwise, bail out. raise RemoteNotValidError(url) @@ -413,7 +418,7 @@ class Git(VersionControl): """ try: cls.run_command( - ["rev-parse", "-q", "--verify", "sha^" + rev], + ['rev-parse', '-q', '--verify', 'sha^' + rev], cwd=location, log_failed_cmd=False, ) @@ -423,11 +428,11 @@ class Git(VersionControl): return True @classmethod - def get_revision(cls, location: str, rev: Optional[str] = None) -> str: + def get_revision(cls, location: str, rev: str | None = None) -> str: if rev is None: - rev = "HEAD" + rev = 'HEAD' current_rev = cls.run_command( - ["rev-parse", rev], + ['rev-parse', rev], show_stdout=False, stdout_only=True, cwd=location, @@ -435,25 +440,25 @@ class Git(VersionControl): return current_rev.strip() @classmethod - def get_subdirectory(cls, location: str) -> Optional[str]: + def get_subdirectory(cls, location: str) -> str | None: """ Return the path to Python project root, relative to the repo root. Return None if the project root is in the repo root. """ # find the repo root git_dir = cls.run_command( - ["rev-parse", "--git-dir"], + ['rev-parse', '--git-dir'], show_stdout=False, stdout_only=True, cwd=location, ).strip() if not os.path.isabs(git_dir): git_dir = os.path.join(location, git_dir) - repo_root = os.path.abspath(os.path.join(git_dir, "..")) + repo_root = os.path.abspath(os.path.join(git_dir, '..')) return find_path_to_project_root_from_repo_root(location, repo_root) @classmethod - def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]: """ Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. That's required because although they use SSH they sometimes don't @@ -463,21 +468,21 @@ class Git(VersionControl): # Works around an apparent Git bug # (see https://article.gmane.org/gmane.comp.version-control.git/146500) scheme, netloc, path, query, fragment = urlsplit(url) - if scheme.endswith("file"): - initial_slashes = path[: -len(path.lstrip("/"))] + if scheme.endswith('file'): + initial_slashes = path[: -len(path.lstrip('/'))] newpath = initial_slashes + urllib.request.url2pathname(path).replace( - "\\", "/" - ).lstrip("/") - after_plus = scheme.find("+") + 1 + '\\', '/', + ).lstrip('/') + after_plus = scheme.find('+') + 1 url = scheme[:after_plus] + urlunsplit( (scheme[after_plus:], netloc, newpath, query, fragment), ) - if "://" not in url: - assert "file:" not in url - url = url.replace("git+", "git+ssh://") + if '://' not in url: + assert 'file:' not in url + url = url.replace('git+', 'git+ssh://') url, rev, user_pass = super().get_url_rev_and_auth(url) - url = url.replace("ssh://", "") + url = url.replace('ssh://', '') else: url, rev, user_pass = super().get_url_rev_and_auth(url) @@ -485,37 +490,37 @@ class Git(VersionControl): @classmethod def update_submodules(cls, location: str) -> None: - if not os.path.exists(os.path.join(location, ".gitmodules")): + if not os.path.exists(os.path.join(location, '.gitmodules')): return cls.run_command( - ["submodule", "update", "--init", "--recursive", "-q"], + ['submodule', 'update', '--init', '--recursive', '-q'], cwd=location, ) @classmethod - def get_repository_root(cls, location: str) -> Optional[str]: + def get_repository_root(cls, location: str) -> str | None: loc = super().get_repository_root(location) if loc: return loc try: r = cls.run_command( - ["rev-parse", "--show-toplevel"], + ['rev-parse', '--show-toplevel'], cwd=location, show_stdout=False, stdout_only=True, - on_returncode="raise", + on_returncode='raise', log_failed_cmd=False, ) except BadCommand: logger.debug( - "could not determine if %s is under git control " - "because git is not available", + 'could not determine if %s is under git control ' + 'because git is not available', location, ) return None except InstallationError: return None - return os.path.normpath(r.rstrip("\r\n")) + return os.path.normpath(r.rstrip('\r\n')) @staticmethod def should_add_vcs_url_prefix(repo_url: str) -> bool: diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/mercurial.py b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/mercurial.py index 2a005e0..21bf756 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/mercurial.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/mercurial.py @@ -1,85 +1,89 @@ +from __future__ import annotations + import configparser import logging import os -from typing import List, Optional, Tuple +from typing import List +from typing import Optional +from typing import Tuple -from pip._internal.exceptions import BadCommand, InstallationError -from pip._internal.utils.misc import HiddenText, display_path +from pip._internal.exceptions import BadCommand +from pip._internal.exceptions import InstallationError +from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import HiddenText from pip._internal.utils.subprocess import make_command from pip._internal.utils.urls import path_to_url -from pip._internal.vcs.versioncontrol import ( - RevOptions, - VersionControl, - find_path_to_project_root_from_repo_root, - vcs, -) +from pip._internal.vcs.versioncontrol import find_path_to_project_root_from_repo_root +from pip._internal.vcs.versioncontrol import RevOptions +from pip._internal.vcs.versioncontrol import vcs +from pip._internal.vcs.versioncontrol import VersionControl logger = logging.getLogger(__name__) class Mercurial(VersionControl): - name = "hg" - dirname = ".hg" - repo_name = "clone" + name = 'hg' + dirname = '.hg' + repo_name = 'clone' schemes = ( - "hg+file", - "hg+http", - "hg+https", - "hg+ssh", - "hg+static-http", + 'hg+file', + 'hg+http', + 'hg+https', + 'hg+ssh', + 'hg+static-http', ) @staticmethod - def get_base_rev_args(rev: str) -> List[str]: + def get_base_rev_args(rev: str) -> list[str]: return [rev] def fetch_new( - self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int, ) -> None: rev_display = rev_options.to_display() logger.info( - "Cloning hg %s%s to %s", + 'Cloning hg %s%s to %s', url, rev_display, display_path(dest), ) if verbosity <= 0: - flags: Tuple[str, ...] = ("--quiet",) + flags: tuple[str, ...] = ('--quiet',) elif verbosity == 1: flags = () elif verbosity == 2: - flags = ("--verbose",) + flags = ('--verbose',) else: - flags = ("--verbose", "--debug") - self.run_command(make_command("clone", "--noupdate", *flags, url, dest)) + flags = ('--verbose', '--debug') + self.run_command(make_command('clone', '--noupdate', *flags, url, dest)) self.run_command( - make_command("update", *flags, rev_options.to_args()), + make_command('update', *flags, rev_options.to_args()), cwd=dest, ) def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: - repo_config = os.path.join(dest, self.dirname, "hgrc") + repo_config = os.path.join(dest, self.dirname, 'hgrc') config = configparser.RawConfigParser() try: config.read(repo_config) - config.set("paths", "default", url.secret) - with open(repo_config, "w") as config_file: + config.set('paths', 'default', url.secret) + with open(repo_config, 'w') as config_file: config.write(config_file) except (OSError, configparser.NoSectionError) as exc: - logger.warning("Could not switch Mercurial repository to %s: %s", url, exc) + logger.warning('Could not switch Mercurial repository to %s: %s', url, exc) else: - cmd_args = make_command("update", "-q", rev_options.to_args()) + cmd_args = make_command('update', '-q', rev_options.to_args()) self.run_command(cmd_args, cwd=dest) def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: - self.run_command(["pull", "-q"], cwd=dest) - cmd_args = make_command("update", "-q", rev_options.to_args()) + self.run_command(['pull', '-q'], cwd=dest) + cmd_args = make_command('update', '-q', rev_options.to_args()) self.run_command(cmd_args, cwd=dest) @classmethod def get_remote_url(cls, location: str) -> str: url = cls.run_command( - ["showconfig", "paths.default"], + ['showconfig', 'paths.default'], show_stdout=False, stdout_only=True, cwd=location, @@ -94,7 +98,7 @@ class Mercurial(VersionControl): Return the repository-local changeset revision number, as an integer. """ current_revision = cls.run_command( - ["parents", "--template={rev}"], + ['parents', '--template={rev}'], show_stdout=False, stdout_only=True, cwd=location, @@ -108,7 +112,7 @@ class Mercurial(VersionControl): hexadecimal string """ current_rev_hash = cls.run_command( - ["parents", "--template={node}"], + ['parents', '--template={node}'], show_stdout=False, stdout_only=True, cwd=location, @@ -116,48 +120,48 @@ class Mercurial(VersionControl): return current_rev_hash @classmethod - def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + def is_commit_id_equal(cls, dest: str, name: str | None) -> bool: """Always assume the versions don't match""" return False @classmethod - def get_subdirectory(cls, location: str) -> Optional[str]: + def get_subdirectory(cls, location: str) -> str | None: """ Return the path to Python project root, relative to the repo root. Return None if the project root is in the repo root. """ # find the repo root repo_root = cls.run_command( - ["root"], show_stdout=False, stdout_only=True, cwd=location + ['root'], show_stdout=False, stdout_only=True, cwd=location, ).strip() if not os.path.isabs(repo_root): repo_root = os.path.abspath(os.path.join(location, repo_root)) return find_path_to_project_root_from_repo_root(location, repo_root) @classmethod - def get_repository_root(cls, location: str) -> Optional[str]: + def get_repository_root(cls, location: str) -> str | None: loc = super().get_repository_root(location) if loc: return loc try: r = cls.run_command( - ["root"], + ['root'], cwd=location, show_stdout=False, stdout_only=True, - on_returncode="raise", + on_returncode='raise', log_failed_cmd=False, ) except BadCommand: logger.debug( - "could not determine if %s is under hg control " - "because hg is not available", + 'could not determine if %s is under hg control ' + 'because hg is not available', location, ) return None except InstallationError: return None - return os.path.normpath(r.rstrip("\r\n")) + return os.path.normpath(r.rstrip('\r\n')) vcs.register(Mercurial) diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/subversion.py b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/subversion.py index 89c8754..2dbef96 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/subversion.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/subversion.py @@ -1,45 +1,46 @@ +from __future__ import annotations + import logging import os import re -from typing import List, Optional, Tuple +from typing import List +from typing import Optional +from typing import Tuple -from pip._internal.utils.misc import ( - HiddenText, - display_path, - is_console_interactive, - is_installable_dir, - split_auth_from_netloc, -) -from pip._internal.utils.subprocess import CommandArgs, make_command -from pip._internal.vcs.versioncontrol import ( - AuthInfo, - RemoteNotFoundError, - RevOptions, - VersionControl, - vcs, -) +from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import HiddenText +from pip._internal.utils.misc import is_console_interactive +from pip._internal.utils.misc import is_installable_dir +from pip._internal.utils.misc import split_auth_from_netloc +from pip._internal.utils.subprocess import CommandArgs +from pip._internal.utils.subprocess import make_command +from pip._internal.vcs.versioncontrol import AuthInfo +from pip._internal.vcs.versioncontrol import RemoteNotFoundError +from pip._internal.vcs.versioncontrol import RevOptions +from pip._internal.vcs.versioncontrol import vcs +from pip._internal.vcs.versioncontrol import VersionControl logger = logging.getLogger(__name__) _svn_xml_url_re = re.compile('url="([^"]+)"') _svn_rev_re = re.compile(r'committed-rev="(\d+)"') _svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') -_svn_info_xml_url_re = re.compile(r"(.*)") +_svn_info_xml_url_re = re.compile(r'(.*)') class Subversion(VersionControl): - name = "svn" - dirname = ".svn" - repo_name = "checkout" - schemes = ("svn+ssh", "svn+http", "svn+https", "svn+svn", "svn+file") + name = 'svn' + dirname = '.svn' + repo_name = 'checkout' + schemes = ('svn+ssh', 'svn+http', 'svn+https', 'svn+svn', 'svn+file') @classmethod def should_add_vcs_url_prefix(cls, remote_url: str) -> bool: return True @staticmethod - def get_base_rev_args(rev: str) -> List[str]: - return ["-r", rev] + def get_base_rev_args(rev: str) -> list[str]: + return ['-r', rev] @classmethod def get_revision(cls, location: str) -> str: @@ -54,7 +55,7 @@ class Subversion(VersionControl): dirs[:] = [] continue # no sense walking uncontrolled subdirs dirs.remove(cls.dirname) - entries_fn = os.path.join(base, cls.dirname, "entries") + entries_fn = os.path.join(base, cls.dirname, 'entries') if not os.path.exists(entries_fn): # FIXME: should we warn? continue @@ -63,7 +64,7 @@ class Subversion(VersionControl): if base == location: assert dirurl is not None - base = dirurl + "/" # save the root url + base = dirurl + '/' # save the root url elif not dirurl or not dirurl.startswith(base): dirs[:] = [] continue # not part of the same svn tree, skip it @@ -72,13 +73,13 @@ class Subversion(VersionControl): @classmethod def get_netloc_and_auth( - cls, netloc: str, scheme: str - ) -> Tuple[str, Tuple[Optional[str], Optional[str]]]: + cls, netloc: str, scheme: str, + ) -> tuple[str, tuple[str | None, str | None]]: """ This override allows the auth information to be passed to svn via the --username and --password options instead of via the URL. """ - if scheme == "ssh": + if scheme == 'ssh': # The --username and --password options can't be used for # svn+ssh URLs, so keep the auth information in the URL. return super().get_netloc_and_auth(netloc, scheme) @@ -86,22 +87,22 @@ class Subversion(VersionControl): return split_auth_from_netloc(netloc) @classmethod - def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]: # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it url, rev, user_pass = super().get_url_rev_and_auth(url) - if url.startswith("ssh://"): - url = "svn+" + url + if url.startswith('ssh://'): + url = 'svn+' + url return url, rev, user_pass @staticmethod def make_rev_args( - username: Optional[str], password: Optional[HiddenText] + username: str | None, password: HiddenText | None, ) -> CommandArgs: extra_args: CommandArgs = [] if username: - extra_args += ["--username", username] + extra_args += ['--username', username] if password: - extra_args += ["--password", password] + extra_args += ['--password', password] return extra_args @@ -117,8 +118,8 @@ class Subversion(VersionControl): # We've traversed up to the root of the filesystem without # finding a Python project. logger.warning( - "Could not find Python project for directory %s (tried all " - "parent directories)", + 'Could not find Python project for directory %s (tried all ' + 'parent directories)', orig_location, ) raise RemoteNotFoundError @@ -130,26 +131,26 @@ class Subversion(VersionControl): return url @classmethod - def _get_svn_url_rev(cls, location: str) -> Tuple[Optional[str], int]: + def _get_svn_url_rev(cls, location: str) -> tuple[str | None, int]: from pip._internal.exceptions import InstallationError - entries_path = os.path.join(location, cls.dirname, "entries") + entries_path = os.path.join(location, cls.dirname, 'entries') if os.path.exists(entries_path): with open(entries_path) as f: data = f.read() else: # subversion >= 1.7 does not have the 'entries' file - data = "" + data = '' url = None - if data.startswith("8") or data.startswith("9") or data.startswith("10"): - entries = list(map(str.splitlines, data.split("\n\x0c\n"))) + if data.startswith('8') or data.startswith('9') or data.startswith('10'): + entries = list(map(str.splitlines, data.split('\n\x0c\n'))) del entries[0][0] # get rid of the '8' url = entries[0][3] revs = [int(d[9]) for d in entries if len(d) > 9 and d[9]] + [0] - elif data.startswith(" bool: + def is_commit_id_equal(cls, dest: str, name: str | None) -> bool: """Always assume the versions don't match""" return False @@ -194,11 +195,11 @@ class Subversion(VersionControl): # Special value definitions: # None: Not evaluated yet. # Empty tuple: Could not parse version. - self._vcs_version: Optional[Tuple[int, ...]] = None + self._vcs_version: tuple[int, ...] | None = None super().__init__() - def call_vcs_version(self) -> Tuple[int, ...]: + def call_vcs_version(self) -> tuple[int, ...]: """Query the version of the currently installed Subversion client. :return: A tuple containing the parts of the version information or @@ -212,13 +213,13 @@ class Subversion(VersionControl): # compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu # svn, version 1.12.0-SlikSvn (SlikSvn/1.12.0) # compiled May 28 2019, 13:44:56 on x86_64-microsoft-windows6.2 - version_prefix = "svn, version " - version = self.run_command(["--version"], show_stdout=False, stdout_only=True) + version_prefix = 'svn, version ' + version = self.run_command(['--version'], show_stdout=False, stdout_only=True) if not version.startswith(version_prefix): return () - version = version[len(version_prefix) :].split()[0] - version_list = version.partition("-")[0].split(".") + version = version[len(version_prefix):].split()[0] + version_list = version.partition('-')[0].split('.') try: parsed_version = tuple(map(int, version_list)) except ValueError: @@ -226,7 +227,7 @@ class Subversion(VersionControl): return parsed_version - def get_vcs_version(self) -> Tuple[int, ...]: + def get_vcs_version(self) -> tuple[int, ...]: """Return the version of the currently installed Subversion client. If the version of the Subversion client has already been queried, @@ -261,7 +262,7 @@ class Subversion(VersionControl): if not self.use_interactive: # --non-interactive switch is available since Subversion 0.14.4. # Subversion < 1.8 runs in interactive mode by default. - return ["--non-interactive"] + return ['--non-interactive'] svn_version = self.get_vcs_version() # By default, Subversion >= 1.8 runs in non-interactive mode if @@ -273,26 +274,26 @@ class Subversion(VersionControl): # SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip # can't safely add the option if the SVN version is < 1.8 (or unknown). if svn_version >= (1, 8): - return ["--force-interactive"] + return ['--force-interactive'] return [] def fetch_new( - self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int, ) -> None: rev_display = rev_options.to_display() logger.info( - "Checking out %s%s to %s", + 'Checking out %s%s to %s', url, rev_display, display_path(dest), ) if verbosity <= 0: - flag = "--quiet" + flag = '--quiet' else: - flag = "" + flag = '' cmd_args = make_command( - "checkout", + 'checkout', flag, self.get_remote_call_options(), rev_options.to_args(), @@ -303,7 +304,7 @@ class Subversion(VersionControl): def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: cmd_args = make_command( - "switch", + 'switch', self.get_remote_call_options(), rev_options.to_args(), url, @@ -313,7 +314,7 @@ class Subversion(VersionControl): def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: cmd_args = make_command( - "update", + 'update', self.get_remote_call_options(), rev_options.to_args(), dest, diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/versioncontrol.py b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/versioncontrol.py index 02bbf68..00020d8 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/vcs/versioncontrol.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/vcs/versioncontrol.py @@ -1,42 +1,38 @@ """Handles all VCS (version control) support""" +from __future__ import annotations import logging import os import shutil import sys import urllib.parse -from typing import ( - TYPE_CHECKING, - Any, - Dict, - Iterable, - Iterator, - List, - Mapping, - Optional, - Tuple, - Type, - Union, -) +from typing import Any +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Mapping +from typing import Optional +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union from pip._internal.cli.spinners import SpinnerInterface -from pip._internal.exceptions import BadCommand, InstallationError -from pip._internal.utils.misc import ( - HiddenText, - ask_path_exists, - backup_dir, - display_path, - hide_url, - hide_value, - is_installable_dir, - rmtree, -) -from pip._internal.utils.subprocess import ( - CommandArgs, - call_subprocess, - format_command_args, - make_command, -) +from pip._internal.exceptions import BadCommand +from pip._internal.exceptions import InstallationError +from pip._internal.utils.misc import ask_path_exists +from pip._internal.utils.misc import backup_dir +from pip._internal.utils.misc import display_path +from pip._internal.utils.misc import HiddenText +from pip._internal.utils.misc import hide_url +from pip._internal.utils.misc import hide_value +from pip._internal.utils.misc import is_installable_dir +from pip._internal.utils.misc import rmtree +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.subprocess import CommandArgs +from pip._internal.utils.subprocess import format_command_args +from pip._internal.utils.subprocess import make_command from pip._internal.utils.urls import get_url_scheme if TYPE_CHECKING: @@ -46,7 +42,7 @@ if TYPE_CHECKING: from typing import Literal -__all__ = ["vcs"] +__all__ = ['vcs'] logger = logging.getLogger(__name__) @@ -61,11 +57,11 @@ def is_url(name: str) -> bool: scheme = get_url_scheme(name) if scheme is None: return False - return scheme in ["http", "https", "file", "ftp"] + vcs.all_schemes + return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes def make_vcs_requirement_url( - repo_url: str, rev: str, project_name: str, subdir: Optional[str] = None + repo_url: str, rev: str, project_name: str, subdir: str | None = None, ) -> str: """ Return the URL for a VCS requirement. @@ -74,17 +70,17 @@ def make_vcs_requirement_url( repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). project_name: the (unescaped) project name. """ - egg_project_name = project_name.replace("-", "_") - req = f"{repo_url}@{rev}#egg={egg_project_name}" + egg_project_name = project_name.replace('-', '_') + req = f'{repo_url}@{rev}#egg={egg_project_name}' if subdir: - req += f"&subdirectory={subdir}" + req += f'&subdirectory={subdir}' return req def find_path_to_project_root_from_repo_root( - location: str, repo_root: str -) -> Optional[str]: + location: str, repo_root: str, +) -> str | None: """ Find the the Python project's root by searching up the filesystem from `location`. Return the path to project root relative to `repo_root`. @@ -99,8 +95,8 @@ def find_path_to_project_root_from_repo_root( # We've traversed up to the root of the filesystem without # finding a Python project. logger.warning( - "Could not find a Python project for directory %s (tried all " - "parent directories)", + 'Could not find a Python project for directory %s (tried all ' + 'parent directories)', orig_location, ) return None @@ -132,9 +128,9 @@ class RevOptions: def __init__( self, - vc_class: Type["VersionControl"], - rev: Optional[str] = None, - extra_args: Optional[CommandArgs] = None, + vc_class: type[VersionControl], + rev: str | None = None, + extra_args: CommandArgs | None = None, ) -> None: """ Args: @@ -148,13 +144,13 @@ class RevOptions: self.extra_args = extra_args self.rev = rev self.vc_class = vc_class - self.branch_name: Optional[str] = None + self.branch_name: str | None = None def __repr__(self) -> str: - return f"" + return f'' @property - def arg_rev(self) -> Optional[str]: + def arg_rev(self) -> str | None: if self.rev is None: return self.vc_class.default_arg_rev @@ -174,11 +170,11 @@ class RevOptions: def to_display(self) -> str: if not self.rev: - return "" + return '' - return f" (to revision {self.rev})" + return f' (to revision {self.rev})' - def make_new(self, rev: str) -> "RevOptions": + def make_new(self, rev: str) -> RevOptions: """ Make a copy of the current instance, but with a new rev. @@ -189,8 +185,8 @@ class RevOptions: class VcsSupport: - _registry: Dict[str, "VersionControl"] = {} - schemes = ["ssh", "git", "hg", "bzr", "sftp", "svn"] + _registry: dict[str, VersionControl] = {} + schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] def __init__(self) -> None: # Register more schemes with urlparse for various version control @@ -202,33 +198,33 @@ class VcsSupport: return self._registry.__iter__() @property - def backends(self) -> List["VersionControl"]: + def backends(self) -> list[VersionControl]: return list(self._registry.values()) @property - def dirnames(self) -> List[str]: + def dirnames(self) -> list[str]: return [backend.dirname for backend in self.backends] @property - def all_schemes(self) -> List[str]: - schemes: List[str] = [] + def all_schemes(self) -> list[str]: + schemes: list[str] = [] for backend in self.backends: schemes.extend(backend.schemes) return schemes - def register(self, cls: Type["VersionControl"]) -> None: - if not hasattr(cls, "name"): - logger.warning("Cannot register VCS %s", cls.__name__) + def register(self, cls: type[VersionControl]) -> None: + if not hasattr(cls, 'name'): + logger.warning('Cannot register VCS %s', cls.__name__) return if cls.name not in self._registry: self._registry[cls.name] = cls() - logger.debug("Registered VCS backend: %s", cls.name) + logger.debug('Registered VCS backend: %s', cls.name) def unregister(self, name: str) -> None: if name in self._registry: del self._registry[name] - def get_backend_for_dir(self, location: str) -> Optional["VersionControl"]: + def get_backend_for_dir(self, location: str) -> VersionControl | None: """ Return a VersionControl object if a repository of that type is found at the given directory. @@ -238,7 +234,7 @@ class VcsSupport: repo_path = vcs_backend.get_repository_root(location) if not repo_path: continue - logger.debug("Determine that %s uses VCS: %s", location, vcs_backend.name) + logger.debug('Determine that %s uses VCS: %s', location, vcs_backend.name) vcs_backends[repo_path] = vcs_backend if not vcs_backends: @@ -251,7 +247,7 @@ class VcsSupport: inner_most_repo_path = max(vcs_backends, key=len) return vcs_backends[inner_most_repo_path] - def get_backend_for_scheme(self, scheme: str) -> Optional["VersionControl"]: + def get_backend_for_scheme(self, scheme: str) -> VersionControl | None: """ Return a VersionControl object or None. """ @@ -260,7 +256,7 @@ class VcsSupport: return vcs_backend return None - def get_backend(self, name: str) -> Optional["VersionControl"]: + def get_backend(self, name: str) -> VersionControl | None: """ Return a VersionControl object or None. """ @@ -272,14 +268,14 @@ vcs = VcsSupport() class VersionControl: - name = "" - dirname = "" - repo_name = "" + name = '' + dirname = '' + repo_name = '' # List of supported schemes for this Version Control - schemes: Tuple[str, ...] = () + schemes: tuple[str, ...] = () # Iterable of environment variable names to pass to call_subprocess(). - unset_environ: Tuple[str, ...] = () - default_arg_rev: Optional[str] = None + unset_environ: tuple[str, ...] = () + default_arg_rev: str | None = None @classmethod def should_add_vcs_url_prefix(cls, remote_url: str) -> bool: @@ -287,10 +283,10 @@ class VersionControl: Return whether the vcs prefix (e.g. "git+") should be added to a repository's remote url when used in a requirement. """ - return not remote_url.lower().startswith(f"{cls.name}:") + return not remote_url.lower().startswith(f'{cls.name}:') @classmethod - def get_subdirectory(cls, location: str) -> Optional[str]: + def get_subdirectory(cls, location: str) -> str | None: """ Return the path to Python project root, relative to the repo root. Return None if the project root is in the repo root. @@ -320,7 +316,7 @@ class VersionControl: repo_url = cls.get_remote_url(repo_dir) if cls.should_add_vcs_url_prefix(repo_url): - repo_url = f"{cls.name}+{repo_url}" + repo_url = f'{cls.name}+{repo_url}' revision = cls.get_requirement_revision(repo_dir) subdir = cls.get_subdirectory(repo_dir) @@ -329,7 +325,7 @@ class VersionControl: return req @staticmethod - def get_base_rev_args(rev: str) -> List[str]: + def get_base_rev_args(rev: str) -> list[str]: """ Return the base revision arguments for a vcs command. @@ -353,7 +349,7 @@ class VersionControl: @classmethod def make_rev_options( - cls, rev: Optional[str] = None, extra_args: Optional[CommandArgs] = None + cls, rev: str | None = None, extra_args: CommandArgs | None = None, ) -> RevOptions: """ Return a RevOptions object. @@ -375,8 +371,8 @@ class VersionControl: @classmethod def get_netloc_and_auth( - cls, netloc: str, scheme: str - ) -> Tuple[str, Tuple[Optional[str], Optional[str]]]: + cls, netloc: str, scheme: str, + ) -> tuple[str, tuple[str | None, str | None]]: """ Parse the repository URL's netloc, and return the new netloc to use along with auth information. @@ -395,7 +391,7 @@ class VersionControl: return netloc, (None, None) @classmethod - def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]: """ Parse the repository URL to use, and return the URL, revision, and auth info to use. @@ -403,44 +399,44 @@ class VersionControl: Returns: (url, rev, (username, password)). """ scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) - if "+" not in scheme: + if '+' not in scheme: raise ValueError( - "Sorry, {!r} is a malformed VCS url. " - "The format is +://, " - "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url) + 'Sorry, {!r} is a malformed VCS url. ' + 'The format is +://, ' + 'e.g. svn+http://myrepo/svn/MyApp#egg=MyApp'.format(url), ) # Remove the vcs prefix. - scheme = scheme.split("+", 1)[1] + scheme = scheme.split('+', 1)[1] netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme) rev = None - if "@" in path: - path, rev = path.rsplit("@", 1) + if '@' in path: + path, rev = path.rsplit('@', 1) if not rev: raise InstallationError( - "The URL {!r} has an empty revision (after @) " - "which is not supported. Include a revision after @ " - "or remove @ from the URL.".format(url) + 'The URL {!r} has an empty revision (after @) ' + 'which is not supported. Include a revision after @ ' + 'or remove @ from the URL.'.format(url), ) - url = urllib.parse.urlunsplit((scheme, netloc, path, query, "")) + url = urllib.parse.urlunsplit((scheme, netloc, path, query, '')) return url, rev, user_pass @staticmethod def make_rev_args( - username: Optional[str], password: Optional[HiddenText] + username: str | None, password: HiddenText | None, ) -> CommandArgs: """ Return the RevOptions "extra arguments" to use in obtain(). """ return [] - def get_url_rev_options(self, url: HiddenText) -> Tuple[HiddenText, RevOptions]: + def get_url_rev_options(self, url: HiddenText) -> tuple[HiddenText, RevOptions]: """ Return the URL and RevOptions object to use in obtain(), as a tuple (url, rev_options). """ secret_url, rev, user_pass = self.get_url_rev_and_auth(url.secret) username, secret_password = user_pass - password: Optional[HiddenText] = None + password: HiddenText | None = None if secret_password is not None: password = hide_value(secret_password) extra_args = self.make_rev_args(username, password) @@ -454,7 +450,7 @@ class VersionControl: Normalize a URL for comparison by unquoting it and removing any trailing slash. """ - return urllib.parse.unquote(url).rstrip("/") + return urllib.parse.unquote(url).rstrip('/') @classmethod def compare_urls(cls, url1: str, url2: str) -> bool: @@ -464,7 +460,7 @@ class VersionControl: return cls.normalize_url(url1) == cls.normalize_url(url2) def fetch_new( - self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int, ) -> None: """ Fetch a revision from a repository, in the case that this is the @@ -496,7 +492,7 @@ class VersionControl: raise NotImplementedError @classmethod - def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + def is_commit_id_equal(cls, dest: str, name: str | None) -> bool: """ Return whether the id of the current commit equals the given name. @@ -526,68 +522,68 @@ class VersionControl: existing_url = self.get_remote_url(dest) if self.compare_urls(existing_url, url.secret): logger.debug( - "%s in %s exists, and has correct URL (%s)", + '%s in %s exists, and has correct URL (%s)', self.repo_name.title(), display_path(dest), url, ) if not self.is_commit_id_equal(dest, rev_options.rev): logger.info( - "Updating %s %s%s", + 'Updating %s %s%s', display_path(dest), self.repo_name, rev_display, ) self.update(dest, url, rev_options) else: - logger.info("Skipping because already up-to-date.") + logger.info('Skipping because already up-to-date.') return logger.warning( - "%s %s in %s exists with URL %s", + '%s %s in %s exists with URL %s', self.name, self.repo_name, display_path(dest), existing_url, ) - prompt = ("(s)witch, (i)gnore, (w)ipe, (b)ackup ", ("s", "i", "w", "b")) + prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', ('s', 'i', 'w', 'b')) else: logger.warning( - "Directory %s already exists, and is not a %s %s.", + 'Directory %s already exists, and is not a %s %s.', dest, self.name, self.repo_name, ) # https://github.com/python/mypy/issues/1174 - prompt = ("(i)gnore, (w)ipe, (b)ackup ", ("i", "w", "b")) # type: ignore + prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) # type: ignore logger.warning( - "The plan is to install the %s repository %s", + 'The plan is to install the %s repository %s', self.name, url, ) - response = ask_path_exists("What to do? {}".format(prompt[0]), prompt[1]) + response = ask_path_exists(f'What to do? {prompt[0]}', prompt[1]) - if response == "a": + if response == 'a': sys.exit(-1) - if response == "w": - logger.warning("Deleting %s", display_path(dest)) + if response == 'w': + logger.warning('Deleting %s', display_path(dest)) rmtree(dest) self.fetch_new(dest, url, rev_options, verbosity=verbosity) return - if response == "b": + if response == 'b': dest_dir = backup_dir(dest) - logger.warning("Backing up %s to %s", display_path(dest), dest_dir) + logger.warning('Backing up %s to %s', display_path(dest), dest_dir) shutil.move(dest, dest_dir) self.fetch_new(dest, url, rev_options, verbosity=verbosity) return # Do nothing if the response is "i". - if response == "s": + if response == 's': logger.info( - "Switching %s %s to %s%s", + 'Switching %s %s to %s%s', self.repo_name, display_path(dest), url, @@ -627,14 +623,14 @@ class VersionControl: @classmethod def run_command( cls, - cmd: Union[List[str], CommandArgs], + cmd: list[str] | CommandArgs, show_stdout: bool = True, - cwd: Optional[str] = None, - on_returncode: 'Literal["raise", "warn", "ignore"]' = "raise", - extra_ok_returncodes: Optional[Iterable[int]] = None, - command_desc: Optional[str] = None, - extra_environ: Optional[Mapping[str, Any]] = None, - spinner: Optional[SpinnerInterface] = None, + cwd: str | None = None, + on_returncode: Literal["raise", "warn", "ignore"] = 'raise', + extra_ok_returncodes: Iterable[int] | None = None, + command_desc: str | None = None, + extra_environ: Mapping[str, Any] | None = None, + spinner: SpinnerInterface | None = None, log_failed_cmd: bool = True, stdout_only: bool = False, ) -> str: @@ -664,8 +660,8 @@ class VersionControl: # errno.ENOENT = no such file or directory # In other words, the VCS executable isn't available raise BadCommand( - f"Cannot find command {cls.name!r} - do you have " - f"{cls.name!r} installed and in your PATH?" + f'Cannot find command {cls.name!r} - do you have ' + f'{cls.name!r} installed and in your PATH?', ) except PermissionError: # errno.EACCES = Permission denied @@ -673,11 +669,11 @@ class VersionControl: # only for another user. So, the current user don't have # permission to call the other user command. raise BadCommand( - f"No permission to execute {cls.name!r} - install it " - f"locally, globally (ask admin), or check your PATH. " - f"See possible solutions at " - f"https://pip.pypa.io/en/latest/reference/pip_freeze/" - f"#fixing-permission-denied." + f'No permission to execute {cls.name!r} - install it ' + f'locally, globally (ask admin), or check your PATH. ' + f'See possible solutions at ' + f'https://pip.pypa.io/en/latest/reference/pip_freeze/' + f'#fixing-permission-denied.', ) @classmethod @@ -685,11 +681,11 @@ class VersionControl: """ Return whether a directory path is a repository directory. """ - logger.debug("Checking in %s for %s (%s)...", path, cls.dirname, cls.name) + logger.debug('Checking in %s for %s (%s)...', path, cls.dirname, cls.name) return os.path.exists(os.path.join(path, cls.dirname)) @classmethod - def get_repository_root(cls, location: str) -> Optional[str]: + def get_repository_root(cls, location: str) -> str | None: """ Return the "root" (top-level) directory controlled by the vcs, or `None` if the directory is not in any. diff --git a/.venv/lib/python3.10/site-packages/pip/_internal/wheel_builder.py b/.venv/lib/python3.10/site-packages/pip/_internal/wheel_builder.py index d066344..e066f59 100644 --- a/.venv/lib/python3.10/site-packages/pip/_internal/wheel_builder.py +++ b/.venv/lib/python3.10/site-packages/pip/_internal/wheel_builder.py @@ -1,18 +1,23 @@ """Orchestrator for building wheels from InstallRequirements. """ +from __future__ import annotations import logging import os.path import re import shutil -from typing import Any, Callable, Iterable, List, Optional, Tuple - -from pip._vendor.packaging.utils import canonicalize_name, canonicalize_version -from pip._vendor.packaging.version import InvalidVersion, Version +from typing import Any +from typing import Callable +from typing import Iterable +from typing import List +from typing import Optional +from typing import Tuple from pip._internal.cache import WheelCache -from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel -from pip._internal.metadata import FilesystemWheel, get_wheel_distribution +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.exceptions import UnsupportedWheel +from pip._internal.metadata import FilesystemWheel +from pip._internal.metadata import get_wheel_distribution from pip._internal.models.link import Link from pip._internal.models.wheel import Wheel from pip._internal.operations.build.wheel import build_wheel_pep517 @@ -20,16 +25,22 @@ from pip._internal.operations.build.wheel_editable import build_wheel_editable from pip._internal.operations.build.wheel_legacy import build_wheel_legacy from pip._internal.req.req_install import InstallRequirement from pip._internal.utils.logging import indent_log -from pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.misc import hash_file +from pip._internal.utils.misc import is_wheel_installed from pip._internal.utils.setuptools_build import make_setuptools_clean_args from pip._internal.utils.subprocess import call_subprocess from pip._internal.utils.temp_dir import TempDirectory from pip._internal.utils.urls import path_to_url from pip._internal.vcs import vcs +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.utils import canonicalize_version +from pip._vendor.packaging.version import InvalidVersion +from pip._vendor.packaging.version import Version logger = logging.getLogger(__name__) -_egg_info_re = re.compile(r"([a-z0-9_.]+)-([a-z0-9_.!+-]+)", re.IGNORECASE) +_egg_info_re = re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.IGNORECASE) BinaryAllowedPredicate = Callable[[InstallRequirement], bool] BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]] @@ -55,7 +66,7 @@ def _should_build( if req.is_wheel: if need_wheel: logger.info( - "Skipping %s, due to already being wheel.", + 'Skipping %s, due to already being wheel.', req.name, ) return False @@ -79,7 +90,7 @@ def _should_build( if not check_binary_allowed(req): logger.info( - "Skipping wheel build for %s, due to binaries being disabled for it.", + 'Skipping wheel build for %s, due to binaries being disabled for it.', req.name, ) return False @@ -107,13 +118,13 @@ def should_build_for_install_command( check_binary_allowed: BinaryAllowedPredicate, ) -> bool: return _should_build( - req, need_wheel=False, check_binary_allowed=check_binary_allowed + req, need_wheel=False, check_binary_allowed=check_binary_allowed, ) def _should_cache( req: InstallRequirement, -) -> Optional[bool]: +) -> bool | None: """ Return whether a built InstallRequirement can be stored in the persistent wheel cache, assuming the wheel cache is available, and _should_build() @@ -164,32 +175,32 @@ def _always_true(_: Any) -> bool: def _verify_one(req: InstallRequirement, wheel_path: str) -> None: - canonical_name = canonicalize_name(req.name or "") + canonical_name = canonicalize_name(req.name or '') w = Wheel(os.path.basename(wheel_path)) if canonicalize_name(w.name) != canonical_name: raise InvalidWheelFilename( - "Wheel has unexpected file name: expected {!r}, " - "got {!r}".format(canonical_name, w.name), + 'Wheel has unexpected file name: expected {!r}, ' + 'got {!r}'.format(canonical_name, w.name), ) dist = get_wheel_distribution(FilesystemWheel(wheel_path), canonical_name) dist_verstr = str(dist.version) if canonicalize_version(dist_verstr) != canonicalize_version(w.version): raise InvalidWheelFilename( - "Wheel has unexpected file name: expected {!r}, " - "got {!r}".format(dist_verstr, w.version), + 'Wheel has unexpected file name: expected {!r}, ' + 'got {!r}'.format(dist_verstr, w.version), ) metadata_version_value = dist.metadata_version if metadata_version_value is None: - raise UnsupportedWheel("Missing Metadata-Version") + raise UnsupportedWheel('Missing Metadata-Version') try: metadata_version = Version(metadata_version_value) except InvalidVersion: - msg = f"Invalid Metadata-Version: {metadata_version_value}" + msg = f'Invalid Metadata-Version: {metadata_version_value}' raise UnsupportedWheel(msg) - if metadata_version >= Version("1.2") and not isinstance(dist.version, Version): + if metadata_version >= Version('1.2') and not isinstance(dist.version, Version): raise UnsupportedWheel( - "Metadata 1.2 mandates PEP 440 version, " - "but {!r} is not".format(dist_verstr) + 'Metadata 1.2 mandates PEP 440 version, ' + 'but {!r} is not'.format(dist_verstr), ) @@ -197,20 +208,20 @@ def _build_one( req: InstallRequirement, output_dir: str, verify: bool, - build_options: List[str], - global_options: List[str], + build_options: list[str], + global_options: list[str], editable: bool, -) -> Optional[str]: +) -> str | None: """Build one wheel. :return: The filename of the built wheel, or None if the build failed. """ - artifact = "editable" if editable else "wheel" + artifact = 'editable' if editable else 'wheel' try: ensure_dir(output_dir) except OSError as e: logger.warning( - "Building %s for %s failed: %s", + 'Building %s for %s failed: %s', artifact, req.name, e, @@ -220,13 +231,13 @@ def _build_one( # Install build deps into temporary directory (PEP 518) with req.build_env: wheel_path = _build_one_inside_env( - req, output_dir, build_options, global_options, editable + req, output_dir, build_options, global_options, editable, ) if wheel_path and verify: try: _verify_one(req, wheel_path) except (InvalidWheelFilename, UnsupportedWheel) as e: - logger.warning("Built %s for %s is invalid: %s", artifact, req.name, e) + logger.warning('Built %s for %s is invalid: %s', artifact, req.name, e) return None return wheel_path @@ -234,22 +245,22 @@ def _build_one( def _build_one_inside_env( req: InstallRequirement, output_dir: str, - build_options: List[str], - global_options: List[str], + build_options: list[str], + global_options: list[str], editable: bool, -) -> Optional[str]: - with TempDirectory(kind="wheel") as temp_dir: +) -> str | None: + with TempDirectory(kind='wheel') as temp_dir: assert req.name if req.use_pep517: assert req.metadata_directory assert req.pep517_backend if global_options: logger.warning( - "Ignoring --global-option when building %s using PEP 517", req.name + 'Ignoring --global-option when building %s using PEP 517', req.name, ) if build_options: logger.warning( - "Ignoring --build-option when building %s using PEP 517", req.name + 'Ignoring --build-option when building %s using PEP 517', req.name, ) if editable: wheel_path = build_wheel_editable( @@ -282,17 +293,17 @@ def _build_one_inside_env( wheel_hash, length = hash_file(wheel_path) shutil.move(wheel_path, dest_path) logger.info( - "Created wheel for %s: filename=%s size=%d sha256=%s", + 'Created wheel for %s: filename=%s size=%d sha256=%s', req.name, wheel_name, length, wheel_hash.hexdigest(), ) - logger.info("Stored in directory: %s", output_dir) + logger.info('Stored in directory: %s', output_dir) return dest_path except Exception as e: logger.warning( - "Building wheel for %s failed: %s", + 'Building wheel for %s failed: %s', req.name, e, ) @@ -302,20 +313,20 @@ def _build_one_inside_env( return None -def _clean_one_legacy(req: InstallRequirement, global_options: List[str]) -> bool: +def _clean_one_legacy(req: InstallRequirement, global_options: list[str]) -> bool: clean_args = make_setuptools_clean_args( req.setup_py_path, global_options=global_options, ) - logger.info("Running setup.py clean for %s", req.name) + logger.info('Running setup.py clean for %s', req.name) try: call_subprocess( - clean_args, command_desc="python setup.py clean", cwd=req.source_dir + clean_args, command_desc='python setup.py clean', cwd=req.source_dir, ) return True except Exception: - logger.error("Failed cleaning build dir for %s", req.name) + logger.error('Failed cleaning build dir for %s', req.name) return False @@ -323,8 +334,8 @@ def build( requirements: Iterable[InstallRequirement], wheel_cache: WheelCache, verify: bool, - build_options: List[str], - global_options: List[str], + build_options: list[str], + global_options: list[str], ) -> BuildResult: """Build wheels. @@ -336,8 +347,8 @@ def build( # Build the wheels. logger.info( - "Building wheels for collected packages: %s", - ", ".join(req.name for req in requirements), # type: ignore + 'Building wheels for collected packages: %s', + ', '.join(req.name for req in requirements), # type: ignore ) with indent_log(): @@ -365,13 +376,13 @@ def build( # notify success/failure if build_successes: logger.info( - "Successfully built %s", - " ".join([req.name for req in build_successes]), # type: ignore + 'Successfully built %s', + ' '.join([req.name for req in build_successes]), # type: ignore ) if build_failures: logger.info( - "Failed to build %s", - " ".join([req.name for req in build_failures]), # type: ignore + 'Failed to build %s', + ' '.join([req.name for req in build_failures]), # type: ignore ) # Return a list of requirements that failed to build return build_successes, build_failures diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/__init__.py index 3843cb0..c47c1c2 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/__init__.py @@ -5,7 +5,7 @@ depend on something external. Files inside of pip._vendor should be considered immutable and should only be updated to versions from upstream. """ -from __future__ import absolute_import +from __future__ import annotations import glob import os.path @@ -27,7 +27,7 @@ WHEEL_DIR = os.path.abspath(os.path.dirname(__file__)) # if the vendored ones do not exist. This idea of this was taken from # https://github.com/kennethreitz/requests/pull/2567. def vendored(modulename): - vendored_name = "{0}.{1}".format(__name__, modulename) + vendored_name = f'{__name__}.{modulename}' try: __import__(modulename, globals(), locals(), level=0) @@ -43,7 +43,7 @@ def vendored(modulename): pass else: sys.modules[vendored_name] = sys.modules[modulename] - base, head = vendored_name.rsplit(".", 1) + base, head = vendored_name.rsplit('.', 1) setattr(sys.modules[base], head, sys.modules[modulename]) @@ -55,57 +55,59 @@ def vendored(modulename): if DEBUNDLED: # Actually look inside of WHEEL_DIR to find .whl files and add them to the # front of our sys.path. - sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path + sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, '*.whl')) + sys.path # Actually alias all of our vendored dependencies. - vendored("cachecontrol") - vendored("certifi") - vendored("colorama") - vendored("distlib") - vendored("distro") - vendored("html5lib") - vendored("six") - vendored("six.moves") - vendored("six.moves.urllib") - vendored("six.moves.urllib.parse") - vendored("packaging") - vendored("packaging.version") - vendored("packaging.specifiers") - vendored("pep517") - vendored("pkg_resources") - vendored("platformdirs") - vendored("progress") - vendored("requests") - vendored("requests.exceptions") - vendored("requests.packages") - vendored("requests.packages.urllib3") - vendored("requests.packages.urllib3._collections") - vendored("requests.packages.urllib3.connection") - vendored("requests.packages.urllib3.connectionpool") - vendored("requests.packages.urllib3.contrib") - vendored("requests.packages.urllib3.contrib.ntlmpool") - vendored("requests.packages.urllib3.contrib.pyopenssl") - vendored("requests.packages.urllib3.exceptions") - vendored("requests.packages.urllib3.fields") - vendored("requests.packages.urllib3.filepost") - vendored("requests.packages.urllib3.packages") - vendored("requests.packages.urllib3.packages.ordered_dict") - vendored("requests.packages.urllib3.packages.six") - vendored("requests.packages.urllib3.packages.ssl_match_hostname") - vendored("requests.packages.urllib3.packages.ssl_match_hostname." - "_implementation") - vendored("requests.packages.urllib3.poolmanager") - vendored("requests.packages.urllib3.request") - vendored("requests.packages.urllib3.response") - vendored("requests.packages.urllib3.util") - vendored("requests.packages.urllib3.util.connection") - vendored("requests.packages.urllib3.util.request") - vendored("requests.packages.urllib3.util.response") - vendored("requests.packages.urllib3.util.retry") - vendored("requests.packages.urllib3.util.ssl_") - vendored("requests.packages.urllib3.util.timeout") - vendored("requests.packages.urllib3.util.url") - vendored("resolvelib") - vendored("tenacity") - vendored("tomli") - vendored("urllib3") + vendored('cachecontrol') + vendored('certifi') + vendored('colorama') + vendored('distlib') + vendored('distro') + vendored('html5lib') + vendored('six') + vendored('six.moves') + vendored('six.moves.urllib') + vendored('six.moves.urllib.parse') + vendored('packaging') + vendored('packaging.version') + vendored('packaging.specifiers') + vendored('pep517') + vendored('pkg_resources') + vendored('platformdirs') + vendored('progress') + vendored('requests') + vendored('requests.exceptions') + vendored('requests.packages') + vendored('requests.packages.urllib3') + vendored('requests.packages.urllib3._collections') + vendored('requests.packages.urllib3.connection') + vendored('requests.packages.urllib3.connectionpool') + vendored('requests.packages.urllib3.contrib') + vendored('requests.packages.urllib3.contrib.ntlmpool') + vendored('requests.packages.urllib3.contrib.pyopenssl') + vendored('requests.packages.urllib3.exceptions') + vendored('requests.packages.urllib3.fields') + vendored('requests.packages.urllib3.filepost') + vendored('requests.packages.urllib3.packages') + vendored('requests.packages.urllib3.packages.ordered_dict') + vendored('requests.packages.urllib3.packages.six') + vendored('requests.packages.urllib3.packages.ssl_match_hostname') + vendored( + 'requests.packages.urllib3.packages.ssl_match_hostname.' + '_implementation', + ) + vendored('requests.packages.urllib3.poolmanager') + vendored('requests.packages.urllib3.request') + vendored('requests.packages.urllib3.response') + vendored('requests.packages.urllib3.util') + vendored('requests.packages.urllib3.util.connection') + vendored('requests.packages.urllib3.util.request') + vendored('requests.packages.urllib3.util.response') + vendored('requests.packages.urllib3.util.retry') + vendored('requests.packages.urllib3.util.ssl_') + vendored('requests.packages.urllib3.util.timeout') + vendored('requests.packages.urllib3.util.url') + vendored('resolvelib') + vendored('tenacity') + vendored('tomli') + vendored('urllib3') diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__init__.py index 8435d62..d90e5ba 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__init__.py @@ -1,14 +1,14 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 - """CacheControl import Interface. Make it easy to import from cachecontrol without long namespaces. """ -__author__ = "Eric Larson" -__email__ = "eric@ionrock.org" -__version__ = "0.12.10" +from __future__ import annotations +__author__ = 'Eric Larson' +__email__ = 'eric@ionrock.org' +__version__ = '0.12.10' from .wrapper import CacheControl from .adapter import CacheControlAdapter diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/_cmd.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/_cmd.py index 4266b5e..645c8a2 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/_cmd.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/_cmd.py @@ -1,17 +1,16 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations import logging +from argparse import ArgumentParser from pip._vendor import requests - from pip._vendor.cachecontrol.adapter import CacheControlAdapter from pip._vendor.cachecontrol.cache import DictCache from pip._vendor.cachecontrol.controller import logger -from argparse import ArgumentParser - def setup_logging(): logger.setLevel(logging.DEBUG) @@ -21,11 +20,11 @@ def setup_logging(): def get_session(): adapter = CacheControlAdapter( - DictCache(), cache_etags=True, serializer=None, heuristic=None + DictCache(), cache_etags=True, serializer=None, heuristic=None, ) sess = requests.Session() - sess.mount("http://", adapter) - sess.mount("https://", adapter) + sess.mount('http://', adapter) + sess.mount('https://', adapter) sess.cache_controller = adapter.controller return sess @@ -33,7 +32,7 @@ def get_session(): def get_args(): parser = ArgumentParser() - parser.add_argument("url", help="The URL to try and cache") + parser.add_argument('url', help='The URL to try and cache') return parser.parse_args() @@ -52,10 +51,10 @@ def main(args=None): # Now try to get it if sess.cache_controller.cached_request(resp.request): - print("Cached!") + print('Cached!') else: - print("Not cached :(") + print('Not cached :(') -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/adapter.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/adapter.py index 94c75e1..c64cff6 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/adapter.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/adapter.py @@ -1,20 +1,22 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations -import types import functools +import types import zlib from pip._vendor.requests.adapters import HTTPAdapter -from .controller import CacheController, PERMANENT_REDIRECT_STATUSES from .cache import DictCache +from .controller import CacheController +from .controller import PERMANENT_REDIRECT_STATUSES from .filewrapper import CallbackFileWrapper class CacheControlAdapter(HTTPAdapter): - invalidating_methods = {"PUT", "PATCH", "DELETE"} + invalidating_methods = {'PUT', 'PATCH', 'DELETE'} def __init__( self, @@ -25,16 +27,16 @@ class CacheControlAdapter(HTTPAdapter): heuristic=None, cacheable_methods=None, *args, - **kw + **kw, ): - super(CacheControlAdapter, self).__init__(*args, **kw) + super().__init__(*args, **kw) self.cache = DictCache() if cache is None else cache self.heuristic = heuristic - self.cacheable_methods = cacheable_methods or ("GET",) + self.cacheable_methods = cacheable_methods or ('GET',) controller_factory = controller_class or CacheController self.controller = controller_factory( - self.cache, cache_etags=cache_etags, serializer=serializer + self.cache, cache_etags=cache_etags, serializer=serializer, ) def send(self, request, cacheable_methods=None, **kw): @@ -54,12 +56,12 @@ class CacheControlAdapter(HTTPAdapter): # check for etags and add headers if appropriate request.headers.update(self.controller.conditional_headers(request)) - resp = super(CacheControlAdapter, self).send(request, **kw) + resp = super().send(request, **kw) return resp def build_response( - self, request, response, from_cache=False, cacheable_methods=None + self, request, response, from_cache=False, cacheable_methods=None, ): """ Build a response by making a request or using the cache. @@ -81,7 +83,7 @@ class CacheControlAdapter(HTTPAdapter): # have an etag. In either case, we want to try and # update the cache if that is the case. cached_response = self.controller.update_cached_response( - request, response + request, response, ) if cached_response is not response: @@ -105,7 +107,7 @@ class CacheControlAdapter(HTTPAdapter): response._fp = CallbackFileWrapper( response._fp, functools.partial( - self.controller.cache_response, request, response + self.controller.cache_response, request, response, ), ) if response.chunked: @@ -117,10 +119,10 @@ class CacheControlAdapter(HTTPAdapter): self._fp._close() response._update_chunk_length = types.MethodType( - _update_chunk_length, response + _update_chunk_length, response, ) - resp = super(CacheControlAdapter, self).build_response(request, response) + resp = super().build_response(request, response) # See if we should invalidate the cache. if request.method in self.invalidating_methods and resp.ok: @@ -134,4 +136,4 @@ class CacheControlAdapter(HTTPAdapter): def close(self): self.cache.close() - super(CacheControlAdapter, self).close() + super().close() diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/cache.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/cache.py index 44e4309..3611183 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/cache.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/cache.py @@ -1,15 +1,16 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 - """ The cache object API for implementing caches. The default is a thread safe in-memory dictionary. """ +from __future__ import annotations + from threading import Lock -class BaseCache(object): +class BaseCache: def get(self, key): raise NotImplementedError() diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/__init__.py index 44becd6..5287cce 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/__init__.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations from .file_cache import FileCache # noqa from .redis_cache import RedisCache # noqa diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py index 6cd1106..d736d74 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations import hashlib import os @@ -28,18 +29,18 @@ def _secure_open_write(filename, fmode): # Do not follow symlinks to prevent someone from making a symlink that # we follow and insecurely open a cache file. - if hasattr(os, "O_NOFOLLOW"): + if hasattr(os, 'O_NOFOLLOW'): flags |= os.O_NOFOLLOW # On Windows we'll mark this file as binary - if hasattr(os, "O_BINARY"): + if hasattr(os, 'O_BINARY'): flags |= os.O_BINARY # Before we open our file, we want to delete any existing file that is # there try: os.remove(filename) - except (IOError, OSError): + except OSError: # The file must not exist already, so we can just skip ahead to opening pass @@ -49,7 +50,7 @@ def _secure_open_write(filename, fmode): # happen if someone is attempting to attack us. fd = os.open(filename, flags, fmode) try: - return os.fdopen(fd, "wb") + return os.fdopen(fd, 'wb') except: # An error occurred wrapping our FD in a file object @@ -70,7 +71,7 @@ class FileCache(BaseCache): ): if use_dir_lock is not None and lock_class is not None: - raise ValueError("Cannot use use_dir_lock and lock_class together") + raise ValueError('Cannot use use_dir_lock and lock_class together') try: from lockfile import LockFile @@ -81,7 +82,7 @@ class FileCache(BaseCache): NOTE: In order to use the FileCache you must have lockfile installed. You can install it via pip: pip install lockfile - """ + """, ) raise ImportError(notice) @@ -112,7 +113,7 @@ class FileCache(BaseCache): def get(self, key): name = self._fn(key) try: - with open(name, "rb") as fh: + with open(name, 'rb') as fh: return fh.read() except FileNotFoundError: @@ -124,7 +125,7 @@ class FileCache(BaseCache): # Make sure the directory exists try: os.makedirs(os.path.dirname(name), self.dirmode) - except (IOError, OSError): + except OSError: pass with self.lock_class(name) as lock: diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py index 720b507..c46bd25 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py @@ -1,10 +1,10 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 - -from __future__ import division +from __future__ import annotations from datetime import datetime + from pip._vendor.cachecontrol.cache import BaseCache diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/compat.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/compat.py index ccec937..ca04dfd 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/compat.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/compat.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations try: from urllib.parse import urljoin diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/controller.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/controller.py index d7e7380..0199396 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/controller.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/controller.py @@ -1,13 +1,14 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 - """ The httplib2 algorithms ported for use with requests. """ +from __future__ import annotations + +import calendar import logging import re -import calendar import time from email.utils import parsedate_tz @@ -19,7 +20,7 @@ from .serialize import Serializer logger = logging.getLogger(__name__) -URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?") +URI = re.compile(r'^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?') PERMANENT_REDIRECT_STATUSES = (301, 308) @@ -33,12 +34,12 @@ def parse_uri(uri): return (groups[1], groups[3], groups[4], groups[6], groups[8]) -class CacheController(object): +class CacheController: """An interface to see if request should cached or not. """ def __init__( - self, cache=None, cache_etags=True, serializer=None, status_codes=None + self, cache=None, cache_etags=True, serializer=None, status_codes=None, ): self.cache = DictCache() if cache is None else cache self.cache_etags = cache_etags @@ -50,18 +51,18 @@ class CacheController(object): """Normalize the URL to create a safe key for the cache""" (scheme, authority, path, query, fragment) = parse_uri(uri) if not scheme or not authority: - raise Exception("Only absolute URIs are allowed. uri = %s" % uri) + raise Exception('Only absolute URIs are allowed. uri = %s' % uri) scheme = scheme.lower() authority = authority.lower() if not path: - path = "/" + path = '/' # Could do syntax based normalization of the URI before # computing the digest. See Section 6.2.2 of Std 66. - request_uri = query and "?".join([path, query]) or path - defrag_uri = scheme + "://" + authority + request_uri + request_uri = query and '?'.join([path, query]) or path + defrag_uri = scheme + '://' + authority + request_uri return defrag_uri @@ -72,35 +73,35 @@ class CacheController(object): def parse_cache_control(self, headers): known_directives = { # https://tools.ietf.org/html/rfc7234#section-5.2 - "max-age": (int, True), - "max-stale": (int, False), - "min-fresh": (int, True), - "no-cache": (None, False), - "no-store": (None, False), - "no-transform": (None, False), - "only-if-cached": (None, False), - "must-revalidate": (None, False), - "public": (None, False), - "private": (None, False), - "proxy-revalidate": (None, False), - "s-maxage": (int, True), + 'max-age': (int, True), + 'max-stale': (int, False), + 'min-fresh': (int, True), + 'no-cache': (None, False), + 'no-store': (None, False), + 'no-transform': (None, False), + 'only-if-cached': (None, False), + 'must-revalidate': (None, False), + 'public': (None, False), + 'private': (None, False), + 'proxy-revalidate': (None, False), + 's-maxage': (int, True), } - cc_headers = headers.get("cache-control", headers.get("Cache-Control", "")) + cc_headers = headers.get('cache-control', headers.get('Cache-Control', '')) retval = {} - for cc_directive in cc_headers.split(","): + for cc_directive in cc_headers.split(','): if not cc_directive.strip(): continue - parts = cc_directive.split("=", 1) + parts = cc_directive.split('=', 1) directive = parts[0].strip() try: typ, required = known_directives[directive] except KeyError: - logger.debug("Ignoring unknown cache-control directive: %s", directive) + logger.debug('Ignoring unknown cache-control directive: %s', directive) continue if not typ or not required: @@ -111,12 +112,12 @@ class CacheController(object): except IndexError: if required: logger.debug( - "Missing value for cache-control " "directive: %s", + 'Missing value for cache-control ' 'directive: %s', directive, ) except ValueError: logger.debug( - "Invalid value for cache-control directive " "%s, must be %s", + 'Invalid value for cache-control directive ' '%s, must be %s', directive, typ.__name__, ) @@ -133,24 +134,24 @@ class CacheController(object): cc = self.parse_cache_control(request.headers) # Bail out if the request insists on fresh data - if "no-cache" in cc: + if 'no-cache' in cc: logger.debug('Request header has "no-cache", cache bypassed') return False - if "max-age" in cc and cc["max-age"] == 0: + if 'max-age' in cc and cc['max-age'] == 0: logger.debug('Request header has "max_age" as 0, cache bypassed') return False # Request allows serving from the cache, let's see if we find something cache_data = self.cache.get(cache_url) if cache_data is None: - logger.debug("No cache entry available") + logger.debug('No cache entry available') return False # Check whether it can be deserialized resp = self.serializer.loads(request, cache_data) if not resp: - logger.warning("Cache entry deserialization failed, entry ignored") + logger.warning('Cache entry deserialization failed, entry ignored') return False # If we have a cached permanent redirect, return it immediately. We @@ -164,26 +165,26 @@ class CacheController(object): # with cache busting headers as usual (ie no-cache). if int(resp.status) in PERMANENT_REDIRECT_STATUSES: msg = ( - "Returning cached permanent redirect response " - "(ignoring date and etag information)" + 'Returning cached permanent redirect response ' + '(ignoring date and etag information)' ) logger.debug(msg) return resp headers = CaseInsensitiveDict(resp.headers) - if not headers or "date" not in headers: - if "etag" not in headers: + if not headers or 'date' not in headers: + if 'etag' not in headers: # Without date or etag, the cached response can never be used # and should be deleted. - logger.debug("Purging cached response: no date or etag") + logger.debug('Purging cached response: no date or etag') self.cache.delete(cache_url) - logger.debug("Ignoring cached response: no date") + logger.debug('Ignoring cached response: no date') return False now = time.time() - date = calendar.timegm(parsedate_tz(headers["date"])) + date = calendar.timegm(parsedate_tz(headers['date'])) current_age = max(0, now - date) - logger.debug("Current age based on date: %i", current_age) + logger.debug('Current age based on date: %i', current_age) # TODO: There is an assumption that the result will be a # urllib3 response object. This may not be best since we @@ -195,40 +196,40 @@ class CacheController(object): freshness_lifetime = 0 # Check the max-age pragma in the cache control header - if "max-age" in resp_cc: - freshness_lifetime = resp_cc["max-age"] - logger.debug("Freshness lifetime from max-age: %i", freshness_lifetime) + if 'max-age' in resp_cc: + freshness_lifetime = resp_cc['max-age'] + logger.debug('Freshness lifetime from max-age: %i', freshness_lifetime) # If there isn't a max-age, check for an expires header - elif "expires" in headers: - expires = parsedate_tz(headers["expires"]) + elif 'expires' in headers: + expires = parsedate_tz(headers['expires']) if expires is not None: expire_time = calendar.timegm(expires) - date freshness_lifetime = max(0, expire_time) - logger.debug("Freshness lifetime from expires: %i", freshness_lifetime) + logger.debug('Freshness lifetime from expires: %i', freshness_lifetime) # Determine if we are setting freshness limit in the # request. Note, this overrides what was in the response. - if "max-age" in cc: - freshness_lifetime = cc["max-age"] + if 'max-age' in cc: + freshness_lifetime = cc['max-age'] logger.debug( - "Freshness lifetime from request max-age: %i", freshness_lifetime + 'Freshness lifetime from request max-age: %i', freshness_lifetime, ) - if "min-fresh" in cc: - min_fresh = cc["min-fresh"] + if 'min-fresh' in cc: + min_fresh = cc['min-fresh'] # adjust our current age by our min fresh current_age += min_fresh - logger.debug("Adjusted current age from min-fresh: %i", current_age) + logger.debug('Adjusted current age from min-fresh: %i', current_age) # Return entry if it is fresh enough if freshness_lifetime > current_age: logger.debug('The response is "fresh", returning cached response') - logger.debug("%i > %i", freshness_lifetime, current_age) + logger.debug('%i > %i', freshness_lifetime, current_age) return resp # we're not fresh. If we don't have an Etag, clear it out - if "etag" not in headers: + if 'etag' not in headers: logger.debug('The cached response is "stale" with no etag, purging') self.cache.delete(cache_url) @@ -243,11 +244,11 @@ class CacheController(object): if resp: headers = CaseInsensitiveDict(resp.headers) - if "etag" in headers: - new_headers["If-None-Match"] = headers["ETag"] + if 'etag' in headers: + new_headers['If-None-Match'] = headers['ETag'] - if "last-modified" in headers: - new_headers["If-Modified-Since"] = headers["Last-Modified"] + if 'last-modified' in headers: + new_headers['If-Modified-Since'] = headers['Last-Modified'] return new_headers @@ -262,14 +263,14 @@ class CacheController(object): cacheable_status_codes = status_codes or self.cacheable_status_codes if response.status not in cacheable_status_codes: logger.debug( - "Status code %s not in %s", response.status, cacheable_status_codes + 'Status code %s not in %s', response.status, cacheable_status_codes, ) return response_headers = CaseInsensitiveDict(response.headers) - if "date" in response_headers: - date = calendar.timegm(parsedate_tz(response_headers["date"])) + if 'date' in response_headers: + date = calendar.timegm(parsedate_tz(response_headers['date'])) else: date = 0 @@ -278,10 +279,10 @@ class CacheController(object): # been given matches the expected size, and if it doesn't we'll just # skip trying to cache it. if ( - body is not None - and "content-length" in response_headers - and response_headers["content-length"].isdigit() - and int(response_headers["content-length"]) != len(body) + body is not None and + 'content-length' in response_headers and + response_headers['content-length'].isdigit() and + int(response_headers['content-length']) != len(body) ): return @@ -293,10 +294,10 @@ class CacheController(object): # Delete it from the cache if we happen to have it stored there no_store = False - if "no-store" in cc: + if 'no-store' in cc: no_store = True logger.debug('Response header has "no-store"') - if "no-store" in cc_req: + if 'no-store' in cc_req: no_store = True logger.debug('Request header has "no-store"') if no_store and self.cache.get(cache_url): @@ -310,22 +311,22 @@ class CacheController(object): # Storing such a response leads to a deserialization warning # during cache lookup and is not allowed to ever be served, # so storing it can be avoided. - if "*" in response_headers.get("vary", ""): + if '*' in response_headers.get('vary', ''): logger.debug('Response header has "Vary: *"') return # If we've been given an etag, then keep the response - if self.cache_etags and "etag" in response_headers: + if self.cache_etags and 'etag' in response_headers: expires_time = 0 - if response_headers.get("expires"): - expires = parsedate_tz(response_headers["expires"]) + if response_headers.get('expires'): + expires = parsedate_tz(response_headers['expires']) if expires is not None: expires_time = calendar.timegm(expires) - date expires_time = max(expires_time, 14 * 86400) - logger.debug("etag object cached for {0} seconds".format(expires_time)) - logger.debug("Caching due to etag") + logger.debug(f'etag object cached for {expires_time} seconds') + logger.debug('Caching due to etag') self.cache.set( cache_url, self.serializer.dumps(request, response, body), @@ -335,18 +336,18 @@ class CacheController(object): # Add to the cache any permanent redirects. We do this before looking # that the Date headers. elif int(response.status) in PERMANENT_REDIRECT_STATUSES: - logger.debug("Caching permanent redirect") - self.cache.set(cache_url, self.serializer.dumps(request, response, b"")) + logger.debug('Caching permanent redirect') + self.cache.set(cache_url, self.serializer.dumps(request, response, b'')) # Add to the cache if the response headers demand it. If there # is no date header then we can't do anything about expiring # the cache. - elif "date" in response_headers: - date = calendar.timegm(parsedate_tz(response_headers["date"])) + elif 'date' in response_headers: + date = calendar.timegm(parsedate_tz(response_headers['date'])) # cache when there is a max-age > 0 - if "max-age" in cc and cc["max-age"] > 0: - logger.debug("Caching b/c date exists and max-age > 0") - expires_time = cc["max-age"] + if 'max-age' in cc and cc['max-age'] > 0: + logger.debug('Caching b/c date exists and max-age > 0') + expires_time = cc['max-age'] self.cache.set( cache_url, self.serializer.dumps(request, response, body), @@ -355,18 +356,18 @@ class CacheController(object): # If the request can expire, it means we should cache it # in the meantime. - elif "expires" in response_headers: - if response_headers["expires"]: - expires = parsedate_tz(response_headers["expires"]) + elif 'expires' in response_headers: + if response_headers['expires']: + expires = parsedate_tz(response_headers['expires']) if expires is not None: expires_time = calendar.timegm(expires) - date else: expires_time = None logger.debug( - "Caching b/c of expires header. expires in {0} seconds".format( - expires_time - ) + 'Caching b/c of expires header. expires in {} seconds'.format( + expires_time, + ), ) self.cache.set( cache_url, @@ -396,14 +397,14 @@ class CacheController(object): # the cached body invalid. But... just in case, we'll be sure # to strip out ones we know that might be problmatic due to # typical assumptions. - excluded_headers = ["content-length"] + excluded_headers = ['content-length'] cached_response.headers.update( - dict( - (k, v) + { + k: v for k, v in response.headers.items() if k.lower() not in excluded_headers - ) + }, ) # we want a 200 b/c we have content via the cache diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/filewrapper.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/filewrapper.py index f5ed5f6..73c3d8c 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/filewrapper.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/filewrapper.py @@ -1,12 +1,13 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations -from tempfile import NamedTemporaryFile import mmap +from tempfile import NamedTemporaryFile -class CallbackFileWrapper(object): +class CallbackFileWrapper: """ Small wrapper around a fp object which will tee everything read into a buffer, and when that file is closed it will execute a callback with the @@ -26,7 +27,7 @@ class CallbackFileWrapper(object): """ def __init__(self, fp, callback): - self.__buf = NamedTemporaryFile("rb+", delete=True) + self.__buf = NamedTemporaryFile('rb+', delete=True) self.__fp = fp self.__callback = callback @@ -39,7 +40,7 @@ class CallbackFileWrapper(object): # self.__fp hasn't been set. # # [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers - fp = self.__getattribute__("_CallbackFileWrapper__fp") + fp = self.__getattribute__('_CallbackFileWrapper__fp') return getattr(fp, name) def __is_fp_closed(self): @@ -63,7 +64,7 @@ class CallbackFileWrapper(object): if self.__callback: if self.__buf.tell() == 0: # Empty file: - result = b"" + result = b'' else: # Return the data without actually loading it into memory, # relying on Python's buffer API and mmap(). mmap() just gives @@ -71,7 +72,7 @@ class CallbackFileWrapper(object): # doesn't result in duplicate memory use. self.__buf.seek(0, 0) result = memoryview( - mmap.mmap(self.__buf.fileno(), 0, access=mmap.ACCESS_READ) + mmap.mmap(self.__buf.fileno(), 0, access=mmap.ACCESS_READ), ) self.__callback(result) @@ -99,7 +100,7 @@ class CallbackFileWrapper(object): def _safe_read(self, amt): data = self.__fp._safe_read(amt) - if amt == 2 and data == b"\r\n": + if amt == 2 and data == b'\r\n': # urllib executes this read to toss the CRLF at the end # of the chunk. return data diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/heuristics.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/heuristics.py index ebe4a96..b756ddd 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/heuristics.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/heuristics.py @@ -1,15 +1,17 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations import calendar import time +from datetime import datetime +from datetime import timedelta +from email.utils import formatdate +from email.utils import parsedate +from email.utils import parsedate_tz -from email.utils import formatdate, parsedate, parsedate_tz - -from datetime import datetime, timedelta - -TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT" +TIME_FMT = '%a, %d %b %Y %H:%M:%S GMT' def expire_after(delta, date=None): @@ -21,7 +23,7 @@ def datetime_to_header(dt): return formatdate(calendar.timegm(dt.timetuple())) -class BaseHeuristic(object): +class BaseHeuristic: def warning(self, response): """ @@ -50,7 +52,7 @@ class BaseHeuristic(object): response.headers.update(updated_headers) warning_header_value = self.warning(response) if warning_header_value is not None: - response.headers.update({"Warning": warning_header_value}) + response.headers.update({'Warning': warning_header_value}) return response @@ -64,11 +66,11 @@ class OneDayCache(BaseHeuristic): def update_headers(self, response): headers = {} - if "expires" not in response.headers: - date = parsedate(response.headers["date"]) + if 'expires' not in response.headers: + date = parsedate(response.headers['date']) expires = expire_after(timedelta(days=1), date=datetime(*date[:6])) - headers["expires"] = datetime_to_header(expires) - headers["cache-control"] = "public" + headers['expires'] = datetime_to_header(expires) + headers['cache-control'] = 'public' return headers @@ -82,10 +84,10 @@ class ExpiresAfter(BaseHeuristic): def update_headers(self, response): expires = expire_after(self.delta) - return {"expires": datetime_to_header(expires), "cache-control": "public"} + return {'expires': datetime_to_header(expires), 'cache-control': 'public'} def warning(self, response): - tmpl = "110 - Automatically cached for %s. Response might be stale" + tmpl = '110 - Automatically cached for %s. Response might be stale' return tmpl % self.delta @@ -102,26 +104,26 @@ class LastModified(BaseHeuristic): Unlike mozilla we limit this to 24-hr. """ cacheable_by_default_statuses = { - 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501 + 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501, } def update_headers(self, resp): headers = resp.headers - if "expires" in headers: + if 'expires' in headers: return {} - if "cache-control" in headers and headers["cache-control"] != "public": + if 'cache-control' in headers and headers['cache-control'] != 'public': return {} if resp.status not in self.cacheable_by_default_statuses: return {} - if "date" not in headers or "last-modified" not in headers: + if 'date' not in headers or 'last-modified' not in headers: return {} - date = calendar.timegm(parsedate_tz(headers["date"])) - last_modified = parsedate(headers["last-modified"]) + date = calendar.timegm(parsedate_tz(headers['date'])) + last_modified = parsedate(headers['last-modified']) if date is None or last_modified is None: return {} @@ -133,7 +135,7 @@ class LastModified(BaseHeuristic): return {} expires = date + freshness_lifetime - return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))} + return {'expires': time.strftime(TIME_FMT, time.gmtime(expires))} def warning(self, resp): return None diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/serialize.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/serialize.py index b075df1..28d1ab6 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/serialize.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/serialize.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations import base64 import io @@ -10,21 +11,23 @@ import zlib from pip._vendor import msgpack from pip._vendor.requests.structures import CaseInsensitiveDict -from .compat import HTTPResponse, pickle, text_type +from .compat import HTTPResponse +from .compat import pickle +from .compat import text_type def _b64_decode_bytes(b): - return base64.b64decode(b.encode("ascii")) + return base64.b64decode(b.encode('ascii')) def _b64_decode_str(s): - return _b64_decode_bytes(s).decode("utf8") + return _b64_decode_bytes(s).decode('utf8') _default_body_read = object() -class Serializer(object): +class Serializer: def dumps(self, request, response, body=None): response_headers = CaseInsensitiveDict(response.headers) @@ -43,31 +46,31 @@ class Serializer(object): # doesn't know the difference. Forcing these to unicode will be # enough to have msgpack know the difference. data = { - u"response": { - u"body": body, - u"headers": dict( - (text_type(k), text_type(v)) for k, v in response.headers.items() - ), - u"status": response.status, - u"version": response.version, - u"reason": text_type(response.reason), - u"strict": response.strict, - u"decode_content": response.decode_content, - } + 'response': { + 'body': body, + 'headers': { + text_type(k): text_type(v) for k, v in response.headers.items() + }, + 'status': response.status, + 'version': response.version, + 'reason': text_type(response.reason), + 'strict': response.strict, + 'decode_content': response.decode_content, + }, } # Construct our vary headers - data[u"vary"] = {} - if u"vary" in response_headers: - varied_headers = response_headers[u"vary"].split(",") + data['vary'] = {} + if 'vary' in response_headers: + varied_headers = response_headers['vary'].split(',') for header in varied_headers: header = text_type(header).strip() header_value = request.headers.get(header, None) if header_value is not None: header_value = text_type(header_value) - data[u"vary"][header] = header_value + data['vary'][header] = header_value - return b",".join([b"cc=4", msgpack.dumps(data, use_bin_type=True)]) + return b','.join([b'cc=4', msgpack.dumps(data, use_bin_type=True)]) def loads(self, request, data): # Short circuit if we've been given an empty set of data @@ -77,22 +80,22 @@ class Serializer(object): # Determine what version of the serializer the data was serialized # with try: - ver, data = data.split(b",", 1) + ver, data = data.split(b',', 1) except ValueError: - ver = b"cc=0" + ver = b'cc=0' # Make sure that our "ver" is actually a version and isn't a false # positive from a , being in the data stream. - if ver[:3] != b"cc=": + if ver[:3] != b'cc=': data = ver + data - ver = b"cc=0" + ver = b'cc=0' # Get the version number out of the cc=N - ver = ver.split(b"=", 1)[-1].decode("ascii") + ver = ver.split(b'=', 1)[-1].decode('ascii') # Dispatch to the actual load method for the given version try: - return getattr(self, "_loads_v{}".format(ver))(request, data) + return getattr(self, f'_loads_v{ver}')(request, data) except AttributeError: # This is a version we don't have a loads function for, so we'll @@ -107,22 +110,22 @@ class Serializer(object): # determine if the cached response is suitable for this request. # This case is also handled in the controller code when creating # a cache entry, but is left here for backwards compatibility. - if "*" in cached.get("vary", {}): + if '*' in cached.get('vary', {}): return # Ensure that the Vary headers for the cached response match our # request - for header, value in cached.get("vary", {}).items(): + for header, value in cached.get('vary', {}).items(): if request.headers.get(header, None) != value: return - body_raw = cached["response"].pop("body") + body_raw = cached['response'].pop('body') - headers = CaseInsensitiveDict(data=cached["response"]["headers"]) - if headers.get("transfer-encoding", "") == "chunked": - headers.pop("transfer-encoding") + headers = CaseInsensitiveDict(data=cached['response']['headers']) + if headers.get('transfer-encoding', '') == 'chunked': + headers.pop('transfer-encoding') - cached["response"]["headers"] = headers + cached['response']['headers'] = headers try: body = io.BytesIO(body_raw) @@ -133,9 +136,9 @@ class Serializer(object): # fail with: # # TypeError: 'str' does not support the buffer interface - body = io.BytesIO(body_raw.encode("utf8")) + body = io.BytesIO(body_raw.encode('utf8')) - return HTTPResponse(body=body, preload_content=False, **cached["response"]) + return HTTPResponse(body=body, preload_content=False, **cached['response']) def _loads_v0(self, request, data): # The original legacy cache data. This doesn't contain enough @@ -153,21 +156,21 @@ class Serializer(object): def _loads_v2(self, request, data): try: - cached = json.loads(zlib.decompress(data).decode("utf8")) + cached = json.loads(zlib.decompress(data).decode('utf8')) except (ValueError, zlib.error): return # We need to decode the items that we've base64 encoded - cached["response"]["body"] = _b64_decode_bytes(cached["response"]["body"]) - cached["response"]["headers"] = dict( - (_b64_decode_str(k), _b64_decode_str(v)) - for k, v in cached["response"]["headers"].items() - ) - cached["response"]["reason"] = _b64_decode_str(cached["response"]["reason"]) - cached["vary"] = dict( - (_b64_decode_str(k), _b64_decode_str(v) if v is not None else v) - for k, v in cached["vary"].items() - ) + cached['response']['body'] = _b64_decode_bytes(cached['response']['body']) + cached['response']['headers'] = { + _b64_decode_str(k): _b64_decode_str(v) + for k, v in cached['response']['headers'].items() + } + cached['response']['reason'] = _b64_decode_str(cached['response']['reason']) + cached['vary'] = { + _b64_decode_str(k): _b64_decode_str(v) if v is not None else v + for k, v in cached['vary'].items() + } return self.prepare_response(request, cached) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/wrapper.py b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/wrapper.py index b6ee7f2..18002cf 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/wrapper.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/wrapper.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2015 Eric Larson # # SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations from .adapter import CacheControlAdapter from .cache import DictCache @@ -27,7 +28,7 @@ def CacheControl( controller_class=controller_class, cacheable_methods=cacheable_methods, ) - sess.mount("http://", adapter) - sess.mount("https://", adapter) + sess.mount('http://', adapter) + sess.mount('https://', adapter) return sess diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/__init__.py index 8db1a0e..d9a3bbe 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/__init__.py @@ -1,3 +1,6 @@ -from .core import contents, where +from __future__ import annotations -__version__ = "2021.10.08" +from .core import contents +from .core import where + +__version__ = '2021.10.08' diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/__main__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/__main__.py index 0037634..7ba8042 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/__main__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/__main__.py @@ -1,9 +1,12 @@ +from __future__ import annotations + import argparse -from pip._vendor.certifi import contents, where +from pip._vendor.certifi import contents +from pip._vendor.certifi import where parser = argparse.ArgumentParser() -parser.add_argument("-c", "--contents", action="store_true") +parser.add_argument('-c', '--contents', action='store_true') args = parser.parse_args() if args.contents: diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/core.py b/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/core.py index b8140cf..83118f5 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/core.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/certifi/core.py @@ -1,11 +1,11 @@ -# -*- coding: utf-8 -*- - """ certifi.py ~~~~~~~~~~ This module returns the installation location of cacert.pem or its contents. """ +from __future__ import annotations + import os @@ -17,7 +17,7 @@ try: # Return a certificate file on disk for a standalone pip zipapp running in # an isolated build environment to use. Passing --cert to the standalone # pip does not work since requests calls where() unconditionally on import. - _PIP_STANDALONE_CERT = os.environ.get("_PIP_STANDALONE_CERT") + _PIP_STANDALONE_CERT = os.environ.get('_PIP_STANDALONE_CERT') if _PIP_STANDALONE_CERT: def where(): return _PIP_STANDALONE_CERT @@ -47,7 +47,7 @@ try: # We also have to hold onto the actual context manager, because # it will do the cleanup whenever it gets garbage collected, so # we will also store that at the global level as well. - _CACERT_CTX = get_path("pip._vendor.certifi", "cacert.pem") + _CACERT_CTX = get_path('pip._vendor.certifi', 'cacert.pem') _CACERT_PATH = str(_CACERT_CTX.__enter__()) return _CACERT_PATH @@ -60,8 +60,8 @@ except ImportError: # importlib.resources module but relies on the existing `where` function # so won't address issues with environments like PyOxidizer that don't set # __file__ on modules. - def read_text(_module, _path, encoding="ascii"): - with open(where(), "r", encoding=encoding) as data: + def read_text(_module, _path, encoding='ascii'): + with open(where(), encoding=encoding) as data: return data.read() # If we don't have importlib.resources, then we will just do the old logic @@ -69,8 +69,8 @@ except ImportError: def where(): f = os.path.dirname(__file__) - return os.path.join(f, "cacert.pem") + return os.path.join(f, 'cacert.pem') def contents(): - return read_text("certifi", "cacert.pem", encoding="ascii") + return read_text('certifi', 'cacert.pem', encoding='ascii') diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/__init__.py index 80ad254..c037863 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/__init__.py @@ -14,11 +14,12 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations - -from .universaldetector import UniversalDetector from .enums import InputState -from .version import __version__, VERSION +from .universaldetector import UniversalDetector +from .version import __version__ +from .version import VERSION __all__ = ['UniversalDetector', 'detect', 'detect_all', '__version__', 'VERSION'] @@ -33,8 +34,10 @@ def detect(byte_str): """ if not isinstance(byte_str, bytearray): if not isinstance(byte_str, bytes): - raise TypeError('Expected object of type bytes or bytearray, got: ' - '{}'.format(type(byte_str))) + raise TypeError( + 'Expected object of type bytes or bytearray, got: ' + '{}'.format(type(byte_str)), + ) else: byte_str = bytearray(byte_str) detector = UniversalDetector() @@ -51,8 +54,10 @@ def detect_all(byte_str): """ if not isinstance(byte_str, bytearray): if not isinstance(byte_str, bytes): - raise TypeError('Expected object of type bytes or bytearray, got: ' - '{}'.format(type(byte_str))) + raise TypeError( + 'Expected object of type bytes or bytearray, got: ' + '{}'.format(type(byte_str)), + ) else: byte_str = bytearray(byte_str) @@ -70,8 +75,10 @@ def detect_all(byte_str): # extra Windows-specific bytes if lower_charset_name.startswith('iso-8859'): if detector._has_win_bytes: - charset_name = detector.ISO_WIN_MAP.get(lower_charset_name, - charset_name) + charset_name = detector.ISO_WIN_MAP.get( + lower_charset_name, + charset_name, + ) results.append({ 'encoding': charset_name, 'confidence': prober.get_confidence(), diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/big5freq.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/big5freq.py index 38f3251..1e7cc51 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/big5freq.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/big5freq.py @@ -24,7 +24,6 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### - # Big5 frequency table # by Taiwan's Mandarin Promotion Council # @@ -39,6 +38,7 @@ # Random Distribution Ration = 512/(5401-512)=0.105 # # Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR +from __future__ import annotations BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75 @@ -46,341 +46,340 @@ BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75 BIG5_TABLE_SIZE = 5376 BIG5_CHAR_TO_FREQ_ORDER = ( - 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16 -3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32 -1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48 - 63,5010,5011, 317,1614, 75, 222, 159,4203,2417,1480,5012,3555,3091, 224,2822, # 64 -3682, 3, 10,3973,1471, 29,2787,1135,2866,1940, 873, 130,3275,1123, 312,5013, # 80 -4511,2052, 507, 252, 682,5014, 142,1915, 124, 206,2947, 34,3556,3204, 64, 604, # 96 -5015,2501,1977,1978, 155,1991, 645, 641,1606,5016,3452, 337, 72, 406,5017, 80, # 112 - 630, 238,3205,1509, 263, 939,1092,2654, 756,1440,1094,3453, 449, 69,2987, 591, # 128 - 179,2096, 471, 115,2035,1844, 60, 50,2988, 134, 806,1869, 734,2036,3454, 180, # 144 - 995,1607, 156, 537,2907, 688,5018, 319,1305, 779,2145, 514,2379, 298,4512, 359, # 160 -2502, 90,2716,1338, 663, 11, 906,1099,2553, 20,2441, 182, 532,1716,5019, 732, # 176 -1376,4204,1311,1420,3206, 25,2317,1056, 113, 399, 382,1950, 242,3455,2474, 529, # 192 -3276, 475,1447,3683,5020, 117, 21, 656, 810,1297,2300,2334,3557,5021, 126,4205, # 208 - 706, 456, 150, 613,4513, 71,1118,2037,4206, 145,3092, 85, 835, 486,2115,1246, # 224 -1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,5022,2128,2359, 347,3815, 221, # 240 -3558,3135,5023,1956,1153,4207, 83, 296,1199,3093, 192, 624, 93,5024, 822,1898, # 256 -2823,3136, 795,2065, 991,1554,1542,1592, 27, 43,2867, 859, 139,1456, 860,4514, # 272 - 437, 712,3974, 164,2397,3137, 695, 211,3037,2097, 195,3975,1608,3559,3560,3684, # 288 -3976, 234, 811,2989,2098,3977,2233,1441,3561,1615,2380, 668,2077,1638, 305, 228, # 304 -1664,4515, 467, 415,5025, 262,2099,1593, 239, 108, 300, 200,1033, 512,1247,2078, # 320 -5026,5027,2176,3207,3685,2682, 593, 845,1062,3277, 88,1723,2038,3978,1951, 212, # 336 - 266, 152, 149, 468,1899,4208,4516, 77, 187,5028,3038, 37, 5,2990,5029,3979, # 352 -5030,5031, 39,2524,4517,2908,3208,2079, 55, 148, 74,4518, 545, 483,1474,1029, # 368 -1665, 217,1870,1531,3138,1104,2655,4209, 24, 172,3562, 900,3980,3563,3564,4519, # 384 - 32,1408,2824,1312, 329, 487,2360,2251,2717, 784,2683, 4,3039,3351,1427,1789, # 400 - 188, 109, 499,5032,3686,1717,1790, 888,1217,3040,4520,5033,3565,5034,3352,1520, # 416 -3687,3981, 196,1034, 775,5035,5036, 929,1816, 249, 439, 38,5037,1063,5038, 794, # 432 -3982,1435,2301, 46, 178,3278,2066,5039,2381,5040, 214,1709,4521, 804, 35, 707, # 448 - 324,3688,1601,2554, 140, 459,4210,5041,5042,1365, 839, 272, 978,2262,2580,3456, # 464 -2129,1363,3689,1423, 697, 100,3094, 48, 70,1231, 495,3139,2196,5043,1294,5044, # 480 -2080, 462, 586,1042,3279, 853, 256, 988, 185,2382,3457,1698, 434,1084,5045,3458, # 496 - 314,2625,2788,4522,2335,2336, 569,2285, 637,1817,2525, 757,1162,1879,1616,3459, # 512 - 287,1577,2116, 768,4523,1671,2868,3566,2526,1321,3816, 909,2418,5046,4211, 933, # 528 -3817,4212,2053,2361,1222,4524, 765,2419,1322, 786,4525,5047,1920,1462,1677,2909, # 544 -1699,5048,4526,1424,2442,3140,3690,2600,3353,1775,1941,3460,3983,4213, 309,1369, # 560 -1130,2825, 364,2234,1653,1299,3984,3567,3985,3986,2656, 525,1085,3041, 902,2001, # 576 -1475, 964,4527, 421,1845,1415,1057,2286, 940,1364,3141, 376,4528,4529,1381, 7, # 592 -2527, 983,2383, 336,1710,2684,1846, 321,3461, 559,1131,3042,2752,1809,1132,1313, # 608 - 265,1481,1858,5049, 352,1203,2826,3280, 167,1089, 420,2827, 776, 792,1724,3568, # 624 -4214,2443,3281,5050,4215,5051, 446, 229, 333,2753, 901,3818,1200,1557,4530,2657, # 640 -1921, 395,2754,2685,3819,4216,1836, 125, 916,3209,2626,4531,5052,5053,3820,5054, # 656 -5055,5056,4532,3142,3691,1133,2555,1757,3462,1510,2318,1409,3569,5057,2146, 438, # 672 -2601,2910,2384,3354,1068, 958,3043, 461, 311,2869,2686,4217,1916,3210,4218,1979, # 688 - 383, 750,2755,2627,4219, 274, 539, 385,1278,1442,5058,1154,1965, 384, 561, 210, # 704 - 98,1295,2556,3570,5059,1711,2420,1482,3463,3987,2911,1257, 129,5060,3821, 642, # 720 - 523,2789,2790,2658,5061, 141,2235,1333, 68, 176, 441, 876, 907,4220, 603,2602, # 736 - 710, 171,3464, 404, 549, 18,3143,2398,1410,3692,1666,5062,3571,4533,2912,4534, # 752 -5063,2991, 368,5064, 146, 366, 99, 871,3693,1543, 748, 807,1586,1185, 22,2263, # 768 - 379,3822,3211,5065,3212, 505,1942,2628,1992,1382,2319,5066, 380,2362, 218, 702, # 784 -1818,1248,3465,3044,3572,3355,3282,5067,2992,3694, 930,3283,3823,5068, 59,5069, # 800 - 585, 601,4221, 497,3466,1112,1314,4535,1802,5070,1223,1472,2177,5071, 749,1837, # 816 - 690,1900,3824,1773,3988,1476, 429,1043,1791,2236,2117, 917,4222, 447,1086,1629, # 832 -5072, 556,5073,5074,2021,1654, 844,1090, 105, 550, 966,1758,2828,1008,1783, 686, # 848 -1095,5075,2287, 793,1602,5076,3573,2603,4536,4223,2948,2302,4537,3825, 980,2503, # 864 - 544, 353, 527,4538, 908,2687,2913,5077, 381,2629,1943,1348,5078,1341,1252, 560, # 880 -3095,5079,3467,2870,5080,2054, 973, 886,2081, 143,4539,5081,5082, 157,3989, 496, # 896 -4224, 57, 840, 540,2039,4540,4541,3468,2118,1445, 970,2264,1748,1966,2082,4225, # 912 -3144,1234,1776,3284,2829,3695, 773,1206,2130,1066,2040,1326,3990,1738,1725,4226, # 928 - 279,3145, 51,1544,2604, 423,1578,2131,2067, 173,4542,1880,5083,5084,1583, 264, # 944 - 610,3696,4543,2444, 280, 154,5085,5086,5087,1739, 338,1282,3096, 693,2871,1411, # 960 -1074,3826,2445,5088,4544,5089,5090,1240, 952,2399,5091,2914,1538,2688, 685,1483, # 976 -4227,2475,1436, 953,4228,2055,4545, 671,2400, 79,4229,2446,3285, 608, 567,2689, # 992 -3469,4230,4231,1691, 393,1261,1792,2401,5092,4546,5093,5094,5095,5096,1383,1672, # 1008 -3827,3213,1464, 522,1119, 661,1150, 216, 675,4547,3991,1432,3574, 609,4548,2690, # 1024 -2402,5097,5098,5099,4232,3045, 0,5100,2476, 315, 231,2447, 301,3356,4549,2385, # 1040 -5101, 233,4233,3697,1819,4550,4551,5102, 96,1777,1315,2083,5103, 257,5104,1810, # 1056 -3698,2718,1139,1820,4234,2022,1124,2164,2791,1778,2659,5105,3097, 363,1655,3214, # 1072 -5106,2993,5107,5108,5109,3992,1567,3993, 718, 103,3215, 849,1443, 341,3357,2949, # 1088 -1484,5110,1712, 127, 67, 339,4235,2403, 679,1412, 821,5111,5112, 834, 738, 351, # 1104 -2994,2147, 846, 235,1497,1881, 418,1993,3828,2719, 186,1100,2148,2756,3575,1545, # 1120 -1355,2950,2872,1377, 583,3994,4236,2581,2995,5113,1298,3699,1078,2557,3700,2363, # 1136 - 78,3829,3830, 267,1289,2100,2002,1594,4237, 348, 369,1274,2197,2178,1838,4552, # 1152 -1821,2830,3701,2757,2288,2003,4553,2951,2758, 144,3358, 882,4554,3995,2759,3470, # 1168 -4555,2915,5114,4238,1726, 320,5115,3996,3046, 788,2996,5116,2831,1774,1327,2873, # 1184 -3997,2832,5117,1306,4556,2004,1700,3831,3576,2364,2660, 787,2023, 506, 824,3702, # 1200 - 534, 323,4557,1044,3359,2024,1901, 946,3471,5118,1779,1500,1678,5119,1882,4558, # 1216 - 165, 243,4559,3703,2528, 123, 683,4239, 764,4560, 36,3998,1793, 589,2916, 816, # 1232 - 626,1667,3047,2237,1639,1555,1622,3832,3999,5120,4000,2874,1370,1228,1933, 891, # 1248 -2084,2917, 304,4240,5121, 292,2997,2720,3577, 691,2101,4241,1115,4561, 118, 662, # 1264 -5122, 611,1156, 854,2386,1316,2875, 2, 386, 515,2918,5123,5124,3286, 868,2238, # 1280 -1486, 855,2661, 785,2216,3048,5125,1040,3216,3578,5126,3146, 448,5127,1525,5128, # 1296 -2165,4562,5129,3833,5130,4242,2833,3579,3147, 503, 818,4001,3148,1568, 814, 676, # 1312 -1444, 306,1749,5131,3834,1416,1030, 197,1428, 805,2834,1501,4563,5132,5133,5134, # 1328 -1994,5135,4564,5136,5137,2198, 13,2792,3704,2998,3149,1229,1917,5138,3835,2132, # 1344 -5139,4243,4565,2404,3580,5140,2217,1511,1727,1120,5141,5142, 646,3836,2448, 307, # 1360 -5143,5144,1595,3217,5145,5146,5147,3705,1113,1356,4002,1465,2529,2530,5148, 519, # 1376 -5149, 128,2133, 92,2289,1980,5150,4003,1512, 342,3150,2199,5151,2793,2218,1981, # 1392 -3360,4244, 290,1656,1317, 789, 827,2365,5152,3837,4566, 562, 581,4004,5153, 401, # 1408 -4567,2252, 94,4568,5154,1399,2794,5155,1463,2025,4569,3218,1944,5156, 828,1105, # 1424 -4245,1262,1394,5157,4246, 605,4570,5158,1784,2876,5159,2835, 819,2102, 578,2200, # 1440 -2952,5160,1502, 436,3287,4247,3288,2836,4005,2919,3472,3473,5161,2721,2320,5162, # 1456 -5163,2337,2068, 23,4571, 193, 826,3838,2103, 699,1630,4248,3098, 390,1794,1064, # 1472 -3581,5164,1579,3099,3100,1400,5165,4249,1839,1640,2877,5166,4572,4573, 137,4250, # 1488 - 598,3101,1967, 780, 104, 974,2953,5167, 278, 899, 253, 402, 572, 504, 493,1339, # 1504 -5168,4006,1275,4574,2582,2558,5169,3706,3049,3102,2253, 565,1334,2722, 863, 41, # 1520 -5170,5171,4575,5172,1657,2338, 19, 463,2760,4251, 606,5173,2999,3289,1087,2085, # 1536 -1323,2662,3000,5174,1631,1623,1750,4252,2691,5175,2878, 791,2723,2663,2339, 232, # 1552 -2421,5176,3001,1498,5177,2664,2630, 755,1366,3707,3290,3151,2026,1609, 119,1918, # 1568 -3474, 862,1026,4253,5178,4007,3839,4576,4008,4577,2265,1952,2477,5179,1125, 817, # 1584 -4254,4255,4009,1513,1766,2041,1487,4256,3050,3291,2837,3840,3152,5180,5181,1507, # 1600 -5182,2692, 733, 40,1632,1106,2879, 345,4257, 841,2531, 230,4578,3002,1847,3292, # 1616 -3475,5183,1263, 986,3476,5184, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562, # 1632 -4010,4011,2954, 967,2761,2665,1349, 592,2134,1692,3361,3003,1995,4258,1679,4012, # 1648 -1902,2188,5185, 739,3708,2724,1296,1290,5186,4259,2201,2202,1922,1563,2605,2559, # 1664 -1871,2762,3004,5187, 435,5188, 343,1108, 596, 17,1751,4579,2239,3477,3709,5189, # 1680 -4580, 294,3582,2955,1693, 477, 979, 281,2042,3583, 643,2043,3710,2631,2795,2266, # 1696 -1031,2340,2135,2303,3584,4581, 367,1249,2560,5190,3585,5191,4582,1283,3362,2005, # 1712 - 240,1762,3363,4583,4584, 836,1069,3153, 474,5192,2149,2532, 268,3586,5193,3219, # 1728 -1521,1284,5194,1658,1546,4260,5195,3587,3588,5196,4261,3364,2693,1685,4262, 961, # 1744 -1673,2632, 190,2006,2203,3841,4585,4586,5197, 570,2504,3711,1490,5198,4587,2633, # 1760 -3293,1957,4588, 584,1514, 396,1045,1945,5199,4589,1968,2449,5200,5201,4590,4013, # 1776 - 619,5202,3154,3294, 215,2007,2796,2561,3220,4591,3221,4592, 763,4263,3842,4593, # 1792 -5203,5204,1958,1767,2956,3365,3712,1174, 452,1477,4594,3366,3155,5205,2838,1253, # 1808 -2387,2189,1091,2290,4264, 492,5206, 638,1169,1825,2136,1752,4014, 648, 926,1021, # 1824 -1324,4595, 520,4596, 997, 847,1007, 892,4597,3843,2267,1872,3713,2405,1785,4598, # 1840 -1953,2957,3103,3222,1728,4265,2044,3714,4599,2008,1701,3156,1551, 30,2268,4266, # 1856 -5207,2027,4600,3589,5208, 501,5209,4267, 594,3478,2166,1822,3590,3479,3591,3223, # 1872 - 829,2839,4268,5210,1680,3157,1225,4269,5211,3295,4601,4270,3158,2341,5212,4602, # 1888 -4271,5213,4015,4016,5214,1848,2388,2606,3367,5215,4603, 374,4017, 652,4272,4273, # 1904 - 375,1140, 798,5216,5217,5218,2366,4604,2269, 546,1659, 138,3051,2450,4605,5219, # 1920 -2254, 612,1849, 910, 796,3844,1740,1371, 825,3845,3846,5220,2920,2562,5221, 692, # 1936 - 444,3052,2634, 801,4606,4274,5222,1491, 244,1053,3053,4275,4276, 340,5223,4018, # 1952 -1041,3005, 293,1168, 87,1357,5224,1539, 959,5225,2240, 721, 694,4277,3847, 219, # 1968 -1478, 644,1417,3368,2666,1413,1401,1335,1389,4019,5226,5227,3006,2367,3159,1826, # 1984 - 730,1515, 184,2840, 66,4607,5228,1660,2958, 246,3369, 378,1457, 226,3480, 975, # 2000 -4020,2959,1264,3592, 674, 696,5229, 163,5230,1141,2422,2167, 713,3593,3370,4608, # 2016 -4021,5231,5232,1186, 15,5233,1079,1070,5234,1522,3224,3594, 276,1050,2725, 758, # 2032 -1126, 653,2960,3296,5235,2342, 889,3595,4022,3104,3007, 903,1250,4609,4023,3481, # 2048 -3596,1342,1681,1718, 766,3297, 286, 89,2961,3715,5236,1713,5237,2607,3371,3008, # 2064 -5238,2962,2219,3225,2880,5239,4610,2505,2533, 181, 387,1075,4024, 731,2190,3372, # 2080 -5240,3298, 310, 313,3482,2304, 770,4278, 54,3054, 189,4611,3105,3848,4025,5241, # 2096 -1230,1617,1850, 355,3597,4279,4612,3373, 111,4280,3716,1350,3160,3483,3055,4281, # 2112 -2150,3299,3598,5242,2797,4026,4027,3009, 722,2009,5243,1071, 247,1207,2343,2478, # 2128 -1378,4613,2010, 864,1437,1214,4614, 373,3849,1142,2220, 667,4615, 442,2763,2563, # 2144 -3850,4028,1969,4282,3300,1840, 837, 170,1107, 934,1336,1883,5244,5245,2119,4283, # 2160 -2841, 743,1569,5246,4616,4284, 582,2389,1418,3484,5247,1803,5248, 357,1395,1729, # 2176 -3717,3301,2423,1564,2241,5249,3106,3851,1633,4617,1114,2086,4285,1532,5250, 482, # 2192 -2451,4618,5251,5252,1492, 833,1466,5253,2726,3599,1641,2842,5254,1526,1272,3718, # 2208 -4286,1686,1795, 416,2564,1903,1954,1804,5255,3852,2798,3853,1159,2321,5256,2881, # 2224 -4619,1610,1584,3056,2424,2764, 443,3302,1163,3161,5257,5258,4029,5259,4287,2506, # 2240 -3057,4620,4030,3162,2104,1647,3600,2011,1873,4288,5260,4289, 431,3485,5261, 250, # 2256 - 97, 81,4290,5262,1648,1851,1558, 160, 848,5263, 866, 740,1694,5264,2204,2843, # 2272 -3226,4291,4621,3719,1687, 950,2479, 426, 469,3227,3720,3721,4031,5265,5266,1188, # 2288 - 424,1996, 861,3601,4292,3854,2205,2694, 168,1235,3602,4293,5267,2087,1674,4622, # 2304 -3374,3303, 220,2565,1009,5268,3855, 670,3010, 332,1208, 717,5269,5270,3603,2452, # 2320 -4032,3375,5271, 513,5272,1209,2882,3376,3163,4623,1080,5273,5274,5275,5276,2534, # 2336 -3722,3604, 815,1587,4033,4034,5277,3605,3486,3856,1254,4624,1328,3058,1390,4035, # 2352 -1741,4036,3857,4037,5278, 236,3858,2453,3304,5279,5280,3723,3859,1273,3860,4625, # 2368 -5281, 308,5282,4626, 245,4627,1852,2480,1307,2583, 430, 715,2137,2454,5283, 270, # 2384 - 199,2883,4038,5284,3606,2727,1753, 761,1754, 725,1661,1841,4628,3487,3724,5285, # 2400 -5286, 587, 14,3305, 227,2608, 326, 480,2270, 943,2765,3607, 291, 650,1884,5287, # 2416 -1702,1226, 102,1547, 62,3488, 904,4629,3489,1164,4294,5288,5289,1224,1548,2766, # 2432 - 391, 498,1493,5290,1386,1419,5291,2056,1177,4630, 813, 880,1081,2368, 566,1145, # 2448 -4631,2291,1001,1035,2566,2609,2242, 394,1286,5292,5293,2069,5294, 86,1494,1730, # 2464 -4039, 491,1588, 745, 897,2963, 843,3377,4040,2767,2884,3306,1768, 998,2221,2070, # 2480 - 397,1827,1195,1970,3725,3011,3378, 284,5295,3861,2507,2138,2120,1904,5296,4041, # 2496 -2151,4042,4295,1036,3490,1905, 114,2567,4296, 209,1527,5297,5298,2964,2844,2635, # 2512 -2390,2728,3164, 812,2568,5299,3307,5300,1559, 737,1885,3726,1210, 885, 28,2695, # 2528 -3608,3862,5301,4297,1004,1780,4632,5302, 346,1982,2222,2696,4633,3863,1742, 797, # 2544 -1642,4043,1934,1072,1384,2152, 896,4044,3308,3727,3228,2885,3609,5303,2569,1959, # 2560 -4634,2455,1786,5304,5305,5306,4045,4298,1005,1308,3728,4299,2729,4635,4636,1528, # 2576 -2610, 161,1178,4300,1983, 987,4637,1101,4301, 631,4046,1157,3229,2425,1343,1241, # 2592 -1016,2243,2570, 372, 877,2344,2508,1160, 555,1935, 911,4047,5307, 466,1170, 169, # 2608 -1051,2921,2697,3729,2481,3012,1182,2012,2571,1251,2636,5308, 992,2345,3491,1540, # 2624 -2730,1201,2071,2406,1997,2482,5309,4638, 528,1923,2191,1503,1874,1570,2369,3379, # 2640 -3309,5310, 557,1073,5311,1828,3492,2088,2271,3165,3059,3107, 767,3108,2799,4639, # 2656 -1006,4302,4640,2346,1267,2179,3730,3230, 778,4048,3231,2731,1597,2667,5312,4641, # 2672 -5313,3493,5314,5315,5316,3310,2698,1433,3311, 131, 95,1504,4049, 723,4303,3166, # 2688 -1842,3610,2768,2192,4050,2028,2105,3731,5317,3013,4051,1218,5318,3380,3232,4052, # 2704 -4304,2584, 248,1634,3864, 912,5319,2845,3732,3060,3865, 654, 53,5320,3014,5321, # 2720 -1688,4642, 777,3494,1032,4053,1425,5322, 191, 820,2121,2846, 971,4643, 931,3233, # 2736 - 135, 664, 783,3866,1998, 772,2922,1936,4054,3867,4644,2923,3234, 282,2732, 640, # 2752 -1372,3495,1127, 922, 325,3381,5323,5324, 711,2045,5325,5326,4055,2223,2800,1937, # 2768 -4056,3382,2224,2255,3868,2305,5327,4645,3869,1258,3312,4057,3235,2139,2965,4058, # 2784 -4059,5328,2225, 258,3236,4646, 101,1227,5329,3313,1755,5330,1391,3314,5331,2924, # 2800 -2057, 893,5332,5333,5334,1402,4305,2347,5335,5336,3237,3611,5337,5338, 878,1325, # 2816 -1781,2801,4647, 259,1385,2585, 744,1183,2272,4648,5339,4060,2509,5340, 684,1024, # 2832 -4306,5341, 472,3612,3496,1165,3315,4061,4062, 322,2153, 881, 455,1695,1152,1340, # 2848 - 660, 554,2154,4649,1058,4650,4307, 830,1065,3383,4063,4651,1924,5342,1703,1919, # 2864 -5343, 932,2273, 122,5344,4652, 947, 677,5345,3870,2637, 297,1906,1925,2274,4653, # 2880 -2322,3316,5346,5347,4308,5348,4309, 84,4310, 112, 989,5349, 547,1059,4064, 701, # 2896 -3613,1019,5350,4311,5351,3497, 942, 639, 457,2306,2456, 993,2966, 407, 851, 494, # 2912 -4654,3384, 927,5352,1237,5353,2426,3385, 573,4312, 680, 921,2925,1279,1875, 285, # 2928 - 790,1448,1984, 719,2168,5354,5355,4655,4065,4066,1649,5356,1541, 563,5357,1077, # 2944 -5358,3386,3061,3498, 511,3015,4067,4068,3733,4069,1268,2572,3387,3238,4656,4657, # 2960 -5359, 535,1048,1276,1189,2926,2029,3167,1438,1373,2847,2967,1134,2013,5360,4313, # 2976 -1238,2586,3109,1259,5361, 700,5362,2968,3168,3734,4314,5363,4315,1146,1876,1907, # 2992 -4658,2611,4070, 781,2427, 132,1589, 203, 147, 273,2802,2407, 898,1787,2155,4071, # 3008 -4072,5364,3871,2803,5365,5366,4659,4660,5367,3239,5368,1635,3872, 965,5369,1805, # 3024 -2699,1516,3614,1121,1082,1329,3317,4073,1449,3873, 65,1128,2848,2927,2769,1590, # 3040 -3874,5370,5371, 12,2668, 45, 976,2587,3169,4661, 517,2535,1013,1037,3240,5372, # 3056 -3875,2849,5373,3876,5374,3499,5375,2612, 614,1999,2323,3877,3110,2733,2638,5376, # 3072 -2588,4316, 599,1269,5377,1811,3735,5378,2700,3111, 759,1060, 489,1806,3388,3318, # 3088 -1358,5379,5380,2391,1387,1215,2639,2256, 490,5381,5382,4317,1759,2392,2348,5383, # 3104 -4662,3878,1908,4074,2640,1807,3241,4663,3500,3319,2770,2349, 874,5384,5385,3501, # 3120 -3736,1859, 91,2928,3737,3062,3879,4664,5386,3170,4075,2669,5387,3502,1202,1403, # 3136 -3880,2969,2536,1517,2510,4665,3503,2511,5388,4666,5389,2701,1886,1495,1731,4076, # 3152 -2370,4667,5390,2030,5391,5392,4077,2702,1216, 237,2589,4318,2324,4078,3881,4668, # 3168 -4669,2703,3615,3504, 445,4670,5393,5394,5395,5396,2771, 61,4079,3738,1823,4080, # 3184 -5397, 687,2046, 935, 925, 405,2670, 703,1096,1860,2734,4671,4081,1877,1367,2704, # 3200 -3389, 918,2106,1782,2483, 334,3320,1611,1093,4672, 564,3171,3505,3739,3390, 945, # 3216 -2641,2058,4673,5398,1926, 872,4319,5399,3506,2705,3112, 349,4320,3740,4082,4674, # 3232 -3882,4321,3741,2156,4083,4675,4676,4322,4677,2408,2047, 782,4084, 400, 251,4323, # 3248 -1624,5400,5401, 277,3742, 299,1265, 476,1191,3883,2122,4324,4325,1109, 205,5402, # 3264 -2590,1000,2157,3616,1861,5403,5404,5405,4678,5406,4679,2573, 107,2484,2158,4085, # 3280 -3507,3172,5407,1533, 541,1301, 158, 753,4326,2886,3617,5408,1696, 370,1088,4327, # 3296 -4680,3618, 579, 327, 440, 162,2244, 269,1938,1374,3508, 968,3063, 56,1396,3113, # 3312 -2107,3321,3391,5409,1927,2159,4681,3016,5410,3619,5411,5412,3743,4682,2485,5413, # 3328 -2804,5414,1650,4683,5415,2613,5416,5417,4086,2671,3392,1149,3393,4087,3884,4088, # 3344 -5418,1076, 49,5419, 951,3242,3322,3323, 450,2850, 920,5420,1812,2805,2371,4328, # 3360 -1909,1138,2372,3885,3509,5421,3243,4684,1910,1147,1518,2428,4685,3886,5422,4686, # 3376 -2393,2614, 260,1796,3244,5423,5424,3887,3324, 708,5425,3620,1704,5426,3621,1351, # 3392 -1618,3394,3017,1887, 944,4329,3395,4330,3064,3396,4331,5427,3744, 422, 413,1714, # 3408 -3325, 500,2059,2350,4332,2486,5428,1344,1911, 954,5429,1668,5430,5431,4089,2409, # 3424 -4333,3622,3888,4334,5432,2307,1318,2512,3114, 133,3115,2887,4687, 629, 31,2851, # 3440 -2706,3889,4688, 850, 949,4689,4090,2970,1732,2089,4335,1496,1853,5433,4091, 620, # 3456 -3245, 981,1242,3745,3397,1619,3746,1643,3326,2140,2457,1971,1719,3510,2169,5434, # 3472 -3246,5435,5436,3398,1829,5437,1277,4690,1565,2048,5438,1636,3623,3116,5439, 869, # 3488 -2852, 655,3890,3891,3117,4092,3018,3892,1310,3624,4691,5440,5441,5442,1733, 558, # 3504 -4692,3747, 335,1549,3065,1756,4336,3748,1946,3511,1830,1291,1192, 470,2735,2108, # 3520 -2806, 913,1054,4093,5443,1027,5444,3066,4094,4693, 982,2672,3399,3173,3512,3247, # 3536 -3248,1947,2807,5445, 571,4694,5446,1831,5447,3625,2591,1523,2429,5448,2090, 984, # 3552 -4695,3749,1960,5449,3750, 852, 923,2808,3513,3751, 969,1519, 999,2049,2325,1705, # 3568 -5450,3118, 615,1662, 151, 597,4095,2410,2326,1049, 275,4696,3752,4337, 568,3753, # 3584 -3626,2487,4338,3754,5451,2430,2275, 409,3249,5452,1566,2888,3514,1002, 769,2853, # 3600 - 194,2091,3174,3755,2226,3327,4339, 628,1505,5453,5454,1763,2180,3019,4096, 521, # 3616 -1161,2592,1788,2206,2411,4697,4097,1625,4340,4341, 412, 42,3119, 464,5455,2642, # 3632 -4698,3400,1760,1571,2889,3515,2537,1219,2207,3893,2643,2141,2373,4699,4700,3328, # 3648 -1651,3401,3627,5456,5457,3628,2488,3516,5458,3756,5459,5460,2276,2092, 460,5461, # 3664 -4701,5462,3020, 962, 588,3629, 289,3250,2644,1116, 52,5463,3067,1797,5464,5465, # 3680 -5466,1467,5467,1598,1143,3757,4342,1985,1734,1067,4702,1280,3402, 465,4703,1572, # 3696 - 510,5468,1928,2245,1813,1644,3630,5469,4704,3758,5470,5471,2673,1573,1534,5472, # 3712 -5473, 536,1808,1761,3517,3894,3175,2645,5474,5475,5476,4705,3518,2929,1912,2809, # 3728 -5477,3329,1122, 377,3251,5478, 360,5479,5480,4343,1529, 551,5481,2060,3759,1769, # 3744 -2431,5482,2930,4344,3330,3120,2327,2109,2031,4706,1404, 136,1468,1479, 672,1171, # 3760 -3252,2308, 271,3176,5483,2772,5484,2050, 678,2736, 865,1948,4707,5485,2014,4098, # 3776 -2971,5486,2737,2227,1397,3068,3760,4708,4709,1735,2931,3403,3631,5487,3895, 509, # 3792 -2854,2458,2890,3896,5488,5489,3177,3178,4710,4345,2538,4711,2309,1166,1010, 552, # 3808 - 681,1888,5490,5491,2972,2973,4099,1287,1596,1862,3179, 358, 453, 736, 175, 478, # 3824 -1117, 905,1167,1097,5492,1854,1530,5493,1706,5494,2181,3519,2292,3761,3520,3632, # 3840 -4346,2093,4347,5495,3404,1193,2489,4348,1458,2193,2208,1863,1889,1421,3331,2932, # 3856 -3069,2182,3521, 595,2123,5496,4100,5497,5498,4349,1707,2646, 223,3762,1359, 751, # 3872 -3121, 183,3522,5499,2810,3021, 419,2374, 633, 704,3897,2394, 241,5500,5501,5502, # 3888 - 838,3022,3763,2277,2773,2459,3898,1939,2051,4101,1309,3122,2246,1181,5503,1136, # 3904 -2209,3899,2375,1446,4350,2310,4712,5504,5505,4351,1055,2615, 484,3764,5506,4102, # 3920 - 625,4352,2278,3405,1499,4353,4103,5507,4104,4354,3253,2279,2280,3523,5508,5509, # 3936 -2774, 808,2616,3765,3406,4105,4355,3123,2539, 526,3407,3900,4356, 955,5510,1620, # 3952 -4357,2647,2432,5511,1429,3766,1669,1832, 994, 928,5512,3633,1260,5513,5514,5515, # 3968 -1949,2293, 741,2933,1626,4358,2738,2460, 867,1184, 362,3408,1392,5516,5517,4106, # 3984 -4359,1770,1736,3254,2934,4713,4714,1929,2707,1459,1158,5518,3070,3409,2891,1292, # 4000 -1930,2513,2855,3767,1986,1187,2072,2015,2617,4360,5519,2574,2514,2170,3768,2490, # 4016 -3332,5520,3769,4715,5521,5522, 666,1003,3023,1022,3634,4361,5523,4716,1814,2257, # 4032 - 574,3901,1603, 295,1535, 705,3902,4362, 283, 858, 417,5524,5525,3255,4717,4718, # 4048 -3071,1220,1890,1046,2281,2461,4107,1393,1599, 689,2575, 388,4363,5526,2491, 802, # 4064 -5527,2811,3903,2061,1405,2258,5528,4719,3904,2110,1052,1345,3256,1585,5529, 809, # 4080 -5530,5531,5532, 575,2739,3524, 956,1552,1469,1144,2328,5533,2329,1560,2462,3635, # 4096 -3257,4108, 616,2210,4364,3180,2183,2294,5534,1833,5535,3525,4720,5536,1319,3770, # 4112 -3771,1211,3636,1023,3258,1293,2812,5537,5538,5539,3905, 607,2311,3906, 762,2892, # 4128 -1439,4365,1360,4721,1485,3072,5540,4722,1038,4366,1450,2062,2648,4367,1379,4723, # 4144 -2593,5541,5542,4368,1352,1414,2330,2935,1172,5543,5544,3907,3908,4724,1798,1451, # 4160 -5545,5546,5547,5548,2936,4109,4110,2492,2351, 411,4111,4112,3637,3333,3124,4725, # 4176 -1561,2674,1452,4113,1375,5549,5550, 47,2974, 316,5551,1406,1591,2937,3181,5552, # 4192 -1025,2142,3125,3182, 354,2740, 884,2228,4369,2412, 508,3772, 726,3638, 996,2433, # 4208 -3639, 729,5553, 392,2194,1453,4114,4726,3773,5554,5555,2463,3640,2618,1675,2813, # 4224 - 919,2352,2975,2353,1270,4727,4115, 73,5556,5557, 647,5558,3259,2856,2259,1550, # 4240 -1346,3024,5559,1332, 883,3526,5560,5561,5562,5563,3334,2775,5564,1212, 831,1347, # 4256 -4370,4728,2331,3909,1864,3073, 720,3910,4729,4730,3911,5565,4371,5566,5567,4731, # 4272 -5568,5569,1799,4732,3774,2619,4733,3641,1645,2376,4734,5570,2938, 669,2211,2675, # 4288 -2434,5571,2893,5572,5573,1028,3260,5574,4372,2413,5575,2260,1353,5576,5577,4735, # 4304 -3183, 518,5578,4116,5579,4373,1961,5580,2143,4374,5581,5582,3025,2354,2355,3912, # 4320 - 516,1834,1454,4117,2708,4375,4736,2229,2620,1972,1129,3642,5583,2776,5584,2976, # 4336 -1422, 577,1470,3026,1524,3410,5585,5586, 432,4376,3074,3527,5587,2594,1455,2515, # 4352 -2230,1973,1175,5588,1020,2741,4118,3528,4737,5589,2742,5590,1743,1361,3075,3529, # 4368 -2649,4119,4377,4738,2295, 895, 924,4378,2171, 331,2247,3076, 166,1627,3077,1098, # 4384 -5591,1232,2894,2231,3411,4739, 657, 403,1196,2377, 542,3775,3412,1600,4379,3530, # 4400 -5592,4740,2777,3261, 576, 530,1362,4741,4742,2540,2676,3776,4120,5593, 842,3913, # 4416 -5594,2814,2032,1014,4121, 213,2709,3413, 665, 621,4380,5595,3777,2939,2435,5596, # 4432 -2436,3335,3643,3414,4743,4381,2541,4382,4744,3644,1682,4383,3531,1380,5597, 724, # 4448 -2282, 600,1670,5598,1337,1233,4745,3126,2248,5599,1621,4746,5600, 651,4384,5601, # 4464 -1612,4385,2621,5602,2857,5603,2743,2312,3078,5604, 716,2464,3079, 174,1255,2710, # 4480 -4122,3645, 548,1320,1398, 728,4123,1574,5605,1891,1197,3080,4124,5606,3081,3082, # 4496 -3778,3646,3779, 747,5607, 635,4386,4747,5608,5609,5610,4387,5611,5612,4748,5613, # 4512 -3415,4749,2437, 451,5614,3780,2542,2073,4388,2744,4389,4125,5615,1764,4750,5616, # 4528 -4390, 350,4751,2283,2395,2493,5617,4391,4126,2249,1434,4127, 488,4752, 458,4392, # 4544 -4128,3781, 771,1330,2396,3914,2576,3184,2160,2414,1553,2677,3185,4393,5618,2494, # 4560 -2895,2622,1720,2711,4394,3416,4753,5619,2543,4395,5620,3262,4396,2778,5621,2016, # 4576 -2745,5622,1155,1017,3782,3915,5623,3336,2313, 201,1865,4397,1430,5624,4129,5625, # 4592 -5626,5627,5628,5629,4398,1604,5630, 414,1866, 371,2595,4754,4755,3532,2017,3127, # 4608 -4756,1708, 960,4399, 887, 389,2172,1536,1663,1721,5631,2232,4130,2356,2940,1580, # 4624 -5632,5633,1744,4757,2544,4758,4759,5634,4760,5635,2074,5636,4761,3647,3417,2896, # 4640 -4400,5637,4401,2650,3418,2815, 673,2712,2465, 709,3533,4131,3648,4402,5638,1148, # 4656 - 502, 634,5639,5640,1204,4762,3649,1575,4763,2623,3783,5641,3784,3128, 948,3263, # 4672 - 121,1745,3916,1110,5642,4403,3083,2516,3027,4132,3785,1151,1771,3917,1488,4133, # 4688 -1987,5643,2438,3534,5644,5645,2094,5646,4404,3918,1213,1407,2816, 531,2746,2545, # 4704 -3264,1011,1537,4764,2779,4405,3129,1061,5647,3786,3787,1867,2897,5648,2018, 120, # 4720 -4406,4407,2063,3650,3265,2314,3919,2678,3419,1955,4765,4134,5649,3535,1047,2713, # 4736 -1266,5650,1368,4766,2858, 649,3420,3920,2546,2747,1102,2859,2679,5651,5652,2000, # 4752 -5653,1111,3651,2977,5654,2495,3921,3652,2817,1855,3421,3788,5655,5656,3422,2415, # 4768 -2898,3337,3266,3653,5657,2577,5658,3654,2818,4135,1460, 856,5659,3655,5660,2899, # 4784 -2978,5661,2900,3922,5662,4408, 632,2517, 875,3923,1697,3924,2296,5663,5664,4767, # 4800 -3028,1239, 580,4768,4409,5665, 914, 936,2075,1190,4136,1039,2124,5666,5667,5668, # 4816 -5669,3423,1473,5670,1354,4410,3925,4769,2173,3084,4137, 915,3338,4411,4412,3339, # 4832 -1605,1835,5671,2748, 398,3656,4413,3926,4138, 328,1913,2860,4139,3927,1331,4414, # 4848 -3029, 937,4415,5672,3657,4140,4141,3424,2161,4770,3425, 524, 742, 538,3085,1012, # 4864 -5673,5674,3928,2466,5675, 658,1103, 225,3929,5676,5677,4771,5678,4772,5679,3267, # 4880 -1243,5680,4142, 963,2250,4773,5681,2714,3658,3186,5682,5683,2596,2332,5684,4774, # 4896 -5685,5686,5687,3536, 957,3426,2547,2033,1931,2941,2467, 870,2019,3659,1746,2780, # 4912 -2781,2439,2468,5688,3930,5689,3789,3130,3790,3537,3427,3791,5690,1179,3086,5691, # 4928 -3187,2378,4416,3792,2548,3188,3131,2749,4143,5692,3428,1556,2549,2297, 977,2901, # 4944 -2034,4144,1205,3429,5693,1765,3430,3189,2125,1271, 714,1689,4775,3538,5694,2333, # 4960 -3931, 533,4417,3660,2184, 617,5695,2469,3340,3539,2315,5696,5697,3190,5698,5699, # 4976 -3932,1988, 618, 427,2651,3540,3431,5700,5701,1244,1690,5702,2819,4418,4776,5703, # 4992 -3541,4777,5704,2284,1576, 473,3661,4419,3432, 972,5705,3662,5706,3087,5707,5708, # 5008 -4778,4779,5709,3793,4145,4146,5710, 153,4780, 356,5711,1892,2902,4420,2144, 408, # 5024 - 803,2357,5712,3933,5713,4421,1646,2578,2518,4781,4782,3934,5714,3935,4422,5715, # 5040 -2416,3433, 752,5716,5717,1962,3341,2979,5718, 746,3030,2470,4783,4423,3794, 698, # 5056 -4784,1893,4424,3663,2550,4785,3664,3936,5719,3191,3434,5720,1824,1302,4147,2715, # 5072 -3937,1974,4425,5721,4426,3192, 823,1303,1288,1236,2861,3542,4148,3435, 774,3938, # 5088 -5722,1581,4786,1304,2862,3939,4787,5723,2440,2162,1083,3268,4427,4149,4428, 344, # 5104 -1173, 288,2316, 454,1683,5724,5725,1461,4788,4150,2597,5726,5727,4789, 985, 894, # 5120 -5728,3436,3193,5729,1914,2942,3795,1989,5730,2111,1975,5731,4151,5732,2579,1194, # 5136 - 425,5733,4790,3194,1245,3796,4429,5734,5735,2863,5736, 636,4791,1856,3940, 760, # 5152 -1800,5737,4430,2212,1508,4792,4152,1894,1684,2298,5738,5739,4793,4431,4432,2213, # 5168 - 479,5740,5741, 832,5742,4153,2496,5743,2980,2497,3797, 990,3132, 627,1815,2652, # 5184 -4433,1582,4434,2126,2112,3543,4794,5744, 799,4435,3195,5745,4795,2113,1737,3031, # 5200 -1018, 543, 754,4436,3342,1676,4796,4797,4154,4798,1489,5746,3544,5747,2624,2903, # 5216 -4155,5748,5749,2981,5750,5751,5752,5753,3196,4799,4800,2185,1722,5754,3269,3270, # 5232 -1843,3665,1715, 481, 365,1976,1857,5755,5756,1963,2498,4801,5757,2127,3666,3271, # 5248 - 433,1895,2064,2076,5758, 602,2750,5759,5760,5761,5762,5763,3032,1628,3437,5764, # 5264 -3197,4802,4156,2904,4803,2519,5765,2551,2782,5766,5767,5768,3343,4804,2905,5769, # 5280 -4805,5770,2864,4806,4807,1221,2982,4157,2520,5771,5772,5773,1868,1990,5774,5775, # 5296 -5776,1896,5777,5778,4808,1897,4158, 318,5779,2095,4159,4437,5780,5781, 485,5782, # 5312 - 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328 -3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344 - 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360 -2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 + 1, 1801, 1506, 255, 1431, 198, 9, 82, 6, 5008, 177, 202, 3681, 1256, 2821, 110, # 16 + 3814, 33, 3274, 261, 76, 44, 2114, 16, 2946, 2187, 1176, 659, 3971, 26, 3451, 2653, # 32 + 1198, 3972, 3350, 4202, 410, 2215, 302, 590, 361, 1964, 8, 204, 58, 4510, 5009, 1932, # 48 + 63, 5010, 5011, 317, 1614, 75, 222, 159, 4203, 2417, 1480, 5012, 3555, 3091, 224, 2822, # 64 + 3682, 3, 10, 3973, 1471, 29, 2787, 1135, 2866, 1940, 873, 130, 3275, 1123, 312, 5013, # 80 + 4511, 2052, 507, 252, 682, 5014, 142, 1915, 124, 206, 2947, 34, 3556, 3204, 64, 604, # 96 + 5015, 2501, 1977, 1978, 155, 1991, 645, 641, 1606, 5016, 3452, 337, 72, 406, 5017, 80, # 112 + 630, 238, 3205, 1509, 263, 939, 1092, 2654, 756, 1440, 1094, 3453, 449, 69, 2987, 591, # 128 + 179, 2096, 471, 115, 2035, 1844, 60, 50, 2988, 134, 806, 1869, 734, 2036, 3454, 180, # 144 + 995, 1607, 156, 537, 2907, 688, 5018, 319, 1305, 779, 2145, 514, 2379, 298, 4512, 359, # 160 + 2502, 90, 2716, 1338, 663, 11, 906, 1099, 2553, 20, 2441, 182, 532, 1716, 5019, 732, # 176 + 1376, 4204, 1311, 1420, 3206, 25, 2317, 1056, 113, 399, 382, 1950, 242, 3455, 2474, 529, # 192 + 3276, 475, 1447, 3683, 5020, 117, 21, 656, 810, 1297, 2300, 2334, 3557, 5021, 126, 4205, # 208 + 706, 456, 150, 613, 4513, 71, 1118, 2037, 4206, 145, 3092, 85, 835, 486, 2115, 1246, # 224 + 1426, 428, 727, 1285, 1015, 800, 106, 623, 303, 1281, 5022, 2128, 2359, 347, 3815, 221, # 240 + 3558, 3135, 5023, 1956, 1153, 4207, 83, 296, 1199, 3093, 192, 624, 93, 5024, 822, 1898, # 256 + 2823, 3136, 795, 2065, 991, 1554, 1542, 1592, 27, 43, 2867, 859, 139, 1456, 860, 4514, # 272 + 437, 712, 3974, 164, 2397, 3137, 695, 211, 3037, 2097, 195, 3975, 1608, 3559, 3560, 3684, # 288 + 3976, 234, 811, 2989, 2098, 3977, 2233, 1441, 3561, 1615, 2380, 668, 2077, 1638, 305, 228, # 304 + 1664, 4515, 467, 415, 5025, 262, 2099, 1593, 239, 108, 300, 200, 1033, 512, 1247, 2078, # 320 + 5026, 5027, 2176, 3207, 3685, 2682, 593, 845, 1062, 3277, 88, 1723, 2038, 3978, 1951, 212, # 336 + 266, 152, 149, 468, 1899, 4208, 4516, 77, 187, 5028, 3038, 37, 5, 2990, 5029, 3979, # 352 + 5030, 5031, 39, 2524, 4517, 2908, 3208, 2079, 55, 148, 74, 4518, 545, 483, 1474, 1029, # 368 + 1665, 217, 1870, 1531, 3138, 1104, 2655, 4209, 24, 172, 3562, 900, 3980, 3563, 3564, 4519, # 384 + 32, 1408, 2824, 1312, 329, 487, 2360, 2251, 2717, 784, 2683, 4, 3039, 3351, 1427, 1789, # 400 + 188, 109, 499, 5032, 3686, 1717, 1790, 888, 1217, 3040, 4520, 5033, 3565, 5034, 3352, 1520, # 416 + 3687, 3981, 196, 1034, 775, 5035, 5036, 929, 1816, 249, 439, 38, 5037, 1063, 5038, 794, # 432 + 3982, 1435, 2301, 46, 178, 3278, 2066, 5039, 2381, 5040, 214, 1709, 4521, 804, 35, 707, # 448 + 324, 3688, 1601, 2554, 140, 459, 4210, 5041, 5042, 1365, 839, 272, 978, 2262, 2580, 3456, # 464 + 2129, 1363, 3689, 1423, 697, 100, 3094, 48, 70, 1231, 495, 3139, 2196, 5043, 1294, 5044, # 480 + 2080, 462, 586, 1042, 3279, 853, 256, 988, 185, 2382, 3457, 1698, 434, 1084, 5045, 3458, # 496 + 314, 2625, 2788, 4522, 2335, 2336, 569, 2285, 637, 1817, 2525, 757, 1162, 1879, 1616, 3459, # 512 + 287, 1577, 2116, 768, 4523, 1671, 2868, 3566, 2526, 1321, 3816, 909, 2418, 5046, 4211, 933, # 528 + 3817, 4212, 2053, 2361, 1222, 4524, 765, 2419, 1322, 786, 4525, 5047, 1920, 1462, 1677, 2909, # 544 + 1699, 5048, 4526, 1424, 2442, 3140, 3690, 2600, 3353, 1775, 1941, 3460, 3983, 4213, 309, 1369, # 560 + 1130, 2825, 364, 2234, 1653, 1299, 3984, 3567, 3985, 3986, 2656, 525, 1085, 3041, 902, 2001, # 576 + 1475, 964, 4527, 421, 1845, 1415, 1057, 2286, 940, 1364, 3141, 376, 4528, 4529, 1381, 7, # 592 + 2527, 983, 2383, 336, 1710, 2684, 1846, 321, 3461, 559, 1131, 3042, 2752, 1809, 1132, 1313, # 608 + 265, 1481, 1858, 5049, 352, 1203, 2826, 3280, 167, 1089, 420, 2827, 776, 792, 1724, 3568, # 624 + 4214, 2443, 3281, 5050, 4215, 5051, 446, 229, 333, 2753, 901, 3818, 1200, 1557, 4530, 2657, # 640 + 1921, 395, 2754, 2685, 3819, 4216, 1836, 125, 916, 3209, 2626, 4531, 5052, 5053, 3820, 5054, # 656 + 5055, 5056, 4532, 3142, 3691, 1133, 2555, 1757, 3462, 1510, 2318, 1409, 3569, 5057, 2146, 438, # 672 + 2601, 2910, 2384, 3354, 1068, 958, 3043, 461, 311, 2869, 2686, 4217, 1916, 3210, 4218, 1979, # 688 + 383, 750, 2755, 2627, 4219, 274, 539, 385, 1278, 1442, 5058, 1154, 1965, 384, 561, 210, # 704 + 98, 1295, 2556, 3570, 5059, 1711, 2420, 1482, 3463, 3987, 2911, 1257, 129, 5060, 3821, 642, # 720 + 523, 2789, 2790, 2658, 5061, 141, 2235, 1333, 68, 176, 441, 876, 907, 4220, 603, 2602, # 736 + 710, 171, 3464, 404, 549, 18, 3143, 2398, 1410, 3692, 1666, 5062, 3571, 4533, 2912, 4534, # 752 + 5063, 2991, 368, 5064, 146, 366, 99, 871, 3693, 1543, 748, 807, 1586, 1185, 22, 2263, # 768 + 379, 3822, 3211, 5065, 3212, 505, 1942, 2628, 1992, 1382, 2319, 5066, 380, 2362, 218, 702, # 784 + 1818, 1248, 3465, 3044, 3572, 3355, 3282, 5067, 2992, 3694, 930, 3283, 3823, 5068, 59, 5069, # 800 + 585, 601, 4221, 497, 3466, 1112, 1314, 4535, 1802, 5070, 1223, 1472, 2177, 5071, 749, 1837, # 816 + 690, 1900, 3824, 1773, 3988, 1476, 429, 1043, 1791, 2236, 2117, 917, 4222, 447, 1086, 1629, # 832 + 5072, 556, 5073, 5074, 2021, 1654, 844, 1090, 105, 550, 966, 1758, 2828, 1008, 1783, 686, # 848 + 1095, 5075, 2287, 793, 1602, 5076, 3573, 2603, 4536, 4223, 2948, 2302, 4537, 3825, 980, 2503, # 864 + 544, 353, 527, 4538, 908, 2687, 2913, 5077, 381, 2629, 1943, 1348, 5078, 1341, 1252, 560, # 880 + 3095, 5079, 3467, 2870, 5080, 2054, 973, 886, 2081, 143, 4539, 5081, 5082, 157, 3989, 496, # 896 + 4224, 57, 840, 540, 2039, 4540, 4541, 3468, 2118, 1445, 970, 2264, 1748, 1966, 2082, 4225, # 912 + 3144, 1234, 1776, 3284, 2829, 3695, 773, 1206, 2130, 1066, 2040, 1326, 3990, 1738, 1725, 4226, # 928 + 279, 3145, 51, 1544, 2604, 423, 1578, 2131, 2067, 173, 4542, 1880, 5083, 5084, 1583, 264, # 944 + 610, 3696, 4543, 2444, 280, 154, 5085, 5086, 5087, 1739, 338, 1282, 3096, 693, 2871, 1411, # 960 + 1074, 3826, 2445, 5088, 4544, 5089, 5090, 1240, 952, 2399, 5091, 2914, 1538, 2688, 685, 1483, # 976 + 4227, 2475, 1436, 953, 4228, 2055, 4545, 671, 2400, 79, 4229, 2446, 3285, 608, 567, 2689, # 992 + 3469, 4230, 4231, 1691, 393, 1261, 1792, 2401, 5092, 4546, 5093, 5094, 5095, 5096, 1383, 1672, # 1008 + 3827, 3213, 1464, 522, 1119, 661, 1150, 216, 675, 4547, 3991, 1432, 3574, 609, 4548, 2690, # 1024 + 2402, 5097, 5098, 5099, 4232, 3045, 0, 5100, 2476, 315, 231, 2447, 301, 3356, 4549, 2385, # 1040 + 5101, 233, 4233, 3697, 1819, 4550, 4551, 5102, 96, 1777, 1315, 2083, 5103, 257, 5104, 1810, # 1056 + 3698, 2718, 1139, 1820, 4234, 2022, 1124, 2164, 2791, 1778, 2659, 5105, 3097, 363, 1655, 3214, # 1072 + 5106, 2993, 5107, 5108, 5109, 3992, 1567, 3993, 718, 103, 3215, 849, 1443, 341, 3357, 2949, # 1088 + 1484, 5110, 1712, 127, 67, 339, 4235, 2403, 679, 1412, 821, 5111, 5112, 834, 738, 351, # 1104 + 2994, 2147, 846, 235, 1497, 1881, 418, 1993, 3828, 2719, 186, 1100, 2148, 2756, 3575, 1545, # 1120 + 1355, 2950, 2872, 1377, 583, 3994, 4236, 2581, 2995, 5113, 1298, 3699, 1078, 2557, 3700, 2363, # 1136 + 78, 3829, 3830, 267, 1289, 2100, 2002, 1594, 4237, 348, 369, 1274, 2197, 2178, 1838, 4552, # 1152 + 1821, 2830, 3701, 2757, 2288, 2003, 4553, 2951, 2758, 144, 3358, 882, 4554, 3995, 2759, 3470, # 1168 + 4555, 2915, 5114, 4238, 1726, 320, 5115, 3996, 3046, 788, 2996, 5116, 2831, 1774, 1327, 2873, # 1184 + 3997, 2832, 5117, 1306, 4556, 2004, 1700, 3831, 3576, 2364, 2660, 787, 2023, 506, 824, 3702, # 1200 + 534, 323, 4557, 1044, 3359, 2024, 1901, 946, 3471, 5118, 1779, 1500, 1678, 5119, 1882, 4558, # 1216 + 165, 243, 4559, 3703, 2528, 123, 683, 4239, 764, 4560, 36, 3998, 1793, 589, 2916, 816, # 1232 + 626, 1667, 3047, 2237, 1639, 1555, 1622, 3832, 3999, 5120, 4000, 2874, 1370, 1228, 1933, 891, # 1248 + 2084, 2917, 304, 4240, 5121, 292, 2997, 2720, 3577, 691, 2101, 4241, 1115, 4561, 118, 662, # 1264 + 5122, 611, 1156, 854, 2386, 1316, 2875, 2, 386, 515, 2918, 5123, 5124, 3286, 868, 2238, # 1280 + 1486, 855, 2661, 785, 2216, 3048, 5125, 1040, 3216, 3578, 5126, 3146, 448, 5127, 1525, 5128, # 1296 + 2165, 4562, 5129, 3833, 5130, 4242, 2833, 3579, 3147, 503, 818, 4001, 3148, 1568, 814, 676, # 1312 + 1444, 306, 1749, 5131, 3834, 1416, 1030, 197, 1428, 805, 2834, 1501, 4563, 5132, 5133, 5134, # 1328 + 1994, 5135, 4564, 5136, 5137, 2198, 13, 2792, 3704, 2998, 3149, 1229, 1917, 5138, 3835, 2132, # 1344 + 5139, 4243, 4565, 2404, 3580, 5140, 2217, 1511, 1727, 1120, 5141, 5142, 646, 3836, 2448, 307, # 1360 + 5143, 5144, 1595, 3217, 5145, 5146, 5147, 3705, 1113, 1356, 4002, 1465, 2529, 2530, 5148, 519, # 1376 + 5149, 128, 2133, 92, 2289, 1980, 5150, 4003, 1512, 342, 3150, 2199, 5151, 2793, 2218, 1981, # 1392 + 3360, 4244, 290, 1656, 1317, 789, 827, 2365, 5152, 3837, 4566, 562, 581, 4004, 5153, 401, # 1408 + 4567, 2252, 94, 4568, 5154, 1399, 2794, 5155, 1463, 2025, 4569, 3218, 1944, 5156, 828, 1105, # 1424 + 4245, 1262, 1394, 5157, 4246, 605, 4570, 5158, 1784, 2876, 5159, 2835, 819, 2102, 578, 2200, # 1440 + 2952, 5160, 1502, 436, 3287, 4247, 3288, 2836, 4005, 2919, 3472, 3473, 5161, 2721, 2320, 5162, # 1456 + 5163, 2337, 2068, 23, 4571, 193, 826, 3838, 2103, 699, 1630, 4248, 3098, 390, 1794, 1064, # 1472 + 3581, 5164, 1579, 3099, 3100, 1400, 5165, 4249, 1839, 1640, 2877, 5166, 4572, 4573, 137, 4250, # 1488 + 598, 3101, 1967, 780, 104, 974, 2953, 5167, 278, 899, 253, 402, 572, 504, 493, 1339, # 1504 + 5168, 4006, 1275, 4574, 2582, 2558, 5169, 3706, 3049, 3102, 2253, 565, 1334, 2722, 863, 41, # 1520 + 5170, 5171, 4575, 5172, 1657, 2338, 19, 463, 2760, 4251, 606, 5173, 2999, 3289, 1087, 2085, # 1536 + 1323, 2662, 3000, 5174, 1631, 1623, 1750, 4252, 2691, 5175, 2878, 791, 2723, 2663, 2339, 232, # 1552 + 2421, 5176, 3001, 1498, 5177, 2664, 2630, 755, 1366, 3707, 3290, 3151, 2026, 1609, 119, 1918, # 1568 + 3474, 862, 1026, 4253, 5178, 4007, 3839, 4576, 4008, 4577, 2265, 1952, 2477, 5179, 1125, 817, # 1584 + 4254, 4255, 4009, 1513, 1766, 2041, 1487, 4256, 3050, 3291, 2837, 3840, 3152, 5180, 5181, 1507, # 1600 + 5182, 2692, 733, 40, 1632, 1106, 2879, 345, 4257, 841, 2531, 230, 4578, 3002, 1847, 3292, # 1616 + 3475, 5183, 1263, 986, 3476, 5184, 735, 879, 254, 1137, 857, 622, 1300, 1180, 1388, 1562, # 1632 + 4010, 4011, 2954, 967, 2761, 2665, 1349, 592, 2134, 1692, 3361, 3003, 1995, 4258, 1679, 4012, # 1648 + 1902, 2188, 5185, 739, 3708, 2724, 1296, 1290, 5186, 4259, 2201, 2202, 1922, 1563, 2605, 2559, # 1664 + 1871, 2762, 3004, 5187, 435, 5188, 343, 1108, 596, 17, 1751, 4579, 2239, 3477, 3709, 5189, # 1680 + 4580, 294, 3582, 2955, 1693, 477, 979, 281, 2042, 3583, 643, 2043, 3710, 2631, 2795, 2266, # 1696 + 1031, 2340, 2135, 2303, 3584, 4581, 367, 1249, 2560, 5190, 3585, 5191, 4582, 1283, 3362, 2005, # 1712 + 240, 1762, 3363, 4583, 4584, 836, 1069, 3153, 474, 5192, 2149, 2532, 268, 3586, 5193, 3219, # 1728 + 1521, 1284, 5194, 1658, 1546, 4260, 5195, 3587, 3588, 5196, 4261, 3364, 2693, 1685, 4262, 961, # 1744 + 1673, 2632, 190, 2006, 2203, 3841, 4585, 4586, 5197, 570, 2504, 3711, 1490, 5198, 4587, 2633, # 1760 + 3293, 1957, 4588, 584, 1514, 396, 1045, 1945, 5199, 4589, 1968, 2449, 5200, 5201, 4590, 4013, # 1776 + 619, 5202, 3154, 3294, 215, 2007, 2796, 2561, 3220, 4591, 3221, 4592, 763, 4263, 3842, 4593, # 1792 + 5203, 5204, 1958, 1767, 2956, 3365, 3712, 1174, 452, 1477, 4594, 3366, 3155, 5205, 2838, 1253, # 1808 + 2387, 2189, 1091, 2290, 4264, 492, 5206, 638, 1169, 1825, 2136, 1752, 4014, 648, 926, 1021, # 1824 + 1324, 4595, 520, 4596, 997, 847, 1007, 892, 4597, 3843, 2267, 1872, 3713, 2405, 1785, 4598, # 1840 + 1953, 2957, 3103, 3222, 1728, 4265, 2044, 3714, 4599, 2008, 1701, 3156, 1551, 30, 2268, 4266, # 1856 + 5207, 2027, 4600, 3589, 5208, 501, 5209, 4267, 594, 3478, 2166, 1822, 3590, 3479, 3591, 3223, # 1872 + 829, 2839, 4268, 5210, 1680, 3157, 1225, 4269, 5211, 3295, 4601, 4270, 3158, 2341, 5212, 4602, # 1888 + 4271, 5213, 4015, 4016, 5214, 1848, 2388, 2606, 3367, 5215, 4603, 374, 4017, 652, 4272, 4273, # 1904 + 375, 1140, 798, 5216, 5217, 5218, 2366, 4604, 2269, 546, 1659, 138, 3051, 2450, 4605, 5219, # 1920 + 2254, 612, 1849, 910, 796, 3844, 1740, 1371, 825, 3845, 3846, 5220, 2920, 2562, 5221, 692, # 1936 + 444, 3052, 2634, 801, 4606, 4274, 5222, 1491, 244, 1053, 3053, 4275, 4276, 340, 5223, 4018, # 1952 + 1041, 3005, 293, 1168, 87, 1357, 5224, 1539, 959, 5225, 2240, 721, 694, 4277, 3847, 219, # 1968 + 1478, 644, 1417, 3368, 2666, 1413, 1401, 1335, 1389, 4019, 5226, 5227, 3006, 2367, 3159, 1826, # 1984 + 730, 1515, 184, 2840, 66, 4607, 5228, 1660, 2958, 246, 3369, 378, 1457, 226, 3480, 975, # 2000 + 4020, 2959, 1264, 3592, 674, 696, 5229, 163, 5230, 1141, 2422, 2167, 713, 3593, 3370, 4608, # 2016 + 4021, 5231, 5232, 1186, 15, 5233, 1079, 1070, 5234, 1522, 3224, 3594, 276, 1050, 2725, 758, # 2032 + 1126, 653, 2960, 3296, 5235, 2342, 889, 3595, 4022, 3104, 3007, 903, 1250, 4609, 4023, 3481, # 2048 + 3596, 1342, 1681, 1718, 766, 3297, 286, 89, 2961, 3715, 5236, 1713, 5237, 2607, 3371, 3008, # 2064 + 5238, 2962, 2219, 3225, 2880, 5239, 4610, 2505, 2533, 181, 387, 1075, 4024, 731, 2190, 3372, # 2080 + 5240, 3298, 310, 313, 3482, 2304, 770, 4278, 54, 3054, 189, 4611, 3105, 3848, 4025, 5241, # 2096 + 1230, 1617, 1850, 355, 3597, 4279, 4612, 3373, 111, 4280, 3716, 1350, 3160, 3483, 3055, 4281, # 2112 + 2150, 3299, 3598, 5242, 2797, 4026, 4027, 3009, 722, 2009, 5243, 1071, 247, 1207, 2343, 2478, # 2128 + 1378, 4613, 2010, 864, 1437, 1214, 4614, 373, 3849, 1142, 2220, 667, 4615, 442, 2763, 2563, # 2144 + 3850, 4028, 1969, 4282, 3300, 1840, 837, 170, 1107, 934, 1336, 1883, 5244, 5245, 2119, 4283, # 2160 + 2841, 743, 1569, 5246, 4616, 4284, 582, 2389, 1418, 3484, 5247, 1803, 5248, 357, 1395, 1729, # 2176 + 3717, 3301, 2423, 1564, 2241, 5249, 3106, 3851, 1633, 4617, 1114, 2086, 4285, 1532, 5250, 482, # 2192 + 2451, 4618, 5251, 5252, 1492, 833, 1466, 5253, 2726, 3599, 1641, 2842, 5254, 1526, 1272, 3718, # 2208 + 4286, 1686, 1795, 416, 2564, 1903, 1954, 1804, 5255, 3852, 2798, 3853, 1159, 2321, 5256, 2881, # 2224 + 4619, 1610, 1584, 3056, 2424, 2764, 443, 3302, 1163, 3161, 5257, 5258, 4029, 5259, 4287, 2506, # 2240 + 3057, 4620, 4030, 3162, 2104, 1647, 3600, 2011, 1873, 4288, 5260, 4289, 431, 3485, 5261, 250, # 2256 + 97, 81, 4290, 5262, 1648, 1851, 1558, 160, 848, 5263, 866, 740, 1694, 5264, 2204, 2843, # 2272 + 3226, 4291, 4621, 3719, 1687, 950, 2479, 426, 469, 3227, 3720, 3721, 4031, 5265, 5266, 1188, # 2288 + 424, 1996, 861, 3601, 4292, 3854, 2205, 2694, 168, 1235, 3602, 4293, 5267, 2087, 1674, 4622, # 2304 + 3374, 3303, 220, 2565, 1009, 5268, 3855, 670, 3010, 332, 1208, 717, 5269, 5270, 3603, 2452, # 2320 + 4032, 3375, 5271, 513, 5272, 1209, 2882, 3376, 3163, 4623, 1080, 5273, 5274, 5275, 5276, 2534, # 2336 + 3722, 3604, 815, 1587, 4033, 4034, 5277, 3605, 3486, 3856, 1254, 4624, 1328, 3058, 1390, 4035, # 2352 + 1741, 4036, 3857, 4037, 5278, 236, 3858, 2453, 3304, 5279, 5280, 3723, 3859, 1273, 3860, 4625, # 2368 + 5281, 308, 5282, 4626, 245, 4627, 1852, 2480, 1307, 2583, 430, 715, 2137, 2454, 5283, 270, # 2384 + 199, 2883, 4038, 5284, 3606, 2727, 1753, 761, 1754, 725, 1661, 1841, 4628, 3487, 3724, 5285, # 2400 + 5286, 587, 14, 3305, 227, 2608, 326, 480, 2270, 943, 2765, 3607, 291, 650, 1884, 5287, # 2416 + 1702, 1226, 102, 1547, 62, 3488, 904, 4629, 3489, 1164, 4294, 5288, 5289, 1224, 1548, 2766, # 2432 + 391, 498, 1493, 5290, 1386, 1419, 5291, 2056, 1177, 4630, 813, 880, 1081, 2368, 566, 1145, # 2448 + 4631, 2291, 1001, 1035, 2566, 2609, 2242, 394, 1286, 5292, 5293, 2069, 5294, 86, 1494, 1730, # 2464 + 4039, 491, 1588, 745, 897, 2963, 843, 3377, 4040, 2767, 2884, 3306, 1768, 998, 2221, 2070, # 2480 + 397, 1827, 1195, 1970, 3725, 3011, 3378, 284, 5295, 3861, 2507, 2138, 2120, 1904, 5296, 4041, # 2496 + 2151, 4042, 4295, 1036, 3490, 1905, 114, 2567, 4296, 209, 1527, 5297, 5298, 2964, 2844, 2635, # 2512 + 2390, 2728, 3164, 812, 2568, 5299, 3307, 5300, 1559, 737, 1885, 3726, 1210, 885, 28, 2695, # 2528 + 3608, 3862, 5301, 4297, 1004, 1780, 4632, 5302, 346, 1982, 2222, 2696, 4633, 3863, 1742, 797, # 2544 + 1642, 4043, 1934, 1072, 1384, 2152, 896, 4044, 3308, 3727, 3228, 2885, 3609, 5303, 2569, 1959, # 2560 + 4634, 2455, 1786, 5304, 5305, 5306, 4045, 4298, 1005, 1308, 3728, 4299, 2729, 4635, 4636, 1528, # 2576 + 2610, 161, 1178, 4300, 1983, 987, 4637, 1101, 4301, 631, 4046, 1157, 3229, 2425, 1343, 1241, # 2592 + 1016, 2243, 2570, 372, 877, 2344, 2508, 1160, 555, 1935, 911, 4047, 5307, 466, 1170, 169, # 2608 + 1051, 2921, 2697, 3729, 2481, 3012, 1182, 2012, 2571, 1251, 2636, 5308, 992, 2345, 3491, 1540, # 2624 + 2730, 1201, 2071, 2406, 1997, 2482, 5309, 4638, 528, 1923, 2191, 1503, 1874, 1570, 2369, 3379, # 2640 + 3309, 5310, 557, 1073, 5311, 1828, 3492, 2088, 2271, 3165, 3059, 3107, 767, 3108, 2799, 4639, # 2656 + 1006, 4302, 4640, 2346, 1267, 2179, 3730, 3230, 778, 4048, 3231, 2731, 1597, 2667, 5312, 4641, # 2672 + 5313, 3493, 5314, 5315, 5316, 3310, 2698, 1433, 3311, 131, 95, 1504, 4049, 723, 4303, 3166, # 2688 + 1842, 3610, 2768, 2192, 4050, 2028, 2105, 3731, 5317, 3013, 4051, 1218, 5318, 3380, 3232, 4052, # 2704 + 4304, 2584, 248, 1634, 3864, 912, 5319, 2845, 3732, 3060, 3865, 654, 53, 5320, 3014, 5321, # 2720 + 1688, 4642, 777, 3494, 1032, 4053, 1425, 5322, 191, 820, 2121, 2846, 971, 4643, 931, 3233, # 2736 + 135, 664, 783, 3866, 1998, 772, 2922, 1936, 4054, 3867, 4644, 2923, 3234, 282, 2732, 640, # 2752 + 1372, 3495, 1127, 922, 325, 3381, 5323, 5324, 711, 2045, 5325, 5326, 4055, 2223, 2800, 1937, # 2768 + 4056, 3382, 2224, 2255, 3868, 2305, 5327, 4645, 3869, 1258, 3312, 4057, 3235, 2139, 2965, 4058, # 2784 + 4059, 5328, 2225, 258, 3236, 4646, 101, 1227, 5329, 3313, 1755, 5330, 1391, 3314, 5331, 2924, # 2800 + 2057, 893, 5332, 5333, 5334, 1402, 4305, 2347, 5335, 5336, 3237, 3611, 5337, 5338, 878, 1325, # 2816 + 1781, 2801, 4647, 259, 1385, 2585, 744, 1183, 2272, 4648, 5339, 4060, 2509, 5340, 684, 1024, # 2832 + 4306, 5341, 472, 3612, 3496, 1165, 3315, 4061, 4062, 322, 2153, 881, 455, 1695, 1152, 1340, # 2848 + 660, 554, 2154, 4649, 1058, 4650, 4307, 830, 1065, 3383, 4063, 4651, 1924, 5342, 1703, 1919, # 2864 + 5343, 932, 2273, 122, 5344, 4652, 947, 677, 5345, 3870, 2637, 297, 1906, 1925, 2274, 4653, # 2880 + 2322, 3316, 5346, 5347, 4308, 5348, 4309, 84, 4310, 112, 989, 5349, 547, 1059, 4064, 701, # 2896 + 3613, 1019, 5350, 4311, 5351, 3497, 942, 639, 457, 2306, 2456, 993, 2966, 407, 851, 494, # 2912 + 4654, 3384, 927, 5352, 1237, 5353, 2426, 3385, 573, 4312, 680, 921, 2925, 1279, 1875, 285, # 2928 + 790, 1448, 1984, 719, 2168, 5354, 5355, 4655, 4065, 4066, 1649, 5356, 1541, 563, 5357, 1077, # 2944 + 5358, 3386, 3061, 3498, 511, 3015, 4067, 4068, 3733, 4069, 1268, 2572, 3387, 3238, 4656, 4657, # 2960 + 5359, 535, 1048, 1276, 1189, 2926, 2029, 3167, 1438, 1373, 2847, 2967, 1134, 2013, 5360, 4313, # 2976 + 1238, 2586, 3109, 1259, 5361, 700, 5362, 2968, 3168, 3734, 4314, 5363, 4315, 1146, 1876, 1907, # 2992 + 4658, 2611, 4070, 781, 2427, 132, 1589, 203, 147, 273, 2802, 2407, 898, 1787, 2155, 4071, # 3008 + 4072, 5364, 3871, 2803, 5365, 5366, 4659, 4660, 5367, 3239, 5368, 1635, 3872, 965, 5369, 1805, # 3024 + 2699, 1516, 3614, 1121, 1082, 1329, 3317, 4073, 1449, 3873, 65, 1128, 2848, 2927, 2769, 1590, # 3040 + 3874, 5370, 5371, 12, 2668, 45, 976, 2587, 3169, 4661, 517, 2535, 1013, 1037, 3240, 5372, # 3056 + 3875, 2849, 5373, 3876, 5374, 3499, 5375, 2612, 614, 1999, 2323, 3877, 3110, 2733, 2638, 5376, # 3072 + 2588, 4316, 599, 1269, 5377, 1811, 3735, 5378, 2700, 3111, 759, 1060, 489, 1806, 3388, 3318, # 3088 + 1358, 5379, 5380, 2391, 1387, 1215, 2639, 2256, 490, 5381, 5382, 4317, 1759, 2392, 2348, 5383, # 3104 + 4662, 3878, 1908, 4074, 2640, 1807, 3241, 4663, 3500, 3319, 2770, 2349, 874, 5384, 5385, 3501, # 3120 + 3736, 1859, 91, 2928, 3737, 3062, 3879, 4664, 5386, 3170, 4075, 2669, 5387, 3502, 1202, 1403, # 3136 + 3880, 2969, 2536, 1517, 2510, 4665, 3503, 2511, 5388, 4666, 5389, 2701, 1886, 1495, 1731, 4076, # 3152 + 2370, 4667, 5390, 2030, 5391, 5392, 4077, 2702, 1216, 237, 2589, 4318, 2324, 4078, 3881, 4668, # 3168 + 4669, 2703, 3615, 3504, 445, 4670, 5393, 5394, 5395, 5396, 2771, 61, 4079, 3738, 1823, 4080, # 3184 + 5397, 687, 2046, 935, 925, 405, 2670, 703, 1096, 1860, 2734, 4671, 4081, 1877, 1367, 2704, # 3200 + 3389, 918, 2106, 1782, 2483, 334, 3320, 1611, 1093, 4672, 564, 3171, 3505, 3739, 3390, 945, # 3216 + 2641, 2058, 4673, 5398, 1926, 872, 4319, 5399, 3506, 2705, 3112, 349, 4320, 3740, 4082, 4674, # 3232 + 3882, 4321, 3741, 2156, 4083, 4675, 4676, 4322, 4677, 2408, 2047, 782, 4084, 400, 251, 4323, # 3248 + 1624, 5400, 5401, 277, 3742, 299, 1265, 476, 1191, 3883, 2122, 4324, 4325, 1109, 205, 5402, # 3264 + 2590, 1000, 2157, 3616, 1861, 5403, 5404, 5405, 4678, 5406, 4679, 2573, 107, 2484, 2158, 4085, # 3280 + 3507, 3172, 5407, 1533, 541, 1301, 158, 753, 4326, 2886, 3617, 5408, 1696, 370, 1088, 4327, # 3296 + 4680, 3618, 579, 327, 440, 162, 2244, 269, 1938, 1374, 3508, 968, 3063, 56, 1396, 3113, # 3312 + 2107, 3321, 3391, 5409, 1927, 2159, 4681, 3016, 5410, 3619, 5411, 5412, 3743, 4682, 2485, 5413, # 3328 + 2804, 5414, 1650, 4683, 5415, 2613, 5416, 5417, 4086, 2671, 3392, 1149, 3393, 4087, 3884, 4088, # 3344 + 5418, 1076, 49, 5419, 951, 3242, 3322, 3323, 450, 2850, 920, 5420, 1812, 2805, 2371, 4328, # 3360 + 1909, 1138, 2372, 3885, 3509, 5421, 3243, 4684, 1910, 1147, 1518, 2428, 4685, 3886, 5422, 4686, # 3376 + 2393, 2614, 260, 1796, 3244, 5423, 5424, 3887, 3324, 708, 5425, 3620, 1704, 5426, 3621, 1351, # 3392 + 1618, 3394, 3017, 1887, 944, 4329, 3395, 4330, 3064, 3396, 4331, 5427, 3744, 422, 413, 1714, # 3408 + 3325, 500, 2059, 2350, 4332, 2486, 5428, 1344, 1911, 954, 5429, 1668, 5430, 5431, 4089, 2409, # 3424 + 4333, 3622, 3888, 4334, 5432, 2307, 1318, 2512, 3114, 133, 3115, 2887, 4687, 629, 31, 2851, # 3440 + 2706, 3889, 4688, 850, 949, 4689, 4090, 2970, 1732, 2089, 4335, 1496, 1853, 5433, 4091, 620, # 3456 + 3245, 981, 1242, 3745, 3397, 1619, 3746, 1643, 3326, 2140, 2457, 1971, 1719, 3510, 2169, 5434, # 3472 + 3246, 5435, 5436, 3398, 1829, 5437, 1277, 4690, 1565, 2048, 5438, 1636, 3623, 3116, 5439, 869, # 3488 + 2852, 655, 3890, 3891, 3117, 4092, 3018, 3892, 1310, 3624, 4691, 5440, 5441, 5442, 1733, 558, # 3504 + 4692, 3747, 335, 1549, 3065, 1756, 4336, 3748, 1946, 3511, 1830, 1291, 1192, 470, 2735, 2108, # 3520 + 2806, 913, 1054, 4093, 5443, 1027, 5444, 3066, 4094, 4693, 982, 2672, 3399, 3173, 3512, 3247, # 3536 + 3248, 1947, 2807, 5445, 571, 4694, 5446, 1831, 5447, 3625, 2591, 1523, 2429, 5448, 2090, 984, # 3552 + 4695, 3749, 1960, 5449, 3750, 852, 923, 2808, 3513, 3751, 969, 1519, 999, 2049, 2325, 1705, # 3568 + 5450, 3118, 615, 1662, 151, 597, 4095, 2410, 2326, 1049, 275, 4696, 3752, 4337, 568, 3753, # 3584 + 3626, 2487, 4338, 3754, 5451, 2430, 2275, 409, 3249, 5452, 1566, 2888, 3514, 1002, 769, 2853, # 3600 + 194, 2091, 3174, 3755, 2226, 3327, 4339, 628, 1505, 5453, 5454, 1763, 2180, 3019, 4096, 521, # 3616 + 1161, 2592, 1788, 2206, 2411, 4697, 4097, 1625, 4340, 4341, 412, 42, 3119, 464, 5455, 2642, # 3632 + 4698, 3400, 1760, 1571, 2889, 3515, 2537, 1219, 2207, 3893, 2643, 2141, 2373, 4699, 4700, 3328, # 3648 + 1651, 3401, 3627, 5456, 5457, 3628, 2488, 3516, 5458, 3756, 5459, 5460, 2276, 2092, 460, 5461, # 3664 + 4701, 5462, 3020, 962, 588, 3629, 289, 3250, 2644, 1116, 52, 5463, 3067, 1797, 5464, 5465, # 3680 + 5466, 1467, 5467, 1598, 1143, 3757, 4342, 1985, 1734, 1067, 4702, 1280, 3402, 465, 4703, 1572, # 3696 + 510, 5468, 1928, 2245, 1813, 1644, 3630, 5469, 4704, 3758, 5470, 5471, 2673, 1573, 1534, 5472, # 3712 + 5473, 536, 1808, 1761, 3517, 3894, 3175, 2645, 5474, 5475, 5476, 4705, 3518, 2929, 1912, 2809, # 3728 + 5477, 3329, 1122, 377, 3251, 5478, 360, 5479, 5480, 4343, 1529, 551, 5481, 2060, 3759, 1769, # 3744 + 2431, 5482, 2930, 4344, 3330, 3120, 2327, 2109, 2031, 4706, 1404, 136, 1468, 1479, 672, 1171, # 3760 + 3252, 2308, 271, 3176, 5483, 2772, 5484, 2050, 678, 2736, 865, 1948, 4707, 5485, 2014, 4098, # 3776 + 2971, 5486, 2737, 2227, 1397, 3068, 3760, 4708, 4709, 1735, 2931, 3403, 3631, 5487, 3895, 509, # 3792 + 2854, 2458, 2890, 3896, 5488, 5489, 3177, 3178, 4710, 4345, 2538, 4711, 2309, 1166, 1010, 552, # 3808 + 681, 1888, 5490, 5491, 2972, 2973, 4099, 1287, 1596, 1862, 3179, 358, 453, 736, 175, 478, # 3824 + 1117, 905, 1167, 1097, 5492, 1854, 1530, 5493, 1706, 5494, 2181, 3519, 2292, 3761, 3520, 3632, # 3840 + 4346, 2093, 4347, 5495, 3404, 1193, 2489, 4348, 1458, 2193, 2208, 1863, 1889, 1421, 3331, 2932, # 3856 + 3069, 2182, 3521, 595, 2123, 5496, 4100, 5497, 5498, 4349, 1707, 2646, 223, 3762, 1359, 751, # 3872 + 3121, 183, 3522, 5499, 2810, 3021, 419, 2374, 633, 704, 3897, 2394, 241, 5500, 5501, 5502, # 3888 + 838, 3022, 3763, 2277, 2773, 2459, 3898, 1939, 2051, 4101, 1309, 3122, 2246, 1181, 5503, 1136, # 3904 + 2209, 3899, 2375, 1446, 4350, 2310, 4712, 5504, 5505, 4351, 1055, 2615, 484, 3764, 5506, 4102, # 3920 + 625, 4352, 2278, 3405, 1499, 4353, 4103, 5507, 4104, 4354, 3253, 2279, 2280, 3523, 5508, 5509, # 3936 + 2774, 808, 2616, 3765, 3406, 4105, 4355, 3123, 2539, 526, 3407, 3900, 4356, 955, 5510, 1620, # 3952 + 4357, 2647, 2432, 5511, 1429, 3766, 1669, 1832, 994, 928, 5512, 3633, 1260, 5513, 5514, 5515, # 3968 + 1949, 2293, 741, 2933, 1626, 4358, 2738, 2460, 867, 1184, 362, 3408, 1392, 5516, 5517, 4106, # 3984 + 4359, 1770, 1736, 3254, 2934, 4713, 4714, 1929, 2707, 1459, 1158, 5518, 3070, 3409, 2891, 1292, # 4000 + 1930, 2513, 2855, 3767, 1986, 1187, 2072, 2015, 2617, 4360, 5519, 2574, 2514, 2170, 3768, 2490, # 4016 + 3332, 5520, 3769, 4715, 5521, 5522, 666, 1003, 3023, 1022, 3634, 4361, 5523, 4716, 1814, 2257, # 4032 + 574, 3901, 1603, 295, 1535, 705, 3902, 4362, 283, 858, 417, 5524, 5525, 3255, 4717, 4718, # 4048 + 3071, 1220, 1890, 1046, 2281, 2461, 4107, 1393, 1599, 689, 2575, 388, 4363, 5526, 2491, 802, # 4064 + 5527, 2811, 3903, 2061, 1405, 2258, 5528, 4719, 3904, 2110, 1052, 1345, 3256, 1585, 5529, 809, # 4080 + 5530, 5531, 5532, 575, 2739, 3524, 956, 1552, 1469, 1144, 2328, 5533, 2329, 1560, 2462, 3635, # 4096 + 3257, 4108, 616, 2210, 4364, 3180, 2183, 2294, 5534, 1833, 5535, 3525, 4720, 5536, 1319, 3770, # 4112 + 3771, 1211, 3636, 1023, 3258, 1293, 2812, 5537, 5538, 5539, 3905, 607, 2311, 3906, 762, 2892, # 4128 + 1439, 4365, 1360, 4721, 1485, 3072, 5540, 4722, 1038, 4366, 1450, 2062, 2648, 4367, 1379, 4723, # 4144 + 2593, 5541, 5542, 4368, 1352, 1414, 2330, 2935, 1172, 5543, 5544, 3907, 3908, 4724, 1798, 1451, # 4160 + 5545, 5546, 5547, 5548, 2936, 4109, 4110, 2492, 2351, 411, 4111, 4112, 3637, 3333, 3124, 4725, # 4176 + 1561, 2674, 1452, 4113, 1375, 5549, 5550, 47, 2974, 316, 5551, 1406, 1591, 2937, 3181, 5552, # 4192 + 1025, 2142, 3125, 3182, 354, 2740, 884, 2228, 4369, 2412, 508, 3772, 726, 3638, 996, 2433, # 4208 + 3639, 729, 5553, 392, 2194, 1453, 4114, 4726, 3773, 5554, 5555, 2463, 3640, 2618, 1675, 2813, # 4224 + 919, 2352, 2975, 2353, 1270, 4727, 4115, 73, 5556, 5557, 647, 5558, 3259, 2856, 2259, 1550, # 4240 + 1346, 3024, 5559, 1332, 883, 3526, 5560, 5561, 5562, 5563, 3334, 2775, 5564, 1212, 831, 1347, # 4256 + 4370, 4728, 2331, 3909, 1864, 3073, 720, 3910, 4729, 4730, 3911, 5565, 4371, 5566, 5567, 4731, # 4272 + 5568, 5569, 1799, 4732, 3774, 2619, 4733, 3641, 1645, 2376, 4734, 5570, 2938, 669, 2211, 2675, # 4288 + 2434, 5571, 2893, 5572, 5573, 1028, 3260, 5574, 4372, 2413, 5575, 2260, 1353, 5576, 5577, 4735, # 4304 + 3183, 518, 5578, 4116, 5579, 4373, 1961, 5580, 2143, 4374, 5581, 5582, 3025, 2354, 2355, 3912, # 4320 + 516, 1834, 1454, 4117, 2708, 4375, 4736, 2229, 2620, 1972, 1129, 3642, 5583, 2776, 5584, 2976, # 4336 + 1422, 577, 1470, 3026, 1524, 3410, 5585, 5586, 432, 4376, 3074, 3527, 5587, 2594, 1455, 2515, # 4352 + 2230, 1973, 1175, 5588, 1020, 2741, 4118, 3528, 4737, 5589, 2742, 5590, 1743, 1361, 3075, 3529, # 4368 + 2649, 4119, 4377, 4738, 2295, 895, 924, 4378, 2171, 331, 2247, 3076, 166, 1627, 3077, 1098, # 4384 + 5591, 1232, 2894, 2231, 3411, 4739, 657, 403, 1196, 2377, 542, 3775, 3412, 1600, 4379, 3530, # 4400 + 5592, 4740, 2777, 3261, 576, 530, 1362, 4741, 4742, 2540, 2676, 3776, 4120, 5593, 842, 3913, # 4416 + 5594, 2814, 2032, 1014, 4121, 213, 2709, 3413, 665, 621, 4380, 5595, 3777, 2939, 2435, 5596, # 4432 + 2436, 3335, 3643, 3414, 4743, 4381, 2541, 4382, 4744, 3644, 1682, 4383, 3531, 1380, 5597, 724, # 4448 + 2282, 600, 1670, 5598, 1337, 1233, 4745, 3126, 2248, 5599, 1621, 4746, 5600, 651, 4384, 5601, # 4464 + 1612, 4385, 2621, 5602, 2857, 5603, 2743, 2312, 3078, 5604, 716, 2464, 3079, 174, 1255, 2710, # 4480 + 4122, 3645, 548, 1320, 1398, 728, 4123, 1574, 5605, 1891, 1197, 3080, 4124, 5606, 3081, 3082, # 4496 + 3778, 3646, 3779, 747, 5607, 635, 4386, 4747, 5608, 5609, 5610, 4387, 5611, 5612, 4748, 5613, # 4512 + 3415, 4749, 2437, 451, 5614, 3780, 2542, 2073, 4388, 2744, 4389, 4125, 5615, 1764, 4750, 5616, # 4528 + 4390, 350, 4751, 2283, 2395, 2493, 5617, 4391, 4126, 2249, 1434, 4127, 488, 4752, 458, 4392, # 4544 + 4128, 3781, 771, 1330, 2396, 3914, 2576, 3184, 2160, 2414, 1553, 2677, 3185, 4393, 5618, 2494, # 4560 + 2895, 2622, 1720, 2711, 4394, 3416, 4753, 5619, 2543, 4395, 5620, 3262, 4396, 2778, 5621, 2016, # 4576 + 2745, 5622, 1155, 1017, 3782, 3915, 5623, 3336, 2313, 201, 1865, 4397, 1430, 5624, 4129, 5625, # 4592 + 5626, 5627, 5628, 5629, 4398, 1604, 5630, 414, 1866, 371, 2595, 4754, 4755, 3532, 2017, 3127, # 4608 + 4756, 1708, 960, 4399, 887, 389, 2172, 1536, 1663, 1721, 5631, 2232, 4130, 2356, 2940, 1580, # 4624 + 5632, 5633, 1744, 4757, 2544, 4758, 4759, 5634, 4760, 5635, 2074, 5636, 4761, 3647, 3417, 2896, # 4640 + 4400, 5637, 4401, 2650, 3418, 2815, 673, 2712, 2465, 709, 3533, 4131, 3648, 4402, 5638, 1148, # 4656 + 502, 634, 5639, 5640, 1204, 4762, 3649, 1575, 4763, 2623, 3783, 5641, 3784, 3128, 948, 3263, # 4672 + 121, 1745, 3916, 1110, 5642, 4403, 3083, 2516, 3027, 4132, 3785, 1151, 1771, 3917, 1488, 4133, # 4688 + 1987, 5643, 2438, 3534, 5644, 5645, 2094, 5646, 4404, 3918, 1213, 1407, 2816, 531, 2746, 2545, # 4704 + 3264, 1011, 1537, 4764, 2779, 4405, 3129, 1061, 5647, 3786, 3787, 1867, 2897, 5648, 2018, 120, # 4720 + 4406, 4407, 2063, 3650, 3265, 2314, 3919, 2678, 3419, 1955, 4765, 4134, 5649, 3535, 1047, 2713, # 4736 + 1266, 5650, 1368, 4766, 2858, 649, 3420, 3920, 2546, 2747, 1102, 2859, 2679, 5651, 5652, 2000, # 4752 + 5653, 1111, 3651, 2977, 5654, 2495, 3921, 3652, 2817, 1855, 3421, 3788, 5655, 5656, 3422, 2415, # 4768 + 2898, 3337, 3266, 3653, 5657, 2577, 5658, 3654, 2818, 4135, 1460, 856, 5659, 3655, 5660, 2899, # 4784 + 2978, 5661, 2900, 3922, 5662, 4408, 632, 2517, 875, 3923, 1697, 3924, 2296, 5663, 5664, 4767, # 4800 + 3028, 1239, 580, 4768, 4409, 5665, 914, 936, 2075, 1190, 4136, 1039, 2124, 5666, 5667, 5668, # 4816 + 5669, 3423, 1473, 5670, 1354, 4410, 3925, 4769, 2173, 3084, 4137, 915, 3338, 4411, 4412, 3339, # 4832 + 1605, 1835, 5671, 2748, 398, 3656, 4413, 3926, 4138, 328, 1913, 2860, 4139, 3927, 1331, 4414, # 4848 + 3029, 937, 4415, 5672, 3657, 4140, 4141, 3424, 2161, 4770, 3425, 524, 742, 538, 3085, 1012, # 4864 + 5673, 5674, 3928, 2466, 5675, 658, 1103, 225, 3929, 5676, 5677, 4771, 5678, 4772, 5679, 3267, # 4880 + 1243, 5680, 4142, 963, 2250, 4773, 5681, 2714, 3658, 3186, 5682, 5683, 2596, 2332, 5684, 4774, # 4896 + 5685, 5686, 5687, 3536, 957, 3426, 2547, 2033, 1931, 2941, 2467, 870, 2019, 3659, 1746, 2780, # 4912 + 2781, 2439, 2468, 5688, 3930, 5689, 3789, 3130, 3790, 3537, 3427, 3791, 5690, 1179, 3086, 5691, # 4928 + 3187, 2378, 4416, 3792, 2548, 3188, 3131, 2749, 4143, 5692, 3428, 1556, 2549, 2297, 977, 2901, # 4944 + 2034, 4144, 1205, 3429, 5693, 1765, 3430, 3189, 2125, 1271, 714, 1689, 4775, 3538, 5694, 2333, # 4960 + 3931, 533, 4417, 3660, 2184, 617, 5695, 2469, 3340, 3539, 2315, 5696, 5697, 3190, 5698, 5699, # 4976 + 3932, 1988, 618, 427, 2651, 3540, 3431, 5700, 5701, 1244, 1690, 5702, 2819, 4418, 4776, 5703, # 4992 + 3541, 4777, 5704, 2284, 1576, 473, 3661, 4419, 3432, 972, 5705, 3662, 5706, 3087, 5707, 5708, # 5008 + 4778, 4779, 5709, 3793, 4145, 4146, 5710, 153, 4780, 356, 5711, 1892, 2902, 4420, 2144, 408, # 5024 + 803, 2357, 5712, 3933, 5713, 4421, 1646, 2578, 2518, 4781, 4782, 3934, 5714, 3935, 4422, 5715, # 5040 + 2416, 3433, 752, 5716, 5717, 1962, 3341, 2979, 5718, 746, 3030, 2470, 4783, 4423, 3794, 698, # 5056 + 4784, 1893, 4424, 3663, 2550, 4785, 3664, 3936, 5719, 3191, 3434, 5720, 1824, 1302, 4147, 2715, # 5072 + 3937, 1974, 4425, 5721, 4426, 3192, 823, 1303, 1288, 1236, 2861, 3542, 4148, 3435, 774, 3938, # 5088 + 5722, 1581, 4786, 1304, 2862, 3939, 4787, 5723, 2440, 2162, 1083, 3268, 4427, 4149, 4428, 344, # 5104 + 1173, 288, 2316, 454, 1683, 5724, 5725, 1461, 4788, 4150, 2597, 5726, 5727, 4789, 985, 894, # 5120 + 5728, 3436, 3193, 5729, 1914, 2942, 3795, 1989, 5730, 2111, 1975, 5731, 4151, 5732, 2579, 1194, # 5136 + 425, 5733, 4790, 3194, 1245, 3796, 4429, 5734, 5735, 2863, 5736, 636, 4791, 1856, 3940, 760, # 5152 + 1800, 5737, 4430, 2212, 1508, 4792, 4152, 1894, 1684, 2298, 5738, 5739, 4793, 4431, 4432, 2213, # 5168 + 479, 5740, 5741, 832, 5742, 4153, 2496, 5743, 2980, 2497, 3797, 990, 3132, 627, 1815, 2652, # 5184 + 4433, 1582, 4434, 2126, 2112, 3543, 4794, 5744, 799, 4435, 3195, 5745, 4795, 2113, 1737, 3031, # 5200 + 1018, 543, 754, 4436, 3342, 1676, 4796, 4797, 4154, 4798, 1489, 5746, 3544, 5747, 2624, 2903, # 5216 + 4155, 5748, 5749, 2981, 5750, 5751, 5752, 5753, 3196, 4799, 4800, 2185, 1722, 5754, 3269, 3270, # 5232 + 1843, 3665, 1715, 481, 365, 1976, 1857, 5755, 5756, 1963, 2498, 4801, 5757, 2127, 3666, 3271, # 5248 + 433, 1895, 2064, 2076, 5758, 602, 2750, 5759, 5760, 5761, 5762, 5763, 3032, 1628, 3437, 5764, # 5264 + 3197, 4802, 4156, 2904, 4803, 2519, 5765, 2551, 2782, 5766, 5767, 5768, 3343, 4804, 2905, 5769, # 5280 + 4805, 5770, 2864, 4806, 4807, 1221, 2982, 4157, 2520, 5771, 5772, 5773, 1868, 1990, 5774, 5775, # 5296 + 5776, 1896, 5777, 5778, 4808, 1897, 4158, 318, 5779, 2095, 4159, 4437, 5780, 5781, 485, 5782, # 5312 + 938, 3941, 553, 2680, 116, 5783, 3942, 3667, 5784, 3545, 2681, 2783, 3438, 3344, 2820, 5785, # 5328 + 3668, 2943, 4160, 1747, 2944, 2983, 5786, 5787, 207, 5788, 4809, 5789, 4810, 2521, 5790, 3033, # 5344 + 890, 3669, 3943, 5791, 1878, 3798, 3439, 5792, 2186, 2358, 3440, 1652, 5793, 5794, 5795, 941, # 5360 + 2299, 208, 3546, 4161, 2020, 330, 4438, 3944, 2906, 2499, 3799, 4439, 4811, 5796, 5797, 5798, # 5376 ) - diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/big5prober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/big5prober.py index 98f9970..0654b19 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/big5prober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/big5prober.py @@ -24,24 +24,25 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations -from .mbcharsetprober import MultiByteCharSetProber -from .codingstatemachine import CodingStateMachine from .chardistribution import Big5DistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber from .mbcssm import BIG5_SM_MODEL class Big5Prober(MultiByteCharSetProber): def __init__(self): - super(Big5Prober, self).__init__() + super().__init__() self.coding_sm = CodingStateMachine(BIG5_SM_MODEL) self.distribution_analyzer = Big5DistributionAnalysis() self.reset() @property def charset_name(self): - return "Big5" + return 'Big5' @property def language(self): - return "Chinese" + return 'Chinese' diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/chardistribution.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/chardistribution.py index c0395f4..0d1cdf3 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/chardistribution.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/chardistribution.py @@ -24,20 +24,26 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations -from .euctwfreq import (EUCTW_CHAR_TO_FREQ_ORDER, EUCTW_TABLE_SIZE, - EUCTW_TYPICAL_DISTRIBUTION_RATIO) -from .euckrfreq import (EUCKR_CHAR_TO_FREQ_ORDER, EUCKR_TABLE_SIZE, - EUCKR_TYPICAL_DISTRIBUTION_RATIO) -from .gb2312freq import (GB2312_CHAR_TO_FREQ_ORDER, GB2312_TABLE_SIZE, - GB2312_TYPICAL_DISTRIBUTION_RATIO) -from .big5freq import (BIG5_CHAR_TO_FREQ_ORDER, BIG5_TABLE_SIZE, - BIG5_TYPICAL_DISTRIBUTION_RATIO) -from .jisfreq import (JIS_CHAR_TO_FREQ_ORDER, JIS_TABLE_SIZE, - JIS_TYPICAL_DISTRIBUTION_RATIO) +from .big5freq import BIG5_CHAR_TO_FREQ_ORDER +from .big5freq import BIG5_TABLE_SIZE +from .big5freq import BIG5_TYPICAL_DISTRIBUTION_RATIO +from .euckrfreq import EUCKR_CHAR_TO_FREQ_ORDER +from .euckrfreq import EUCKR_TABLE_SIZE +from .euckrfreq import EUCKR_TYPICAL_DISTRIBUTION_RATIO +from .euctwfreq import EUCTW_CHAR_TO_FREQ_ORDER +from .euctwfreq import EUCTW_TABLE_SIZE +from .euctwfreq import EUCTW_TYPICAL_DISTRIBUTION_RATIO +from .gb2312freq import GB2312_CHAR_TO_FREQ_ORDER +from .gb2312freq import GB2312_TABLE_SIZE +from .gb2312freq import GB2312_TYPICAL_DISTRIBUTION_RATIO +from .jisfreq import JIS_CHAR_TO_FREQ_ORDER +from .jisfreq import JIS_TABLE_SIZE +from .jisfreq import JIS_TYPICAL_DISTRIBUTION_RATIO -class CharDistributionAnalysis(object): +class CharDistributionAnalysis: ENOUGH_DATA_THRESHOLD = 1024 SURE_YES = 0.99 SURE_NO = 0.01 @@ -89,8 +95,12 @@ class CharDistributionAnalysis(object): return self.SURE_NO if self._total_chars != self._freq_chars: - r = (self._freq_chars / ((self._total_chars - self._freq_chars) - * self.typical_distribution_ratio)) + r = ( + self._freq_chars / ( + (self._total_chars - self._freq_chars) * + self.typical_distribution_ratio + ) + ) if r < self.SURE_YES: return r @@ -112,7 +122,7 @@ class CharDistributionAnalysis(object): class EUCTWDistributionAnalysis(CharDistributionAnalysis): def __init__(self): - super(EUCTWDistributionAnalysis, self).__init__() + super().__init__() self._char_to_freq_order = EUCTW_CHAR_TO_FREQ_ORDER self._table_size = EUCTW_TABLE_SIZE self.typical_distribution_ratio = EUCTW_TYPICAL_DISTRIBUTION_RATIO @@ -131,7 +141,7 @@ class EUCTWDistributionAnalysis(CharDistributionAnalysis): class EUCKRDistributionAnalysis(CharDistributionAnalysis): def __init__(self): - super(EUCKRDistributionAnalysis, self).__init__() + super().__init__() self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER self._table_size = EUCKR_TABLE_SIZE self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO @@ -150,7 +160,7 @@ class EUCKRDistributionAnalysis(CharDistributionAnalysis): class GB2312DistributionAnalysis(CharDistributionAnalysis): def __init__(self): - super(GB2312DistributionAnalysis, self).__init__() + super().__init__() self._char_to_freq_order = GB2312_CHAR_TO_FREQ_ORDER self._table_size = GB2312_TABLE_SIZE self.typical_distribution_ratio = GB2312_TYPICAL_DISTRIBUTION_RATIO @@ -169,7 +179,7 @@ class GB2312DistributionAnalysis(CharDistributionAnalysis): class Big5DistributionAnalysis(CharDistributionAnalysis): def __init__(self): - super(Big5DistributionAnalysis, self).__init__() + super().__init__() self._char_to_freq_order = BIG5_CHAR_TO_FREQ_ORDER self._table_size = BIG5_TABLE_SIZE self.typical_distribution_ratio = BIG5_TYPICAL_DISTRIBUTION_RATIO @@ -191,7 +201,7 @@ class Big5DistributionAnalysis(CharDistributionAnalysis): class SJISDistributionAnalysis(CharDistributionAnalysis): def __init__(self): - super(SJISDistributionAnalysis, self).__init__() + super().__init__() self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER self._table_size = JIS_TABLE_SIZE self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO @@ -216,7 +226,7 @@ class SJISDistributionAnalysis(CharDistributionAnalysis): class EUCJPDistributionAnalysis(CharDistributionAnalysis): def __init__(self): - super(EUCJPDistributionAnalysis, self).__init__() + super().__init__() self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER self._table_size = JIS_TABLE_SIZE self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetgroupprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetgroupprober.py index 5812cef..f148543 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetgroupprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetgroupprober.py @@ -24,20 +24,21 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations -from .enums import ProbingState from .charsetprober import CharSetProber +from .enums import ProbingState class CharSetGroupProber(CharSetProber): def __init__(self, lang_filter=None): - super(CharSetGroupProber, self).__init__(lang_filter=lang_filter) + super().__init__(lang_filter=lang_filter) self._active_num = 0 self.probers = [] self._best_guess_prober = None def reset(self): - super(CharSetGroupProber, self).reset() + super().reset() self._active_num = 0 for prober in self.probers: if prober: diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetprober.py index eac4e59..859d324 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetprober.py @@ -25,6 +25,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations import logging import re @@ -32,7 +33,7 @@ import re from .enums import ProbingState -class CharSetProber(object): +class CharSetProber: SHORTCUT_THRESHOLD = 0.95 @@ -83,8 +84,10 @@ class CharSetProber(object): # This regex expression filters out only words that have at-least one # international character. The word may include one marker character at # the end. - words = re.findall(b'[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?', - buf) + words = re.findall( + b'[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?', + buf, + ) for word in words: filtered.extend(word[:-1]) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/__init__.py index 8b13789..e69de29 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/__init__.py @@ -1 +0,0 @@ - diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/chardetect.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/chardetect.py index 6d6f93a..0eea249 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/chardetect.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/chardetect.py @@ -11,8 +11,7 @@ Example:: If no paths are provided, it takes its input from stdin. """ - -from __future__ import absolute_import, print_function, unicode_literals +from __future__ import annotations import argparse import sys @@ -44,10 +43,12 @@ def description_of(lines, name='stdin'): if PY2: name = name.decode(sys.getfilesystemencoding(), 'ignore') if result['encoding']: - return '{}: {} with confidence {}'.format(name, result['encoding'], - result['confidence']) + return '{}: {} with confidence {}'.format( + name, result['encoding'], + result['confidence'], + ) else: - return '{}: no result'.format(name) + return f'{name}: no result' def main(argv=None): @@ -60,23 +61,30 @@ def main(argv=None): """ # Get command line arguments parser = argparse.ArgumentParser( - description="Takes one or more file paths and reports their detected \ - encodings") - parser.add_argument('input', - help='File whose encoding we would like to determine. \ + description='Takes one or more file paths and reports their detected \ + encodings', + ) + parser.add_argument( + 'input', + help='File whose encoding we would like to determine. \ (default: stdin)', - type=argparse.FileType('rb'), nargs='*', - default=[sys.stdin if PY2 else sys.stdin.buffer]) - parser.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + type=argparse.FileType('rb'), nargs='*', + default=[sys.stdin if PY2 else sys.stdin.buffer], + ) + parser.add_argument( + '--version', action='version', + version=f'%(prog)s {__version__}', + ) args = parser.parse_args(argv) for f in args.input: if f.isatty(): - print("You are running chardetect interactively. Press " + - "CTRL-D twice at the start of a blank line to signal the " + - "end of your input. If you want help, run chardetect " + - "--help\n", file=sys.stderr) + print( + 'You are running chardetect interactively. Press ' + + 'CTRL-D twice at the start of a blank line to signal the ' + + 'end of your input. If you want help, run chardetect ' + + '--help\n', file=sys.stderr, + ) print(description_of(f, f.name)) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/codingstatemachine.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/codingstatemachine.py index 68fba44..9e2dab0 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/codingstatemachine.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/codingstatemachine.py @@ -24,13 +24,14 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations import logging from .enums import MachineState -class CodingStateMachine(object): +class CodingStateMachine: """ A state machine to verify a byte sequence for a particular encoding. For each byte the detector receives, it will feed that byte to every active @@ -52,6 +53,7 @@ class CodingStateMachine(object): negative answer for this encoding. Detector will exclude this encoding from consideration from here on. """ + def __init__(self, sm): self._model = sm self._curr_byte_pos = 0 @@ -71,8 +73,10 @@ class CodingStateMachine(object): self._curr_byte_pos = 0 self._curr_char_len = self._model['char_len_table'][byte_class] # from byte's class and state_table, we get its next state - curr_state = (self._curr_state * self._model['class_factor'] - + byte_class) + curr_state = ( + self._curr_state * self._model['class_factor'] + + byte_class + ) self._curr_state = self._model['state_table'][curr_state] self._curr_byte_pos += 1 return self._curr_state diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/compat.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/compat.py index 8941572..3633d7b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/compat.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/compat.py @@ -18,19 +18,13 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations import sys -if sys.version_info < (3, 0): - PY2 = True - PY3 = False - string_types = (str, unicode) - text_type = unicode - iteritems = dict.iteritems -else: - PY2 = False - PY3 = True - string_types = (bytes, str) - text_type = str - iteritems = dict.items +PY2 = False +PY3 = True +string_types = (bytes, str) +text_type = str +iteritems = dict.items diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cp949prober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cp949prober.py index efd793a..8e03cc0 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cp949prober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/cp949prober.py @@ -24,6 +24,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from .chardistribution import EUCKRDistributionAnalysis from .codingstatemachine import CodingStateMachine @@ -33,7 +34,7 @@ from .mbcssm import CP949_SM_MODEL class CP949Prober(MultiByteCharSetProber): def __init__(self): - super(CP949Prober, self).__init__() + super().__init__() self.coding_sm = CodingStateMachine(CP949_SM_MODEL) # NOTE: CP949 is a superset of EUC-KR, so the distribution should be # not different. @@ -42,8 +43,8 @@ class CP949Prober(MultiByteCharSetProber): @property def charset_name(self): - return "CP949" + return 'CP949' @property def language(self): - return "Korean" + return 'Korean' diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/enums.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/enums.py index 0451207..a6e51f8 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/enums.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/enums.py @@ -3,9 +3,10 @@ All of the Enums that are used throughout the chardet package. :author: Dan Blanchard (dan.blanchard@gmail.com) """ +from __future__ import annotations -class InputState(object): +class InputState: """ This enum represents the different states a universal detector can be in. """ @@ -14,7 +15,7 @@ class InputState(object): HIGH_BYTE = 2 -class LanguageFilter(object): +class LanguageFilter: """ This enum represents the different language filters we can apply to a ``UniversalDetector``. @@ -29,7 +30,7 @@ class LanguageFilter(object): CJK = CHINESE | JAPANESE | KOREAN -class ProbingState(object): +class ProbingState: """ This enum represents the different states a prober can be in. """ @@ -38,7 +39,7 @@ class ProbingState(object): NOT_ME = 2 -class MachineState(object): +class MachineState: """ This enum represents the different states a state machine can be in. """ @@ -47,7 +48,7 @@ class MachineState(object): ITS_ME = 2 -class SequenceLikelihood(object): +class SequenceLikelihood: """ This enum represents the likelihood of a character following the previous one. """ @@ -62,7 +63,7 @@ class SequenceLikelihood(object): return 4 -class CharacterCategory(object): +class CharacterCategory: """ This enum represents the different categories language models for ``SingleByteCharsetProber`` put characters into. diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/escprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/escprober.py index c70493f..6353fd2 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/escprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/escprober.py @@ -24,12 +24,17 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from .charsetprober import CharSetProber from .codingstatemachine import CodingStateMachine -from .enums import LanguageFilter, ProbingState, MachineState -from .escsm import (HZ_SM_MODEL, ISO2022CN_SM_MODEL, ISO2022JP_SM_MODEL, - ISO2022KR_SM_MODEL) +from .enums import LanguageFilter +from .enums import MachineState +from .enums import ProbingState +from .escsm import HZ_SM_MODEL +from .escsm import ISO2022CN_SM_MODEL +from .escsm import ISO2022JP_SM_MODEL +from .escsm import ISO2022KR_SM_MODEL class EscCharSetProber(CharSetProber): @@ -40,7 +45,7 @@ class EscCharSetProber(CharSetProber): """ def __init__(self, lang_filter=None): - super(EscCharSetProber, self).__init__(lang_filter=lang_filter) + super().__init__(lang_filter=lang_filter) self.coding_sm = [] if self.lang_filter & LanguageFilter.CHINESE_SIMPLIFIED: self.coding_sm.append(CodingStateMachine(HZ_SM_MODEL)) @@ -56,7 +61,7 @@ class EscCharSetProber(CharSetProber): self.reset() def reset(self): - super(EscCharSetProber, self).reset() + super().reset() for coding_sm in self.coding_sm: if not coding_sm: continue diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/escsm.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/escsm.py index 0069523..1047bd1 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/escsm.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/escsm.py @@ -24,223 +24,230 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from .enums import MachineState HZ_CLS = ( -1,0,0,0,0,0,0,0, # 00 - 07 -0,0,0,0,0,0,0,0, # 08 - 0f -0,0,0,0,0,0,0,0, # 10 - 17 -0,0,0,1,0,0,0,0, # 18 - 1f -0,0,0,0,0,0,0,0, # 20 - 27 -0,0,0,0,0,0,0,0, # 28 - 2f -0,0,0,0,0,0,0,0, # 30 - 37 -0,0,0,0,0,0,0,0, # 38 - 3f -0,0,0,0,0,0,0,0, # 40 - 47 -0,0,0,0,0,0,0,0, # 48 - 4f -0,0,0,0,0,0,0,0, # 50 - 57 -0,0,0,0,0,0,0,0, # 58 - 5f -0,0,0,0,0,0,0,0, # 60 - 67 -0,0,0,0,0,0,0,0, # 68 - 6f -0,0,0,0,0,0,0,0, # 70 - 77 -0,0,0,4,0,5,2,0, # 78 - 7f -1,1,1,1,1,1,1,1, # 80 - 87 -1,1,1,1,1,1,1,1, # 88 - 8f -1,1,1,1,1,1,1,1, # 90 - 97 -1,1,1,1,1,1,1,1, # 98 - 9f -1,1,1,1,1,1,1,1, # a0 - a7 -1,1,1,1,1,1,1,1, # a8 - af -1,1,1,1,1,1,1,1, # b0 - b7 -1,1,1,1,1,1,1,1, # b8 - bf -1,1,1,1,1,1,1,1, # c0 - c7 -1,1,1,1,1,1,1,1, # c8 - cf -1,1,1,1,1,1,1,1, # d0 - d7 -1,1,1,1,1,1,1,1, # d8 - df -1,1,1,1,1,1,1,1, # e0 - e7 -1,1,1,1,1,1,1,1, # e8 - ef -1,1,1,1,1,1,1,1, # f0 - f7 -1,1,1,1,1,1,1,1, # f8 - ff + 1, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 + 0, 0, 0, 0, 0, 0, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 4, 0, 5, 2, 0, # 78 - 7f + 1, 1, 1, 1, 1, 1, 1, 1, # 80 - 87 + 1, 1, 1, 1, 1, 1, 1, 1, # 88 - 8f + 1, 1, 1, 1, 1, 1, 1, 1, # 90 - 97 + 1, 1, 1, 1, 1, 1, 1, 1, # 98 - 9f + 1, 1, 1, 1, 1, 1, 1, 1, # a0 - a7 + 1, 1, 1, 1, 1, 1, 1, 1, # a8 - af + 1, 1, 1, 1, 1, 1, 1, 1, # b0 - b7 + 1, 1, 1, 1, 1, 1, 1, 1, # b8 - bf + 1, 1, 1, 1, 1, 1, 1, 1, # c0 - c7 + 1, 1, 1, 1, 1, 1, 1, 1, # c8 - cf + 1, 1, 1, 1, 1, 1, 1, 1, # d0 - d7 + 1, 1, 1, 1, 1, 1, 1, 1, # d8 - df + 1, 1, 1, 1, 1, 1, 1, 1, # e0 - e7 + 1, 1, 1, 1, 1, 1, 1, 1, # e8 - ef + 1, 1, 1, 1, 1, 1, 1, 1, # f0 - f7 + 1, 1, 1, 1, 1, 1, 1, 1, # f8 - ff ) HZ_ST = ( -MachineState.START,MachineState.ERROR, 3,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,# 00-07 -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 08-0f -MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START, 4,MachineState.ERROR,# 10-17 - 5,MachineState.ERROR, 6,MachineState.ERROR, 5, 5, 4,MachineState.ERROR,# 18-1f - 4,MachineState.ERROR, 4, 4, 4,MachineState.ERROR, 4,MachineState.ERROR,# 20-27 - 4,MachineState.ITS_ME,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 28-2f + MachineState.START, MachineState.ERROR, 3, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, 4, MachineState.ERROR, # 10-17 + 5, MachineState.ERROR, 6, MachineState.ERROR, 5, 5, 4, MachineState.ERROR, # 18-1f + 4, MachineState.ERROR, 4, 4, 4, MachineState.ERROR, 4, MachineState.ERROR, # 20-27 + 4, MachineState.ITS_ME, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 28-2f ) HZ_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) -HZ_SM_MODEL = {'class_table': HZ_CLS, - 'class_factor': 6, - 'state_table': HZ_ST, - 'char_len_table': HZ_CHAR_LEN_TABLE, - 'name': "HZ-GB-2312", - 'language': 'Chinese'} +HZ_SM_MODEL = { + 'class_table': HZ_CLS, + 'class_factor': 6, + 'state_table': HZ_ST, + 'char_len_table': HZ_CHAR_LEN_TABLE, + 'name': 'HZ-GB-2312', + 'language': 'Chinese', +} ISO2022CN_CLS = ( -2,0,0,0,0,0,0,0, # 00 - 07 -0,0,0,0,0,0,0,0, # 08 - 0f -0,0,0,0,0,0,0,0, # 10 - 17 -0,0,0,1,0,0,0,0, # 18 - 1f -0,0,0,0,0,0,0,0, # 20 - 27 -0,3,0,0,0,0,0,0, # 28 - 2f -0,0,0,0,0,0,0,0, # 30 - 37 -0,0,0,0,0,0,0,0, # 38 - 3f -0,0,0,4,0,0,0,0, # 40 - 47 -0,0,0,0,0,0,0,0, # 48 - 4f -0,0,0,0,0,0,0,0, # 50 - 57 -0,0,0,0,0,0,0,0, # 58 - 5f -0,0,0,0,0,0,0,0, # 60 - 67 -0,0,0,0,0,0,0,0, # 68 - 6f -0,0,0,0,0,0,0,0, # 70 - 77 -0,0,0,0,0,0,0,0, # 78 - 7f -2,2,2,2,2,2,2,2, # 80 - 87 -2,2,2,2,2,2,2,2, # 88 - 8f -2,2,2,2,2,2,2,2, # 90 - 97 -2,2,2,2,2,2,2,2, # 98 - 9f -2,2,2,2,2,2,2,2, # a0 - a7 -2,2,2,2,2,2,2,2, # a8 - af -2,2,2,2,2,2,2,2, # b0 - b7 -2,2,2,2,2,2,2,2, # b8 - bf -2,2,2,2,2,2,2,2, # c0 - c7 -2,2,2,2,2,2,2,2, # c8 - cf -2,2,2,2,2,2,2,2, # d0 - d7 -2,2,2,2,2,2,2,2, # d8 - df -2,2,2,2,2,2,2,2, # e0 - e7 -2,2,2,2,2,2,2,2, # e8 - ef -2,2,2,2,2,2,2,2, # f0 - f7 -2,2,2,2,2,2,2,2, # f8 - ff + 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 + 0, 3, 0, 0, 0, 0, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 4, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87 + 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f + 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97 + 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f + 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 + 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef + 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 + 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff ) ISO2022CN_ST = ( -MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 00-07 -MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 08-0f -MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 10-17 -MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,# 18-1f -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 20-27 - 5, 6,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 28-2f -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 30-37 -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,# 38-3f + MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 00-07 + MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 08-0f + MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 10-17 + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, # 18-1f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 20-27 + 5, 6, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 28-2f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 30-37 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.START, # 38-3f ) ISO2022CN_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0) -ISO2022CN_SM_MODEL = {'class_table': ISO2022CN_CLS, - 'class_factor': 9, - 'state_table': ISO2022CN_ST, - 'char_len_table': ISO2022CN_CHAR_LEN_TABLE, - 'name': "ISO-2022-CN", - 'language': 'Chinese'} +ISO2022CN_SM_MODEL = { + 'class_table': ISO2022CN_CLS, + 'class_factor': 9, + 'state_table': ISO2022CN_ST, + 'char_len_table': ISO2022CN_CHAR_LEN_TABLE, + 'name': 'ISO-2022-CN', + 'language': 'Chinese', +} ISO2022JP_CLS = ( -2,0,0,0,0,0,0,0, # 00 - 07 -0,0,0,0,0,0,2,2, # 08 - 0f -0,0,0,0,0,0,0,0, # 10 - 17 -0,0,0,1,0,0,0,0, # 18 - 1f -0,0,0,0,7,0,0,0, # 20 - 27 -3,0,0,0,0,0,0,0, # 28 - 2f -0,0,0,0,0,0,0,0, # 30 - 37 -0,0,0,0,0,0,0,0, # 38 - 3f -6,0,4,0,8,0,0,0, # 40 - 47 -0,9,5,0,0,0,0,0, # 48 - 4f -0,0,0,0,0,0,0,0, # 50 - 57 -0,0,0,0,0,0,0,0, # 58 - 5f -0,0,0,0,0,0,0,0, # 60 - 67 -0,0,0,0,0,0,0,0, # 68 - 6f -0,0,0,0,0,0,0,0, # 70 - 77 -0,0,0,0,0,0,0,0, # 78 - 7f -2,2,2,2,2,2,2,2, # 80 - 87 -2,2,2,2,2,2,2,2, # 88 - 8f -2,2,2,2,2,2,2,2, # 90 - 97 -2,2,2,2,2,2,2,2, # 98 - 9f -2,2,2,2,2,2,2,2, # a0 - a7 -2,2,2,2,2,2,2,2, # a8 - af -2,2,2,2,2,2,2,2, # b0 - b7 -2,2,2,2,2,2,2,2, # b8 - bf -2,2,2,2,2,2,2,2, # c0 - c7 -2,2,2,2,2,2,2,2, # c8 - cf -2,2,2,2,2,2,2,2, # d0 - d7 -2,2,2,2,2,2,2,2, # d8 - df -2,2,2,2,2,2,2,2, # e0 - e7 -2,2,2,2,2,2,2,2, # e8 - ef -2,2,2,2,2,2,2,2, # f0 - f7 -2,2,2,2,2,2,2,2, # f8 - ff + 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 0, 0, 0, 0, 2, 2, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 7, 0, 0, 0, # 20 - 27 + 3, 0, 0, 0, 0, 0, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 6, 0, 4, 0, 8, 0, 0, 0, # 40 - 47 + 0, 9, 5, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87 + 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f + 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97 + 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f + 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 + 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef + 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 + 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff ) ISO2022JP_ST = ( -MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 00-07 -MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 08-0f -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 10-17 -MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,# 18-1f -MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,MachineState.ERROR,# 20-27 -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 6,MachineState.ITS_ME,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,# 28-2f -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,# 30-37 -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 38-3f -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.START,# 40-47 + MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 00-07 + MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 08-0f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 10-17 + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, # 18-1f + MachineState.ERROR, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, MachineState.ERROR, # 20-27 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 6, MachineState.ITS_ME, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, # 28-2f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, # 30-37 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 38-3f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.START, MachineState.START, # 40-47 ) ISO2022JP_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) -ISO2022JP_SM_MODEL = {'class_table': ISO2022JP_CLS, - 'class_factor': 10, - 'state_table': ISO2022JP_ST, - 'char_len_table': ISO2022JP_CHAR_LEN_TABLE, - 'name': "ISO-2022-JP", - 'language': 'Japanese'} +ISO2022JP_SM_MODEL = { + 'class_table': ISO2022JP_CLS, + 'class_factor': 10, + 'state_table': ISO2022JP_ST, + 'char_len_table': ISO2022JP_CHAR_LEN_TABLE, + 'name': 'ISO-2022-JP', + 'language': 'Japanese', +} ISO2022KR_CLS = ( -2,0,0,0,0,0,0,0, # 00 - 07 -0,0,0,0,0,0,0,0, # 08 - 0f -0,0,0,0,0,0,0,0, # 10 - 17 -0,0,0,1,0,0,0,0, # 18 - 1f -0,0,0,0,3,0,0,0, # 20 - 27 -0,4,0,0,0,0,0,0, # 28 - 2f -0,0,0,0,0,0,0,0, # 30 - 37 -0,0,0,0,0,0,0,0, # 38 - 3f -0,0,0,5,0,0,0,0, # 40 - 47 -0,0,0,0,0,0,0,0, # 48 - 4f -0,0,0,0,0,0,0,0, # 50 - 57 -0,0,0,0,0,0,0,0, # 58 - 5f -0,0,0,0,0,0,0,0, # 60 - 67 -0,0,0,0,0,0,0,0, # 68 - 6f -0,0,0,0,0,0,0,0, # 70 - 77 -0,0,0,0,0,0,0,0, # 78 - 7f -2,2,2,2,2,2,2,2, # 80 - 87 -2,2,2,2,2,2,2,2, # 88 - 8f -2,2,2,2,2,2,2,2, # 90 - 97 -2,2,2,2,2,2,2,2, # 98 - 9f -2,2,2,2,2,2,2,2, # a0 - a7 -2,2,2,2,2,2,2,2, # a8 - af -2,2,2,2,2,2,2,2, # b0 - b7 -2,2,2,2,2,2,2,2, # b8 - bf -2,2,2,2,2,2,2,2, # c0 - c7 -2,2,2,2,2,2,2,2, # c8 - cf -2,2,2,2,2,2,2,2, # d0 - d7 -2,2,2,2,2,2,2,2, # d8 - df -2,2,2,2,2,2,2,2, # e0 - e7 -2,2,2,2,2,2,2,2, # e8 - ef -2,2,2,2,2,2,2,2, # f0 - f7 -2,2,2,2,2,2,2,2, # f8 - ff + 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 3, 0, 0, 0, # 20 - 27 + 0, 4, 0, 0, 0, 0, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 5, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87 + 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f + 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97 + 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f + 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 + 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef + 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 + 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff ) ISO2022KR_ST = ( -MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,# 00-07 -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 08-0f -MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,MachineState.ERROR,# 10-17 -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 18-1f -MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 20-27 + MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, MachineState.ERROR, # 10-17 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 18-1f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 20-27 ) ISO2022KR_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) -ISO2022KR_SM_MODEL = {'class_table': ISO2022KR_CLS, - 'class_factor': 6, - 'state_table': ISO2022KR_ST, - 'char_len_table': ISO2022KR_CHAR_LEN_TABLE, - 'name': "ISO-2022-KR", - 'language': 'Korean'} - - +ISO2022KR_SM_MODEL = { + 'class_table': ISO2022KR_CLS, + 'class_factor': 6, + 'state_table': ISO2022KR_ST, + 'char_len_table': ISO2022KR_CHAR_LEN_TABLE, + 'name': 'ISO-2022-KR', + 'language': 'Korean', +} diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/eucjpprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/eucjpprober.py index 20ce8f7..0cc7716 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/eucjpprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/eucjpprober.py @@ -24,42 +24,46 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations -from .enums import ProbingState, MachineState -from .mbcharsetprober import MultiByteCharSetProber -from .codingstatemachine import CodingStateMachine from .chardistribution import EUCJPDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .enums import MachineState +from .enums import ProbingState from .jpcntx import EUCJPContextAnalysis +from .mbcharsetprober import MultiByteCharSetProber from .mbcssm import EUCJP_SM_MODEL class EUCJPProber(MultiByteCharSetProber): def __init__(self): - super(EUCJPProber, self).__init__() + super().__init__() self.coding_sm = CodingStateMachine(EUCJP_SM_MODEL) self.distribution_analyzer = EUCJPDistributionAnalysis() self.context_analyzer = EUCJPContextAnalysis() self.reset() def reset(self): - super(EUCJPProber, self).reset() + super().reset() self.context_analyzer.reset() @property def charset_name(self): - return "EUC-JP" + return 'EUC-JP' @property def language(self): - return "Japanese" + return 'Japanese' def feed(self, byte_str): for i in range(len(byte_str)): # PY3K: byte_str is a byte array, so byte_str[i] is an int, not a byte coding_state = self.coding_sm.next_state(byte_str[i]) if coding_state == MachineState.ERROR: - self.logger.debug('%s %s prober hit error at byte %s', - self.charset_name, self.language, i) + self.logger.debug( + '%s %s prober hit error at byte %s', + self.charset_name, self.language, i, + ) self._state = ProbingState.NOT_ME break elif coding_state == MachineState.ITS_ME: @@ -72,16 +76,22 @@ class EUCJPProber(MultiByteCharSetProber): self.context_analyzer.feed(self._last_char, char_len) self.distribution_analyzer.feed(self._last_char, char_len) else: - self.context_analyzer.feed(byte_str[i - 1:i + 1], - char_len) - self.distribution_analyzer.feed(byte_str[i - 1:i + 1], - char_len) + self.context_analyzer.feed( + byte_str[i - 1:i + 1], + char_len, + ) + self.distribution_analyzer.feed( + byte_str[i - 1:i + 1], + char_len, + ) self._last_char[0] = byte_str[-1] if self.state == ProbingState.DETECTING: - if (self.context_analyzer.got_enough_data() and - (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + if ( + self.context_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD) + ): self._state = ProbingState.FOUND_IT return self.state diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrfreq.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrfreq.py index b68078c..e85eaec 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrfreq.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrfreq.py @@ -24,9 +24,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### - # Sampling from about 20M text materials include literature and computer technology - # 128 --> 0.79 # 256 --> 0.92 # 512 --> 0.986 @@ -37,6 +35,7 @@ # Random Distribution Ration = 512 / (2350-512) = 0.279. # # Typical Distribution Ratio +from __future__ import annotations EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 @@ -44,152 +43,151 @@ EUCKR_TABLE_SIZE = 2352 # Char to FreqOrder table , EUCKR_CHAR_TO_FREQ_ORDER = ( - 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, -1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, -1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734, - 945,1400,1735, 47, 904,1270,1736,1737, 773, 248,1738, 409, 313, 786, 429,1739, - 116, 987, 813,1401, 683, 75,1204, 145,1740,1741,1742,1743, 16, 847, 667, 622, - 708,1744,1745,1746, 966, 787, 304, 129,1747, 60, 820, 123, 676,1748,1749,1750, -1751, 617,1752, 626,1753,1754,1755,1756, 653,1757,1758,1759,1760,1761,1762, 856, - 344,1763,1764,1765,1766, 89, 401, 418, 806, 905, 848,1767,1768,1769, 946,1205, - 709,1770,1118,1771, 241,1772,1773,1774,1271,1775, 569,1776, 999,1777,1778,1779, -1780, 337, 751,1058, 28, 628, 254,1781, 177, 906, 270, 349, 891,1079,1782, 19, -1783, 379,1784, 315,1785, 629, 754,1402, 559,1786, 636, 203,1206,1787, 710, 567, -1788, 935, 814,1789,1790,1207, 766, 528,1791,1792,1208,1793,1794,1795,1796,1797, -1403,1798,1799, 533,1059,1404,1405,1156,1406, 936, 884,1080,1800, 351,1801,1802, -1803,1804,1805, 801,1806,1807,1808,1119,1809,1157, 714, 474,1407,1810, 298, 899, - 885,1811,1120, 802,1158,1812, 892,1813,1814,1408, 659,1815,1816,1121,1817,1818, -1819,1820,1821,1822, 319,1823, 594, 545,1824, 815, 937,1209,1825,1826, 573,1409, -1022,1827,1210,1828,1829,1830,1831,1832,1833, 556, 722, 807,1122,1060,1834, 697, -1835, 900, 557, 715,1836,1410, 540,1411, 752,1159, 294, 597,1211, 976, 803, 770, -1412,1837,1838, 39, 794,1413, 358,1839, 371, 925,1840, 453, 661, 788, 531, 723, - 544,1023,1081, 869, 91,1841, 392, 430, 790, 602,1414, 677,1082, 457,1415,1416, -1842,1843, 475, 327,1024,1417, 795, 121,1844, 733, 403,1418,1845,1846,1847, 300, - 119, 711,1212, 627,1848,1272, 207,1849,1850, 796,1213, 382,1851, 519,1852,1083, - 893,1853,1854,1855, 367, 809, 487, 671,1856, 663,1857,1858, 956, 471, 306, 857, -1859,1860,1160,1084,1861,1862,1863,1864,1865,1061,1866,1867,1868,1869,1870,1871, - 282, 96, 574,1872, 502,1085,1873,1214,1874, 907,1875,1876, 827, 977,1419,1420, -1421, 268,1877,1422,1878,1879,1880, 308,1881, 2, 537,1882,1883,1215,1884,1885, - 127, 791,1886,1273,1423,1887, 34, 336, 404, 643,1888, 571, 654, 894, 840,1889, - 0, 886,1274, 122, 575, 260, 908, 938,1890,1275, 410, 316,1891,1892, 100,1893, -1894,1123, 48,1161,1124,1025,1895, 633, 901,1276,1896,1897, 115, 816,1898, 317, -1899, 694,1900, 909, 734,1424, 572, 866,1425, 691, 85, 524,1010, 543, 394, 841, -1901,1902,1903,1026,1904,1905,1906,1907,1908,1909, 30, 451, 651, 988, 310,1910, -1911,1426, 810,1216, 93,1912,1913,1277,1217,1914, 858, 759, 45, 58, 181, 610, - 269,1915,1916, 131,1062, 551, 443,1000, 821,1427, 957, 895,1086,1917,1918, 375, -1919, 359,1920, 687,1921, 822,1922, 293,1923,1924, 40, 662, 118, 692, 29, 939, - 887, 640, 482, 174,1925, 69,1162, 728,1428, 910,1926,1278,1218,1279, 386, 870, - 217, 854,1163, 823,1927,1928,1929,1930, 834,1931, 78,1932, 859,1933,1063,1934, -1935,1936,1937, 438,1164, 208, 595,1938,1939,1940,1941,1219,1125,1942, 280, 888, -1429,1430,1220,1431,1943,1944,1945,1946,1947,1280, 150, 510,1432,1948,1949,1950, -1951,1952,1953,1954,1011,1087,1955,1433,1043,1956, 881,1957, 614, 958,1064,1065, -1221,1958, 638,1001, 860, 967, 896,1434, 989, 492, 553,1281,1165,1959,1282,1002, -1283,1222,1960,1961,1962,1963, 36, 383, 228, 753, 247, 454,1964, 876, 678,1965, -1966,1284, 126, 464, 490, 835, 136, 672, 529, 940,1088,1435, 473,1967,1968, 467, - 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882,1126,1285, - 639,1044, 133, 140, 288, 360, 811, 563,1027, 561, 142, 523,1969,1970,1971, 7, - 103, 296, 439, 407, 506, 634, 990,1972,1973,1974,1975, 645,1976,1977,1978,1979, -1980,1981, 236,1982,1436,1983,1984,1089, 192, 828, 618, 518,1166, 333,1127,1985, - 818,1223,1986,1987,1988,1989,1990,1991,1992,1993, 342,1128,1286, 746, 842,1994, -1995, 560, 223,1287, 98, 8, 189, 650, 978,1288,1996,1437,1997, 17, 345, 250, - 423, 277, 234, 512, 226, 97, 289, 42, 167,1998, 201,1999,2000, 843, 836, 824, - 532, 338, 783,1090, 182, 576, 436,1438,1439, 527, 500,2001, 947, 889,2002,2003, -2004,2005, 262, 600, 314, 447,2006, 547,2007, 693, 738,1129,2008, 71,1440, 745, - 619, 688,2009, 829,2010,2011, 147,2012, 33, 948,2013,2014, 74, 224,2015, 61, - 191, 918, 399, 637,2016,1028,1130, 257, 902,2017,2018,2019,2020,2021,2022,2023, -2024,2025,2026, 837,2027,2028,2029,2030, 179, 874, 591, 52, 724, 246,2031,2032, -2033,2034,1167, 969,2035,1289, 630, 605, 911,1091,1168,2036,2037,2038,1441, 912, -2039, 623,2040,2041, 253,1169,1290,2042,1442, 146, 620, 611, 577, 433,2043,1224, - 719,1170, 959, 440, 437, 534, 84, 388, 480,1131, 159, 220, 198, 679,2044,1012, - 819,1066,1443, 113,1225, 194, 318,1003,1029,2045,2046,2047,2048,1067,2049,2050, -2051,2052,2053, 59, 913, 112,2054, 632,2055, 455, 144, 739,1291,2056, 273, 681, - 499,2057, 448,2058,2059, 760,2060,2061, 970, 384, 169, 245,1132,2062,2063, 414, -1444,2064,2065, 41, 235,2066, 157, 252, 877, 568, 919, 789, 580,2067, 725,2068, -2069,1292,2070,2071,1445,2072,1446,2073,2074, 55, 588, 66,1447, 271,1092,2075, -1226,2076, 960,1013, 372,2077,2078,2079,2080,2081,1293,2082,2083,2084,2085, 850, -2086,2087,2088,2089,2090, 186,2091,1068, 180,2092,2093,2094, 109,1227, 522, 606, -2095, 867,1448,1093, 991,1171, 926, 353,1133,2096, 581,2097,2098,2099,1294,1449, -1450,2100, 596,1172,1014,1228,2101,1451,1295,1173,1229,2102,2103,1296,1134,1452, - 949,1135,2104,2105,1094,1453,1454,1455,2106,1095,2107,2108,2109,2110,2111,2112, -2113,2114,2115,2116,2117, 804,2118,2119,1230,1231, 805,1456, 405,1136,2120,2121, -2122,2123,2124, 720, 701,1297, 992,1457, 927,1004,2125,2126,2127,2128,2129,2130, - 22, 417,2131, 303,2132, 385,2133, 971, 520, 513,2134,1174, 73,1096, 231, 274, - 962,1458, 673,2135,1459,2136, 152,1137,2137,2138,2139,2140,1005,1138,1460,1139, -2141,2142,2143,2144, 11, 374, 844,2145, 154,1232, 46,1461,2146, 838, 830, 721, -1233, 106,2147, 90, 428, 462, 578, 566,1175, 352,2148,2149, 538,1234, 124,1298, -2150,1462, 761, 565,2151, 686,2152, 649,2153, 72, 173,2154, 460, 415,2155,1463, -2156,1235, 305,2157,2158,2159,2160,2161,2162, 579,2163,2164,2165,2166,2167, 747, -2168,2169,2170,2171,1464, 669,2172,2173,2174,2175,2176,1465,2177, 23, 530, 285, -2178, 335, 729,2179, 397,2180,2181,2182,1030,2183,2184, 698,2185,2186, 325,2187, -2188, 369,2189, 799,1097,1015, 348,2190,1069, 680,2191, 851,1466,2192,2193, 10, -2194, 613, 424,2195, 979, 108, 449, 589, 27, 172, 81,1031, 80, 774, 281, 350, -1032, 525, 301, 582,1176,2196, 674,1045,2197,2198,1467, 730, 762,2199,2200,2201, -2202,1468,2203, 993,2204,2205, 266,1070, 963,1140,2206,2207,2208, 664,1098, 972, -2209,2210,2211,1177,1469,1470, 871,2212,2213,2214,2215,2216,1471,2217,2218,2219, -2220,2221,2222,2223,2224,2225,2226,2227,1472,1236,2228,2229,2230,2231,2232,2233, -2234,2235,1299,2236,2237, 200,2238, 477, 373,2239,2240, 731, 825, 777,2241,2242, -2243, 521, 486, 548,2244,2245,2246,1473,1300, 53, 549, 137, 875, 76, 158,2247, -1301,1474, 469, 396,1016, 278, 712,2248, 321, 442, 503, 767, 744, 941,1237,1178, -1475,2249, 82, 178,1141,1179, 973,2250,1302,2251, 297,2252,2253, 570,2254,2255, -2256, 18, 450, 206,2257, 290, 292,1142,2258, 511, 162, 99, 346, 164, 735,2259, -1476,1477, 4, 554, 343, 798,1099,2260,1100,2261, 43, 171,1303, 139, 215,2262, -2263, 717, 775,2264,1033, 322, 216,2265, 831,2266, 149,2267,1304,2268,2269, 702, -1238, 135, 845, 347, 309,2270, 484,2271, 878, 655, 238,1006,1478,2272, 67,2273, - 295,2274,2275, 461,2276, 478, 942, 412,2277,1034,2278,2279,2280, 265,2281, 541, -2282,2283,2284,2285,2286, 70, 852,1071,2287,2288,2289,2290, 21, 56, 509, 117, - 432,2291,2292, 331, 980, 552,1101, 148, 284, 105, 393,1180,1239, 755,2293, 187, -2294,1046,1479,2295, 340,2296, 63,1047, 230,2297,2298,1305, 763,1306, 101, 800, - 808, 494,2299,2300,2301, 903,2302, 37,1072, 14, 5,2303, 79, 675,2304, 312, -2305,2306,2307,2308,2309,1480, 6,1307,2310,2311,2312, 1, 470, 35, 24, 229, -2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964,2314, 259,2315, - 501, 380,2316,2317, 83, 981, 153, 689,1308,1481,1482,1483,2318,2319, 716,1484, -2320,2321,2322,2323,2324,2325,1485,2326,2327, 128, 57, 68, 261,1048, 211, 170, -1240, 31,2328, 51, 435, 742,2329,2330,2331, 635,2332, 264, 456,2333,2334,2335, - 425,2336,1486, 143, 507, 263, 943,2337, 363, 920,1487, 256,1488,1102, 243, 601, -1489,2338,2339,2340,2341,2342,2343,2344, 861,2345,2346,2347,2348,2349,2350, 395, -2351,1490,1491, 62, 535, 166, 225,2352,2353, 668, 419,1241, 138, 604, 928,2354, -1181,2355,1492,1493,2356,2357,2358,1143,2359, 696,2360, 387, 307,1309, 682, 476, -2361,2362, 332, 12, 222, 156,2363, 232,2364, 641, 276, 656, 517,1494,1495,1035, - 416, 736,1496,2365,1017, 586,2366,2367,2368,1497,2369, 242,2370,2371,2372,1498, -2373, 965, 713,2374,2375,2376,2377, 740, 982,1499, 944,1500,1007,2378,2379,1310, -1501,2380,2381,2382, 785, 329,2383,2384,1502,2385,2386,2387, 932,2388,1503,2389, -2390,2391,2392,1242,2393,2394,2395,2396,2397, 994, 950,2398,2399,2400,2401,1504, -1311,2402,2403,2404,2405,1049, 749,2406,2407, 853, 718,1144,1312,2408,1182,1505, -2409,2410, 255, 516, 479, 564, 550, 214,1506,1507,1313, 413, 239, 444, 339,1145, -1036,1508,1509,1314,1037,1510,1315,2411,1511,2412,2413,2414, 176, 703, 497, 624, - 593, 921, 302,2415, 341, 165,1103,1512,2416,1513,2417,2418,2419, 376,2420, 700, -2421,2422,2423, 258, 768,1316,2424,1183,2425, 995, 608,2426,2427,2428,2429, 221, -2430,2431,2432,2433,2434,2435,2436,2437, 195, 323, 726, 188, 897, 983,1317, 377, - 644,1050, 879,2438, 452,2439,2440,2441,2442,2443,2444, 914,2445,2446,2447,2448, - 915, 489,2449,1514,1184,2450,2451, 515, 64, 427, 495,2452, 583,2453, 483, 485, -1038, 562, 213,1515, 748, 666,2454,2455,2456,2457, 334,2458, 780, 996,1008, 705, -1243,2459,2460,2461,2462,2463, 114,2464, 493,1146, 366, 163,1516, 961,1104,2465, - 291,2466,1318,1105,2467,1517, 365,2468, 355, 951,1244,2469,1319,2470, 631,2471, -2472, 218,1320, 364, 320, 756,1518,1519,1321,1520,1322,2473,2474,2475,2476, 997, -2477,2478,2479,2480, 665,1185,2481, 916,1521,2482,2483,2484, 584, 684,2485,2486, - 797,2487,1051,1186,2488,2489,2490,1522,2491,2492, 370,2493,1039,1187, 65,2494, - 434, 205, 463,1188,2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771, - 585,2496, 590, 505,1073,2497, 599, 244, 219, 917,1018, 952, 646,1523,2498,1323, -2499,2500, 49, 984, 354, 741,2501, 625,2502,1324,2503,1019, 190, 357, 757, 491, - 95, 782, 868,2504,2505,2506,2507,2508,2509, 134,1524,1074, 422,1525, 898,2510, - 161,2511,2512,2513,2514, 769,2515,1526,2516,2517, 411,1325,2518, 472,1527,2519, -2520,2521,2522,2523,2524, 985,2525,2526,2527,2528,2529,2530, 764,2531,1245,2532, -2533, 25, 204, 311,2534, 496,2535,1052,2536,2537,2538,2539,2540,2541,2542, 199, - 704, 504, 468, 758, 657,1528, 196, 44, 839,1246, 272, 750,2543, 765, 862,2544, -2545,1326,2546, 132, 615, 933,2547, 732,2548,2549,2550,1189,1529,2551, 283,1247, -1053, 607, 929,2552,2553,2554, 930, 183, 872, 616,1040,1147,2555,1148,1020, 441, - 249,1075,2556,2557,2558, 466, 743,2559,2560,2561, 92, 514, 426, 420, 526,2562, -2563,2564,2565,2566,2567,2568, 185,2569,2570,2571,2572, 776,1530, 658,2573, 362, -2574, 361, 922,1076, 793,2575,2576,2577,2578,2579,2580,1531, 251,2581,2582,2583, -2584,1532, 54, 612, 237,1327,2585,2586, 275, 408, 647, 111,2587,1533,1106, 465, - 3, 458, 9, 38,2588, 107, 110, 890, 209, 26, 737, 498,2589,1534,2590, 431, - 202, 88,1535, 356, 287,1107, 660,1149,2591, 381,1536, 986,1150, 445,1248,1151, - 974,2592,2593, 846,2594, 446, 953, 184,1249,1250, 727,2595, 923, 193, 883,2596, -2597,2598, 102, 324, 539, 817,2599, 421,1041,2600, 832,2601, 94, 175, 197, 406, -2602, 459,2603,2604,2605,2606,2607, 330, 555,2608,2609,2610, 706,1108, 389,2611, -2612,2613,2614, 233,2615, 833, 558, 931, 954,1251,2616,2617,1537, 546,2618,2619, -1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628, -2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042, - 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256 + 13, 130, 120, 1396, 481, 1719, 1720, 328, 609, 212, 1721, 707, 400, 299, 1722, 87, + 1397, 1723, 104, 536, 1117, 1203, 1724, 1267, 685, 1268, 508, 1725, 1726, 1727, 1728, 1398, + 1399, 1729, 1730, 1731, 141, 621, 326, 1057, 368, 1732, 267, 488, 20, 1733, 1269, 1734, + 945, 1400, 1735, 47, 904, 1270, 1736, 1737, 773, 248, 1738, 409, 313, 786, 429, 1739, + 116, 987, 813, 1401, 683, 75, 1204, 145, 1740, 1741, 1742, 1743, 16, 847, 667, 622, + 708, 1744, 1745, 1746, 966, 787, 304, 129, 1747, 60, 820, 123, 676, 1748, 1749, 1750, + 1751, 617, 1752, 626, 1753, 1754, 1755, 1756, 653, 1757, 1758, 1759, 1760, 1761, 1762, 856, + 344, 1763, 1764, 1765, 1766, 89, 401, 418, 806, 905, 848, 1767, 1768, 1769, 946, 1205, + 709, 1770, 1118, 1771, 241, 1772, 1773, 1774, 1271, 1775, 569, 1776, 999, 1777, 1778, 1779, + 1780, 337, 751, 1058, 28, 628, 254, 1781, 177, 906, 270, 349, 891, 1079, 1782, 19, + 1783, 379, 1784, 315, 1785, 629, 754, 1402, 559, 1786, 636, 203, 1206, 1787, 710, 567, + 1788, 935, 814, 1789, 1790, 1207, 766, 528, 1791, 1792, 1208, 1793, 1794, 1795, 1796, 1797, + 1403, 1798, 1799, 533, 1059, 1404, 1405, 1156, 1406, 936, 884, 1080, 1800, 351, 1801, 1802, + 1803, 1804, 1805, 801, 1806, 1807, 1808, 1119, 1809, 1157, 714, 474, 1407, 1810, 298, 899, + 885, 1811, 1120, 802, 1158, 1812, 892, 1813, 1814, 1408, 659, 1815, 1816, 1121, 1817, 1818, + 1819, 1820, 1821, 1822, 319, 1823, 594, 545, 1824, 815, 937, 1209, 1825, 1826, 573, 1409, + 1022, 1827, 1210, 1828, 1829, 1830, 1831, 1832, 1833, 556, 722, 807, 1122, 1060, 1834, 697, + 1835, 900, 557, 715, 1836, 1410, 540, 1411, 752, 1159, 294, 597, 1211, 976, 803, 770, + 1412, 1837, 1838, 39, 794, 1413, 358, 1839, 371, 925, 1840, 453, 661, 788, 531, 723, + 544, 1023, 1081, 869, 91, 1841, 392, 430, 790, 602, 1414, 677, 1082, 457, 1415, 1416, + 1842, 1843, 475, 327, 1024, 1417, 795, 121, 1844, 733, 403, 1418, 1845, 1846, 1847, 300, + 119, 711, 1212, 627, 1848, 1272, 207, 1849, 1850, 796, 1213, 382, 1851, 519, 1852, 1083, + 893, 1853, 1854, 1855, 367, 809, 487, 671, 1856, 663, 1857, 1858, 956, 471, 306, 857, + 1859, 1860, 1160, 1084, 1861, 1862, 1863, 1864, 1865, 1061, 1866, 1867, 1868, 1869, 1870, 1871, + 282, 96, 574, 1872, 502, 1085, 1873, 1214, 1874, 907, 1875, 1876, 827, 977, 1419, 1420, + 1421, 268, 1877, 1422, 1878, 1879, 1880, 308, 1881, 2, 537, 1882, 1883, 1215, 1884, 1885, + 127, 791, 1886, 1273, 1423, 1887, 34, 336, 404, 643, 1888, 571, 654, 894, 840, 1889, + 0, 886, 1274, 122, 575, 260, 908, 938, 1890, 1275, 410, 316, 1891, 1892, 100, 1893, + 1894, 1123, 48, 1161, 1124, 1025, 1895, 633, 901, 1276, 1896, 1897, 115, 816, 1898, 317, + 1899, 694, 1900, 909, 734, 1424, 572, 866, 1425, 691, 85, 524, 1010, 543, 394, 841, + 1901, 1902, 1903, 1026, 1904, 1905, 1906, 1907, 1908, 1909, 30, 451, 651, 988, 310, 1910, + 1911, 1426, 810, 1216, 93, 1912, 1913, 1277, 1217, 1914, 858, 759, 45, 58, 181, 610, + 269, 1915, 1916, 131, 1062, 551, 443, 1000, 821, 1427, 957, 895, 1086, 1917, 1918, 375, + 1919, 359, 1920, 687, 1921, 822, 1922, 293, 1923, 1924, 40, 662, 118, 692, 29, 939, + 887, 640, 482, 174, 1925, 69, 1162, 728, 1428, 910, 1926, 1278, 1218, 1279, 386, 870, + 217, 854, 1163, 823, 1927, 1928, 1929, 1930, 834, 1931, 78, 1932, 859, 1933, 1063, 1934, + 1935, 1936, 1937, 438, 1164, 208, 595, 1938, 1939, 1940, 1941, 1219, 1125, 1942, 280, 888, + 1429, 1430, 1220, 1431, 1943, 1944, 1945, 1946, 1947, 1280, 150, 510, 1432, 1948, 1949, 1950, + 1951, 1952, 1953, 1954, 1011, 1087, 1955, 1433, 1043, 1956, 881, 1957, 614, 958, 1064, 1065, + 1221, 1958, 638, 1001, 860, 967, 896, 1434, 989, 492, 553, 1281, 1165, 1959, 1282, 1002, + 1283, 1222, 1960, 1961, 1962, 1963, 36, 383, 228, 753, 247, 454, 1964, 876, 678, 1965, + 1966, 1284, 126, 464, 490, 835, 136, 672, 529, 940, 1088, 1435, 473, 1967, 1968, 467, + 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882, 1126, 1285, + 639, 1044, 133, 140, 288, 360, 811, 563, 1027, 561, 142, 523, 1969, 1970, 1971, 7, + 103, 296, 439, 407, 506, 634, 990, 1972, 1973, 1974, 1975, 645, 1976, 1977, 1978, 1979, + 1980, 1981, 236, 1982, 1436, 1983, 1984, 1089, 192, 828, 618, 518, 1166, 333, 1127, 1985, + 818, 1223, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 342, 1128, 1286, 746, 842, 1994, + 1995, 560, 223, 1287, 98, 8, 189, 650, 978, 1288, 1996, 1437, 1997, 17, 345, 250, + 423, 277, 234, 512, 226, 97, 289, 42, 167, 1998, 201, 1999, 2000, 843, 836, 824, + 532, 338, 783, 1090, 182, 576, 436, 1438, 1439, 527, 500, 2001, 947, 889, 2002, 2003, + 2004, 2005, 262, 600, 314, 447, 2006, 547, 2007, 693, 738, 1129, 2008, 71, 1440, 745, + 619, 688, 2009, 829, 2010, 2011, 147, 2012, 33, 948, 2013, 2014, 74, 224, 2015, 61, + 191, 918, 399, 637, 2016, 1028, 1130, 257, 902, 2017, 2018, 2019, 2020, 2021, 2022, 2023, + 2024, 2025, 2026, 837, 2027, 2028, 2029, 2030, 179, 874, 591, 52, 724, 246, 2031, 2032, + 2033, 2034, 1167, 969, 2035, 1289, 630, 605, 911, 1091, 1168, 2036, 2037, 2038, 1441, 912, + 2039, 623, 2040, 2041, 253, 1169, 1290, 2042, 1442, 146, 620, 611, 577, 433, 2043, 1224, + 719, 1170, 959, 440, 437, 534, 84, 388, 480, 1131, 159, 220, 198, 679, 2044, 1012, + 819, 1066, 1443, 113, 1225, 194, 318, 1003, 1029, 2045, 2046, 2047, 2048, 1067, 2049, 2050, + 2051, 2052, 2053, 59, 913, 112, 2054, 632, 2055, 455, 144, 739, 1291, 2056, 273, 681, + 499, 2057, 448, 2058, 2059, 760, 2060, 2061, 970, 384, 169, 245, 1132, 2062, 2063, 414, + 1444, 2064, 2065, 41, 235, 2066, 157, 252, 877, 568, 919, 789, 580, 2067, 725, 2068, + 2069, 1292, 2070, 2071, 1445, 2072, 1446, 2073, 2074, 55, 588, 66, 1447, 271, 1092, 2075, + 1226, 2076, 960, 1013, 372, 2077, 2078, 2079, 2080, 2081, 1293, 2082, 2083, 2084, 2085, 850, + 2086, 2087, 2088, 2089, 2090, 186, 2091, 1068, 180, 2092, 2093, 2094, 109, 1227, 522, 606, + 2095, 867, 1448, 1093, 991, 1171, 926, 353, 1133, 2096, 581, 2097, 2098, 2099, 1294, 1449, + 1450, 2100, 596, 1172, 1014, 1228, 2101, 1451, 1295, 1173, 1229, 2102, 2103, 1296, 1134, 1452, + 949, 1135, 2104, 2105, 1094, 1453, 1454, 1455, 2106, 1095, 2107, 2108, 2109, 2110, 2111, 2112, + 2113, 2114, 2115, 2116, 2117, 804, 2118, 2119, 1230, 1231, 805, 1456, 405, 1136, 2120, 2121, + 2122, 2123, 2124, 720, 701, 1297, 992, 1457, 927, 1004, 2125, 2126, 2127, 2128, 2129, 2130, + 22, 417, 2131, 303, 2132, 385, 2133, 971, 520, 513, 2134, 1174, 73, 1096, 231, 274, + 962, 1458, 673, 2135, 1459, 2136, 152, 1137, 2137, 2138, 2139, 2140, 1005, 1138, 1460, 1139, + 2141, 2142, 2143, 2144, 11, 374, 844, 2145, 154, 1232, 46, 1461, 2146, 838, 830, 721, + 1233, 106, 2147, 90, 428, 462, 578, 566, 1175, 352, 2148, 2149, 538, 1234, 124, 1298, + 2150, 1462, 761, 565, 2151, 686, 2152, 649, 2153, 72, 173, 2154, 460, 415, 2155, 1463, + 2156, 1235, 305, 2157, 2158, 2159, 2160, 2161, 2162, 579, 2163, 2164, 2165, 2166, 2167, 747, + 2168, 2169, 2170, 2171, 1464, 669, 2172, 2173, 2174, 2175, 2176, 1465, 2177, 23, 530, 285, + 2178, 335, 729, 2179, 397, 2180, 2181, 2182, 1030, 2183, 2184, 698, 2185, 2186, 325, 2187, + 2188, 369, 2189, 799, 1097, 1015, 348, 2190, 1069, 680, 2191, 851, 1466, 2192, 2193, 10, + 2194, 613, 424, 2195, 979, 108, 449, 589, 27, 172, 81, 1031, 80, 774, 281, 350, + 1032, 525, 301, 582, 1176, 2196, 674, 1045, 2197, 2198, 1467, 730, 762, 2199, 2200, 2201, + 2202, 1468, 2203, 993, 2204, 2205, 266, 1070, 963, 1140, 2206, 2207, 2208, 664, 1098, 972, + 2209, 2210, 2211, 1177, 1469, 1470, 871, 2212, 2213, 2214, 2215, 2216, 1471, 2217, 2218, 2219, + 2220, 2221, 2222, 2223, 2224, 2225, 2226, 2227, 1472, 1236, 2228, 2229, 2230, 2231, 2232, 2233, + 2234, 2235, 1299, 2236, 2237, 200, 2238, 477, 373, 2239, 2240, 731, 825, 777, 2241, 2242, + 2243, 521, 486, 548, 2244, 2245, 2246, 1473, 1300, 53, 549, 137, 875, 76, 158, 2247, + 1301, 1474, 469, 396, 1016, 278, 712, 2248, 321, 442, 503, 767, 744, 941, 1237, 1178, + 1475, 2249, 82, 178, 1141, 1179, 973, 2250, 1302, 2251, 297, 2252, 2253, 570, 2254, 2255, + 2256, 18, 450, 206, 2257, 290, 292, 1142, 2258, 511, 162, 99, 346, 164, 735, 2259, + 1476, 1477, 4, 554, 343, 798, 1099, 2260, 1100, 2261, 43, 171, 1303, 139, 215, 2262, + 2263, 717, 775, 2264, 1033, 322, 216, 2265, 831, 2266, 149, 2267, 1304, 2268, 2269, 702, + 1238, 135, 845, 347, 309, 2270, 484, 2271, 878, 655, 238, 1006, 1478, 2272, 67, 2273, + 295, 2274, 2275, 461, 2276, 478, 942, 412, 2277, 1034, 2278, 2279, 2280, 265, 2281, 541, + 2282, 2283, 2284, 2285, 2286, 70, 852, 1071, 2287, 2288, 2289, 2290, 21, 56, 509, 117, + 432, 2291, 2292, 331, 980, 552, 1101, 148, 284, 105, 393, 1180, 1239, 755, 2293, 187, + 2294, 1046, 1479, 2295, 340, 2296, 63, 1047, 230, 2297, 2298, 1305, 763, 1306, 101, 800, + 808, 494, 2299, 2300, 2301, 903, 2302, 37, 1072, 14, 5, 2303, 79, 675, 2304, 312, + 2305, 2306, 2307, 2308, 2309, 1480, 6, 1307, 2310, 2311, 2312, 1, 470, 35, 24, 229, + 2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964, 2314, 259, 2315, + 501, 380, 2316, 2317, 83, 981, 153, 689, 1308, 1481, 1482, 1483, 2318, 2319, 716, 1484, + 2320, 2321, 2322, 2323, 2324, 2325, 1485, 2326, 2327, 128, 57, 68, 261, 1048, 211, 170, + 1240, 31, 2328, 51, 435, 742, 2329, 2330, 2331, 635, 2332, 264, 456, 2333, 2334, 2335, + 425, 2336, 1486, 143, 507, 263, 943, 2337, 363, 920, 1487, 256, 1488, 1102, 243, 601, + 1489, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 861, 2345, 2346, 2347, 2348, 2349, 2350, 395, + 2351, 1490, 1491, 62, 535, 166, 225, 2352, 2353, 668, 419, 1241, 138, 604, 928, 2354, + 1181, 2355, 1492, 1493, 2356, 2357, 2358, 1143, 2359, 696, 2360, 387, 307, 1309, 682, 476, + 2361, 2362, 332, 12, 222, 156, 2363, 232, 2364, 641, 276, 656, 517, 1494, 1495, 1035, + 416, 736, 1496, 2365, 1017, 586, 2366, 2367, 2368, 1497, 2369, 242, 2370, 2371, 2372, 1498, + 2373, 965, 713, 2374, 2375, 2376, 2377, 740, 982, 1499, 944, 1500, 1007, 2378, 2379, 1310, + 1501, 2380, 2381, 2382, 785, 329, 2383, 2384, 1502, 2385, 2386, 2387, 932, 2388, 1503, 2389, + 2390, 2391, 2392, 1242, 2393, 2394, 2395, 2396, 2397, 994, 950, 2398, 2399, 2400, 2401, 1504, + 1311, 2402, 2403, 2404, 2405, 1049, 749, 2406, 2407, 853, 718, 1144, 1312, 2408, 1182, 1505, + 2409, 2410, 255, 516, 479, 564, 550, 214, 1506, 1507, 1313, 413, 239, 444, 339, 1145, + 1036, 1508, 1509, 1314, 1037, 1510, 1315, 2411, 1511, 2412, 2413, 2414, 176, 703, 497, 624, + 593, 921, 302, 2415, 341, 165, 1103, 1512, 2416, 1513, 2417, 2418, 2419, 376, 2420, 700, + 2421, 2422, 2423, 258, 768, 1316, 2424, 1183, 2425, 995, 608, 2426, 2427, 2428, 2429, 221, + 2430, 2431, 2432, 2433, 2434, 2435, 2436, 2437, 195, 323, 726, 188, 897, 983, 1317, 377, + 644, 1050, 879, 2438, 452, 2439, 2440, 2441, 2442, 2443, 2444, 914, 2445, 2446, 2447, 2448, + 915, 489, 2449, 1514, 1184, 2450, 2451, 515, 64, 427, 495, 2452, 583, 2453, 483, 485, + 1038, 562, 213, 1515, 748, 666, 2454, 2455, 2456, 2457, 334, 2458, 780, 996, 1008, 705, + 1243, 2459, 2460, 2461, 2462, 2463, 114, 2464, 493, 1146, 366, 163, 1516, 961, 1104, 2465, + 291, 2466, 1318, 1105, 2467, 1517, 365, 2468, 355, 951, 1244, 2469, 1319, 2470, 631, 2471, + 2472, 218, 1320, 364, 320, 756, 1518, 1519, 1321, 1520, 1322, 2473, 2474, 2475, 2476, 997, + 2477, 2478, 2479, 2480, 665, 1185, 2481, 916, 1521, 2482, 2483, 2484, 584, 684, 2485, 2486, + 797, 2487, 1051, 1186, 2488, 2489, 2490, 1522, 2491, 2492, 370, 2493, 1039, 1187, 65, 2494, + 434, 205, 463, 1188, 2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771, + 585, 2496, 590, 505, 1073, 2497, 599, 244, 219, 917, 1018, 952, 646, 1523, 2498, 1323, + 2499, 2500, 49, 984, 354, 741, 2501, 625, 2502, 1324, 2503, 1019, 190, 357, 757, 491, + 95, 782, 868, 2504, 2505, 2506, 2507, 2508, 2509, 134, 1524, 1074, 422, 1525, 898, 2510, + 161, 2511, 2512, 2513, 2514, 769, 2515, 1526, 2516, 2517, 411, 1325, 2518, 472, 1527, 2519, + 2520, 2521, 2522, 2523, 2524, 985, 2525, 2526, 2527, 2528, 2529, 2530, 764, 2531, 1245, 2532, + 2533, 25, 204, 311, 2534, 496, 2535, 1052, 2536, 2537, 2538, 2539, 2540, 2541, 2542, 199, + 704, 504, 468, 758, 657, 1528, 196, 44, 839, 1246, 272, 750, 2543, 765, 862, 2544, + 2545, 1326, 2546, 132, 615, 933, 2547, 732, 2548, 2549, 2550, 1189, 1529, 2551, 283, 1247, + 1053, 607, 929, 2552, 2553, 2554, 930, 183, 872, 616, 1040, 1147, 2555, 1148, 1020, 441, + 249, 1075, 2556, 2557, 2558, 466, 743, 2559, 2560, 2561, 92, 514, 426, 420, 526, 2562, + 2563, 2564, 2565, 2566, 2567, 2568, 185, 2569, 2570, 2571, 2572, 776, 1530, 658, 2573, 362, + 2574, 361, 922, 1076, 793, 2575, 2576, 2577, 2578, 2579, 2580, 1531, 251, 2581, 2582, 2583, + 2584, 1532, 54, 612, 237, 1327, 2585, 2586, 275, 408, 647, 111, 2587, 1533, 1106, 465, + 3, 458, 9, 38, 2588, 107, 110, 890, 209, 26, 737, 498, 2589, 1534, 2590, 431, + 202, 88, 1535, 356, 287, 1107, 660, 1149, 2591, 381, 1536, 986, 1150, 445, 1248, 1151, + 974, 2592, 2593, 846, 2594, 446, 953, 184, 1249, 1250, 727, 2595, 923, 193, 883, 2596, + 2597, 2598, 102, 324, 539, 817, 2599, 421, 1041, 2600, 832, 2601, 94, 175, 197, 406, + 2602, 459, 2603, 2604, 2605, 2606, 2607, 330, 555, 2608, 2609, 2610, 706, 1108, 389, 2611, + 2612, 2613, 2614, 233, 2615, 833, 558, 931, 954, 1251, 2616, 2617, 1537, 546, 2618, 2619, + 1009, 2620, 2621, 2622, 1538, 690, 1328, 2623, 955, 2624, 1539, 2625, 2626, 772, 2627, 2628, + 2629, 2630, 2631, 924, 648, 863, 603, 2632, 2633, 934, 1540, 864, 865, 2634, 642, 1042, + 670, 1190, 2635, 2636, 2637, 2638, 168, 2639, 652, 873, 542, 1054, 1541, 2640, 2641, 2642, # 512, 256 ) - diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrprober.py index 345a060..5a48eec 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrprober.py @@ -24,24 +24,25 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations -from .mbcharsetprober import MultiByteCharSetProber -from .codingstatemachine import CodingStateMachine from .chardistribution import EUCKRDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber from .mbcssm import EUCKR_SM_MODEL class EUCKRProber(MultiByteCharSetProber): def __init__(self): - super(EUCKRProber, self).__init__() + super().__init__() self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL) self.distribution_analyzer = EUCKRDistributionAnalysis() self.reset() @property def charset_name(self): - return "EUC-KR" + return 'EUC-KR' @property def language(self): - return "Korean" + return 'Korean' diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwfreq.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwfreq.py index ed7a995..3910f91 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwfreq.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwfreq.py @@ -24,12 +24,10 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### - # EUCTW frequency table # Converted from big5 work # by Taiwan's Mandarin Promotion Council # - # 128 --> 0.42261 # 256 --> 0.57851 # 512 --> 0.74851 @@ -40,6 +38,7 @@ # Random Distribution Ration = 512/(5401-512)=0.105 # # Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR +from __future__ import annotations EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 @@ -47,341 +46,340 @@ EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 EUCTW_TABLE_SIZE = 5376 EUCTW_CHAR_TO_FREQ_ORDER = ( - 1,1800,1506, 255,1431, 198, 9, 82, 6,7310, 177, 202,3615,1256,2808, 110, # 2742 -3735, 33,3241, 261, 76, 44,2113, 16,2931,2184,1176, 659,3868, 26,3404,2643, # 2758 -1198,3869,3313,4060, 410,2211, 302, 590, 361,1963, 8, 204, 58,4296,7311,1931, # 2774 - 63,7312,7313, 317,1614, 75, 222, 159,4061,2412,1480,7314,3500,3068, 224,2809, # 2790 -3616, 3, 10,3870,1471, 29,2774,1135,2852,1939, 873, 130,3242,1123, 312,7315, # 2806 -4297,2051, 507, 252, 682,7316, 142,1914, 124, 206,2932, 34,3501,3173, 64, 604, # 2822 -7317,2494,1976,1977, 155,1990, 645, 641,1606,7318,3405, 337, 72, 406,7319, 80, # 2838 - 630, 238,3174,1509, 263, 939,1092,2644, 756,1440,1094,3406, 449, 69,2969, 591, # 2854 - 179,2095, 471, 115,2034,1843, 60, 50,2970, 134, 806,1868, 734,2035,3407, 180, # 2870 - 995,1607, 156, 537,2893, 688,7320, 319,1305, 779,2144, 514,2374, 298,4298, 359, # 2886 -2495, 90,2707,1338, 663, 11, 906,1099,2545, 20,2436, 182, 532,1716,7321, 732, # 2902 -1376,4062,1311,1420,3175, 25,2312,1056, 113, 399, 382,1949, 242,3408,2467, 529, # 2918 -3243, 475,1447,3617,7322, 117, 21, 656, 810,1297,2295,2329,3502,7323, 126,4063, # 2934 - 706, 456, 150, 613,4299, 71,1118,2036,4064, 145,3069, 85, 835, 486,2114,1246, # 2950 -1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,7324,2127,2354, 347,3736, 221, # 2966 -3503,3110,7325,1955,1153,4065, 83, 296,1199,3070, 192, 624, 93,7326, 822,1897, # 2982 -2810,3111, 795,2064, 991,1554,1542,1592, 27, 43,2853, 859, 139,1456, 860,4300, # 2998 - 437, 712,3871, 164,2392,3112, 695, 211,3017,2096, 195,3872,1608,3504,3505,3618, # 3014 -3873, 234, 811,2971,2097,3874,2229,1441,3506,1615,2375, 668,2076,1638, 305, 228, # 3030 -1664,4301, 467, 415,7327, 262,2098,1593, 239, 108, 300, 200,1033, 512,1247,2077, # 3046 -7328,7329,2173,3176,3619,2673, 593, 845,1062,3244, 88,1723,2037,3875,1950, 212, # 3062 - 266, 152, 149, 468,1898,4066,4302, 77, 187,7330,3018, 37, 5,2972,7331,3876, # 3078 -7332,7333, 39,2517,4303,2894,3177,2078, 55, 148, 74,4304, 545, 483,1474,1029, # 3094 -1665, 217,1869,1531,3113,1104,2645,4067, 24, 172,3507, 900,3877,3508,3509,4305, # 3110 - 32,1408,2811,1312, 329, 487,2355,2247,2708, 784,2674, 4,3019,3314,1427,1788, # 3126 - 188, 109, 499,7334,3620,1717,1789, 888,1217,3020,4306,7335,3510,7336,3315,1520, # 3142 -3621,3878, 196,1034, 775,7337,7338, 929,1815, 249, 439, 38,7339,1063,7340, 794, # 3158 -3879,1435,2296, 46, 178,3245,2065,7341,2376,7342, 214,1709,4307, 804, 35, 707, # 3174 - 324,3622,1601,2546, 140, 459,4068,7343,7344,1365, 839, 272, 978,2257,2572,3409, # 3190 -2128,1363,3623,1423, 697, 100,3071, 48, 70,1231, 495,3114,2193,7345,1294,7346, # 3206 -2079, 462, 586,1042,3246, 853, 256, 988, 185,2377,3410,1698, 434,1084,7347,3411, # 3222 - 314,2615,2775,4308,2330,2331, 569,2280, 637,1816,2518, 757,1162,1878,1616,3412, # 3238 - 287,1577,2115, 768,4309,1671,2854,3511,2519,1321,3737, 909,2413,7348,4069, 933, # 3254 -3738,7349,2052,2356,1222,4310, 765,2414,1322, 786,4311,7350,1919,1462,1677,2895, # 3270 -1699,7351,4312,1424,2437,3115,3624,2590,3316,1774,1940,3413,3880,4070, 309,1369, # 3286 -1130,2812, 364,2230,1653,1299,3881,3512,3882,3883,2646, 525,1085,3021, 902,2000, # 3302 -1475, 964,4313, 421,1844,1415,1057,2281, 940,1364,3116, 376,4314,4315,1381, 7, # 3318 -2520, 983,2378, 336,1710,2675,1845, 321,3414, 559,1131,3022,2742,1808,1132,1313, # 3334 - 265,1481,1857,7352, 352,1203,2813,3247, 167,1089, 420,2814, 776, 792,1724,3513, # 3350 -4071,2438,3248,7353,4072,7354, 446, 229, 333,2743, 901,3739,1200,1557,4316,2647, # 3366 -1920, 395,2744,2676,3740,4073,1835, 125, 916,3178,2616,4317,7355,7356,3741,7357, # 3382 -7358,7359,4318,3117,3625,1133,2547,1757,3415,1510,2313,1409,3514,7360,2145, 438, # 3398 -2591,2896,2379,3317,1068, 958,3023, 461, 311,2855,2677,4074,1915,3179,4075,1978, # 3414 - 383, 750,2745,2617,4076, 274, 539, 385,1278,1442,7361,1154,1964, 384, 561, 210, # 3430 - 98,1295,2548,3515,7362,1711,2415,1482,3416,3884,2897,1257, 129,7363,3742, 642, # 3446 - 523,2776,2777,2648,7364, 141,2231,1333, 68, 176, 441, 876, 907,4077, 603,2592, # 3462 - 710, 171,3417, 404, 549, 18,3118,2393,1410,3626,1666,7365,3516,4319,2898,4320, # 3478 -7366,2973, 368,7367, 146, 366, 99, 871,3627,1543, 748, 807,1586,1185, 22,2258, # 3494 - 379,3743,3180,7368,3181, 505,1941,2618,1991,1382,2314,7369, 380,2357, 218, 702, # 3510 -1817,1248,3418,3024,3517,3318,3249,7370,2974,3628, 930,3250,3744,7371, 59,7372, # 3526 - 585, 601,4078, 497,3419,1112,1314,4321,1801,7373,1223,1472,2174,7374, 749,1836, # 3542 - 690,1899,3745,1772,3885,1476, 429,1043,1790,2232,2116, 917,4079, 447,1086,1629, # 3558 -7375, 556,7376,7377,2020,1654, 844,1090, 105, 550, 966,1758,2815,1008,1782, 686, # 3574 -1095,7378,2282, 793,1602,7379,3518,2593,4322,4080,2933,2297,4323,3746, 980,2496, # 3590 - 544, 353, 527,4324, 908,2678,2899,7380, 381,2619,1942,1348,7381,1341,1252, 560, # 3606 -3072,7382,3420,2856,7383,2053, 973, 886,2080, 143,4325,7384,7385, 157,3886, 496, # 3622 -4081, 57, 840, 540,2038,4326,4327,3421,2117,1445, 970,2259,1748,1965,2081,4082, # 3638 -3119,1234,1775,3251,2816,3629, 773,1206,2129,1066,2039,1326,3887,1738,1725,4083, # 3654 - 279,3120, 51,1544,2594, 423,1578,2130,2066, 173,4328,1879,7386,7387,1583, 264, # 3670 - 610,3630,4329,2439, 280, 154,7388,7389,7390,1739, 338,1282,3073, 693,2857,1411, # 3686 -1074,3747,2440,7391,4330,7392,7393,1240, 952,2394,7394,2900,1538,2679, 685,1483, # 3702 -4084,2468,1436, 953,4085,2054,4331, 671,2395, 79,4086,2441,3252, 608, 567,2680, # 3718 -3422,4087,4088,1691, 393,1261,1791,2396,7395,4332,7396,7397,7398,7399,1383,1672, # 3734 -3748,3182,1464, 522,1119, 661,1150, 216, 675,4333,3888,1432,3519, 609,4334,2681, # 3750 -2397,7400,7401,7402,4089,3025, 0,7403,2469, 315, 231,2442, 301,3319,4335,2380, # 3766 -7404, 233,4090,3631,1818,4336,4337,7405, 96,1776,1315,2082,7406, 257,7407,1809, # 3782 -3632,2709,1139,1819,4091,2021,1124,2163,2778,1777,2649,7408,3074, 363,1655,3183, # 3798 -7409,2975,7410,7411,7412,3889,1567,3890, 718, 103,3184, 849,1443, 341,3320,2934, # 3814 -1484,7413,1712, 127, 67, 339,4092,2398, 679,1412, 821,7414,7415, 834, 738, 351, # 3830 -2976,2146, 846, 235,1497,1880, 418,1992,3749,2710, 186,1100,2147,2746,3520,1545, # 3846 -1355,2935,2858,1377, 583,3891,4093,2573,2977,7416,1298,3633,1078,2549,3634,2358, # 3862 - 78,3750,3751, 267,1289,2099,2001,1594,4094, 348, 369,1274,2194,2175,1837,4338, # 3878 -1820,2817,3635,2747,2283,2002,4339,2936,2748, 144,3321, 882,4340,3892,2749,3423, # 3894 -4341,2901,7417,4095,1726, 320,7418,3893,3026, 788,2978,7419,2818,1773,1327,2859, # 3910 -3894,2819,7420,1306,4342,2003,1700,3752,3521,2359,2650, 787,2022, 506, 824,3636, # 3926 - 534, 323,4343,1044,3322,2023,1900, 946,3424,7421,1778,1500,1678,7422,1881,4344, # 3942 - 165, 243,4345,3637,2521, 123, 683,4096, 764,4346, 36,3895,1792, 589,2902, 816, # 3958 - 626,1667,3027,2233,1639,1555,1622,3753,3896,7423,3897,2860,1370,1228,1932, 891, # 3974 -2083,2903, 304,4097,7424, 292,2979,2711,3522, 691,2100,4098,1115,4347, 118, 662, # 3990 -7425, 611,1156, 854,2381,1316,2861, 2, 386, 515,2904,7426,7427,3253, 868,2234, # 4006 -1486, 855,2651, 785,2212,3028,7428,1040,3185,3523,7429,3121, 448,7430,1525,7431, # 4022 -2164,4348,7432,3754,7433,4099,2820,3524,3122, 503, 818,3898,3123,1568, 814, 676, # 4038 -1444, 306,1749,7434,3755,1416,1030, 197,1428, 805,2821,1501,4349,7435,7436,7437, # 4054 -1993,7438,4350,7439,7440,2195, 13,2779,3638,2980,3124,1229,1916,7441,3756,2131, # 4070 -7442,4100,4351,2399,3525,7443,2213,1511,1727,1120,7444,7445, 646,3757,2443, 307, # 4086 -7446,7447,1595,3186,7448,7449,7450,3639,1113,1356,3899,1465,2522,2523,7451, 519, # 4102 -7452, 128,2132, 92,2284,1979,7453,3900,1512, 342,3125,2196,7454,2780,2214,1980, # 4118 -3323,7455, 290,1656,1317, 789, 827,2360,7456,3758,4352, 562, 581,3901,7457, 401, # 4134 -4353,2248, 94,4354,1399,2781,7458,1463,2024,4355,3187,1943,7459, 828,1105,4101, # 4150 -1262,1394,7460,4102, 605,4356,7461,1783,2862,7462,2822, 819,2101, 578,2197,2937, # 4166 -7463,1502, 436,3254,4103,3255,2823,3902,2905,3425,3426,7464,2712,2315,7465,7466, # 4182 -2332,2067, 23,4357, 193, 826,3759,2102, 699,1630,4104,3075, 390,1793,1064,3526, # 4198 -7467,1579,3076,3077,1400,7468,4105,1838,1640,2863,7469,4358,4359, 137,4106, 598, # 4214 -3078,1966, 780, 104, 974,2938,7470, 278, 899, 253, 402, 572, 504, 493,1339,7471, # 4230 -3903,1275,4360,2574,2550,7472,3640,3029,3079,2249, 565,1334,2713, 863, 41,7473, # 4246 -7474,4361,7475,1657,2333, 19, 463,2750,4107, 606,7476,2981,3256,1087,2084,1323, # 4262 -2652,2982,7477,1631,1623,1750,4108,2682,7478,2864, 791,2714,2653,2334, 232,2416, # 4278 -7479,2983,1498,7480,2654,2620, 755,1366,3641,3257,3126,2025,1609, 119,1917,3427, # 4294 - 862,1026,4109,7481,3904,3760,4362,3905,4363,2260,1951,2470,7482,1125, 817,4110, # 4310 -4111,3906,1513,1766,2040,1487,4112,3030,3258,2824,3761,3127,7483,7484,1507,7485, # 4326 -2683, 733, 40,1632,1106,2865, 345,4113, 841,2524, 230,4364,2984,1846,3259,3428, # 4342 -7486,1263, 986,3429,7487, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562,3907, # 4358 -3908,2939, 967,2751,2655,1349, 592,2133,1692,3324,2985,1994,4114,1679,3909,1901, # 4374 -2185,7488, 739,3642,2715,1296,1290,7489,4115,2198,2199,1921,1563,2595,2551,1870, # 4390 -2752,2986,7490, 435,7491, 343,1108, 596, 17,1751,4365,2235,3430,3643,7492,4366, # 4406 - 294,3527,2940,1693, 477, 979, 281,2041,3528, 643,2042,3644,2621,2782,2261,1031, # 4422 -2335,2134,2298,3529,4367, 367,1249,2552,7493,3530,7494,4368,1283,3325,2004, 240, # 4438 -1762,3326,4369,4370, 836,1069,3128, 474,7495,2148,2525, 268,3531,7496,3188,1521, # 4454 -1284,7497,1658,1546,4116,7498,3532,3533,7499,4117,3327,2684,1685,4118, 961,1673, # 4470 -2622, 190,2005,2200,3762,4371,4372,7500, 570,2497,3645,1490,7501,4373,2623,3260, # 4486 -1956,4374, 584,1514, 396,1045,1944,7502,4375,1967,2444,7503,7504,4376,3910, 619, # 4502 -7505,3129,3261, 215,2006,2783,2553,3189,4377,3190,4378, 763,4119,3763,4379,7506, # 4518 -7507,1957,1767,2941,3328,3646,1174, 452,1477,4380,3329,3130,7508,2825,1253,2382, # 4534 -2186,1091,2285,4120, 492,7509, 638,1169,1824,2135,1752,3911, 648, 926,1021,1324, # 4550 -4381, 520,4382, 997, 847,1007, 892,4383,3764,2262,1871,3647,7510,2400,1784,4384, # 4566 -1952,2942,3080,3191,1728,4121,2043,3648,4385,2007,1701,3131,1551, 30,2263,4122, # 4582 -7511,2026,4386,3534,7512, 501,7513,4123, 594,3431,2165,1821,3535,3432,3536,3192, # 4598 - 829,2826,4124,7514,1680,3132,1225,4125,7515,3262,4387,4126,3133,2336,7516,4388, # 4614 -4127,7517,3912,3913,7518,1847,2383,2596,3330,7519,4389, 374,3914, 652,4128,4129, # 4630 - 375,1140, 798,7520,7521,7522,2361,4390,2264, 546,1659, 138,3031,2445,4391,7523, # 4646 -2250, 612,1848, 910, 796,3765,1740,1371, 825,3766,3767,7524,2906,2554,7525, 692, # 4662 - 444,3032,2624, 801,4392,4130,7526,1491, 244,1053,3033,4131,4132, 340,7527,3915, # 4678 -1041,2987, 293,1168, 87,1357,7528,1539, 959,7529,2236, 721, 694,4133,3768, 219, # 4694 -1478, 644,1417,3331,2656,1413,1401,1335,1389,3916,7530,7531,2988,2362,3134,1825, # 4710 - 730,1515, 184,2827, 66,4393,7532,1660,2943, 246,3332, 378,1457, 226,3433, 975, # 4726 -3917,2944,1264,3537, 674, 696,7533, 163,7534,1141,2417,2166, 713,3538,3333,4394, # 4742 -3918,7535,7536,1186, 15,7537,1079,1070,7538,1522,3193,3539, 276,1050,2716, 758, # 4758 -1126, 653,2945,3263,7539,2337, 889,3540,3919,3081,2989, 903,1250,4395,3920,3434, # 4774 -3541,1342,1681,1718, 766,3264, 286, 89,2946,3649,7540,1713,7541,2597,3334,2990, # 4790 -7542,2947,2215,3194,2866,7543,4396,2498,2526, 181, 387,1075,3921, 731,2187,3335, # 4806 -7544,3265, 310, 313,3435,2299, 770,4134, 54,3034, 189,4397,3082,3769,3922,7545, # 4822 -1230,1617,1849, 355,3542,4135,4398,3336, 111,4136,3650,1350,3135,3436,3035,4137, # 4838 -2149,3266,3543,7546,2784,3923,3924,2991, 722,2008,7547,1071, 247,1207,2338,2471, # 4854 -1378,4399,2009, 864,1437,1214,4400, 373,3770,1142,2216, 667,4401, 442,2753,2555, # 4870 -3771,3925,1968,4138,3267,1839, 837, 170,1107, 934,1336,1882,7548,7549,2118,4139, # 4886 -2828, 743,1569,7550,4402,4140, 582,2384,1418,3437,7551,1802,7552, 357,1395,1729, # 4902 -3651,3268,2418,1564,2237,7553,3083,3772,1633,4403,1114,2085,4141,1532,7554, 482, # 4918 -2446,4404,7555,7556,1492, 833,1466,7557,2717,3544,1641,2829,7558,1526,1272,3652, # 4934 -4142,1686,1794, 416,2556,1902,1953,1803,7559,3773,2785,3774,1159,2316,7560,2867, # 4950 -4405,1610,1584,3036,2419,2754, 443,3269,1163,3136,7561,7562,3926,7563,4143,2499, # 4966 -3037,4406,3927,3137,2103,1647,3545,2010,1872,4144,7564,4145, 431,3438,7565, 250, # 4982 - 97, 81,4146,7566,1648,1850,1558, 160, 848,7567, 866, 740,1694,7568,2201,2830, # 4998 -3195,4147,4407,3653,1687, 950,2472, 426, 469,3196,3654,3655,3928,7569,7570,1188, # 5014 - 424,1995, 861,3546,4148,3775,2202,2685, 168,1235,3547,4149,7571,2086,1674,4408, # 5030 -3337,3270, 220,2557,1009,7572,3776, 670,2992, 332,1208, 717,7573,7574,3548,2447, # 5046 -3929,3338,7575, 513,7576,1209,2868,3339,3138,4409,1080,7577,7578,7579,7580,2527, # 5062 -3656,3549, 815,1587,3930,3931,7581,3550,3439,3777,1254,4410,1328,3038,1390,3932, # 5078 -1741,3933,3778,3934,7582, 236,3779,2448,3271,7583,7584,3657,3780,1273,3781,4411, # 5094 -7585, 308,7586,4412, 245,4413,1851,2473,1307,2575, 430, 715,2136,2449,7587, 270, # 5110 - 199,2869,3935,7588,3551,2718,1753, 761,1754, 725,1661,1840,4414,3440,3658,7589, # 5126 -7590, 587, 14,3272, 227,2598, 326, 480,2265, 943,2755,3552, 291, 650,1883,7591, # 5142 -1702,1226, 102,1547, 62,3441, 904,4415,3442,1164,4150,7592,7593,1224,1548,2756, # 5158 - 391, 498,1493,7594,1386,1419,7595,2055,1177,4416, 813, 880,1081,2363, 566,1145, # 5174 -4417,2286,1001,1035,2558,2599,2238, 394,1286,7596,7597,2068,7598, 86,1494,1730, # 5190 -3936, 491,1588, 745, 897,2948, 843,3340,3937,2757,2870,3273,1768, 998,2217,2069, # 5206 - 397,1826,1195,1969,3659,2993,3341, 284,7599,3782,2500,2137,2119,1903,7600,3938, # 5222 -2150,3939,4151,1036,3443,1904, 114,2559,4152, 209,1527,7601,7602,2949,2831,2625, # 5238 -2385,2719,3139, 812,2560,7603,3274,7604,1559, 737,1884,3660,1210, 885, 28,2686, # 5254 -3553,3783,7605,4153,1004,1779,4418,7606, 346,1981,2218,2687,4419,3784,1742, 797, # 5270 -1642,3940,1933,1072,1384,2151, 896,3941,3275,3661,3197,2871,3554,7607,2561,1958, # 5286 -4420,2450,1785,7608,7609,7610,3942,4154,1005,1308,3662,4155,2720,4421,4422,1528, # 5302 -2600, 161,1178,4156,1982, 987,4423,1101,4157, 631,3943,1157,3198,2420,1343,1241, # 5318 -1016,2239,2562, 372, 877,2339,2501,1160, 555,1934, 911,3944,7611, 466,1170, 169, # 5334 -1051,2907,2688,3663,2474,2994,1182,2011,2563,1251,2626,7612, 992,2340,3444,1540, # 5350 -2721,1201,2070,2401,1996,2475,7613,4424, 528,1922,2188,1503,1873,1570,2364,3342, # 5366 -3276,7614, 557,1073,7615,1827,3445,2087,2266,3140,3039,3084, 767,3085,2786,4425, # 5382 -1006,4158,4426,2341,1267,2176,3664,3199, 778,3945,3200,2722,1597,2657,7616,4427, # 5398 -7617,3446,7618,7619,7620,3277,2689,1433,3278, 131, 95,1504,3946, 723,4159,3141, # 5414 -1841,3555,2758,2189,3947,2027,2104,3665,7621,2995,3948,1218,7622,3343,3201,3949, # 5430 -4160,2576, 248,1634,3785, 912,7623,2832,3666,3040,3786, 654, 53,7624,2996,7625, # 5446 -1688,4428, 777,3447,1032,3950,1425,7626, 191, 820,2120,2833, 971,4429, 931,3202, # 5462 - 135, 664, 783,3787,1997, 772,2908,1935,3951,3788,4430,2909,3203, 282,2723, 640, # 5478 -1372,3448,1127, 922, 325,3344,7627,7628, 711,2044,7629,7630,3952,2219,2787,1936, # 5494 -3953,3345,2220,2251,3789,2300,7631,4431,3790,1258,3279,3954,3204,2138,2950,3955, # 5510 -3956,7632,2221, 258,3205,4432, 101,1227,7633,3280,1755,7634,1391,3281,7635,2910, # 5526 -2056, 893,7636,7637,7638,1402,4161,2342,7639,7640,3206,3556,7641,7642, 878,1325, # 5542 -1780,2788,4433, 259,1385,2577, 744,1183,2267,4434,7643,3957,2502,7644, 684,1024, # 5558 -4162,7645, 472,3557,3449,1165,3282,3958,3959, 322,2152, 881, 455,1695,1152,1340, # 5574 - 660, 554,2153,4435,1058,4436,4163, 830,1065,3346,3960,4437,1923,7646,1703,1918, # 5590 -7647, 932,2268, 122,7648,4438, 947, 677,7649,3791,2627, 297,1905,1924,2269,4439, # 5606 -2317,3283,7650,7651,4164,7652,4165, 84,4166, 112, 989,7653, 547,1059,3961, 701, # 5622 -3558,1019,7654,4167,7655,3450, 942, 639, 457,2301,2451, 993,2951, 407, 851, 494, # 5638 -4440,3347, 927,7656,1237,7657,2421,3348, 573,4168, 680, 921,2911,1279,1874, 285, # 5654 - 790,1448,1983, 719,2167,7658,7659,4441,3962,3963,1649,7660,1541, 563,7661,1077, # 5670 -7662,3349,3041,3451, 511,2997,3964,3965,3667,3966,1268,2564,3350,3207,4442,4443, # 5686 -7663, 535,1048,1276,1189,2912,2028,3142,1438,1373,2834,2952,1134,2012,7664,4169, # 5702 -1238,2578,3086,1259,7665, 700,7666,2953,3143,3668,4170,7667,4171,1146,1875,1906, # 5718 -4444,2601,3967, 781,2422, 132,1589, 203, 147, 273,2789,2402, 898,1786,2154,3968, # 5734 -3969,7668,3792,2790,7669,7670,4445,4446,7671,3208,7672,1635,3793, 965,7673,1804, # 5750 -2690,1516,3559,1121,1082,1329,3284,3970,1449,3794, 65,1128,2835,2913,2759,1590, # 5766 -3795,7674,7675, 12,2658, 45, 976,2579,3144,4447, 517,2528,1013,1037,3209,7676, # 5782 -3796,2836,7677,3797,7678,3452,7679,2602, 614,1998,2318,3798,3087,2724,2628,7680, # 5798 -2580,4172, 599,1269,7681,1810,3669,7682,2691,3088, 759,1060, 489,1805,3351,3285, # 5814 -1358,7683,7684,2386,1387,1215,2629,2252, 490,7685,7686,4173,1759,2387,2343,7687, # 5830 -4448,3799,1907,3971,2630,1806,3210,4449,3453,3286,2760,2344, 874,7688,7689,3454, # 5846 -3670,1858, 91,2914,3671,3042,3800,4450,7690,3145,3972,2659,7691,3455,1202,1403, # 5862 -3801,2954,2529,1517,2503,4451,3456,2504,7692,4452,7693,2692,1885,1495,1731,3973, # 5878 -2365,4453,7694,2029,7695,7696,3974,2693,1216, 237,2581,4174,2319,3975,3802,4454, # 5894 -4455,2694,3560,3457, 445,4456,7697,7698,7699,7700,2761, 61,3976,3672,1822,3977, # 5910 -7701, 687,2045, 935, 925, 405,2660, 703,1096,1859,2725,4457,3978,1876,1367,2695, # 5926 -3352, 918,2105,1781,2476, 334,3287,1611,1093,4458, 564,3146,3458,3673,3353, 945, # 5942 -2631,2057,4459,7702,1925, 872,4175,7703,3459,2696,3089, 349,4176,3674,3979,4460, # 5958 -3803,4177,3675,2155,3980,4461,4462,4178,4463,2403,2046, 782,3981, 400, 251,4179, # 5974 -1624,7704,7705, 277,3676, 299,1265, 476,1191,3804,2121,4180,4181,1109, 205,7706, # 5990 -2582,1000,2156,3561,1860,7707,7708,7709,4464,7710,4465,2565, 107,2477,2157,3982, # 6006 -3460,3147,7711,1533, 541,1301, 158, 753,4182,2872,3562,7712,1696, 370,1088,4183, # 6022 -4466,3563, 579, 327, 440, 162,2240, 269,1937,1374,3461, 968,3043, 56,1396,3090, # 6038 -2106,3288,3354,7713,1926,2158,4467,2998,7714,3564,7715,7716,3677,4468,2478,7717, # 6054 -2791,7718,1650,4469,7719,2603,7720,7721,3983,2661,3355,1149,3356,3984,3805,3985, # 6070 -7722,1076, 49,7723, 951,3211,3289,3290, 450,2837, 920,7724,1811,2792,2366,4184, # 6086 -1908,1138,2367,3806,3462,7725,3212,4470,1909,1147,1518,2423,4471,3807,7726,4472, # 6102 -2388,2604, 260,1795,3213,7727,7728,3808,3291, 708,7729,3565,1704,7730,3566,1351, # 6118 -1618,3357,2999,1886, 944,4185,3358,4186,3044,3359,4187,7731,3678, 422, 413,1714, # 6134 -3292, 500,2058,2345,4188,2479,7732,1344,1910, 954,7733,1668,7734,7735,3986,2404, # 6150 -4189,3567,3809,4190,7736,2302,1318,2505,3091, 133,3092,2873,4473, 629, 31,2838, # 6166 -2697,3810,4474, 850, 949,4475,3987,2955,1732,2088,4191,1496,1852,7737,3988, 620, # 6182 -3214, 981,1242,3679,3360,1619,3680,1643,3293,2139,2452,1970,1719,3463,2168,7738, # 6198 -3215,7739,7740,3361,1828,7741,1277,4476,1565,2047,7742,1636,3568,3093,7743, 869, # 6214 -2839, 655,3811,3812,3094,3989,3000,3813,1310,3569,4477,7744,7745,7746,1733, 558, # 6230 -4478,3681, 335,1549,3045,1756,4192,3682,1945,3464,1829,1291,1192, 470,2726,2107, # 6246 -2793, 913,1054,3990,7747,1027,7748,3046,3991,4479, 982,2662,3362,3148,3465,3216, # 6262 -3217,1946,2794,7749, 571,4480,7750,1830,7751,3570,2583,1523,2424,7752,2089, 984, # 6278 -4481,3683,1959,7753,3684, 852, 923,2795,3466,3685, 969,1519, 999,2048,2320,1705, # 6294 -7754,3095, 615,1662, 151, 597,3992,2405,2321,1049, 275,4482,3686,4193, 568,3687, # 6310 -3571,2480,4194,3688,7755,2425,2270, 409,3218,7756,1566,2874,3467,1002, 769,2840, # 6326 - 194,2090,3149,3689,2222,3294,4195, 628,1505,7757,7758,1763,2177,3001,3993, 521, # 6342 -1161,2584,1787,2203,2406,4483,3994,1625,4196,4197, 412, 42,3096, 464,7759,2632, # 6358 -4484,3363,1760,1571,2875,3468,2530,1219,2204,3814,2633,2140,2368,4485,4486,3295, # 6374 -1651,3364,3572,7760,7761,3573,2481,3469,7762,3690,7763,7764,2271,2091, 460,7765, # 6390 -4487,7766,3002, 962, 588,3574, 289,3219,2634,1116, 52,7767,3047,1796,7768,7769, # 6406 -7770,1467,7771,1598,1143,3691,4198,1984,1734,1067,4488,1280,3365, 465,4489,1572, # 6422 - 510,7772,1927,2241,1812,1644,3575,7773,4490,3692,7774,7775,2663,1573,1534,7776, # 6438 -7777,4199, 536,1807,1761,3470,3815,3150,2635,7778,7779,7780,4491,3471,2915,1911, # 6454 -2796,7781,3296,1122, 377,3220,7782, 360,7783,7784,4200,1529, 551,7785,2059,3693, # 6470 -1769,2426,7786,2916,4201,3297,3097,2322,2108,2030,4492,1404, 136,1468,1479, 672, # 6486 -1171,3221,2303, 271,3151,7787,2762,7788,2049, 678,2727, 865,1947,4493,7789,2013, # 6502 -3995,2956,7790,2728,2223,1397,3048,3694,4494,4495,1735,2917,3366,3576,7791,3816, # 6518 - 509,2841,2453,2876,3817,7792,7793,3152,3153,4496,4202,2531,4497,2304,1166,1010, # 6534 - 552, 681,1887,7794,7795,2957,2958,3996,1287,1596,1861,3154, 358, 453, 736, 175, # 6550 - 478,1117, 905,1167,1097,7796,1853,1530,7797,1706,7798,2178,3472,2287,3695,3473, # 6566 -3577,4203,2092,4204,7799,3367,1193,2482,4205,1458,2190,2205,1862,1888,1421,3298, # 6582 -2918,3049,2179,3474, 595,2122,7800,3997,7801,7802,4206,1707,2636, 223,3696,1359, # 6598 - 751,3098, 183,3475,7803,2797,3003, 419,2369, 633, 704,3818,2389, 241,7804,7805, # 6614 -7806, 838,3004,3697,2272,2763,2454,3819,1938,2050,3998,1309,3099,2242,1181,7807, # 6630 -1136,2206,3820,2370,1446,4207,2305,4498,7808,7809,4208,1055,2605, 484,3698,7810, # 6646 -3999, 625,4209,2273,3368,1499,4210,4000,7811,4001,4211,3222,2274,2275,3476,7812, # 6662 -7813,2764, 808,2606,3699,3369,4002,4212,3100,2532, 526,3370,3821,4213, 955,7814, # 6678 -1620,4214,2637,2427,7815,1429,3700,1669,1831, 994, 928,7816,3578,1260,7817,7818, # 6694 -7819,1948,2288, 741,2919,1626,4215,2729,2455, 867,1184, 362,3371,1392,7820,7821, # 6710 -4003,4216,1770,1736,3223,2920,4499,4500,1928,2698,1459,1158,7822,3050,3372,2877, # 6726 -1292,1929,2506,2842,3701,1985,1187,2071,2014,2607,4217,7823,2566,2507,2169,3702, # 6742 -2483,3299,7824,3703,4501,7825,7826, 666,1003,3005,1022,3579,4218,7827,4502,1813, # 6758 -2253, 574,3822,1603, 295,1535, 705,3823,4219, 283, 858, 417,7828,7829,3224,4503, # 6774 -4504,3051,1220,1889,1046,2276,2456,4004,1393,1599, 689,2567, 388,4220,7830,2484, # 6790 - 802,7831,2798,3824,2060,1405,2254,7832,4505,3825,2109,1052,1345,3225,1585,7833, # 6806 - 809,7834,7835,7836, 575,2730,3477, 956,1552,1469,1144,2323,7837,2324,1560,2457, # 6822 -3580,3226,4005, 616,2207,3155,2180,2289,7838,1832,7839,3478,4506,7840,1319,3704, # 6838 -3705,1211,3581,1023,3227,1293,2799,7841,7842,7843,3826, 607,2306,3827, 762,2878, # 6854 -1439,4221,1360,7844,1485,3052,7845,4507,1038,4222,1450,2061,2638,4223,1379,4508, # 6870 -2585,7846,7847,4224,1352,1414,2325,2921,1172,7848,7849,3828,3829,7850,1797,1451, # 6886 -7851,7852,7853,7854,2922,4006,4007,2485,2346, 411,4008,4009,3582,3300,3101,4509, # 6902 -1561,2664,1452,4010,1375,7855,7856, 47,2959, 316,7857,1406,1591,2923,3156,7858, # 6918 -1025,2141,3102,3157, 354,2731, 884,2224,4225,2407, 508,3706, 726,3583, 996,2428, # 6934 -3584, 729,7859, 392,2191,1453,4011,4510,3707,7860,7861,2458,3585,2608,1675,2800, # 6950 - 919,2347,2960,2348,1270,4511,4012, 73,7862,7863, 647,7864,3228,2843,2255,1550, # 6966 -1346,3006,7865,1332, 883,3479,7866,7867,7868,7869,3301,2765,7870,1212, 831,1347, # 6982 -4226,4512,2326,3830,1863,3053, 720,3831,4513,4514,3832,7871,4227,7872,7873,4515, # 6998 -7874,7875,1798,4516,3708,2609,4517,3586,1645,2371,7876,7877,2924, 669,2208,2665, # 7014 -2429,7878,2879,7879,7880,1028,3229,7881,4228,2408,7882,2256,1353,7883,7884,4518, # 7030 -3158, 518,7885,4013,7886,4229,1960,7887,2142,4230,7888,7889,3007,2349,2350,3833, # 7046 - 516,1833,1454,4014,2699,4231,4519,2225,2610,1971,1129,3587,7890,2766,7891,2961, # 7062 -1422, 577,1470,3008,1524,3373,7892,7893, 432,4232,3054,3480,7894,2586,1455,2508, # 7078 -2226,1972,1175,7895,1020,2732,4015,3481,4520,7896,2733,7897,1743,1361,3055,3482, # 7094 -2639,4016,4233,4521,2290, 895, 924,4234,2170, 331,2243,3056, 166,1627,3057,1098, # 7110 -7898,1232,2880,2227,3374,4522, 657, 403,1196,2372, 542,3709,3375,1600,4235,3483, # 7126 -7899,4523,2767,3230, 576, 530,1362,7900,4524,2533,2666,3710,4017,7901, 842,3834, # 7142 -7902,2801,2031,1014,4018, 213,2700,3376, 665, 621,4236,7903,3711,2925,2430,7904, # 7158 -2431,3302,3588,3377,7905,4237,2534,4238,4525,3589,1682,4239,3484,1380,7906, 724, # 7174 -2277, 600,1670,7907,1337,1233,4526,3103,2244,7908,1621,4527,7909, 651,4240,7910, # 7190 -1612,4241,2611,7911,2844,7912,2734,2307,3058,7913, 716,2459,3059, 174,1255,2701, # 7206 -4019,3590, 548,1320,1398, 728,4020,1574,7914,1890,1197,3060,4021,7915,3061,3062, # 7222 -3712,3591,3713, 747,7916, 635,4242,4528,7917,7918,7919,4243,7920,7921,4529,7922, # 7238 -3378,4530,2432, 451,7923,3714,2535,2072,4244,2735,4245,4022,7924,1764,4531,7925, # 7254 -4246, 350,7926,2278,2390,2486,7927,4247,4023,2245,1434,4024, 488,4532, 458,4248, # 7270 -4025,3715, 771,1330,2391,3835,2568,3159,2159,2409,1553,2667,3160,4249,7928,2487, # 7286 -2881,2612,1720,2702,4250,3379,4533,7929,2536,4251,7930,3231,4252,2768,7931,2015, # 7302 -2736,7932,1155,1017,3716,3836,7933,3303,2308, 201,1864,4253,1430,7934,4026,7935, # 7318 -7936,7937,7938,7939,4254,1604,7940, 414,1865, 371,2587,4534,4535,3485,2016,3104, # 7334 -4536,1708, 960,4255, 887, 389,2171,1536,1663,1721,7941,2228,4027,2351,2926,1580, # 7350 -7942,7943,7944,1744,7945,2537,4537,4538,7946,4539,7947,2073,7948,7949,3592,3380, # 7366 -2882,4256,7950,4257,2640,3381,2802, 673,2703,2460, 709,3486,4028,3593,4258,7951, # 7382 -1148, 502, 634,7952,7953,1204,4540,3594,1575,4541,2613,3717,7954,3718,3105, 948, # 7398 -3232, 121,1745,3837,1110,7955,4259,3063,2509,3009,4029,3719,1151,1771,3838,1488, # 7414 -4030,1986,7956,2433,3487,7957,7958,2093,7959,4260,3839,1213,1407,2803, 531,2737, # 7430 -2538,3233,1011,1537,7960,2769,4261,3106,1061,7961,3720,3721,1866,2883,7962,2017, # 7446 - 120,4262,4263,2062,3595,3234,2309,3840,2668,3382,1954,4542,7963,7964,3488,1047, # 7462 -2704,1266,7965,1368,4543,2845, 649,3383,3841,2539,2738,1102,2846,2669,7966,7967, # 7478 -1999,7968,1111,3596,2962,7969,2488,3842,3597,2804,1854,3384,3722,7970,7971,3385, # 7494 -2410,2884,3304,3235,3598,7972,2569,7973,3599,2805,4031,1460, 856,7974,3600,7975, # 7510 -2885,2963,7976,2886,3843,7977,4264, 632,2510, 875,3844,1697,3845,2291,7978,7979, # 7526 -4544,3010,1239, 580,4545,4265,7980, 914, 936,2074,1190,4032,1039,2123,7981,7982, # 7542 -7983,3386,1473,7984,1354,4266,3846,7985,2172,3064,4033, 915,3305,4267,4268,3306, # 7558 -1605,1834,7986,2739, 398,3601,4269,3847,4034, 328,1912,2847,4035,3848,1331,4270, # 7574 -3011, 937,4271,7987,3602,4036,4037,3387,2160,4546,3388, 524, 742, 538,3065,1012, # 7590 -7988,7989,3849,2461,7990, 658,1103, 225,3850,7991,7992,4547,7993,4548,7994,3236, # 7606 -1243,7995,4038, 963,2246,4549,7996,2705,3603,3161,7997,7998,2588,2327,7999,4550, # 7622 -8000,8001,8002,3489,3307, 957,3389,2540,2032,1930,2927,2462, 870,2018,3604,1746, # 7638 -2770,2771,2434,2463,8003,3851,8004,3723,3107,3724,3490,3390,3725,8005,1179,3066, # 7654 -8006,3162,2373,4272,3726,2541,3163,3108,2740,4039,8007,3391,1556,2542,2292, 977, # 7670 -2887,2033,4040,1205,3392,8008,1765,3393,3164,2124,1271,1689, 714,4551,3491,8009, # 7686 -2328,3852, 533,4273,3605,2181, 617,8010,2464,3308,3492,2310,8011,8012,3165,8013, # 7702 -8014,3853,1987, 618, 427,2641,3493,3394,8015,8016,1244,1690,8017,2806,4274,4552, # 7718 -8018,3494,8019,8020,2279,1576, 473,3606,4275,3395, 972,8021,3607,8022,3067,8023, # 7734 -8024,4553,4554,8025,3727,4041,4042,8026, 153,4555, 356,8027,1891,2888,4276,2143, # 7750 - 408, 803,2352,8028,3854,8029,4277,1646,2570,2511,4556,4557,3855,8030,3856,4278, # 7766 -8031,2411,3396, 752,8032,8033,1961,2964,8034, 746,3012,2465,8035,4279,3728, 698, # 7782 -4558,1892,4280,3608,2543,4559,3609,3857,8036,3166,3397,8037,1823,1302,4043,2706, # 7798 -3858,1973,4281,8038,4282,3167, 823,1303,1288,1236,2848,3495,4044,3398, 774,3859, # 7814 -8039,1581,4560,1304,2849,3860,4561,8040,2435,2161,1083,3237,4283,4045,4284, 344, # 7830 -1173, 288,2311, 454,1683,8041,8042,1461,4562,4046,2589,8043,8044,4563, 985, 894, # 7846 -8045,3399,3168,8046,1913,2928,3729,1988,8047,2110,1974,8048,4047,8049,2571,1194, # 7862 - 425,8050,4564,3169,1245,3730,4285,8051,8052,2850,8053, 636,4565,1855,3861, 760, # 7878 -1799,8054,4286,2209,1508,4566,4048,1893,1684,2293,8055,8056,8057,4287,4288,2210, # 7894 - 479,8058,8059, 832,8060,4049,2489,8061,2965,2490,3731, 990,3109, 627,1814,2642, # 7910 -4289,1582,4290,2125,2111,3496,4567,8062, 799,4291,3170,8063,4568,2112,1737,3013, # 7926 -1018, 543, 754,4292,3309,1676,4569,4570,4050,8064,1489,8065,3497,8066,2614,2889, # 7942 -4051,8067,8068,2966,8069,8070,8071,8072,3171,4571,4572,2182,1722,8073,3238,3239, # 7958 -1842,3610,1715, 481, 365,1975,1856,8074,8075,1962,2491,4573,8076,2126,3611,3240, # 7974 - 433,1894,2063,2075,8077, 602,2741,8078,8079,8080,8081,8082,3014,1628,3400,8083, # 7990 -3172,4574,4052,2890,4575,2512,8084,2544,2772,8085,8086,8087,3310,4576,2891,8088, # 8006 -4577,8089,2851,4578,4579,1221,2967,4053,2513,8090,8091,8092,1867,1989,8093,8094, # 8022 -8095,1895,8096,8097,4580,1896,4054, 318,8098,2094,4055,4293,8099,8100, 485,8101, # 8038 - 938,3862, 553,2670, 116,8102,3863,3612,8103,3498,2671,2773,3401,3311,2807,8104, # 8054 -3613,2929,4056,1747,2930,2968,8105,8106, 207,8107,8108,2672,4581,2514,8109,3015, # 8070 - 890,3614,3864,8110,1877,3732,3402,8111,2183,2353,3403,1652,8112,8113,8114, 941, # 8086 -2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, # 8102 + 1, 1800, 1506, 255, 1431, 198, 9, 82, 6, 7310, 177, 202, 3615, 1256, 2808, 110, # 2742 + 3735, 33, 3241, 261, 76, 44, 2113, 16, 2931, 2184, 1176, 659, 3868, 26, 3404, 2643, # 2758 + 1198, 3869, 3313, 4060, 410, 2211, 302, 590, 361, 1963, 8, 204, 58, 4296, 7311, 1931, # 2774 + 63, 7312, 7313, 317, 1614, 75, 222, 159, 4061, 2412, 1480, 7314, 3500, 3068, 224, 2809, # 2790 + 3616, 3, 10, 3870, 1471, 29, 2774, 1135, 2852, 1939, 873, 130, 3242, 1123, 312, 7315, # 2806 + 4297, 2051, 507, 252, 682, 7316, 142, 1914, 124, 206, 2932, 34, 3501, 3173, 64, 604, # 2822 + 7317, 2494, 1976, 1977, 155, 1990, 645, 641, 1606, 7318, 3405, 337, 72, 406, 7319, 80, # 2838 + 630, 238, 3174, 1509, 263, 939, 1092, 2644, 756, 1440, 1094, 3406, 449, 69, 2969, 591, # 2854 + 179, 2095, 471, 115, 2034, 1843, 60, 50, 2970, 134, 806, 1868, 734, 2035, 3407, 180, # 2870 + 995, 1607, 156, 537, 2893, 688, 7320, 319, 1305, 779, 2144, 514, 2374, 298, 4298, 359, # 2886 + 2495, 90, 2707, 1338, 663, 11, 906, 1099, 2545, 20, 2436, 182, 532, 1716, 7321, 732, # 2902 + 1376, 4062, 1311, 1420, 3175, 25, 2312, 1056, 113, 399, 382, 1949, 242, 3408, 2467, 529, # 2918 + 3243, 475, 1447, 3617, 7322, 117, 21, 656, 810, 1297, 2295, 2329, 3502, 7323, 126, 4063, # 2934 + 706, 456, 150, 613, 4299, 71, 1118, 2036, 4064, 145, 3069, 85, 835, 486, 2114, 1246, # 2950 + 1426, 428, 727, 1285, 1015, 800, 106, 623, 303, 1281, 7324, 2127, 2354, 347, 3736, 221, # 2966 + 3503, 3110, 7325, 1955, 1153, 4065, 83, 296, 1199, 3070, 192, 624, 93, 7326, 822, 1897, # 2982 + 2810, 3111, 795, 2064, 991, 1554, 1542, 1592, 27, 43, 2853, 859, 139, 1456, 860, 4300, # 2998 + 437, 712, 3871, 164, 2392, 3112, 695, 211, 3017, 2096, 195, 3872, 1608, 3504, 3505, 3618, # 3014 + 3873, 234, 811, 2971, 2097, 3874, 2229, 1441, 3506, 1615, 2375, 668, 2076, 1638, 305, 228, # 3030 + 1664, 4301, 467, 415, 7327, 262, 2098, 1593, 239, 108, 300, 200, 1033, 512, 1247, 2077, # 3046 + 7328, 7329, 2173, 3176, 3619, 2673, 593, 845, 1062, 3244, 88, 1723, 2037, 3875, 1950, 212, # 3062 + 266, 152, 149, 468, 1898, 4066, 4302, 77, 187, 7330, 3018, 37, 5, 2972, 7331, 3876, # 3078 + 7332, 7333, 39, 2517, 4303, 2894, 3177, 2078, 55, 148, 74, 4304, 545, 483, 1474, 1029, # 3094 + 1665, 217, 1869, 1531, 3113, 1104, 2645, 4067, 24, 172, 3507, 900, 3877, 3508, 3509, 4305, # 3110 + 32, 1408, 2811, 1312, 329, 487, 2355, 2247, 2708, 784, 2674, 4, 3019, 3314, 1427, 1788, # 3126 + 188, 109, 499, 7334, 3620, 1717, 1789, 888, 1217, 3020, 4306, 7335, 3510, 7336, 3315, 1520, # 3142 + 3621, 3878, 196, 1034, 775, 7337, 7338, 929, 1815, 249, 439, 38, 7339, 1063, 7340, 794, # 3158 + 3879, 1435, 2296, 46, 178, 3245, 2065, 7341, 2376, 7342, 214, 1709, 4307, 804, 35, 707, # 3174 + 324, 3622, 1601, 2546, 140, 459, 4068, 7343, 7344, 1365, 839, 272, 978, 2257, 2572, 3409, # 3190 + 2128, 1363, 3623, 1423, 697, 100, 3071, 48, 70, 1231, 495, 3114, 2193, 7345, 1294, 7346, # 3206 + 2079, 462, 586, 1042, 3246, 853, 256, 988, 185, 2377, 3410, 1698, 434, 1084, 7347, 3411, # 3222 + 314, 2615, 2775, 4308, 2330, 2331, 569, 2280, 637, 1816, 2518, 757, 1162, 1878, 1616, 3412, # 3238 + 287, 1577, 2115, 768, 4309, 1671, 2854, 3511, 2519, 1321, 3737, 909, 2413, 7348, 4069, 933, # 3254 + 3738, 7349, 2052, 2356, 1222, 4310, 765, 2414, 1322, 786, 4311, 7350, 1919, 1462, 1677, 2895, # 3270 + 1699, 7351, 4312, 1424, 2437, 3115, 3624, 2590, 3316, 1774, 1940, 3413, 3880, 4070, 309, 1369, # 3286 + 1130, 2812, 364, 2230, 1653, 1299, 3881, 3512, 3882, 3883, 2646, 525, 1085, 3021, 902, 2000, # 3302 + 1475, 964, 4313, 421, 1844, 1415, 1057, 2281, 940, 1364, 3116, 376, 4314, 4315, 1381, 7, # 3318 + 2520, 983, 2378, 336, 1710, 2675, 1845, 321, 3414, 559, 1131, 3022, 2742, 1808, 1132, 1313, # 3334 + 265, 1481, 1857, 7352, 352, 1203, 2813, 3247, 167, 1089, 420, 2814, 776, 792, 1724, 3513, # 3350 + 4071, 2438, 3248, 7353, 4072, 7354, 446, 229, 333, 2743, 901, 3739, 1200, 1557, 4316, 2647, # 3366 + 1920, 395, 2744, 2676, 3740, 4073, 1835, 125, 916, 3178, 2616, 4317, 7355, 7356, 3741, 7357, # 3382 + 7358, 7359, 4318, 3117, 3625, 1133, 2547, 1757, 3415, 1510, 2313, 1409, 3514, 7360, 2145, 438, # 3398 + 2591, 2896, 2379, 3317, 1068, 958, 3023, 461, 311, 2855, 2677, 4074, 1915, 3179, 4075, 1978, # 3414 + 383, 750, 2745, 2617, 4076, 274, 539, 385, 1278, 1442, 7361, 1154, 1964, 384, 561, 210, # 3430 + 98, 1295, 2548, 3515, 7362, 1711, 2415, 1482, 3416, 3884, 2897, 1257, 129, 7363, 3742, 642, # 3446 + 523, 2776, 2777, 2648, 7364, 141, 2231, 1333, 68, 176, 441, 876, 907, 4077, 603, 2592, # 3462 + 710, 171, 3417, 404, 549, 18, 3118, 2393, 1410, 3626, 1666, 7365, 3516, 4319, 2898, 4320, # 3478 + 7366, 2973, 368, 7367, 146, 366, 99, 871, 3627, 1543, 748, 807, 1586, 1185, 22, 2258, # 3494 + 379, 3743, 3180, 7368, 3181, 505, 1941, 2618, 1991, 1382, 2314, 7369, 380, 2357, 218, 702, # 3510 + 1817, 1248, 3418, 3024, 3517, 3318, 3249, 7370, 2974, 3628, 930, 3250, 3744, 7371, 59, 7372, # 3526 + 585, 601, 4078, 497, 3419, 1112, 1314, 4321, 1801, 7373, 1223, 1472, 2174, 7374, 749, 1836, # 3542 + 690, 1899, 3745, 1772, 3885, 1476, 429, 1043, 1790, 2232, 2116, 917, 4079, 447, 1086, 1629, # 3558 + 7375, 556, 7376, 7377, 2020, 1654, 844, 1090, 105, 550, 966, 1758, 2815, 1008, 1782, 686, # 3574 + 1095, 7378, 2282, 793, 1602, 7379, 3518, 2593, 4322, 4080, 2933, 2297, 4323, 3746, 980, 2496, # 3590 + 544, 353, 527, 4324, 908, 2678, 2899, 7380, 381, 2619, 1942, 1348, 7381, 1341, 1252, 560, # 3606 + 3072, 7382, 3420, 2856, 7383, 2053, 973, 886, 2080, 143, 4325, 7384, 7385, 157, 3886, 496, # 3622 + 4081, 57, 840, 540, 2038, 4326, 4327, 3421, 2117, 1445, 970, 2259, 1748, 1965, 2081, 4082, # 3638 + 3119, 1234, 1775, 3251, 2816, 3629, 773, 1206, 2129, 1066, 2039, 1326, 3887, 1738, 1725, 4083, # 3654 + 279, 3120, 51, 1544, 2594, 423, 1578, 2130, 2066, 173, 4328, 1879, 7386, 7387, 1583, 264, # 3670 + 610, 3630, 4329, 2439, 280, 154, 7388, 7389, 7390, 1739, 338, 1282, 3073, 693, 2857, 1411, # 3686 + 1074, 3747, 2440, 7391, 4330, 7392, 7393, 1240, 952, 2394, 7394, 2900, 1538, 2679, 685, 1483, # 3702 + 4084, 2468, 1436, 953, 4085, 2054, 4331, 671, 2395, 79, 4086, 2441, 3252, 608, 567, 2680, # 3718 + 3422, 4087, 4088, 1691, 393, 1261, 1791, 2396, 7395, 4332, 7396, 7397, 7398, 7399, 1383, 1672, # 3734 + 3748, 3182, 1464, 522, 1119, 661, 1150, 216, 675, 4333, 3888, 1432, 3519, 609, 4334, 2681, # 3750 + 2397, 7400, 7401, 7402, 4089, 3025, 0, 7403, 2469, 315, 231, 2442, 301, 3319, 4335, 2380, # 3766 + 7404, 233, 4090, 3631, 1818, 4336, 4337, 7405, 96, 1776, 1315, 2082, 7406, 257, 7407, 1809, # 3782 + 3632, 2709, 1139, 1819, 4091, 2021, 1124, 2163, 2778, 1777, 2649, 7408, 3074, 363, 1655, 3183, # 3798 + 7409, 2975, 7410, 7411, 7412, 3889, 1567, 3890, 718, 103, 3184, 849, 1443, 341, 3320, 2934, # 3814 + 1484, 7413, 1712, 127, 67, 339, 4092, 2398, 679, 1412, 821, 7414, 7415, 834, 738, 351, # 3830 + 2976, 2146, 846, 235, 1497, 1880, 418, 1992, 3749, 2710, 186, 1100, 2147, 2746, 3520, 1545, # 3846 + 1355, 2935, 2858, 1377, 583, 3891, 4093, 2573, 2977, 7416, 1298, 3633, 1078, 2549, 3634, 2358, # 3862 + 78, 3750, 3751, 267, 1289, 2099, 2001, 1594, 4094, 348, 369, 1274, 2194, 2175, 1837, 4338, # 3878 + 1820, 2817, 3635, 2747, 2283, 2002, 4339, 2936, 2748, 144, 3321, 882, 4340, 3892, 2749, 3423, # 3894 + 4341, 2901, 7417, 4095, 1726, 320, 7418, 3893, 3026, 788, 2978, 7419, 2818, 1773, 1327, 2859, # 3910 + 3894, 2819, 7420, 1306, 4342, 2003, 1700, 3752, 3521, 2359, 2650, 787, 2022, 506, 824, 3636, # 3926 + 534, 323, 4343, 1044, 3322, 2023, 1900, 946, 3424, 7421, 1778, 1500, 1678, 7422, 1881, 4344, # 3942 + 165, 243, 4345, 3637, 2521, 123, 683, 4096, 764, 4346, 36, 3895, 1792, 589, 2902, 816, # 3958 + 626, 1667, 3027, 2233, 1639, 1555, 1622, 3753, 3896, 7423, 3897, 2860, 1370, 1228, 1932, 891, # 3974 + 2083, 2903, 304, 4097, 7424, 292, 2979, 2711, 3522, 691, 2100, 4098, 1115, 4347, 118, 662, # 3990 + 7425, 611, 1156, 854, 2381, 1316, 2861, 2, 386, 515, 2904, 7426, 7427, 3253, 868, 2234, # 4006 + 1486, 855, 2651, 785, 2212, 3028, 7428, 1040, 3185, 3523, 7429, 3121, 448, 7430, 1525, 7431, # 4022 + 2164, 4348, 7432, 3754, 7433, 4099, 2820, 3524, 3122, 503, 818, 3898, 3123, 1568, 814, 676, # 4038 + 1444, 306, 1749, 7434, 3755, 1416, 1030, 197, 1428, 805, 2821, 1501, 4349, 7435, 7436, 7437, # 4054 + 1993, 7438, 4350, 7439, 7440, 2195, 13, 2779, 3638, 2980, 3124, 1229, 1916, 7441, 3756, 2131, # 4070 + 7442, 4100, 4351, 2399, 3525, 7443, 2213, 1511, 1727, 1120, 7444, 7445, 646, 3757, 2443, 307, # 4086 + 7446, 7447, 1595, 3186, 7448, 7449, 7450, 3639, 1113, 1356, 3899, 1465, 2522, 2523, 7451, 519, # 4102 + 7452, 128, 2132, 92, 2284, 1979, 7453, 3900, 1512, 342, 3125, 2196, 7454, 2780, 2214, 1980, # 4118 + 3323, 7455, 290, 1656, 1317, 789, 827, 2360, 7456, 3758, 4352, 562, 581, 3901, 7457, 401, # 4134 + 4353, 2248, 94, 4354, 1399, 2781, 7458, 1463, 2024, 4355, 3187, 1943, 7459, 828, 1105, 4101, # 4150 + 1262, 1394, 7460, 4102, 605, 4356, 7461, 1783, 2862, 7462, 2822, 819, 2101, 578, 2197, 2937, # 4166 + 7463, 1502, 436, 3254, 4103, 3255, 2823, 3902, 2905, 3425, 3426, 7464, 2712, 2315, 7465, 7466, # 4182 + 2332, 2067, 23, 4357, 193, 826, 3759, 2102, 699, 1630, 4104, 3075, 390, 1793, 1064, 3526, # 4198 + 7467, 1579, 3076, 3077, 1400, 7468, 4105, 1838, 1640, 2863, 7469, 4358, 4359, 137, 4106, 598, # 4214 + 3078, 1966, 780, 104, 974, 2938, 7470, 278, 899, 253, 402, 572, 504, 493, 1339, 7471, # 4230 + 3903, 1275, 4360, 2574, 2550, 7472, 3640, 3029, 3079, 2249, 565, 1334, 2713, 863, 41, 7473, # 4246 + 7474, 4361, 7475, 1657, 2333, 19, 463, 2750, 4107, 606, 7476, 2981, 3256, 1087, 2084, 1323, # 4262 + 2652, 2982, 7477, 1631, 1623, 1750, 4108, 2682, 7478, 2864, 791, 2714, 2653, 2334, 232, 2416, # 4278 + 7479, 2983, 1498, 7480, 2654, 2620, 755, 1366, 3641, 3257, 3126, 2025, 1609, 119, 1917, 3427, # 4294 + 862, 1026, 4109, 7481, 3904, 3760, 4362, 3905, 4363, 2260, 1951, 2470, 7482, 1125, 817, 4110, # 4310 + 4111, 3906, 1513, 1766, 2040, 1487, 4112, 3030, 3258, 2824, 3761, 3127, 7483, 7484, 1507, 7485, # 4326 + 2683, 733, 40, 1632, 1106, 2865, 345, 4113, 841, 2524, 230, 4364, 2984, 1846, 3259, 3428, # 4342 + 7486, 1263, 986, 3429, 7487, 735, 879, 254, 1137, 857, 622, 1300, 1180, 1388, 1562, 3907, # 4358 + 3908, 2939, 967, 2751, 2655, 1349, 592, 2133, 1692, 3324, 2985, 1994, 4114, 1679, 3909, 1901, # 4374 + 2185, 7488, 739, 3642, 2715, 1296, 1290, 7489, 4115, 2198, 2199, 1921, 1563, 2595, 2551, 1870, # 4390 + 2752, 2986, 7490, 435, 7491, 343, 1108, 596, 17, 1751, 4365, 2235, 3430, 3643, 7492, 4366, # 4406 + 294, 3527, 2940, 1693, 477, 979, 281, 2041, 3528, 643, 2042, 3644, 2621, 2782, 2261, 1031, # 4422 + 2335, 2134, 2298, 3529, 4367, 367, 1249, 2552, 7493, 3530, 7494, 4368, 1283, 3325, 2004, 240, # 4438 + 1762, 3326, 4369, 4370, 836, 1069, 3128, 474, 7495, 2148, 2525, 268, 3531, 7496, 3188, 1521, # 4454 + 1284, 7497, 1658, 1546, 4116, 7498, 3532, 3533, 7499, 4117, 3327, 2684, 1685, 4118, 961, 1673, # 4470 + 2622, 190, 2005, 2200, 3762, 4371, 4372, 7500, 570, 2497, 3645, 1490, 7501, 4373, 2623, 3260, # 4486 + 1956, 4374, 584, 1514, 396, 1045, 1944, 7502, 4375, 1967, 2444, 7503, 7504, 4376, 3910, 619, # 4502 + 7505, 3129, 3261, 215, 2006, 2783, 2553, 3189, 4377, 3190, 4378, 763, 4119, 3763, 4379, 7506, # 4518 + 7507, 1957, 1767, 2941, 3328, 3646, 1174, 452, 1477, 4380, 3329, 3130, 7508, 2825, 1253, 2382, # 4534 + 2186, 1091, 2285, 4120, 492, 7509, 638, 1169, 1824, 2135, 1752, 3911, 648, 926, 1021, 1324, # 4550 + 4381, 520, 4382, 997, 847, 1007, 892, 4383, 3764, 2262, 1871, 3647, 7510, 2400, 1784, 4384, # 4566 + 1952, 2942, 3080, 3191, 1728, 4121, 2043, 3648, 4385, 2007, 1701, 3131, 1551, 30, 2263, 4122, # 4582 + 7511, 2026, 4386, 3534, 7512, 501, 7513, 4123, 594, 3431, 2165, 1821, 3535, 3432, 3536, 3192, # 4598 + 829, 2826, 4124, 7514, 1680, 3132, 1225, 4125, 7515, 3262, 4387, 4126, 3133, 2336, 7516, 4388, # 4614 + 4127, 7517, 3912, 3913, 7518, 1847, 2383, 2596, 3330, 7519, 4389, 374, 3914, 652, 4128, 4129, # 4630 + 375, 1140, 798, 7520, 7521, 7522, 2361, 4390, 2264, 546, 1659, 138, 3031, 2445, 4391, 7523, # 4646 + 2250, 612, 1848, 910, 796, 3765, 1740, 1371, 825, 3766, 3767, 7524, 2906, 2554, 7525, 692, # 4662 + 444, 3032, 2624, 801, 4392, 4130, 7526, 1491, 244, 1053, 3033, 4131, 4132, 340, 7527, 3915, # 4678 + 1041, 2987, 293, 1168, 87, 1357, 7528, 1539, 959, 7529, 2236, 721, 694, 4133, 3768, 219, # 4694 + 1478, 644, 1417, 3331, 2656, 1413, 1401, 1335, 1389, 3916, 7530, 7531, 2988, 2362, 3134, 1825, # 4710 + 730, 1515, 184, 2827, 66, 4393, 7532, 1660, 2943, 246, 3332, 378, 1457, 226, 3433, 975, # 4726 + 3917, 2944, 1264, 3537, 674, 696, 7533, 163, 7534, 1141, 2417, 2166, 713, 3538, 3333, 4394, # 4742 + 3918, 7535, 7536, 1186, 15, 7537, 1079, 1070, 7538, 1522, 3193, 3539, 276, 1050, 2716, 758, # 4758 + 1126, 653, 2945, 3263, 7539, 2337, 889, 3540, 3919, 3081, 2989, 903, 1250, 4395, 3920, 3434, # 4774 + 3541, 1342, 1681, 1718, 766, 3264, 286, 89, 2946, 3649, 7540, 1713, 7541, 2597, 3334, 2990, # 4790 + 7542, 2947, 2215, 3194, 2866, 7543, 4396, 2498, 2526, 181, 387, 1075, 3921, 731, 2187, 3335, # 4806 + 7544, 3265, 310, 313, 3435, 2299, 770, 4134, 54, 3034, 189, 4397, 3082, 3769, 3922, 7545, # 4822 + 1230, 1617, 1849, 355, 3542, 4135, 4398, 3336, 111, 4136, 3650, 1350, 3135, 3436, 3035, 4137, # 4838 + 2149, 3266, 3543, 7546, 2784, 3923, 3924, 2991, 722, 2008, 7547, 1071, 247, 1207, 2338, 2471, # 4854 + 1378, 4399, 2009, 864, 1437, 1214, 4400, 373, 3770, 1142, 2216, 667, 4401, 442, 2753, 2555, # 4870 + 3771, 3925, 1968, 4138, 3267, 1839, 837, 170, 1107, 934, 1336, 1882, 7548, 7549, 2118, 4139, # 4886 + 2828, 743, 1569, 7550, 4402, 4140, 582, 2384, 1418, 3437, 7551, 1802, 7552, 357, 1395, 1729, # 4902 + 3651, 3268, 2418, 1564, 2237, 7553, 3083, 3772, 1633, 4403, 1114, 2085, 4141, 1532, 7554, 482, # 4918 + 2446, 4404, 7555, 7556, 1492, 833, 1466, 7557, 2717, 3544, 1641, 2829, 7558, 1526, 1272, 3652, # 4934 + 4142, 1686, 1794, 416, 2556, 1902, 1953, 1803, 7559, 3773, 2785, 3774, 1159, 2316, 7560, 2867, # 4950 + 4405, 1610, 1584, 3036, 2419, 2754, 443, 3269, 1163, 3136, 7561, 7562, 3926, 7563, 4143, 2499, # 4966 + 3037, 4406, 3927, 3137, 2103, 1647, 3545, 2010, 1872, 4144, 7564, 4145, 431, 3438, 7565, 250, # 4982 + 97, 81, 4146, 7566, 1648, 1850, 1558, 160, 848, 7567, 866, 740, 1694, 7568, 2201, 2830, # 4998 + 3195, 4147, 4407, 3653, 1687, 950, 2472, 426, 469, 3196, 3654, 3655, 3928, 7569, 7570, 1188, # 5014 + 424, 1995, 861, 3546, 4148, 3775, 2202, 2685, 168, 1235, 3547, 4149, 7571, 2086, 1674, 4408, # 5030 + 3337, 3270, 220, 2557, 1009, 7572, 3776, 670, 2992, 332, 1208, 717, 7573, 7574, 3548, 2447, # 5046 + 3929, 3338, 7575, 513, 7576, 1209, 2868, 3339, 3138, 4409, 1080, 7577, 7578, 7579, 7580, 2527, # 5062 + 3656, 3549, 815, 1587, 3930, 3931, 7581, 3550, 3439, 3777, 1254, 4410, 1328, 3038, 1390, 3932, # 5078 + 1741, 3933, 3778, 3934, 7582, 236, 3779, 2448, 3271, 7583, 7584, 3657, 3780, 1273, 3781, 4411, # 5094 + 7585, 308, 7586, 4412, 245, 4413, 1851, 2473, 1307, 2575, 430, 715, 2136, 2449, 7587, 270, # 5110 + 199, 2869, 3935, 7588, 3551, 2718, 1753, 761, 1754, 725, 1661, 1840, 4414, 3440, 3658, 7589, # 5126 + 7590, 587, 14, 3272, 227, 2598, 326, 480, 2265, 943, 2755, 3552, 291, 650, 1883, 7591, # 5142 + 1702, 1226, 102, 1547, 62, 3441, 904, 4415, 3442, 1164, 4150, 7592, 7593, 1224, 1548, 2756, # 5158 + 391, 498, 1493, 7594, 1386, 1419, 7595, 2055, 1177, 4416, 813, 880, 1081, 2363, 566, 1145, # 5174 + 4417, 2286, 1001, 1035, 2558, 2599, 2238, 394, 1286, 7596, 7597, 2068, 7598, 86, 1494, 1730, # 5190 + 3936, 491, 1588, 745, 897, 2948, 843, 3340, 3937, 2757, 2870, 3273, 1768, 998, 2217, 2069, # 5206 + 397, 1826, 1195, 1969, 3659, 2993, 3341, 284, 7599, 3782, 2500, 2137, 2119, 1903, 7600, 3938, # 5222 + 2150, 3939, 4151, 1036, 3443, 1904, 114, 2559, 4152, 209, 1527, 7601, 7602, 2949, 2831, 2625, # 5238 + 2385, 2719, 3139, 812, 2560, 7603, 3274, 7604, 1559, 737, 1884, 3660, 1210, 885, 28, 2686, # 5254 + 3553, 3783, 7605, 4153, 1004, 1779, 4418, 7606, 346, 1981, 2218, 2687, 4419, 3784, 1742, 797, # 5270 + 1642, 3940, 1933, 1072, 1384, 2151, 896, 3941, 3275, 3661, 3197, 2871, 3554, 7607, 2561, 1958, # 5286 + 4420, 2450, 1785, 7608, 7609, 7610, 3942, 4154, 1005, 1308, 3662, 4155, 2720, 4421, 4422, 1528, # 5302 + 2600, 161, 1178, 4156, 1982, 987, 4423, 1101, 4157, 631, 3943, 1157, 3198, 2420, 1343, 1241, # 5318 + 1016, 2239, 2562, 372, 877, 2339, 2501, 1160, 555, 1934, 911, 3944, 7611, 466, 1170, 169, # 5334 + 1051, 2907, 2688, 3663, 2474, 2994, 1182, 2011, 2563, 1251, 2626, 7612, 992, 2340, 3444, 1540, # 5350 + 2721, 1201, 2070, 2401, 1996, 2475, 7613, 4424, 528, 1922, 2188, 1503, 1873, 1570, 2364, 3342, # 5366 + 3276, 7614, 557, 1073, 7615, 1827, 3445, 2087, 2266, 3140, 3039, 3084, 767, 3085, 2786, 4425, # 5382 + 1006, 4158, 4426, 2341, 1267, 2176, 3664, 3199, 778, 3945, 3200, 2722, 1597, 2657, 7616, 4427, # 5398 + 7617, 3446, 7618, 7619, 7620, 3277, 2689, 1433, 3278, 131, 95, 1504, 3946, 723, 4159, 3141, # 5414 + 1841, 3555, 2758, 2189, 3947, 2027, 2104, 3665, 7621, 2995, 3948, 1218, 7622, 3343, 3201, 3949, # 5430 + 4160, 2576, 248, 1634, 3785, 912, 7623, 2832, 3666, 3040, 3786, 654, 53, 7624, 2996, 7625, # 5446 + 1688, 4428, 777, 3447, 1032, 3950, 1425, 7626, 191, 820, 2120, 2833, 971, 4429, 931, 3202, # 5462 + 135, 664, 783, 3787, 1997, 772, 2908, 1935, 3951, 3788, 4430, 2909, 3203, 282, 2723, 640, # 5478 + 1372, 3448, 1127, 922, 325, 3344, 7627, 7628, 711, 2044, 7629, 7630, 3952, 2219, 2787, 1936, # 5494 + 3953, 3345, 2220, 2251, 3789, 2300, 7631, 4431, 3790, 1258, 3279, 3954, 3204, 2138, 2950, 3955, # 5510 + 3956, 7632, 2221, 258, 3205, 4432, 101, 1227, 7633, 3280, 1755, 7634, 1391, 3281, 7635, 2910, # 5526 + 2056, 893, 7636, 7637, 7638, 1402, 4161, 2342, 7639, 7640, 3206, 3556, 7641, 7642, 878, 1325, # 5542 + 1780, 2788, 4433, 259, 1385, 2577, 744, 1183, 2267, 4434, 7643, 3957, 2502, 7644, 684, 1024, # 5558 + 4162, 7645, 472, 3557, 3449, 1165, 3282, 3958, 3959, 322, 2152, 881, 455, 1695, 1152, 1340, # 5574 + 660, 554, 2153, 4435, 1058, 4436, 4163, 830, 1065, 3346, 3960, 4437, 1923, 7646, 1703, 1918, # 5590 + 7647, 932, 2268, 122, 7648, 4438, 947, 677, 7649, 3791, 2627, 297, 1905, 1924, 2269, 4439, # 5606 + 2317, 3283, 7650, 7651, 4164, 7652, 4165, 84, 4166, 112, 989, 7653, 547, 1059, 3961, 701, # 5622 + 3558, 1019, 7654, 4167, 7655, 3450, 942, 639, 457, 2301, 2451, 993, 2951, 407, 851, 494, # 5638 + 4440, 3347, 927, 7656, 1237, 7657, 2421, 3348, 573, 4168, 680, 921, 2911, 1279, 1874, 285, # 5654 + 790, 1448, 1983, 719, 2167, 7658, 7659, 4441, 3962, 3963, 1649, 7660, 1541, 563, 7661, 1077, # 5670 + 7662, 3349, 3041, 3451, 511, 2997, 3964, 3965, 3667, 3966, 1268, 2564, 3350, 3207, 4442, 4443, # 5686 + 7663, 535, 1048, 1276, 1189, 2912, 2028, 3142, 1438, 1373, 2834, 2952, 1134, 2012, 7664, 4169, # 5702 + 1238, 2578, 3086, 1259, 7665, 700, 7666, 2953, 3143, 3668, 4170, 7667, 4171, 1146, 1875, 1906, # 5718 + 4444, 2601, 3967, 781, 2422, 132, 1589, 203, 147, 273, 2789, 2402, 898, 1786, 2154, 3968, # 5734 + 3969, 7668, 3792, 2790, 7669, 7670, 4445, 4446, 7671, 3208, 7672, 1635, 3793, 965, 7673, 1804, # 5750 + 2690, 1516, 3559, 1121, 1082, 1329, 3284, 3970, 1449, 3794, 65, 1128, 2835, 2913, 2759, 1590, # 5766 + 3795, 7674, 7675, 12, 2658, 45, 976, 2579, 3144, 4447, 517, 2528, 1013, 1037, 3209, 7676, # 5782 + 3796, 2836, 7677, 3797, 7678, 3452, 7679, 2602, 614, 1998, 2318, 3798, 3087, 2724, 2628, 7680, # 5798 + 2580, 4172, 599, 1269, 7681, 1810, 3669, 7682, 2691, 3088, 759, 1060, 489, 1805, 3351, 3285, # 5814 + 1358, 7683, 7684, 2386, 1387, 1215, 2629, 2252, 490, 7685, 7686, 4173, 1759, 2387, 2343, 7687, # 5830 + 4448, 3799, 1907, 3971, 2630, 1806, 3210, 4449, 3453, 3286, 2760, 2344, 874, 7688, 7689, 3454, # 5846 + 3670, 1858, 91, 2914, 3671, 3042, 3800, 4450, 7690, 3145, 3972, 2659, 7691, 3455, 1202, 1403, # 5862 + 3801, 2954, 2529, 1517, 2503, 4451, 3456, 2504, 7692, 4452, 7693, 2692, 1885, 1495, 1731, 3973, # 5878 + 2365, 4453, 7694, 2029, 7695, 7696, 3974, 2693, 1216, 237, 2581, 4174, 2319, 3975, 3802, 4454, # 5894 + 4455, 2694, 3560, 3457, 445, 4456, 7697, 7698, 7699, 7700, 2761, 61, 3976, 3672, 1822, 3977, # 5910 + 7701, 687, 2045, 935, 925, 405, 2660, 703, 1096, 1859, 2725, 4457, 3978, 1876, 1367, 2695, # 5926 + 3352, 918, 2105, 1781, 2476, 334, 3287, 1611, 1093, 4458, 564, 3146, 3458, 3673, 3353, 945, # 5942 + 2631, 2057, 4459, 7702, 1925, 872, 4175, 7703, 3459, 2696, 3089, 349, 4176, 3674, 3979, 4460, # 5958 + 3803, 4177, 3675, 2155, 3980, 4461, 4462, 4178, 4463, 2403, 2046, 782, 3981, 400, 251, 4179, # 5974 + 1624, 7704, 7705, 277, 3676, 299, 1265, 476, 1191, 3804, 2121, 4180, 4181, 1109, 205, 7706, # 5990 + 2582, 1000, 2156, 3561, 1860, 7707, 7708, 7709, 4464, 7710, 4465, 2565, 107, 2477, 2157, 3982, # 6006 + 3460, 3147, 7711, 1533, 541, 1301, 158, 753, 4182, 2872, 3562, 7712, 1696, 370, 1088, 4183, # 6022 + 4466, 3563, 579, 327, 440, 162, 2240, 269, 1937, 1374, 3461, 968, 3043, 56, 1396, 3090, # 6038 + 2106, 3288, 3354, 7713, 1926, 2158, 4467, 2998, 7714, 3564, 7715, 7716, 3677, 4468, 2478, 7717, # 6054 + 2791, 7718, 1650, 4469, 7719, 2603, 7720, 7721, 3983, 2661, 3355, 1149, 3356, 3984, 3805, 3985, # 6070 + 7722, 1076, 49, 7723, 951, 3211, 3289, 3290, 450, 2837, 920, 7724, 1811, 2792, 2366, 4184, # 6086 + 1908, 1138, 2367, 3806, 3462, 7725, 3212, 4470, 1909, 1147, 1518, 2423, 4471, 3807, 7726, 4472, # 6102 + 2388, 2604, 260, 1795, 3213, 7727, 7728, 3808, 3291, 708, 7729, 3565, 1704, 7730, 3566, 1351, # 6118 + 1618, 3357, 2999, 1886, 944, 4185, 3358, 4186, 3044, 3359, 4187, 7731, 3678, 422, 413, 1714, # 6134 + 3292, 500, 2058, 2345, 4188, 2479, 7732, 1344, 1910, 954, 7733, 1668, 7734, 7735, 3986, 2404, # 6150 + 4189, 3567, 3809, 4190, 7736, 2302, 1318, 2505, 3091, 133, 3092, 2873, 4473, 629, 31, 2838, # 6166 + 2697, 3810, 4474, 850, 949, 4475, 3987, 2955, 1732, 2088, 4191, 1496, 1852, 7737, 3988, 620, # 6182 + 3214, 981, 1242, 3679, 3360, 1619, 3680, 1643, 3293, 2139, 2452, 1970, 1719, 3463, 2168, 7738, # 6198 + 3215, 7739, 7740, 3361, 1828, 7741, 1277, 4476, 1565, 2047, 7742, 1636, 3568, 3093, 7743, 869, # 6214 + 2839, 655, 3811, 3812, 3094, 3989, 3000, 3813, 1310, 3569, 4477, 7744, 7745, 7746, 1733, 558, # 6230 + 4478, 3681, 335, 1549, 3045, 1756, 4192, 3682, 1945, 3464, 1829, 1291, 1192, 470, 2726, 2107, # 6246 + 2793, 913, 1054, 3990, 7747, 1027, 7748, 3046, 3991, 4479, 982, 2662, 3362, 3148, 3465, 3216, # 6262 + 3217, 1946, 2794, 7749, 571, 4480, 7750, 1830, 7751, 3570, 2583, 1523, 2424, 7752, 2089, 984, # 6278 + 4481, 3683, 1959, 7753, 3684, 852, 923, 2795, 3466, 3685, 969, 1519, 999, 2048, 2320, 1705, # 6294 + 7754, 3095, 615, 1662, 151, 597, 3992, 2405, 2321, 1049, 275, 4482, 3686, 4193, 568, 3687, # 6310 + 3571, 2480, 4194, 3688, 7755, 2425, 2270, 409, 3218, 7756, 1566, 2874, 3467, 1002, 769, 2840, # 6326 + 194, 2090, 3149, 3689, 2222, 3294, 4195, 628, 1505, 7757, 7758, 1763, 2177, 3001, 3993, 521, # 6342 + 1161, 2584, 1787, 2203, 2406, 4483, 3994, 1625, 4196, 4197, 412, 42, 3096, 464, 7759, 2632, # 6358 + 4484, 3363, 1760, 1571, 2875, 3468, 2530, 1219, 2204, 3814, 2633, 2140, 2368, 4485, 4486, 3295, # 6374 + 1651, 3364, 3572, 7760, 7761, 3573, 2481, 3469, 7762, 3690, 7763, 7764, 2271, 2091, 460, 7765, # 6390 + 4487, 7766, 3002, 962, 588, 3574, 289, 3219, 2634, 1116, 52, 7767, 3047, 1796, 7768, 7769, # 6406 + 7770, 1467, 7771, 1598, 1143, 3691, 4198, 1984, 1734, 1067, 4488, 1280, 3365, 465, 4489, 1572, # 6422 + 510, 7772, 1927, 2241, 1812, 1644, 3575, 7773, 4490, 3692, 7774, 7775, 2663, 1573, 1534, 7776, # 6438 + 7777, 4199, 536, 1807, 1761, 3470, 3815, 3150, 2635, 7778, 7779, 7780, 4491, 3471, 2915, 1911, # 6454 + 2796, 7781, 3296, 1122, 377, 3220, 7782, 360, 7783, 7784, 4200, 1529, 551, 7785, 2059, 3693, # 6470 + 1769, 2426, 7786, 2916, 4201, 3297, 3097, 2322, 2108, 2030, 4492, 1404, 136, 1468, 1479, 672, # 6486 + 1171, 3221, 2303, 271, 3151, 7787, 2762, 7788, 2049, 678, 2727, 865, 1947, 4493, 7789, 2013, # 6502 + 3995, 2956, 7790, 2728, 2223, 1397, 3048, 3694, 4494, 4495, 1735, 2917, 3366, 3576, 7791, 3816, # 6518 + 509, 2841, 2453, 2876, 3817, 7792, 7793, 3152, 3153, 4496, 4202, 2531, 4497, 2304, 1166, 1010, # 6534 + 552, 681, 1887, 7794, 7795, 2957, 2958, 3996, 1287, 1596, 1861, 3154, 358, 453, 736, 175, # 6550 + 478, 1117, 905, 1167, 1097, 7796, 1853, 1530, 7797, 1706, 7798, 2178, 3472, 2287, 3695, 3473, # 6566 + 3577, 4203, 2092, 4204, 7799, 3367, 1193, 2482, 4205, 1458, 2190, 2205, 1862, 1888, 1421, 3298, # 6582 + 2918, 3049, 2179, 3474, 595, 2122, 7800, 3997, 7801, 7802, 4206, 1707, 2636, 223, 3696, 1359, # 6598 + 751, 3098, 183, 3475, 7803, 2797, 3003, 419, 2369, 633, 704, 3818, 2389, 241, 7804, 7805, # 6614 + 7806, 838, 3004, 3697, 2272, 2763, 2454, 3819, 1938, 2050, 3998, 1309, 3099, 2242, 1181, 7807, # 6630 + 1136, 2206, 3820, 2370, 1446, 4207, 2305, 4498, 7808, 7809, 4208, 1055, 2605, 484, 3698, 7810, # 6646 + 3999, 625, 4209, 2273, 3368, 1499, 4210, 4000, 7811, 4001, 4211, 3222, 2274, 2275, 3476, 7812, # 6662 + 7813, 2764, 808, 2606, 3699, 3369, 4002, 4212, 3100, 2532, 526, 3370, 3821, 4213, 955, 7814, # 6678 + 1620, 4214, 2637, 2427, 7815, 1429, 3700, 1669, 1831, 994, 928, 7816, 3578, 1260, 7817, 7818, # 6694 + 7819, 1948, 2288, 741, 2919, 1626, 4215, 2729, 2455, 867, 1184, 362, 3371, 1392, 7820, 7821, # 6710 + 4003, 4216, 1770, 1736, 3223, 2920, 4499, 4500, 1928, 2698, 1459, 1158, 7822, 3050, 3372, 2877, # 6726 + 1292, 1929, 2506, 2842, 3701, 1985, 1187, 2071, 2014, 2607, 4217, 7823, 2566, 2507, 2169, 3702, # 6742 + 2483, 3299, 7824, 3703, 4501, 7825, 7826, 666, 1003, 3005, 1022, 3579, 4218, 7827, 4502, 1813, # 6758 + 2253, 574, 3822, 1603, 295, 1535, 705, 3823, 4219, 283, 858, 417, 7828, 7829, 3224, 4503, # 6774 + 4504, 3051, 1220, 1889, 1046, 2276, 2456, 4004, 1393, 1599, 689, 2567, 388, 4220, 7830, 2484, # 6790 + 802, 7831, 2798, 3824, 2060, 1405, 2254, 7832, 4505, 3825, 2109, 1052, 1345, 3225, 1585, 7833, # 6806 + 809, 7834, 7835, 7836, 575, 2730, 3477, 956, 1552, 1469, 1144, 2323, 7837, 2324, 1560, 2457, # 6822 + 3580, 3226, 4005, 616, 2207, 3155, 2180, 2289, 7838, 1832, 7839, 3478, 4506, 7840, 1319, 3704, # 6838 + 3705, 1211, 3581, 1023, 3227, 1293, 2799, 7841, 7842, 7843, 3826, 607, 2306, 3827, 762, 2878, # 6854 + 1439, 4221, 1360, 7844, 1485, 3052, 7845, 4507, 1038, 4222, 1450, 2061, 2638, 4223, 1379, 4508, # 6870 + 2585, 7846, 7847, 4224, 1352, 1414, 2325, 2921, 1172, 7848, 7849, 3828, 3829, 7850, 1797, 1451, # 6886 + 7851, 7852, 7853, 7854, 2922, 4006, 4007, 2485, 2346, 411, 4008, 4009, 3582, 3300, 3101, 4509, # 6902 + 1561, 2664, 1452, 4010, 1375, 7855, 7856, 47, 2959, 316, 7857, 1406, 1591, 2923, 3156, 7858, # 6918 + 1025, 2141, 3102, 3157, 354, 2731, 884, 2224, 4225, 2407, 508, 3706, 726, 3583, 996, 2428, # 6934 + 3584, 729, 7859, 392, 2191, 1453, 4011, 4510, 3707, 7860, 7861, 2458, 3585, 2608, 1675, 2800, # 6950 + 919, 2347, 2960, 2348, 1270, 4511, 4012, 73, 7862, 7863, 647, 7864, 3228, 2843, 2255, 1550, # 6966 + 1346, 3006, 7865, 1332, 883, 3479, 7866, 7867, 7868, 7869, 3301, 2765, 7870, 1212, 831, 1347, # 6982 + 4226, 4512, 2326, 3830, 1863, 3053, 720, 3831, 4513, 4514, 3832, 7871, 4227, 7872, 7873, 4515, # 6998 + 7874, 7875, 1798, 4516, 3708, 2609, 4517, 3586, 1645, 2371, 7876, 7877, 2924, 669, 2208, 2665, # 7014 + 2429, 7878, 2879, 7879, 7880, 1028, 3229, 7881, 4228, 2408, 7882, 2256, 1353, 7883, 7884, 4518, # 7030 + 3158, 518, 7885, 4013, 7886, 4229, 1960, 7887, 2142, 4230, 7888, 7889, 3007, 2349, 2350, 3833, # 7046 + 516, 1833, 1454, 4014, 2699, 4231, 4519, 2225, 2610, 1971, 1129, 3587, 7890, 2766, 7891, 2961, # 7062 + 1422, 577, 1470, 3008, 1524, 3373, 7892, 7893, 432, 4232, 3054, 3480, 7894, 2586, 1455, 2508, # 7078 + 2226, 1972, 1175, 7895, 1020, 2732, 4015, 3481, 4520, 7896, 2733, 7897, 1743, 1361, 3055, 3482, # 7094 + 2639, 4016, 4233, 4521, 2290, 895, 924, 4234, 2170, 331, 2243, 3056, 166, 1627, 3057, 1098, # 7110 + 7898, 1232, 2880, 2227, 3374, 4522, 657, 403, 1196, 2372, 542, 3709, 3375, 1600, 4235, 3483, # 7126 + 7899, 4523, 2767, 3230, 576, 530, 1362, 7900, 4524, 2533, 2666, 3710, 4017, 7901, 842, 3834, # 7142 + 7902, 2801, 2031, 1014, 4018, 213, 2700, 3376, 665, 621, 4236, 7903, 3711, 2925, 2430, 7904, # 7158 + 2431, 3302, 3588, 3377, 7905, 4237, 2534, 4238, 4525, 3589, 1682, 4239, 3484, 1380, 7906, 724, # 7174 + 2277, 600, 1670, 7907, 1337, 1233, 4526, 3103, 2244, 7908, 1621, 4527, 7909, 651, 4240, 7910, # 7190 + 1612, 4241, 2611, 7911, 2844, 7912, 2734, 2307, 3058, 7913, 716, 2459, 3059, 174, 1255, 2701, # 7206 + 4019, 3590, 548, 1320, 1398, 728, 4020, 1574, 7914, 1890, 1197, 3060, 4021, 7915, 3061, 3062, # 7222 + 3712, 3591, 3713, 747, 7916, 635, 4242, 4528, 7917, 7918, 7919, 4243, 7920, 7921, 4529, 7922, # 7238 + 3378, 4530, 2432, 451, 7923, 3714, 2535, 2072, 4244, 2735, 4245, 4022, 7924, 1764, 4531, 7925, # 7254 + 4246, 350, 7926, 2278, 2390, 2486, 7927, 4247, 4023, 2245, 1434, 4024, 488, 4532, 458, 4248, # 7270 + 4025, 3715, 771, 1330, 2391, 3835, 2568, 3159, 2159, 2409, 1553, 2667, 3160, 4249, 7928, 2487, # 7286 + 2881, 2612, 1720, 2702, 4250, 3379, 4533, 7929, 2536, 4251, 7930, 3231, 4252, 2768, 7931, 2015, # 7302 + 2736, 7932, 1155, 1017, 3716, 3836, 7933, 3303, 2308, 201, 1864, 4253, 1430, 7934, 4026, 7935, # 7318 + 7936, 7937, 7938, 7939, 4254, 1604, 7940, 414, 1865, 371, 2587, 4534, 4535, 3485, 2016, 3104, # 7334 + 4536, 1708, 960, 4255, 887, 389, 2171, 1536, 1663, 1721, 7941, 2228, 4027, 2351, 2926, 1580, # 7350 + 7942, 7943, 7944, 1744, 7945, 2537, 4537, 4538, 7946, 4539, 7947, 2073, 7948, 7949, 3592, 3380, # 7366 + 2882, 4256, 7950, 4257, 2640, 3381, 2802, 673, 2703, 2460, 709, 3486, 4028, 3593, 4258, 7951, # 7382 + 1148, 502, 634, 7952, 7953, 1204, 4540, 3594, 1575, 4541, 2613, 3717, 7954, 3718, 3105, 948, # 7398 + 3232, 121, 1745, 3837, 1110, 7955, 4259, 3063, 2509, 3009, 4029, 3719, 1151, 1771, 3838, 1488, # 7414 + 4030, 1986, 7956, 2433, 3487, 7957, 7958, 2093, 7959, 4260, 3839, 1213, 1407, 2803, 531, 2737, # 7430 + 2538, 3233, 1011, 1537, 7960, 2769, 4261, 3106, 1061, 7961, 3720, 3721, 1866, 2883, 7962, 2017, # 7446 + 120, 4262, 4263, 2062, 3595, 3234, 2309, 3840, 2668, 3382, 1954, 4542, 7963, 7964, 3488, 1047, # 7462 + 2704, 1266, 7965, 1368, 4543, 2845, 649, 3383, 3841, 2539, 2738, 1102, 2846, 2669, 7966, 7967, # 7478 + 1999, 7968, 1111, 3596, 2962, 7969, 2488, 3842, 3597, 2804, 1854, 3384, 3722, 7970, 7971, 3385, # 7494 + 2410, 2884, 3304, 3235, 3598, 7972, 2569, 7973, 3599, 2805, 4031, 1460, 856, 7974, 3600, 7975, # 7510 + 2885, 2963, 7976, 2886, 3843, 7977, 4264, 632, 2510, 875, 3844, 1697, 3845, 2291, 7978, 7979, # 7526 + 4544, 3010, 1239, 580, 4545, 4265, 7980, 914, 936, 2074, 1190, 4032, 1039, 2123, 7981, 7982, # 7542 + 7983, 3386, 1473, 7984, 1354, 4266, 3846, 7985, 2172, 3064, 4033, 915, 3305, 4267, 4268, 3306, # 7558 + 1605, 1834, 7986, 2739, 398, 3601, 4269, 3847, 4034, 328, 1912, 2847, 4035, 3848, 1331, 4270, # 7574 + 3011, 937, 4271, 7987, 3602, 4036, 4037, 3387, 2160, 4546, 3388, 524, 742, 538, 3065, 1012, # 7590 + 7988, 7989, 3849, 2461, 7990, 658, 1103, 225, 3850, 7991, 7992, 4547, 7993, 4548, 7994, 3236, # 7606 + 1243, 7995, 4038, 963, 2246, 4549, 7996, 2705, 3603, 3161, 7997, 7998, 2588, 2327, 7999, 4550, # 7622 + 8000, 8001, 8002, 3489, 3307, 957, 3389, 2540, 2032, 1930, 2927, 2462, 870, 2018, 3604, 1746, # 7638 + 2770, 2771, 2434, 2463, 8003, 3851, 8004, 3723, 3107, 3724, 3490, 3390, 3725, 8005, 1179, 3066, # 7654 + 8006, 3162, 2373, 4272, 3726, 2541, 3163, 3108, 2740, 4039, 8007, 3391, 1556, 2542, 2292, 977, # 7670 + 2887, 2033, 4040, 1205, 3392, 8008, 1765, 3393, 3164, 2124, 1271, 1689, 714, 4551, 3491, 8009, # 7686 + 2328, 3852, 533, 4273, 3605, 2181, 617, 8010, 2464, 3308, 3492, 2310, 8011, 8012, 3165, 8013, # 7702 + 8014, 3853, 1987, 618, 427, 2641, 3493, 3394, 8015, 8016, 1244, 1690, 8017, 2806, 4274, 4552, # 7718 + 8018, 3494, 8019, 8020, 2279, 1576, 473, 3606, 4275, 3395, 972, 8021, 3607, 8022, 3067, 8023, # 7734 + 8024, 4553, 4554, 8025, 3727, 4041, 4042, 8026, 153, 4555, 356, 8027, 1891, 2888, 4276, 2143, # 7750 + 408, 803, 2352, 8028, 3854, 8029, 4277, 1646, 2570, 2511, 4556, 4557, 3855, 8030, 3856, 4278, # 7766 + 8031, 2411, 3396, 752, 8032, 8033, 1961, 2964, 8034, 746, 3012, 2465, 8035, 4279, 3728, 698, # 7782 + 4558, 1892, 4280, 3608, 2543, 4559, 3609, 3857, 8036, 3166, 3397, 8037, 1823, 1302, 4043, 2706, # 7798 + 3858, 1973, 4281, 8038, 4282, 3167, 823, 1303, 1288, 1236, 2848, 3495, 4044, 3398, 774, 3859, # 7814 + 8039, 1581, 4560, 1304, 2849, 3860, 4561, 8040, 2435, 2161, 1083, 3237, 4283, 4045, 4284, 344, # 7830 + 1173, 288, 2311, 454, 1683, 8041, 8042, 1461, 4562, 4046, 2589, 8043, 8044, 4563, 985, 894, # 7846 + 8045, 3399, 3168, 8046, 1913, 2928, 3729, 1988, 8047, 2110, 1974, 8048, 4047, 8049, 2571, 1194, # 7862 + 425, 8050, 4564, 3169, 1245, 3730, 4285, 8051, 8052, 2850, 8053, 636, 4565, 1855, 3861, 760, # 7878 + 1799, 8054, 4286, 2209, 1508, 4566, 4048, 1893, 1684, 2293, 8055, 8056, 8057, 4287, 4288, 2210, # 7894 + 479, 8058, 8059, 832, 8060, 4049, 2489, 8061, 2965, 2490, 3731, 990, 3109, 627, 1814, 2642, # 7910 + 4289, 1582, 4290, 2125, 2111, 3496, 4567, 8062, 799, 4291, 3170, 8063, 4568, 2112, 1737, 3013, # 7926 + 1018, 543, 754, 4292, 3309, 1676, 4569, 4570, 4050, 8064, 1489, 8065, 3497, 8066, 2614, 2889, # 7942 + 4051, 8067, 8068, 2966, 8069, 8070, 8071, 8072, 3171, 4571, 4572, 2182, 1722, 8073, 3238, 3239, # 7958 + 1842, 3610, 1715, 481, 365, 1975, 1856, 8074, 8075, 1962, 2491, 4573, 8076, 2126, 3611, 3240, # 7974 + 433, 1894, 2063, 2075, 8077, 602, 2741, 8078, 8079, 8080, 8081, 8082, 3014, 1628, 3400, 8083, # 7990 + 3172, 4574, 4052, 2890, 4575, 2512, 8084, 2544, 2772, 8085, 8086, 8087, 3310, 4576, 2891, 8088, # 8006 + 4577, 8089, 2851, 4578, 4579, 1221, 2967, 4053, 2513, 8090, 8091, 8092, 1867, 1989, 8093, 8094, # 8022 + 8095, 1895, 8096, 8097, 4580, 1896, 4054, 318, 8098, 2094, 4055, 4293, 8099, 8100, 485, 8101, # 8038 + 938, 3862, 553, 2670, 116, 8102, 3863, 3612, 8103, 3498, 2671, 2773, 3401, 3311, 2807, 8104, # 8054 + 3613, 2929, 4056, 1747, 2930, 2968, 8105, 8106, 207, 8107, 8108, 2672, 4581, 2514, 8109, 3015, # 8070 + 890, 3614, 3864, 8110, 1877, 3732, 3402, 8111, 2183, 2353, 3403, 1652, 8112, 8113, 8114, 941, # 8086 + 2294, 208, 3499, 4057, 2019, 330, 4294, 3865, 2892, 2492, 3733, 4295, 8115, 8116, 8117, 8118, # 8102 ) - diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwprober.py index 35669cc..bc11b79 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwprober.py @@ -24,23 +24,25 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations -from .mbcharsetprober import MultiByteCharSetProber -from .codingstatemachine import CodingStateMachine from .chardistribution import EUCTWDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber from .mbcssm import EUCTW_SM_MODEL + class EUCTWProber(MultiByteCharSetProber): def __init__(self): - super(EUCTWProber, self).__init__() + super().__init__() self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL) self.distribution_analyzer = EUCTWDistributionAnalysis() self.reset() @property def charset_name(self): - return "EUC-TW" + return 'EUC-TW' @property def language(self): - return "Taiwan" + return 'Taiwan' diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312freq.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312freq.py index 697837b..c14e427 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312freq.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312freq.py @@ -24,11 +24,9 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### - # GB2312 most frequently used character table # # Char to FreqOrder table , from hz6763 - # 512 --> 0.79 -- 0.79 # 1024 --> 0.92 -- 0.13 # 2048 --> 0.98 -- 0.06 @@ -38,246 +36,246 @@ # Random Distribution Ration = 512 / (3755 - 512) = 0.157 # # Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR +from __future__ import annotations GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9 GB2312_TABLE_SIZE = 3760 GB2312_CHAR_TO_FREQ_ORDER = ( -1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, -2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, -2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, - 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670, -1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820, -1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585, - 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566, -1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575, -2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853, -3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061, - 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155, -1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406, - 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816, -2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606, - 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023, -2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414, -1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513, -3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052, - 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570, -1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575, - 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250, -2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506, -1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26, -3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835, -1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686, -2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054, -1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894, - 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105, -3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403, -3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694, - 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873, -3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940, - 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121, -1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648, -3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992, -2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233, -1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157, - 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807, -1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094, -4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258, - 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478, -3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152, -3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909, - 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272, -1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221, -2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252, -1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301, -1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254, - 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070, -3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461, -3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360, -4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124, - 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535, -3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243, -1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713, -1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071, -4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442, - 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946, - 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257, -3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180, -1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427, - 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781, -1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724, -2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937, - 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943, - 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789, - 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552, -3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246, -4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451, -3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310, - 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860, -2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297, -2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780, -2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745, - 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936, -2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032, - 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657, - 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414, - 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976, -3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436, -2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254, -2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536, -1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238, - 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059, -2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741, - 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447, - 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601, -1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269, -1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894, - 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173, - 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994, -1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956, -2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437, -3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154, -2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240, -2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143, -2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634, -3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472, -1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541, -1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143, -2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312, -1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414, -3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754, -1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424, -1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302, -3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739, - 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004, -2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484, -1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739, -4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535, -1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641, -1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307, -3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573, -1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533, - 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965, - 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99, -1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280, - 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505, -1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012, -1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039, - 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982, -3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530, -4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392, -3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656, -2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220, -2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766, -1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535, -3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728, -2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338, -1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627, -1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885, - 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411, -2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671, -2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162, -3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774, -4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524, -3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346, - 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040, -3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188, -2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280, -1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131, - 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947, - 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970, -3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814, -4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557, -2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997, -1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972, -1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369, - 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376, -1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480, -3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610, - 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128, - 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769, -1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207, - 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392, -1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623, - 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782, -2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650, - 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478, -2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773, -2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007, -1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323, -1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598, -2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961, - 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302, -1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409, -1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683, -2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191, -2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616, -3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302, -1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774, -4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147, - 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731, - 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464, -3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377, -1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315, - 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557, -3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903, -1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060, -4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261, -1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092, -2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810, -1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708, - 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658, -1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871, -3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503, - 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229, -2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112, - 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504, -1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389, -1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27, -1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542, -3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861, -2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845, -3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700, -3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469, -3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582, - 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999, -2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274, - 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020, -2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601, - 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628, -1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31, - 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668, - 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778, -1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169, -3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667, -3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881, -1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276, -1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320, -3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751, -2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432, -2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772, -1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843, -3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116, - 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904, -4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652, -1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664, -2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770, -3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283, -3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626, -1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713, - 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333, - 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062, -2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555, - 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014, -1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510, - 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015, -1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459, -1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390, -1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238, -1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, -1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, - 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, - 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, #last 512 + 1671, 749, 1443, 2364, 3924, 3807, 2330, 3921, 1704, 3463, 2691, 1511, 1515, 572, 3191, 2205, + 2361, 224, 2558, 479, 1711, 963, 3162, 440, 4060, 1905, 2966, 2947, 3580, 2647, 3961, 3842, + 2204, 869, 4207, 970, 2678, 5626, 2944, 2956, 1479, 4048, 514, 3595, 588, 1346, 2820, 3409, + 249, 4088, 1746, 1873, 2047, 1774, 581, 1813, 358, 1174, 3590, 1014, 1561, 4844, 2245, 670, + 1636, 3112, 889, 1286, 953, 556, 2327, 3060, 1290, 3141, 613, 185, 3477, 1367, 850, 3820, + 1715, 2428, 2642, 2303, 2732, 3041, 2562, 2648, 3566, 3946, 1349, 388, 3098, 2091, 1360, 3585, + 152, 1687, 1539, 738, 1559, 59, 1232, 2925, 2267, 1388, 1249, 1741, 1679, 2960, 151, 1566, + 1125, 1352, 4271, 924, 4296, 385, 3166, 4459, 310, 1245, 2850, 70, 3285, 2729, 3534, 3575, + 2398, 3298, 3466, 1960, 2265, 217, 3647, 864, 1909, 2084, 4401, 2773, 1010, 3269, 5152, 853, + 3051, 3121, 1244, 4251, 1895, 364, 1499, 1540, 2313, 1180, 3655, 2268, 562, 715, 2417, 3061, + 544, 336, 3768, 2380, 1752, 4075, 950, 280, 2425, 4382, 183, 2759, 3272, 333, 4297, 2155, + 1688, 2356, 1444, 1039, 4540, 736, 1177, 3349, 2443, 2368, 2144, 2225, 565, 196, 1482, 3406, + 927, 1335, 4147, 692, 878, 1311, 1653, 3911, 3622, 1378, 4200, 1840, 2969, 3149, 2126, 1816, + 2534, 1546, 2393, 2760, 737, 2494, 13, 447, 245, 2747, 38, 2765, 2129, 2589, 1079, 606, + 360, 471, 3755, 2890, 404, 848, 699, 1785, 1236, 370, 2221, 1023, 3746, 2074, 2026, 2023, + 2388, 1581, 2119, 812, 1141, 3091, 2536, 1519, 804, 2053, 406, 1596, 1090, 784, 548, 4414, + 1806, 2264, 2936, 1100, 343, 4114, 5096, 622, 3358, 743, 3668, 1510, 1626, 5020, 3567, 2513, + 3195, 4115, 5627, 2489, 2991, 24, 2065, 2697, 1087, 2719, 48, 1634, 315, 68, 985, 2052, + 198, 2239, 1347, 1107, 1439, 597, 2366, 2172, 871, 3307, 919, 2487, 2790, 1867, 236, 2570, + 1413, 3794, 906, 3365, 3381, 1701, 1982, 1818, 1524, 2924, 1205, 616, 2586, 2072, 2004, 575, + 253, 3099, 32, 1365, 1182, 197, 1714, 2454, 1201, 554, 3388, 3224, 2748, 756, 2587, 250, + 2567, 1507, 1517, 3529, 1922, 2761, 2337, 3416, 1961, 1677, 2452, 2238, 3153, 615, 911, 1506, + 1474, 2495, 1265, 1906, 2749, 3756, 3280, 2161, 898, 2714, 1759, 3450, 2243, 2444, 563, 26, + 3286, 2266, 3769, 3344, 2707, 3677, 611, 1402, 531, 1028, 2871, 4548, 1375, 261, 2948, 835, + 1190, 4134, 353, 840, 2684, 1900, 3082, 1435, 2109, 1207, 1674, 329, 1872, 2781, 4055, 2686, + 2104, 608, 3318, 2423, 2957, 2768, 1108, 3739, 3512, 3271, 3985, 2203, 1771, 3520, 1418, 2054, + 1681, 1153, 225, 1627, 2929, 162, 2050, 2511, 3687, 1954, 124, 1859, 2431, 1684, 3032, 2894, + 585, 4805, 3969, 2869, 2704, 2088, 2032, 2095, 3656, 2635, 4362, 2209, 256, 518, 2042, 2105, + 3777, 3657, 643, 2298, 1148, 1779, 190, 989, 3544, 414, 11, 2135, 2063, 2979, 1471, 403, + 3678, 126, 770, 1563, 671, 2499, 3216, 2877, 600, 1179, 307, 2805, 4937, 1268, 1297, 2694, + 252, 4032, 1448, 1494, 1331, 1394, 127, 2256, 222, 1647, 1035, 1481, 3056, 1915, 1048, 873, + 3651, 210, 33, 1608, 2516, 200, 1520, 415, 102, 0, 3389, 1287, 817, 91, 3299, 2940, + 836, 1814, 549, 2197, 1396, 1669, 2987, 3582, 2297, 2848, 4528, 1070, 687, 20, 1819, 121, + 1552, 1364, 1461, 1968, 2617, 3540, 2824, 2083, 177, 948, 4938, 2291, 110, 4549, 2066, 648, + 3359, 1755, 2110, 2114, 4642, 4845, 1693, 3937, 3308, 1257, 1869, 2123, 208, 1804, 3159, 2992, + 2531, 2549, 3361, 2418, 1350, 2347, 2800, 2568, 1291, 2036, 2680, 72, 842, 1990, 212, 1233, + 1154, 1586, 75, 2027, 3410, 4900, 1823, 1337, 2710, 2676, 728, 2810, 1522, 3026, 4995, 157, + 755, 1050, 4022, 710, 785, 1936, 2194, 2085, 1406, 2777, 2400, 150, 1250, 4049, 1206, 807, + 1910, 534, 529, 3309, 1721, 1660, 274, 39, 2827, 661, 2670, 1578, 925, 3248, 3815, 1094, + 4278, 4901, 4252, 41, 1150, 3747, 2572, 2227, 4501, 3658, 4902, 3813, 3357, 3617, 2884, 2258, + 887, 538, 4187, 3199, 1294, 2439, 3042, 2329, 2343, 2497, 1255, 107, 543, 1527, 521, 3478, + 3568, 194, 5062, 15, 961, 3870, 1241, 1192, 2664, 66, 5215, 3260, 2111, 1295, 1127, 2152, + 3805, 4135, 901, 1164, 1976, 398, 1278, 530, 1460, 748, 904, 1054, 1966, 1426, 53, 2909, + 509, 523, 2279, 1534, 536, 1019, 239, 1685, 460, 2353, 673, 1065, 2401, 3600, 4298, 2272, + 1272, 2363, 284, 1753, 3679, 4064, 1695, 81, 815, 2677, 2757, 2731, 1386, 859, 500, 4221, + 2190, 2566, 757, 1006, 2519, 2068, 1166, 1455, 337, 2654, 3203, 1863, 1682, 1914, 3025, 1252, + 1409, 1366, 847, 714, 2834, 2038, 3209, 964, 2970, 1901, 885, 2553, 1078, 1756, 3049, 301, + 1572, 3326, 688, 2130, 1996, 2429, 1805, 1648, 2930, 3421, 2750, 3652, 3088, 262, 1158, 1254, + 389, 1641, 1812, 526, 1719, 923, 2073, 1073, 1902, 468, 489, 4625, 1140, 857, 2375, 3070, + 3319, 2863, 380, 116, 1328, 2693, 1161, 2244, 273, 1212, 1884, 2769, 3011, 1775, 1142, 461, + 3066, 1200, 2147, 2212, 790, 702, 2695, 4222, 1601, 1058, 434, 2338, 5153, 3640, 67, 2360, + 4099, 2502, 618, 3472, 1329, 416, 1132, 830, 2782, 1807, 2653, 3211, 3510, 1662, 192, 2124, + 296, 3979, 1739, 1611, 3684, 23, 118, 324, 446, 1239, 1225, 293, 2520, 3814, 3795, 2535, + 3116, 17, 1074, 467, 2692, 2201, 387, 2922, 45, 1326, 3055, 1645, 3659, 2817, 958, 243, + 1903, 2320, 1339, 2825, 1784, 3289, 356, 576, 865, 2315, 2381, 3377, 3916, 1088, 3122, 1713, + 1655, 935, 628, 4689, 1034, 1327, 441, 800, 720, 894, 1979, 2183, 1528, 5289, 2702, 1071, + 4046, 3572, 2399, 1571, 3281, 79, 761, 1103, 327, 134, 758, 1899, 1371, 1615, 879, 442, + 215, 2605, 2579, 173, 2048, 2485, 1057, 2975, 3317, 1097, 2253, 3801, 4263, 1403, 1650, 2946, + 814, 4968, 3487, 1548, 2644, 1567, 1285, 2, 295, 2636, 97, 946, 3576, 832, 141, 4257, + 3273, 760, 3821, 3521, 3156, 2607, 949, 1024, 1733, 1516, 1803, 1920, 2125, 2283, 2665, 3180, + 1501, 2064, 3560, 2171, 1592, 803, 3518, 1416, 732, 3897, 4258, 1363, 1362, 2458, 119, 1427, + 602, 1525, 2608, 1605, 1639, 3175, 694, 3064, 10, 465, 76, 2000, 4846, 4208, 444, 3781, + 1619, 3353, 2206, 1273, 3796, 740, 2483, 320, 1723, 2377, 3660, 2619, 1359, 1137, 1762, 1724, + 2345, 2842, 1850, 1862, 912, 821, 1866, 612, 2625, 1735, 2573, 3369, 1093, 844, 89, 937, + 930, 1424, 3564, 2413, 2972, 1004, 3046, 3019, 2011, 711, 3171, 1452, 4178, 428, 801, 1943, + 432, 445, 2811, 206, 4136, 1472, 730, 349, 73, 397, 2802, 2547, 998, 1637, 1167, 789, + 396, 3217, 154, 1218, 716, 1120, 1780, 2819, 4826, 1931, 3334, 3762, 2139, 1215, 2627, 552, + 3664, 3628, 3232, 1405, 2383, 3111, 1356, 2652, 3577, 3320, 3101, 1703, 640, 1045, 1370, 1246, + 4996, 371, 1575, 2436, 1621, 2210, 984, 4033, 1734, 2638, 16, 4529, 663, 2755, 3255, 1451, + 3917, 2257, 1253, 1955, 2234, 1263, 2951, 214, 1229, 617, 485, 359, 1831, 1969, 473, 2310, + 750, 2058, 165, 80, 2864, 2419, 361, 4344, 2416, 2479, 1134, 796, 3726, 1266, 2943, 860, + 2715, 938, 390, 2734, 1313, 1384, 248, 202, 877, 1064, 2854, 522, 3907, 279, 1602, 297, + 2357, 395, 3740, 137, 2075, 944, 4089, 2584, 1267, 3802, 62, 1533, 2285, 178, 176, 780, + 2440, 201, 3707, 590, 478, 1560, 4354, 2117, 1075, 30, 74, 4643, 4004, 1635, 1441, 2745, + 776, 2596, 238, 1077, 1692, 1912, 2844, 605, 499, 1742, 3947, 241, 3053, 980, 1749, 936, + 2640, 4511, 2582, 515, 1543, 2162, 5322, 2892, 2993, 890, 2148, 1924, 665, 1827, 3581, 1032, + 968, 3163, 339, 1044, 1896, 270, 583, 1791, 1720, 4367, 1194, 3488, 3669, 43, 2523, 1657, + 163, 2167, 290, 1209, 1622, 3378, 550, 634, 2508, 2510, 695, 2634, 2384, 2512, 1476, 1414, + 220, 1469, 2341, 2138, 2852, 3183, 2900, 4939, 2865, 3502, 1211, 3680, 854, 3227, 1299, 2976, + 3172, 186, 2998, 1459, 443, 1067, 3251, 1495, 321, 1932, 3054, 909, 753, 1410, 1828, 436, + 2441, 1119, 1587, 3164, 2186, 1258, 227, 231, 1425, 1890, 3200, 3942, 247, 959, 725, 5254, + 2741, 577, 2158, 2079, 929, 120, 174, 838, 2813, 591, 1115, 417, 2024, 40, 3240, 1536, + 1037, 291, 4151, 2354, 632, 1298, 2406, 2500, 3535, 1825, 1846, 3451, 205, 1171, 345, 4238, + 18, 1163, 811, 685, 2208, 1217, 425, 1312, 1508, 1175, 4308, 2552, 1033, 587, 1381, 3059, + 2984, 3482, 340, 1316, 4023, 3972, 792, 3176, 519, 777, 4690, 918, 933, 4130, 2981, 3741, + 90, 3360, 2911, 2200, 5184, 4550, 609, 3079, 2030, 272, 3379, 2736, 363, 3881, 1130, 1447, + 286, 779, 357, 1169, 3350, 3137, 1630, 1220, 2687, 2391, 747, 1277, 3688, 2618, 2682, 2601, + 1156, 3196, 5290, 4034, 3102, 1689, 3596, 3128, 874, 219, 2783, 798, 508, 1843, 2461, 269, + 1658, 1776, 1392, 1913, 2983, 3287, 2866, 2159, 2372, 829, 4076, 46, 4253, 2873, 1889, 1894, + 915, 1834, 1631, 2181, 2318, 298, 664, 2818, 3555, 2735, 954, 3228, 3117, 527, 3511, 2173, + 681, 2712, 3033, 2247, 2346, 3467, 1652, 155, 2164, 3382, 113, 1994, 450, 899, 494, 994, + 1237, 2958, 1875, 2336, 1926, 3727, 545, 1577, 1550, 633, 3473, 204, 1305, 3072, 2410, 1956, + 2471, 707, 2134, 841, 2195, 2196, 2663, 3843, 1026, 4940, 990, 3252, 4997, 368, 1092, 437, + 3212, 3258, 1933, 1829, 675, 2977, 2893, 412, 943, 3723, 4644, 3294, 3283, 2230, 2373, 5154, + 2389, 2241, 2661, 2323, 1404, 2524, 593, 787, 677, 3008, 1275, 2059, 438, 2709, 2609, 2240, + 2269, 2246, 1446, 36, 1568, 1373, 3892, 1574, 2301, 1456, 3962, 693, 2276, 5216, 2035, 1143, + 2720, 1919, 1797, 1811, 2763, 4137, 2597, 1830, 1699, 1488, 1198, 2090, 424, 1694, 312, 3634, + 3390, 4179, 3335, 2252, 1214, 561, 1059, 3243, 2295, 2561, 975, 5155, 2321, 2751, 3772, 472, + 1537, 3282, 3398, 1047, 2077, 2348, 2878, 1323, 3340, 3076, 690, 2906, 51, 369, 170, 3541, + 1060, 2187, 2688, 3670, 2541, 1083, 1683, 928, 3918, 459, 109, 4427, 599, 3744, 4286, 143, + 2101, 2730, 2490, 82, 1588, 3036, 2121, 281, 1860, 477, 4035, 1238, 2812, 3020, 2716, 3312, + 1530, 2188, 2055, 1317, 843, 636, 1808, 1173, 3495, 649, 181, 1002, 147, 3641, 1159, 2414, + 3750, 2289, 2795, 813, 3123, 2610, 1136, 4368, 5, 3391, 4541, 2174, 420, 429, 1728, 754, + 1228, 2115, 2219, 347, 2223, 2733, 735, 1518, 3003, 2355, 3134, 1764, 3948, 3329, 1888, 2424, + 1001, 1234, 1972, 3321, 3363, 1672, 1021, 1450, 1584, 226, 765, 655, 2526, 3404, 3244, 2302, + 3665, 731, 594, 2184, 319, 1576, 621, 658, 2656, 4299, 2099, 3864, 1279, 2071, 2598, 2739, + 795, 3086, 3699, 3908, 1707, 2352, 2402, 1382, 3136, 2475, 1465, 4847, 3496, 3865, 1085, 3004, + 2591, 1084, 213, 2287, 1963, 3565, 2250, 822, 793, 4574, 3187, 1772, 1789, 3050, 595, 1484, + 1959, 2770, 1080, 2650, 456, 422, 2996, 940, 3322, 4328, 4345, 3092, 2742, 965, 2784, 739, + 4124, 952, 1358, 2498, 2949, 2565, 332, 2698, 2378, 660, 2260, 2473, 4194, 3856, 2919, 535, + 1260, 2651, 1208, 1428, 1300, 1949, 1303, 2942, 433, 2455, 2450, 1251, 1946, 614, 1269, 641, + 1306, 1810, 2737, 3078, 2912, 564, 2365, 1419, 1415, 1497, 4460, 2367, 2185, 1379, 3005, 1307, + 3218, 2175, 1897, 3063, 682, 1157, 4040, 4005, 1712, 1160, 1941, 1399, 394, 402, 2952, 1573, + 1151, 2986, 2404, 862, 299, 2033, 1489, 3006, 346, 171, 2886, 3401, 1726, 2932, 168, 2533, + 47, 2507, 1030, 3735, 1145, 3370, 1395, 1318, 1579, 3609, 4560, 2857, 4116, 1457, 2529, 1965, + 504, 1036, 2690, 2988, 2405, 745, 5871, 849, 2397, 2056, 3081, 863, 2359, 3857, 2096, 99, + 1397, 1769, 2300, 4428, 1643, 3455, 1978, 1757, 3718, 1440, 35, 4879, 3742, 1296, 4228, 2280, + 160, 5063, 1599, 2013, 166, 520, 3479, 1646, 3345, 3012, 490, 1937, 1545, 1264, 2182, 2505, + 1096, 1188, 1369, 1436, 2421, 1667, 2792, 2460, 1270, 2122, 727, 3167, 2143, 806, 1706, 1012, + 1800, 3037, 960, 2218, 1882, 805, 139, 2456, 1139, 1521, 851, 1052, 3093, 3089, 342, 2039, + 744, 5097, 1468, 1502, 1585, 2087, 223, 939, 326, 2140, 2577, 892, 2481, 1623, 4077, 982, + 3708, 135, 2131, 87, 2503, 3114, 2326, 1106, 876, 1616, 547, 2997, 2831, 2093, 3441, 4530, + 4314, 9, 3256, 4229, 4148, 659, 1462, 1986, 1710, 2046, 2913, 2231, 4090, 4880, 5255, 3392, + 3274, 1368, 3689, 4645, 1477, 705, 3384, 3635, 1068, 1529, 2941, 1458, 3782, 1509, 100, 1656, + 2548, 718, 2339, 408, 1590, 2780, 3548, 1838, 4117, 3719, 1345, 3530, 717, 3442, 2778, 3220, + 2898, 1892, 4590, 3614, 3371, 2043, 1998, 1224, 3483, 891, 635, 584, 2559, 3355, 733, 1766, + 1729, 1172, 3789, 1891, 2307, 781, 2982, 2271, 1957, 1580, 5773, 2633, 2005, 4195, 3097, 1535, + 3213, 1189, 1934, 5693, 3262, 586, 3118, 1324, 1598, 517, 1564, 2217, 1868, 1893, 4445, 3728, + 2703, 3139, 1526, 1787, 1992, 3882, 2875, 1549, 1199, 1056, 2224, 1904, 2711, 5098, 4287, 338, + 1993, 3129, 3489, 2689, 1809, 2815, 1997, 957, 1855, 3898, 2550, 3275, 3057, 1105, 1319, 627, + 1505, 1911, 1883, 3526, 698, 3629, 3456, 1833, 1431, 746, 77, 1261, 2017, 2296, 1977, 1885, + 125, 1334, 1600, 525, 1798, 1109, 2222, 1470, 1945, 559, 2236, 1186, 3443, 2476, 1929, 1411, + 2411, 3135, 1777, 3372, 2621, 1841, 1613, 3229, 668, 1430, 1839, 2643, 2916, 195, 1989, 2671, + 2358, 1387, 629, 3205, 2293, 5256, 4439, 123, 1310, 888, 1879, 4300, 3021, 3605, 1003, 1162, + 3192, 2910, 2010, 140, 2395, 2859, 55, 1082, 2012, 2901, 662, 419, 2081, 1438, 680, 2774, + 4654, 3912, 1620, 1731, 1625, 5035, 4065, 2328, 512, 1344, 802, 5443, 2163, 2311, 2537, 524, + 3399, 98, 1155, 2103, 1918, 2606, 3925, 2816, 1393, 2465, 1504, 3773, 2177, 3963, 1478, 4346, + 180, 1113, 4655, 3461, 2028, 1698, 833, 2696, 1235, 1322, 1594, 4408, 3623, 3013, 3225, 2040, + 3022, 541, 2881, 607, 3632, 2029, 1665, 1219, 639, 1385, 1686, 1099, 2803, 3231, 1938, 3188, + 2858, 427, 676, 2772, 1168, 2025, 454, 3253, 2486, 3556, 230, 1950, 580, 791, 1991, 1280, + 1086, 1974, 2034, 630, 257, 3338, 2788, 4903, 1017, 86, 4790, 966, 2789, 1995, 1696, 1131, + 259, 3095, 4188, 1308, 179, 1463, 5257, 289, 4107, 1248, 42, 3413, 1725, 2288, 896, 1947, + 774, 4474, 4254, 604, 3430, 4264, 392, 2514, 2588, 452, 237, 1408, 3018, 988, 4531, 1970, + 3034, 3310, 540, 2370, 1562, 1288, 2990, 502, 4765, 1147, 4, 1853, 2708, 207, 294, 2814, + 4078, 2902, 2509, 684, 34, 3105, 3532, 2551, 644, 709, 2801, 2344, 573, 1727, 3573, 3557, + 2021, 1081, 3100, 4315, 2100, 3681, 199, 2263, 1837, 2385, 146, 3484, 1195, 2776, 3949, 997, + 1939, 3973, 1008, 1091, 1202, 1962, 1847, 1149, 4209, 5444, 1076, 493, 117, 5400, 2521, 972, + 1490, 2934, 1796, 4542, 2374, 1512, 2933, 2657, 413, 2888, 1135, 2762, 2314, 2156, 1355, 2369, + 766, 2007, 2527, 2170, 3124, 2491, 2593, 2632, 4757, 2437, 234, 3125, 3591, 1898, 1750, 1376, + 1942, 3468, 3138, 570, 2127, 2145, 3276, 4131, 962, 132, 1445, 4196, 19, 941, 3624, 3480, + 3366, 1973, 1374, 4461, 3431, 2629, 283, 2415, 2275, 808, 2887, 3620, 2112, 2563, 1353, 3610, + 955, 1089, 3103, 1053, 96, 88, 4097, 823, 3808, 1583, 399, 292, 4091, 3313, 421, 1128, + 642, 4006, 903, 2539, 1877, 2082, 596, 29, 4066, 1790, 722, 2157, 130, 995, 1569, 769, + 1485, 464, 513, 2213, 288, 1923, 1101, 2453, 4316, 133, 486, 2445, 50, 625, 487, 2207, + 57, 423, 481, 2962, 159, 3729, 1558, 491, 303, 482, 501, 240, 2837, 112, 3648, 2392, + 1783, 362, 8, 3433, 3422, 610, 2793, 3277, 1390, 1284, 1654, 21, 3823, 734, 367, 623, + 193, 287, 374, 1009, 1483, 816, 476, 313, 2255, 2340, 1262, 2150, 2899, 1146, 2581, 782, + 2116, 1659, 2018, 1880, 255, 3586, 3314, 1110, 2867, 2137, 2564, 986, 2767, 5185, 2006, 650, + 158, 926, 762, 881, 3157, 2717, 2362, 3587, 306, 3690, 3245, 1542, 3077, 2427, 1691, 2478, + 2118, 2985, 3490, 2438, 539, 2305, 983, 129, 1754, 355, 4201, 2386, 827, 2923, 104, 1773, + 2838, 2771, 411, 2905, 3919, 376, 767, 122, 1114, 828, 2422, 1817, 3506, 266, 3460, 1007, + 1609, 4998, 945, 2612, 4429, 2274, 726, 1247, 1964, 2914, 2199, 2070, 4002, 4108, 657, 3323, + 1422, 579, 455, 2764, 4737, 1222, 2895, 1670, 824, 1223, 1487, 2525, 558, 861, 3080, 598, + 2659, 2515, 1967, 752, 2583, 2376, 2214, 4180, 977, 704, 2464, 4999, 2622, 4109, 1210, 2961, + 819, 1541, 142, 2284, 44, 418, 457, 1126, 3730, 4347, 4626, 1644, 1876, 3671, 1864, 302, + 1063, 5694, 624, 723, 1984, 3745, 1314, 1676, 2488, 1610, 1449, 3558, 3569, 2166, 2098, 409, + 1011, 2325, 3704, 2306, 818, 1732, 1383, 1824, 1844, 3757, 999, 2705, 3497, 1216, 1423, 2683, + 2426, 2954, 2501, 2726, 2229, 1475, 2554, 5064, 1971, 1794, 1666, 2014, 1343, 783, 724, 191, + 2434, 1354, 2220, 5065, 1763, 2752, 2472, 4152, 131, 175, 2885, 3434, 92, 1466, 4920, 2616, + 3871, 3872, 3866, 128, 1551, 1632, 669, 1854, 3682, 4691, 4125, 1230, 188, 2973, 3290, 1302, + 1213, 560, 3266, 917, 763, 3909, 3249, 1760, 868, 1958, 764, 1782, 2097, 145, 2277, 3774, + 4462, 64, 1491, 3062, 971, 2132, 3606, 2442, 221, 1226, 1617, 218, 323, 1185, 3207, 3147, + 571, 619, 1473, 1005, 1744, 2281, 449, 1887, 2396, 3685, 275, 375, 3816, 1743, 3844, 3731, + 845, 1983, 2350, 4210, 1377, 773, 967, 3499, 3052, 3743, 2725, 4007, 1697, 1022, 3943, 1464, + 3264, 2855, 2722, 1952, 1029, 2839, 2467, 84, 4383, 2215, 820, 1391, 2015, 2448, 3672, 377, + 1948, 2168, 797, 2545, 3536, 2578, 2645, 94, 2874, 1678, 405, 1259, 3071, 771, 546, 1315, + 470, 1243, 3083, 895, 2468, 981, 969, 2037, 846, 4181, 653, 1276, 2928, 14, 2594, 557, + 3007, 2474, 156, 902, 1338, 1740, 2574, 537, 2518, 973, 2282, 2216, 2433, 1928, 138, 2903, + 1293, 2631, 1612, 646, 3457, 839, 2935, 111, 496, 2191, 2847, 589, 3186, 149, 3994, 2060, + 4031, 2641, 4067, 3145, 1870, 37, 3597, 2136, 1025, 2051, 3009, 3383, 3549, 1121, 1016, 3261, + 1301, 251, 2446, 2599, 2153, 872, 3246, 637, 334, 3705, 831, 884, 921, 3065, 3140, 4092, + 2198, 1944, 246, 2964, 108, 2045, 1152, 1921, 2308, 1031, 203, 3173, 4170, 1907, 3890, 810, + 1401, 2003, 1690, 506, 647, 1242, 2828, 1761, 1649, 3208, 2249, 1589, 3709, 2931, 5156, 1708, + 498, 666, 2613, 834, 3817, 1231, 184, 2851, 1124, 883, 3197, 2261, 3710, 1765, 1553, 2658, + 1178, 2639, 2351, 93, 1193, 942, 2538, 2141, 4402, 235, 1821, 870, 1591, 2192, 1709, 1871, + 3341, 1618, 4126, 2595, 2334, 603, 651, 69, 701, 268, 2662, 3411, 2555, 1380, 1606, 503, + 448, 254, 2371, 2646, 574, 1187, 2309, 1770, 322, 2235, 1292, 1801, 305, 566, 1133, 229, + 2067, 2057, 706, 167, 483, 2002, 2672, 3295, 1820, 3561, 3067, 316, 378, 2746, 3452, 1112, + 136, 1981, 507, 1651, 2917, 1117, 285, 4591, 182, 2580, 3522, 1304, 335, 3303, 1835, 2504, + 1795, 1792, 2248, 674, 1018, 2106, 2449, 1857, 2292, 2845, 976, 3047, 1781, 2600, 2727, 1389, + 1281, 52, 3152, 153, 265, 3950, 672, 3485, 3951, 4463, 430, 1183, 365, 278, 2169, 27, + 1407, 1336, 2304, 209, 1340, 1730, 2202, 1852, 2403, 2883, 979, 1737, 1062, 631, 2829, 2542, + 3876, 2592, 825, 2086, 2226, 3048, 3625, 352, 1417, 3724, 542, 991, 431, 1351, 3938, 1861, + 2294, 826, 1361, 2927, 3142, 3503, 1738, 463, 2462, 2723, 582, 1916, 1595, 2808, 400, 3845, + 3891, 2868, 3621, 2254, 58, 2492, 1123, 910, 2160, 2614, 1372, 1603, 1196, 1072, 3385, 1700, + 3267, 1980, 696, 480, 2430, 920, 799, 1570, 2920, 1951, 2041, 4047, 2540, 1321, 4223, 2469, + 3562, 2228, 1271, 2602, 401, 2833, 3351, 2575, 5157, 907, 2312, 1256, 410, 263, 3507, 1582, + 996, 678, 1849, 2316, 1480, 908, 3545, 2237, 703, 2322, 667, 1826, 2849, 1531, 2604, 2999, + 2407, 3146, 2151, 2630, 1786, 3711, 469, 3542, 497, 3899, 2409, 858, 837, 4446, 3393, 1274, + 786, 620, 1845, 2001, 3311, 484, 308, 3367, 1204, 1815, 3691, 2332, 1532, 2557, 1842, 2020, + 2724, 1927, 2333, 4440, 567, 22, 1673, 2728, 4475, 1987, 1858, 1144, 1597, 101, 1832, 3601, + 12, 974, 3783, 4391, 951, 1412, 1, 3720, 453, 4608, 4041, 528, 1041, 1027, 3230, 2628, + 1129, 875, 1051, 3291, 1203, 2262, 1069, 2860, 2799, 2149, 2615, 3278, 144, 1758, 3040, 31, + 475, 1680, 366, 2685, 3184, 311, 1642, 4008, 2466, 5036, 1593, 1493, 2809, 216, 1420, 1668, + 233, 304, 2128, 3284, 232, 1429, 1768, 1040, 2008, 3407, 2740, 2967, 2543, 242, 2133, 778, + 1565, 2022, 2620, 505, 2189, 2756, 1098, 2273, 372, 1614, 708, 553, 2846, 2094, 2278, 169, + 3626, 2835, 4161, 228, 2674, 3165, 809, 1454, 1309, 466, 1705, 1095, 900, 3423, 880, 2667, + 3751, 5258, 2317, 3109, 2571, 4317, 2766, 1503, 1342, 866, 4447, 1118, 63, 2076, 314, 1881, + 1348, 1061, 172, 978, 3515, 1747, 532, 511, 3970, 6, 601, 905, 2699, 3300, 1751, 276, + 1467, 3725, 2668, 65, 4239, 2544, 2779, 2556, 1604, 578, 2451, 1802, 992, 2331, 2624, 1320, + 3446, 713, 1513, 1013, 103, 2786, 2447, 1661, 886, 1702, 916, 654, 3574, 2031, 1556, 751, + 2178, 2821, 2179, 1498, 1538, 2176, 271, 914, 2251, 2080, 1325, 638, 1953, 2937, 3877, 2432, + 2754, 95, 3265, 1716, 260, 1227, 4083, 775, 106, 1357, 3254, 426, 1607, 555, 2480, 772, + 1985, 244, 2546, 474, 495, 1046, 2611, 1851, 2061, 71, 2089, 1675, 2590, 742, 3758, 2843, + 3222, 1433, 267, 2180, 2576, 2826, 2233, 2092, 3913, 2435, 956, 1745, 3075, 856, 2113, 1116, + 451, 3, 1988, 2896, 1398, 993, 2463, 1878, 2049, 1341, 2718, 2721, 2870, 2108, 712, 2904, + 4363, 2753, 2324, 277, 2872, 2349, 2649, 384, 987, 435, 691, 3000, 922, 164, 3939, 652, + 1500, 1184, 4153, 2482, 3373, 2165, 4848, 2335, 3775, 3508, 3154, 2806, 2830, 1554, 2102, 1664, + 2530, 1434, 2408, 893, 1547, 2623, 3447, 2832, 2242, 2532, 3169, 2856, 3223, 2078, 49, 3770, + 3469, 462, 318, 656, 2259, 3250, 3069, 679, 1629, 2758, 344, 1138, 1104, 3120, 1836, 1283, + 3115, 2154, 1437, 4448, 934, 759, 1999, 794, 2862, 1038, 533, 2560, 1722, 2342, 855, 2626, + 1197, 1663, 4476, 3127, 85, 4240, 2528, 25, 1111, 1181, 3673, 407, 3470, 4561, 2679, 2713, + 768, 1925, 2841, 3986, 1544, 1165, 932, 373, 1240, 2146, 1930, 2673, 721, 4766, 354, 4333, + 391, 2963, 187, 61, 3364, 1442, 1102, 330, 1940, 1767, 341, 3809, 4118, 393, 2496, 2062, + 2211, 105, 331, 300, 439, 913, 1332, 626, 379, 3304, 1557, 328, 689, 3952, 309, 1555, + 931, 317, 2517, 3027, 325, 569, 686, 2107, 3084, 60, 1042, 1333, 2794, 264, 3177, 4014, + 1628, 258, 3712, 7, 4464, 1176, 1043, 1778, 683, 114, 1975, 78, 1492, 383, 1886, 510, + 386, 645, 5291, 2891, 2069, 3305, 4138, 3867, 2939, 2603, 2493, 1935, 1066, 1848, 3588, 1015, + 1282, 1289, 4609, 697, 1453, 3044, 2666, 3611, 1856, 2412, 54, 719, 1330, 568, 3778, 2459, + 1748, 788, 492, 551, 1191, 1000, 488, 3394, 3763, 282, 1799, 348, 2016, 1523, 3155, 2390, + 1049, 382, 2019, 1788, 1170, 729, 2968, 3523, 897, 3926, 2785, 2938, 3292, 350, 2319, 3238, + 1718, 1717, 2655, 3453, 3143, 4465, 161, 2889, 2980, 2009, 1421, 56, 1908, 1640, 2387, 2232, + 1917, 1874, 2477, 4921, 148, 83, 3438, 592, 4245, 2882, 1822, 1055, 741, 115, 1496, 1624, + 381, 1638, 4592, 1020, 516, 3214, 458, 947, 4575, 1432, 211, 1514, 2926, 1865, 2142, 189, + 852, 1221, 1400, 1486, 882, 2299, 4036, 351, 28, 1122, 700, 6479, 6480, 6481, 6482, 6483, # last 512 ) - diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312prober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312prober.py index 8446d2d..a234f38 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312prober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312prober.py @@ -24,23 +24,25 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations -from .mbcharsetprober import MultiByteCharSetProber -from .codingstatemachine import CodingStateMachine from .chardistribution import GB2312DistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber from .mbcssm import GB2312_SM_MODEL + class GB2312Prober(MultiByteCharSetProber): def __init__(self): - super(GB2312Prober, self).__init__() + super().__init__() self.coding_sm = CodingStateMachine(GB2312_SM_MODEL) self.distribution_analyzer = GB2312DistributionAnalysis() self.reset() @property def charset_name(self): - return "GB2312" + return 'GB2312' @property def language(self): - return "Chinese" + return 'Chinese' diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/hebrewprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/hebrewprober.py index b0e1bf4..54c9a8b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/hebrewprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/hebrewprober.py @@ -24,6 +24,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from .charsetprober import CharSetProber from .enums import ProbingState @@ -125,6 +126,7 @@ from .enums import ProbingState # model probers scores. The answer is returned in the form of the name of the # charset identified, either "windows-1255" or "ISO-8859-8". + class HebrewProber(CharSetProber): # windows-1255 / ISO-8859-8 code points of interest FINAL_KAF = 0xea @@ -148,11 +150,11 @@ class HebrewProber(CharSetProber): # distance. MIN_MODEL_DISTANCE = 0.01 - VISUAL_HEBREW_NAME = "ISO-8859-8" - LOGICAL_HEBREW_NAME = "windows-1255" + VISUAL_HEBREW_NAME = 'ISO-8859-8' + LOGICAL_HEBREW_NAME = 'windows-1255' def __init__(self): - super(HebrewProber, self).__init__() + super().__init__() self._final_char_logical_score = None self._final_char_visual_score = None self._prev = None @@ -176,8 +178,10 @@ class HebrewProber(CharSetProber): self._visual_prober = visualProber def is_final(self, c): - return c in [self.FINAL_KAF, self.FINAL_MEM, self.FINAL_NUN, - self.FINAL_PE, self.FINAL_TSADI] + return c in [ + self.FINAL_KAF, self.FINAL_MEM, self.FINAL_NUN, + self.FINAL_PE, self.FINAL_TSADI, + ] def is_non_final(self, c): # The normal Tsadi is not a good Non-Final letter due to words like @@ -190,8 +194,10 @@ class HebrewProber(CharSetProber): # for example legally end with a Non-Final Pe or Kaf. However, the # benefit of these letters as Non-Final letters outweighs the damage # since these words are quite rare. - return c in [self.NORMAL_KAF, self.NORMAL_MEM, - self.NORMAL_NUN, self.NORMAL_PE] + return c in [ + self.NORMAL_KAF, self.NORMAL_MEM, + self.NORMAL_NUN, self.NORMAL_PE, + ] def feed(self, byte_str): # Final letter analysis for logical-visual decision. @@ -263,8 +269,10 @@ class HebrewProber(CharSetProber): return self.VISUAL_HEBREW_NAME # It's not dominant enough, try to rely on the model scores instead. - modelsub = (self._logical_prober.get_confidence() - - self._visual_prober.get_confidence()) + modelsub = ( + self._logical_prober.get_confidence() - + self._visual_prober.get_confidence() + ) if modelsub > self.MIN_MODEL_DISTANCE: return self.LOGICAL_HEBREW_NAME if modelsub < -self.MIN_MODEL_DISTANCE: diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/jisfreq.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/jisfreq.py index 83fc082..171b574 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/jisfreq.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/jisfreq.py @@ -24,12 +24,10 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### - # Sampling from about 20M text materials include literature and computer technology # # Japanese frequency table, applied to both S-JIS and EUC-JP # They are sorted in order. - # 128 --> 0.77094 # 256 --> 0.85710 # 512 --> 0.92635 @@ -40,6 +38,7 @@ # Random Distribution Ration = 512 / (2965+62+83+86-512) = 0.191 # # Typical Distribution Ratio, 25% of IDR +from __future__ import annotations JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0 @@ -47,279 +46,277 @@ JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0 JIS_TABLE_SIZE = 4368 JIS_CHAR_TO_FREQ_ORDER = ( - 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16 -3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32 -1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48 -2042,1061,1062, 48, 49, 44, 45, 433, 434,1040,1041, 996, 787,2997,1255,4305, # 64 -2108,4609,1684,1648,5073,5074,5075,5076,5077,5078,3687,5079,4610,5080,3927,3928, # 80 -5081,3296,3432, 290,2285,1471,2187,5082,2580,2825,1303,2140,1739,1445,2691,3375, # 96 -1691,3297,4306,4307,4611, 452,3376,1182,2713,3688,3069,4308,5083,5084,5085,5086, # 112 -5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102, # 128 -5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,4097,5113,5114,5115,5116,5117, # 144 -5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133, # 160 -5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149, # 176 -5150,5151,5152,4612,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164, # 192 -5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,1472, 598, 618, 820,1205, # 208 -1309,1412,1858,1307,1692,5176,5177,5178,5179,5180,5181,5182,1142,1452,1234,1172, # 224 -1875,2043,2149,1793,1382,2973, 925,2404,1067,1241, 960,1377,2935,1491, 919,1217, # 240 -1865,2030,1406,1499,2749,4098,5183,5184,5185,5186,5187,5188,2561,4099,3117,1804, # 256 -2049,3689,4309,3513,1663,5189,3166,3118,3298,1587,1561,3433,5190,3119,1625,2998, # 272 -3299,4613,1766,3690,2786,4614,5191,5192,5193,5194,2161, 26,3377, 2,3929, 20, # 288 -3691, 47,4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304 - 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320 - 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151,1069, 181, 275, # 336 -1591, 83, 132,1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55,1079, 312, # 352 - 109, 38, 23, 25, 10, 19, 79,5195, 61, 382,1124, 8, 30,5196,5197,5198, # 368 -5199,5200,5201,5202,5203,5204,5205,5206, 89, 62, 74, 34,2416, 112, 139, 196, # 384 - 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400 - 32, 364, 121,1508, 169,1547, 68, 235, 145,2999, 41, 360,3027, 70, 63, 31, # 416 - 43, 259, 262,1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432 - 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104,1140, # 448 - 54, 51, 36, 87, 67,3070, 185,2618,2936,2020, 28,1066,2390,2059,5207,5208, # 464 -5209,5210,5211,5212,5213,5214,5215,5216,4615,5217,5218,5219,5220,5221,5222,5223, # 480 -5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,3514,5237,5238, # 496 -5239,5240,5241,5242,5243,5244,2297,2031,4616,4310,3692,5245,3071,5246,3598,5247, # 512 -4617,3231,3515,5248,4101,4311,4618,3808,4312,4102,5249,4103,4104,3599,5250,5251, # 528 -5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267, # 544 -5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283, # 560 -5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299, # 576 -5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315, # 592 -5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331, # 608 -5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347, # 624 -5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, # 640 -5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379, # 656 -5380,5381, 363, 642,2787,2878,2788,2789,2316,3232,2317,3434,2011, 165,1942,3930, # 672 -3931,3932,3933,5382,4619,5383,4620,5384,5385,5386,5387,5388,5389,5390,5391,5392, # 688 -5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, # 704 -5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424, # 720 -5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440, # 736 -5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456, # 752 -5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472, # 768 -5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488, # 784 -5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504, # 800 -5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520, # 816 -5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536, # 832 -5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552, # 848 -5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568, # 864 -5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584, # 880 -5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600, # 896 -5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616, # 912 -5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632, # 928 -5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, # 944 -5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664, # 960 -5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680, # 976 -5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696, # 992 -5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712, # 1008 -5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728, # 1024 -5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744, # 1040 -5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760, # 1056 -5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776, # 1072 -5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792, # 1088 -5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808, # 1104 -5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824, # 1120 -5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840, # 1136 -5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856, # 1152 -5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872, # 1168 -5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, # 1184 -5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904, # 1200 -5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, # 1216 -5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936, # 1232 -5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952, # 1248 -5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968, # 1264 -5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984, # 1280 -5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000, # 1296 -6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016, # 1312 -6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032, # 1328 -6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048, # 1344 -6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064, # 1360 -6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080, # 1376 -6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096, # 1392 -6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112, # 1408 -6113,6114,2044,2060,4621, 997,1235, 473,1186,4622, 920,3378,6115,6116, 379,1108, # 1424 -4313,2657,2735,3934,6117,3809, 636,3233, 573,1026,3693,3435,2974,3300,2298,4105, # 1440 - 854,2937,2463, 393,2581,2417, 539, 752,1280,2750,2480, 140,1161, 440, 708,1569, # 1456 - 665,2497,1746,1291,1523,3000, 164,1603, 847,1331, 537,1997, 486, 508,1693,2418, # 1472 -1970,2227, 878,1220, 299,1030, 969, 652,2751, 624,1137,3301,2619, 65,3302,2045, # 1488 -1761,1859,3120,1930,3694,3516, 663,1767, 852, 835,3695, 269, 767,2826,2339,1305, # 1504 - 896,1150, 770,1616,6118, 506,1502,2075,1012,2519, 775,2520,2975,2340,2938,4314, # 1520 -3028,2086,1224,1943,2286,6119,3072,4315,2240,1273,1987,3935,1557, 175, 597, 985, # 1536 -3517,2419,2521,1416,3029, 585, 938,1931,1007,1052,1932,1685,6120,3379,4316,4623, # 1552 - 804, 599,3121,1333,2128,2539,1159,1554,2032,3810, 687,2033,2904, 952, 675,1467, # 1568 -3436,6121,2241,1096,1786,2440,1543,1924, 980,1813,2228, 781,2692,1879, 728,1918, # 1584 -3696,4624, 548,1950,4625,1809,1088,1356,3303,2522,1944, 502, 972, 373, 513,2827, # 1600 - 586,2377,2391,1003,1976,1631,6122,2464,1084, 648,1776,4626,2141, 324, 962,2012, # 1616 -2177,2076,1384, 742,2178,1448,1173,1810, 222, 102, 301, 445, 125,2420, 662,2498, # 1632 - 277, 200,1476,1165,1068, 224,2562,1378,1446, 450,1880, 659, 791, 582,4627,2939, # 1648 -3936,1516,1274, 555,2099,3697,1020,1389,1526,3380,1762,1723,1787,2229, 412,2114, # 1664 -1900,2392,3518, 512,2597, 427,1925,2341,3122,1653,1686,2465,2499, 697, 330, 273, # 1680 - 380,2162, 951, 832, 780, 991,1301,3073, 965,2270,3519, 668,2523,2636,1286, 535, # 1696 -1407, 518, 671, 957,2658,2378, 267, 611,2197,3030,6123, 248,2299, 967,1799,2356, # 1712 - 850,1418,3437,1876,1256,1480,2828,1718,6124,6125,1755,1664,2405,6126,4628,2879, # 1728 -2829, 499,2179, 676,4629, 557,2329,2214,2090, 325,3234, 464, 811,3001, 992,2342, # 1744 -2481,1232,1469, 303,2242, 466,1070,2163, 603,1777,2091,4630,2752,4631,2714, 322, # 1760 -2659,1964,1768, 481,2188,1463,2330,2857,3600,2092,3031,2421,4632,2318,2070,1849, # 1776 -2598,4633,1302,2254,1668,1701,2422,3811,2905,3032,3123,2046,4106,1763,1694,4634, # 1792 -1604, 943,1724,1454, 917, 868,2215,1169,2940, 552,1145,1800,1228,1823,1955, 316, # 1808 -1080,2510, 361,1807,2830,4107,2660,3381,1346,1423,1134,4108,6127, 541,1263,1229, # 1824 -1148,2540, 545, 465,1833,2880,3438,1901,3074,2482, 816,3937, 713,1788,2500, 122, # 1840 -1575, 195,1451,2501,1111,6128, 859, 374,1225,2243,2483,4317, 390,1033,3439,3075, # 1856 -2524,1687, 266, 793,1440,2599, 946, 779, 802, 507, 897,1081, 528,2189,1292, 711, # 1872 -1866,1725,1167,1640, 753, 398,2661,1053, 246, 348,4318, 137,1024,3440,1600,2077, # 1888 -2129, 825,4319, 698, 238, 521, 187,2300,1157,2423,1641,1605,1464,1610,1097,2541, # 1904 -1260,1436, 759,2255,1814,2150, 705,3235, 409,2563,3304, 561,3033,2005,2564, 726, # 1920 -1956,2343,3698,4109, 949,3812,3813,3520,1669, 653,1379,2525, 881,2198, 632,2256, # 1936 -1027, 778,1074, 733,1957, 514,1481,2466, 554,2180, 702,3938,1606,1017,1398,6129, # 1952 -1380,3521, 921, 993,1313, 594, 449,1489,1617,1166, 768,1426,1360, 495,1794,3601, # 1968 -1177,3602,1170,4320,2344, 476, 425,3167,4635,3168,1424, 401,2662,1171,3382,1998, # 1984 -1089,4110, 477,3169, 474,6130,1909, 596,2831,1842, 494, 693,1051,1028,1207,3076, # 2000 - 606,2115, 727,2790,1473,1115, 743,3522, 630, 805,1532,4321,2021, 366,1057, 838, # 2016 - 684,1114,2142,4322,2050,1492,1892,1808,2271,3814,2424,1971,1447,1373,3305,1090, # 2032 -1536,3939,3523,3306,1455,2199, 336, 369,2331,1035, 584,2393, 902, 718,2600,6131, # 2048 -2753, 463,2151,1149,1611,2467, 715,1308,3124,1268, 343,1413,3236,1517,1347,2663, # 2064 -2093,3940,2022,1131,1553,2100,2941,1427,3441,2942,1323,2484,6132,1980, 872,2368, # 2080 -2441,2943, 320,2369,2116,1082, 679,1933,3941,2791,3815, 625,1143,2023, 422,2200, # 2096 -3816,6133, 730,1695, 356,2257,1626,2301,2858,2637,1627,1778, 937, 883,2906,2693, # 2112 -3002,1769,1086, 400,1063,1325,3307,2792,4111,3077, 456,2345,1046, 747,6134,1524, # 2128 - 884,1094,3383,1474,2164,1059, 974,1688,2181,2258,1047, 345,1665,1187, 358, 875, # 2144 -3170, 305, 660,3524,2190,1334,1135,3171,1540,1649,2542,1527, 927, 968,2793, 885, # 2160 -1972,1850, 482, 500,2638,1218,1109,1085,2543,1654,2034, 876, 78,2287,1482,1277, # 2176 - 861,1675,1083,1779, 724,2754, 454, 397,1132,1612,2332, 893, 672,1237, 257,2259, # 2192 -2370, 135,3384, 337,2244, 547, 352, 340, 709,2485,1400, 788,1138,2511, 540, 772, # 2208 -1682,2260,2272,2544,2013,1843,1902,4636,1999,1562,2288,4637,2201,1403,1533, 407, # 2224 - 576,3308,1254,2071, 978,3385, 170, 136,1201,3125,2664,3172,2394, 213, 912, 873, # 2240 -3603,1713,2202, 699,3604,3699, 813,3442, 493, 531,1054, 468,2907,1483, 304, 281, # 2256 -4112,1726,1252,2094, 339,2319,2130,2639, 756,1563,2944, 748, 571,2976,1588,2425, # 2272 -2715,1851,1460,2426,1528,1392,1973,3237, 288,3309, 685,3386, 296, 892,2716,2216, # 2288 -1570,2245, 722,1747,2217, 905,3238,1103,6135,1893,1441,1965, 251,1805,2371,3700, # 2304 -2601,1919,1078, 75,2182,1509,1592,1270,2640,4638,2152,6136,3310,3817, 524, 706, # 2320 -1075, 292,3818,1756,2602, 317, 98,3173,3605,3525,1844,2218,3819,2502, 814, 567, # 2336 - 385,2908,1534,6137, 534,1642,3239, 797,6138,1670,1529, 953,4323, 188,1071, 538, # 2352 - 178, 729,3240,2109,1226,1374,2000,2357,2977, 731,2468,1116,2014,2051,6139,1261, # 2368 -1593, 803,2859,2736,3443, 556, 682, 823,1541,6140,1369,2289,1706,2794, 845, 462, # 2384 -2603,2665,1361, 387, 162,2358,1740, 739,1770,1720,1304,1401,3241,1049, 627,1571, # 2400 -2427,3526,1877,3942,1852,1500, 431,1910,1503, 677, 297,2795, 286,1433,1038,1198, # 2416 -2290,1133,1596,4113,4639,2469,1510,1484,3943,6141,2442, 108, 712,4640,2372, 866, # 2432 -3701,2755,3242,1348, 834,1945,1408,3527,2395,3243,1811, 824, 994,1179,2110,1548, # 2448 -1453, 790,3003, 690,4324,4325,2832,2909,3820,1860,3821, 225,1748, 310, 346,1780, # 2464 -2470, 821,1993,2717,2796, 828, 877,3528,2860,2471,1702,2165,2910,2486,1789, 453, # 2480 - 359,2291,1676, 73,1164,1461,1127,3311, 421, 604, 314,1037, 589, 116,2487, 737, # 2496 - 837,1180, 111, 244, 735,6142,2261,1861,1362, 986, 523, 418, 581,2666,3822, 103, # 2512 - 855, 503,1414,1867,2488,1091, 657,1597, 979, 605,1316,4641,1021,2443,2078,2001, # 2528 -1209, 96, 587,2166,1032, 260,1072,2153, 173, 94, 226,3244, 819,2006,4642,4114, # 2544 -2203, 231,1744, 782, 97,2667, 786,3387, 887, 391, 442,2219,4326,1425,6143,2694, # 2560 - 633,1544,1202, 483,2015, 592,2052,1958,2472,1655, 419, 129,4327,3444,3312,1714, # 2576 -1257,3078,4328,1518,1098, 865,1310,1019,1885,1512,1734, 469,2444, 148, 773, 436, # 2592 -1815,1868,1128,1055,4329,1245,2756,3445,2154,1934,1039,4643, 579,1238, 932,2320, # 2608 - 353, 205, 801, 115,2428, 944,2321,1881, 399,2565,1211, 678, 766,3944, 335,2101, # 2624 -1459,1781,1402,3945,2737,2131,1010, 844, 981,1326,1013, 550,1816,1545,2620,1335, # 2640 -1008, 371,2881, 936,1419,1613,3529,1456,1395,2273,1834,2604,1317,2738,2503, 416, # 2656 -1643,4330, 806,1126, 229, 591,3946,1314,1981,1576,1837,1666, 347,1790, 977,3313, # 2672 - 764,2861,1853, 688,2429,1920,1462, 77, 595, 415,2002,3034, 798,1192,4115,6144, # 2688 -2978,4331,3035,2695,2582,2072,2566, 430,2430,1727, 842,1396,3947,3702, 613, 377, # 2704 - 278, 236,1417,3388,3314,3174, 757,1869, 107,3530,6145,1194, 623,2262, 207,1253, # 2720 -2167,3446,3948, 492,1117,1935, 536,1838,2757,1246,4332, 696,2095,2406,1393,1572, # 2736 -3175,1782, 583, 190, 253,1390,2230, 830,3126,3389, 934,3245,1703,1749,2979,1870, # 2752 -2545,1656,2204, 869,2346,4116,3176,1817, 496,1764,4644, 942,1504, 404,1903,1122, # 2768 -1580,3606,2945,1022, 515, 372,1735, 955,2431,3036,6146,2797,1110,2302,2798, 617, # 2784 -6147, 441, 762,1771,3447,3607,3608,1904, 840,3037, 86, 939,1385, 572,1370,2445, # 2800 -1336, 114,3703, 898, 294, 203,3315, 703,1583,2274, 429, 961,4333,1854,1951,3390, # 2816 -2373,3704,4334,1318,1381, 966,1911,2322,1006,1155, 309, 989, 458,2718,1795,1372, # 2832 -1203, 252,1689,1363,3177, 517,1936, 168,1490, 562, 193,3823,1042,4117,1835, 551, # 2848 - 470,4645, 395, 489,3448,1871,1465,2583,2641, 417,1493, 279,1295, 511,1236,1119, # 2864 - 72,1231,1982,1812,3004, 871,1564, 984,3449,1667,2696,2096,4646,2347,2833,1673, # 2880 -3609, 695,3246,2668, 807,1183,4647, 890, 388,2333,1801,1457,2911,1765,1477,1031, # 2896 -3316,3317,1278,3391,2799,2292,2526, 163,3450,4335,2669,1404,1802,6148,2323,2407, # 2912 -1584,1728,1494,1824,1269, 298, 909,3318,1034,1632, 375, 776,1683,2061, 291, 210, # 2928 -1123, 809,1249,1002,2642,3038, 206,1011,2132, 144, 975, 882,1565, 342, 667, 754, # 2944 -1442,2143,1299,2303,2062, 447, 626,2205,1221,2739,2912,1144,1214,2206,2584, 760, # 2960 -1715, 614, 950,1281,2670,2621, 810, 577,1287,2546,4648, 242,2168, 250,2643, 691, # 2976 - 123,2644, 647, 313,1029, 689,1357,2946,1650, 216, 771,1339,1306, 808,2063, 549, # 2992 - 913,1371,2913,2914,6149,1466,1092,1174,1196,1311,2605,2396,1783,1796,3079, 406, # 3008 -2671,2117,3949,4649, 487,1825,2220,6150,2915, 448,2348,1073,6151,2397,1707, 130, # 3024 - 900,1598, 329, 176,1959,2527,1620,6152,2275,4336,3319,1983,2191,3705,3610,2155, # 3040 -3706,1912,1513,1614,6153,1988, 646, 392,2304,1589,3320,3039,1826,1239,1352,1340, # 3056 -2916, 505,2567,1709,1437,2408,2547, 906,6154,2672, 384,1458,1594,1100,1329, 710, # 3072 - 423,3531,2064,2231,2622,1989,2673,1087,1882, 333, 841,3005,1296,2882,2379, 580, # 3088 -1937,1827,1293,2585, 601, 574, 249,1772,4118,2079,1120, 645, 901,1176,1690, 795, # 3104 -2207, 478,1434, 516,1190,1530, 761,2080, 930,1264, 355, 435,1552, 644,1791, 987, # 3120 - 220,1364,1163,1121,1538, 306,2169,1327,1222, 546,2645, 218, 241, 610,1704,3321, # 3136 -1984,1839,1966,2528, 451,6155,2586,3707,2568, 907,3178, 254,2947, 186,1845,4650, # 3152 - 745, 432,1757, 428,1633, 888,2246,2221,2489,3611,2118,1258,1265, 956,3127,1784, # 3168 -4337,2490, 319, 510, 119, 457,3612, 274,2035,2007,4651,1409,3128, 970,2758, 590, # 3184 -2800, 661,2247,4652,2008,3950,1420,1549,3080,3322,3951,1651,1375,2111, 485,2491, # 3200 -1429,1156,6156,2548,2183,1495, 831,1840,2529,2446, 501,1657, 307,1894,3247,1341, # 3216 - 666, 899,2156,1539,2549,1559, 886, 349,2208,3081,2305,1736,3824,2170,2759,1014, # 3232 -1913,1386, 542,1397,2948, 490, 368, 716, 362, 159, 282,2569,1129,1658,1288,1750, # 3248 -2674, 276, 649,2016, 751,1496, 658,1818,1284,1862,2209,2087,2512,3451, 622,2834, # 3264 - 376, 117,1060,2053,1208,1721,1101,1443, 247,1250,3179,1792,3952,2760,2398,3953, # 3280 -6157,2144,3708, 446,2432,1151,2570,3452,2447,2761,2835,1210,2448,3082, 424,2222, # 3296 -1251,2449,2119,2836, 504,1581,4338, 602, 817, 857,3825,2349,2306, 357,3826,1470, # 3312 -1883,2883, 255, 958, 929,2917,3248, 302,4653,1050,1271,1751,2307,1952,1430,2697, # 3328 -2719,2359, 354,3180, 777, 158,2036,4339,1659,4340,4654,2308,2949,2248,1146,2232, # 3344 -3532,2720,1696,2623,3827,6158,3129,1550,2698,1485,1297,1428, 637, 931,2721,2145, # 3360 - 914,2550,2587, 81,2450, 612, 827,2646,1242,4655,1118,2884, 472,1855,3181,3533, # 3376 -3534, 569,1353,2699,1244,1758,2588,4119,2009,2762,2171,3709,1312,1531,6159,1152, # 3392 -1938, 134,1830, 471,3710,2276,1112,1535,3323,3453,3535, 982,1337,2950, 488, 826, # 3408 - 674,1058,1628,4120,2017, 522,2399, 211, 568,1367,3454, 350, 293,1872,1139,3249, # 3424 -1399,1946,3006,1300,2360,3324, 588, 736,6160,2606, 744, 669,3536,3828,6161,1358, # 3440 - 199, 723, 848, 933, 851,1939,1505,1514,1338,1618,1831,4656,1634,3613, 443,2740, # 3456 -3829, 717,1947, 491,1914,6162,2551,1542,4121,1025,6163,1099,1223, 198,3040,2722, # 3472 - 370, 410,1905,2589, 998,1248,3182,2380, 519,1449,4122,1710, 947, 928,1153,4341, # 3488 -2277, 344,2624,1511, 615, 105, 161,1212,1076,1960,3130,2054,1926,1175,1906,2473, # 3504 - 414,1873,2801,6164,2309, 315,1319,3325, 318,2018,2146,2157, 963, 631, 223,4342, # 3520 -4343,2675, 479,3711,1197,2625,3712,2676,2361,6165,4344,4123,6166,2451,3183,1886, # 3536 -2184,1674,1330,1711,1635,1506, 799, 219,3250,3083,3954,1677,3713,3326,2081,3614, # 3552 -1652,2073,4657,1147,3041,1752, 643,1961, 147,1974,3955,6167,1716,2037, 918,3007, # 3568 -1994, 120,1537, 118, 609,3184,4345, 740,3455,1219, 332,1615,3830,6168,1621,2980, # 3584 -1582, 783, 212, 553,2350,3714,1349,2433,2082,4124, 889,6169,2310,1275,1410, 973, # 3600 - 166,1320,3456,1797,1215,3185,2885,1846,2590,2763,4658, 629, 822,3008, 763, 940, # 3616 -1990,2862, 439,2409,1566,1240,1622, 926,1282,1907,2764, 654,2210,1607, 327,1130, # 3632 -3956,1678,1623,6170,2434,2192, 686, 608,3831,3715, 903,3957,3042,6171,2741,1522, # 3648 -1915,1105,1555,2552,1359, 323,3251,4346,3457, 738,1354,2553,2311,2334,1828,2003, # 3664 -3832,1753,2351,1227,6172,1887,4125,1478,6173,2410,1874,1712,1847, 520,1204,2607, # 3680 - 264,4659, 836,2677,2102, 600,4660,3833,2278,3084,6174,4347,3615,1342, 640, 532, # 3696 - 543,2608,1888,2400,2591,1009,4348,1497, 341,1737,3616,2723,1394, 529,3252,1321, # 3712 - 983,4661,1515,2120, 971,2592, 924, 287,1662,3186,4349,2700,4350,1519, 908,1948, # 3728 -2452, 156, 796,1629,1486,2223,2055, 694,4126,1259,1036,3392,1213,2249,2742,1889, # 3744 -1230,3958,1015, 910, 408, 559,3617,4662, 746, 725, 935,4663,3959,3009,1289, 563, # 3760 - 867,4664,3960,1567,2981,2038,2626, 988,2263,2381,4351, 143,2374, 704,1895,6175, # 3776 -1188,3716,2088, 673,3085,2362,4352, 484,1608,1921,2765,2918, 215, 904,3618,3537, # 3792 - 894, 509, 976,3043,2701,3961,4353,2837,2982, 498,6176,6177,1102,3538,1332,3393, # 3808 -1487,1636,1637, 233, 245,3962, 383, 650, 995,3044, 460,1520,1206,2352, 749,3327, # 3824 - 530, 700, 389,1438,1560,1773,3963,2264, 719,2951,2724,3834, 870,1832,1644,1000, # 3840 - 839,2474,3717, 197,1630,3394, 365,2886,3964,1285,2133, 734, 922, 818,1106, 732, # 3856 - 480,2083,1774,3458, 923,2279,1350, 221,3086, 85,2233,2234,3835,1585,3010,2147, # 3872 -1387,1705,2382,1619,2475, 133, 239,2802,1991,1016,2084,2383, 411,2838,1113, 651, # 3888 -1985,1160,3328, 990,1863,3087,1048,1276,2647, 265,2627,1599,3253,2056, 150, 638, # 3904 -2019, 656, 853, 326,1479, 680,1439,4354,1001,1759, 413,3459,3395,2492,1431, 459, # 3920 -4355,1125,3329,2265,1953,1450,2065,2863, 849, 351,2678,3131,3254,3255,1104,1577, # 3936 - 227,1351,1645,2453,2193,1421,2887, 812,2121, 634, 95,2435, 201,2312,4665,1646, # 3952 -1671,2743,1601,2554,2702,2648,2280,1315,1366,2089,3132,1573,3718,3965,1729,1189, # 3968 - 328,2679,1077,1940,1136, 558,1283, 964,1195, 621,2074,1199,1743,3460,3619,1896, # 3984 -1916,1890,3836,2952,1154,2112,1064, 862, 378,3011,2066,2113,2803,1568,2839,6178, # 4000 -3088,2919,1941,1660,2004,1992,2194, 142, 707,1590,1708,1624,1922,1023,1836,1233, # 4016 -1004,2313, 789, 741,3620,6179,1609,2411,1200,4127,3719,3720,4666,2057,3721, 593, # 4032 -2840, 367,2920,1878,6180,3461,1521, 628,1168, 692,2211,2649, 300, 720,2067,2571, # 4048 -2953,3396, 959,2504,3966,3539,3462,1977, 701,6181, 954,1043, 800, 681, 183,3722, # 4064 -1803,1730,3540,4128,2103, 815,2314, 174, 467, 230,2454,1093,2134, 755,3541,3397, # 4080 -1141,1162,6182,1738,2039, 270,3256,2513,1005,1647,2185,3837, 858,1679,1897,1719, # 4096 -2954,2324,1806, 402, 670, 167,4129,1498,2158,2104, 750,6183, 915, 189,1680,1551, # 4112 - 455,4356,1501,2455, 405,1095,2955, 338,1586,1266,1819, 570, 641,1324, 237,1556, # 4128 -2650,1388,3723,6184,1368,2384,1343,1978,3089,2436, 879,3724, 792,1191, 758,3012, # 4144 -1411,2135,1322,4357, 240,4667,1848,3725,1574,6185, 420,3045,1546,1391, 714,4358, # 4160 -1967, 941,1864, 863, 664, 426, 560,1731,2680,1785,2864,1949,2363, 403,3330,1415, # 4176 -1279,2136,1697,2335, 204, 721,2097,3838, 90,6186,2085,2505, 191,3967, 124,2148, # 4192 -1376,1798,1178,1107,1898,1405, 860,4359,1243,1272,2375,2983,1558,2456,1638, 113, # 4208 -3621, 578,1923,2609, 880, 386,4130, 784,2186,2266,1422,2956,2172,1722, 497, 263, # 4224 -2514,1267,2412,2610, 177,2703,3542, 774,1927,1344, 616,1432,1595,1018, 172,4360, # 4240 -2325, 911,4361, 438,1468,3622, 794,3968,2024,2173,1681,1829,2957, 945, 895,3090, # 4256 - 575,2212,2476, 475,2401,2681, 785,2744,1745,2293,2555,1975,3133,2865, 394,4668, # 4272 -3839, 635,4131, 639, 202,1507,2195,2766,1345,1435,2572,3726,1908,1184,1181,2457, # 4288 -3727,3134,4362, 843,2611, 437, 916,4669, 234, 769,1884,3046,3047,3623, 833,6187, # 4304 -1639,2250,2402,1355,1185,2010,2047, 999, 525,1732,1290,1488,2612, 948,1578,3728, # 4320 -2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336 -1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352 -2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512 + 40, 1, 6, 182, 152, 180, 295, 2127, 285, 381, 3295, 4304, 3068, 4606, 3165, 3510, # 16 + 3511, 1822, 2785, 4607, 1193, 2226, 5070, 4608, 171, 2996, 1247, 18, 179, 5071, 856, 1661, # 32 + 1262, 5072, 619, 127, 3431, 3512, 3230, 1899, 1700, 232, 228, 1294, 1298, 284, 283, 2041, # 48 + 2042, 1061, 1062, 48, 49, 44, 45, 433, 434, 1040, 1041, 996, 787, 2997, 1255, 4305, # 64 + 2108, 4609, 1684, 1648, 5073, 5074, 5075, 5076, 5077, 5078, 3687, 5079, 4610, 5080, 3927, 3928, # 80 + 5081, 3296, 3432, 290, 2285, 1471, 2187, 5082, 2580, 2825, 1303, 2140, 1739, 1445, 2691, 3375, # 96 + 1691, 3297, 4306, 4307, 4611, 452, 3376, 1182, 2713, 3688, 3069, 4308, 5083, 5084, 5085, 5086, # 112 + 5087, 5088, 5089, 5090, 5091, 5092, 5093, 5094, 5095, 5096, 5097, 5098, 5099, 5100, 5101, 5102, # 128 + 5103, 5104, 5105, 5106, 5107, 5108, 5109, 5110, 5111, 5112, 4097, 5113, 5114, 5115, 5116, 5117, # 144 + 5118, 5119, 5120, 5121, 5122, 5123, 5124, 5125, 5126, 5127, 5128, 5129, 5130, 5131, 5132, 5133, # 160 + 5134, 5135, 5136, 5137, 5138, 5139, 5140, 5141, 5142, 5143, 5144, 5145, 5146, 5147, 5148, 5149, # 176 + 5150, 5151, 5152, 4612, 5153, 5154, 5155, 5156, 5157, 5158, 5159, 5160, 5161, 5162, 5163, 5164, # 192 + 5165, 5166, 5167, 5168, 5169, 5170, 5171, 5172, 5173, 5174, 5175, 1472, 598, 618, 820, 1205, # 208 + 1309, 1412, 1858, 1307, 1692, 5176, 5177, 5178, 5179, 5180, 5181, 5182, 1142, 1452, 1234, 1172, # 224 + 1875, 2043, 2149, 1793, 1382, 2973, 925, 2404, 1067, 1241, 960, 1377, 2935, 1491, 919, 1217, # 240 + 1865, 2030, 1406, 1499, 2749, 4098, 5183, 5184, 5185, 5186, 5187, 5188, 2561, 4099, 3117, 1804, # 256 + 2049, 3689, 4309, 3513, 1663, 5189, 3166, 3118, 3298, 1587, 1561, 3433, 5190, 3119, 1625, 2998, # 272 + 3299, 4613, 1766, 3690, 2786, 4614, 5191, 5192, 5193, 5194, 2161, 26, 3377, 2, 3929, 20, # 288 + 3691, 47, 4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304 + 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320 + 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151, 1069, 181, 275, # 336 + 1591, 83, 132, 1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55, 1079, 312, # 352 + 109, 38, 23, 25, 10, 19, 79, 5195, 61, 382, 1124, 8, 30, 5196, 5197, 5198, # 368 + 5199, 5200, 5201, 5202, 5203, 5204, 5205, 5206, 89, 62, 74, 34, 2416, 112, 139, 196, # 384 + 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400 + 32, 364, 121, 1508, 169, 1547, 68, 235, 145, 2999, 41, 360, 3027, 70, 63, 31, # 416 + 43, 259, 262, 1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432 + 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104, 1140, # 448 + 54, 51, 36, 87, 67, 3070, 185, 2618, 2936, 2020, 28, 1066, 2390, 2059, 5207, 5208, # 464 + 5209, 5210, 5211, 5212, 5213, 5214, 5215, 5216, 4615, 5217, 5218, 5219, 5220, 5221, 5222, 5223, # 480 + 5224, 5225, 5226, 5227, 5228, 5229, 5230, 5231, 5232, 5233, 5234, 5235, 5236, 3514, 5237, 5238, # 496 + 5239, 5240, 5241, 5242, 5243, 5244, 2297, 2031, 4616, 4310, 3692, 5245, 3071, 5246, 3598, 5247, # 512 + 4617, 3231, 3515, 5248, 4101, 4311, 4618, 3808, 4312, 4102, 5249, 4103, 4104, 3599, 5250, 5251, # 528 + 5252, 5253, 5254, 5255, 5256, 5257, 5258, 5259, 5260, 5261, 5262, 5263, 5264, 5265, 5266, 5267, # 544 + 5268, 5269, 5270, 5271, 5272, 5273, 5274, 5275, 5276, 5277, 5278, 5279, 5280, 5281, 5282, 5283, # 560 + 5284, 5285, 5286, 5287, 5288, 5289, 5290, 5291, 5292, 5293, 5294, 5295, 5296, 5297, 5298, 5299, # 576 + 5300, 5301, 5302, 5303, 5304, 5305, 5306, 5307, 5308, 5309, 5310, 5311, 5312, 5313, 5314, 5315, # 592 + 5316, 5317, 5318, 5319, 5320, 5321, 5322, 5323, 5324, 5325, 5326, 5327, 5328, 5329, 5330, 5331, # 608 + 5332, 5333, 5334, 5335, 5336, 5337, 5338, 5339, 5340, 5341, 5342, 5343, 5344, 5345, 5346, 5347, # 624 + 5348, 5349, 5350, 5351, 5352, 5353, 5354, 5355, 5356, 5357, 5358, 5359, 5360, 5361, 5362, 5363, # 640 + 5364, 5365, 5366, 5367, 5368, 5369, 5370, 5371, 5372, 5373, 5374, 5375, 5376, 5377, 5378, 5379, # 656 + 5380, 5381, 363, 642, 2787, 2878, 2788, 2789, 2316, 3232, 2317, 3434, 2011, 165, 1942, 3930, # 672 + 3931, 3932, 3933, 5382, 4619, 5383, 4620, 5384, 5385, 5386, 5387, 5388, 5389, 5390, 5391, 5392, # 688 + 5393, 5394, 5395, 5396, 5397, 5398, 5399, 5400, 5401, 5402, 5403, 5404, 5405, 5406, 5407, 5408, # 704 + 5409, 5410, 5411, 5412, 5413, 5414, 5415, 5416, 5417, 5418, 5419, 5420, 5421, 5422, 5423, 5424, # 720 + 5425, 5426, 5427, 5428, 5429, 5430, 5431, 5432, 5433, 5434, 5435, 5436, 5437, 5438, 5439, 5440, # 736 + 5441, 5442, 5443, 5444, 5445, 5446, 5447, 5448, 5449, 5450, 5451, 5452, 5453, 5454, 5455, 5456, # 752 + 5457, 5458, 5459, 5460, 5461, 5462, 5463, 5464, 5465, 5466, 5467, 5468, 5469, 5470, 5471, 5472, # 768 + 5473, 5474, 5475, 5476, 5477, 5478, 5479, 5480, 5481, 5482, 5483, 5484, 5485, 5486, 5487, 5488, # 784 + 5489, 5490, 5491, 5492, 5493, 5494, 5495, 5496, 5497, 5498, 5499, 5500, 5501, 5502, 5503, 5504, # 800 + 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5515, 5516, 5517, 5518, 5519, 5520, # 816 + 5521, 5522, 5523, 5524, 5525, 5526, 5527, 5528, 5529, 5530, 5531, 5532, 5533, 5534, 5535, 5536, # 832 + 5537, 5538, 5539, 5540, 5541, 5542, 5543, 5544, 5545, 5546, 5547, 5548, 5549, 5550, 5551, 5552, # 848 + 5553, 5554, 5555, 5556, 5557, 5558, 5559, 5560, 5561, 5562, 5563, 5564, 5565, 5566, 5567, 5568, # 864 + 5569, 5570, 5571, 5572, 5573, 5574, 5575, 5576, 5577, 5578, 5579, 5580, 5581, 5582, 5583, 5584, # 880 + 5585, 5586, 5587, 5588, 5589, 5590, 5591, 5592, 5593, 5594, 5595, 5596, 5597, 5598, 5599, 5600, # 896 + 5601, 5602, 5603, 5604, 5605, 5606, 5607, 5608, 5609, 5610, 5611, 5612, 5613, 5614, 5615, 5616, # 912 + 5617, 5618, 5619, 5620, 5621, 5622, 5623, 5624, 5625, 5626, 5627, 5628, 5629, 5630, 5631, 5632, # 928 + 5633, 5634, 5635, 5636, 5637, 5638, 5639, 5640, 5641, 5642, 5643, 5644, 5645, 5646, 5647, 5648, # 944 + 5649, 5650, 5651, 5652, 5653, 5654, 5655, 5656, 5657, 5658, 5659, 5660, 5661, 5662, 5663, 5664, # 960 + 5665, 5666, 5667, 5668, 5669, 5670, 5671, 5672, 5673, 5674, 5675, 5676, 5677, 5678, 5679, 5680, # 976 + 5681, 5682, 5683, 5684, 5685, 5686, 5687, 5688, 5689, 5690, 5691, 5692, 5693, 5694, 5695, 5696, # 992 + 5697, 5698, 5699, 5700, 5701, 5702, 5703, 5704, 5705, 5706, 5707, 5708, 5709, 5710, 5711, 5712, # 1008 + 5713, 5714, 5715, 5716, 5717, 5718, 5719, 5720, 5721, 5722, 5723, 5724, 5725, 5726, 5727, 5728, # 1024 + 5729, 5730, 5731, 5732, 5733, 5734, 5735, 5736, 5737, 5738, 5739, 5740, 5741, 5742, 5743, 5744, # 1040 + 5745, 5746, 5747, 5748, 5749, 5750, 5751, 5752, 5753, 5754, 5755, 5756, 5757, 5758, 5759, 5760, # 1056 + 5761, 5762, 5763, 5764, 5765, 5766, 5767, 5768, 5769, 5770, 5771, 5772, 5773, 5774, 5775, 5776, # 1072 + 5777, 5778, 5779, 5780, 5781, 5782, 5783, 5784, 5785, 5786, 5787, 5788, 5789, 5790, 5791, 5792, # 1088 + 5793, 5794, 5795, 5796, 5797, 5798, 5799, 5800, 5801, 5802, 5803, 5804, 5805, 5806, 5807, 5808, # 1104 + 5809, 5810, 5811, 5812, 5813, 5814, 5815, 5816, 5817, 5818, 5819, 5820, 5821, 5822, 5823, 5824, # 1120 + 5825, 5826, 5827, 5828, 5829, 5830, 5831, 5832, 5833, 5834, 5835, 5836, 5837, 5838, 5839, 5840, # 1136 + 5841, 5842, 5843, 5844, 5845, 5846, 5847, 5848, 5849, 5850, 5851, 5852, 5853, 5854, 5855, 5856, # 1152 + 5857, 5858, 5859, 5860, 5861, 5862, 5863, 5864, 5865, 5866, 5867, 5868, 5869, 5870, 5871, 5872, # 1168 + 5873, 5874, 5875, 5876, 5877, 5878, 5879, 5880, 5881, 5882, 5883, 5884, 5885, 5886, 5887, 5888, # 1184 + 5889, 5890, 5891, 5892, 5893, 5894, 5895, 5896, 5897, 5898, 5899, 5900, 5901, 5902, 5903, 5904, # 1200 + 5905, 5906, 5907, 5908, 5909, 5910, 5911, 5912, 5913, 5914, 5915, 5916, 5917, 5918, 5919, 5920, # 1216 + 5921, 5922, 5923, 5924, 5925, 5926, 5927, 5928, 5929, 5930, 5931, 5932, 5933, 5934, 5935, 5936, # 1232 + 5937, 5938, 5939, 5940, 5941, 5942, 5943, 5944, 5945, 5946, 5947, 5948, 5949, 5950, 5951, 5952, # 1248 + 5953, 5954, 5955, 5956, 5957, 5958, 5959, 5960, 5961, 5962, 5963, 5964, 5965, 5966, 5967, 5968, # 1264 + 5969, 5970, 5971, 5972, 5973, 5974, 5975, 5976, 5977, 5978, 5979, 5980, 5981, 5982, 5983, 5984, # 1280 + 5985, 5986, 5987, 5988, 5989, 5990, 5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999, 6000, # 1296 + 6001, 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6009, 6010, 6011, 6012, 6013, 6014, 6015, 6016, # 1312 + 6017, 6018, 6019, 6020, 6021, 6022, 6023, 6024, 6025, 6026, 6027, 6028, 6029, 6030, 6031, 6032, # 1328 + 6033, 6034, 6035, 6036, 6037, 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, 6048, # 1344 + 6049, 6050, 6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, 6062, 6063, 6064, # 1360 + 6065, 6066, 6067, 6068, 6069, 6070, 6071, 6072, 6073, 6074, 6075, 6076, 6077, 6078, 6079, 6080, # 1376 + 6081, 6082, 6083, 6084, 6085, 6086, 6087, 6088, 6089, 6090, 6091, 6092, 6093, 6094, 6095, 6096, # 1392 + 6097, 6098, 6099, 6100, 6101, 6102, 6103, 6104, 6105, 6106, 6107, 6108, 6109, 6110, 6111, 6112, # 1408 + 6113, 6114, 2044, 2060, 4621, 997, 1235, 473, 1186, 4622, 920, 3378, 6115, 6116, 379, 1108, # 1424 + 4313, 2657, 2735, 3934, 6117, 3809, 636, 3233, 573, 1026, 3693, 3435, 2974, 3300, 2298, 4105, # 1440 + 854, 2937, 2463, 393, 2581, 2417, 539, 752, 1280, 2750, 2480, 140, 1161, 440, 708, 1569, # 1456 + 665, 2497, 1746, 1291, 1523, 3000, 164, 1603, 847, 1331, 537, 1997, 486, 508, 1693, 2418, # 1472 + 1970, 2227, 878, 1220, 299, 1030, 969, 652, 2751, 624, 1137, 3301, 2619, 65, 3302, 2045, # 1488 + 1761, 1859, 3120, 1930, 3694, 3516, 663, 1767, 852, 835, 3695, 269, 767, 2826, 2339, 1305, # 1504 + 896, 1150, 770, 1616, 6118, 506, 1502, 2075, 1012, 2519, 775, 2520, 2975, 2340, 2938, 4314, # 1520 + 3028, 2086, 1224, 1943, 2286, 6119, 3072, 4315, 2240, 1273, 1987, 3935, 1557, 175, 597, 985, # 1536 + 3517, 2419, 2521, 1416, 3029, 585, 938, 1931, 1007, 1052, 1932, 1685, 6120, 3379, 4316, 4623, # 1552 + 804, 599, 3121, 1333, 2128, 2539, 1159, 1554, 2032, 3810, 687, 2033, 2904, 952, 675, 1467, # 1568 + 3436, 6121, 2241, 1096, 1786, 2440, 1543, 1924, 980, 1813, 2228, 781, 2692, 1879, 728, 1918, # 1584 + 3696, 4624, 548, 1950, 4625, 1809, 1088, 1356, 3303, 2522, 1944, 502, 972, 373, 513, 2827, # 1600 + 586, 2377, 2391, 1003, 1976, 1631, 6122, 2464, 1084, 648, 1776, 4626, 2141, 324, 962, 2012, # 1616 + 2177, 2076, 1384, 742, 2178, 1448, 1173, 1810, 222, 102, 301, 445, 125, 2420, 662, 2498, # 1632 + 277, 200, 1476, 1165, 1068, 224, 2562, 1378, 1446, 450, 1880, 659, 791, 582, 4627, 2939, # 1648 + 3936, 1516, 1274, 555, 2099, 3697, 1020, 1389, 1526, 3380, 1762, 1723, 1787, 2229, 412, 2114, # 1664 + 1900, 2392, 3518, 512, 2597, 427, 1925, 2341, 3122, 1653, 1686, 2465, 2499, 697, 330, 273, # 1680 + 380, 2162, 951, 832, 780, 991, 1301, 3073, 965, 2270, 3519, 668, 2523, 2636, 1286, 535, # 1696 + 1407, 518, 671, 957, 2658, 2378, 267, 611, 2197, 3030, 6123, 248, 2299, 967, 1799, 2356, # 1712 + 850, 1418, 3437, 1876, 1256, 1480, 2828, 1718, 6124, 6125, 1755, 1664, 2405, 6126, 4628, 2879, # 1728 + 2829, 499, 2179, 676, 4629, 557, 2329, 2214, 2090, 325, 3234, 464, 811, 3001, 992, 2342, # 1744 + 2481, 1232, 1469, 303, 2242, 466, 1070, 2163, 603, 1777, 2091, 4630, 2752, 4631, 2714, 322, # 1760 + 2659, 1964, 1768, 481, 2188, 1463, 2330, 2857, 3600, 2092, 3031, 2421, 4632, 2318, 2070, 1849, # 1776 + 2598, 4633, 1302, 2254, 1668, 1701, 2422, 3811, 2905, 3032, 3123, 2046, 4106, 1763, 1694, 4634, # 1792 + 1604, 943, 1724, 1454, 917, 868, 2215, 1169, 2940, 552, 1145, 1800, 1228, 1823, 1955, 316, # 1808 + 1080, 2510, 361, 1807, 2830, 4107, 2660, 3381, 1346, 1423, 1134, 4108, 6127, 541, 1263, 1229, # 1824 + 1148, 2540, 545, 465, 1833, 2880, 3438, 1901, 3074, 2482, 816, 3937, 713, 1788, 2500, 122, # 1840 + 1575, 195, 1451, 2501, 1111, 6128, 859, 374, 1225, 2243, 2483, 4317, 390, 1033, 3439, 3075, # 1856 + 2524, 1687, 266, 793, 1440, 2599, 946, 779, 802, 507, 897, 1081, 528, 2189, 1292, 711, # 1872 + 1866, 1725, 1167, 1640, 753, 398, 2661, 1053, 246, 348, 4318, 137, 1024, 3440, 1600, 2077, # 1888 + 2129, 825, 4319, 698, 238, 521, 187, 2300, 1157, 2423, 1641, 1605, 1464, 1610, 1097, 2541, # 1904 + 1260, 1436, 759, 2255, 1814, 2150, 705, 3235, 409, 2563, 3304, 561, 3033, 2005, 2564, 726, # 1920 + 1956, 2343, 3698, 4109, 949, 3812, 3813, 3520, 1669, 653, 1379, 2525, 881, 2198, 632, 2256, # 1936 + 1027, 778, 1074, 733, 1957, 514, 1481, 2466, 554, 2180, 702, 3938, 1606, 1017, 1398, 6129, # 1952 + 1380, 3521, 921, 993, 1313, 594, 449, 1489, 1617, 1166, 768, 1426, 1360, 495, 1794, 3601, # 1968 + 1177, 3602, 1170, 4320, 2344, 476, 425, 3167, 4635, 3168, 1424, 401, 2662, 1171, 3382, 1998, # 1984 + 1089, 4110, 477, 3169, 474, 6130, 1909, 596, 2831, 1842, 494, 693, 1051, 1028, 1207, 3076, # 2000 + 606, 2115, 727, 2790, 1473, 1115, 743, 3522, 630, 805, 1532, 4321, 2021, 366, 1057, 838, # 2016 + 684, 1114, 2142, 4322, 2050, 1492, 1892, 1808, 2271, 3814, 2424, 1971, 1447, 1373, 3305, 1090, # 2032 + 1536, 3939, 3523, 3306, 1455, 2199, 336, 369, 2331, 1035, 584, 2393, 902, 718, 2600, 6131, # 2048 + 2753, 463, 2151, 1149, 1611, 2467, 715, 1308, 3124, 1268, 343, 1413, 3236, 1517, 1347, 2663, # 2064 + 2093, 3940, 2022, 1131, 1553, 2100, 2941, 1427, 3441, 2942, 1323, 2484, 6132, 1980, 872, 2368, # 2080 + 2441, 2943, 320, 2369, 2116, 1082, 679, 1933, 3941, 2791, 3815, 625, 1143, 2023, 422, 2200, # 2096 + 3816, 6133, 730, 1695, 356, 2257, 1626, 2301, 2858, 2637, 1627, 1778, 937, 883, 2906, 2693, # 2112 + 3002, 1769, 1086, 400, 1063, 1325, 3307, 2792, 4111, 3077, 456, 2345, 1046, 747, 6134, 1524, # 2128 + 884, 1094, 3383, 1474, 2164, 1059, 974, 1688, 2181, 2258, 1047, 345, 1665, 1187, 358, 875, # 2144 + 3170, 305, 660, 3524, 2190, 1334, 1135, 3171, 1540, 1649, 2542, 1527, 927, 968, 2793, 885, # 2160 + 1972, 1850, 482, 500, 2638, 1218, 1109, 1085, 2543, 1654, 2034, 876, 78, 2287, 1482, 1277, # 2176 + 861, 1675, 1083, 1779, 724, 2754, 454, 397, 1132, 1612, 2332, 893, 672, 1237, 257, 2259, # 2192 + 2370, 135, 3384, 337, 2244, 547, 352, 340, 709, 2485, 1400, 788, 1138, 2511, 540, 772, # 2208 + 1682, 2260, 2272, 2544, 2013, 1843, 1902, 4636, 1999, 1562, 2288, 4637, 2201, 1403, 1533, 407, # 2224 + 576, 3308, 1254, 2071, 978, 3385, 170, 136, 1201, 3125, 2664, 3172, 2394, 213, 912, 873, # 2240 + 3603, 1713, 2202, 699, 3604, 3699, 813, 3442, 493, 531, 1054, 468, 2907, 1483, 304, 281, # 2256 + 4112, 1726, 1252, 2094, 339, 2319, 2130, 2639, 756, 1563, 2944, 748, 571, 2976, 1588, 2425, # 2272 + 2715, 1851, 1460, 2426, 1528, 1392, 1973, 3237, 288, 3309, 685, 3386, 296, 892, 2716, 2216, # 2288 + 1570, 2245, 722, 1747, 2217, 905, 3238, 1103, 6135, 1893, 1441, 1965, 251, 1805, 2371, 3700, # 2304 + 2601, 1919, 1078, 75, 2182, 1509, 1592, 1270, 2640, 4638, 2152, 6136, 3310, 3817, 524, 706, # 2320 + 1075, 292, 3818, 1756, 2602, 317, 98, 3173, 3605, 3525, 1844, 2218, 3819, 2502, 814, 567, # 2336 + 385, 2908, 1534, 6137, 534, 1642, 3239, 797, 6138, 1670, 1529, 953, 4323, 188, 1071, 538, # 2352 + 178, 729, 3240, 2109, 1226, 1374, 2000, 2357, 2977, 731, 2468, 1116, 2014, 2051, 6139, 1261, # 2368 + 1593, 803, 2859, 2736, 3443, 556, 682, 823, 1541, 6140, 1369, 2289, 1706, 2794, 845, 462, # 2384 + 2603, 2665, 1361, 387, 162, 2358, 1740, 739, 1770, 1720, 1304, 1401, 3241, 1049, 627, 1571, # 2400 + 2427, 3526, 1877, 3942, 1852, 1500, 431, 1910, 1503, 677, 297, 2795, 286, 1433, 1038, 1198, # 2416 + 2290, 1133, 1596, 4113, 4639, 2469, 1510, 1484, 3943, 6141, 2442, 108, 712, 4640, 2372, 866, # 2432 + 3701, 2755, 3242, 1348, 834, 1945, 1408, 3527, 2395, 3243, 1811, 824, 994, 1179, 2110, 1548, # 2448 + 1453, 790, 3003, 690, 4324, 4325, 2832, 2909, 3820, 1860, 3821, 225, 1748, 310, 346, 1780, # 2464 + 2470, 821, 1993, 2717, 2796, 828, 877, 3528, 2860, 2471, 1702, 2165, 2910, 2486, 1789, 453, # 2480 + 359, 2291, 1676, 73, 1164, 1461, 1127, 3311, 421, 604, 314, 1037, 589, 116, 2487, 737, # 2496 + 837, 1180, 111, 244, 735, 6142, 2261, 1861, 1362, 986, 523, 418, 581, 2666, 3822, 103, # 2512 + 855, 503, 1414, 1867, 2488, 1091, 657, 1597, 979, 605, 1316, 4641, 1021, 2443, 2078, 2001, # 2528 + 1209, 96, 587, 2166, 1032, 260, 1072, 2153, 173, 94, 226, 3244, 819, 2006, 4642, 4114, # 2544 + 2203, 231, 1744, 782, 97, 2667, 786, 3387, 887, 391, 442, 2219, 4326, 1425, 6143, 2694, # 2560 + 633, 1544, 1202, 483, 2015, 592, 2052, 1958, 2472, 1655, 419, 129, 4327, 3444, 3312, 1714, # 2576 + 1257, 3078, 4328, 1518, 1098, 865, 1310, 1019, 1885, 1512, 1734, 469, 2444, 148, 773, 436, # 2592 + 1815, 1868, 1128, 1055, 4329, 1245, 2756, 3445, 2154, 1934, 1039, 4643, 579, 1238, 932, 2320, # 2608 + 353, 205, 801, 115, 2428, 944, 2321, 1881, 399, 2565, 1211, 678, 766, 3944, 335, 2101, # 2624 + 1459, 1781, 1402, 3945, 2737, 2131, 1010, 844, 981, 1326, 1013, 550, 1816, 1545, 2620, 1335, # 2640 + 1008, 371, 2881, 936, 1419, 1613, 3529, 1456, 1395, 2273, 1834, 2604, 1317, 2738, 2503, 416, # 2656 + 1643, 4330, 806, 1126, 229, 591, 3946, 1314, 1981, 1576, 1837, 1666, 347, 1790, 977, 3313, # 2672 + 764, 2861, 1853, 688, 2429, 1920, 1462, 77, 595, 415, 2002, 3034, 798, 1192, 4115, 6144, # 2688 + 2978, 4331, 3035, 2695, 2582, 2072, 2566, 430, 2430, 1727, 842, 1396, 3947, 3702, 613, 377, # 2704 + 278, 236, 1417, 3388, 3314, 3174, 757, 1869, 107, 3530, 6145, 1194, 623, 2262, 207, 1253, # 2720 + 2167, 3446, 3948, 492, 1117, 1935, 536, 1838, 2757, 1246, 4332, 696, 2095, 2406, 1393, 1572, # 2736 + 3175, 1782, 583, 190, 253, 1390, 2230, 830, 3126, 3389, 934, 3245, 1703, 1749, 2979, 1870, # 2752 + 2545, 1656, 2204, 869, 2346, 4116, 3176, 1817, 496, 1764, 4644, 942, 1504, 404, 1903, 1122, # 2768 + 1580, 3606, 2945, 1022, 515, 372, 1735, 955, 2431, 3036, 6146, 2797, 1110, 2302, 2798, 617, # 2784 + 6147, 441, 762, 1771, 3447, 3607, 3608, 1904, 840, 3037, 86, 939, 1385, 572, 1370, 2445, # 2800 + 1336, 114, 3703, 898, 294, 203, 3315, 703, 1583, 2274, 429, 961, 4333, 1854, 1951, 3390, # 2816 + 2373, 3704, 4334, 1318, 1381, 966, 1911, 2322, 1006, 1155, 309, 989, 458, 2718, 1795, 1372, # 2832 + 1203, 252, 1689, 1363, 3177, 517, 1936, 168, 1490, 562, 193, 3823, 1042, 4117, 1835, 551, # 2848 + 470, 4645, 395, 489, 3448, 1871, 1465, 2583, 2641, 417, 1493, 279, 1295, 511, 1236, 1119, # 2864 + 72, 1231, 1982, 1812, 3004, 871, 1564, 984, 3449, 1667, 2696, 2096, 4646, 2347, 2833, 1673, # 2880 + 3609, 695, 3246, 2668, 807, 1183, 4647, 890, 388, 2333, 1801, 1457, 2911, 1765, 1477, 1031, # 2896 + 3316, 3317, 1278, 3391, 2799, 2292, 2526, 163, 3450, 4335, 2669, 1404, 1802, 6148, 2323, 2407, # 2912 + 1584, 1728, 1494, 1824, 1269, 298, 909, 3318, 1034, 1632, 375, 776, 1683, 2061, 291, 210, # 2928 + 1123, 809, 1249, 1002, 2642, 3038, 206, 1011, 2132, 144, 975, 882, 1565, 342, 667, 754, # 2944 + 1442, 2143, 1299, 2303, 2062, 447, 626, 2205, 1221, 2739, 2912, 1144, 1214, 2206, 2584, 760, # 2960 + 1715, 614, 950, 1281, 2670, 2621, 810, 577, 1287, 2546, 4648, 242, 2168, 250, 2643, 691, # 2976 + 123, 2644, 647, 313, 1029, 689, 1357, 2946, 1650, 216, 771, 1339, 1306, 808, 2063, 549, # 2992 + 913, 1371, 2913, 2914, 6149, 1466, 1092, 1174, 1196, 1311, 2605, 2396, 1783, 1796, 3079, 406, # 3008 + 2671, 2117, 3949, 4649, 487, 1825, 2220, 6150, 2915, 448, 2348, 1073, 6151, 2397, 1707, 130, # 3024 + 900, 1598, 329, 176, 1959, 2527, 1620, 6152, 2275, 4336, 3319, 1983, 2191, 3705, 3610, 2155, # 3040 + 3706, 1912, 1513, 1614, 6153, 1988, 646, 392, 2304, 1589, 3320, 3039, 1826, 1239, 1352, 1340, # 3056 + 2916, 505, 2567, 1709, 1437, 2408, 2547, 906, 6154, 2672, 384, 1458, 1594, 1100, 1329, 710, # 3072 + 423, 3531, 2064, 2231, 2622, 1989, 2673, 1087, 1882, 333, 841, 3005, 1296, 2882, 2379, 580, # 3088 + 1937, 1827, 1293, 2585, 601, 574, 249, 1772, 4118, 2079, 1120, 645, 901, 1176, 1690, 795, # 3104 + 2207, 478, 1434, 516, 1190, 1530, 761, 2080, 930, 1264, 355, 435, 1552, 644, 1791, 987, # 3120 + 220, 1364, 1163, 1121, 1538, 306, 2169, 1327, 1222, 546, 2645, 218, 241, 610, 1704, 3321, # 3136 + 1984, 1839, 1966, 2528, 451, 6155, 2586, 3707, 2568, 907, 3178, 254, 2947, 186, 1845, 4650, # 3152 + 745, 432, 1757, 428, 1633, 888, 2246, 2221, 2489, 3611, 2118, 1258, 1265, 956, 3127, 1784, # 3168 + 4337, 2490, 319, 510, 119, 457, 3612, 274, 2035, 2007, 4651, 1409, 3128, 970, 2758, 590, # 3184 + 2800, 661, 2247, 4652, 2008, 3950, 1420, 1549, 3080, 3322, 3951, 1651, 1375, 2111, 485, 2491, # 3200 + 1429, 1156, 6156, 2548, 2183, 1495, 831, 1840, 2529, 2446, 501, 1657, 307, 1894, 3247, 1341, # 3216 + 666, 899, 2156, 1539, 2549, 1559, 886, 349, 2208, 3081, 2305, 1736, 3824, 2170, 2759, 1014, # 3232 + 1913, 1386, 542, 1397, 2948, 490, 368, 716, 362, 159, 282, 2569, 1129, 1658, 1288, 1750, # 3248 + 2674, 276, 649, 2016, 751, 1496, 658, 1818, 1284, 1862, 2209, 2087, 2512, 3451, 622, 2834, # 3264 + 376, 117, 1060, 2053, 1208, 1721, 1101, 1443, 247, 1250, 3179, 1792, 3952, 2760, 2398, 3953, # 3280 + 6157, 2144, 3708, 446, 2432, 1151, 2570, 3452, 2447, 2761, 2835, 1210, 2448, 3082, 424, 2222, # 3296 + 1251, 2449, 2119, 2836, 504, 1581, 4338, 602, 817, 857, 3825, 2349, 2306, 357, 3826, 1470, # 3312 + 1883, 2883, 255, 958, 929, 2917, 3248, 302, 4653, 1050, 1271, 1751, 2307, 1952, 1430, 2697, # 3328 + 2719, 2359, 354, 3180, 777, 158, 2036, 4339, 1659, 4340, 4654, 2308, 2949, 2248, 1146, 2232, # 3344 + 3532, 2720, 1696, 2623, 3827, 6158, 3129, 1550, 2698, 1485, 1297, 1428, 637, 931, 2721, 2145, # 3360 + 914, 2550, 2587, 81, 2450, 612, 827, 2646, 1242, 4655, 1118, 2884, 472, 1855, 3181, 3533, # 3376 + 3534, 569, 1353, 2699, 1244, 1758, 2588, 4119, 2009, 2762, 2171, 3709, 1312, 1531, 6159, 1152, # 3392 + 1938, 134, 1830, 471, 3710, 2276, 1112, 1535, 3323, 3453, 3535, 982, 1337, 2950, 488, 826, # 3408 + 674, 1058, 1628, 4120, 2017, 522, 2399, 211, 568, 1367, 3454, 350, 293, 1872, 1139, 3249, # 3424 + 1399, 1946, 3006, 1300, 2360, 3324, 588, 736, 6160, 2606, 744, 669, 3536, 3828, 6161, 1358, # 3440 + 199, 723, 848, 933, 851, 1939, 1505, 1514, 1338, 1618, 1831, 4656, 1634, 3613, 443, 2740, # 3456 + 3829, 717, 1947, 491, 1914, 6162, 2551, 1542, 4121, 1025, 6163, 1099, 1223, 198, 3040, 2722, # 3472 + 370, 410, 1905, 2589, 998, 1248, 3182, 2380, 519, 1449, 4122, 1710, 947, 928, 1153, 4341, # 3488 + 2277, 344, 2624, 1511, 615, 105, 161, 1212, 1076, 1960, 3130, 2054, 1926, 1175, 1906, 2473, # 3504 + 414, 1873, 2801, 6164, 2309, 315, 1319, 3325, 318, 2018, 2146, 2157, 963, 631, 223, 4342, # 3520 + 4343, 2675, 479, 3711, 1197, 2625, 3712, 2676, 2361, 6165, 4344, 4123, 6166, 2451, 3183, 1886, # 3536 + 2184, 1674, 1330, 1711, 1635, 1506, 799, 219, 3250, 3083, 3954, 1677, 3713, 3326, 2081, 3614, # 3552 + 1652, 2073, 4657, 1147, 3041, 1752, 643, 1961, 147, 1974, 3955, 6167, 1716, 2037, 918, 3007, # 3568 + 1994, 120, 1537, 118, 609, 3184, 4345, 740, 3455, 1219, 332, 1615, 3830, 6168, 1621, 2980, # 3584 + 1582, 783, 212, 553, 2350, 3714, 1349, 2433, 2082, 4124, 889, 6169, 2310, 1275, 1410, 973, # 3600 + 166, 1320, 3456, 1797, 1215, 3185, 2885, 1846, 2590, 2763, 4658, 629, 822, 3008, 763, 940, # 3616 + 1990, 2862, 439, 2409, 1566, 1240, 1622, 926, 1282, 1907, 2764, 654, 2210, 1607, 327, 1130, # 3632 + 3956, 1678, 1623, 6170, 2434, 2192, 686, 608, 3831, 3715, 903, 3957, 3042, 6171, 2741, 1522, # 3648 + 1915, 1105, 1555, 2552, 1359, 323, 3251, 4346, 3457, 738, 1354, 2553, 2311, 2334, 1828, 2003, # 3664 + 3832, 1753, 2351, 1227, 6172, 1887, 4125, 1478, 6173, 2410, 1874, 1712, 1847, 520, 1204, 2607, # 3680 + 264, 4659, 836, 2677, 2102, 600, 4660, 3833, 2278, 3084, 6174, 4347, 3615, 1342, 640, 532, # 3696 + 543, 2608, 1888, 2400, 2591, 1009, 4348, 1497, 341, 1737, 3616, 2723, 1394, 529, 3252, 1321, # 3712 + 983, 4661, 1515, 2120, 971, 2592, 924, 287, 1662, 3186, 4349, 2700, 4350, 1519, 908, 1948, # 3728 + 2452, 156, 796, 1629, 1486, 2223, 2055, 694, 4126, 1259, 1036, 3392, 1213, 2249, 2742, 1889, # 3744 + 1230, 3958, 1015, 910, 408, 559, 3617, 4662, 746, 725, 935, 4663, 3959, 3009, 1289, 563, # 3760 + 867, 4664, 3960, 1567, 2981, 2038, 2626, 988, 2263, 2381, 4351, 143, 2374, 704, 1895, 6175, # 3776 + 1188, 3716, 2088, 673, 3085, 2362, 4352, 484, 1608, 1921, 2765, 2918, 215, 904, 3618, 3537, # 3792 + 894, 509, 976, 3043, 2701, 3961, 4353, 2837, 2982, 498, 6176, 6177, 1102, 3538, 1332, 3393, # 3808 + 1487, 1636, 1637, 233, 245, 3962, 383, 650, 995, 3044, 460, 1520, 1206, 2352, 749, 3327, # 3824 + 530, 700, 389, 1438, 1560, 1773, 3963, 2264, 719, 2951, 2724, 3834, 870, 1832, 1644, 1000, # 3840 + 839, 2474, 3717, 197, 1630, 3394, 365, 2886, 3964, 1285, 2133, 734, 922, 818, 1106, 732, # 3856 + 480, 2083, 1774, 3458, 923, 2279, 1350, 221, 3086, 85, 2233, 2234, 3835, 1585, 3010, 2147, # 3872 + 1387, 1705, 2382, 1619, 2475, 133, 239, 2802, 1991, 1016, 2084, 2383, 411, 2838, 1113, 651, # 3888 + 1985, 1160, 3328, 990, 1863, 3087, 1048, 1276, 2647, 265, 2627, 1599, 3253, 2056, 150, 638, # 3904 + 2019, 656, 853, 326, 1479, 680, 1439, 4354, 1001, 1759, 413, 3459, 3395, 2492, 1431, 459, # 3920 + 4355, 1125, 3329, 2265, 1953, 1450, 2065, 2863, 849, 351, 2678, 3131, 3254, 3255, 1104, 1577, # 3936 + 227, 1351, 1645, 2453, 2193, 1421, 2887, 812, 2121, 634, 95, 2435, 201, 2312, 4665, 1646, # 3952 + 1671, 2743, 1601, 2554, 2702, 2648, 2280, 1315, 1366, 2089, 3132, 1573, 3718, 3965, 1729, 1189, # 3968 + 328, 2679, 1077, 1940, 1136, 558, 1283, 964, 1195, 621, 2074, 1199, 1743, 3460, 3619, 1896, # 3984 + 1916, 1890, 3836, 2952, 1154, 2112, 1064, 862, 378, 3011, 2066, 2113, 2803, 1568, 2839, 6178, # 4000 + 3088, 2919, 1941, 1660, 2004, 1992, 2194, 142, 707, 1590, 1708, 1624, 1922, 1023, 1836, 1233, # 4016 + 1004, 2313, 789, 741, 3620, 6179, 1609, 2411, 1200, 4127, 3719, 3720, 4666, 2057, 3721, 593, # 4032 + 2840, 367, 2920, 1878, 6180, 3461, 1521, 628, 1168, 692, 2211, 2649, 300, 720, 2067, 2571, # 4048 + 2953, 3396, 959, 2504, 3966, 3539, 3462, 1977, 701, 6181, 954, 1043, 800, 681, 183, 3722, # 4064 + 1803, 1730, 3540, 4128, 2103, 815, 2314, 174, 467, 230, 2454, 1093, 2134, 755, 3541, 3397, # 4080 + 1141, 1162, 6182, 1738, 2039, 270, 3256, 2513, 1005, 1647, 2185, 3837, 858, 1679, 1897, 1719, # 4096 + 2954, 2324, 1806, 402, 670, 167, 4129, 1498, 2158, 2104, 750, 6183, 915, 189, 1680, 1551, # 4112 + 455, 4356, 1501, 2455, 405, 1095, 2955, 338, 1586, 1266, 1819, 570, 641, 1324, 237, 1556, # 4128 + 2650, 1388, 3723, 6184, 1368, 2384, 1343, 1978, 3089, 2436, 879, 3724, 792, 1191, 758, 3012, # 4144 + 1411, 2135, 1322, 4357, 240, 4667, 1848, 3725, 1574, 6185, 420, 3045, 1546, 1391, 714, 4358, # 4160 + 1967, 941, 1864, 863, 664, 426, 560, 1731, 2680, 1785, 2864, 1949, 2363, 403, 3330, 1415, # 4176 + 1279, 2136, 1697, 2335, 204, 721, 2097, 3838, 90, 6186, 2085, 2505, 191, 3967, 124, 2148, # 4192 + 1376, 1798, 1178, 1107, 1898, 1405, 860, 4359, 1243, 1272, 2375, 2983, 1558, 2456, 1638, 113, # 4208 + 3621, 578, 1923, 2609, 880, 386, 4130, 784, 2186, 2266, 1422, 2956, 2172, 1722, 497, 263, # 4224 + 2514, 1267, 2412, 2610, 177, 2703, 3542, 774, 1927, 1344, 616, 1432, 1595, 1018, 172, 4360, # 4240 + 2325, 911, 4361, 438, 1468, 3622, 794, 3968, 2024, 2173, 1681, 1829, 2957, 945, 895, 3090, # 4256 + 575, 2212, 2476, 475, 2401, 2681, 785, 2744, 1745, 2293, 2555, 1975, 3133, 2865, 394, 4668, # 4272 + 3839, 635, 4131, 639, 202, 1507, 2195, 2766, 1345, 1435, 2572, 3726, 1908, 1184, 1181, 2457, # 4288 + 3727, 3134, 4362, 843, 2611, 437, 916, 4669, 234, 769, 1884, 3046, 3047, 3623, 833, 6187, # 4304 + 1639, 2250, 2402, 1355, 1185, 2010, 2047, 999, 525, 1732, 1290, 1488, 2612, 948, 1578, 3728, # 4320 + 2413, 2477, 1216, 2725, 2159, 334, 3840, 1328, 3624, 2921, 1525, 4132, 564, 1056, 891, 4363, # 4336 + 1444, 1698, 2385, 2251, 3729, 1365, 2281, 2235, 1717, 6188, 864, 3841, 2515, 444, 527, 2767, # 4352 + 2922, 3625, 544, 461, 6189, 566, 209, 2437, 3398, 2098, 1065, 2068, 3331, 3626, 3257, 2137, # 4368 #last 512 ) - - diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/jpcntx.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/jpcntx.py index 20044e4..8d09851 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/jpcntx.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/jpcntx.py @@ -24,96 +24,96 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### - - # This is hiragana 2-char sequence table, the number in each cell represents its frequency category +from __future__ import annotations jp2CharContext = ( -(0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1), -(2,4,0,4,0,3,0,4,0,3,4,4,4,2,4,3,3,4,3,2,3,3,4,2,3,3,3,2,4,1,4,3,3,1,5,4,3,4,3,4,3,5,3,0,3,5,4,2,0,3,1,0,3,3,0,3,3,0,1,1,0,4,3,0,3,3,0,4,0,2,0,3,5,5,5,5,4,0,4,1,0,3,4), -(0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2), -(0,4,0,5,0,5,0,4,0,4,5,4,4,3,5,3,5,1,5,3,4,3,4,4,3,4,3,3,4,3,5,4,4,3,5,5,3,5,5,5,3,5,5,3,4,5,5,3,1,3,2,0,3,4,0,4,2,0,4,2,1,5,3,2,3,5,0,4,0,2,0,5,4,4,5,4,5,0,4,0,0,4,4), -(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), -(0,3,0,4,0,3,0,3,0,4,5,4,3,3,3,3,4,3,5,4,4,3,5,4,4,3,4,3,4,4,4,4,5,3,4,4,3,4,5,5,4,5,5,1,4,5,4,3,0,3,3,1,3,3,0,4,4,0,3,3,1,5,3,3,3,5,0,4,0,3,0,4,4,3,4,3,3,0,4,1,1,3,4), -(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), -(0,4,0,3,0,3,0,4,0,3,4,4,3,2,2,1,2,1,3,1,3,3,3,3,3,4,3,1,3,3,5,3,3,0,4,3,0,5,4,3,3,5,4,4,3,4,4,5,0,1,2,0,1,2,0,2,2,0,1,0,0,5,2,2,1,4,0,3,0,1,0,4,4,3,5,4,3,0,2,1,0,4,3), -(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), -(0,3,0,5,0,4,0,2,1,4,4,2,4,1,4,2,4,2,4,3,3,3,4,3,3,3,3,1,4,2,3,3,3,1,4,4,1,1,1,4,3,3,2,0,2,4,3,2,0,3,3,0,3,1,1,0,0,0,3,3,0,4,2,2,3,4,0,4,0,3,0,4,4,5,3,4,4,0,3,0,0,1,4), -(1,4,0,4,0,4,0,4,0,3,5,4,4,3,4,3,5,4,3,3,4,3,5,4,4,4,4,3,4,2,4,3,3,1,5,4,3,2,4,5,4,5,5,4,4,5,4,4,0,3,2,2,3,3,0,4,3,1,3,2,1,4,3,3,4,5,0,3,0,2,0,4,5,5,4,5,4,0,4,0,0,5,4), -(0,5,0,5,0,4,0,3,0,4,4,3,4,3,3,3,4,0,4,4,4,3,4,3,4,3,3,1,4,2,4,3,4,0,5,4,1,4,5,4,4,5,3,2,4,3,4,3,2,4,1,3,3,3,2,3,2,0,4,3,3,4,3,3,3,4,0,4,0,3,0,4,5,4,4,4,3,0,4,1,0,1,3), -(0,3,1,4,0,3,0,2,0,3,4,4,3,1,4,2,3,3,4,3,4,3,4,3,4,4,3,2,3,1,5,4,4,1,4,4,3,5,4,4,3,5,5,4,3,4,4,3,1,2,3,1,2,2,0,3,2,0,3,1,0,5,3,3,3,4,3,3,3,3,4,4,4,4,5,4,2,0,3,3,2,4,3), -(0,2,0,3,0,1,0,1,0,0,3,2,0,0,2,0,1,0,2,1,3,3,3,1,2,3,1,0,1,0,4,2,1,1,3,3,0,4,3,3,1,4,3,3,0,3,3,2,0,0,0,0,1,0,0,2,0,0,0,0,0,4,1,0,2,3,2,2,2,1,3,3,3,4,4,3,2,0,3,1,0,3,3), -(0,4,0,4,0,3,0,3,0,4,4,4,3,3,3,3,3,3,4,3,4,2,4,3,4,3,3,2,4,3,4,5,4,1,4,5,3,5,4,5,3,5,4,0,3,5,5,3,1,3,3,2,2,3,0,3,4,1,3,3,2,4,3,3,3,4,0,4,0,3,0,4,5,4,4,5,3,0,4,1,0,3,4), -(0,2,0,3,0,3,0,0,0,2,2,2,1,0,1,0,0,0,3,0,3,0,3,0,1,3,1,0,3,1,3,3,3,1,3,3,3,0,1,3,1,3,4,0,0,3,1,1,0,3,2,0,0,0,0,1,3,0,1,0,0,3,3,2,0,3,0,0,0,0,0,3,4,3,4,3,3,0,3,0,0,2,3), -(2,3,0,3,0,2,0,1,0,3,3,4,3,1,3,1,1,1,3,1,4,3,4,3,3,3,0,0,3,1,5,4,3,1,4,3,2,5,5,4,4,4,4,3,3,4,4,4,0,2,1,1,3,2,0,1,2,0,0,1,0,4,1,3,3,3,0,3,0,1,0,4,4,4,5,5,3,0,2,0,0,4,4), -(0,2,0,1,0,3,1,3,0,2,3,3,3,0,3,1,0,0,3,0,3,2,3,1,3,2,1,1,0,0,4,2,1,0,2,3,1,4,3,2,0,4,4,3,1,3,1,3,0,1,0,0,1,0,0,0,1,0,0,0,0,4,1,1,1,2,0,3,0,0,0,3,4,2,4,3,2,0,1,0,0,3,3), -(0,1,0,4,0,5,0,4,0,2,4,4,2,3,3,2,3,3,5,3,3,3,4,3,4,2,3,0,4,3,3,3,4,1,4,3,2,1,5,5,3,4,5,1,3,5,4,2,0,3,3,0,1,3,0,4,2,0,1,3,1,4,3,3,3,3,0,3,0,1,0,3,4,4,4,5,5,0,3,0,1,4,5), -(0,2,0,3,0,3,0,0,0,2,3,1,3,0,4,0,1,1,3,0,3,4,3,2,3,1,0,3,3,2,3,1,3,0,2,3,0,2,1,4,1,2,2,0,0,3,3,0,0,2,0,0,0,1,0,0,0,0,2,2,0,3,2,1,3,3,0,2,0,2,0,0,3,3,1,2,4,0,3,0,2,2,3), -(2,4,0,5,0,4,0,4,0,2,4,4,4,3,4,3,3,3,1,2,4,3,4,3,4,4,5,0,3,3,3,3,2,0,4,3,1,4,3,4,1,4,4,3,3,4,4,3,1,2,3,0,4,2,0,4,1,0,3,3,0,4,3,3,3,4,0,4,0,2,0,3,5,3,4,5,2,0,3,0,0,4,5), -(0,3,0,4,0,1,0,1,0,1,3,2,2,1,3,0,3,0,2,0,2,0,3,0,2,0,0,0,1,0,1,1,0,0,3,1,0,0,0,4,0,3,1,0,2,1,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,4,2,2,3,1,0,3,0,0,0,1,4,4,4,3,0,0,4,0,0,1,4), -(1,4,1,5,0,3,0,3,0,4,5,4,4,3,5,3,3,4,4,3,4,1,3,3,3,3,2,1,4,1,5,4,3,1,4,4,3,5,4,4,3,5,4,3,3,4,4,4,0,3,3,1,2,3,0,3,1,0,3,3,0,5,4,4,4,4,4,4,3,3,5,4,4,3,3,5,4,0,3,2,0,4,4), -(0,2,0,3,0,1,0,0,0,1,3,3,3,2,4,1,3,0,3,1,3,0,2,2,1,1,0,0,2,0,4,3,1,0,4,3,0,4,4,4,1,4,3,1,1,3,3,1,0,2,0,0,1,3,0,0,0,0,2,0,0,4,3,2,4,3,5,4,3,3,3,4,3,3,4,3,3,0,2,1,0,3,3), -(0,2,0,4,0,3,0,2,0,2,5,5,3,4,4,4,4,1,4,3,3,0,4,3,4,3,1,3,3,2,4,3,0,3,4,3,0,3,4,4,2,4,4,0,4,5,3,3,2,2,1,1,1,2,0,1,5,0,3,3,2,4,3,3,3,4,0,3,0,2,0,4,4,3,5,5,0,0,3,0,2,3,3), -(0,3,0,4,0,3,0,1,0,3,4,3,3,1,3,3,3,0,3,1,3,0,4,3,3,1,1,0,3,0,3,3,0,0,4,4,0,1,5,4,3,3,5,0,3,3,4,3,0,2,0,1,1,1,0,1,3,0,1,2,1,3,3,2,3,3,0,3,0,1,0,1,3,3,4,4,1,0,1,2,2,1,3), -(0,1,0,4,0,4,0,3,0,1,3,3,3,2,3,1,1,0,3,0,3,3,4,3,2,4,2,0,1,0,4,3,2,0,4,3,0,5,3,3,2,4,4,4,3,3,3,4,0,1,3,0,0,1,0,0,1,0,0,0,0,4,2,3,3,3,0,3,0,0,0,4,4,4,5,3,2,0,3,3,0,3,5), -(0,2,0,3,0,0,0,3,0,1,3,0,2,0,0,0,1,0,3,1,1,3,3,0,0,3,0,0,3,0,2,3,1,0,3,1,0,3,3,2,0,4,2,2,0,2,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,2,1,2,0,1,0,1,0,0,0,1,3,1,2,0,0,0,1,0,0,1,4), -(0,3,0,3,0,5,0,1,0,2,4,3,1,3,3,2,1,1,5,2,1,0,5,1,2,0,0,0,3,3,2,2,3,2,4,3,0,0,3,3,1,3,3,0,2,5,3,4,0,3,3,0,1,2,0,2,2,0,3,2,0,2,2,3,3,3,0,2,0,1,0,3,4,4,2,5,4,0,3,0,0,3,5), -(0,3,0,3,0,3,0,1,0,3,3,3,3,0,3,0,2,0,2,1,1,0,2,0,1,0,0,0,2,1,0,0,1,0,3,2,0,0,3,3,1,2,3,1,0,3,3,0,0,1,0,0,0,0,0,2,0,0,0,0,0,2,3,1,2,3,0,3,0,1,0,3,2,1,0,4,3,0,1,1,0,3,3), -(0,4,0,5,0,3,0,3,0,4,5,5,4,3,5,3,4,3,5,3,3,2,5,3,4,4,4,3,4,3,4,5,5,3,4,4,3,4,4,5,4,4,4,3,4,5,5,4,2,3,4,2,3,4,0,3,3,1,4,3,2,4,3,3,5,5,0,3,0,3,0,5,5,5,5,4,4,0,4,0,1,4,4), -(0,4,0,4,0,3,0,3,0,3,5,4,4,2,3,2,5,1,3,2,5,1,4,2,3,2,3,3,4,3,3,3,3,2,5,4,1,3,3,5,3,4,4,0,4,4,3,1,1,3,1,0,2,3,0,2,3,0,3,0,0,4,3,1,3,4,0,3,0,2,0,4,4,4,3,4,5,0,4,0,0,3,4), -(0,3,0,3,0,3,1,2,0,3,4,4,3,3,3,0,2,2,4,3,3,1,3,3,3,1,1,0,3,1,4,3,2,3,4,4,2,4,4,4,3,4,4,3,2,4,4,3,1,3,3,1,3,3,0,4,1,0,2,2,1,4,3,2,3,3,5,4,3,3,5,4,4,3,3,0,4,0,3,2,2,4,4), -(0,2,0,1,0,0,0,0,0,1,2,1,3,0,0,0,0,0,2,0,1,2,1,0,0,1,0,0,0,0,3,0,0,1,0,1,1,3,1,0,0,0,1,1,0,1,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,1,2,2,0,3,4,0,0,0,1,1,0,0,1,0,0,0,0,0,1,1), -(0,1,0,0,0,1,0,0,0,0,4,0,4,1,4,0,3,0,4,0,3,0,4,0,3,0,3,0,4,1,5,1,4,0,0,3,0,5,0,5,2,0,1,0,0,0,2,1,4,0,1,3,0,0,3,0,0,3,1,1,4,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0), -(1,4,0,5,0,3,0,2,0,3,5,4,4,3,4,3,5,3,4,3,3,0,4,3,3,3,3,3,3,2,4,4,3,1,3,4,4,5,4,4,3,4,4,1,3,5,4,3,3,3,1,2,2,3,3,1,3,1,3,3,3,5,3,3,4,5,0,3,0,3,0,3,4,3,4,4,3,0,3,0,2,4,3), -(0,1,0,4,0,0,0,0,0,1,4,0,4,1,4,2,4,0,3,0,1,0,1,0,0,0,0,0,2,0,3,1,1,1,0,3,0,0,0,1,2,1,0,0,1,1,1,1,0,1,0,0,0,1,0,0,3,0,0,0,0,3,2,0,2,2,0,1,0,0,0,2,3,2,3,3,0,0,0,0,2,1,0), -(0,5,1,5,0,3,0,3,0,5,4,4,5,1,5,3,3,0,4,3,4,3,5,3,4,3,3,2,4,3,4,3,3,0,3,3,1,4,4,3,4,4,4,3,4,5,5,3,2,3,1,1,3,3,1,3,1,1,3,3,2,4,5,3,3,5,0,4,0,3,0,4,4,3,5,3,3,0,3,4,0,4,3), -(0,5,0,5,0,3,0,2,0,4,4,3,5,2,4,3,3,3,4,4,4,3,5,3,5,3,3,1,4,0,4,3,3,0,3,3,0,4,4,4,4,5,4,3,3,5,5,3,2,3,1,2,3,2,0,1,0,0,3,2,2,4,4,3,1,5,0,4,0,3,0,4,3,1,3,2,1,0,3,3,0,3,3), -(0,4,0,5,0,5,0,4,0,4,5,5,5,3,4,3,3,2,5,4,4,3,5,3,5,3,4,0,4,3,4,4,3,2,4,4,3,4,5,4,4,5,5,0,3,5,5,4,1,3,3,2,3,3,1,3,1,0,4,3,1,4,4,3,4,5,0,4,0,2,0,4,3,4,4,3,3,0,4,0,0,5,5), -(0,4,0,4,0,5,0,1,1,3,3,4,4,3,4,1,3,0,5,1,3,0,3,1,3,1,1,0,3,0,3,3,4,0,4,3,0,4,4,4,3,4,4,0,3,5,4,1,0,3,0,0,2,3,0,3,1,0,3,1,0,3,2,1,3,5,0,3,0,1,0,3,2,3,3,4,4,0,2,2,0,4,4), -(2,4,0,5,0,4,0,3,0,4,5,5,4,3,5,3,5,3,5,3,5,2,5,3,4,3,3,4,3,4,5,3,2,1,5,4,3,2,3,4,5,3,4,1,2,5,4,3,0,3,3,0,3,2,0,2,3,0,4,1,0,3,4,3,3,5,0,3,0,1,0,4,5,5,5,4,3,0,4,2,0,3,5), -(0,5,0,4,0,4,0,2,0,5,4,3,4,3,4,3,3,3,4,3,4,2,5,3,5,3,4,1,4,3,4,4,4,0,3,5,0,4,4,4,4,5,3,1,3,4,5,3,3,3,3,3,3,3,0,2,2,0,3,3,2,4,3,3,3,5,3,4,1,3,3,5,3,2,0,0,0,0,4,3,1,3,3), -(0,1,0,3,0,3,0,1,0,1,3,3,3,2,3,3,3,0,3,0,0,0,3,1,3,0,0,0,2,2,2,3,0,0,3,2,0,1,2,4,1,3,3,0,0,3,3,3,0,1,0,0,2,1,0,0,3,0,3,1,0,3,0,0,1,3,0,2,0,1,0,3,3,1,3,3,0,0,1,1,0,3,3), -(0,2,0,3,0,2,1,4,0,2,2,3,1,1,3,1,1,0,2,0,3,1,2,3,1,3,0,0,1,0,4,3,2,3,3,3,1,4,2,3,3,3,3,1,0,3,1,4,0,1,1,0,1,2,0,1,1,0,1,1,0,3,1,3,2,2,0,1,0,0,0,2,3,3,3,1,0,0,0,0,0,2,3), -(0,5,0,4,0,5,0,2,0,4,5,5,3,3,4,3,3,1,5,4,4,2,4,4,4,3,4,2,4,3,5,5,4,3,3,4,3,3,5,5,4,5,5,1,3,4,5,3,1,4,3,1,3,3,0,3,3,1,4,3,1,4,5,3,3,5,0,4,0,3,0,5,3,3,1,4,3,0,4,0,1,5,3), -(0,5,0,5,0,4,0,2,0,4,4,3,4,3,3,3,3,3,5,4,4,4,4,4,4,5,3,3,5,2,4,4,4,3,4,4,3,3,4,4,5,5,3,3,4,3,4,3,3,4,3,3,3,3,1,2,2,1,4,3,3,5,4,4,3,4,0,4,0,3,0,4,4,4,4,4,1,0,4,2,0,2,4), -(0,4,0,4,0,3,0,1,0,3,5,2,3,0,3,0,2,1,4,2,3,3,4,1,4,3,3,2,4,1,3,3,3,0,3,3,0,0,3,3,3,5,3,3,3,3,3,2,0,2,0,0,2,0,0,2,0,0,1,0,0,3,1,2,2,3,0,3,0,2,0,4,4,3,3,4,1,0,3,0,0,2,4), -(0,0,0,4,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,1,0,2,0,1,0,0,0,0,0,3,1,3,0,3,2,0,0,0,1,0,3,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,0,2,0,0,0,0,0,0,2), -(0,2,1,3,0,2,0,2,0,3,3,3,3,1,3,1,3,3,3,3,3,3,4,2,2,1,2,1,4,0,4,3,1,3,3,3,2,4,3,5,4,3,3,3,3,3,3,3,0,1,3,0,2,0,0,1,0,0,1,0,0,4,2,0,2,3,0,3,3,0,3,3,4,2,3,1,4,0,1,2,0,2,3), -(0,3,0,3,0,1,0,3,0,2,3,3,3,0,3,1,2,0,3,3,2,3,3,2,3,2,3,1,3,0,4,3,2,0,3,3,1,4,3,3,2,3,4,3,1,3,3,1,1,0,1,1,0,1,0,1,0,1,0,0,0,4,1,1,0,3,0,3,1,0,2,3,3,3,3,3,1,0,0,2,0,3,3), -(0,0,0,0,0,0,0,0,0,0,3,0,2,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,3,0,3,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,2,0,2,3,0,0,0,0,0,0,0,0,3), -(0,2,0,3,1,3,0,3,0,2,3,3,3,1,3,1,3,1,3,1,3,3,3,1,3,0,2,3,1,1,4,3,3,2,3,3,1,2,2,4,1,3,3,0,1,4,2,3,0,1,3,0,3,0,0,1,3,0,2,0,0,3,3,2,1,3,0,3,0,2,0,3,4,4,4,3,1,0,3,0,0,3,3), -(0,2,0,1,0,2,0,0,0,1,3,2,2,1,3,0,1,1,3,0,3,2,3,1,2,0,2,0,1,1,3,3,3,0,3,3,1,1,2,3,2,3,3,1,2,3,2,0,0,1,0,0,0,0,0,0,3,0,1,0,0,2,1,2,1,3,0,3,0,0,0,3,4,4,4,3,2,0,2,0,0,2,4), -(0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,3,1,0,0,0,0,0,0,0,3), -(0,3,0,3,0,2,0,3,0,3,3,3,2,3,2,2,2,0,3,1,3,3,3,2,3,3,0,0,3,0,3,2,2,0,2,3,1,4,3,4,3,3,2,3,1,5,4,4,0,3,1,2,1,3,0,3,1,1,2,0,2,3,1,3,1,3,0,3,0,1,0,3,3,4,4,2,1,0,2,1,0,2,4), -(0,1,0,3,0,1,0,2,0,1,4,2,5,1,4,0,2,0,2,1,3,1,4,0,2,1,0,0,2,1,4,1,1,0,3,3,0,5,1,3,2,3,3,1,0,3,2,3,0,1,0,0,0,0,0,0,1,0,0,0,0,4,0,1,0,3,0,2,0,1,0,3,3,3,4,3,3,0,0,0,0,2,3), -(0,0,0,1,0,0,0,0,0,0,2,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,1,0,0,0,0,0,3), -(0,1,0,3,0,4,0,3,0,2,4,3,1,0,3,2,2,1,3,1,2,2,3,1,1,1,2,1,3,0,1,2,0,1,3,2,1,3,0,5,5,1,0,0,1,3,2,1,0,3,0,0,1,0,0,0,0,0,3,4,0,1,1,1,3,2,0,2,0,1,0,2,3,3,1,2,3,0,1,0,1,0,4), -(0,0,0,1,0,3,0,3,0,2,2,1,0,0,4,0,3,0,3,1,3,0,3,0,3,0,1,0,3,0,3,1,3,0,3,3,0,0,1,2,1,1,1,0,1,2,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,2,2,1,2,0,0,2,0,0,0,0,2,3,3,3,3,0,0,0,0,1,4), -(0,0,0,3,0,3,0,0,0,0,3,1,1,0,3,0,1,0,2,0,1,0,0,0,0,0,0,0,1,0,3,0,2,0,2,3,0,0,2,2,3,1,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,2,3), -(2,4,0,5,0,5,0,4,0,3,4,3,3,3,4,3,3,3,4,3,4,4,5,4,5,5,5,2,3,0,5,5,4,1,5,4,3,1,5,4,3,4,4,3,3,4,3,3,0,3,2,0,2,3,0,3,0,0,3,3,0,5,3,2,3,3,0,3,0,3,0,3,4,5,4,5,3,0,4,3,0,3,4), -(0,3,0,3,0,3,0,3,0,3,3,4,3,2,3,2,3,0,4,3,3,3,3,3,3,3,3,0,3,2,4,3,3,1,3,4,3,4,4,4,3,4,4,3,2,4,4,1,0,2,0,0,1,1,0,2,0,0,3,1,0,5,3,2,1,3,0,3,0,1,2,4,3,2,4,3,3,0,3,2,0,4,4), -(0,3,0,3,0,1,0,0,0,1,4,3,3,2,3,1,3,1,4,2,3,2,4,2,3,4,3,0,2,2,3,3,3,0,3,3,3,0,3,4,1,3,3,0,3,4,3,3,0,1,1,0,1,0,0,0,4,0,3,0,0,3,1,2,1,3,0,4,0,1,0,4,3,3,4,3,3,0,2,0,0,3,3), -(0,3,0,4,0,1,0,3,0,3,4,3,3,0,3,3,3,1,3,1,3,3,4,3,3,3,0,0,3,1,5,3,3,1,3,3,2,5,4,3,3,4,5,3,2,5,3,4,0,1,0,0,0,0,0,2,0,0,1,1,0,4,2,2,1,3,0,3,0,2,0,4,4,3,5,3,2,0,1,1,0,3,4), -(0,5,0,4,0,5,0,2,0,4,4,3,3,2,3,3,3,1,4,3,4,1,5,3,4,3,4,0,4,2,4,3,4,1,5,4,0,4,4,4,4,5,4,1,3,5,4,2,1,4,1,1,3,2,0,3,1,0,3,2,1,4,3,3,3,4,0,4,0,3,0,4,4,4,3,3,3,0,4,2,0,3,4), -(1,4,0,4,0,3,0,1,0,3,3,3,1,1,3,3,2,2,3,3,1,0,3,2,2,1,2,0,3,1,2,1,2,0,3,2,0,2,2,3,3,4,3,0,3,3,1,2,0,1,1,3,1,2,0,0,3,0,1,1,0,3,2,2,3,3,0,3,0,0,0,2,3,3,4,3,3,0,1,0,0,1,4), -(0,4,0,4,0,4,0,0,0,3,4,4,3,1,4,2,3,2,3,3,3,1,4,3,4,0,3,0,4,2,3,3,2,2,5,4,2,1,3,4,3,4,3,1,3,3,4,2,0,2,1,0,3,3,0,0,2,0,3,1,0,4,4,3,4,3,0,4,0,1,0,2,4,4,4,4,4,0,3,2,0,3,3), -(0,0,0,1,0,4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,3,2,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2), -(0,2,0,3,0,4,0,4,0,1,3,3,3,0,4,0,2,1,2,1,1,1,2,0,3,1,1,0,1,0,3,1,0,0,3,3,2,0,1,1,0,0,0,0,0,1,0,2,0,2,2,0,3,1,0,0,1,0,1,1,0,1,2,0,3,0,0,0,0,1,0,0,3,3,4,3,1,0,1,0,3,0,2), -(0,0,0,3,0,5,0,0,0,0,1,0,2,0,3,1,0,1,3,0,0,0,2,0,0,0,1,0,0,0,1,1,0,0,4,0,0,0,2,3,0,1,4,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,0,0,0,0,2,0,0,3,0,0,0,0,0,3), -(0,2,0,5,0,5,0,1,0,2,4,3,3,2,5,1,3,2,3,3,3,0,4,1,2,0,3,0,4,0,2,2,1,1,5,3,0,0,1,4,2,3,2,0,3,3,3,2,0,2,4,1,1,2,0,1,1,0,3,1,0,1,3,1,2,3,0,2,0,0,0,1,3,5,4,4,4,0,3,0,0,1,3), -(0,4,0,5,0,4,0,4,0,4,5,4,3,3,4,3,3,3,4,3,4,4,5,3,4,5,4,2,4,2,3,4,3,1,4,4,1,3,5,4,4,5,5,4,4,5,5,5,2,3,3,1,4,3,1,3,3,0,3,3,1,4,3,4,4,4,0,3,0,4,0,3,3,4,4,5,0,0,4,3,0,4,5), -(0,4,0,4,0,3,0,3,0,3,4,4,4,3,3,2,4,3,4,3,4,3,5,3,4,3,2,1,4,2,4,4,3,1,3,4,2,4,5,5,3,4,5,4,1,5,4,3,0,3,2,2,3,2,1,3,1,0,3,3,3,5,3,3,3,5,4,4,2,3,3,4,3,3,3,2,1,0,3,2,1,4,3), -(0,4,0,5,0,4,0,3,0,3,5,5,3,2,4,3,4,0,5,4,4,1,4,4,4,3,3,3,4,3,5,5,2,3,3,4,1,2,5,5,3,5,5,2,3,5,5,4,0,3,2,0,3,3,1,1,5,1,4,1,0,4,3,2,3,5,0,4,0,3,0,5,4,3,4,3,0,0,4,1,0,4,4), -(1,3,0,4,0,2,0,2,0,2,5,5,3,3,3,3,3,0,4,2,3,4,4,4,3,4,0,0,3,4,5,4,3,3,3,3,2,5,5,4,5,5,5,4,3,5,5,5,1,3,1,0,1,0,0,3,2,0,4,2,0,5,2,3,2,4,1,3,0,3,0,4,5,4,5,4,3,0,4,2,0,5,4), -(0,3,0,4,0,5,0,3,0,3,4,4,3,2,3,2,3,3,3,3,3,2,4,3,3,2,2,0,3,3,3,3,3,1,3,3,3,0,4,4,3,4,4,1,1,4,4,2,0,3,1,0,1,1,0,4,1,0,2,3,1,3,3,1,3,4,0,3,0,1,0,3,1,3,0,0,1,0,2,0,0,4,4), -(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), -(0,3,0,3,0,2,0,3,0,1,5,4,3,3,3,1,4,2,1,2,3,4,4,2,4,4,5,0,3,1,4,3,4,0,4,3,3,3,2,3,2,5,3,4,3,2,2,3,0,0,3,0,2,1,0,1,2,0,0,0,0,2,1,1,3,1,0,2,0,4,0,3,4,4,4,5,2,0,2,0,0,1,3), -(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0,1,1,0,0,0,4,2,1,1,0,1,0,3,2,0,0,3,1,1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,1,0,0,0,2,0,0,0,1,4,0,4,2,1,0,0,0,0,0,1), -(0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,3,1,0,0,0,2,0,2,1,0,0,1,2,1,0,1,1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,1,3,1,0,0,0,0,0,1,0,0,2,1,0,0,0,0,0,0,0,0,2), -(0,4,0,4,0,4,0,3,0,4,4,3,4,2,4,3,2,0,4,4,4,3,5,3,5,3,3,2,4,2,4,3,4,3,1,4,0,2,3,4,4,4,3,3,3,4,4,4,3,4,1,3,4,3,2,1,2,1,3,3,3,4,4,3,3,5,0,4,0,3,0,4,3,3,3,2,1,0,3,0,0,3,3), -(0,4,0,3,0,3,0,3,0,3,5,5,3,3,3,3,4,3,4,3,3,3,4,4,4,3,3,3,3,4,3,5,3,3,1,3,2,4,5,5,5,5,4,3,4,5,5,3,2,2,3,3,3,3,2,3,3,1,2,3,2,4,3,3,3,4,0,4,0,2,0,4,3,2,2,1,2,0,3,0,0,4,1), + (0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1), + (2, 4, 0, 4, 0, 3, 0, 4, 0, 3, 4, 4, 4, 2, 4, 3, 3, 4, 3, 2, 3, 3, 4, 2, 3, 3, 3, 2, 4, 1, 4, 3, 3, 1, 5, 4, 3, 4, 3, 4, 3, 5, 3, 0, 3, 5, 4, 2, 0, 3, 1, 0, 3, 3, 0, 3, 3, 0, 1, 1, 0, 4, 3, 0, 3, 3, 0, 4, 0, 2, 0, 3, 5, 5, 5, 5, 4, 0, 4, 1, 0, 3, 4), + (0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2), + (0, 4, 0, 5, 0, 5, 0, 4, 0, 4, 5, 4, 4, 3, 5, 3, 5, 1, 5, 3, 4, 3, 4, 4, 3, 4, 3, 3, 4, 3, 5, 4, 4, 3, 5, 5, 3, 5, 5, 5, 3, 5, 5, 3, 4, 5, 5, 3, 1, 3, 2, 0, 3, 4, 0, 4, 2, 0, 4, 2, 1, 5, 3, 2, 3, 5, 0, 4, 0, 2, 0, 5, 4, 4, 5, 4, 5, 0, 4, 0, 0, 4, 4), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, 3, 0, 4, 0, 3, 0, 3, 0, 4, 5, 4, 3, 3, 3, 3, 4, 3, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 4, 4, 4, 4, 5, 3, 4, 4, 3, 4, 5, 5, 4, 5, 5, 1, 4, 5, 4, 3, 0, 3, 3, 1, 3, 3, 0, 4, 4, 0, 3, 3, 1, 5, 3, 3, 3, 5, 0, 4, 0, 3, 0, 4, 4, 3, 4, 3, 3, 0, 4, 1, 1, 3, 4), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, 4, 0, 3, 0, 3, 0, 4, 0, 3, 4, 4, 3, 2, 2, 1, 2, 1, 3, 1, 3, 3, 3, 3, 3, 4, 3, 1, 3, 3, 5, 3, 3, 0, 4, 3, 0, 5, 4, 3, 3, 5, 4, 4, 3, 4, 4, 5, 0, 1, 2, 0, 1, 2, 0, 2, 2, 0, 1, 0, 0, 5, 2, 2, 1, 4, 0, 3, 0, 1, 0, 4, 4, 3, 5, 4, 3, 0, 2, 1, 0, 4, 3), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, 3, 0, 5, 0, 4, 0, 2, 1, 4, 4, 2, 4, 1, 4, 2, 4, 2, 4, 3, 3, 3, 4, 3, 3, 3, 3, 1, 4, 2, 3, 3, 3, 1, 4, 4, 1, 1, 1, 4, 3, 3, 2, 0, 2, 4, 3, 2, 0, 3, 3, 0, 3, 1, 1, 0, 0, 0, 3, 3, 0, 4, 2, 2, 3, 4, 0, 4, 0, 3, 0, 4, 4, 5, 3, 4, 4, 0, 3, 0, 0, 1, 4), + (1, 4, 0, 4, 0, 4, 0, 4, 0, 3, 5, 4, 4, 3, 4, 3, 5, 4, 3, 3, 4, 3, 5, 4, 4, 4, 4, 3, 4, 2, 4, 3, 3, 1, 5, 4, 3, 2, 4, 5, 4, 5, 5, 4, 4, 5, 4, 4, 0, 3, 2, 2, 3, 3, 0, 4, 3, 1, 3, 2, 1, 4, 3, 3, 4, 5, 0, 3, 0, 2, 0, 4, 5, 5, 4, 5, 4, 0, 4, 0, 0, 5, 4), + (0, 5, 0, 5, 0, 4, 0, 3, 0, 4, 4, 3, 4, 3, 3, 3, 4, 0, 4, 4, 4, 3, 4, 3, 4, 3, 3, 1, 4, 2, 4, 3, 4, 0, 5, 4, 1, 4, 5, 4, 4, 5, 3, 2, 4, 3, 4, 3, 2, 4, 1, 3, 3, 3, 2, 3, 2, 0, 4, 3, 3, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 5, 4, 4, 4, 3, 0, 4, 1, 0, 1, 3), + (0, 3, 1, 4, 0, 3, 0, 2, 0, 3, 4, 4, 3, 1, 4, 2, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 2, 3, 1, 5, 4, 4, 1, 4, 4, 3, 5, 4, 4, 3, 5, 5, 4, 3, 4, 4, 3, 1, 2, 3, 1, 2, 2, 0, 3, 2, 0, 3, 1, 0, 5, 3, 3, 3, 4, 3, 3, 3, 3, 4, 4, 4, 4, 5, 4, 2, 0, 3, 3, 2, 4, 3), + (0, 2, 0, 3, 0, 1, 0, 1, 0, 0, 3, 2, 0, 0, 2, 0, 1, 0, 2, 1, 3, 3, 3, 1, 2, 3, 1, 0, 1, 0, 4, 2, 1, 1, 3, 3, 0, 4, 3, 3, 1, 4, 3, 3, 0, 3, 3, 2, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 4, 1, 0, 2, 3, 2, 2, 2, 1, 3, 3, 3, 4, 4, 3, 2, 0, 3, 1, 0, 3, 3), + (0, 4, 0, 4, 0, 3, 0, 3, 0, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 2, 4, 3, 4, 3, 3, 2, 4, 3, 4, 5, 4, 1, 4, 5, 3, 5, 4, 5, 3, 5, 4, 0, 3, 5, 5, 3, 1, 3, 3, 2, 2, 3, 0, 3, 4, 1, 3, 3, 2, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 5, 4, 4, 5, 3, 0, 4, 1, 0, 3, 4), + (0, 2, 0, 3, 0, 3, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 3, 0, 3, 0, 3, 0, 1, 3, 1, 0, 3, 1, 3, 3, 3, 1, 3, 3, 3, 0, 1, 3, 1, 3, 4, 0, 0, 3, 1, 1, 0, 3, 2, 0, 0, 0, 0, 1, 3, 0, 1, 0, 0, 3, 3, 2, 0, 3, 0, 0, 0, 0, 0, 3, 4, 3, 4, 3, 3, 0, 3, 0, 0, 2, 3), + (2, 3, 0, 3, 0, 2, 0, 1, 0, 3, 3, 4, 3, 1, 3, 1, 1, 1, 3, 1, 4, 3, 4, 3, 3, 3, 0, 0, 3, 1, 5, 4, 3, 1, 4, 3, 2, 5, 5, 4, 4, 4, 4, 3, 3, 4, 4, 4, 0, 2, 1, 1, 3, 2, 0, 1, 2, 0, 0, 1, 0, 4, 1, 3, 3, 3, 0, 3, 0, 1, 0, 4, 4, 4, 5, 5, 3, 0, 2, 0, 0, 4, 4), + (0, 2, 0, 1, 0, 3, 1, 3, 0, 2, 3, 3, 3, 0, 3, 1, 0, 0, 3, 0, 3, 2, 3, 1, 3, 2, 1, 1, 0, 0, 4, 2, 1, 0, 2, 3, 1, 4, 3, 2, 0, 4, 4, 3, 1, 3, 1, 3, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 4, 1, 1, 1, 2, 0, 3, 0, 0, 0, 3, 4, 2, 4, 3, 2, 0, 1, 0, 0, 3, 3), + (0, 1, 0, 4, 0, 5, 0, 4, 0, 2, 4, 4, 2, 3, 3, 2, 3, 3, 5, 3, 3, 3, 4, 3, 4, 2, 3, 0, 4, 3, 3, 3, 4, 1, 4, 3, 2, 1, 5, 5, 3, 4, 5, 1, 3, 5, 4, 2, 0, 3, 3, 0, 1, 3, 0, 4, 2, 0, 1, 3, 1, 4, 3, 3, 3, 3, 0, 3, 0, 1, 0, 3, 4, 4, 4, 5, 5, 0, 3, 0, 1, 4, 5), + (0, 2, 0, 3, 0, 3, 0, 0, 0, 2, 3, 1, 3, 0, 4, 0, 1, 1, 3, 0, 3, 4, 3, 2, 3, 1, 0, 3, 3, 2, 3, 1, 3, 0, 2, 3, 0, 2, 1, 4, 1, 2, 2, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 2, 2, 0, 3, 2, 1, 3, 3, 0, 2, 0, 2, 0, 0, 3, 3, 1, 2, 4, 0, 3, 0, 2, 2, 3), + (2, 4, 0, 5, 0, 4, 0, 4, 0, 2, 4, 4, 4, 3, 4, 3, 3, 3, 1, 2, 4, 3, 4, 3, 4, 4, 5, 0, 3, 3, 3, 3, 2, 0, 4, 3, 1, 4, 3, 4, 1, 4, 4, 3, 3, 4, 4, 3, 1, 2, 3, 0, 4, 2, 0, 4, 1, 0, 3, 3, 0, 4, 3, 3, 3, 4, 0, 4, 0, 2, 0, 3, 5, 3, 4, 5, 2, 0, 3, 0, 0, 4, 5), + (0, 3, 0, 4, 0, 1, 0, 1, 0, 1, 3, 2, 2, 1, 3, 0, 3, 0, 2, 0, 2, 0, 3, 0, 2, 0, 0, 0, 1, 0, 1, 1, 0, 0, 3, 1, 0, 0, 0, 4, 0, 3, 1, 0, 2, 1, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 3, 1, 0, 3, 0, 0, 0, 1, 4, 4, 4, 3, 0, 0, 4, 0, 0, 1, 4), + (1, 4, 1, 5, 0, 3, 0, 3, 0, 4, 5, 4, 4, 3, 5, 3, 3, 4, 4, 3, 4, 1, 3, 3, 3, 3, 2, 1, 4, 1, 5, 4, 3, 1, 4, 4, 3, 5, 4, 4, 3, 5, 4, 3, 3, 4, 4, 4, 0, 3, 3, 1, 2, 3, 0, 3, 1, 0, 3, 3, 0, 5, 4, 4, 4, 4, 4, 4, 3, 3, 5, 4, 4, 3, 3, 5, 4, 0, 3, 2, 0, 4, 4), + (0, 2, 0, 3, 0, 1, 0, 0, 0, 1, 3, 3, 3, 2, 4, 1, 3, 0, 3, 1, 3, 0, 2, 2, 1, 1, 0, 0, 2, 0, 4, 3, 1, 0, 4, 3, 0, 4, 4, 4, 1, 4, 3, 1, 1, 3, 3, 1, 0, 2, 0, 0, 1, 3, 0, 0, 0, 0, 2, 0, 0, 4, 3, 2, 4, 3, 5, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 0, 2, 1, 0, 3, 3), + (0, 2, 0, 4, 0, 3, 0, 2, 0, 2, 5, 5, 3, 4, 4, 4, 4, 1, 4, 3, 3, 0, 4, 3, 4, 3, 1, 3, 3, 2, 4, 3, 0, 3, 4, 3, 0, 3, 4, 4, 2, 4, 4, 0, 4, 5, 3, 3, 2, 2, 1, 1, 1, 2, 0, 1, 5, 0, 3, 3, 2, 4, 3, 3, 3, 4, 0, 3, 0, 2, 0, 4, 4, 3, 5, 5, 0, 0, 3, 0, 2, 3, 3), + (0, 3, 0, 4, 0, 3, 0, 1, 0, 3, 4, 3, 3, 1, 3, 3, 3, 0, 3, 1, 3, 0, 4, 3, 3, 1, 1, 0, 3, 0, 3, 3, 0, 0, 4, 4, 0, 1, 5, 4, 3, 3, 5, 0, 3, 3, 4, 3, 0, 2, 0, 1, 1, 1, 0, 1, 3, 0, 1, 2, 1, 3, 3, 2, 3, 3, 0, 3, 0, 1, 0, 1, 3, 3, 4, 4, 1, 0, 1, 2, 2, 1, 3), + (0, 1, 0, 4, 0, 4, 0, 3, 0, 1, 3, 3, 3, 2, 3, 1, 1, 0, 3, 0, 3, 3, 4, 3, 2, 4, 2, 0, 1, 0, 4, 3, 2, 0, 4, 3, 0, 5, 3, 3, 2, 4, 4, 4, 3, 3, 3, 4, 0, 1, 3, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 4, 2, 3, 3, 3, 0, 3, 0, 0, 0, 4, 4, 4, 5, 3, 2, 0, 3, 3, 0, 3, 5), + (0, 2, 0, 3, 0, 0, 0, 3, 0, 1, 3, 0, 2, 0, 0, 0, 1, 0, 3, 1, 1, 3, 3, 0, 0, 3, 0, 0, 3, 0, 2, 3, 1, 0, 3, 1, 0, 3, 3, 2, 0, 4, 2, 2, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 1, 0, 1, 0, 0, 0, 1, 3, 1, 2, 0, 0, 0, 1, 0, 0, 1, 4), + (0, 3, 0, 3, 0, 5, 0, 1, 0, 2, 4, 3, 1, 3, 3, 2, 1, 1, 5, 2, 1, 0, 5, 1, 2, 0, 0, 0, 3, 3, 2, 2, 3, 2, 4, 3, 0, 0, 3, 3, 1, 3, 3, 0, 2, 5, 3, 4, 0, 3, 3, 0, 1, 2, 0, 2, 2, 0, 3, 2, 0, 2, 2, 3, 3, 3, 0, 2, 0, 1, 0, 3, 4, 4, 2, 5, 4, 0, 3, 0, 0, 3, 5), + (0, 3, 0, 3, 0, 3, 0, 1, 0, 3, 3, 3, 3, 0, 3, 0, 2, 0, 2, 1, 1, 0, 2, 0, 1, 0, 0, 0, 2, 1, 0, 0, 1, 0, 3, 2, 0, 0, 3, 3, 1, 2, 3, 1, 0, 3, 3, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 3, 1, 2, 3, 0, 3, 0, 1, 0, 3, 2, 1, 0, 4, 3, 0, 1, 1, 0, 3, 3), + (0, 4, 0, 5, 0, 3, 0, 3, 0, 4, 5, 5, 4, 3, 5, 3, 4, 3, 5, 3, 3, 2, 5, 3, 4, 4, 4, 3, 4, 3, 4, 5, 5, 3, 4, 4, 3, 4, 4, 5, 4, 4, 4, 3, 4, 5, 5, 4, 2, 3, 4, 2, 3, 4, 0, 3, 3, 1, 4, 3, 2, 4, 3, 3, 5, 5, 0, 3, 0, 3, 0, 5, 5, 5, 5, 4, 4, 0, 4, 0, 1, 4, 4), + (0, 4, 0, 4, 0, 3, 0, 3, 0, 3, 5, 4, 4, 2, 3, 2, 5, 1, 3, 2, 5, 1, 4, 2, 3, 2, 3, 3, 4, 3, 3, 3, 3, 2, 5, 4, 1, 3, 3, 5, 3, 4, 4, 0, 4, 4, 3, 1, 1, 3, 1, 0, 2, 3, 0, 2, 3, 0, 3, 0, 0, 4, 3, 1, 3, 4, 0, 3, 0, 2, 0, 4, 4, 4, 3, 4, 5, 0, 4, 0, 0, 3, 4), + (0, 3, 0, 3, 0, 3, 1, 2, 0, 3, 4, 4, 3, 3, 3, 0, 2, 2, 4, 3, 3, 1, 3, 3, 3, 1, 1, 0, 3, 1, 4, 3, 2, 3, 4, 4, 2, 4, 4, 4, 3, 4, 4, 3, 2, 4, 4, 3, 1, 3, 3, 1, 3, 3, 0, 4, 1, 0, 2, 2, 1, 4, 3, 2, 3, 3, 5, 4, 3, 3, 5, 4, 4, 3, 3, 0, 4, 0, 3, 2, 2, 4, 4), + (0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 2, 1, 3, 0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 1, 0, 1, 1, 3, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 0, 3, 4, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1), + (0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 4, 1, 4, 0, 3, 0, 4, 0, 3, 0, 4, 0, 3, 0, 3, 0, 4, 1, 5, 1, 4, 0, 0, 3, 0, 5, 0, 5, 2, 0, 1, 0, 0, 0, 2, 1, 4, 0, 1, 3, 0, 0, 3, 0, 0, 3, 1, 1, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0), + (1, 4, 0, 5, 0, 3, 0, 2, 0, 3, 5, 4, 4, 3, 4, 3, 5, 3, 4, 3, 3, 0, 4, 3, 3, 3, 3, 3, 3, 2, 4, 4, 3, 1, 3, 4, 4, 5, 4, 4, 3, 4, 4, 1, 3, 5, 4, 3, 3, 3, 1, 2, 2, 3, 3, 1, 3, 1, 3, 3, 3, 5, 3, 3, 4, 5, 0, 3, 0, 3, 0, 3, 4, 3, 4, 4, 3, 0, 3, 0, 2, 4, 3), + (0, 1, 0, 4, 0, 0, 0, 0, 0, 1, 4, 0, 4, 1, 4, 2, 4, 0, 3, 0, 1, 0, 1, 0, 0, 0, 0, 0, 2, 0, 3, 1, 1, 1, 0, 3, 0, 0, 0, 1, 2, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 3, 2, 0, 2, 2, 0, 1, 0, 0, 0, 2, 3, 2, 3, 3, 0, 0, 0, 0, 2, 1, 0), + (0, 5, 1, 5, 0, 3, 0, 3, 0, 5, 4, 4, 5, 1, 5, 3, 3, 0, 4, 3, 4, 3, 5, 3, 4, 3, 3, 2, 4, 3, 4, 3, 3, 0, 3, 3, 1, 4, 4, 3, 4, 4, 4, 3, 4, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 3, 1, 1, 3, 3, 2, 4, 5, 3, 3, 5, 0, 4, 0, 3, 0, 4, 4, 3, 5, 3, 3, 0, 3, 4, 0, 4, 3), + (0, 5, 0, 5, 0, 3, 0, 2, 0, 4, 4, 3, 5, 2, 4, 3, 3, 3, 4, 4, 4, 3, 5, 3, 5, 3, 3, 1, 4, 0, 4, 3, 3, 0, 3, 3, 0, 4, 4, 4, 4, 5, 4, 3, 3, 5, 5, 3, 2, 3, 1, 2, 3, 2, 0, 1, 0, 0, 3, 2, 2, 4, 4, 3, 1, 5, 0, 4, 0, 3, 0, 4, 3, 1, 3, 2, 1, 0, 3, 3, 0, 3, 3), + (0, 4, 0, 5, 0, 5, 0, 4, 0, 4, 5, 5, 5, 3, 4, 3, 3, 2, 5, 4, 4, 3, 5, 3, 5, 3, 4, 0, 4, 3, 4, 4, 3, 2, 4, 4, 3, 4, 5, 4, 4, 5, 5, 0, 3, 5, 5, 4, 1, 3, 3, 2, 3, 3, 1, 3, 1, 0, 4, 3, 1, 4, 4, 3, 4, 5, 0, 4, 0, 2, 0, 4, 3, 4, 4, 3, 3, 0, 4, 0, 0, 5, 5), + (0, 4, 0, 4, 0, 5, 0, 1, 1, 3, 3, 4, 4, 3, 4, 1, 3, 0, 5, 1, 3, 0, 3, 1, 3, 1, 1, 0, 3, 0, 3, 3, 4, 0, 4, 3, 0, 4, 4, 4, 3, 4, 4, 0, 3, 5, 4, 1, 0, 3, 0, 0, 2, 3, 0, 3, 1, 0, 3, 1, 0, 3, 2, 1, 3, 5, 0, 3, 0, 1, 0, 3, 2, 3, 3, 4, 4, 0, 2, 2, 0, 4, 4), + (2, 4, 0, 5, 0, 4, 0, 3, 0, 4, 5, 5, 4, 3, 5, 3, 5, 3, 5, 3, 5, 2, 5, 3, 4, 3, 3, 4, 3, 4, 5, 3, 2, 1, 5, 4, 3, 2, 3, 4, 5, 3, 4, 1, 2, 5, 4, 3, 0, 3, 3, 0, 3, 2, 0, 2, 3, 0, 4, 1, 0, 3, 4, 3, 3, 5, 0, 3, 0, 1, 0, 4, 5, 5, 5, 4, 3, 0, 4, 2, 0, 3, 5), + (0, 5, 0, 4, 0, 4, 0, 2, 0, 5, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 2, 5, 3, 5, 3, 4, 1, 4, 3, 4, 4, 4, 0, 3, 5, 0, 4, 4, 4, 4, 5, 3, 1, 3, 4, 5, 3, 3, 3, 3, 3, 3, 3, 0, 2, 2, 0, 3, 3, 2, 4, 3, 3, 3, 5, 3, 4, 1, 3, 3, 5, 3, 2, 0, 0, 0, 0, 4, 3, 1, 3, 3), + (0, 1, 0, 3, 0, 3, 0, 1, 0, 1, 3, 3, 3, 2, 3, 3, 3, 0, 3, 0, 0, 0, 3, 1, 3, 0, 0, 0, 2, 2, 2, 3, 0, 0, 3, 2, 0, 1, 2, 4, 1, 3, 3, 0, 0, 3, 3, 3, 0, 1, 0, 0, 2, 1, 0, 0, 3, 0, 3, 1, 0, 3, 0, 0, 1, 3, 0, 2, 0, 1, 0, 3, 3, 1, 3, 3, 0, 0, 1, 1, 0, 3, 3), + (0, 2, 0, 3, 0, 2, 1, 4, 0, 2, 2, 3, 1, 1, 3, 1, 1, 0, 2, 0, 3, 1, 2, 3, 1, 3, 0, 0, 1, 0, 4, 3, 2, 3, 3, 3, 1, 4, 2, 3, 3, 3, 3, 1, 0, 3, 1, 4, 0, 1, 1, 0, 1, 2, 0, 1, 1, 0, 1, 1, 0, 3, 1, 3, 2, 2, 0, 1, 0, 0, 0, 2, 3, 3, 3, 1, 0, 0, 0, 0, 0, 2, 3), + (0, 5, 0, 4, 0, 5, 0, 2, 0, 4, 5, 5, 3, 3, 4, 3, 3, 1, 5, 4, 4, 2, 4, 4, 4, 3, 4, 2, 4, 3, 5, 5, 4, 3, 3, 4, 3, 3, 5, 5, 4, 5, 5, 1, 3, 4, 5, 3, 1, 4, 3, 1, 3, 3, 0, 3, 3, 1, 4, 3, 1, 4, 5, 3, 3, 5, 0, 4, 0, 3, 0, 5, 3, 3, 1, 4, 3, 0, 4, 0, 1, 5, 3), + (0, 5, 0, 5, 0, 4, 0, 2, 0, 4, 4, 3, 4, 3, 3, 3, 3, 3, 5, 4, 4, 4, 4, 4, 4, 5, 3, 3, 5, 2, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4, 5, 5, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 3, 3, 1, 2, 2, 1, 4, 3, 3, 5, 4, 4, 3, 4, 0, 4, 0, 3, 0, 4, 4, 4, 4, 4, 1, 0, 4, 2, 0, 2, 4), + (0, 4, 0, 4, 0, 3, 0, 1, 0, 3, 5, 2, 3, 0, 3, 0, 2, 1, 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 1, 3, 3, 3, 0, 3, 3, 0, 0, 3, 3, 3, 5, 3, 3, 3, 3, 3, 2, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 1, 0, 0, 3, 1, 2, 2, 3, 0, 3, 0, 2, 0, 4, 4, 3, 3, 4, 1, 0, 3, 0, 0, 2, 4), + (0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 3, 1, 3, 0, 3, 2, 0, 0, 0, 1, 0, 3, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 2, 0, 0, 0, 0, 0, 0, 2), + (0, 2, 1, 3, 0, 2, 0, 2, 0, 3, 3, 3, 3, 1, 3, 1, 3, 3, 3, 3, 3, 3, 4, 2, 2, 1, 2, 1, 4, 0, 4, 3, 1, 3, 3, 3, 2, 4, 3, 5, 4, 3, 3, 3, 3, 3, 3, 3, 0, 1, 3, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 4, 2, 0, 2, 3, 0, 3, 3, 0, 3, 3, 4, 2, 3, 1, 4, 0, 1, 2, 0, 2, 3), + (0, 3, 0, 3, 0, 1, 0, 3, 0, 2, 3, 3, 3, 0, 3, 1, 2, 0, 3, 3, 2, 3, 3, 2, 3, 2, 3, 1, 3, 0, 4, 3, 2, 0, 3, 3, 1, 4, 3, 3, 2, 3, 4, 3, 1, 3, 3, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 4, 1, 1, 0, 3, 0, 3, 1, 0, 2, 3, 3, 3, 3, 3, 1, 0, 0, 2, 0, 3, 3), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3), + (0, 2, 0, 3, 1, 3, 0, 3, 0, 2, 3, 3, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 1, 3, 0, 2, 3, 1, 1, 4, 3, 3, 2, 3, 3, 1, 2, 2, 4, 1, 3, 3, 0, 1, 4, 2, 3, 0, 1, 3, 0, 3, 0, 0, 1, 3, 0, 2, 0, 0, 3, 3, 2, 1, 3, 0, 3, 0, 2, 0, 3, 4, 4, 4, 3, 1, 0, 3, 0, 0, 3, 3), + (0, 2, 0, 1, 0, 2, 0, 0, 0, 1, 3, 2, 2, 1, 3, 0, 1, 1, 3, 0, 3, 2, 3, 1, 2, 0, 2, 0, 1, 1, 3, 3, 3, 0, 3, 3, 1, 1, 2, 3, 2, 3, 3, 1, 2, 3, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 2, 1, 2, 1, 3, 0, 3, 0, 0, 0, 3, 4, 4, 4, 3, 2, 0, 2, 0, 0, 2, 4), + (0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 3), + (0, 3, 0, 3, 0, 2, 0, 3, 0, 3, 3, 3, 2, 3, 2, 2, 2, 0, 3, 1, 3, 3, 3, 2, 3, 3, 0, 0, 3, 0, 3, 2, 2, 0, 2, 3, 1, 4, 3, 4, 3, 3, 2, 3, 1, 5, 4, 4, 0, 3, 1, 2, 1, 3, 0, 3, 1, 1, 2, 0, 2, 3, 1, 3, 1, 3, 0, 3, 0, 1, 0, 3, 3, 4, 4, 2, 1, 0, 2, 1, 0, 2, 4), + (0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 4, 2, 5, 1, 4, 0, 2, 0, 2, 1, 3, 1, 4, 0, 2, 1, 0, 0, 2, 1, 4, 1, 1, 0, 3, 3, 0, 5, 1, 3, 2, 3, 3, 1, 0, 3, 2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 1, 0, 3, 0, 2, 0, 1, 0, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 2, 3), + (0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 3), + (0, 1, 0, 3, 0, 4, 0, 3, 0, 2, 4, 3, 1, 0, 3, 2, 2, 1, 3, 1, 2, 2, 3, 1, 1, 1, 2, 1, 3, 0, 1, 2, 0, 1, 3, 2, 1, 3, 0, 5, 5, 1, 0, 0, 1, 3, 2, 1, 0, 3, 0, 0, 1, 0, 0, 0, 0, 0, 3, 4, 0, 1, 1, 1, 3, 2, 0, 2, 0, 1, 0, 2, 3, 3, 1, 2, 3, 0, 1, 0, 1, 0, 4), + (0, 0, 0, 1, 0, 3, 0, 3, 0, 2, 2, 1, 0, 0, 4, 0, 3, 0, 3, 1, 3, 0, 3, 0, 3, 0, 1, 0, 3, 0, 3, 1, 3, 0, 3, 3, 0, 0, 1, 2, 1, 1, 1, 0, 1, 2, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 2, 0, 0, 2, 0, 0, 0, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, 1, 4), + (0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, 1, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 0, 2, 3, 0, 0, 2, 2, 3, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0, 2, 3), + (2, 4, 0, 5, 0, 5, 0, 4, 0, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 4, 5, 4, 5, 5, 5, 2, 3, 0, 5, 5, 4, 1, 5, 4, 3, 1, 5, 4, 3, 4, 4, 3, 3, 4, 3, 3, 0, 3, 2, 0, 2, 3, 0, 3, 0, 0, 3, 3, 0, 5, 3, 2, 3, 3, 0, 3, 0, 3, 0, 3, 4, 5, 4, 5, 3, 0, 4, 3, 0, 3, 4), + (0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 3, 4, 3, 2, 3, 2, 3, 0, 4, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 2, 4, 3, 3, 1, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 2, 4, 4, 1, 0, 2, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0, 5, 3, 2, 1, 3, 0, 3, 0, 1, 2, 4, 3, 2, 4, 3, 3, 0, 3, 2, 0, 4, 4), + (0, 3, 0, 3, 0, 1, 0, 0, 0, 1, 4, 3, 3, 2, 3, 1, 3, 1, 4, 2, 3, 2, 4, 2, 3, 4, 3, 0, 2, 2, 3, 3, 3, 0, 3, 3, 3, 0, 3, 4, 1, 3, 3, 0, 3, 4, 3, 3, 0, 1, 1, 0, 1, 0, 0, 0, 4, 0, 3, 0, 0, 3, 1, 2, 1, 3, 0, 4, 0, 1, 0, 4, 3, 3, 4, 3, 3, 0, 2, 0, 0, 3, 3), + (0, 3, 0, 4, 0, 1, 0, 3, 0, 3, 4, 3, 3, 0, 3, 3, 3, 1, 3, 1, 3, 3, 4, 3, 3, 3, 0, 0, 3, 1, 5, 3, 3, 1, 3, 3, 2, 5, 4, 3, 3, 4, 5, 3, 2, 5, 3, 4, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 0, 4, 2, 2, 1, 3, 0, 3, 0, 2, 0, 4, 4, 3, 5, 3, 2, 0, 1, 1, 0, 3, 4), + (0, 5, 0, 4, 0, 5, 0, 2, 0, 4, 4, 3, 3, 2, 3, 3, 3, 1, 4, 3, 4, 1, 5, 3, 4, 3, 4, 0, 4, 2, 4, 3, 4, 1, 5, 4, 0, 4, 4, 4, 4, 5, 4, 1, 3, 5, 4, 2, 1, 4, 1, 1, 3, 2, 0, 3, 1, 0, 3, 2, 1, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 4, 4, 3, 3, 3, 0, 4, 2, 0, 3, 4), + (1, 4, 0, 4, 0, 3, 0, 1, 0, 3, 3, 3, 1, 1, 3, 3, 2, 2, 3, 3, 1, 0, 3, 2, 2, 1, 2, 0, 3, 1, 2, 1, 2, 0, 3, 2, 0, 2, 2, 3, 3, 4, 3, 0, 3, 3, 1, 2, 0, 1, 1, 3, 1, 2, 0, 0, 3, 0, 1, 1, 0, 3, 2, 2, 3, 3, 0, 3, 0, 0, 0, 2, 3, 3, 4, 3, 3, 0, 1, 0, 0, 1, 4), + (0, 4, 0, 4, 0, 4, 0, 0, 0, 3, 4, 4, 3, 1, 4, 2, 3, 2, 3, 3, 3, 1, 4, 3, 4, 0, 3, 0, 4, 2, 3, 3, 2, 2, 5, 4, 2, 1, 3, 4, 3, 4, 3, 1, 3, 3, 4, 2, 0, 2, 1, 0, 3, 3, 0, 0, 2, 0, 3, 1, 0, 4, 4, 3, 4, 3, 0, 4, 0, 1, 0, 2, 4, 4, 4, 4, 4, 0, 3, 2, 0, 3, 3), + (0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2), + (0, 2, 0, 3, 0, 4, 0, 4, 0, 1, 3, 3, 3, 0, 4, 0, 2, 1, 2, 1, 1, 1, 2, 0, 3, 1, 1, 0, 1, 0, 3, 1, 0, 0, 3, 3, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 2, 2, 0, 3, 1, 0, 0, 1, 0, 1, 1, 0, 1, 2, 0, 3, 0, 0, 0, 0, 1, 0, 0, 3, 3, 4, 3, 1, 0, 1, 0, 3, 0, 2), + (0, 0, 0, 3, 0, 5, 0, 0, 0, 0, 1, 0, 2, 0, 3, 1, 0, 1, 3, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 4, 0, 0, 0, 2, 3, 0, 1, 4, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 3), + (0, 2, 0, 5, 0, 5, 0, 1, 0, 2, 4, 3, 3, 2, 5, 1, 3, 2, 3, 3, 3, 0, 4, 1, 2, 0, 3, 0, 4, 0, 2, 2, 1, 1, 5, 3, 0, 0, 1, 4, 2, 3, 2, 0, 3, 3, 3, 2, 0, 2, 4, 1, 1, 2, 0, 1, 1, 0, 3, 1, 0, 1, 3, 1, 2, 3, 0, 2, 0, 0, 0, 1, 3, 5, 4, 4, 4, 0, 3, 0, 0, 1, 3), + (0, 4, 0, 5, 0, 4, 0, 4, 0, 4, 5, 4, 3, 3, 4, 3, 3, 3, 4, 3, 4, 4, 5, 3, 4, 5, 4, 2, 4, 2, 3, 4, 3, 1, 4, 4, 1, 3, 5, 4, 4, 5, 5, 4, 4, 5, 5, 5, 2, 3, 3, 1, 4, 3, 1, 3, 3, 0, 3, 3, 1, 4, 3, 4, 4, 4, 0, 3, 0, 4, 0, 3, 3, 4, 4, 5, 0, 0, 4, 3, 0, 4, 5), + (0, 4, 0, 4, 0, 3, 0, 3, 0, 3, 4, 4, 4, 3, 3, 2, 4, 3, 4, 3, 4, 3, 5, 3, 4, 3, 2, 1, 4, 2, 4, 4, 3, 1, 3, 4, 2, 4, 5, 5, 3, 4, 5, 4, 1, 5, 4, 3, 0, 3, 2, 2, 3, 2, 1, 3, 1, 0, 3, 3, 3, 5, 3, 3, 3, 5, 4, 4, 2, 3, 3, 4, 3, 3, 3, 2, 1, 0, 3, 2, 1, 4, 3), + (0, 4, 0, 5, 0, 4, 0, 3, 0, 3, 5, 5, 3, 2, 4, 3, 4, 0, 5, 4, 4, 1, 4, 4, 4, 3, 3, 3, 4, 3, 5, 5, 2, 3, 3, 4, 1, 2, 5, 5, 3, 5, 5, 2, 3, 5, 5, 4, 0, 3, 2, 0, 3, 3, 1, 1, 5, 1, 4, 1, 0, 4, 3, 2, 3, 5, 0, 4, 0, 3, 0, 5, 4, 3, 4, 3, 0, 0, 4, 1, 0, 4, 4), + (1, 3, 0, 4, 0, 2, 0, 2, 0, 2, 5, 5, 3, 3, 3, 3, 3, 0, 4, 2, 3, 4, 4, 4, 3, 4, 0, 0, 3, 4, 5, 4, 3, 3, 3, 3, 2, 5, 5, 4, 5, 5, 5, 4, 3, 5, 5, 5, 1, 3, 1, 0, 1, 0, 0, 3, 2, 0, 4, 2, 0, 5, 2, 3, 2, 4, 1, 3, 0, 3, 0, 4, 5, 4, 5, 4, 3, 0, 4, 2, 0, 5, 4), + (0, 3, 0, 4, 0, 5, 0, 3, 0, 3, 4, 4, 3, 2, 3, 2, 3, 3, 3, 3, 3, 2, 4, 3, 3, 2, 2, 0, 3, 3, 3, 3, 3, 1, 3, 3, 3, 0, 4, 4, 3, 4, 4, 1, 1, 4, 4, 2, 0, 3, 1, 0, 1, 1, 0, 4, 1, 0, 2, 3, 1, 3, 3, 1, 3, 4, 0, 3, 0, 1, 0, 3, 1, 3, 0, 0, 1, 0, 2, 0, 0, 4, 4), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, 3, 0, 3, 0, 2, 0, 3, 0, 1, 5, 4, 3, 3, 3, 1, 4, 2, 1, 2, 3, 4, 4, 2, 4, 4, 5, 0, 3, 1, 4, 3, 4, 0, 4, 3, 3, 3, 2, 3, 2, 5, 3, 4, 3, 2, 2, 3, 0, 0, 3, 0, 2, 1, 0, 1, 2, 0, 0, 0, 0, 2, 1, 1, 3, 1, 0, 2, 0, 4, 0, 3, 4, 4, 4, 5, 2, 0, 2, 0, 0, 1, 3), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 4, 2, 1, 1, 0, 1, 0, 3, 2, 0, 0, 3, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 4, 0, 4, 2, 1, 0, 0, 0, 0, 0, 1), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 2, 0, 2, 1, 0, 0, 1, 2, 1, 0, 1, 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2), + (0, 4, 0, 4, 0, 4, 0, 3, 0, 4, 4, 3, 4, 2, 4, 3, 2, 0, 4, 4, 4, 3, 5, 3, 5, 3, 3, 2, 4, 2, 4, 3, 4, 3, 1, 4, 0, 2, 3, 4, 4, 4, 3, 3, 3, 4, 4, 4, 3, 4, 1, 3, 4, 3, 2, 1, 2, 1, 3, 3, 3, 4, 4, 3, 3, 5, 0, 4, 0, 3, 0, 4, 3, 3, 3, 2, 1, 0, 3, 0, 0, 3, 3), + (0, 4, 0, 3, 0, 3, 0, 3, 0, 3, 5, 5, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 4, 3, 5, 3, 3, 1, 3, 2, 4, 5, 5, 5, 5, 4, 3, 4, 5, 5, 3, 2, 2, 3, 3, 3, 3, 2, 3, 3, 1, 2, 3, 2, 4, 3, 3, 3, 4, 0, 4, 0, 2, 0, 4, 3, 2, 2, 1, 2, 0, 3, 0, 0, 4, 1), ) -class JapaneseContextAnalysis(object): + +class JapaneseContextAnalysis: NUM_OF_CATEGORY = 6 DONT_KNOW = -1 ENOUGH_REL_THRESHOLD = 100 @@ -180,10 +180,11 @@ class JapaneseContextAnalysis(object): def get_order(self, byte_str): return -1, 1 + class SJISContextAnalysis(JapaneseContextAnalysis): def __init__(self): - super(SJISContextAnalysis, self).__init__() - self._charset_name = "SHIFT_JIS" + super().__init__() + self._charset_name = 'SHIFT_JIS' @property def charset_name(self): @@ -197,7 +198,7 @@ class SJISContextAnalysis(JapaneseContextAnalysis): if (0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC): char_len = 2 if (first_char == 0x87) or (0xFA <= first_char <= 0xFC): - self._charset_name = "CP932" + self._charset_name = 'CP932' else: char_len = 1 @@ -209,6 +210,7 @@ class SJISContextAnalysis(JapaneseContextAnalysis): return -1, char_len + class EUCJPContextAnalysis(JapaneseContextAnalysis): def get_order(self, byte_str): if not byte_str: @@ -229,5 +231,3 @@ class EUCJPContextAnalysis(JapaneseContextAnalysis): return second_char - 0xA1, char_len return -1, char_len - - diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langbulgarianmodel.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langbulgarianmodel.py index e963a50..2a964a6 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langbulgarianmodel.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langbulgarianmodel.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +from __future__ import annotations from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel @@ -4115,536 +4115,539 @@ BULGARIAN_LANG_MODEL = { # Character Mapping Table(s): ISO_8859_5_BULGARIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 77, # 'A' - 66: 90, # 'B' - 67: 99, # 'C' - 68: 100, # 'D' - 69: 72, # 'E' - 70: 109, # 'F' - 71: 107, # 'G' - 72: 101, # 'H' - 73: 79, # 'I' - 74: 185, # 'J' - 75: 81, # 'K' - 76: 102, # 'L' - 77: 76, # 'M' - 78: 94, # 'N' - 79: 82, # 'O' - 80: 110, # 'P' - 81: 186, # 'Q' - 82: 108, # 'R' - 83: 91, # 'S' - 84: 74, # 'T' - 85: 119, # 'U' - 86: 84, # 'V' - 87: 96, # 'W' - 88: 111, # 'X' - 89: 187, # 'Y' - 90: 115, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 65, # 'a' - 98: 69, # 'b' - 99: 70, # 'c' - 100: 66, # 'd' - 101: 63, # 'e' - 102: 68, # 'f' - 103: 112, # 'g' - 104: 103, # 'h' - 105: 92, # 'i' - 106: 194, # 'j' - 107: 104, # 'k' - 108: 95, # 'l' - 109: 86, # 'm' - 110: 87, # 'n' - 111: 71, # 'o' - 112: 116, # 'p' - 113: 195, # 'q' - 114: 85, # 'r' - 115: 93, # 's' - 116: 97, # 't' - 117: 113, # 'u' - 118: 196, # 'v' - 119: 197, # 'w' - 120: 198, # 'x' - 121: 199, # 'y' - 122: 200, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 194, # '\x80' - 129: 195, # '\x81' - 130: 196, # '\x82' - 131: 197, # '\x83' - 132: 198, # '\x84' - 133: 199, # '\x85' - 134: 200, # '\x86' - 135: 201, # '\x87' - 136: 202, # '\x88' - 137: 203, # '\x89' - 138: 204, # '\x8a' - 139: 205, # '\x8b' - 140: 206, # '\x8c' - 141: 207, # '\x8d' - 142: 208, # '\x8e' - 143: 209, # '\x8f' - 144: 210, # '\x90' - 145: 211, # '\x91' - 146: 212, # '\x92' - 147: 213, # '\x93' - 148: 214, # '\x94' - 149: 215, # '\x95' - 150: 216, # '\x96' - 151: 217, # '\x97' - 152: 218, # '\x98' - 153: 219, # '\x99' - 154: 220, # '\x9a' - 155: 221, # '\x9b' - 156: 222, # '\x9c' - 157: 223, # '\x9d' - 158: 224, # '\x9e' - 159: 225, # '\x9f' - 160: 81, # '\xa0' - 161: 226, # 'Ё' - 162: 227, # 'Ђ' - 163: 228, # 'Ѓ' - 164: 229, # 'Є' - 165: 230, # 'Ѕ' - 166: 105, # 'І' - 167: 231, # 'Ї' - 168: 232, # 'Ј' - 169: 233, # 'Љ' - 170: 234, # 'Њ' - 171: 235, # 'Ћ' - 172: 236, # 'Ќ' - 173: 45, # '\xad' - 174: 237, # 'Ў' - 175: 238, # 'Џ' - 176: 31, # 'А' - 177: 32, # 'Б' - 178: 35, # 'В' - 179: 43, # 'Г' - 180: 37, # 'Д' - 181: 44, # 'Е' - 182: 55, # 'Ж' - 183: 47, # 'З' - 184: 40, # 'И' - 185: 59, # 'Й' - 186: 33, # 'К' - 187: 46, # 'Л' - 188: 38, # 'М' - 189: 36, # 'Н' - 190: 41, # 'О' - 191: 30, # 'П' - 192: 39, # 'Р' - 193: 28, # 'С' - 194: 34, # 'Т' - 195: 51, # 'У' - 196: 48, # 'Ф' - 197: 49, # 'Х' - 198: 53, # 'Ц' - 199: 50, # 'Ч' - 200: 54, # 'Ш' - 201: 57, # 'Щ' - 202: 61, # 'Ъ' - 203: 239, # 'Ы' - 204: 67, # 'Ь' - 205: 240, # 'Э' - 206: 60, # 'Ю' - 207: 56, # 'Я' - 208: 1, # 'а' - 209: 18, # 'б' - 210: 9, # 'в' - 211: 20, # 'г' - 212: 11, # 'д' - 213: 3, # 'е' - 214: 23, # 'ж' - 215: 15, # 'з' - 216: 2, # 'и' - 217: 26, # 'й' - 218: 12, # 'к' - 219: 10, # 'л' - 220: 14, # 'м' - 221: 6, # 'н' - 222: 4, # 'о' - 223: 13, # 'п' - 224: 7, # 'р' - 225: 8, # 'с' - 226: 5, # 'т' - 227: 19, # 'у' - 228: 29, # 'ф' - 229: 25, # 'х' - 230: 22, # 'ц' - 231: 21, # 'ч' - 232: 27, # 'ш' - 233: 24, # 'щ' - 234: 17, # 'ъ' - 235: 75, # 'ы' - 236: 52, # 'ь' - 237: 241, # 'э' - 238: 42, # 'ю' - 239: 16, # 'я' - 240: 62, # '№' - 241: 242, # 'ё' - 242: 243, # 'ђ' - 243: 244, # 'ѓ' - 244: 58, # 'є' - 245: 245, # 'ѕ' - 246: 98, # 'і' - 247: 246, # 'ї' - 248: 247, # 'ј' - 249: 248, # 'љ' - 250: 249, # 'њ' - 251: 250, # 'ћ' - 252: 251, # 'ќ' - 253: 91, # '§' - 254: 252, # 'ў' - 255: 253, # 'џ' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 77, # 'A' + 66: 90, # 'B' + 67: 99, # 'C' + 68: 100, # 'D' + 69: 72, # 'E' + 70: 109, # 'F' + 71: 107, # 'G' + 72: 101, # 'H' + 73: 79, # 'I' + 74: 185, # 'J' + 75: 81, # 'K' + 76: 102, # 'L' + 77: 76, # 'M' + 78: 94, # 'N' + 79: 82, # 'O' + 80: 110, # 'P' + 81: 186, # 'Q' + 82: 108, # 'R' + 83: 91, # 'S' + 84: 74, # 'T' + 85: 119, # 'U' + 86: 84, # 'V' + 87: 96, # 'W' + 88: 111, # 'X' + 89: 187, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 65, # 'a' + 98: 69, # 'b' + 99: 70, # 'c' + 100: 66, # 'd' + 101: 63, # 'e' + 102: 68, # 'f' + 103: 112, # 'g' + 104: 103, # 'h' + 105: 92, # 'i' + 106: 194, # 'j' + 107: 104, # 'k' + 108: 95, # 'l' + 109: 86, # 'm' + 110: 87, # 'n' + 111: 71, # 'o' + 112: 116, # 'p' + 113: 195, # 'q' + 114: 85, # 'r' + 115: 93, # 's' + 116: 97, # 't' + 117: 113, # 'u' + 118: 196, # 'v' + 119: 197, # 'w' + 120: 198, # 'x' + 121: 199, # 'y' + 122: 200, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 194, # '\x80' + 129: 195, # '\x81' + 130: 196, # '\x82' + 131: 197, # '\x83' + 132: 198, # '\x84' + 133: 199, # '\x85' + 134: 200, # '\x86' + 135: 201, # '\x87' + 136: 202, # '\x88' + 137: 203, # '\x89' + 138: 204, # '\x8a' + 139: 205, # '\x8b' + 140: 206, # '\x8c' + 141: 207, # '\x8d' + 142: 208, # '\x8e' + 143: 209, # '\x8f' + 144: 210, # '\x90' + 145: 211, # '\x91' + 146: 212, # '\x92' + 147: 213, # '\x93' + 148: 214, # '\x94' + 149: 215, # '\x95' + 150: 216, # '\x96' + 151: 217, # '\x97' + 152: 218, # '\x98' + 153: 219, # '\x99' + 154: 220, # '\x9a' + 155: 221, # '\x9b' + 156: 222, # '\x9c' + 157: 223, # '\x9d' + 158: 224, # '\x9e' + 159: 225, # '\x9f' + 160: 81, # '\xa0' + 161: 226, # 'Ё' + 162: 227, # 'Ђ' + 163: 228, # 'Ѓ' + 164: 229, # 'Є' + 165: 230, # 'Ѕ' + 166: 105, # 'І' + 167: 231, # 'Ї' + 168: 232, # 'Ј' + 169: 233, # 'Љ' + 170: 234, # 'Њ' + 171: 235, # 'Ћ' + 172: 236, # 'Ќ' + 173: 45, # '\xad' + 174: 237, # 'Ў' + 175: 238, # 'Џ' + 176: 31, # 'А' + 177: 32, # 'Б' + 178: 35, # 'В' + 179: 43, # 'Г' + 180: 37, # 'Д' + 181: 44, # 'Е' + 182: 55, # 'Ж' + 183: 47, # 'З' + 184: 40, # 'И' + 185: 59, # 'Й' + 186: 33, # 'К' + 187: 46, # 'Л' + 188: 38, # 'М' + 189: 36, # 'Н' + 190: 41, # 'О' + 191: 30, # 'П' + 192: 39, # 'Р' + 193: 28, # 'С' + 194: 34, # 'Т' + 195: 51, # 'У' + 196: 48, # 'Ф' + 197: 49, # 'Х' + 198: 53, # 'Ц' + 199: 50, # 'Ч' + 200: 54, # 'Ш' + 201: 57, # 'Щ' + 202: 61, # 'Ъ' + 203: 239, # 'Ы' + 204: 67, # 'Ь' + 205: 240, # 'Э' + 206: 60, # 'Ю' + 207: 56, # 'Я' + 208: 1, # 'а' + 209: 18, # 'б' + 210: 9, # 'в' + 211: 20, # 'г' + 212: 11, # 'д' + 213: 3, # 'е' + 214: 23, # 'ж' + 215: 15, # 'з' + 216: 2, # 'и' + 217: 26, # 'й' + 218: 12, # 'к' + 219: 10, # 'л' + 220: 14, # 'м' + 221: 6, # 'н' + 222: 4, # 'о' + 223: 13, # 'п' + 224: 7, # 'р' + 225: 8, # 'с' + 226: 5, # 'т' + 227: 19, # 'у' + 228: 29, # 'ф' + 229: 25, # 'х' + 230: 22, # 'ц' + 231: 21, # 'ч' + 232: 27, # 'ш' + 233: 24, # 'щ' + 234: 17, # 'ъ' + 235: 75, # 'ы' + 236: 52, # 'ь' + 237: 241, # 'э' + 238: 42, # 'ю' + 239: 16, # 'я' + 240: 62, # '№' + 241: 242, # 'ё' + 242: 243, # 'ђ' + 243: 244, # 'ѓ' + 244: 58, # 'є' + 245: 245, # 'ѕ' + 246: 98, # 'і' + 247: 246, # 'ї' + 248: 247, # 'ј' + 249: 248, # 'љ' + 250: 249, # 'њ' + 251: 250, # 'ћ' + 252: 251, # 'ќ' + 253: 91, # '§' + 254: 252, # 'ў' + 255: 253, # 'џ' } -ISO_8859_5_BULGARIAN_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-5', - language='Bulgarian', - char_to_order_map=ISO_8859_5_BULGARIAN_CHAR_TO_ORDER, - language_model=BULGARIAN_LANG_MODEL, - typical_positive_ratio=0.969392, - keep_ascii_letters=False, - alphabet='АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя') +ISO_8859_5_BULGARIAN_MODEL = SingleByteCharSetModel( + charset_name='ISO-8859-5', + language='Bulgarian', + char_to_order_map=ISO_8859_5_BULGARIAN_CHAR_TO_ORDER, + language_model=BULGARIAN_LANG_MODEL, + typical_positive_ratio=0.969392, + keep_ascii_letters=False, + alphabet='АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя', +) WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 77, # 'A' - 66: 90, # 'B' - 67: 99, # 'C' - 68: 100, # 'D' - 69: 72, # 'E' - 70: 109, # 'F' - 71: 107, # 'G' - 72: 101, # 'H' - 73: 79, # 'I' - 74: 185, # 'J' - 75: 81, # 'K' - 76: 102, # 'L' - 77: 76, # 'M' - 78: 94, # 'N' - 79: 82, # 'O' - 80: 110, # 'P' - 81: 186, # 'Q' - 82: 108, # 'R' - 83: 91, # 'S' - 84: 74, # 'T' - 85: 119, # 'U' - 86: 84, # 'V' - 87: 96, # 'W' - 88: 111, # 'X' - 89: 187, # 'Y' - 90: 115, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 65, # 'a' - 98: 69, # 'b' - 99: 70, # 'c' - 100: 66, # 'd' - 101: 63, # 'e' - 102: 68, # 'f' - 103: 112, # 'g' - 104: 103, # 'h' - 105: 92, # 'i' - 106: 194, # 'j' - 107: 104, # 'k' - 108: 95, # 'l' - 109: 86, # 'm' - 110: 87, # 'n' - 111: 71, # 'o' - 112: 116, # 'p' - 113: 195, # 'q' - 114: 85, # 'r' - 115: 93, # 's' - 116: 97, # 't' - 117: 113, # 'u' - 118: 196, # 'v' - 119: 197, # 'w' - 120: 198, # 'x' - 121: 199, # 'y' - 122: 200, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 206, # 'Ђ' - 129: 207, # 'Ѓ' - 130: 208, # '‚' - 131: 209, # 'ѓ' - 132: 210, # '„' - 133: 211, # '…' - 134: 212, # '†' - 135: 213, # '‡' - 136: 120, # '€' - 137: 214, # '‰' - 138: 215, # 'Љ' - 139: 216, # '‹' - 140: 217, # 'Њ' - 141: 218, # 'Ќ' - 142: 219, # 'Ћ' - 143: 220, # 'Џ' - 144: 221, # 'ђ' - 145: 78, # '‘' - 146: 64, # '’' - 147: 83, # '“' - 148: 121, # '”' - 149: 98, # '•' - 150: 117, # '–' - 151: 105, # '—' - 152: 222, # None - 153: 223, # '™' - 154: 224, # 'љ' - 155: 225, # '›' - 156: 226, # 'њ' - 157: 227, # 'ќ' - 158: 228, # 'ћ' - 159: 229, # 'џ' - 160: 88, # '\xa0' - 161: 230, # 'Ў' - 162: 231, # 'ў' - 163: 232, # 'Ј' - 164: 233, # '¤' - 165: 122, # 'Ґ' - 166: 89, # '¦' - 167: 106, # '§' - 168: 234, # 'Ё' - 169: 235, # '©' - 170: 236, # 'Є' - 171: 237, # '«' - 172: 238, # '¬' - 173: 45, # '\xad' - 174: 239, # '®' - 175: 240, # 'Ї' - 176: 73, # '°' - 177: 80, # '±' - 178: 118, # 'І' - 179: 114, # 'і' - 180: 241, # 'ґ' - 181: 242, # 'µ' - 182: 243, # '¶' - 183: 244, # '·' - 184: 245, # 'ё' - 185: 62, # '№' - 186: 58, # 'є' - 187: 246, # '»' - 188: 247, # 'ј' - 189: 248, # 'Ѕ' - 190: 249, # 'ѕ' - 191: 250, # 'ї' - 192: 31, # 'А' - 193: 32, # 'Б' - 194: 35, # 'В' - 195: 43, # 'Г' - 196: 37, # 'Д' - 197: 44, # 'Е' - 198: 55, # 'Ж' - 199: 47, # 'З' - 200: 40, # 'И' - 201: 59, # 'Й' - 202: 33, # 'К' - 203: 46, # 'Л' - 204: 38, # 'М' - 205: 36, # 'Н' - 206: 41, # 'О' - 207: 30, # 'П' - 208: 39, # 'Р' - 209: 28, # 'С' - 210: 34, # 'Т' - 211: 51, # 'У' - 212: 48, # 'Ф' - 213: 49, # 'Х' - 214: 53, # 'Ц' - 215: 50, # 'Ч' - 216: 54, # 'Ш' - 217: 57, # 'Щ' - 218: 61, # 'Ъ' - 219: 251, # 'Ы' - 220: 67, # 'Ь' - 221: 252, # 'Э' - 222: 60, # 'Ю' - 223: 56, # 'Я' - 224: 1, # 'а' - 225: 18, # 'б' - 226: 9, # 'в' - 227: 20, # 'г' - 228: 11, # 'д' - 229: 3, # 'е' - 230: 23, # 'ж' - 231: 15, # 'з' - 232: 2, # 'и' - 233: 26, # 'й' - 234: 12, # 'к' - 235: 10, # 'л' - 236: 14, # 'м' - 237: 6, # 'н' - 238: 4, # 'о' - 239: 13, # 'п' - 240: 7, # 'р' - 241: 8, # 'с' - 242: 5, # 'т' - 243: 19, # 'у' - 244: 29, # 'ф' - 245: 25, # 'х' - 246: 22, # 'ц' - 247: 21, # 'ч' - 248: 27, # 'ш' - 249: 24, # 'щ' - 250: 17, # 'ъ' - 251: 75, # 'ы' - 252: 52, # 'ь' - 253: 253, # 'э' - 254: 42, # 'ю' - 255: 16, # 'я' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 77, # 'A' + 66: 90, # 'B' + 67: 99, # 'C' + 68: 100, # 'D' + 69: 72, # 'E' + 70: 109, # 'F' + 71: 107, # 'G' + 72: 101, # 'H' + 73: 79, # 'I' + 74: 185, # 'J' + 75: 81, # 'K' + 76: 102, # 'L' + 77: 76, # 'M' + 78: 94, # 'N' + 79: 82, # 'O' + 80: 110, # 'P' + 81: 186, # 'Q' + 82: 108, # 'R' + 83: 91, # 'S' + 84: 74, # 'T' + 85: 119, # 'U' + 86: 84, # 'V' + 87: 96, # 'W' + 88: 111, # 'X' + 89: 187, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 65, # 'a' + 98: 69, # 'b' + 99: 70, # 'c' + 100: 66, # 'd' + 101: 63, # 'e' + 102: 68, # 'f' + 103: 112, # 'g' + 104: 103, # 'h' + 105: 92, # 'i' + 106: 194, # 'j' + 107: 104, # 'k' + 108: 95, # 'l' + 109: 86, # 'm' + 110: 87, # 'n' + 111: 71, # 'o' + 112: 116, # 'p' + 113: 195, # 'q' + 114: 85, # 'r' + 115: 93, # 's' + 116: 97, # 't' + 117: 113, # 'u' + 118: 196, # 'v' + 119: 197, # 'w' + 120: 198, # 'x' + 121: 199, # 'y' + 122: 200, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 206, # 'Ђ' + 129: 207, # 'Ѓ' + 130: 208, # '‚' + 131: 209, # 'ѓ' + 132: 210, # '„' + 133: 211, # '…' + 134: 212, # '†' + 135: 213, # '‡' + 136: 120, # '€' + 137: 214, # '‰' + 138: 215, # 'Љ' + 139: 216, # '‹' + 140: 217, # 'Њ' + 141: 218, # 'Ќ' + 142: 219, # 'Ћ' + 143: 220, # 'Џ' + 144: 221, # 'ђ' + 145: 78, # '‘' + 146: 64, # '’' + 147: 83, # '“' + 148: 121, # '”' + 149: 98, # '•' + 150: 117, # '–' + 151: 105, # '—' + 152: 222, # None + 153: 223, # '™' + 154: 224, # 'љ' + 155: 225, # '›' + 156: 226, # 'њ' + 157: 227, # 'ќ' + 158: 228, # 'ћ' + 159: 229, # 'џ' + 160: 88, # '\xa0' + 161: 230, # 'Ў' + 162: 231, # 'ў' + 163: 232, # 'Ј' + 164: 233, # '¤' + 165: 122, # 'Ґ' + 166: 89, # '¦' + 167: 106, # '§' + 168: 234, # 'Ё' + 169: 235, # '©' + 170: 236, # 'Є' + 171: 237, # '«' + 172: 238, # '¬' + 173: 45, # '\xad' + 174: 239, # '®' + 175: 240, # 'Ї' + 176: 73, # '°' + 177: 80, # '±' + 178: 118, # 'І' + 179: 114, # 'і' + 180: 241, # 'ґ' + 181: 242, # 'µ' + 182: 243, # '¶' + 183: 244, # '·' + 184: 245, # 'ё' + 185: 62, # '№' + 186: 58, # 'є' + 187: 246, # '»' + 188: 247, # 'ј' + 189: 248, # 'Ѕ' + 190: 249, # 'ѕ' + 191: 250, # 'ї' + 192: 31, # 'А' + 193: 32, # 'Б' + 194: 35, # 'В' + 195: 43, # 'Г' + 196: 37, # 'Д' + 197: 44, # 'Е' + 198: 55, # 'Ж' + 199: 47, # 'З' + 200: 40, # 'И' + 201: 59, # 'Й' + 202: 33, # 'К' + 203: 46, # 'Л' + 204: 38, # 'М' + 205: 36, # 'Н' + 206: 41, # 'О' + 207: 30, # 'П' + 208: 39, # 'Р' + 209: 28, # 'С' + 210: 34, # 'Т' + 211: 51, # 'У' + 212: 48, # 'Ф' + 213: 49, # 'Х' + 214: 53, # 'Ц' + 215: 50, # 'Ч' + 216: 54, # 'Ш' + 217: 57, # 'Щ' + 218: 61, # 'Ъ' + 219: 251, # 'Ы' + 220: 67, # 'Ь' + 221: 252, # 'Э' + 222: 60, # 'Ю' + 223: 56, # 'Я' + 224: 1, # 'а' + 225: 18, # 'б' + 226: 9, # 'в' + 227: 20, # 'г' + 228: 11, # 'д' + 229: 3, # 'е' + 230: 23, # 'ж' + 231: 15, # 'з' + 232: 2, # 'и' + 233: 26, # 'й' + 234: 12, # 'к' + 235: 10, # 'л' + 236: 14, # 'м' + 237: 6, # 'н' + 238: 4, # 'о' + 239: 13, # 'п' + 240: 7, # 'р' + 241: 8, # 'с' + 242: 5, # 'т' + 243: 19, # 'у' + 244: 29, # 'ф' + 245: 25, # 'х' + 246: 22, # 'ц' + 247: 21, # 'ч' + 248: 27, # 'ш' + 249: 24, # 'щ' + 250: 17, # 'ъ' + 251: 75, # 'ы' + 252: 52, # 'ь' + 253: 253, # 'э' + 254: 42, # 'ю' + 255: 16, # 'я' } -WINDOWS_1251_BULGARIAN_MODEL = SingleByteCharSetModel(charset_name='windows-1251', - language='Bulgarian', - char_to_order_map=WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER, - language_model=BULGARIAN_LANG_MODEL, - typical_positive_ratio=0.969392, - keep_ascii_letters=False, - alphabet='АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя') - +WINDOWS_1251_BULGARIAN_MODEL = SingleByteCharSetModel( + charset_name='windows-1251', + language='Bulgarian', + char_to_order_map=WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER, + language_model=BULGARIAN_LANG_MODEL, + typical_positive_ratio=0.969392, + keep_ascii_letters=False, + alphabet='АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя', +) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langgreekmodel.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langgreekmodel.py index d99528e..9fa22a2 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langgreekmodel.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langgreekmodel.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +from __future__ import annotations from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel @@ -3863,536 +3863,539 @@ GREEK_LANG_MODEL = { # Character Mapping Table(s): WINDOWS_1253_GREEK_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 82, # 'A' - 66: 100, # 'B' - 67: 104, # 'C' - 68: 94, # 'D' - 69: 98, # 'E' - 70: 101, # 'F' - 71: 116, # 'G' - 72: 102, # 'H' - 73: 111, # 'I' - 74: 187, # 'J' - 75: 117, # 'K' - 76: 92, # 'L' - 77: 88, # 'M' - 78: 113, # 'N' - 79: 85, # 'O' - 80: 79, # 'P' - 81: 118, # 'Q' - 82: 105, # 'R' - 83: 83, # 'S' - 84: 67, # 'T' - 85: 114, # 'U' - 86: 119, # 'V' - 87: 95, # 'W' - 88: 99, # 'X' - 89: 109, # 'Y' - 90: 188, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 72, # 'a' - 98: 70, # 'b' - 99: 80, # 'c' - 100: 81, # 'd' - 101: 60, # 'e' - 102: 96, # 'f' - 103: 93, # 'g' - 104: 89, # 'h' - 105: 68, # 'i' - 106: 120, # 'j' - 107: 97, # 'k' - 108: 77, # 'l' - 109: 86, # 'm' - 110: 69, # 'n' - 111: 55, # 'o' - 112: 78, # 'p' - 113: 115, # 'q' - 114: 65, # 'r' - 115: 66, # 's' - 116: 58, # 't' - 117: 76, # 'u' - 118: 106, # 'v' - 119: 103, # 'w' - 120: 87, # 'x' - 121: 107, # 'y' - 122: 112, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 255, # '€' - 129: 255, # None - 130: 255, # '‚' - 131: 255, # 'ƒ' - 132: 255, # '„' - 133: 255, # '…' - 134: 255, # '†' - 135: 255, # '‡' - 136: 255, # None - 137: 255, # '‰' - 138: 255, # None - 139: 255, # '‹' - 140: 255, # None - 141: 255, # None - 142: 255, # None - 143: 255, # None - 144: 255, # None - 145: 255, # '‘' - 146: 255, # '’' - 147: 255, # '“' - 148: 255, # '”' - 149: 255, # '•' - 150: 255, # '–' - 151: 255, # '—' - 152: 255, # None - 153: 255, # '™' - 154: 255, # None - 155: 255, # '›' - 156: 255, # None - 157: 255, # None - 158: 255, # None - 159: 255, # None - 160: 253, # '\xa0' - 161: 233, # '΅' - 162: 61, # 'Ά' - 163: 253, # '£' - 164: 253, # '¤' - 165: 253, # '¥' - 166: 253, # '¦' - 167: 253, # '§' - 168: 253, # '¨' - 169: 253, # '©' - 170: 253, # None - 171: 253, # '«' - 172: 253, # '¬' - 173: 74, # '\xad' - 174: 253, # '®' - 175: 253, # '―' - 176: 253, # '°' - 177: 253, # '±' - 178: 253, # '²' - 179: 253, # '³' - 180: 247, # '΄' - 181: 253, # 'µ' - 182: 253, # '¶' - 183: 36, # '·' - 184: 46, # 'Έ' - 185: 71, # 'Ή' - 186: 73, # 'Ί' - 187: 253, # '»' - 188: 54, # 'Ό' - 189: 253, # '½' - 190: 108, # 'Ύ' - 191: 123, # 'Ώ' - 192: 110, # 'ΐ' - 193: 31, # 'Α' - 194: 51, # 'Β' - 195: 43, # 'Γ' - 196: 41, # 'Δ' - 197: 34, # 'Ε' - 198: 91, # 'Ζ' - 199: 40, # 'Η' - 200: 52, # 'Θ' - 201: 47, # 'Ι' - 202: 44, # 'Κ' - 203: 53, # 'Λ' - 204: 38, # 'Μ' - 205: 49, # 'Ν' - 206: 59, # 'Ξ' - 207: 39, # 'Ο' - 208: 35, # 'Π' - 209: 48, # 'Ρ' - 210: 250, # None - 211: 37, # 'Σ' - 212: 33, # 'Τ' - 213: 45, # 'Υ' - 214: 56, # 'Φ' - 215: 50, # 'Χ' - 216: 84, # 'Ψ' - 217: 57, # 'Ω' - 218: 120, # 'Ϊ' - 219: 121, # 'Ϋ' - 220: 17, # 'ά' - 221: 18, # 'έ' - 222: 22, # 'ή' - 223: 15, # 'ί' - 224: 124, # 'ΰ' - 225: 1, # 'α' - 226: 29, # 'β' - 227: 20, # 'γ' - 228: 21, # 'δ' - 229: 3, # 'ε' - 230: 32, # 'ζ' - 231: 13, # 'η' - 232: 25, # 'θ' - 233: 5, # 'ι' - 234: 11, # 'κ' - 235: 16, # 'λ' - 236: 10, # 'μ' - 237: 6, # 'ν' - 238: 30, # 'ξ' - 239: 4, # 'ο' - 240: 9, # 'π' - 241: 8, # 'ρ' - 242: 14, # 'ς' - 243: 7, # 'σ' - 244: 2, # 'τ' - 245: 12, # 'υ' - 246: 28, # 'φ' - 247: 23, # 'χ' - 248: 42, # 'ψ' - 249: 24, # 'ω' - 250: 64, # 'ϊ' - 251: 75, # 'ϋ' - 252: 19, # 'ό' - 253: 26, # 'ύ' - 254: 27, # 'ώ' - 255: 253, # None + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '€' + 129: 255, # None + 130: 255, # '‚' + 131: 255, # 'ƒ' + 132: 255, # '„' + 133: 255, # '…' + 134: 255, # '†' + 135: 255, # '‡' + 136: 255, # None + 137: 255, # '‰' + 138: 255, # None + 139: 255, # '‹' + 140: 255, # None + 141: 255, # None + 142: 255, # None + 143: 255, # None + 144: 255, # None + 145: 255, # '‘' + 146: 255, # '’' + 147: 255, # '“' + 148: 255, # '”' + 149: 255, # '•' + 150: 255, # '–' + 151: 255, # '—' + 152: 255, # None + 153: 255, # '™' + 154: 255, # None + 155: 255, # '›' + 156: 255, # None + 157: 255, # None + 158: 255, # None + 159: 255, # None + 160: 253, # '\xa0' + 161: 233, # '΅' + 162: 61, # 'Ά' + 163: 253, # '£' + 164: 253, # '¤' + 165: 253, # '¥' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # None + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # '®' + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 253, # 'µ' + 182: 253, # '¶' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None } -WINDOWS_1253_GREEK_MODEL = SingleByteCharSetModel(charset_name='windows-1253', - language='Greek', - char_to_order_map=WINDOWS_1253_GREEK_CHAR_TO_ORDER, - language_model=GREEK_LANG_MODEL, - typical_positive_ratio=0.982851, - keep_ascii_letters=False, - alphabet='ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ') +WINDOWS_1253_GREEK_MODEL = SingleByteCharSetModel( + charset_name='windows-1253', + language='Greek', + char_to_order_map=WINDOWS_1253_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet='ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ', +) ISO_8859_7_GREEK_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 82, # 'A' - 66: 100, # 'B' - 67: 104, # 'C' - 68: 94, # 'D' - 69: 98, # 'E' - 70: 101, # 'F' - 71: 116, # 'G' - 72: 102, # 'H' - 73: 111, # 'I' - 74: 187, # 'J' - 75: 117, # 'K' - 76: 92, # 'L' - 77: 88, # 'M' - 78: 113, # 'N' - 79: 85, # 'O' - 80: 79, # 'P' - 81: 118, # 'Q' - 82: 105, # 'R' - 83: 83, # 'S' - 84: 67, # 'T' - 85: 114, # 'U' - 86: 119, # 'V' - 87: 95, # 'W' - 88: 99, # 'X' - 89: 109, # 'Y' - 90: 188, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 72, # 'a' - 98: 70, # 'b' - 99: 80, # 'c' - 100: 81, # 'd' - 101: 60, # 'e' - 102: 96, # 'f' - 103: 93, # 'g' - 104: 89, # 'h' - 105: 68, # 'i' - 106: 120, # 'j' - 107: 97, # 'k' - 108: 77, # 'l' - 109: 86, # 'm' - 110: 69, # 'n' - 111: 55, # 'o' - 112: 78, # 'p' - 113: 115, # 'q' - 114: 65, # 'r' - 115: 66, # 's' - 116: 58, # 't' - 117: 76, # 'u' - 118: 106, # 'v' - 119: 103, # 'w' - 120: 87, # 'x' - 121: 107, # 'y' - 122: 112, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 255, # '\x80' - 129: 255, # '\x81' - 130: 255, # '\x82' - 131: 255, # '\x83' - 132: 255, # '\x84' - 133: 255, # '\x85' - 134: 255, # '\x86' - 135: 255, # '\x87' - 136: 255, # '\x88' - 137: 255, # '\x89' - 138: 255, # '\x8a' - 139: 255, # '\x8b' - 140: 255, # '\x8c' - 141: 255, # '\x8d' - 142: 255, # '\x8e' - 143: 255, # '\x8f' - 144: 255, # '\x90' - 145: 255, # '\x91' - 146: 255, # '\x92' - 147: 255, # '\x93' - 148: 255, # '\x94' - 149: 255, # '\x95' - 150: 255, # '\x96' - 151: 255, # '\x97' - 152: 255, # '\x98' - 153: 255, # '\x99' - 154: 255, # '\x9a' - 155: 255, # '\x9b' - 156: 255, # '\x9c' - 157: 255, # '\x9d' - 158: 255, # '\x9e' - 159: 255, # '\x9f' - 160: 253, # '\xa0' - 161: 233, # '‘' - 162: 90, # '’' - 163: 253, # '£' - 164: 253, # '€' - 165: 253, # '₯' - 166: 253, # '¦' - 167: 253, # '§' - 168: 253, # '¨' - 169: 253, # '©' - 170: 253, # 'ͺ' - 171: 253, # '«' - 172: 253, # '¬' - 173: 74, # '\xad' - 174: 253, # None - 175: 253, # '―' - 176: 253, # '°' - 177: 253, # '±' - 178: 253, # '²' - 179: 253, # '³' - 180: 247, # '΄' - 181: 248, # '΅' - 182: 61, # 'Ά' - 183: 36, # '·' - 184: 46, # 'Έ' - 185: 71, # 'Ή' - 186: 73, # 'Ί' - 187: 253, # '»' - 188: 54, # 'Ό' - 189: 253, # '½' - 190: 108, # 'Ύ' - 191: 123, # 'Ώ' - 192: 110, # 'ΐ' - 193: 31, # 'Α' - 194: 51, # 'Β' - 195: 43, # 'Γ' - 196: 41, # 'Δ' - 197: 34, # 'Ε' - 198: 91, # 'Ζ' - 199: 40, # 'Η' - 200: 52, # 'Θ' - 201: 47, # 'Ι' - 202: 44, # 'Κ' - 203: 53, # 'Λ' - 204: 38, # 'Μ' - 205: 49, # 'Ν' - 206: 59, # 'Ξ' - 207: 39, # 'Ο' - 208: 35, # 'Π' - 209: 48, # 'Ρ' - 210: 250, # None - 211: 37, # 'Σ' - 212: 33, # 'Τ' - 213: 45, # 'Υ' - 214: 56, # 'Φ' - 215: 50, # 'Χ' - 216: 84, # 'Ψ' - 217: 57, # 'Ω' - 218: 120, # 'Ϊ' - 219: 121, # 'Ϋ' - 220: 17, # 'ά' - 221: 18, # 'έ' - 222: 22, # 'ή' - 223: 15, # 'ί' - 224: 124, # 'ΰ' - 225: 1, # 'α' - 226: 29, # 'β' - 227: 20, # 'γ' - 228: 21, # 'δ' - 229: 3, # 'ε' - 230: 32, # 'ζ' - 231: 13, # 'η' - 232: 25, # 'θ' - 233: 5, # 'ι' - 234: 11, # 'κ' - 235: 16, # 'λ' - 236: 10, # 'μ' - 237: 6, # 'ν' - 238: 30, # 'ξ' - 239: 4, # 'ο' - 240: 9, # 'π' - 241: 8, # 'ρ' - 242: 14, # 'ς' - 243: 7, # 'σ' - 244: 2, # 'τ' - 245: 12, # 'υ' - 246: 28, # 'φ' - 247: 23, # 'χ' - 248: 42, # 'ψ' - 249: 24, # 'ω' - 250: 64, # 'ϊ' - 251: 75, # 'ϋ' - 252: 19, # 'ό' - 253: 26, # 'ύ' - 254: 27, # 'ώ' - 255: 253, # None + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '\x80' + 129: 255, # '\x81' + 130: 255, # '\x82' + 131: 255, # '\x83' + 132: 255, # '\x84' + 133: 255, # '\x85' + 134: 255, # '\x86' + 135: 255, # '\x87' + 136: 255, # '\x88' + 137: 255, # '\x89' + 138: 255, # '\x8a' + 139: 255, # '\x8b' + 140: 255, # '\x8c' + 141: 255, # '\x8d' + 142: 255, # '\x8e' + 143: 255, # '\x8f' + 144: 255, # '\x90' + 145: 255, # '\x91' + 146: 255, # '\x92' + 147: 255, # '\x93' + 148: 255, # '\x94' + 149: 255, # '\x95' + 150: 255, # '\x96' + 151: 255, # '\x97' + 152: 255, # '\x98' + 153: 255, # '\x99' + 154: 255, # '\x9a' + 155: 255, # '\x9b' + 156: 255, # '\x9c' + 157: 255, # '\x9d' + 158: 255, # '\x9e' + 159: 255, # '\x9f' + 160: 253, # '\xa0' + 161: 233, # '‘' + 162: 90, # '’' + 163: 253, # '£' + 164: 253, # '€' + 165: 253, # '₯' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # 'ͺ' + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # None + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 248, # '΅' + 182: 61, # 'Ά' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None } -ISO_8859_7_GREEK_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-7', - language='Greek', - char_to_order_map=ISO_8859_7_GREEK_CHAR_TO_ORDER, - language_model=GREEK_LANG_MODEL, - typical_positive_ratio=0.982851, - keep_ascii_letters=False, - alphabet='ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ') - +ISO_8859_7_GREEK_MODEL = SingleByteCharSetModel( + charset_name='ISO-8859-7', + language='Greek', + char_to_order_map=ISO_8859_7_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet='ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ', +) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langhebrewmodel.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langhebrewmodel.py index 484c652..6293f41 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langhebrewmodel.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langhebrewmodel.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +from __future__ import annotations from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel @@ -4115,269 +4115,270 @@ HEBREW_LANG_MODEL = { # Character Mapping Table(s): WINDOWS_1255_HEBREW_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 69, # 'A' - 66: 91, # 'B' - 67: 79, # 'C' - 68: 80, # 'D' - 69: 92, # 'E' - 70: 89, # 'F' - 71: 97, # 'G' - 72: 90, # 'H' - 73: 68, # 'I' - 74: 111, # 'J' - 75: 112, # 'K' - 76: 82, # 'L' - 77: 73, # 'M' - 78: 95, # 'N' - 79: 85, # 'O' - 80: 78, # 'P' - 81: 121, # 'Q' - 82: 86, # 'R' - 83: 71, # 'S' - 84: 67, # 'T' - 85: 102, # 'U' - 86: 107, # 'V' - 87: 84, # 'W' - 88: 114, # 'X' - 89: 103, # 'Y' - 90: 115, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 50, # 'a' - 98: 74, # 'b' - 99: 60, # 'c' - 100: 61, # 'd' - 101: 42, # 'e' - 102: 76, # 'f' - 103: 70, # 'g' - 104: 64, # 'h' - 105: 53, # 'i' - 106: 105, # 'j' - 107: 93, # 'k' - 108: 56, # 'l' - 109: 65, # 'm' - 110: 54, # 'n' - 111: 49, # 'o' - 112: 66, # 'p' - 113: 110, # 'q' - 114: 51, # 'r' - 115: 43, # 's' - 116: 44, # 't' - 117: 63, # 'u' - 118: 81, # 'v' - 119: 77, # 'w' - 120: 98, # 'x' - 121: 75, # 'y' - 122: 108, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 124, # '€' - 129: 202, # None - 130: 203, # '‚' - 131: 204, # 'ƒ' - 132: 205, # '„' - 133: 40, # '…' - 134: 58, # '†' - 135: 206, # '‡' - 136: 207, # 'ˆ' - 137: 208, # '‰' - 138: 209, # None - 139: 210, # '‹' - 140: 211, # None - 141: 212, # None - 142: 213, # None - 143: 214, # None - 144: 215, # None - 145: 83, # '‘' - 146: 52, # '’' - 147: 47, # '“' - 148: 46, # '”' - 149: 72, # '•' - 150: 32, # '–' - 151: 94, # '—' - 152: 216, # '˜' - 153: 113, # '™' - 154: 217, # None - 155: 109, # '›' - 156: 218, # None - 157: 219, # None - 158: 220, # None - 159: 221, # None - 160: 34, # '\xa0' - 161: 116, # '¡' - 162: 222, # '¢' - 163: 118, # '£' - 164: 100, # '₪' - 165: 223, # '¥' - 166: 224, # '¦' - 167: 117, # '§' - 168: 119, # '¨' - 169: 104, # '©' - 170: 125, # '×' - 171: 225, # '«' - 172: 226, # '¬' - 173: 87, # '\xad' - 174: 99, # '®' - 175: 227, # '¯' - 176: 106, # '°' - 177: 122, # '±' - 178: 123, # '²' - 179: 228, # '³' - 180: 55, # '´' - 181: 229, # 'µ' - 182: 230, # '¶' - 183: 101, # '·' - 184: 231, # '¸' - 185: 232, # '¹' - 186: 120, # '÷' - 187: 233, # '»' - 188: 48, # '¼' - 189: 39, # '½' - 190: 57, # '¾' - 191: 234, # '¿' - 192: 30, # 'ְ' - 193: 59, # 'ֱ' - 194: 41, # 'ֲ' - 195: 88, # 'ֳ' - 196: 33, # 'ִ' - 197: 37, # 'ֵ' - 198: 36, # 'ֶ' - 199: 31, # 'ַ' - 200: 29, # 'ָ' - 201: 35, # 'ֹ' - 202: 235, # None - 203: 62, # 'ֻ' - 204: 28, # 'ּ' - 205: 236, # 'ֽ' - 206: 126, # '־' - 207: 237, # 'ֿ' - 208: 238, # '׀' - 209: 38, # 'ׁ' - 210: 45, # 'ׂ' - 211: 239, # '׃' - 212: 240, # 'װ' - 213: 241, # 'ױ' - 214: 242, # 'ײ' - 215: 243, # '׳' - 216: 127, # '״' - 217: 244, # None - 218: 245, # None - 219: 246, # None - 220: 247, # None - 221: 248, # None - 222: 249, # None - 223: 250, # None - 224: 9, # 'א' - 225: 8, # 'ב' - 226: 20, # 'ג' - 227: 16, # 'ד' - 228: 3, # 'ה' - 229: 2, # 'ו' - 230: 24, # 'ז' - 231: 14, # 'ח' - 232: 22, # 'ט' - 233: 1, # 'י' - 234: 25, # 'ך' - 235: 15, # 'כ' - 236: 4, # 'ל' - 237: 11, # 'ם' - 238: 6, # 'מ' - 239: 23, # 'ן' - 240: 12, # 'נ' - 241: 19, # 'ס' - 242: 13, # 'ע' - 243: 26, # 'ף' - 244: 18, # 'פ' - 245: 27, # 'ץ' - 246: 21, # 'צ' - 247: 17, # 'ק' - 248: 7, # 'ר' - 249: 10, # 'ש' - 250: 5, # 'ת' - 251: 251, # None - 252: 252, # None - 253: 128, # '\u200e' - 254: 96, # '\u200f' - 255: 253, # None + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 69, # 'A' + 66: 91, # 'B' + 67: 79, # 'C' + 68: 80, # 'D' + 69: 92, # 'E' + 70: 89, # 'F' + 71: 97, # 'G' + 72: 90, # 'H' + 73: 68, # 'I' + 74: 111, # 'J' + 75: 112, # 'K' + 76: 82, # 'L' + 77: 73, # 'M' + 78: 95, # 'N' + 79: 85, # 'O' + 80: 78, # 'P' + 81: 121, # 'Q' + 82: 86, # 'R' + 83: 71, # 'S' + 84: 67, # 'T' + 85: 102, # 'U' + 86: 107, # 'V' + 87: 84, # 'W' + 88: 114, # 'X' + 89: 103, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 50, # 'a' + 98: 74, # 'b' + 99: 60, # 'c' + 100: 61, # 'd' + 101: 42, # 'e' + 102: 76, # 'f' + 103: 70, # 'g' + 104: 64, # 'h' + 105: 53, # 'i' + 106: 105, # 'j' + 107: 93, # 'k' + 108: 56, # 'l' + 109: 65, # 'm' + 110: 54, # 'n' + 111: 49, # 'o' + 112: 66, # 'p' + 113: 110, # 'q' + 114: 51, # 'r' + 115: 43, # 's' + 116: 44, # 't' + 117: 63, # 'u' + 118: 81, # 'v' + 119: 77, # 'w' + 120: 98, # 'x' + 121: 75, # 'y' + 122: 108, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 124, # '€' + 129: 202, # None + 130: 203, # '‚' + 131: 204, # 'ƒ' + 132: 205, # '„' + 133: 40, # '…' + 134: 58, # '†' + 135: 206, # '‡' + 136: 207, # 'ˆ' + 137: 208, # '‰' + 138: 209, # None + 139: 210, # '‹' + 140: 211, # None + 141: 212, # None + 142: 213, # None + 143: 214, # None + 144: 215, # None + 145: 83, # '‘' + 146: 52, # '’' + 147: 47, # '“' + 148: 46, # '”' + 149: 72, # '•' + 150: 32, # '–' + 151: 94, # '—' + 152: 216, # '˜' + 153: 113, # '™' + 154: 217, # None + 155: 109, # '›' + 156: 218, # None + 157: 219, # None + 158: 220, # None + 159: 221, # None + 160: 34, # '\xa0' + 161: 116, # '¡' + 162: 222, # '¢' + 163: 118, # '£' + 164: 100, # '₪' + 165: 223, # '¥' + 166: 224, # '¦' + 167: 117, # '§' + 168: 119, # '¨' + 169: 104, # '©' + 170: 125, # '×' + 171: 225, # '«' + 172: 226, # '¬' + 173: 87, # '\xad' + 174: 99, # '®' + 175: 227, # '¯' + 176: 106, # '°' + 177: 122, # '±' + 178: 123, # '²' + 179: 228, # '³' + 180: 55, # '´' + 181: 229, # 'µ' + 182: 230, # '¶' + 183: 101, # '·' + 184: 231, # '¸' + 185: 232, # '¹' + 186: 120, # '÷' + 187: 233, # '»' + 188: 48, # '¼' + 189: 39, # '½' + 190: 57, # '¾' + 191: 234, # '¿' + 192: 30, # 'ְ' + 193: 59, # 'ֱ' + 194: 41, # 'ֲ' + 195: 88, # 'ֳ' + 196: 33, # 'ִ' + 197: 37, # 'ֵ' + 198: 36, # 'ֶ' + 199: 31, # 'ַ' + 200: 29, # 'ָ' + 201: 35, # 'ֹ' + 202: 235, # None + 203: 62, # 'ֻ' + 204: 28, # 'ּ' + 205: 236, # 'ֽ' + 206: 126, # '־' + 207: 237, # 'ֿ' + 208: 238, # '׀' + 209: 38, # 'ׁ' + 210: 45, # 'ׂ' + 211: 239, # '׃' + 212: 240, # 'װ' + 213: 241, # 'ױ' + 214: 242, # 'ײ' + 215: 243, # '׳' + 216: 127, # '״' + 217: 244, # None + 218: 245, # None + 219: 246, # None + 220: 247, # None + 221: 248, # None + 222: 249, # None + 223: 250, # None + 224: 9, # 'א' + 225: 8, # 'ב' + 226: 20, # 'ג' + 227: 16, # 'ד' + 228: 3, # 'ה' + 229: 2, # 'ו' + 230: 24, # 'ז' + 231: 14, # 'ח' + 232: 22, # 'ט' + 233: 1, # 'י' + 234: 25, # 'ך' + 235: 15, # 'כ' + 236: 4, # 'ל' + 237: 11, # 'ם' + 238: 6, # 'מ' + 239: 23, # 'ן' + 240: 12, # 'נ' + 241: 19, # 'ס' + 242: 13, # 'ע' + 243: 26, # 'ף' + 244: 18, # 'פ' + 245: 27, # 'ץ' + 246: 21, # 'צ' + 247: 17, # 'ק' + 248: 7, # 'ר' + 249: 10, # 'ש' + 250: 5, # 'ת' + 251: 251, # None + 252: 252, # None + 253: 128, # '\u200e' + 254: 96, # '\u200f' + 255: 253, # None } -WINDOWS_1255_HEBREW_MODEL = SingleByteCharSetModel(charset_name='windows-1255', - language='Hebrew', - char_to_order_map=WINDOWS_1255_HEBREW_CHAR_TO_ORDER, - language_model=HEBREW_LANG_MODEL, - typical_positive_ratio=0.984004, - keep_ascii_letters=False, - alphabet='אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ') - +WINDOWS_1255_HEBREW_MODEL = SingleByteCharSetModel( + charset_name='windows-1255', + language='Hebrew', + char_to_order_map=WINDOWS_1255_HEBREW_CHAR_TO_ORDER, + language_model=HEBREW_LANG_MODEL, + typical_positive_ratio=0.984004, + keep_ascii_letters=False, + alphabet='אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ', +) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langhungarianmodel.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langhungarianmodel.py index bbc5cda..52116c5 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langhungarianmodel.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langhungarianmodel.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +from __future__ import annotations from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel @@ -4115,536 +4115,539 @@ HUNGARIAN_LANG_MODEL = { # Character Mapping Table(s): WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 28, # 'A' - 66: 40, # 'B' - 67: 54, # 'C' - 68: 45, # 'D' - 69: 32, # 'E' - 70: 50, # 'F' - 71: 49, # 'G' - 72: 38, # 'H' - 73: 39, # 'I' - 74: 53, # 'J' - 75: 36, # 'K' - 76: 41, # 'L' - 77: 34, # 'M' - 78: 35, # 'N' - 79: 47, # 'O' - 80: 46, # 'P' - 81: 72, # 'Q' - 82: 43, # 'R' - 83: 33, # 'S' - 84: 37, # 'T' - 85: 57, # 'U' - 86: 48, # 'V' - 87: 64, # 'W' - 88: 68, # 'X' - 89: 55, # 'Y' - 90: 52, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 2, # 'a' - 98: 18, # 'b' - 99: 26, # 'c' - 100: 17, # 'd' - 101: 1, # 'e' - 102: 27, # 'f' - 103: 12, # 'g' - 104: 20, # 'h' - 105: 9, # 'i' - 106: 22, # 'j' - 107: 7, # 'k' - 108: 6, # 'l' - 109: 13, # 'm' - 110: 4, # 'n' - 111: 8, # 'o' - 112: 23, # 'p' - 113: 67, # 'q' - 114: 10, # 'r' - 115: 5, # 's' - 116: 3, # 't' - 117: 21, # 'u' - 118: 19, # 'v' - 119: 65, # 'w' - 120: 62, # 'x' - 121: 16, # 'y' - 122: 11, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 161, # '€' - 129: 162, # None - 130: 163, # '‚' - 131: 164, # None - 132: 165, # '„' - 133: 166, # '…' - 134: 167, # '†' - 135: 168, # '‡' - 136: 169, # None - 137: 170, # '‰' - 138: 171, # 'Š' - 139: 172, # '‹' - 140: 173, # 'Ś' - 141: 174, # 'Ť' - 142: 175, # 'Ž' - 143: 176, # 'Ź' - 144: 177, # None - 145: 178, # '‘' - 146: 179, # '’' - 147: 180, # '“' - 148: 78, # '”' - 149: 181, # '•' - 150: 69, # '–' - 151: 182, # '—' - 152: 183, # None - 153: 184, # '™' - 154: 185, # 'š' - 155: 186, # '›' - 156: 187, # 'ś' - 157: 188, # 'ť' - 158: 189, # 'ž' - 159: 190, # 'ź' - 160: 191, # '\xa0' - 161: 192, # 'ˇ' - 162: 193, # '˘' - 163: 194, # 'Ł' - 164: 195, # '¤' - 165: 196, # 'Ą' - 166: 197, # '¦' - 167: 76, # '§' - 168: 198, # '¨' - 169: 199, # '©' - 170: 200, # 'Ş' - 171: 201, # '«' - 172: 202, # '¬' - 173: 203, # '\xad' - 174: 204, # '®' - 175: 205, # 'Ż' - 176: 81, # '°' - 177: 206, # '±' - 178: 207, # '˛' - 179: 208, # 'ł' - 180: 209, # '´' - 181: 210, # 'µ' - 182: 211, # '¶' - 183: 212, # '·' - 184: 213, # '¸' - 185: 214, # 'ą' - 186: 215, # 'ş' - 187: 216, # '»' - 188: 217, # 'Ľ' - 189: 218, # '˝' - 190: 219, # 'ľ' - 191: 220, # 'ż' - 192: 221, # 'Ŕ' - 193: 51, # 'Á' - 194: 83, # 'Â' - 195: 222, # 'Ă' - 196: 80, # 'Ä' - 197: 223, # 'Ĺ' - 198: 224, # 'Ć' - 199: 225, # 'Ç' - 200: 226, # 'Č' - 201: 44, # 'É' - 202: 227, # 'Ę' - 203: 228, # 'Ë' - 204: 229, # 'Ě' - 205: 61, # 'Í' - 206: 230, # 'Î' - 207: 231, # 'Ď' - 208: 232, # 'Đ' - 209: 233, # 'Ń' - 210: 234, # 'Ň' - 211: 58, # 'Ó' - 212: 235, # 'Ô' - 213: 66, # 'Ő' - 214: 59, # 'Ö' - 215: 236, # '×' - 216: 237, # 'Ř' - 217: 238, # 'Ů' - 218: 60, # 'Ú' - 219: 70, # 'Ű' - 220: 63, # 'Ü' - 221: 239, # 'Ý' - 222: 240, # 'Ţ' - 223: 241, # 'ß' - 224: 84, # 'ŕ' - 225: 14, # 'á' - 226: 75, # 'â' - 227: 242, # 'ă' - 228: 71, # 'ä' - 229: 82, # 'ĺ' - 230: 243, # 'ć' - 231: 73, # 'ç' - 232: 244, # 'č' - 233: 15, # 'é' - 234: 85, # 'ę' - 235: 79, # 'ë' - 236: 86, # 'ě' - 237: 30, # 'í' - 238: 77, # 'î' - 239: 87, # 'ď' - 240: 245, # 'đ' - 241: 246, # 'ń' - 242: 247, # 'ň' - 243: 25, # 'ó' - 244: 74, # 'ô' - 245: 42, # 'ő' - 246: 24, # 'ö' - 247: 248, # '÷' - 248: 249, # 'ř' - 249: 250, # 'ů' - 250: 31, # 'ú' - 251: 56, # 'ű' - 252: 29, # 'ü' - 253: 251, # 'ý' - 254: 252, # 'ţ' - 255: 253, # '˙' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 28, # 'A' + 66: 40, # 'B' + 67: 54, # 'C' + 68: 45, # 'D' + 69: 32, # 'E' + 70: 50, # 'F' + 71: 49, # 'G' + 72: 38, # 'H' + 73: 39, # 'I' + 74: 53, # 'J' + 75: 36, # 'K' + 76: 41, # 'L' + 77: 34, # 'M' + 78: 35, # 'N' + 79: 47, # 'O' + 80: 46, # 'P' + 81: 72, # 'Q' + 82: 43, # 'R' + 83: 33, # 'S' + 84: 37, # 'T' + 85: 57, # 'U' + 86: 48, # 'V' + 87: 64, # 'W' + 88: 68, # 'X' + 89: 55, # 'Y' + 90: 52, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 2, # 'a' + 98: 18, # 'b' + 99: 26, # 'c' + 100: 17, # 'd' + 101: 1, # 'e' + 102: 27, # 'f' + 103: 12, # 'g' + 104: 20, # 'h' + 105: 9, # 'i' + 106: 22, # 'j' + 107: 7, # 'k' + 108: 6, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 8, # 'o' + 112: 23, # 'p' + 113: 67, # 'q' + 114: 10, # 'r' + 115: 5, # 's' + 116: 3, # 't' + 117: 21, # 'u' + 118: 19, # 'v' + 119: 65, # 'w' + 120: 62, # 'x' + 121: 16, # 'y' + 122: 11, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 161, # '€' + 129: 162, # None + 130: 163, # '‚' + 131: 164, # None + 132: 165, # '„' + 133: 166, # '…' + 134: 167, # '†' + 135: 168, # '‡' + 136: 169, # None + 137: 170, # '‰' + 138: 171, # 'Š' + 139: 172, # '‹' + 140: 173, # 'Ś' + 141: 174, # 'Ť' + 142: 175, # 'Ž' + 143: 176, # 'Ź' + 144: 177, # None + 145: 178, # '‘' + 146: 179, # '’' + 147: 180, # '“' + 148: 78, # '”' + 149: 181, # '•' + 150: 69, # '–' + 151: 182, # '—' + 152: 183, # None + 153: 184, # '™' + 154: 185, # 'š' + 155: 186, # '›' + 156: 187, # 'ś' + 157: 188, # 'ť' + 158: 189, # 'ž' + 159: 190, # 'ź' + 160: 191, # '\xa0' + 161: 192, # 'ˇ' + 162: 193, # '˘' + 163: 194, # 'Ł' + 164: 195, # '¤' + 165: 196, # 'Ą' + 166: 197, # '¦' + 167: 76, # '§' + 168: 198, # '¨' + 169: 199, # '©' + 170: 200, # 'Ş' + 171: 201, # '«' + 172: 202, # '¬' + 173: 203, # '\xad' + 174: 204, # '®' + 175: 205, # 'Ż' + 176: 81, # '°' + 177: 206, # '±' + 178: 207, # '˛' + 179: 208, # 'ł' + 180: 209, # '´' + 181: 210, # 'µ' + 182: 211, # '¶' + 183: 212, # '·' + 184: 213, # '¸' + 185: 214, # 'ą' + 186: 215, # 'ş' + 187: 216, # '»' + 188: 217, # 'Ľ' + 189: 218, # '˝' + 190: 219, # 'ľ' + 191: 220, # 'ż' + 192: 221, # 'Ŕ' + 193: 51, # 'Á' + 194: 83, # 'Â' + 195: 222, # 'Ă' + 196: 80, # 'Ä' + 197: 223, # 'Ĺ' + 198: 224, # 'Ć' + 199: 225, # 'Ç' + 200: 226, # 'Č' + 201: 44, # 'É' + 202: 227, # 'Ę' + 203: 228, # 'Ë' + 204: 229, # 'Ě' + 205: 61, # 'Í' + 206: 230, # 'Î' + 207: 231, # 'Ď' + 208: 232, # 'Đ' + 209: 233, # 'Ń' + 210: 234, # 'Ň' + 211: 58, # 'Ó' + 212: 235, # 'Ô' + 213: 66, # 'Ő' + 214: 59, # 'Ö' + 215: 236, # '×' + 216: 237, # 'Ř' + 217: 238, # 'Ů' + 218: 60, # 'Ú' + 219: 70, # 'Ű' + 220: 63, # 'Ü' + 221: 239, # 'Ý' + 222: 240, # 'Ţ' + 223: 241, # 'ß' + 224: 84, # 'ŕ' + 225: 14, # 'á' + 226: 75, # 'â' + 227: 242, # 'ă' + 228: 71, # 'ä' + 229: 82, # 'ĺ' + 230: 243, # 'ć' + 231: 73, # 'ç' + 232: 244, # 'č' + 233: 15, # 'é' + 234: 85, # 'ę' + 235: 79, # 'ë' + 236: 86, # 'ě' + 237: 30, # 'í' + 238: 77, # 'î' + 239: 87, # 'ď' + 240: 245, # 'đ' + 241: 246, # 'ń' + 242: 247, # 'ň' + 243: 25, # 'ó' + 244: 74, # 'ô' + 245: 42, # 'ő' + 246: 24, # 'ö' + 247: 248, # '÷' + 248: 249, # 'ř' + 249: 250, # 'ů' + 250: 31, # 'ú' + 251: 56, # 'ű' + 252: 29, # 'ü' + 253: 251, # 'ý' + 254: 252, # 'ţ' + 255: 253, # '˙' } -WINDOWS_1250_HUNGARIAN_MODEL = SingleByteCharSetModel(charset_name='windows-1250', - language='Hungarian', - char_to_order_map=WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER, - language_model=HUNGARIAN_LANG_MODEL, - typical_positive_ratio=0.947368, - keep_ascii_letters=True, - alphabet='ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű') +WINDOWS_1250_HUNGARIAN_MODEL = SingleByteCharSetModel( + charset_name='windows-1250', + language='Hungarian', + char_to_order_map=WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER, + language_model=HUNGARIAN_LANG_MODEL, + typical_positive_ratio=0.947368, + keep_ascii_letters=True, + alphabet='ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű', +) ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 28, # 'A' - 66: 40, # 'B' - 67: 54, # 'C' - 68: 45, # 'D' - 69: 32, # 'E' - 70: 50, # 'F' - 71: 49, # 'G' - 72: 38, # 'H' - 73: 39, # 'I' - 74: 53, # 'J' - 75: 36, # 'K' - 76: 41, # 'L' - 77: 34, # 'M' - 78: 35, # 'N' - 79: 47, # 'O' - 80: 46, # 'P' - 81: 71, # 'Q' - 82: 43, # 'R' - 83: 33, # 'S' - 84: 37, # 'T' - 85: 57, # 'U' - 86: 48, # 'V' - 87: 64, # 'W' - 88: 68, # 'X' - 89: 55, # 'Y' - 90: 52, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 2, # 'a' - 98: 18, # 'b' - 99: 26, # 'c' - 100: 17, # 'd' - 101: 1, # 'e' - 102: 27, # 'f' - 103: 12, # 'g' - 104: 20, # 'h' - 105: 9, # 'i' - 106: 22, # 'j' - 107: 7, # 'k' - 108: 6, # 'l' - 109: 13, # 'm' - 110: 4, # 'n' - 111: 8, # 'o' - 112: 23, # 'p' - 113: 67, # 'q' - 114: 10, # 'r' - 115: 5, # 's' - 116: 3, # 't' - 117: 21, # 'u' - 118: 19, # 'v' - 119: 65, # 'w' - 120: 62, # 'x' - 121: 16, # 'y' - 122: 11, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 159, # '\x80' - 129: 160, # '\x81' - 130: 161, # '\x82' - 131: 162, # '\x83' - 132: 163, # '\x84' - 133: 164, # '\x85' - 134: 165, # '\x86' - 135: 166, # '\x87' - 136: 167, # '\x88' - 137: 168, # '\x89' - 138: 169, # '\x8a' - 139: 170, # '\x8b' - 140: 171, # '\x8c' - 141: 172, # '\x8d' - 142: 173, # '\x8e' - 143: 174, # '\x8f' - 144: 175, # '\x90' - 145: 176, # '\x91' - 146: 177, # '\x92' - 147: 178, # '\x93' - 148: 179, # '\x94' - 149: 180, # '\x95' - 150: 181, # '\x96' - 151: 182, # '\x97' - 152: 183, # '\x98' - 153: 184, # '\x99' - 154: 185, # '\x9a' - 155: 186, # '\x9b' - 156: 187, # '\x9c' - 157: 188, # '\x9d' - 158: 189, # '\x9e' - 159: 190, # '\x9f' - 160: 191, # '\xa0' - 161: 192, # 'Ą' - 162: 193, # '˘' - 163: 194, # 'Ł' - 164: 195, # '¤' - 165: 196, # 'Ľ' - 166: 197, # 'Ś' - 167: 75, # '§' - 168: 198, # '¨' - 169: 199, # 'Š' - 170: 200, # 'Ş' - 171: 201, # 'Ť' - 172: 202, # 'Ź' - 173: 203, # '\xad' - 174: 204, # 'Ž' - 175: 205, # 'Ż' - 176: 79, # '°' - 177: 206, # 'ą' - 178: 207, # '˛' - 179: 208, # 'ł' - 180: 209, # '´' - 181: 210, # 'ľ' - 182: 211, # 'ś' - 183: 212, # 'ˇ' - 184: 213, # '¸' - 185: 214, # 'š' - 186: 215, # 'ş' - 187: 216, # 'ť' - 188: 217, # 'ź' - 189: 218, # '˝' - 190: 219, # 'ž' - 191: 220, # 'ż' - 192: 221, # 'Ŕ' - 193: 51, # 'Á' - 194: 81, # 'Â' - 195: 222, # 'Ă' - 196: 78, # 'Ä' - 197: 223, # 'Ĺ' - 198: 224, # 'Ć' - 199: 225, # 'Ç' - 200: 226, # 'Č' - 201: 44, # 'É' - 202: 227, # 'Ę' - 203: 228, # 'Ë' - 204: 229, # 'Ě' - 205: 61, # 'Í' - 206: 230, # 'Î' - 207: 231, # 'Ď' - 208: 232, # 'Đ' - 209: 233, # 'Ń' - 210: 234, # 'Ň' - 211: 58, # 'Ó' - 212: 235, # 'Ô' - 213: 66, # 'Ő' - 214: 59, # 'Ö' - 215: 236, # '×' - 216: 237, # 'Ř' - 217: 238, # 'Ů' - 218: 60, # 'Ú' - 219: 69, # 'Ű' - 220: 63, # 'Ü' - 221: 239, # 'Ý' - 222: 240, # 'Ţ' - 223: 241, # 'ß' - 224: 82, # 'ŕ' - 225: 14, # 'á' - 226: 74, # 'â' - 227: 242, # 'ă' - 228: 70, # 'ä' - 229: 80, # 'ĺ' - 230: 243, # 'ć' - 231: 72, # 'ç' - 232: 244, # 'č' - 233: 15, # 'é' - 234: 83, # 'ę' - 235: 77, # 'ë' - 236: 84, # 'ě' - 237: 30, # 'í' - 238: 76, # 'î' - 239: 85, # 'ď' - 240: 245, # 'đ' - 241: 246, # 'ń' - 242: 247, # 'ň' - 243: 25, # 'ó' - 244: 73, # 'ô' - 245: 42, # 'ő' - 246: 24, # 'ö' - 247: 248, # '÷' - 248: 249, # 'ř' - 249: 250, # 'ů' - 250: 31, # 'ú' - 251: 56, # 'ű' - 252: 29, # 'ü' - 253: 251, # 'ý' - 254: 252, # 'ţ' - 255: 253, # '˙' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 28, # 'A' + 66: 40, # 'B' + 67: 54, # 'C' + 68: 45, # 'D' + 69: 32, # 'E' + 70: 50, # 'F' + 71: 49, # 'G' + 72: 38, # 'H' + 73: 39, # 'I' + 74: 53, # 'J' + 75: 36, # 'K' + 76: 41, # 'L' + 77: 34, # 'M' + 78: 35, # 'N' + 79: 47, # 'O' + 80: 46, # 'P' + 81: 71, # 'Q' + 82: 43, # 'R' + 83: 33, # 'S' + 84: 37, # 'T' + 85: 57, # 'U' + 86: 48, # 'V' + 87: 64, # 'W' + 88: 68, # 'X' + 89: 55, # 'Y' + 90: 52, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 2, # 'a' + 98: 18, # 'b' + 99: 26, # 'c' + 100: 17, # 'd' + 101: 1, # 'e' + 102: 27, # 'f' + 103: 12, # 'g' + 104: 20, # 'h' + 105: 9, # 'i' + 106: 22, # 'j' + 107: 7, # 'k' + 108: 6, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 8, # 'o' + 112: 23, # 'p' + 113: 67, # 'q' + 114: 10, # 'r' + 115: 5, # 's' + 116: 3, # 't' + 117: 21, # 'u' + 118: 19, # 'v' + 119: 65, # 'w' + 120: 62, # 'x' + 121: 16, # 'y' + 122: 11, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 159, # '\x80' + 129: 160, # '\x81' + 130: 161, # '\x82' + 131: 162, # '\x83' + 132: 163, # '\x84' + 133: 164, # '\x85' + 134: 165, # '\x86' + 135: 166, # '\x87' + 136: 167, # '\x88' + 137: 168, # '\x89' + 138: 169, # '\x8a' + 139: 170, # '\x8b' + 140: 171, # '\x8c' + 141: 172, # '\x8d' + 142: 173, # '\x8e' + 143: 174, # '\x8f' + 144: 175, # '\x90' + 145: 176, # '\x91' + 146: 177, # '\x92' + 147: 178, # '\x93' + 148: 179, # '\x94' + 149: 180, # '\x95' + 150: 181, # '\x96' + 151: 182, # '\x97' + 152: 183, # '\x98' + 153: 184, # '\x99' + 154: 185, # '\x9a' + 155: 186, # '\x9b' + 156: 187, # '\x9c' + 157: 188, # '\x9d' + 158: 189, # '\x9e' + 159: 190, # '\x9f' + 160: 191, # '\xa0' + 161: 192, # 'Ą' + 162: 193, # '˘' + 163: 194, # 'Ł' + 164: 195, # '¤' + 165: 196, # 'Ľ' + 166: 197, # 'Ś' + 167: 75, # '§' + 168: 198, # '¨' + 169: 199, # 'Š' + 170: 200, # 'Ş' + 171: 201, # 'Ť' + 172: 202, # 'Ź' + 173: 203, # '\xad' + 174: 204, # 'Ž' + 175: 205, # 'Ż' + 176: 79, # '°' + 177: 206, # 'ą' + 178: 207, # '˛' + 179: 208, # 'ł' + 180: 209, # '´' + 181: 210, # 'ľ' + 182: 211, # 'ś' + 183: 212, # 'ˇ' + 184: 213, # '¸' + 185: 214, # 'š' + 186: 215, # 'ş' + 187: 216, # 'ť' + 188: 217, # 'ź' + 189: 218, # '˝' + 190: 219, # 'ž' + 191: 220, # 'ż' + 192: 221, # 'Ŕ' + 193: 51, # 'Á' + 194: 81, # 'Â' + 195: 222, # 'Ă' + 196: 78, # 'Ä' + 197: 223, # 'Ĺ' + 198: 224, # 'Ć' + 199: 225, # 'Ç' + 200: 226, # 'Č' + 201: 44, # 'É' + 202: 227, # 'Ę' + 203: 228, # 'Ë' + 204: 229, # 'Ě' + 205: 61, # 'Í' + 206: 230, # 'Î' + 207: 231, # 'Ď' + 208: 232, # 'Đ' + 209: 233, # 'Ń' + 210: 234, # 'Ň' + 211: 58, # 'Ó' + 212: 235, # 'Ô' + 213: 66, # 'Ő' + 214: 59, # 'Ö' + 215: 236, # '×' + 216: 237, # 'Ř' + 217: 238, # 'Ů' + 218: 60, # 'Ú' + 219: 69, # 'Ű' + 220: 63, # 'Ü' + 221: 239, # 'Ý' + 222: 240, # 'Ţ' + 223: 241, # 'ß' + 224: 82, # 'ŕ' + 225: 14, # 'á' + 226: 74, # 'â' + 227: 242, # 'ă' + 228: 70, # 'ä' + 229: 80, # 'ĺ' + 230: 243, # 'ć' + 231: 72, # 'ç' + 232: 244, # 'č' + 233: 15, # 'é' + 234: 83, # 'ę' + 235: 77, # 'ë' + 236: 84, # 'ě' + 237: 30, # 'í' + 238: 76, # 'î' + 239: 85, # 'ď' + 240: 245, # 'đ' + 241: 246, # 'ń' + 242: 247, # 'ň' + 243: 25, # 'ó' + 244: 73, # 'ô' + 245: 42, # 'ő' + 246: 24, # 'ö' + 247: 248, # '÷' + 248: 249, # 'ř' + 249: 250, # 'ů' + 250: 31, # 'ú' + 251: 56, # 'ű' + 252: 29, # 'ü' + 253: 251, # 'ý' + 254: 252, # 'ţ' + 255: 253, # '˙' } -ISO_8859_2_HUNGARIAN_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-2', - language='Hungarian', - char_to_order_map=ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER, - language_model=HUNGARIAN_LANG_MODEL, - typical_positive_ratio=0.947368, - keep_ascii_letters=True, - alphabet='ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű') - +ISO_8859_2_HUNGARIAN_MODEL = SingleByteCharSetModel( + charset_name='ISO-8859-2', + language='Hungarian', + char_to_order_map=ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER, + language_model=HUNGARIAN_LANG_MODEL, + typical_positive_ratio=0.947368, + keep_ascii_letters=True, + alphabet='ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű', +) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langrussianmodel.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langrussianmodel.py index 5594452..7926df4 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langrussianmodel.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langrussianmodel.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +from __future__ import annotations from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel @@ -4115,1604 +4115,1615 @@ RUSSIAN_LANG_MODEL = { # Character Mapping Table(s): IBM866_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 37, # 'А' - 129: 44, # 'Б' - 130: 33, # 'В' - 131: 46, # 'Г' - 132: 41, # 'Д' - 133: 48, # 'Е' - 134: 56, # 'Ж' - 135: 51, # 'З' - 136: 42, # 'И' - 137: 60, # 'Й' - 138: 36, # 'К' - 139: 49, # 'Л' - 140: 38, # 'М' - 141: 31, # 'Н' - 142: 34, # 'О' - 143: 35, # 'П' - 144: 45, # 'Р' - 145: 32, # 'С' - 146: 40, # 'Т' - 147: 52, # 'У' - 148: 53, # 'Ф' - 149: 55, # 'Х' - 150: 58, # 'Ц' - 151: 50, # 'Ч' - 152: 57, # 'Ш' - 153: 63, # 'Щ' - 154: 70, # 'Ъ' - 155: 62, # 'Ы' - 156: 61, # 'Ь' - 157: 47, # 'Э' - 158: 59, # 'Ю' - 159: 43, # 'Я' - 160: 3, # 'а' - 161: 21, # 'б' - 162: 10, # 'в' - 163: 19, # 'г' - 164: 13, # 'д' - 165: 2, # 'е' - 166: 24, # 'ж' - 167: 20, # 'з' - 168: 4, # 'и' - 169: 23, # 'й' - 170: 11, # 'к' - 171: 8, # 'л' - 172: 12, # 'м' - 173: 5, # 'н' - 174: 1, # 'о' - 175: 15, # 'п' - 176: 191, # '░' - 177: 192, # '▒' - 178: 193, # '▓' - 179: 194, # '│' - 180: 195, # '┤' - 181: 196, # '╡' - 182: 197, # '╢' - 183: 198, # '╖' - 184: 199, # '╕' - 185: 200, # '╣' - 186: 201, # '║' - 187: 202, # '╗' - 188: 203, # '╝' - 189: 204, # '╜' - 190: 205, # '╛' - 191: 206, # '┐' - 192: 207, # '└' - 193: 208, # '┴' - 194: 209, # '┬' - 195: 210, # '├' - 196: 211, # '─' - 197: 212, # '┼' - 198: 213, # '╞' - 199: 214, # '╟' - 200: 215, # '╚' - 201: 216, # '╔' - 202: 217, # '╩' - 203: 218, # '╦' - 204: 219, # '╠' - 205: 220, # '═' - 206: 221, # '╬' - 207: 222, # '╧' - 208: 223, # '╨' - 209: 224, # '╤' - 210: 225, # '╥' - 211: 226, # '╙' - 212: 227, # '╘' - 213: 228, # '╒' - 214: 229, # '╓' - 215: 230, # '╫' - 216: 231, # '╪' - 217: 232, # '┘' - 218: 233, # '┌' - 219: 234, # '█' - 220: 235, # '▄' - 221: 236, # '▌' - 222: 237, # '▐' - 223: 238, # '▀' - 224: 9, # 'р' - 225: 7, # 'с' - 226: 6, # 'т' - 227: 14, # 'у' - 228: 39, # 'ф' - 229: 26, # 'х' - 230: 28, # 'ц' - 231: 22, # 'ч' - 232: 25, # 'ш' - 233: 29, # 'щ' - 234: 54, # 'ъ' - 235: 18, # 'ы' - 236: 17, # 'ь' - 237: 30, # 'э' - 238: 27, # 'ю' - 239: 16, # 'я' - 240: 239, # 'Ё' - 241: 68, # 'ё' - 242: 240, # 'Є' - 243: 241, # 'є' - 244: 242, # 'Ї' - 245: 243, # 'ї' - 246: 244, # 'Ў' - 247: 245, # 'ў' - 248: 246, # '°' - 249: 247, # '∙' - 250: 248, # '·' - 251: 249, # '√' - 252: 250, # '№' - 253: 251, # '¤' - 254: 252, # '■' - 255: 255, # '\xa0' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 37, # 'А' + 129: 44, # 'Б' + 130: 33, # 'В' + 131: 46, # 'Г' + 132: 41, # 'Д' + 133: 48, # 'Е' + 134: 56, # 'Ж' + 135: 51, # 'З' + 136: 42, # 'И' + 137: 60, # 'Й' + 138: 36, # 'К' + 139: 49, # 'Л' + 140: 38, # 'М' + 141: 31, # 'Н' + 142: 34, # 'О' + 143: 35, # 'П' + 144: 45, # 'Р' + 145: 32, # 'С' + 146: 40, # 'Т' + 147: 52, # 'У' + 148: 53, # 'Ф' + 149: 55, # 'Х' + 150: 58, # 'Ц' + 151: 50, # 'Ч' + 152: 57, # 'Ш' + 153: 63, # 'Щ' + 154: 70, # 'Ъ' + 155: 62, # 'Ы' + 156: 61, # 'Ь' + 157: 47, # 'Э' + 158: 59, # 'Ю' + 159: 43, # 'Я' + 160: 3, # 'а' + 161: 21, # 'б' + 162: 10, # 'в' + 163: 19, # 'г' + 164: 13, # 'д' + 165: 2, # 'е' + 166: 24, # 'ж' + 167: 20, # 'з' + 168: 4, # 'и' + 169: 23, # 'й' + 170: 11, # 'к' + 171: 8, # 'л' + 172: 12, # 'м' + 173: 5, # 'н' + 174: 1, # 'о' + 175: 15, # 'п' + 176: 191, # '░' + 177: 192, # '▒' + 178: 193, # '▓' + 179: 194, # '│' + 180: 195, # '┤' + 181: 196, # '╡' + 182: 197, # '╢' + 183: 198, # '╖' + 184: 199, # '╕' + 185: 200, # '╣' + 186: 201, # '║' + 187: 202, # '╗' + 188: 203, # '╝' + 189: 204, # '╜' + 190: 205, # '╛' + 191: 206, # '┐' + 192: 207, # '└' + 193: 208, # '┴' + 194: 209, # '┬' + 195: 210, # '├' + 196: 211, # '─' + 197: 212, # '┼' + 198: 213, # '╞' + 199: 214, # '╟' + 200: 215, # '╚' + 201: 216, # '╔' + 202: 217, # '╩' + 203: 218, # '╦' + 204: 219, # '╠' + 205: 220, # '═' + 206: 221, # '╬' + 207: 222, # '╧' + 208: 223, # '╨' + 209: 224, # '╤' + 210: 225, # '╥' + 211: 226, # '╙' + 212: 227, # '╘' + 213: 228, # '╒' + 214: 229, # '╓' + 215: 230, # '╫' + 216: 231, # '╪' + 217: 232, # '┘' + 218: 233, # '┌' + 219: 234, # '█' + 220: 235, # '▄' + 221: 236, # '▌' + 222: 237, # '▐' + 223: 238, # '▀' + 224: 9, # 'р' + 225: 7, # 'с' + 226: 6, # 'т' + 227: 14, # 'у' + 228: 39, # 'ф' + 229: 26, # 'х' + 230: 28, # 'ц' + 231: 22, # 'ч' + 232: 25, # 'ш' + 233: 29, # 'щ' + 234: 54, # 'ъ' + 235: 18, # 'ы' + 236: 17, # 'ь' + 237: 30, # 'э' + 238: 27, # 'ю' + 239: 16, # 'я' + 240: 239, # 'Ё' + 241: 68, # 'ё' + 242: 240, # 'Є' + 243: 241, # 'є' + 244: 242, # 'Ї' + 245: 243, # 'ї' + 246: 244, # 'Ў' + 247: 245, # 'ў' + 248: 246, # '°' + 249: 247, # '∙' + 250: 248, # '·' + 251: 249, # '√' + 252: 250, # '№' + 253: 251, # '¤' + 254: 252, # '■' + 255: 255, # '\xa0' } -IBM866_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='IBM866', - language='Russian', - char_to_order_map=IBM866_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') +IBM866_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name='IBM866', + language='Russian', + char_to_order_map=IBM866_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё', +) WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 191, # 'Ђ' - 129: 192, # 'Ѓ' - 130: 193, # '‚' - 131: 194, # 'ѓ' - 132: 195, # '„' - 133: 196, # '…' - 134: 197, # '†' - 135: 198, # '‡' - 136: 199, # '€' - 137: 200, # '‰' - 138: 201, # 'Љ' - 139: 202, # '‹' - 140: 203, # 'Њ' - 141: 204, # 'Ќ' - 142: 205, # 'Ћ' - 143: 206, # 'Џ' - 144: 207, # 'ђ' - 145: 208, # '‘' - 146: 209, # '’' - 147: 210, # '“' - 148: 211, # '”' - 149: 212, # '•' - 150: 213, # '–' - 151: 214, # '—' - 152: 215, # None - 153: 216, # '™' - 154: 217, # 'љ' - 155: 218, # '›' - 156: 219, # 'њ' - 157: 220, # 'ќ' - 158: 221, # 'ћ' - 159: 222, # 'џ' - 160: 223, # '\xa0' - 161: 224, # 'Ў' - 162: 225, # 'ў' - 163: 226, # 'Ј' - 164: 227, # '¤' - 165: 228, # 'Ґ' - 166: 229, # '¦' - 167: 230, # '§' - 168: 231, # 'Ё' - 169: 232, # '©' - 170: 233, # 'Є' - 171: 234, # '«' - 172: 235, # '¬' - 173: 236, # '\xad' - 174: 237, # '®' - 175: 238, # 'Ї' - 176: 239, # '°' - 177: 240, # '±' - 178: 241, # 'І' - 179: 242, # 'і' - 180: 243, # 'ґ' - 181: 244, # 'µ' - 182: 245, # '¶' - 183: 246, # '·' - 184: 68, # 'ё' - 185: 247, # '№' - 186: 248, # 'є' - 187: 249, # '»' - 188: 250, # 'ј' - 189: 251, # 'Ѕ' - 190: 252, # 'ѕ' - 191: 253, # 'ї' - 192: 37, # 'А' - 193: 44, # 'Б' - 194: 33, # 'В' - 195: 46, # 'Г' - 196: 41, # 'Д' - 197: 48, # 'Е' - 198: 56, # 'Ж' - 199: 51, # 'З' - 200: 42, # 'И' - 201: 60, # 'Й' - 202: 36, # 'К' - 203: 49, # 'Л' - 204: 38, # 'М' - 205: 31, # 'Н' - 206: 34, # 'О' - 207: 35, # 'П' - 208: 45, # 'Р' - 209: 32, # 'С' - 210: 40, # 'Т' - 211: 52, # 'У' - 212: 53, # 'Ф' - 213: 55, # 'Х' - 214: 58, # 'Ц' - 215: 50, # 'Ч' - 216: 57, # 'Ш' - 217: 63, # 'Щ' - 218: 70, # 'Ъ' - 219: 62, # 'Ы' - 220: 61, # 'Ь' - 221: 47, # 'Э' - 222: 59, # 'Ю' - 223: 43, # 'Я' - 224: 3, # 'а' - 225: 21, # 'б' - 226: 10, # 'в' - 227: 19, # 'г' - 228: 13, # 'д' - 229: 2, # 'е' - 230: 24, # 'ж' - 231: 20, # 'з' - 232: 4, # 'и' - 233: 23, # 'й' - 234: 11, # 'к' - 235: 8, # 'л' - 236: 12, # 'м' - 237: 5, # 'н' - 238: 1, # 'о' - 239: 15, # 'п' - 240: 9, # 'р' - 241: 7, # 'с' - 242: 6, # 'т' - 243: 14, # 'у' - 244: 39, # 'ф' - 245: 26, # 'х' - 246: 28, # 'ц' - 247: 22, # 'ч' - 248: 25, # 'ш' - 249: 29, # 'щ' - 250: 54, # 'ъ' - 251: 18, # 'ы' - 252: 17, # 'ь' - 253: 30, # 'э' - 254: 27, # 'ю' - 255: 16, # 'я' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # 'Ђ' + 129: 192, # 'Ѓ' + 130: 193, # '‚' + 131: 194, # 'ѓ' + 132: 195, # '„' + 133: 196, # '…' + 134: 197, # '†' + 135: 198, # '‡' + 136: 199, # '€' + 137: 200, # '‰' + 138: 201, # 'Љ' + 139: 202, # '‹' + 140: 203, # 'Њ' + 141: 204, # 'Ќ' + 142: 205, # 'Ћ' + 143: 206, # 'Џ' + 144: 207, # 'ђ' + 145: 208, # '‘' + 146: 209, # '’' + 147: 210, # '“' + 148: 211, # '”' + 149: 212, # '•' + 150: 213, # '–' + 151: 214, # '—' + 152: 215, # None + 153: 216, # '™' + 154: 217, # 'љ' + 155: 218, # '›' + 156: 219, # 'њ' + 157: 220, # 'ќ' + 158: 221, # 'ћ' + 159: 222, # 'џ' + 160: 223, # '\xa0' + 161: 224, # 'Ў' + 162: 225, # 'ў' + 163: 226, # 'Ј' + 164: 227, # '¤' + 165: 228, # 'Ґ' + 166: 229, # '¦' + 167: 230, # '§' + 168: 231, # 'Ё' + 169: 232, # '©' + 170: 233, # 'Є' + 171: 234, # '«' + 172: 235, # '¬' + 173: 236, # '\xad' + 174: 237, # '®' + 175: 238, # 'Ї' + 176: 239, # '°' + 177: 240, # '±' + 178: 241, # 'І' + 179: 242, # 'і' + 180: 243, # 'ґ' + 181: 244, # 'µ' + 182: 245, # '¶' + 183: 246, # '·' + 184: 68, # 'ё' + 185: 247, # '№' + 186: 248, # 'є' + 187: 249, # '»' + 188: 250, # 'ј' + 189: 251, # 'Ѕ' + 190: 252, # 'ѕ' + 191: 253, # 'ї' + 192: 37, # 'А' + 193: 44, # 'Б' + 194: 33, # 'В' + 195: 46, # 'Г' + 196: 41, # 'Д' + 197: 48, # 'Е' + 198: 56, # 'Ж' + 199: 51, # 'З' + 200: 42, # 'И' + 201: 60, # 'Й' + 202: 36, # 'К' + 203: 49, # 'Л' + 204: 38, # 'М' + 205: 31, # 'Н' + 206: 34, # 'О' + 207: 35, # 'П' + 208: 45, # 'Р' + 209: 32, # 'С' + 210: 40, # 'Т' + 211: 52, # 'У' + 212: 53, # 'Ф' + 213: 55, # 'Х' + 214: 58, # 'Ц' + 215: 50, # 'Ч' + 216: 57, # 'Ш' + 217: 63, # 'Щ' + 218: 70, # 'Ъ' + 219: 62, # 'Ы' + 220: 61, # 'Ь' + 221: 47, # 'Э' + 222: 59, # 'Ю' + 223: 43, # 'Я' + 224: 3, # 'а' + 225: 21, # 'б' + 226: 10, # 'в' + 227: 19, # 'г' + 228: 13, # 'д' + 229: 2, # 'е' + 230: 24, # 'ж' + 231: 20, # 'з' + 232: 4, # 'и' + 233: 23, # 'й' + 234: 11, # 'к' + 235: 8, # 'л' + 236: 12, # 'м' + 237: 5, # 'н' + 238: 1, # 'о' + 239: 15, # 'п' + 240: 9, # 'р' + 241: 7, # 'с' + 242: 6, # 'т' + 243: 14, # 'у' + 244: 39, # 'ф' + 245: 26, # 'х' + 246: 28, # 'ц' + 247: 22, # 'ч' + 248: 25, # 'ш' + 249: 29, # 'щ' + 250: 54, # 'ъ' + 251: 18, # 'ы' + 252: 17, # 'ь' + 253: 30, # 'э' + 254: 27, # 'ю' + 255: 16, # 'я' } -WINDOWS_1251_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='windows-1251', - language='Russian', - char_to_order_map=WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') +WINDOWS_1251_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name='windows-1251', + language='Russian', + char_to_order_map=WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё', +) IBM855_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 191, # 'ђ' - 129: 192, # 'Ђ' - 130: 193, # 'ѓ' - 131: 194, # 'Ѓ' - 132: 68, # 'ё' - 133: 195, # 'Ё' - 134: 196, # 'є' - 135: 197, # 'Є' - 136: 198, # 'ѕ' - 137: 199, # 'Ѕ' - 138: 200, # 'і' - 139: 201, # 'І' - 140: 202, # 'ї' - 141: 203, # 'Ї' - 142: 204, # 'ј' - 143: 205, # 'Ј' - 144: 206, # 'љ' - 145: 207, # 'Љ' - 146: 208, # 'њ' - 147: 209, # 'Њ' - 148: 210, # 'ћ' - 149: 211, # 'Ћ' - 150: 212, # 'ќ' - 151: 213, # 'Ќ' - 152: 214, # 'ў' - 153: 215, # 'Ў' - 154: 216, # 'џ' - 155: 217, # 'Џ' - 156: 27, # 'ю' - 157: 59, # 'Ю' - 158: 54, # 'ъ' - 159: 70, # 'Ъ' - 160: 3, # 'а' - 161: 37, # 'А' - 162: 21, # 'б' - 163: 44, # 'Б' - 164: 28, # 'ц' - 165: 58, # 'Ц' - 166: 13, # 'д' - 167: 41, # 'Д' - 168: 2, # 'е' - 169: 48, # 'Е' - 170: 39, # 'ф' - 171: 53, # 'Ф' - 172: 19, # 'г' - 173: 46, # 'Г' - 174: 218, # '«' - 175: 219, # '»' - 176: 220, # '░' - 177: 221, # '▒' - 178: 222, # '▓' - 179: 223, # '│' - 180: 224, # '┤' - 181: 26, # 'х' - 182: 55, # 'Х' - 183: 4, # 'и' - 184: 42, # 'И' - 185: 225, # '╣' - 186: 226, # '║' - 187: 227, # '╗' - 188: 228, # '╝' - 189: 23, # 'й' - 190: 60, # 'Й' - 191: 229, # '┐' - 192: 230, # '└' - 193: 231, # '┴' - 194: 232, # '┬' - 195: 233, # '├' - 196: 234, # '─' - 197: 235, # '┼' - 198: 11, # 'к' - 199: 36, # 'К' - 200: 236, # '╚' - 201: 237, # '╔' - 202: 238, # '╩' - 203: 239, # '╦' - 204: 240, # '╠' - 205: 241, # '═' - 206: 242, # '╬' - 207: 243, # '¤' - 208: 8, # 'л' - 209: 49, # 'Л' - 210: 12, # 'м' - 211: 38, # 'М' - 212: 5, # 'н' - 213: 31, # 'Н' - 214: 1, # 'о' - 215: 34, # 'О' - 216: 15, # 'п' - 217: 244, # '┘' - 218: 245, # '┌' - 219: 246, # '█' - 220: 247, # '▄' - 221: 35, # 'П' - 222: 16, # 'я' - 223: 248, # '▀' - 224: 43, # 'Я' - 225: 9, # 'р' - 226: 45, # 'Р' - 227: 7, # 'с' - 228: 32, # 'С' - 229: 6, # 'т' - 230: 40, # 'Т' - 231: 14, # 'у' - 232: 52, # 'У' - 233: 24, # 'ж' - 234: 56, # 'Ж' - 235: 10, # 'в' - 236: 33, # 'В' - 237: 17, # 'ь' - 238: 61, # 'Ь' - 239: 249, # '№' - 240: 250, # '\xad' - 241: 18, # 'ы' - 242: 62, # 'Ы' - 243: 20, # 'з' - 244: 51, # 'З' - 245: 25, # 'ш' - 246: 57, # 'Ш' - 247: 30, # 'э' - 248: 47, # 'Э' - 249: 29, # 'щ' - 250: 63, # 'Щ' - 251: 22, # 'ч' - 252: 50, # 'Ч' - 253: 251, # '§' - 254: 252, # '■' - 255: 255, # '\xa0' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # 'ђ' + 129: 192, # 'Ђ' + 130: 193, # 'ѓ' + 131: 194, # 'Ѓ' + 132: 68, # 'ё' + 133: 195, # 'Ё' + 134: 196, # 'є' + 135: 197, # 'Є' + 136: 198, # 'ѕ' + 137: 199, # 'Ѕ' + 138: 200, # 'і' + 139: 201, # 'І' + 140: 202, # 'ї' + 141: 203, # 'Ї' + 142: 204, # 'ј' + 143: 205, # 'Ј' + 144: 206, # 'љ' + 145: 207, # 'Љ' + 146: 208, # 'њ' + 147: 209, # 'Њ' + 148: 210, # 'ћ' + 149: 211, # 'Ћ' + 150: 212, # 'ќ' + 151: 213, # 'Ќ' + 152: 214, # 'ў' + 153: 215, # 'Ў' + 154: 216, # 'џ' + 155: 217, # 'Џ' + 156: 27, # 'ю' + 157: 59, # 'Ю' + 158: 54, # 'ъ' + 159: 70, # 'Ъ' + 160: 3, # 'а' + 161: 37, # 'А' + 162: 21, # 'б' + 163: 44, # 'Б' + 164: 28, # 'ц' + 165: 58, # 'Ц' + 166: 13, # 'д' + 167: 41, # 'Д' + 168: 2, # 'е' + 169: 48, # 'Е' + 170: 39, # 'ф' + 171: 53, # 'Ф' + 172: 19, # 'г' + 173: 46, # 'Г' + 174: 218, # '«' + 175: 219, # '»' + 176: 220, # '░' + 177: 221, # '▒' + 178: 222, # '▓' + 179: 223, # '│' + 180: 224, # '┤' + 181: 26, # 'х' + 182: 55, # 'Х' + 183: 4, # 'и' + 184: 42, # 'И' + 185: 225, # '╣' + 186: 226, # '║' + 187: 227, # '╗' + 188: 228, # '╝' + 189: 23, # 'й' + 190: 60, # 'Й' + 191: 229, # '┐' + 192: 230, # '└' + 193: 231, # '┴' + 194: 232, # '┬' + 195: 233, # '├' + 196: 234, # '─' + 197: 235, # '┼' + 198: 11, # 'к' + 199: 36, # 'К' + 200: 236, # '╚' + 201: 237, # '╔' + 202: 238, # '╩' + 203: 239, # '╦' + 204: 240, # '╠' + 205: 241, # '═' + 206: 242, # '╬' + 207: 243, # '¤' + 208: 8, # 'л' + 209: 49, # 'Л' + 210: 12, # 'м' + 211: 38, # 'М' + 212: 5, # 'н' + 213: 31, # 'Н' + 214: 1, # 'о' + 215: 34, # 'О' + 216: 15, # 'п' + 217: 244, # '┘' + 218: 245, # '┌' + 219: 246, # '█' + 220: 247, # '▄' + 221: 35, # 'П' + 222: 16, # 'я' + 223: 248, # '▀' + 224: 43, # 'Я' + 225: 9, # 'р' + 226: 45, # 'Р' + 227: 7, # 'с' + 228: 32, # 'С' + 229: 6, # 'т' + 230: 40, # 'Т' + 231: 14, # 'у' + 232: 52, # 'У' + 233: 24, # 'ж' + 234: 56, # 'Ж' + 235: 10, # 'в' + 236: 33, # 'В' + 237: 17, # 'ь' + 238: 61, # 'Ь' + 239: 249, # '№' + 240: 250, # '\xad' + 241: 18, # 'ы' + 242: 62, # 'Ы' + 243: 20, # 'з' + 244: 51, # 'З' + 245: 25, # 'ш' + 246: 57, # 'Ш' + 247: 30, # 'э' + 248: 47, # 'Э' + 249: 29, # 'щ' + 250: 63, # 'Щ' + 251: 22, # 'ч' + 252: 50, # 'Ч' + 253: 251, # '§' + 254: 252, # '■' + 255: 255, # '\xa0' } -IBM855_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='IBM855', - language='Russian', - char_to_order_map=IBM855_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') +IBM855_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name='IBM855', + language='Russian', + char_to_order_map=IBM855_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё', +) KOI8_R_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 191, # '─' - 129: 192, # '│' - 130: 193, # '┌' - 131: 194, # '┐' - 132: 195, # '└' - 133: 196, # '┘' - 134: 197, # '├' - 135: 198, # '┤' - 136: 199, # '┬' - 137: 200, # '┴' - 138: 201, # '┼' - 139: 202, # '▀' - 140: 203, # '▄' - 141: 204, # '█' - 142: 205, # '▌' - 143: 206, # '▐' - 144: 207, # '░' - 145: 208, # '▒' - 146: 209, # '▓' - 147: 210, # '⌠' - 148: 211, # '■' - 149: 212, # '∙' - 150: 213, # '√' - 151: 214, # '≈' - 152: 215, # '≤' - 153: 216, # '≥' - 154: 217, # '\xa0' - 155: 218, # '⌡' - 156: 219, # '°' - 157: 220, # '²' - 158: 221, # '·' - 159: 222, # '÷' - 160: 223, # '═' - 161: 224, # '║' - 162: 225, # '╒' - 163: 68, # 'ё' - 164: 226, # '╓' - 165: 227, # '╔' - 166: 228, # '╕' - 167: 229, # '╖' - 168: 230, # '╗' - 169: 231, # '╘' - 170: 232, # '╙' - 171: 233, # '╚' - 172: 234, # '╛' - 173: 235, # '╜' - 174: 236, # '╝' - 175: 237, # '╞' - 176: 238, # '╟' - 177: 239, # '╠' - 178: 240, # '╡' - 179: 241, # 'Ё' - 180: 242, # '╢' - 181: 243, # '╣' - 182: 244, # '╤' - 183: 245, # '╥' - 184: 246, # '╦' - 185: 247, # '╧' - 186: 248, # '╨' - 187: 249, # '╩' - 188: 250, # '╪' - 189: 251, # '╫' - 190: 252, # '╬' - 191: 253, # '©' - 192: 27, # 'ю' - 193: 3, # 'а' - 194: 21, # 'б' - 195: 28, # 'ц' - 196: 13, # 'д' - 197: 2, # 'е' - 198: 39, # 'ф' - 199: 19, # 'г' - 200: 26, # 'х' - 201: 4, # 'и' - 202: 23, # 'й' - 203: 11, # 'к' - 204: 8, # 'л' - 205: 12, # 'м' - 206: 5, # 'н' - 207: 1, # 'о' - 208: 15, # 'п' - 209: 16, # 'я' - 210: 9, # 'р' - 211: 7, # 'с' - 212: 6, # 'т' - 213: 14, # 'у' - 214: 24, # 'ж' - 215: 10, # 'в' - 216: 17, # 'ь' - 217: 18, # 'ы' - 218: 20, # 'з' - 219: 25, # 'ш' - 220: 30, # 'э' - 221: 29, # 'щ' - 222: 22, # 'ч' - 223: 54, # 'ъ' - 224: 59, # 'Ю' - 225: 37, # 'А' - 226: 44, # 'Б' - 227: 58, # 'Ц' - 228: 41, # 'Д' - 229: 48, # 'Е' - 230: 53, # 'Ф' - 231: 46, # 'Г' - 232: 55, # 'Х' - 233: 42, # 'И' - 234: 60, # 'Й' - 235: 36, # 'К' - 236: 49, # 'Л' - 237: 38, # 'М' - 238: 31, # 'Н' - 239: 34, # 'О' - 240: 35, # 'П' - 241: 43, # 'Я' - 242: 45, # 'Р' - 243: 32, # 'С' - 244: 40, # 'Т' - 245: 52, # 'У' - 246: 56, # 'Ж' - 247: 33, # 'В' - 248: 61, # 'Ь' - 249: 62, # 'Ы' - 250: 51, # 'З' - 251: 57, # 'Ш' - 252: 47, # 'Э' - 253: 63, # 'Щ' - 254: 50, # 'Ч' - 255: 70, # 'Ъ' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # '─' + 129: 192, # '│' + 130: 193, # '┌' + 131: 194, # '┐' + 132: 195, # '└' + 133: 196, # '┘' + 134: 197, # '├' + 135: 198, # '┤' + 136: 199, # '┬' + 137: 200, # '┴' + 138: 201, # '┼' + 139: 202, # '▀' + 140: 203, # '▄' + 141: 204, # '█' + 142: 205, # '▌' + 143: 206, # '▐' + 144: 207, # '░' + 145: 208, # '▒' + 146: 209, # '▓' + 147: 210, # '⌠' + 148: 211, # '■' + 149: 212, # '∙' + 150: 213, # '√' + 151: 214, # '≈' + 152: 215, # '≤' + 153: 216, # '≥' + 154: 217, # '\xa0' + 155: 218, # '⌡' + 156: 219, # '°' + 157: 220, # '²' + 158: 221, # '·' + 159: 222, # '÷' + 160: 223, # '═' + 161: 224, # '║' + 162: 225, # '╒' + 163: 68, # 'ё' + 164: 226, # '╓' + 165: 227, # '╔' + 166: 228, # '╕' + 167: 229, # '╖' + 168: 230, # '╗' + 169: 231, # '╘' + 170: 232, # '╙' + 171: 233, # '╚' + 172: 234, # '╛' + 173: 235, # '╜' + 174: 236, # '╝' + 175: 237, # '╞' + 176: 238, # '╟' + 177: 239, # '╠' + 178: 240, # '╡' + 179: 241, # 'Ё' + 180: 242, # '╢' + 181: 243, # '╣' + 182: 244, # '╤' + 183: 245, # '╥' + 184: 246, # '╦' + 185: 247, # '╧' + 186: 248, # '╨' + 187: 249, # '╩' + 188: 250, # '╪' + 189: 251, # '╫' + 190: 252, # '╬' + 191: 253, # '©' + 192: 27, # 'ю' + 193: 3, # 'а' + 194: 21, # 'б' + 195: 28, # 'ц' + 196: 13, # 'д' + 197: 2, # 'е' + 198: 39, # 'ф' + 199: 19, # 'г' + 200: 26, # 'х' + 201: 4, # 'и' + 202: 23, # 'й' + 203: 11, # 'к' + 204: 8, # 'л' + 205: 12, # 'м' + 206: 5, # 'н' + 207: 1, # 'о' + 208: 15, # 'п' + 209: 16, # 'я' + 210: 9, # 'р' + 211: 7, # 'с' + 212: 6, # 'т' + 213: 14, # 'у' + 214: 24, # 'ж' + 215: 10, # 'в' + 216: 17, # 'ь' + 217: 18, # 'ы' + 218: 20, # 'з' + 219: 25, # 'ш' + 220: 30, # 'э' + 221: 29, # 'щ' + 222: 22, # 'ч' + 223: 54, # 'ъ' + 224: 59, # 'Ю' + 225: 37, # 'А' + 226: 44, # 'Б' + 227: 58, # 'Ц' + 228: 41, # 'Д' + 229: 48, # 'Е' + 230: 53, # 'Ф' + 231: 46, # 'Г' + 232: 55, # 'Х' + 233: 42, # 'И' + 234: 60, # 'Й' + 235: 36, # 'К' + 236: 49, # 'Л' + 237: 38, # 'М' + 238: 31, # 'Н' + 239: 34, # 'О' + 240: 35, # 'П' + 241: 43, # 'Я' + 242: 45, # 'Р' + 243: 32, # 'С' + 244: 40, # 'Т' + 245: 52, # 'У' + 246: 56, # 'Ж' + 247: 33, # 'В' + 248: 61, # 'Ь' + 249: 62, # 'Ы' + 250: 51, # 'З' + 251: 57, # 'Ш' + 252: 47, # 'Э' + 253: 63, # 'Щ' + 254: 50, # 'Ч' + 255: 70, # 'Ъ' } -KOI8_R_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='KOI8-R', - language='Russian', - char_to_order_map=KOI8_R_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') +KOI8_R_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name='KOI8-R', + language='Russian', + char_to_order_map=KOI8_R_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё', +) MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 37, # 'А' - 129: 44, # 'Б' - 130: 33, # 'В' - 131: 46, # 'Г' - 132: 41, # 'Д' - 133: 48, # 'Е' - 134: 56, # 'Ж' - 135: 51, # 'З' - 136: 42, # 'И' - 137: 60, # 'Й' - 138: 36, # 'К' - 139: 49, # 'Л' - 140: 38, # 'М' - 141: 31, # 'Н' - 142: 34, # 'О' - 143: 35, # 'П' - 144: 45, # 'Р' - 145: 32, # 'С' - 146: 40, # 'Т' - 147: 52, # 'У' - 148: 53, # 'Ф' - 149: 55, # 'Х' - 150: 58, # 'Ц' - 151: 50, # 'Ч' - 152: 57, # 'Ш' - 153: 63, # 'Щ' - 154: 70, # 'Ъ' - 155: 62, # 'Ы' - 156: 61, # 'Ь' - 157: 47, # 'Э' - 158: 59, # 'Ю' - 159: 43, # 'Я' - 160: 191, # '†' - 161: 192, # '°' - 162: 193, # 'Ґ' - 163: 194, # '£' - 164: 195, # '§' - 165: 196, # '•' - 166: 197, # '¶' - 167: 198, # 'І' - 168: 199, # '®' - 169: 200, # '©' - 170: 201, # '™' - 171: 202, # 'Ђ' - 172: 203, # 'ђ' - 173: 204, # '≠' - 174: 205, # 'Ѓ' - 175: 206, # 'ѓ' - 176: 207, # '∞' - 177: 208, # '±' - 178: 209, # '≤' - 179: 210, # '≥' - 180: 211, # 'і' - 181: 212, # 'µ' - 182: 213, # 'ґ' - 183: 214, # 'Ј' - 184: 215, # 'Є' - 185: 216, # 'є' - 186: 217, # 'Ї' - 187: 218, # 'ї' - 188: 219, # 'Љ' - 189: 220, # 'љ' - 190: 221, # 'Њ' - 191: 222, # 'њ' - 192: 223, # 'ј' - 193: 224, # 'Ѕ' - 194: 225, # '¬' - 195: 226, # '√' - 196: 227, # 'ƒ' - 197: 228, # '≈' - 198: 229, # '∆' - 199: 230, # '«' - 200: 231, # '»' - 201: 232, # '…' - 202: 233, # '\xa0' - 203: 234, # 'Ћ' - 204: 235, # 'ћ' - 205: 236, # 'Ќ' - 206: 237, # 'ќ' - 207: 238, # 'ѕ' - 208: 239, # '–' - 209: 240, # '—' - 210: 241, # '“' - 211: 242, # '”' - 212: 243, # '‘' - 213: 244, # '’' - 214: 245, # '÷' - 215: 246, # '„' - 216: 247, # 'Ў' - 217: 248, # 'ў' - 218: 249, # 'Џ' - 219: 250, # 'џ' - 220: 251, # '№' - 221: 252, # 'Ё' - 222: 68, # 'ё' - 223: 16, # 'я' - 224: 3, # 'а' - 225: 21, # 'б' - 226: 10, # 'в' - 227: 19, # 'г' - 228: 13, # 'д' - 229: 2, # 'е' - 230: 24, # 'ж' - 231: 20, # 'з' - 232: 4, # 'и' - 233: 23, # 'й' - 234: 11, # 'к' - 235: 8, # 'л' - 236: 12, # 'м' - 237: 5, # 'н' - 238: 1, # 'о' - 239: 15, # 'п' - 240: 9, # 'р' - 241: 7, # 'с' - 242: 6, # 'т' - 243: 14, # 'у' - 244: 39, # 'ф' - 245: 26, # 'х' - 246: 28, # 'ц' - 247: 22, # 'ч' - 248: 25, # 'ш' - 249: 29, # 'щ' - 250: 54, # 'ъ' - 251: 18, # 'ы' - 252: 17, # 'ь' - 253: 30, # 'э' - 254: 27, # 'ю' - 255: 255, # '€' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 37, # 'А' + 129: 44, # 'Б' + 130: 33, # 'В' + 131: 46, # 'Г' + 132: 41, # 'Д' + 133: 48, # 'Е' + 134: 56, # 'Ж' + 135: 51, # 'З' + 136: 42, # 'И' + 137: 60, # 'Й' + 138: 36, # 'К' + 139: 49, # 'Л' + 140: 38, # 'М' + 141: 31, # 'Н' + 142: 34, # 'О' + 143: 35, # 'П' + 144: 45, # 'Р' + 145: 32, # 'С' + 146: 40, # 'Т' + 147: 52, # 'У' + 148: 53, # 'Ф' + 149: 55, # 'Х' + 150: 58, # 'Ц' + 151: 50, # 'Ч' + 152: 57, # 'Ш' + 153: 63, # 'Щ' + 154: 70, # 'Ъ' + 155: 62, # 'Ы' + 156: 61, # 'Ь' + 157: 47, # 'Э' + 158: 59, # 'Ю' + 159: 43, # 'Я' + 160: 191, # '†' + 161: 192, # '°' + 162: 193, # 'Ґ' + 163: 194, # '£' + 164: 195, # '§' + 165: 196, # '•' + 166: 197, # '¶' + 167: 198, # 'І' + 168: 199, # '®' + 169: 200, # '©' + 170: 201, # '™' + 171: 202, # 'Ђ' + 172: 203, # 'ђ' + 173: 204, # '≠' + 174: 205, # 'Ѓ' + 175: 206, # 'ѓ' + 176: 207, # '∞' + 177: 208, # '±' + 178: 209, # '≤' + 179: 210, # '≥' + 180: 211, # 'і' + 181: 212, # 'µ' + 182: 213, # 'ґ' + 183: 214, # 'Ј' + 184: 215, # 'Є' + 185: 216, # 'є' + 186: 217, # 'Ї' + 187: 218, # 'ї' + 188: 219, # 'Љ' + 189: 220, # 'љ' + 190: 221, # 'Њ' + 191: 222, # 'њ' + 192: 223, # 'ј' + 193: 224, # 'Ѕ' + 194: 225, # '¬' + 195: 226, # '√' + 196: 227, # 'ƒ' + 197: 228, # '≈' + 198: 229, # '∆' + 199: 230, # '«' + 200: 231, # '»' + 201: 232, # '…' + 202: 233, # '\xa0' + 203: 234, # 'Ћ' + 204: 235, # 'ћ' + 205: 236, # 'Ќ' + 206: 237, # 'ќ' + 207: 238, # 'ѕ' + 208: 239, # '–' + 209: 240, # '—' + 210: 241, # '“' + 211: 242, # '”' + 212: 243, # '‘' + 213: 244, # '’' + 214: 245, # '÷' + 215: 246, # '„' + 216: 247, # 'Ў' + 217: 248, # 'ў' + 218: 249, # 'Џ' + 219: 250, # 'џ' + 220: 251, # '№' + 221: 252, # 'Ё' + 222: 68, # 'ё' + 223: 16, # 'я' + 224: 3, # 'а' + 225: 21, # 'б' + 226: 10, # 'в' + 227: 19, # 'г' + 228: 13, # 'д' + 229: 2, # 'е' + 230: 24, # 'ж' + 231: 20, # 'з' + 232: 4, # 'и' + 233: 23, # 'й' + 234: 11, # 'к' + 235: 8, # 'л' + 236: 12, # 'м' + 237: 5, # 'н' + 238: 1, # 'о' + 239: 15, # 'п' + 240: 9, # 'р' + 241: 7, # 'с' + 242: 6, # 'т' + 243: 14, # 'у' + 244: 39, # 'ф' + 245: 26, # 'х' + 246: 28, # 'ц' + 247: 22, # 'ч' + 248: 25, # 'ш' + 249: 29, # 'щ' + 250: 54, # 'ъ' + 251: 18, # 'ы' + 252: 17, # 'ь' + 253: 30, # 'э' + 254: 27, # 'ю' + 255: 255, # '€' } -MACCYRILLIC_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='MacCyrillic', - language='Russian', - char_to_order_map=MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') +MACCYRILLIC_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name='MacCyrillic', + language='Russian', + char_to_order_map=MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё', +) ISO_8859_5_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 191, # '\x80' - 129: 192, # '\x81' - 130: 193, # '\x82' - 131: 194, # '\x83' - 132: 195, # '\x84' - 133: 196, # '\x85' - 134: 197, # '\x86' - 135: 198, # '\x87' - 136: 199, # '\x88' - 137: 200, # '\x89' - 138: 201, # '\x8a' - 139: 202, # '\x8b' - 140: 203, # '\x8c' - 141: 204, # '\x8d' - 142: 205, # '\x8e' - 143: 206, # '\x8f' - 144: 207, # '\x90' - 145: 208, # '\x91' - 146: 209, # '\x92' - 147: 210, # '\x93' - 148: 211, # '\x94' - 149: 212, # '\x95' - 150: 213, # '\x96' - 151: 214, # '\x97' - 152: 215, # '\x98' - 153: 216, # '\x99' - 154: 217, # '\x9a' - 155: 218, # '\x9b' - 156: 219, # '\x9c' - 157: 220, # '\x9d' - 158: 221, # '\x9e' - 159: 222, # '\x9f' - 160: 223, # '\xa0' - 161: 224, # 'Ё' - 162: 225, # 'Ђ' - 163: 226, # 'Ѓ' - 164: 227, # 'Є' - 165: 228, # 'Ѕ' - 166: 229, # 'І' - 167: 230, # 'Ї' - 168: 231, # 'Ј' - 169: 232, # 'Љ' - 170: 233, # 'Њ' - 171: 234, # 'Ћ' - 172: 235, # 'Ќ' - 173: 236, # '\xad' - 174: 237, # 'Ў' - 175: 238, # 'Џ' - 176: 37, # 'А' - 177: 44, # 'Б' - 178: 33, # 'В' - 179: 46, # 'Г' - 180: 41, # 'Д' - 181: 48, # 'Е' - 182: 56, # 'Ж' - 183: 51, # 'З' - 184: 42, # 'И' - 185: 60, # 'Й' - 186: 36, # 'К' - 187: 49, # 'Л' - 188: 38, # 'М' - 189: 31, # 'Н' - 190: 34, # 'О' - 191: 35, # 'П' - 192: 45, # 'Р' - 193: 32, # 'С' - 194: 40, # 'Т' - 195: 52, # 'У' - 196: 53, # 'Ф' - 197: 55, # 'Х' - 198: 58, # 'Ц' - 199: 50, # 'Ч' - 200: 57, # 'Ш' - 201: 63, # 'Щ' - 202: 70, # 'Ъ' - 203: 62, # 'Ы' - 204: 61, # 'Ь' - 205: 47, # 'Э' - 206: 59, # 'Ю' - 207: 43, # 'Я' - 208: 3, # 'а' - 209: 21, # 'б' - 210: 10, # 'в' - 211: 19, # 'г' - 212: 13, # 'д' - 213: 2, # 'е' - 214: 24, # 'ж' - 215: 20, # 'з' - 216: 4, # 'и' - 217: 23, # 'й' - 218: 11, # 'к' - 219: 8, # 'л' - 220: 12, # 'м' - 221: 5, # 'н' - 222: 1, # 'о' - 223: 15, # 'п' - 224: 9, # 'р' - 225: 7, # 'с' - 226: 6, # 'т' - 227: 14, # 'у' - 228: 39, # 'ф' - 229: 26, # 'х' - 230: 28, # 'ц' - 231: 22, # 'ч' - 232: 25, # 'ш' - 233: 29, # 'щ' - 234: 54, # 'ъ' - 235: 18, # 'ы' - 236: 17, # 'ь' - 237: 30, # 'э' - 238: 27, # 'ю' - 239: 16, # 'я' - 240: 239, # '№' - 241: 68, # 'ё' - 242: 240, # 'ђ' - 243: 241, # 'ѓ' - 244: 242, # 'є' - 245: 243, # 'ѕ' - 246: 244, # 'і' - 247: 245, # 'ї' - 248: 246, # 'ј' - 249: 247, # 'љ' - 250: 248, # 'њ' - 251: 249, # 'ћ' - 252: 250, # 'ќ' - 253: 251, # '§' - 254: 252, # 'ў' - 255: 255, # 'џ' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # '\x80' + 129: 192, # '\x81' + 130: 193, # '\x82' + 131: 194, # '\x83' + 132: 195, # '\x84' + 133: 196, # '\x85' + 134: 197, # '\x86' + 135: 198, # '\x87' + 136: 199, # '\x88' + 137: 200, # '\x89' + 138: 201, # '\x8a' + 139: 202, # '\x8b' + 140: 203, # '\x8c' + 141: 204, # '\x8d' + 142: 205, # '\x8e' + 143: 206, # '\x8f' + 144: 207, # '\x90' + 145: 208, # '\x91' + 146: 209, # '\x92' + 147: 210, # '\x93' + 148: 211, # '\x94' + 149: 212, # '\x95' + 150: 213, # '\x96' + 151: 214, # '\x97' + 152: 215, # '\x98' + 153: 216, # '\x99' + 154: 217, # '\x9a' + 155: 218, # '\x9b' + 156: 219, # '\x9c' + 157: 220, # '\x9d' + 158: 221, # '\x9e' + 159: 222, # '\x9f' + 160: 223, # '\xa0' + 161: 224, # 'Ё' + 162: 225, # 'Ђ' + 163: 226, # 'Ѓ' + 164: 227, # 'Є' + 165: 228, # 'Ѕ' + 166: 229, # 'І' + 167: 230, # 'Ї' + 168: 231, # 'Ј' + 169: 232, # 'Љ' + 170: 233, # 'Њ' + 171: 234, # 'Ћ' + 172: 235, # 'Ќ' + 173: 236, # '\xad' + 174: 237, # 'Ў' + 175: 238, # 'Џ' + 176: 37, # 'А' + 177: 44, # 'Б' + 178: 33, # 'В' + 179: 46, # 'Г' + 180: 41, # 'Д' + 181: 48, # 'Е' + 182: 56, # 'Ж' + 183: 51, # 'З' + 184: 42, # 'И' + 185: 60, # 'Й' + 186: 36, # 'К' + 187: 49, # 'Л' + 188: 38, # 'М' + 189: 31, # 'Н' + 190: 34, # 'О' + 191: 35, # 'П' + 192: 45, # 'Р' + 193: 32, # 'С' + 194: 40, # 'Т' + 195: 52, # 'У' + 196: 53, # 'Ф' + 197: 55, # 'Х' + 198: 58, # 'Ц' + 199: 50, # 'Ч' + 200: 57, # 'Ш' + 201: 63, # 'Щ' + 202: 70, # 'Ъ' + 203: 62, # 'Ы' + 204: 61, # 'Ь' + 205: 47, # 'Э' + 206: 59, # 'Ю' + 207: 43, # 'Я' + 208: 3, # 'а' + 209: 21, # 'б' + 210: 10, # 'в' + 211: 19, # 'г' + 212: 13, # 'д' + 213: 2, # 'е' + 214: 24, # 'ж' + 215: 20, # 'з' + 216: 4, # 'и' + 217: 23, # 'й' + 218: 11, # 'к' + 219: 8, # 'л' + 220: 12, # 'м' + 221: 5, # 'н' + 222: 1, # 'о' + 223: 15, # 'п' + 224: 9, # 'р' + 225: 7, # 'с' + 226: 6, # 'т' + 227: 14, # 'у' + 228: 39, # 'ф' + 229: 26, # 'х' + 230: 28, # 'ц' + 231: 22, # 'ч' + 232: 25, # 'ш' + 233: 29, # 'щ' + 234: 54, # 'ъ' + 235: 18, # 'ы' + 236: 17, # 'ь' + 237: 30, # 'э' + 238: 27, # 'ю' + 239: 16, # 'я' + 240: 239, # '№' + 241: 68, # 'ё' + 242: 240, # 'ђ' + 243: 241, # 'ѓ' + 244: 242, # 'є' + 245: 243, # 'ѕ' + 246: 244, # 'і' + 247: 245, # 'ї' + 248: 246, # 'ј' + 249: 247, # 'љ' + 250: 248, # 'њ' + 251: 249, # 'ћ' + 252: 250, # 'ќ' + 253: 251, # '§' + 254: 252, # 'ў' + 255: 255, # 'џ' } -ISO_8859_5_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-5', - language='Russian', - char_to_order_map=ISO_8859_5_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') - +ISO_8859_5_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name='ISO-8859-5', + language='Russian', + char_to_order_map=ISO_8859_5_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё', +) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langthaimodel.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langthaimodel.py index 9a37db5..06db920 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langthaimodel.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langthaimodel.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +from __future__ import annotations from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel @@ -4115,269 +4115,270 @@ THAI_LANG_MODEL = { # Character Mapping Table(s): TIS_620_THAI_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 182, # 'A' - 66: 106, # 'B' - 67: 107, # 'C' - 68: 100, # 'D' - 69: 183, # 'E' - 70: 184, # 'F' - 71: 185, # 'G' - 72: 101, # 'H' - 73: 94, # 'I' - 74: 186, # 'J' - 75: 187, # 'K' - 76: 108, # 'L' - 77: 109, # 'M' - 78: 110, # 'N' - 79: 111, # 'O' - 80: 188, # 'P' - 81: 189, # 'Q' - 82: 190, # 'R' - 83: 89, # 'S' - 84: 95, # 'T' - 85: 112, # 'U' - 86: 113, # 'V' - 87: 191, # 'W' - 88: 192, # 'X' - 89: 193, # 'Y' - 90: 194, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 64, # 'a' - 98: 72, # 'b' - 99: 73, # 'c' - 100: 114, # 'd' - 101: 74, # 'e' - 102: 115, # 'f' - 103: 116, # 'g' - 104: 102, # 'h' - 105: 81, # 'i' - 106: 201, # 'j' - 107: 117, # 'k' - 108: 90, # 'l' - 109: 103, # 'm' - 110: 78, # 'n' - 111: 82, # 'o' - 112: 96, # 'p' - 113: 202, # 'q' - 114: 91, # 'r' - 115: 79, # 's' - 116: 84, # 't' - 117: 104, # 'u' - 118: 105, # 'v' - 119: 97, # 'w' - 120: 98, # 'x' - 121: 92, # 'y' - 122: 203, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 209, # '\x80' - 129: 210, # '\x81' - 130: 211, # '\x82' - 131: 212, # '\x83' - 132: 213, # '\x84' - 133: 88, # '\x85' - 134: 214, # '\x86' - 135: 215, # '\x87' - 136: 216, # '\x88' - 137: 217, # '\x89' - 138: 218, # '\x8a' - 139: 219, # '\x8b' - 140: 220, # '\x8c' - 141: 118, # '\x8d' - 142: 221, # '\x8e' - 143: 222, # '\x8f' - 144: 223, # '\x90' - 145: 224, # '\x91' - 146: 99, # '\x92' - 147: 85, # '\x93' - 148: 83, # '\x94' - 149: 225, # '\x95' - 150: 226, # '\x96' - 151: 227, # '\x97' - 152: 228, # '\x98' - 153: 229, # '\x99' - 154: 230, # '\x9a' - 155: 231, # '\x9b' - 156: 232, # '\x9c' - 157: 233, # '\x9d' - 158: 234, # '\x9e' - 159: 235, # '\x9f' - 160: 236, # None - 161: 5, # 'ก' - 162: 30, # 'ข' - 163: 237, # 'ฃ' - 164: 24, # 'ค' - 165: 238, # 'ฅ' - 166: 75, # 'ฆ' - 167: 8, # 'ง' - 168: 26, # 'จ' - 169: 52, # 'ฉ' - 170: 34, # 'ช' - 171: 51, # 'ซ' - 172: 119, # 'ฌ' - 173: 47, # 'ญ' - 174: 58, # 'ฎ' - 175: 57, # 'ฏ' - 176: 49, # 'ฐ' - 177: 53, # 'ฑ' - 178: 55, # 'ฒ' - 179: 43, # 'ณ' - 180: 20, # 'ด' - 181: 19, # 'ต' - 182: 44, # 'ถ' - 183: 14, # 'ท' - 184: 48, # 'ธ' - 185: 3, # 'น' - 186: 17, # 'บ' - 187: 25, # 'ป' - 188: 39, # 'ผ' - 189: 62, # 'ฝ' - 190: 31, # 'พ' - 191: 54, # 'ฟ' - 192: 45, # 'ภ' - 193: 9, # 'ม' - 194: 16, # 'ย' - 195: 2, # 'ร' - 196: 61, # 'ฤ' - 197: 15, # 'ล' - 198: 239, # 'ฦ' - 199: 12, # 'ว' - 200: 42, # 'ศ' - 201: 46, # 'ษ' - 202: 18, # 'ส' - 203: 21, # 'ห' - 204: 76, # 'ฬ' - 205: 4, # 'อ' - 206: 66, # 'ฮ' - 207: 63, # 'ฯ' - 208: 22, # 'ะ' - 209: 10, # 'ั' - 210: 1, # 'า' - 211: 36, # 'ำ' - 212: 23, # 'ิ' - 213: 13, # 'ี' - 214: 40, # 'ึ' - 215: 27, # 'ื' - 216: 32, # 'ุ' - 217: 35, # 'ู' - 218: 86, # 'ฺ' - 219: 240, # None - 220: 241, # None - 221: 242, # None - 222: 243, # None - 223: 244, # '฿' - 224: 11, # 'เ' - 225: 28, # 'แ' - 226: 41, # 'โ' - 227: 29, # 'ใ' - 228: 33, # 'ไ' - 229: 245, # 'ๅ' - 230: 50, # 'ๆ' - 231: 37, # '็' - 232: 6, # '่' - 233: 7, # '้' - 234: 67, # '๊' - 235: 77, # '๋' - 236: 38, # '์' - 237: 93, # 'ํ' - 238: 246, # '๎' - 239: 247, # '๏' - 240: 68, # '๐' - 241: 56, # '๑' - 242: 59, # '๒' - 243: 65, # '๓' - 244: 69, # '๔' - 245: 60, # '๕' - 246: 70, # '๖' - 247: 80, # '๗' - 248: 71, # '๘' - 249: 87, # '๙' - 250: 248, # '๚' - 251: 249, # '๛' - 252: 250, # None - 253: 251, # None - 254: 252, # None - 255: 253, # None + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 182, # 'A' + 66: 106, # 'B' + 67: 107, # 'C' + 68: 100, # 'D' + 69: 183, # 'E' + 70: 184, # 'F' + 71: 185, # 'G' + 72: 101, # 'H' + 73: 94, # 'I' + 74: 186, # 'J' + 75: 187, # 'K' + 76: 108, # 'L' + 77: 109, # 'M' + 78: 110, # 'N' + 79: 111, # 'O' + 80: 188, # 'P' + 81: 189, # 'Q' + 82: 190, # 'R' + 83: 89, # 'S' + 84: 95, # 'T' + 85: 112, # 'U' + 86: 113, # 'V' + 87: 191, # 'W' + 88: 192, # 'X' + 89: 193, # 'Y' + 90: 194, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 64, # 'a' + 98: 72, # 'b' + 99: 73, # 'c' + 100: 114, # 'd' + 101: 74, # 'e' + 102: 115, # 'f' + 103: 116, # 'g' + 104: 102, # 'h' + 105: 81, # 'i' + 106: 201, # 'j' + 107: 117, # 'k' + 108: 90, # 'l' + 109: 103, # 'm' + 110: 78, # 'n' + 111: 82, # 'o' + 112: 96, # 'p' + 113: 202, # 'q' + 114: 91, # 'r' + 115: 79, # 's' + 116: 84, # 't' + 117: 104, # 'u' + 118: 105, # 'v' + 119: 97, # 'w' + 120: 98, # 'x' + 121: 92, # 'y' + 122: 203, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 209, # '\x80' + 129: 210, # '\x81' + 130: 211, # '\x82' + 131: 212, # '\x83' + 132: 213, # '\x84' + 133: 88, # '\x85' + 134: 214, # '\x86' + 135: 215, # '\x87' + 136: 216, # '\x88' + 137: 217, # '\x89' + 138: 218, # '\x8a' + 139: 219, # '\x8b' + 140: 220, # '\x8c' + 141: 118, # '\x8d' + 142: 221, # '\x8e' + 143: 222, # '\x8f' + 144: 223, # '\x90' + 145: 224, # '\x91' + 146: 99, # '\x92' + 147: 85, # '\x93' + 148: 83, # '\x94' + 149: 225, # '\x95' + 150: 226, # '\x96' + 151: 227, # '\x97' + 152: 228, # '\x98' + 153: 229, # '\x99' + 154: 230, # '\x9a' + 155: 231, # '\x9b' + 156: 232, # '\x9c' + 157: 233, # '\x9d' + 158: 234, # '\x9e' + 159: 235, # '\x9f' + 160: 236, # None + 161: 5, # 'ก' + 162: 30, # 'ข' + 163: 237, # 'ฃ' + 164: 24, # 'ค' + 165: 238, # 'ฅ' + 166: 75, # 'ฆ' + 167: 8, # 'ง' + 168: 26, # 'จ' + 169: 52, # 'ฉ' + 170: 34, # 'ช' + 171: 51, # 'ซ' + 172: 119, # 'ฌ' + 173: 47, # 'ญ' + 174: 58, # 'ฎ' + 175: 57, # 'ฏ' + 176: 49, # 'ฐ' + 177: 53, # 'ฑ' + 178: 55, # 'ฒ' + 179: 43, # 'ณ' + 180: 20, # 'ด' + 181: 19, # 'ต' + 182: 44, # 'ถ' + 183: 14, # 'ท' + 184: 48, # 'ธ' + 185: 3, # 'น' + 186: 17, # 'บ' + 187: 25, # 'ป' + 188: 39, # 'ผ' + 189: 62, # 'ฝ' + 190: 31, # 'พ' + 191: 54, # 'ฟ' + 192: 45, # 'ภ' + 193: 9, # 'ม' + 194: 16, # 'ย' + 195: 2, # 'ร' + 196: 61, # 'ฤ' + 197: 15, # 'ล' + 198: 239, # 'ฦ' + 199: 12, # 'ว' + 200: 42, # 'ศ' + 201: 46, # 'ษ' + 202: 18, # 'ส' + 203: 21, # 'ห' + 204: 76, # 'ฬ' + 205: 4, # 'อ' + 206: 66, # 'ฮ' + 207: 63, # 'ฯ' + 208: 22, # 'ะ' + 209: 10, # 'ั' + 210: 1, # 'า' + 211: 36, # 'ำ' + 212: 23, # 'ิ' + 213: 13, # 'ี' + 214: 40, # 'ึ' + 215: 27, # 'ื' + 216: 32, # 'ุ' + 217: 35, # 'ู' + 218: 86, # 'ฺ' + 219: 240, # None + 220: 241, # None + 221: 242, # None + 222: 243, # None + 223: 244, # '฿' + 224: 11, # 'เ' + 225: 28, # 'แ' + 226: 41, # 'โ' + 227: 29, # 'ใ' + 228: 33, # 'ไ' + 229: 245, # 'ๅ' + 230: 50, # 'ๆ' + 231: 37, # '็' + 232: 6, # '่' + 233: 7, # '้' + 234: 67, # '๊' + 235: 77, # '๋' + 236: 38, # '์' + 237: 93, # 'ํ' + 238: 246, # '๎' + 239: 247, # '๏' + 240: 68, # '๐' + 241: 56, # '๑' + 242: 59, # '๒' + 243: 65, # '๓' + 244: 69, # '๔' + 245: 60, # '๕' + 246: 70, # '๖' + 247: 80, # '๗' + 248: 71, # '๘' + 249: 87, # '๙' + 250: 248, # '๚' + 251: 249, # '๛' + 252: 250, # None + 253: 251, # None + 254: 252, # None + 255: 253, # None } -TIS_620_THAI_MODEL = SingleByteCharSetModel(charset_name='TIS-620', - language='Thai', - char_to_order_map=TIS_620_THAI_CHAR_TO_ORDER, - language_model=THAI_LANG_MODEL, - typical_positive_ratio=0.926386, - keep_ascii_letters=False, - alphabet='กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛') - +TIS_620_THAI_MODEL = SingleByteCharSetModel( + charset_name='TIS-620', + language='Thai', + char_to_order_map=TIS_620_THAI_CHAR_TO_ORDER, + language_model=THAI_LANG_MODEL, + typical_positive_ratio=0.926386, + keep_ascii_letters=False, + alphabet='กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛', +) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langturkishmodel.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langturkishmodel.py index 43f4230..1157685 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langturkishmodel.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/langturkishmodel.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +from __future__ import annotations from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel @@ -4115,269 +4115,270 @@ TURKISH_LANG_MODEL = { # Character Mapping Table(s): ISO_8859_9_TURKISH_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 255, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 255, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 255, # ' ' - 33: 255, # '!' - 34: 255, # '"' - 35: 255, # '#' - 36: 255, # '$' - 37: 255, # '%' - 38: 255, # '&' - 39: 255, # "'" - 40: 255, # '(' - 41: 255, # ')' - 42: 255, # '*' - 43: 255, # '+' - 44: 255, # ',' - 45: 255, # '-' - 46: 255, # '.' - 47: 255, # '/' - 48: 255, # '0' - 49: 255, # '1' - 50: 255, # '2' - 51: 255, # '3' - 52: 255, # '4' - 53: 255, # '5' - 54: 255, # '6' - 55: 255, # '7' - 56: 255, # '8' - 57: 255, # '9' - 58: 255, # ':' - 59: 255, # ';' - 60: 255, # '<' - 61: 255, # '=' - 62: 255, # '>' - 63: 255, # '?' - 64: 255, # '@' - 65: 23, # 'A' - 66: 37, # 'B' - 67: 47, # 'C' - 68: 39, # 'D' - 69: 29, # 'E' - 70: 52, # 'F' - 71: 36, # 'G' - 72: 45, # 'H' - 73: 53, # 'I' - 74: 60, # 'J' - 75: 16, # 'K' - 76: 49, # 'L' - 77: 20, # 'M' - 78: 46, # 'N' - 79: 42, # 'O' - 80: 48, # 'P' - 81: 69, # 'Q' - 82: 44, # 'R' - 83: 35, # 'S' - 84: 31, # 'T' - 85: 51, # 'U' - 86: 38, # 'V' - 87: 62, # 'W' - 88: 65, # 'X' - 89: 43, # 'Y' - 90: 56, # 'Z' - 91: 255, # '[' - 92: 255, # '\\' - 93: 255, # ']' - 94: 255, # '^' - 95: 255, # '_' - 96: 255, # '`' - 97: 1, # 'a' - 98: 21, # 'b' - 99: 28, # 'c' - 100: 12, # 'd' - 101: 2, # 'e' - 102: 18, # 'f' - 103: 27, # 'g' - 104: 25, # 'h' - 105: 3, # 'i' - 106: 24, # 'j' - 107: 10, # 'k' - 108: 5, # 'l' - 109: 13, # 'm' - 110: 4, # 'n' - 111: 15, # 'o' - 112: 26, # 'p' - 113: 64, # 'q' - 114: 7, # 'r' - 115: 8, # 's' - 116: 9, # 't' - 117: 14, # 'u' - 118: 32, # 'v' - 119: 57, # 'w' - 120: 58, # 'x' - 121: 11, # 'y' - 122: 22, # 'z' - 123: 255, # '{' - 124: 255, # '|' - 125: 255, # '}' - 126: 255, # '~' - 127: 255, # '\x7f' - 128: 180, # '\x80' - 129: 179, # '\x81' - 130: 178, # '\x82' - 131: 177, # '\x83' - 132: 176, # '\x84' - 133: 175, # '\x85' - 134: 174, # '\x86' - 135: 173, # '\x87' - 136: 172, # '\x88' - 137: 171, # '\x89' - 138: 170, # '\x8a' - 139: 169, # '\x8b' - 140: 168, # '\x8c' - 141: 167, # '\x8d' - 142: 166, # '\x8e' - 143: 165, # '\x8f' - 144: 164, # '\x90' - 145: 163, # '\x91' - 146: 162, # '\x92' - 147: 161, # '\x93' - 148: 160, # '\x94' - 149: 159, # '\x95' - 150: 101, # '\x96' - 151: 158, # '\x97' - 152: 157, # '\x98' - 153: 156, # '\x99' - 154: 155, # '\x9a' - 155: 154, # '\x9b' - 156: 153, # '\x9c' - 157: 152, # '\x9d' - 158: 151, # '\x9e' - 159: 106, # '\x9f' - 160: 150, # '\xa0' - 161: 149, # '¡' - 162: 148, # '¢' - 163: 147, # '£' - 164: 146, # '¤' - 165: 145, # '¥' - 166: 144, # '¦' - 167: 100, # '§' - 168: 143, # '¨' - 169: 142, # '©' - 170: 141, # 'ª' - 171: 140, # '«' - 172: 139, # '¬' - 173: 138, # '\xad' - 174: 137, # '®' - 175: 136, # '¯' - 176: 94, # '°' - 177: 80, # '±' - 178: 93, # '²' - 179: 135, # '³' - 180: 105, # '´' - 181: 134, # 'µ' - 182: 133, # '¶' - 183: 63, # '·' - 184: 132, # '¸' - 185: 131, # '¹' - 186: 130, # 'º' - 187: 129, # '»' - 188: 128, # '¼' - 189: 127, # '½' - 190: 126, # '¾' - 191: 125, # '¿' - 192: 124, # 'À' - 193: 104, # 'Á' - 194: 73, # 'Â' - 195: 99, # 'Ã' - 196: 79, # 'Ä' - 197: 85, # 'Å' - 198: 123, # 'Æ' - 199: 54, # 'Ç' - 200: 122, # 'È' - 201: 98, # 'É' - 202: 92, # 'Ê' - 203: 121, # 'Ë' - 204: 120, # 'Ì' - 205: 91, # 'Í' - 206: 103, # 'Î' - 207: 119, # 'Ï' - 208: 68, # 'Ğ' - 209: 118, # 'Ñ' - 210: 117, # 'Ò' - 211: 97, # 'Ó' - 212: 116, # 'Ô' - 213: 115, # 'Õ' - 214: 50, # 'Ö' - 215: 90, # '×' - 216: 114, # 'Ø' - 217: 113, # 'Ù' - 218: 112, # 'Ú' - 219: 111, # 'Û' - 220: 55, # 'Ü' - 221: 41, # 'İ' - 222: 40, # 'Ş' - 223: 86, # 'ß' - 224: 89, # 'à' - 225: 70, # 'á' - 226: 59, # 'â' - 227: 78, # 'ã' - 228: 71, # 'ä' - 229: 82, # 'å' - 230: 88, # 'æ' - 231: 33, # 'ç' - 232: 77, # 'è' - 233: 66, # 'é' - 234: 84, # 'ê' - 235: 83, # 'ë' - 236: 110, # 'ì' - 237: 75, # 'í' - 238: 61, # 'î' - 239: 96, # 'ï' - 240: 30, # 'ğ' - 241: 67, # 'ñ' - 242: 109, # 'ò' - 243: 74, # 'ó' - 244: 87, # 'ô' - 245: 102, # 'õ' - 246: 34, # 'ö' - 247: 95, # '÷' - 248: 81, # 'ø' - 249: 108, # 'ù' - 250: 76, # 'ú' - 251: 72, # 'û' - 252: 17, # 'ü' - 253: 6, # 'ı' - 254: 19, # 'ş' - 255: 107, # 'ÿ' + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 255, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 255, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 255, # ' ' + 33: 255, # '!' + 34: 255, # '"' + 35: 255, # '#' + 36: 255, # '$' + 37: 255, # '%' + 38: 255, # '&' + 39: 255, # "'" + 40: 255, # '(' + 41: 255, # ')' + 42: 255, # '*' + 43: 255, # '+' + 44: 255, # ',' + 45: 255, # '-' + 46: 255, # '.' + 47: 255, # '/' + 48: 255, # '0' + 49: 255, # '1' + 50: 255, # '2' + 51: 255, # '3' + 52: 255, # '4' + 53: 255, # '5' + 54: 255, # '6' + 55: 255, # '7' + 56: 255, # '8' + 57: 255, # '9' + 58: 255, # ':' + 59: 255, # ';' + 60: 255, # '<' + 61: 255, # '=' + 62: 255, # '>' + 63: 255, # '?' + 64: 255, # '@' + 65: 23, # 'A' + 66: 37, # 'B' + 67: 47, # 'C' + 68: 39, # 'D' + 69: 29, # 'E' + 70: 52, # 'F' + 71: 36, # 'G' + 72: 45, # 'H' + 73: 53, # 'I' + 74: 60, # 'J' + 75: 16, # 'K' + 76: 49, # 'L' + 77: 20, # 'M' + 78: 46, # 'N' + 79: 42, # 'O' + 80: 48, # 'P' + 81: 69, # 'Q' + 82: 44, # 'R' + 83: 35, # 'S' + 84: 31, # 'T' + 85: 51, # 'U' + 86: 38, # 'V' + 87: 62, # 'W' + 88: 65, # 'X' + 89: 43, # 'Y' + 90: 56, # 'Z' + 91: 255, # '[' + 92: 255, # '\\' + 93: 255, # ']' + 94: 255, # '^' + 95: 255, # '_' + 96: 255, # '`' + 97: 1, # 'a' + 98: 21, # 'b' + 99: 28, # 'c' + 100: 12, # 'd' + 101: 2, # 'e' + 102: 18, # 'f' + 103: 27, # 'g' + 104: 25, # 'h' + 105: 3, # 'i' + 106: 24, # 'j' + 107: 10, # 'k' + 108: 5, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 15, # 'o' + 112: 26, # 'p' + 113: 64, # 'q' + 114: 7, # 'r' + 115: 8, # 's' + 116: 9, # 't' + 117: 14, # 'u' + 118: 32, # 'v' + 119: 57, # 'w' + 120: 58, # 'x' + 121: 11, # 'y' + 122: 22, # 'z' + 123: 255, # '{' + 124: 255, # '|' + 125: 255, # '}' + 126: 255, # '~' + 127: 255, # '\x7f' + 128: 180, # '\x80' + 129: 179, # '\x81' + 130: 178, # '\x82' + 131: 177, # '\x83' + 132: 176, # '\x84' + 133: 175, # '\x85' + 134: 174, # '\x86' + 135: 173, # '\x87' + 136: 172, # '\x88' + 137: 171, # '\x89' + 138: 170, # '\x8a' + 139: 169, # '\x8b' + 140: 168, # '\x8c' + 141: 167, # '\x8d' + 142: 166, # '\x8e' + 143: 165, # '\x8f' + 144: 164, # '\x90' + 145: 163, # '\x91' + 146: 162, # '\x92' + 147: 161, # '\x93' + 148: 160, # '\x94' + 149: 159, # '\x95' + 150: 101, # '\x96' + 151: 158, # '\x97' + 152: 157, # '\x98' + 153: 156, # '\x99' + 154: 155, # '\x9a' + 155: 154, # '\x9b' + 156: 153, # '\x9c' + 157: 152, # '\x9d' + 158: 151, # '\x9e' + 159: 106, # '\x9f' + 160: 150, # '\xa0' + 161: 149, # '¡' + 162: 148, # '¢' + 163: 147, # '£' + 164: 146, # '¤' + 165: 145, # '¥' + 166: 144, # '¦' + 167: 100, # '§' + 168: 143, # '¨' + 169: 142, # '©' + 170: 141, # 'ª' + 171: 140, # '«' + 172: 139, # '¬' + 173: 138, # '\xad' + 174: 137, # '®' + 175: 136, # '¯' + 176: 94, # '°' + 177: 80, # '±' + 178: 93, # '²' + 179: 135, # '³' + 180: 105, # '´' + 181: 134, # 'µ' + 182: 133, # '¶' + 183: 63, # '·' + 184: 132, # '¸' + 185: 131, # '¹' + 186: 130, # 'º' + 187: 129, # '»' + 188: 128, # '¼' + 189: 127, # '½' + 190: 126, # '¾' + 191: 125, # '¿' + 192: 124, # 'À' + 193: 104, # 'Á' + 194: 73, # 'Â' + 195: 99, # 'Ã' + 196: 79, # 'Ä' + 197: 85, # 'Å' + 198: 123, # 'Æ' + 199: 54, # 'Ç' + 200: 122, # 'È' + 201: 98, # 'É' + 202: 92, # 'Ê' + 203: 121, # 'Ë' + 204: 120, # 'Ì' + 205: 91, # 'Í' + 206: 103, # 'Î' + 207: 119, # 'Ï' + 208: 68, # 'Ğ' + 209: 118, # 'Ñ' + 210: 117, # 'Ò' + 211: 97, # 'Ó' + 212: 116, # 'Ô' + 213: 115, # 'Õ' + 214: 50, # 'Ö' + 215: 90, # '×' + 216: 114, # 'Ø' + 217: 113, # 'Ù' + 218: 112, # 'Ú' + 219: 111, # 'Û' + 220: 55, # 'Ü' + 221: 41, # 'İ' + 222: 40, # 'Ş' + 223: 86, # 'ß' + 224: 89, # 'à' + 225: 70, # 'á' + 226: 59, # 'â' + 227: 78, # 'ã' + 228: 71, # 'ä' + 229: 82, # 'å' + 230: 88, # 'æ' + 231: 33, # 'ç' + 232: 77, # 'è' + 233: 66, # 'é' + 234: 84, # 'ê' + 235: 83, # 'ë' + 236: 110, # 'ì' + 237: 75, # 'í' + 238: 61, # 'î' + 239: 96, # 'ï' + 240: 30, # 'ğ' + 241: 67, # 'ñ' + 242: 109, # 'ò' + 243: 74, # 'ó' + 244: 87, # 'ô' + 245: 102, # 'õ' + 246: 34, # 'ö' + 247: 95, # '÷' + 248: 81, # 'ø' + 249: 108, # 'ù' + 250: 76, # 'ú' + 251: 72, # 'û' + 252: 17, # 'ü' + 253: 6, # 'ı' + 254: 19, # 'ş' + 255: 107, # 'ÿ' } -ISO_8859_9_TURKISH_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-9', - language='Turkish', - char_to_order_map=ISO_8859_9_TURKISH_CHAR_TO_ORDER, - language_model=TURKISH_LANG_MODEL, - typical_positive_ratio=0.97029, - keep_ascii_letters=True, - alphabet='ABCDEFGHIJKLMNOPRSTUVYZabcdefghijklmnoprstuvyzÂÇÎÖÛÜâçîöûüĞğİıŞş') - +ISO_8859_9_TURKISH_MODEL = SingleByteCharSetModel( + charset_name='ISO-8859-9', + language='Turkish', + char_to_order_map=ISO_8859_9_TURKISH_CHAR_TO_ORDER, + language_model=TURKISH_LANG_MODEL, + typical_positive_ratio=0.97029, + keep_ascii_letters=True, + alphabet='ABCDEFGHIJKLMNOPRSTUVYZabcdefghijklmnoprstuvyzÂÇÎÖÛÜâçîöûüĞğİıŞş', +) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/latin1prober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/latin1prober.py index 7d1e8c2..597dae0 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/latin1prober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/latin1prober.py @@ -25,6 +25,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from .charsetprober import CharSetProber from .enums import ProbingState @@ -81,21 +82,21 @@ Latin1_CharToClass = ( # 2 : normal # 3 : very likely Latin1ClassModel = ( -# UDF OTH ASC ASS ACV ACO ASV ASO - 0, 0, 0, 0, 0, 0, 0, 0, # UDF - 0, 3, 3, 3, 3, 3, 3, 3, # OTH - 0, 3, 3, 3, 3, 3, 3, 3, # ASC - 0, 3, 3, 3, 1, 1, 3, 3, # ASS - 0, 3, 3, 3, 1, 2, 1, 2, # ACV - 0, 3, 3, 3, 3, 3, 3, 3, # ACO - 0, 3, 1, 3, 1, 1, 1, 3, # ASV - 0, 3, 1, 3, 1, 1, 3, 3, # ASO + # UDF OTH ASC ASS ACV ACO ASV ASO + 0, 0, 0, 0, 0, 0, 0, 0, # UDF + 0, 3, 3, 3, 3, 3, 3, 3, # OTH + 0, 3, 3, 3, 3, 3, 3, 3, # ASC + 0, 3, 3, 3, 1, 1, 3, 3, # ASS + 0, 3, 3, 3, 1, 2, 1, 2, # ACV + 0, 3, 3, 3, 3, 3, 3, 3, # ACO + 0, 3, 1, 3, 1, 1, 1, 3, # ASV + 0, 3, 1, 3, 1, 1, 3, 3, # ASO ) class Latin1Prober(CharSetProber): def __init__(self): - super(Latin1Prober, self).__init__() + super().__init__() self._last_char_class = None self._freq_counter = None self.reset() @@ -107,18 +108,20 @@ class Latin1Prober(CharSetProber): @property def charset_name(self): - return "ISO-8859-1" + return 'ISO-8859-1' @property def language(self): - return "" + return '' def feed(self, byte_str): byte_str = self.filter_with_english_letters(byte_str) for c in byte_str: char_class = Latin1_CharToClass[c] - freq = Latin1ClassModel[(self._last_char_class * CLASS_NUM) - + char_class] + freq = Latin1ClassModel[ + (self._last_char_class * CLASS_NUM) + + char_class + ] if freq == 0: self._state = ProbingState.NOT_ME break @@ -135,8 +138,10 @@ class Latin1Prober(CharSetProber): if total < 0.01: confidence = 0.0 else: - confidence = ((self._freq_counter[3] - self._freq_counter[1] * 20.0) - / total) + confidence = ( + (self._freq_counter[3] - self._freq_counter[1] * 20.0) / + total + ) if confidence < 0.0: confidence = 0.0 # lower the confidence of latin1 so that other more accurate diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcharsetprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcharsetprober.py index 6256ecf..eeb7fb6 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcharsetprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcharsetprober.py @@ -26,9 +26,11 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from .charsetprober import CharSetProber -from .enums import ProbingState, MachineState +from .enums import MachineState +from .enums import ProbingState class MultiByteCharSetProber(CharSetProber): @@ -37,13 +39,13 @@ class MultiByteCharSetProber(CharSetProber): """ def __init__(self, lang_filter=None): - super(MultiByteCharSetProber, self).__init__(lang_filter=lang_filter) + super().__init__(lang_filter=lang_filter) self.distribution_analyzer = None self.coding_sm = None self._last_char = [0, 0] def reset(self): - super(MultiByteCharSetProber, self).reset() + super().reset() if self.coding_sm: self.coding_sm.reset() if self.distribution_analyzer: @@ -62,8 +64,10 @@ class MultiByteCharSetProber(CharSetProber): for i in range(len(byte_str)): coding_state = self.coding_sm.next_state(byte_str[i]) if coding_state == MachineState.ERROR: - self.logger.debug('%s %s prober hit error at byte %s', - self.charset_name, self.language, i) + self.logger.debug( + '%s %s prober hit error at byte %s', + self.charset_name, self.language, i, + ) self._state = ProbingState.NOT_ME break elif coding_state == MachineState.ITS_ME: @@ -75,14 +79,18 @@ class MultiByteCharSetProber(CharSetProber): self._last_char[1] = byte_str[0] self.distribution_analyzer.feed(self._last_char, char_len) else: - self.distribution_analyzer.feed(byte_str[i - 1:i + 1], - char_len) + self.distribution_analyzer.feed( + byte_str[i - 1:i + 1], + char_len, + ) self._last_char[0] = byte_str[-1] if self.state == ProbingState.DETECTING: - if (self.distribution_analyzer.got_enough_data() and - (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + if ( + self.distribution_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD) + ): self._state = ProbingState.FOUND_IT return self.state diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcsgroupprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcsgroupprober.py index 530abe7..d483559 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcsgroupprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcsgroupprober.py @@ -26,21 +26,22 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations -from .charsetgroupprober import CharSetGroupProber -from .utf8prober import UTF8Prober -from .sjisprober import SJISProber -from .eucjpprober import EUCJPProber -from .gb2312prober import GB2312Prober -from .euckrprober import EUCKRProber -from .cp949prober import CP949Prober from .big5prober import Big5Prober +from .charsetgroupprober import CharSetGroupProber +from .cp949prober import CP949Prober +from .eucjpprober import EUCJPProber +from .euckrprober import EUCKRProber from .euctwprober import EUCTWProber +from .gb2312prober import GB2312Prober +from .sjisprober import SJISProber +from .utf8prober import UTF8Prober class MBCSGroupProber(CharSetGroupProber): def __init__(self, lang_filter=None): - super(MBCSGroupProber, self).__init__(lang_filter=lang_filter) + super().__init__(lang_filter=lang_filter) self.probers = [ UTF8Prober(), SJISProber(), @@ -49,6 +50,6 @@ class MBCSGroupProber(CharSetGroupProber): EUCKRProber(), CP949Prober(), Big5Prober(), - EUCTWProber() + EUCTWProber(), ] self.reset() diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcssm.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcssm.py index 8360d0f..3aba0cc 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcssm.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcssm.py @@ -24,301 +24,312 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from .enums import MachineState # BIG5 BIG5_CLS = ( - 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as legal value - 1,1,1,1,1,1,0,0, # 08 - 0f - 1,1,1,1,1,1,1,1, # 10 - 17 - 1,1,1,0,1,1,1,1, # 18 - 1f - 1,1,1,1,1,1,1,1, # 20 - 27 - 1,1,1,1,1,1,1,1, # 28 - 2f - 1,1,1,1,1,1,1,1, # 30 - 37 - 1,1,1,1,1,1,1,1, # 38 - 3f - 2,2,2,2,2,2,2,2, # 40 - 47 - 2,2,2,2,2,2,2,2, # 48 - 4f - 2,2,2,2,2,2,2,2, # 50 - 57 - 2,2,2,2,2,2,2,2, # 58 - 5f - 2,2,2,2,2,2,2,2, # 60 - 67 - 2,2,2,2,2,2,2,2, # 68 - 6f - 2,2,2,2,2,2,2,2, # 70 - 77 - 2,2,2,2,2,2,2,1, # 78 - 7f - 4,4,4,4,4,4,4,4, # 80 - 87 - 4,4,4,4,4,4,4,4, # 88 - 8f - 4,4,4,4,4,4,4,4, # 90 - 97 - 4,4,4,4,4,4,4,4, # 98 - 9f - 4,3,3,3,3,3,3,3, # a0 - a7 - 3,3,3,3,3,3,3,3, # a8 - af - 3,3,3,3,3,3,3,3, # b0 - b7 - 3,3,3,3,3,3,3,3, # b8 - bf - 3,3,3,3,3,3,3,3, # c0 - c7 - 3,3,3,3,3,3,3,3, # c8 - cf - 3,3,3,3,3,3,3,3, # d0 - d7 - 3,3,3,3,3,3,3,3, # d8 - df - 3,3,3,3,3,3,3,3, # e0 - e7 - 3,3,3,3,3,3,3,3, # e8 - ef - 3,3,3,3,3,3,3,3, # f0 - f7 - 3,3,3,3,3,3,3,0 # f8 - ff + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 #allow 0x00 as legal value + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 + 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f + 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 + 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f + 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 + 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f + 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 + 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f + 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 + 2, 2, 2, 2, 2, 2, 2, 1, # 78 - 7f + 4, 4, 4, 4, 4, 4, 4, 4, # 80 - 87 + 4, 4, 4, 4, 4, 4, 4, 4, # 88 - 8f + 4, 4, 4, 4, 4, 4, 4, 4, # 90 - 97 + 4, 4, 4, 4, 4, 4, 4, 4, # 98 - 9f + 4, 3, 3, 3, 3, 3, 3, 3, # a0 - a7 + 3, 3, 3, 3, 3, 3, 3, 3, # a8 - af + 3, 3, 3, 3, 3, 3, 3, 3, # b0 - b7 + 3, 3, 3, 3, 3, 3, 3, 3, # b8 - bf + 3, 3, 3, 3, 3, 3, 3, 3, # c0 - c7 + 3, 3, 3, 3, 3, 3, 3, 3, # c8 - cf + 3, 3, 3, 3, 3, 3, 3, 3, # d0 - d7 + 3, 3, 3, 3, 3, 3, 3, 3, # d8 - df + 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7 + 3, 3, 3, 3, 3, 3, 3, 3, # e8 - ef + 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7 + 3, 3, 3, 3, 3, 3, 3, 0, # f8 - ff ) BIG5_ST = ( - MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,#08-0f - MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START#10-17 + MachineState.ERROR, MachineState.START, MachineState.START, 3, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, # 08-0f + MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 10-17 ) BIG5_CHAR_LEN_TABLE = (0, 1, 1, 2, 0) -BIG5_SM_MODEL = {'class_table': BIG5_CLS, - 'class_factor': 5, - 'state_table': BIG5_ST, - 'char_len_table': BIG5_CHAR_LEN_TABLE, - 'name': 'Big5'} +BIG5_SM_MODEL = { + 'class_table': BIG5_CLS, + 'class_factor': 5, + 'state_table': BIG5_ST, + 'char_len_table': BIG5_CHAR_LEN_TABLE, + 'name': 'Big5', +} # CP949 -CP949_CLS = ( - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, # 00 - 0f - 1,1,1,1,1,1,1,1, 1,1,1,0,1,1,1,1, # 10 - 1f - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 20 - 2f - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 30 - 3f - 1,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4, # 40 - 4f - 4,4,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 50 - 5f - 1,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5, # 60 - 6f - 5,5,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 70 - 7f - 0,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 80 - 8f - 6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 90 - 9f - 6,7,7,7,7,7,7,7, 7,7,7,7,7,8,8,8, # a0 - af - 7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7, # b0 - bf - 7,7,7,7,7,7,9,2, 2,3,2,2,2,2,2,2, # c0 - cf - 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # d0 - df - 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # e0 - ef - 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,0, # f0 - ff +CP949_CLS = ( + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, # 00 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, # 10 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 3f + 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, # 40 - 4f + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, # 50 - 5f + 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, # 60 - 6f + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, # 70 - 7f + 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, # 80 - 8f + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, # 90 - 9f + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, # a0 - af + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, # b0 - bf + 7, 7, 7, 7, 7, 7, 9, 2, 2, 3, 2, 2, 2, 2, 2, 2, # c0 - cf + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, # d0 - df + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, # e0 - ef + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, # f0 - ff ) CP949_ST = ( -#cls= 0 1 2 3 4 5 6 7 8 9 # previous state = - MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START, 4, 5,MachineState.ERROR, 6, # MachineState.START - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, # MachineState.ERROR - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 3 - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 4 - MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 5 - MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 6 + #cls= 0 1 2 3 4 5 6 7 8 9 # previous state = + MachineState.ERROR, MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, 4, 5, MachineState.ERROR, 6, # MachineState.START + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # MachineState.ERROR + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # MachineState.ITS_ME + MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, # 3 + MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 4 + MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 5 + MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, # 6 ) CP949_CHAR_LEN_TABLE = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) -CP949_SM_MODEL = {'class_table': CP949_CLS, - 'class_factor': 10, - 'state_table': CP949_ST, - 'char_len_table': CP949_CHAR_LEN_TABLE, - 'name': 'CP949'} +CP949_SM_MODEL = { + 'class_table': CP949_CLS, + 'class_factor': 10, + 'state_table': CP949_ST, + 'char_len_table': CP949_CHAR_LEN_TABLE, + 'name': 'CP949', +} # EUC-JP EUCJP_CLS = ( - 4,4,4,4,4,4,4,4, # 00 - 07 - 4,4,4,4,4,4,5,5, # 08 - 0f - 4,4,4,4,4,4,4,4, # 10 - 17 - 4,4,4,5,4,4,4,4, # 18 - 1f - 4,4,4,4,4,4,4,4, # 20 - 27 - 4,4,4,4,4,4,4,4, # 28 - 2f - 4,4,4,4,4,4,4,4, # 30 - 37 - 4,4,4,4,4,4,4,4, # 38 - 3f - 4,4,4,4,4,4,4,4, # 40 - 47 - 4,4,4,4,4,4,4,4, # 48 - 4f - 4,4,4,4,4,4,4,4, # 50 - 57 - 4,4,4,4,4,4,4,4, # 58 - 5f - 4,4,4,4,4,4,4,4, # 60 - 67 - 4,4,4,4,4,4,4,4, # 68 - 6f - 4,4,4,4,4,4,4,4, # 70 - 77 - 4,4,4,4,4,4,4,4, # 78 - 7f - 5,5,5,5,5,5,5,5, # 80 - 87 - 5,5,5,5,5,5,1,3, # 88 - 8f - 5,5,5,5,5,5,5,5, # 90 - 97 - 5,5,5,5,5,5,5,5, # 98 - 9f - 5,2,2,2,2,2,2,2, # a0 - a7 - 2,2,2,2,2,2,2,2, # a8 - af - 2,2,2,2,2,2,2,2, # b0 - b7 - 2,2,2,2,2,2,2,2, # b8 - bf - 2,2,2,2,2,2,2,2, # c0 - c7 - 2,2,2,2,2,2,2,2, # c8 - cf - 2,2,2,2,2,2,2,2, # d0 - d7 - 2,2,2,2,2,2,2,2, # d8 - df - 0,0,0,0,0,0,0,0, # e0 - e7 - 0,0,0,0,0,0,0,0, # e8 - ef - 0,0,0,0,0,0,0,0, # f0 - f7 - 0,0,0,0,0,0,0,5 # f8 - ff + 4, 4, 4, 4, 4, 4, 4, 4, # 00 - 07 + 4, 4, 4, 4, 4, 4, 5, 5, # 08 - 0f + 4, 4, 4, 4, 4, 4, 4, 4, # 10 - 17 + 4, 4, 4, 5, 4, 4, 4, 4, # 18 - 1f + 4, 4, 4, 4, 4, 4, 4, 4, # 20 - 27 + 4, 4, 4, 4, 4, 4, 4, 4, # 28 - 2f + 4, 4, 4, 4, 4, 4, 4, 4, # 30 - 37 + 4, 4, 4, 4, 4, 4, 4, 4, # 38 - 3f + 4, 4, 4, 4, 4, 4, 4, 4, # 40 - 47 + 4, 4, 4, 4, 4, 4, 4, 4, # 48 - 4f + 4, 4, 4, 4, 4, 4, 4, 4, # 50 - 57 + 4, 4, 4, 4, 4, 4, 4, 4, # 58 - 5f + 4, 4, 4, 4, 4, 4, 4, 4, # 60 - 67 + 4, 4, 4, 4, 4, 4, 4, 4, # 68 - 6f + 4, 4, 4, 4, 4, 4, 4, 4, # 70 - 77 + 4, 4, 4, 4, 4, 4, 4, 4, # 78 - 7f + 5, 5, 5, 5, 5, 5, 5, 5, # 80 - 87 + 5, 5, 5, 5, 5, 5, 1, 3, # 88 - 8f + 5, 5, 5, 5, 5, 5, 5, 5, # 90 - 97 + 5, 5, 5, 5, 5, 5, 5, 5, # 98 - 9f + 5, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7 + 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef + 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7 + 0, 0, 0, 0, 0, 0, 0, 5, # f8 - ff ) EUCJP_ST = ( - 3, 4, 3, 5,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 3,MachineState.ERROR,#18-1f - 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START#20-27 + 3, 4, 3, 5, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.START, MachineState.ERROR, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 10-17 + MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 3, MachineState.ERROR, # 18-1f + 3, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 20-27 ) EUCJP_CHAR_LEN_TABLE = (2, 2, 2, 3, 1, 0) -EUCJP_SM_MODEL = {'class_table': EUCJP_CLS, - 'class_factor': 6, - 'state_table': EUCJP_ST, - 'char_len_table': EUCJP_CHAR_LEN_TABLE, - 'name': 'EUC-JP'} +EUCJP_SM_MODEL = { + 'class_table': EUCJP_CLS, + 'class_factor': 6, + 'state_table': EUCJP_ST, + 'char_len_table': EUCJP_CHAR_LEN_TABLE, + 'name': 'EUC-JP', +} # EUC-KR -EUCKR_CLS = ( - 1,1,1,1,1,1,1,1, # 00 - 07 - 1,1,1,1,1,1,0,0, # 08 - 0f - 1,1,1,1,1,1,1,1, # 10 - 17 - 1,1,1,0,1,1,1,1, # 18 - 1f - 1,1,1,1,1,1,1,1, # 20 - 27 - 1,1,1,1,1,1,1,1, # 28 - 2f - 1,1,1,1,1,1,1,1, # 30 - 37 - 1,1,1,1,1,1,1,1, # 38 - 3f - 1,1,1,1,1,1,1,1, # 40 - 47 - 1,1,1,1,1,1,1,1, # 48 - 4f - 1,1,1,1,1,1,1,1, # 50 - 57 - 1,1,1,1,1,1,1,1, # 58 - 5f - 1,1,1,1,1,1,1,1, # 60 - 67 - 1,1,1,1,1,1,1,1, # 68 - 6f - 1,1,1,1,1,1,1,1, # 70 - 77 - 1,1,1,1,1,1,1,1, # 78 - 7f - 0,0,0,0,0,0,0,0, # 80 - 87 - 0,0,0,0,0,0,0,0, # 88 - 8f - 0,0,0,0,0,0,0,0, # 90 - 97 - 0,0,0,0,0,0,0,0, # 98 - 9f - 0,2,2,2,2,2,2,2, # a0 - a7 - 2,2,2,2,2,3,3,3, # a8 - af - 2,2,2,2,2,2,2,2, # b0 - b7 - 2,2,2,2,2,2,2,2, # b8 - bf - 2,2,2,2,2,2,2,2, # c0 - c7 - 2,3,2,2,2,2,2,2, # c8 - cf - 2,2,2,2,2,2,2,2, # d0 - d7 - 2,2,2,2,2,2,2,2, # d8 - df - 2,2,2,2,2,2,2,2, # e0 - e7 - 2,2,2,2,2,2,2,2, # e8 - ef - 2,2,2,2,2,2,2,2, # f0 - f7 - 2,2,2,2,2,2,2,0 # f8 - ff +EUCKR_CLS = ( + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 + 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f + 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, # 48 - 4f + 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 57 + 1, 1, 1, 1, 1, 1, 1, 1, # 58 - 5f + 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 67 + 1, 1, 1, 1, 1, 1, 1, 1, # 68 - 6f + 1, 1, 1, 1, 1, 1, 1, 1, # 70 - 77 + 1, 1, 1, 1, 1, 1, 1, 1, # 78 - 7f + 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 + 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f + 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 + 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f + 0, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 3, 3, 3, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 3, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 + 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef + 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 + 2, 2, 2, 2, 2, 2, 2, 0, # f8 - ff ) EUCKR_ST = ( - MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #08-0f + MachineState.ERROR, MachineState.START, 3, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 00-07 + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, # 08-0f ) EUCKR_CHAR_LEN_TABLE = (0, 1, 2, 0) -EUCKR_SM_MODEL = {'class_table': EUCKR_CLS, - 'class_factor': 4, - 'state_table': EUCKR_ST, - 'char_len_table': EUCKR_CHAR_LEN_TABLE, - 'name': 'EUC-KR'} +EUCKR_SM_MODEL = { + 'class_table': EUCKR_CLS, + 'class_factor': 4, + 'state_table': EUCKR_ST, + 'char_len_table': EUCKR_CHAR_LEN_TABLE, + 'name': 'EUC-KR', +} # EUC-TW EUCTW_CLS = ( - 2,2,2,2,2,2,2,2, # 00 - 07 - 2,2,2,2,2,2,0,0, # 08 - 0f - 2,2,2,2,2,2,2,2, # 10 - 17 - 2,2,2,0,2,2,2,2, # 18 - 1f - 2,2,2,2,2,2,2,2, # 20 - 27 - 2,2,2,2,2,2,2,2, # 28 - 2f - 2,2,2,2,2,2,2,2, # 30 - 37 - 2,2,2,2,2,2,2,2, # 38 - 3f - 2,2,2,2,2,2,2,2, # 40 - 47 - 2,2,2,2,2,2,2,2, # 48 - 4f - 2,2,2,2,2,2,2,2, # 50 - 57 - 2,2,2,2,2,2,2,2, # 58 - 5f - 2,2,2,2,2,2,2,2, # 60 - 67 - 2,2,2,2,2,2,2,2, # 68 - 6f - 2,2,2,2,2,2,2,2, # 70 - 77 - 2,2,2,2,2,2,2,2, # 78 - 7f - 0,0,0,0,0,0,0,0, # 80 - 87 - 0,0,0,0,0,0,6,0, # 88 - 8f - 0,0,0,0,0,0,0,0, # 90 - 97 - 0,0,0,0,0,0,0,0, # 98 - 9f - 0,3,4,4,4,4,4,4, # a0 - a7 - 5,5,1,1,1,1,1,1, # a8 - af - 1,1,1,1,1,1,1,1, # b0 - b7 - 1,1,1,1,1,1,1,1, # b8 - bf - 1,1,3,1,3,3,3,3, # c0 - c7 - 3,3,3,3,3,3,3,3, # c8 - cf - 3,3,3,3,3,3,3,3, # d0 - d7 - 3,3,3,3,3,3,3,3, # d8 - df - 3,3,3,3,3,3,3,3, # e0 - e7 - 3,3,3,3,3,3,3,3, # e8 - ef - 3,3,3,3,3,3,3,3, # f0 - f7 - 3,3,3,3,3,3,3,0 # f8 - ff + 2, 2, 2, 2, 2, 2, 2, 2, # 00 - 07 + 2, 2, 2, 2, 2, 2, 0, 0, # 08 - 0f + 2, 2, 2, 2, 2, 2, 2, 2, # 10 - 17 + 2, 2, 2, 0, 2, 2, 2, 2, # 18 - 1f + 2, 2, 2, 2, 2, 2, 2, 2, # 20 - 27 + 2, 2, 2, 2, 2, 2, 2, 2, # 28 - 2f + 2, 2, 2, 2, 2, 2, 2, 2, # 30 - 37 + 2, 2, 2, 2, 2, 2, 2, 2, # 38 - 3f + 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 + 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f + 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 + 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f + 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 + 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f + 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 + 2, 2, 2, 2, 2, 2, 2, 2, # 78 - 7f + 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 + 0, 0, 0, 0, 0, 0, 6, 0, # 88 - 8f + 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 + 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f + 0, 3, 4, 4, 4, 4, 4, 4, # a0 - a7 + 5, 5, 1, 1, 1, 1, 1, 1, # a8 - af + 1, 1, 1, 1, 1, 1, 1, 1, # b0 - b7 + 1, 1, 1, 1, 1, 1, 1, 1, # b8 - bf + 1, 1, 3, 1, 3, 3, 3, 3, # c0 - c7 + 3, 3, 3, 3, 3, 3, 3, 3, # c8 - cf + 3, 3, 3, 3, 3, 3, 3, 3, # d0 - d7 + 3, 3, 3, 3, 3, 3, 3, 3, # d8 - df + 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7 + 3, 3, 3, 3, 3, 3, 3, 3, # e8 - ef + 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7 + 3, 3, 3, 3, 3, 3, 3, 0, # f8 - ff ) EUCTW_ST = ( - MachineState.ERROR,MachineState.ERROR,MachineState.START, 3, 3, 3, 4,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.ERROR,#10-17 - MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f - 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,#20-27 - MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f + MachineState.ERROR, MachineState.ERROR, MachineState.START, 3, 3, 3, 4, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.START, MachineState.ERROR, # 10-17 + MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 18-1f + 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.ERROR, MachineState.START, MachineState.START, # 20-27 + MachineState.START, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 28-2f ) EUCTW_CHAR_LEN_TABLE = (0, 0, 1, 2, 2, 2, 3) -EUCTW_SM_MODEL = {'class_table': EUCTW_CLS, - 'class_factor': 7, - 'state_table': EUCTW_ST, - 'char_len_table': EUCTW_CHAR_LEN_TABLE, - 'name': 'x-euc-tw'} +EUCTW_SM_MODEL = { + 'class_table': EUCTW_CLS, + 'class_factor': 7, + 'state_table': EUCTW_ST, + 'char_len_table': EUCTW_CHAR_LEN_TABLE, + 'name': 'x-euc-tw', +} # GB2312 GB2312_CLS = ( - 1,1,1,1,1,1,1,1, # 00 - 07 - 1,1,1,1,1,1,0,0, # 08 - 0f - 1,1,1,1,1,1,1,1, # 10 - 17 - 1,1,1,0,1,1,1,1, # 18 - 1f - 1,1,1,1,1,1,1,1, # 20 - 27 - 1,1,1,1,1,1,1,1, # 28 - 2f - 3,3,3,3,3,3,3,3, # 30 - 37 - 3,3,1,1,1,1,1,1, # 38 - 3f - 2,2,2,2,2,2,2,2, # 40 - 47 - 2,2,2,2,2,2,2,2, # 48 - 4f - 2,2,2,2,2,2,2,2, # 50 - 57 - 2,2,2,2,2,2,2,2, # 58 - 5f - 2,2,2,2,2,2,2,2, # 60 - 67 - 2,2,2,2,2,2,2,2, # 68 - 6f - 2,2,2,2,2,2,2,2, # 70 - 77 - 2,2,2,2,2,2,2,4, # 78 - 7f - 5,6,6,6,6,6,6,6, # 80 - 87 - 6,6,6,6,6,6,6,6, # 88 - 8f - 6,6,6,6,6,6,6,6, # 90 - 97 - 6,6,6,6,6,6,6,6, # 98 - 9f - 6,6,6,6,6,6,6,6, # a0 - a7 - 6,6,6,6,6,6,6,6, # a8 - af - 6,6,6,6,6,6,6,6, # b0 - b7 - 6,6,6,6,6,6,6,6, # b8 - bf - 6,6,6,6,6,6,6,6, # c0 - c7 - 6,6,6,6,6,6,6,6, # c8 - cf - 6,6,6,6,6,6,6,6, # d0 - d7 - 6,6,6,6,6,6,6,6, # d8 - df - 6,6,6,6,6,6,6,6, # e0 - e7 - 6,6,6,6,6,6,6,6, # e8 - ef - 6,6,6,6,6,6,6,6, # f0 - f7 - 6,6,6,6,6,6,6,0 # f8 - ff + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 3, 3, 3, 3, 3, 3, 3, 3, # 30 - 37 + 3, 3, 1, 1, 1, 1, 1, 1, # 38 - 3f + 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 + 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f + 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 + 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f + 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 + 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f + 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 + 2, 2, 2, 2, 2, 2, 2, 4, # 78 - 7f + 5, 6, 6, 6, 6, 6, 6, 6, # 80 - 87 + 6, 6, 6, 6, 6, 6, 6, 6, # 88 - 8f + 6, 6, 6, 6, 6, 6, 6, 6, # 90 - 97 + 6, 6, 6, 6, 6, 6, 6, 6, # 98 - 9f + 6, 6, 6, 6, 6, 6, 6, 6, # a0 - a7 + 6, 6, 6, 6, 6, 6, 6, 6, # a8 - af + 6, 6, 6, 6, 6, 6, 6, 6, # b0 - b7 + 6, 6, 6, 6, 6, 6, 6, 6, # b8 - bf + 6, 6, 6, 6, 6, 6, 6, 6, # c0 - c7 + 6, 6, 6, 6, 6, 6, 6, 6, # c8 - cf + 6, 6, 6, 6, 6, 6, 6, 6, # d0 - d7 + 6, 6, 6, 6, 6, 6, 6, 6, # d8 - df + 6, 6, 6, 6, 6, 6, 6, 6, # e0 - e7 + 6, 6, 6, 6, 6, 6, 6, 6, # e8 - ef + 6, 6, 6, 6, 6, 6, 6, 6, # f0 - f7 + 6, 6, 6, 6, 6, 6, 6, 0, # f8 - ff ) GB2312_ST = ( - MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, 3,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,#10-17 - 4,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f - MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#20-27 - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f + MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, 3, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.START, # 10-17 + 4, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 18-1f + MachineState.ERROR, MachineState.ERROR, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, # 20-27 + MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 28-2f ) # To be accurate, the length of class 6 can be either 2 or 4. @@ -328,245 +339,256 @@ GB2312_ST = ( # 2 here. GB2312_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 1, 2) -GB2312_SM_MODEL = {'class_table': GB2312_CLS, - 'class_factor': 7, - 'state_table': GB2312_ST, - 'char_len_table': GB2312_CHAR_LEN_TABLE, - 'name': 'GB2312'} +GB2312_SM_MODEL = { + 'class_table': GB2312_CLS, + 'class_factor': 7, + 'state_table': GB2312_ST, + 'char_len_table': GB2312_CHAR_LEN_TABLE, + 'name': 'GB2312', +} # Shift_JIS SJIS_CLS = ( - 1,1,1,1,1,1,1,1, # 00 - 07 - 1,1,1,1,1,1,0,0, # 08 - 0f - 1,1,1,1,1,1,1,1, # 10 - 17 - 1,1,1,0,1,1,1,1, # 18 - 1f - 1,1,1,1,1,1,1,1, # 20 - 27 - 1,1,1,1,1,1,1,1, # 28 - 2f - 1,1,1,1,1,1,1,1, # 30 - 37 - 1,1,1,1,1,1,1,1, # 38 - 3f - 2,2,2,2,2,2,2,2, # 40 - 47 - 2,2,2,2,2,2,2,2, # 48 - 4f - 2,2,2,2,2,2,2,2, # 50 - 57 - 2,2,2,2,2,2,2,2, # 58 - 5f - 2,2,2,2,2,2,2,2, # 60 - 67 - 2,2,2,2,2,2,2,2, # 68 - 6f - 2,2,2,2,2,2,2,2, # 70 - 77 - 2,2,2,2,2,2,2,1, # 78 - 7f - 3,3,3,3,3,2,2,3, # 80 - 87 - 3,3,3,3,3,3,3,3, # 88 - 8f - 3,3,3,3,3,3,3,3, # 90 - 97 - 3,3,3,3,3,3,3,3, # 98 - 9f + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 + 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f + 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 + 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f + 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 + 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f + 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 + 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f + 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 + 2, 2, 2, 2, 2, 2, 2, 1, # 78 - 7f + 3, 3, 3, 3, 3, 2, 2, 3, # 80 - 87 + 3, 3, 3, 3, 3, 3, 3, 3, # 88 - 8f + 3, 3, 3, 3, 3, 3, 3, 3, # 90 - 97 + 3, 3, 3, 3, 3, 3, 3, 3, # 98 - 9f #0xa0 is illegal in sjis encoding, but some pages does #contain such byte. We need to be more error forgiven. - 2,2,2,2,2,2,2,2, # a0 - a7 - 2,2,2,2,2,2,2,2, # a8 - af - 2,2,2,2,2,2,2,2, # b0 - b7 - 2,2,2,2,2,2,2,2, # b8 - bf - 2,2,2,2,2,2,2,2, # c0 - c7 - 2,2,2,2,2,2,2,2, # c8 - cf - 2,2,2,2,2,2,2,2, # d0 - d7 - 2,2,2,2,2,2,2,2, # d8 - df - 3,3,3,3,3,3,3,3, # e0 - e7 - 3,3,3,3,3,4,4,4, # e8 - ef - 3,3,3,3,3,3,3,3, # f0 - f7 - 3,3,3,3,3,0,0,0) # f8 - ff + 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7 + 3, 3, 3, 3, 3, 4, 4, 4, # e8 - ef + 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7 + 3, 3, 3, 3, 3, 0, 0, 0, +) # f8 - ff SJIS_ST = ( - MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START #10-17 + MachineState.ERROR, MachineState.START, MachineState.START, 3, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 10-17 ) SJIS_CHAR_LEN_TABLE = (0, 1, 1, 2, 0, 0) -SJIS_SM_MODEL = {'class_table': SJIS_CLS, - 'class_factor': 6, - 'state_table': SJIS_ST, - 'char_len_table': SJIS_CHAR_LEN_TABLE, - 'name': 'Shift_JIS'} +SJIS_SM_MODEL = { + 'class_table': SJIS_CLS, + 'class_factor': 6, + 'state_table': SJIS_ST, + 'char_len_table': SJIS_CHAR_LEN_TABLE, + 'name': 'Shift_JIS', +} # UCS2-BE UCS2BE_CLS = ( - 0,0,0,0,0,0,0,0, # 00 - 07 - 0,0,1,0,0,2,0,0, # 08 - 0f - 0,0,0,0,0,0,0,0, # 10 - 17 - 0,0,0,3,0,0,0,0, # 18 - 1f - 0,0,0,0,0,0,0,0, # 20 - 27 - 0,3,3,3,3,3,0,0, # 28 - 2f - 0,0,0,0,0,0,0,0, # 30 - 37 - 0,0,0,0,0,0,0,0, # 38 - 3f - 0,0,0,0,0,0,0,0, # 40 - 47 - 0,0,0,0,0,0,0,0, # 48 - 4f - 0,0,0,0,0,0,0,0, # 50 - 57 - 0,0,0,0,0,0,0,0, # 58 - 5f - 0,0,0,0,0,0,0,0, # 60 - 67 - 0,0,0,0,0,0,0,0, # 68 - 6f - 0,0,0,0,0,0,0,0, # 70 - 77 - 0,0,0,0,0,0,0,0, # 78 - 7f - 0,0,0,0,0,0,0,0, # 80 - 87 - 0,0,0,0,0,0,0,0, # 88 - 8f - 0,0,0,0,0,0,0,0, # 90 - 97 - 0,0,0,0,0,0,0,0, # 98 - 9f - 0,0,0,0,0,0,0,0, # a0 - a7 - 0,0,0,0,0,0,0,0, # a8 - af - 0,0,0,0,0,0,0,0, # b0 - b7 - 0,0,0,0,0,0,0,0, # b8 - bf - 0,0,0,0,0,0,0,0, # c0 - c7 - 0,0,0,0,0,0,0,0, # c8 - cf - 0,0,0,0,0,0,0,0, # d0 - d7 - 0,0,0,0,0,0,0,0, # d8 - df - 0,0,0,0,0,0,0,0, # e0 - e7 - 0,0,0,0,0,0,0,0, # e8 - ef - 0,0,0,0,0,0,0,0, # f0 - f7 - 0,0,0,0,0,0,4,5 # f8 - ff + 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 1, 0, 0, 2, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 3, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 + 0, 3, 3, 3, 3, 3, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 + 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f + 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 + 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f + 0, 0, 0, 0, 0, 0, 0, 0, # a0 - a7 + 0, 0, 0, 0, 0, 0, 0, 0, # a8 - af + 0, 0, 0, 0, 0, 0, 0, 0, # b0 - b7 + 0, 0, 0, 0, 0, 0, 0, 0, # b8 - bf + 0, 0, 0, 0, 0, 0, 0, 0, # c0 - c7 + 0, 0, 0, 0, 0, 0, 0, 0, # c8 - cf + 0, 0, 0, 0, 0, 0, 0, 0, # d0 - d7 + 0, 0, 0, 0, 0, 0, 0, 0, # d8 - df + 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7 + 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef + 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7 + 0, 0, 0, 0, 0, 0, 4, 5, # f8 - ff ) -UCS2BE_ST = ( - 5, 7, 7,MachineState.ERROR, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME, 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,#10-17 - 6, 6, 6, 6, 6,MachineState.ITS_ME, 6, 6,#18-1f - 6, 6, 6, 6, 5, 7, 7,MachineState.ERROR,#20-27 - 5, 8, 6, 6,MachineState.ERROR, 6, 6, 6,#28-2f - 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #30-37 +UCS2BE_ST = ( + 5, 7, 7, MachineState.ERROR, 4, 3, MachineState.ERROR, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f + MachineState.ITS_ME, MachineState.ITS_ME, 6, 6, 6, 6, MachineState.ERROR, MachineState.ERROR, # 10-17 + 6, 6, 6, 6, 6, MachineState.ITS_ME, 6, 6, # 18-1f + 6, 6, 6, 6, 5, 7, 7, MachineState.ERROR, # 20-27 + 5, 8, 6, 6, MachineState.ERROR, 6, 6, 6, # 28-2f + 6, 6, 6, 6, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, # 30-37 ) UCS2BE_CHAR_LEN_TABLE = (2, 2, 2, 0, 2, 2) -UCS2BE_SM_MODEL = {'class_table': UCS2BE_CLS, - 'class_factor': 6, - 'state_table': UCS2BE_ST, - 'char_len_table': UCS2BE_CHAR_LEN_TABLE, - 'name': 'UTF-16BE'} +UCS2BE_SM_MODEL = { + 'class_table': UCS2BE_CLS, + 'class_factor': 6, + 'state_table': UCS2BE_ST, + 'char_len_table': UCS2BE_CHAR_LEN_TABLE, + 'name': 'UTF-16BE', +} # UCS2-LE UCS2LE_CLS = ( - 0,0,0,0,0,0,0,0, # 00 - 07 - 0,0,1,0,0,2,0,0, # 08 - 0f - 0,0,0,0,0,0,0,0, # 10 - 17 - 0,0,0,3,0,0,0,0, # 18 - 1f - 0,0,0,0,0,0,0,0, # 20 - 27 - 0,3,3,3,3,3,0,0, # 28 - 2f - 0,0,0,0,0,0,0,0, # 30 - 37 - 0,0,0,0,0,0,0,0, # 38 - 3f - 0,0,0,0,0,0,0,0, # 40 - 47 - 0,0,0,0,0,0,0,0, # 48 - 4f - 0,0,0,0,0,0,0,0, # 50 - 57 - 0,0,0,0,0,0,0,0, # 58 - 5f - 0,0,0,0,0,0,0,0, # 60 - 67 - 0,0,0,0,0,0,0,0, # 68 - 6f - 0,0,0,0,0,0,0,0, # 70 - 77 - 0,0,0,0,0,0,0,0, # 78 - 7f - 0,0,0,0,0,0,0,0, # 80 - 87 - 0,0,0,0,0,0,0,0, # 88 - 8f - 0,0,0,0,0,0,0,0, # 90 - 97 - 0,0,0,0,0,0,0,0, # 98 - 9f - 0,0,0,0,0,0,0,0, # a0 - a7 - 0,0,0,0,0,0,0,0, # a8 - af - 0,0,0,0,0,0,0,0, # b0 - b7 - 0,0,0,0,0,0,0,0, # b8 - bf - 0,0,0,0,0,0,0,0, # c0 - c7 - 0,0,0,0,0,0,0,0, # c8 - cf - 0,0,0,0,0,0,0,0, # d0 - d7 - 0,0,0,0,0,0,0,0, # d8 - df - 0,0,0,0,0,0,0,0, # e0 - e7 - 0,0,0,0,0,0,0,0, # e8 - ef - 0,0,0,0,0,0,0,0, # f0 - f7 - 0,0,0,0,0,0,4,5 # f8 - ff + 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 1, 0, 0, 2, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 3, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 + 0, 3, 3, 3, 3, 3, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 + 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f + 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 + 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f + 0, 0, 0, 0, 0, 0, 0, 0, # a0 - a7 + 0, 0, 0, 0, 0, 0, 0, 0, # a8 - af + 0, 0, 0, 0, 0, 0, 0, 0, # b0 - b7 + 0, 0, 0, 0, 0, 0, 0, 0, # b8 - bf + 0, 0, 0, 0, 0, 0, 0, 0, # c0 - c7 + 0, 0, 0, 0, 0, 0, 0, 0, # c8 - cf + 0, 0, 0, 0, 0, 0, 0, 0, # d0 - d7 + 0, 0, 0, 0, 0, 0, 0, 0, # d8 - df + 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7 + 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef + 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7 + 0, 0, 0, 0, 0, 0, 4, 5, # f8 - ff ) UCS2LE_ST = ( - 6, 6, 7, 6, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME, 5, 5, 5,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#10-17 - 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR, 6, 6,#18-1f - 7, 6, 8, 8, 5, 5, 5,MachineState.ERROR,#20-27 - 5, 5, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5,#28-2f - 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR,MachineState.START,MachineState.START #30-37 + 6, 6, 7, 6, 4, 3, MachineState.ERROR, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f + MachineState.ITS_ME, MachineState.ITS_ME, 5, 5, 5, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, # 10-17 + 5, 5, 5, MachineState.ERROR, 5, MachineState.ERROR, 6, 6, # 18-1f + 7, 6, 8, 8, 5, 5, 5, MachineState.ERROR, # 20-27 + 5, 5, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 5, 5, # 28-2f + 5, 5, 5, MachineState.ERROR, 5, MachineState.ERROR, MachineState.START, MachineState.START, # 30-37 ) UCS2LE_CHAR_LEN_TABLE = (2, 2, 2, 2, 2, 2) -UCS2LE_SM_MODEL = {'class_table': UCS2LE_CLS, - 'class_factor': 6, - 'state_table': UCS2LE_ST, - 'char_len_table': UCS2LE_CHAR_LEN_TABLE, - 'name': 'UTF-16LE'} +UCS2LE_SM_MODEL = { + 'class_table': UCS2LE_CLS, + 'class_factor': 6, + 'state_table': UCS2LE_ST, + 'char_len_table': UCS2LE_CHAR_LEN_TABLE, + 'name': 'UTF-16LE', +} # UTF-8 UTF8_CLS = ( - 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as a legal value - 1,1,1,1,1,1,0,0, # 08 - 0f - 1,1,1,1,1,1,1,1, # 10 - 17 - 1,1,1,0,1,1,1,1, # 18 - 1f - 1,1,1,1,1,1,1,1, # 20 - 27 - 1,1,1,1,1,1,1,1, # 28 - 2f - 1,1,1,1,1,1,1,1, # 30 - 37 - 1,1,1,1,1,1,1,1, # 38 - 3f - 1,1,1,1,1,1,1,1, # 40 - 47 - 1,1,1,1,1,1,1,1, # 48 - 4f - 1,1,1,1,1,1,1,1, # 50 - 57 - 1,1,1,1,1,1,1,1, # 58 - 5f - 1,1,1,1,1,1,1,1, # 60 - 67 - 1,1,1,1,1,1,1,1, # 68 - 6f - 1,1,1,1,1,1,1,1, # 70 - 77 - 1,1,1,1,1,1,1,1, # 78 - 7f - 2,2,2,2,3,3,3,3, # 80 - 87 - 4,4,4,4,4,4,4,4, # 88 - 8f - 4,4,4,4,4,4,4,4, # 90 - 97 - 4,4,4,4,4,4,4,4, # 98 - 9f - 5,5,5,5,5,5,5,5, # a0 - a7 - 5,5,5,5,5,5,5,5, # a8 - af - 5,5,5,5,5,5,5,5, # b0 - b7 - 5,5,5,5,5,5,5,5, # b8 - bf - 0,0,6,6,6,6,6,6, # c0 - c7 - 6,6,6,6,6,6,6,6, # c8 - cf - 6,6,6,6,6,6,6,6, # d0 - d7 - 6,6,6,6,6,6,6,6, # d8 - df - 7,8,8,8,8,8,8,8, # e0 - e7 - 8,8,8,8,8,9,8,8, # e8 - ef - 10,11,11,11,11,11,11,11, # f0 - f7 - 12,13,13,13,14,15,0,0 # f8 - ff + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 #allow 0x00 as a legal value + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 + 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f + 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, # 48 - 4f + 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 57 + 1, 1, 1, 1, 1, 1, 1, 1, # 58 - 5f + 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 67 + 1, 1, 1, 1, 1, 1, 1, 1, # 68 - 6f + 1, 1, 1, 1, 1, 1, 1, 1, # 70 - 77 + 1, 1, 1, 1, 1, 1, 1, 1, # 78 - 7f + 2, 2, 2, 2, 3, 3, 3, 3, # 80 - 87 + 4, 4, 4, 4, 4, 4, 4, 4, # 88 - 8f + 4, 4, 4, 4, 4, 4, 4, 4, # 90 - 97 + 4, 4, 4, 4, 4, 4, 4, 4, # 98 - 9f + 5, 5, 5, 5, 5, 5, 5, 5, # a0 - a7 + 5, 5, 5, 5, 5, 5, 5, 5, # a8 - af + 5, 5, 5, 5, 5, 5, 5, 5, # b0 - b7 + 5, 5, 5, 5, 5, 5, 5, 5, # b8 - bf + 0, 0, 6, 6, 6, 6, 6, 6, # c0 - c7 + 6, 6, 6, 6, 6, 6, 6, 6, # c8 - cf + 6, 6, 6, 6, 6, 6, 6, 6, # d0 - d7 + 6, 6, 6, 6, 6, 6, 6, 6, # d8 - df + 7, 8, 8, 8, 8, 8, 8, 8, # e0 - e7 + 8, 8, 8, 8, 8, 9, 8, 8, # e8 - ef + 10, 11, 11, 11, 11, 11, 11, 11, # f0 - f7 + 12, 13, 13, 13, 14, 15, 0, 0, # f8 - ff ) UTF8_ST = ( - MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12, 10,#00-07 - 9, 11, 8, 7, 6, 5, 4, 3,#08-0f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#20-27 - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#28-2f - MachineState.ERROR,MachineState.ERROR, 5, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#30-37 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#38-3f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#40-47 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#48-4f - MachineState.ERROR,MachineState.ERROR, 7, 7, 7, 7,MachineState.ERROR,MachineState.ERROR,#50-57 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#58-5f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 7, 7,MachineState.ERROR,MachineState.ERROR,#60-67 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#68-6f - MachineState.ERROR,MachineState.ERROR, 9, 9, 9, 9,MachineState.ERROR,MachineState.ERROR,#70-77 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#78-7f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 9,MachineState.ERROR,MachineState.ERROR,#80-87 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#88-8f - MachineState.ERROR,MachineState.ERROR, 12, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,#90-97 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#98-9f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12,MachineState.ERROR,MachineState.ERROR,#a0-a7 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#a8-af - MachineState.ERROR,MachineState.ERROR, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b0-b7 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b8-bf - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,#c0-c7 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR #c8-cf + MachineState.ERROR, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 12, 10, # 00-07 + 9, 11, 8, 7, 6, 5, 4, 3, # 08-0f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 10-17 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 18-1f + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 20-27 + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 28-2f + MachineState.ERROR, MachineState.ERROR, 5, 5, 5, 5, MachineState.ERROR, MachineState.ERROR, # 30-37 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 38-3f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 5, 5, 5, MachineState.ERROR, MachineState.ERROR, # 40-47 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 48-4f + MachineState.ERROR, MachineState.ERROR, 7, 7, 7, 7, MachineState.ERROR, MachineState.ERROR, # 50-57 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 58-5f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 7, 7, MachineState.ERROR, MachineState.ERROR, # 60-67 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 68-6f + MachineState.ERROR, MachineState.ERROR, 9, 9, 9, 9, MachineState.ERROR, MachineState.ERROR, # 70-77 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 78-7f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 9, MachineState.ERROR, MachineState.ERROR, # 80-87 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 88-8f + MachineState.ERROR, MachineState.ERROR, 12, 12, 12, 12, MachineState.ERROR, MachineState.ERROR, # 90-97 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 98-9f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 12, MachineState.ERROR, MachineState.ERROR, # a0-a7 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # a8-af + MachineState.ERROR, MachineState.ERROR, 12, 12, 12, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # b0-b7 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # b8-bf + MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, # c0-c7 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # c8-cf ) UTF8_CHAR_LEN_TABLE = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) -UTF8_SM_MODEL = {'class_table': UTF8_CLS, - 'class_factor': 16, - 'state_table': UTF8_ST, - 'char_len_table': UTF8_CHAR_LEN_TABLE, - 'name': 'UTF-8'} +UTF8_SM_MODEL = { + 'class_table': UTF8_CLS, + 'class_factor': 16, + 'state_table': UTF8_ST, + 'char_len_table': UTF8_CHAR_LEN_TABLE, + 'name': 'UTF-8', +} diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/languages.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/languages.py index 3237d5a..5dc831f 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/languages.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/languages.py @@ -1,19 +1,18 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Metadata about languages used by our model training code for our SingleByteCharSetProbers. Could be used for other things in the future. This code is based on the language metadata from the uchardet project. """ -from __future__ import absolute_import, print_function +from __future__ import annotations from string import ascii_letters # TODO: Add Ukranian (KOI8-U) -class Language(object): +class Language: """Metadata about a language useful for training models :ivar name: The human name for the language, in English. @@ -33,9 +32,12 @@ class Language(object): Wikipedia for training data. :type wiki_start_pages: list of str """ - def __init__(self, name=None, iso_code=None, use_ascii=True, charsets=None, - alphabet=None, wiki_start_pages=None): - super(Language, self).__init__() + + def __init__( + self, name=None, iso_code=None, use_ascii=True, charsets=None, + alphabet=None, wiki_start_pages=None, + ): + super().__init__() self.name = name self.iso_code = iso_code self.use_ascii = use_ascii @@ -51,260 +53,393 @@ class Language(object): self.wiki_start_pages = wiki_start_pages def __repr__(self): - return '{}({})'.format(self.__class__.__name__, - ', '.join('{}={!r}'.format(k, v) - for k, v in self.__dict__.items() - if not k.startswith('_'))) + return '{}({})'.format( + self.__class__.__name__, + ', '.join( + f'{k}={v!r}' + for k, v in self.__dict__.items() + if not k.startswith('_') + ), + ) -LANGUAGES = {'Arabic': Language(name='Arabic', - iso_code='ar', - use_ascii=False, - # We only support encodings that use isolated - # forms, because the current recommendation is - # that the rendering system handles presentation - # forms. This means we purposefully skip IBM864. - charsets=['ISO-8859-6', 'WINDOWS-1256', - 'CP720', 'CP864'], - alphabet=u'ءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّ', - wiki_start_pages=[u'الصفحة_الرئيسية']), - 'Belarusian': Language(name='Belarusian', - iso_code='be', - use_ascii=False, - charsets=['ISO-8859-5', 'WINDOWS-1251', - 'IBM866', 'MacCyrillic'], - alphabet=(u'АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШЫЬЭЮЯ' - u'абвгдеёжзійклмнопрстуўфхцчшыьэюяʼ'), - wiki_start_pages=[u'Галоўная_старонка']), - 'Bulgarian': Language(name='Bulgarian', - iso_code='bg', - use_ascii=False, - charsets=['ISO-8859-5', 'WINDOWS-1251', - 'IBM855'], - alphabet=(u'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯ' - u'абвгдежзийклмнопрстуфхцчшщъьюя'), - wiki_start_pages=[u'Начална_страница']), - 'Czech': Language(name='Czech', - iso_code='cz', - use_ascii=True, - charsets=['ISO-8859-2', 'WINDOWS-1250'], - alphabet=u'áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ', - wiki_start_pages=[u'Hlavní_strana']), - 'Danish': Language(name='Danish', - iso_code='da', - use_ascii=True, - charsets=['ISO-8859-1', 'ISO-8859-15', - 'WINDOWS-1252'], - alphabet=u'æøåÆØÅ', - wiki_start_pages=[u'Forside']), - 'German': Language(name='German', - iso_code='de', - use_ascii=True, - charsets=['ISO-8859-1', 'WINDOWS-1252'], - alphabet=u'äöüßÄÖÜ', - wiki_start_pages=[u'Wikipedia:Hauptseite']), - 'Greek': Language(name='Greek', - iso_code='el', - use_ascii=False, - charsets=['ISO-8859-7', 'WINDOWS-1253'], - alphabet=(u'αβγδεζηθικλμνξοπρσςτυφχψωάέήίόύώ' - u'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΣΤΥΦΧΨΩΆΈΉΊΌΎΏ'), - wiki_start_pages=[u'Πύλη:Κύρια']), - 'English': Language(name='English', - iso_code='en', - use_ascii=True, - charsets=['ISO-8859-1', 'WINDOWS-1252'], - wiki_start_pages=[u'Main_Page']), - 'Esperanto': Language(name='Esperanto', - iso_code='eo', - # Q, W, X, and Y not used at all - use_ascii=False, - charsets=['ISO-8859-3'], - alphabet=(u'abcĉdefgĝhĥijĵklmnoprsŝtuŭvz' - u'ABCĈDEFGĜHĤIJĴKLMNOPRSŜTUŬVZ'), - wiki_start_pages=[u'Vikipedio:Ĉefpaĝo']), - 'Spanish': Language(name='Spanish', - iso_code='es', - use_ascii=True, - charsets=['ISO-8859-1', 'ISO-8859-15', - 'WINDOWS-1252'], - alphabet=u'ñáéíóúüÑÁÉÍÓÚÜ', - wiki_start_pages=[u'Wikipedia:Portada']), - 'Estonian': Language(name='Estonian', - iso_code='et', - use_ascii=False, - charsets=['ISO-8859-4', 'ISO-8859-13', - 'WINDOWS-1257'], - # C, F, Š, Q, W, X, Y, Z, Ž are only for - # loanwords - alphabet=(u'ABDEGHIJKLMNOPRSTUVÕÄÖÜ' - u'abdeghijklmnoprstuvõäöü'), - wiki_start_pages=[u'Esileht']), - 'Finnish': Language(name='Finnish', - iso_code='fi', - use_ascii=True, - charsets=['ISO-8859-1', 'ISO-8859-15', - 'WINDOWS-1252'], - alphabet=u'ÅÄÖŠŽåäöšž', - wiki_start_pages=[u'Wikipedia:Etusivu']), - 'French': Language(name='French', - iso_code='fr', - use_ascii=True, - charsets=['ISO-8859-1', 'ISO-8859-15', - 'WINDOWS-1252'], - alphabet=u'œàâçèéîïùûêŒÀÂÇÈÉÎÏÙÛÊ', - wiki_start_pages=[u'Wikipédia:Accueil_principal', - u'Bœuf (animal)']), - 'Hebrew': Language(name='Hebrew', - iso_code='he', - use_ascii=False, - charsets=['ISO-8859-8', 'WINDOWS-1255'], - alphabet=u'אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ', - wiki_start_pages=[u'עמוד_ראשי']), - 'Croatian': Language(name='Croatian', - iso_code='hr', - # Q, W, X, Y are only used for foreign words. - use_ascii=False, - charsets=['ISO-8859-2', 'WINDOWS-1250'], - alphabet=(u'abcčćdđefghijklmnoprsštuvzž' - u'ABCČĆDĐEFGHIJKLMNOPRSŠTUVZŽ'), - wiki_start_pages=[u'Glavna_stranica']), - 'Hungarian': Language(name='Hungarian', - iso_code='hu', - # Q, W, X, Y are only used for foreign words. - use_ascii=False, - charsets=['ISO-8859-2', 'WINDOWS-1250'], - alphabet=(u'abcdefghijklmnoprstuvzáéíóöőúüű' - u'ABCDEFGHIJKLMNOPRSTUVZÁÉÍÓÖŐÚÜŰ'), - wiki_start_pages=[u'Kezdőlap']), - 'Italian': Language(name='Italian', - iso_code='it', - use_ascii=True, - charsets=['ISO-8859-1', 'ISO-8859-15', - 'WINDOWS-1252'], - alphabet=u'ÀÈÉÌÒÓÙàèéìòóù', - wiki_start_pages=[u'Pagina_principale']), - 'Lithuanian': Language(name='Lithuanian', - iso_code='lt', - use_ascii=False, - charsets=['ISO-8859-13', 'WINDOWS-1257', - 'ISO-8859-4'], - # Q, W, and X not used at all - alphabet=(u'AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ' - u'aąbcčdeęėfghiįyjklmnoprsštuųūvzž'), - wiki_start_pages=[u'Pagrindinis_puslapis']), - 'Latvian': Language(name='Latvian', - iso_code='lv', - use_ascii=False, - charsets=['ISO-8859-13', 'WINDOWS-1257', - 'ISO-8859-4'], - # Q, W, X, Y are only for loanwords - alphabet=(u'AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ' - u'aābcčdeēfgģhiījkķlļmnņoprsštuūvzž'), - wiki_start_pages=[u'Sākumlapa']), - 'Macedonian': Language(name='Macedonian', - iso_code='mk', - use_ascii=False, - charsets=['ISO-8859-5', 'WINDOWS-1251', - 'MacCyrillic', 'IBM855'], - alphabet=(u'АБВГДЃЕЖЗЅИЈКЛЉМНЊОПРСТЌУФХЦЧЏШ' - u'абвгдѓежзѕијклљмнњопрстќуфхцчџш'), - wiki_start_pages=[u'Главна_страница']), - 'Dutch': Language(name='Dutch', - iso_code='nl', - use_ascii=True, - charsets=['ISO-8859-1', 'WINDOWS-1252'], - wiki_start_pages=[u'Hoofdpagina']), - 'Polish': Language(name='Polish', - iso_code='pl', - # Q and X are only used for foreign words. - use_ascii=False, - charsets=['ISO-8859-2', 'WINDOWS-1250'], - alphabet=(u'AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŹŻ' - u'aąbcćdeęfghijklłmnńoóprsśtuwyzźż'), - wiki_start_pages=[u'Wikipedia:Strona_główna']), - 'Portuguese': Language(name='Portuguese', - iso_code='pt', - use_ascii=True, - charsets=['ISO-8859-1', 'ISO-8859-15', - 'WINDOWS-1252'], - alphabet=u'ÁÂÃÀÇÉÊÍÓÔÕÚáâãàçéêíóôõú', - wiki_start_pages=[u'Wikipédia:Página_principal']), - 'Romanian': Language(name='Romanian', - iso_code='ro', - use_ascii=True, - charsets=['ISO-8859-2', 'WINDOWS-1250'], - alphabet=u'ăâîșțĂÂÎȘȚ', - wiki_start_pages=[u'Pagina_principală']), - 'Russian': Language(name='Russian', - iso_code='ru', - use_ascii=False, - charsets=['ISO-8859-5', 'WINDOWS-1251', - 'KOI8-R', 'MacCyrillic', 'IBM866', - 'IBM855'], - alphabet=(u'абвгдеёжзийклмнопрстуфхцчшщъыьэюя' - u'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'), - wiki_start_pages=[u'Заглавная_страница']), - 'Slovak': Language(name='Slovak', - iso_code='sk', - use_ascii=True, - charsets=['ISO-8859-2', 'WINDOWS-1250'], - alphabet=u'áäčďéíĺľňóôŕšťúýžÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽ', - wiki_start_pages=[u'Hlavná_stránka']), - 'Slovene': Language(name='Slovene', - iso_code='sl', - # Q, W, X, Y are only used for foreign words. - use_ascii=False, - charsets=['ISO-8859-2', 'WINDOWS-1250'], - alphabet=(u'abcčdefghijklmnoprsštuvzž' - u'ABCČDEFGHIJKLMNOPRSŠTUVZŽ'), - wiki_start_pages=[u'Glavna_stran']), - # Serbian can be written in both Latin and Cyrillic, but there's no - # simple way to get the Latin alphabet pages from Wikipedia through - # the API, so for now we just support Cyrillic. - 'Serbian': Language(name='Serbian', - iso_code='sr', - alphabet=(u'АБВГДЂЕЖЗИЈКЛЉМНЊОПРСТЋУФХЦЧЏШ' - u'абвгдђежзијклљмнњопрстћуфхцчџш'), - charsets=['ISO-8859-5', 'WINDOWS-1251', - 'MacCyrillic', 'IBM855'], - wiki_start_pages=[u'Главна_страна']), - 'Thai': Language(name='Thai', - iso_code='th', - use_ascii=False, - charsets=['ISO-8859-11', 'TIS-620', 'CP874'], - alphabet=u'กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛', - wiki_start_pages=[u'หน้าหลัก']), - 'Turkish': Language(name='Turkish', - iso_code='tr', - # Q, W, and X are not used by Turkish - use_ascii=False, - charsets=['ISO-8859-3', 'ISO-8859-9', - 'WINDOWS-1254'], - alphabet=(u'abcçdefgğhıijklmnoöprsştuüvyzâîû' - u'ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZÂÎÛ'), - wiki_start_pages=[u'Ana_Sayfa']), - 'Vietnamese': Language(name='Vietnamese', - iso_code='vi', - use_ascii=False, - # Windows-1258 is the only common 8-bit - # Vietnamese encoding supported by Python. - # From Wikipedia: - # For systems that lack support for Unicode, - # dozens of 8-bit Vietnamese code pages are - # available.[1] The most common are VISCII - # (TCVN 5712:1993), VPS, and Windows-1258.[3] - # Where ASCII is required, such as when - # ensuring readability in plain text e-mail, - # Vietnamese letters are often encoded - # according to Vietnamese Quoted-Readable - # (VIQR) or VSCII Mnemonic (VSCII-MNEM),[4] - # though usage of either variable-width - # scheme has declined dramatically following - # the adoption of Unicode on the World Wide - # Web. - charsets=['WINDOWS-1258'], - alphabet=(u'aăâbcdđeêghiklmnoôơpqrstuưvxy' - u'AĂÂBCDĐEÊGHIKLMNOÔƠPQRSTUƯVXY'), - wiki_start_pages=[u'Chữ_Quốc_ngữ']), - } +LANGUAGES = { + 'Arabic': Language( + name='Arabic', + iso_code='ar', + use_ascii=False, + # We only support encodings that use isolated + # forms, because the current recommendation is + # that the rendering system handles presentation + # forms. This means we purposefully skip IBM864. + charsets=[ + 'ISO-8859-6', 'WINDOWS-1256', + 'CP720', 'CP864', + ], + alphabet='ءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّ', + wiki_start_pages=['الصفحة_الرئيسية'], + ), + 'Belarusian': Language( + name='Belarusian', + iso_code='be', + use_ascii=False, + charsets=[ + 'ISO-8859-5', 'WINDOWS-1251', + 'IBM866', 'MacCyrillic', + ], + alphabet=( + 'АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШЫЬЭЮЯ' + 'абвгдеёжзійклмнопрстуўфхцчшыьэюяʼ' + ), + wiki_start_pages=['Галоўная_старонка'], + ), + 'Bulgarian': Language( + name='Bulgarian', + iso_code='bg', + use_ascii=False, + charsets=[ + 'ISO-8859-5', 'WINDOWS-1251', + 'IBM855', + ], + alphabet=( + 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯ' + 'абвгдежзийклмнопрстуфхцчшщъьюя' + ), + wiki_start_pages=['Начална_страница'], + ), + 'Czech': Language( + name='Czech', + iso_code='cz', + use_ascii=True, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet='áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ', + wiki_start_pages=['Hlavní_strana'], + ), + 'Danish': Language( + name='Danish', + iso_code='da', + use_ascii=True, + charsets=[ + 'ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252', + ], + alphabet='æøåÆØÅ', + wiki_start_pages=['Forside'], + ), + 'German': Language( + name='German', + iso_code='de', + use_ascii=True, + charsets=['ISO-8859-1', 'WINDOWS-1252'], + alphabet='äöüßÄÖÜ', + wiki_start_pages=['Wikipedia:Hauptseite'], + ), + 'Greek': Language( + name='Greek', + iso_code='el', + use_ascii=False, + charsets=['ISO-8859-7', 'WINDOWS-1253'], + alphabet=( + 'αβγδεζηθικλμνξοπρσςτυφχψωάέήίόύώ' + 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΣΤΥΦΧΨΩΆΈΉΊΌΎΏ' + ), + wiki_start_pages=['Πύλη:Κύρια'], + ), + 'English': Language( + name='English', + iso_code='en', + use_ascii=True, + charsets=['ISO-8859-1', 'WINDOWS-1252'], + wiki_start_pages=['Main_Page'], + ), + 'Esperanto': Language( + name='Esperanto', + iso_code='eo', + # Q, W, X, and Y not used at all + use_ascii=False, + charsets=['ISO-8859-3'], + alphabet=( + 'abcĉdefgĝhĥijĵklmnoprsŝtuŭvz' + 'ABCĈDEFGĜHĤIJĴKLMNOPRSŜTUŬVZ' + ), + wiki_start_pages=['Vikipedio:Ĉefpaĝo'], + ), + 'Spanish': Language( + name='Spanish', + iso_code='es', + use_ascii=True, + charsets=[ + 'ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252', + ], + alphabet='ñáéíóúüÑÁÉÍÓÚÜ', + wiki_start_pages=['Wikipedia:Portada'], + ), + 'Estonian': Language( + name='Estonian', + iso_code='et', + use_ascii=False, + charsets=[ + 'ISO-8859-4', 'ISO-8859-13', + 'WINDOWS-1257', + ], + # C, F, Š, Q, W, X, Y, Z, Ž are only for + # loanwords + alphabet=( + 'ABDEGHIJKLMNOPRSTUVÕÄÖÜ' + 'abdeghijklmnoprstuvõäöü' + ), + wiki_start_pages=['Esileht'], + ), + 'Finnish': Language( + name='Finnish', + iso_code='fi', + use_ascii=True, + charsets=[ + 'ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252', + ], + alphabet='ÅÄÖŠŽåäöšž', + wiki_start_pages=['Wikipedia:Etusivu'], + ), + 'French': Language( + name='French', + iso_code='fr', + use_ascii=True, + charsets=[ + 'ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252', + ], + alphabet='œàâçèéîïùûêŒÀÂÇÈÉÎÏÙÛÊ', + wiki_start_pages=[ + 'Wikipédia:Accueil_principal', + 'Bœuf (animal)', + ], + ), + 'Hebrew': Language( + name='Hebrew', + iso_code='he', + use_ascii=False, + charsets=['ISO-8859-8', 'WINDOWS-1255'], + alphabet='אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ', + wiki_start_pages=['עמוד_ראשי'], + ), + 'Croatian': Language( + name='Croatian', + iso_code='hr', + # Q, W, X, Y are only used for foreign words. + use_ascii=False, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=( + 'abcčćdđefghijklmnoprsštuvzž' + 'ABCČĆDĐEFGHIJKLMNOPRSŠTUVZŽ' + ), + wiki_start_pages=['Glavna_stranica'], + ), + 'Hungarian': Language( + name='Hungarian', + iso_code='hu', + # Q, W, X, Y are only used for foreign words. + use_ascii=False, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=( + 'abcdefghijklmnoprstuvzáéíóöőúüű' + 'ABCDEFGHIJKLMNOPRSTUVZÁÉÍÓÖŐÚÜŰ' + ), + wiki_start_pages=['Kezdőlap'], + ), + 'Italian': Language( + name='Italian', + iso_code='it', + use_ascii=True, + charsets=[ + 'ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252', + ], + alphabet='ÀÈÉÌÒÓÙàèéìòóù', + wiki_start_pages=['Pagina_principale'], + ), + 'Lithuanian': Language( + name='Lithuanian', + iso_code='lt', + use_ascii=False, + charsets=[ + 'ISO-8859-13', 'WINDOWS-1257', + 'ISO-8859-4', + ], + # Q, W, and X not used at all + alphabet=( + 'AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ' + 'aąbcčdeęėfghiįyjklmnoprsštuųūvzž' + ), + wiki_start_pages=['Pagrindinis_puslapis'], + ), + 'Latvian': Language( + name='Latvian', + iso_code='lv', + use_ascii=False, + charsets=[ + 'ISO-8859-13', 'WINDOWS-1257', + 'ISO-8859-4', + ], + # Q, W, X, Y are only for loanwords + alphabet=( + 'AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ' + 'aābcčdeēfgģhiījkķlļmnņoprsštuūvzž' + ), + wiki_start_pages=['Sākumlapa'], + ), + 'Macedonian': Language( + name='Macedonian', + iso_code='mk', + use_ascii=False, + charsets=[ + 'ISO-8859-5', 'WINDOWS-1251', + 'MacCyrillic', 'IBM855', + ], + alphabet=( + 'АБВГДЃЕЖЗЅИЈКЛЉМНЊОПРСТЌУФХЦЧЏШ' + 'абвгдѓежзѕијклљмнњопрстќуфхцчџш' + ), + wiki_start_pages=['Главна_страница'], + ), + 'Dutch': Language( + name='Dutch', + iso_code='nl', + use_ascii=True, + charsets=['ISO-8859-1', 'WINDOWS-1252'], + wiki_start_pages=['Hoofdpagina'], + ), + 'Polish': Language( + name='Polish', + iso_code='pl', + # Q and X are only used for foreign words. + use_ascii=False, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=( + 'AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŹŻ' + 'aąbcćdeęfghijklłmnńoóprsśtuwyzźż' + ), + wiki_start_pages=['Wikipedia:Strona_główna'], + ), + 'Portuguese': Language( + name='Portuguese', + iso_code='pt', + use_ascii=True, + charsets=[ + 'ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252', + ], + alphabet='ÁÂÃÀÇÉÊÍÓÔÕÚáâãàçéêíóôõú', + wiki_start_pages=['Wikipédia:Página_principal'], + ), + 'Romanian': Language( + name='Romanian', + iso_code='ro', + use_ascii=True, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet='ăâîșțĂÂÎȘȚ', + wiki_start_pages=['Pagina_principală'], + ), + 'Russian': Language( + name='Russian', + iso_code='ru', + use_ascii=False, + charsets=[ + 'ISO-8859-5', 'WINDOWS-1251', + 'KOI8-R', 'MacCyrillic', 'IBM866', + 'IBM855', + ], + alphabet=( + 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя' + 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ' + ), + wiki_start_pages=['Заглавная_страница'], + ), + 'Slovak': Language( + name='Slovak', + iso_code='sk', + use_ascii=True, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet='áäčďéíĺľňóôŕšťúýžÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽ', + wiki_start_pages=['Hlavná_stránka'], + ), + 'Slovene': Language( + name='Slovene', + iso_code='sl', + # Q, W, X, Y are only used for foreign words. + use_ascii=False, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=( + 'abcčdefghijklmnoprsštuvzž' + 'ABCČDEFGHIJKLMNOPRSŠTUVZŽ' + ), + wiki_start_pages=['Glavna_stran'], + ), + # Serbian can be written in both Latin and Cyrillic, but there's no + # simple way to get the Latin alphabet pages from Wikipedia through + # the API, so for now we just support Cyrillic. + 'Serbian': Language( + name='Serbian', + iso_code='sr', + alphabet=( + 'АБВГДЂЕЖЗИЈКЛЉМНЊОПРСТЋУФХЦЧЏШ' + 'абвгдђежзијклљмнњопрстћуфхцчџш' + ), + charsets=[ + 'ISO-8859-5', 'WINDOWS-1251', + 'MacCyrillic', 'IBM855', + ], + wiki_start_pages=['Главна_страна'], + ), + 'Thai': Language( + name='Thai', + iso_code='th', + use_ascii=False, + charsets=['ISO-8859-11', 'TIS-620', 'CP874'], + alphabet='กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛', + wiki_start_pages=['หน้าหลัก'], + ), + 'Turkish': Language( + name='Turkish', + iso_code='tr', + # Q, W, and X are not used by Turkish + use_ascii=False, + charsets=[ + 'ISO-8859-3', 'ISO-8859-9', + 'WINDOWS-1254', + ], + alphabet=( + 'abcçdefgğhıijklmnoöprsştuüvyzâîû' + 'ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZÂÎÛ' + ), + wiki_start_pages=['Ana_Sayfa'], + ), + 'Vietnamese': Language( + name='Vietnamese', + iso_code='vi', + use_ascii=False, + # Windows-1258 is the only common 8-bit + # Vietnamese encoding supported by Python. + # From Wikipedia: + # For systems that lack support for Unicode, + # dozens of 8-bit Vietnamese code pages are + # available.[1] The most common are VISCII + # (TCVN 5712:1993), VPS, and Windows-1258.[3] + # Where ASCII is required, such as when + # ensuring readability in plain text e-mail, + # Vietnamese letters are often encoded + # according to Vietnamese Quoted-Readable + # (VIQR) or VSCII Mnemonic (VSCII-MNEM),[4] + # though usage of either variable-width + # scheme has declined dramatically following + # the adoption of Unicode on the World Wide + # Web. + charsets=['WINDOWS-1258'], + alphabet=( + 'aăâbcdđeêghiklmnoôơpqrstuưvxy' + 'AĂÂBCDĐEÊGHIKLMNOÔƠPQRSTUƯVXY' + ), + wiki_start_pages=['Chữ_Quốc_ngữ'], + ), +} diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcharsetprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcharsetprober.py index 46ba835..733acc4 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcharsetprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcharsetprober.py @@ -25,31 +25,38 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from collections import namedtuple from .charsetprober import CharSetProber -from .enums import CharacterCategory, ProbingState, SequenceLikelihood +from .enums import CharacterCategory +from .enums import ProbingState +from .enums import SequenceLikelihood -SingleByteCharSetModel = namedtuple('SingleByteCharSetModel', - ['charset_name', - 'language', - 'char_to_order_map', - 'language_model', - 'typical_positive_ratio', - 'keep_ascii_letters', - 'alphabet']) +SingleByteCharSetModel = namedtuple( + 'SingleByteCharSetModel', + [ + 'charset_name', + 'language', + 'char_to_order_map', + 'language_model', + 'typical_positive_ratio', + 'keep_ascii_letters', + 'alphabet', + ], +) class SingleByteCharSetProber(CharSetProber): SAMPLE_SIZE = 64 - SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 + SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 POSITIVE_SHORTCUT_THRESHOLD = 0.95 NEGATIVE_SHORTCUT_THRESHOLD = 0.05 def __init__(self, model, reversed=False, name_prober=None): - super(SingleByteCharSetProber, self).__init__() + super().__init__() self._model = model # TRUE if we need to reverse every pair in the model lookup self._reversed = reversed @@ -63,7 +70,7 @@ class SingleByteCharSetProber(CharSetProber): self.reset() def reset(self): - super(SingleByteCharSetProber, self).reset() + super().reset() # char order of last character self._last_order = 255 self._seq_counters = [0] * SequenceLikelihood.get_num_categories() @@ -122,14 +129,18 @@ class SingleByteCharSetProber(CharSetProber): if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: confidence = self.get_confidence() if confidence > self.POSITIVE_SHORTCUT_THRESHOLD: - self.logger.debug('%s confidence = %s, we have a winner', - charset_name, confidence) + self.logger.debug( + '%s confidence = %s, we have a winner', + charset_name, confidence, + ) self._state = ProbingState.FOUND_IT elif confidence < self.NEGATIVE_SHORTCUT_THRESHOLD: - self.logger.debug('%s confidence = %s, below negative ' - 'shortcut threshhold %s', charset_name, - confidence, - self.NEGATIVE_SHORTCUT_THRESHOLD) + self.logger.debug( + '%s confidence = %s, below negative ' + 'shortcut threshhold %s', charset_name, + confidence, + self.NEGATIVE_SHORTCUT_THRESHOLD, + ) self._state = ProbingState.NOT_ME return self.state @@ -137,8 +148,10 @@ class SingleByteCharSetProber(CharSetProber): def get_confidence(self): r = 0.01 if self._total_seqs > 0: - r = ((1.0 * self._seq_counters[SequenceLikelihood.POSITIVE]) / - self._total_seqs / self._model.typical_positive_ratio) + r = ( + (1.0 * self._seq_counters[SequenceLikelihood.POSITIVE]) / + self._total_seqs / self._model.typical_positive_ratio + ) r = r * self._freq_char / self._total_char if r >= 1.0: r = 0.99 diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcsgroupprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcsgroupprober.py index bdeef4e..b26b061 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcsgroupprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcsgroupprober.py @@ -25,36 +25,46 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from .charsetgroupprober import CharSetGroupProber from .hebrewprober import HebrewProber -from .langbulgarianmodel import (ISO_8859_5_BULGARIAN_MODEL, - WINDOWS_1251_BULGARIAN_MODEL) -from .langgreekmodel import ISO_8859_7_GREEK_MODEL, WINDOWS_1253_GREEK_MODEL +from .langbulgarianmodel import ISO_8859_5_BULGARIAN_MODEL +from .langbulgarianmodel import WINDOWS_1251_BULGARIAN_MODEL +from .langgreekmodel import ISO_8859_7_GREEK_MODEL +from .langgreekmodel import WINDOWS_1253_GREEK_MODEL from .langhebrewmodel import WINDOWS_1255_HEBREW_MODEL -# from .langhungarianmodel import (ISO_8859_2_HUNGARIAN_MODEL, -# WINDOWS_1250_HUNGARIAN_MODEL) -from .langrussianmodel import (IBM855_RUSSIAN_MODEL, IBM866_RUSSIAN_MODEL, - ISO_8859_5_RUSSIAN_MODEL, KOI8_R_RUSSIAN_MODEL, - MACCYRILLIC_RUSSIAN_MODEL, - WINDOWS_1251_RUSSIAN_MODEL) +from .langrussianmodel import IBM855_RUSSIAN_MODEL +from .langrussianmodel import IBM866_RUSSIAN_MODEL +from .langrussianmodel import ISO_8859_5_RUSSIAN_MODEL +from .langrussianmodel import KOI8_R_RUSSIAN_MODEL +from .langrussianmodel import MACCYRILLIC_RUSSIAN_MODEL +from .langrussianmodel import WINDOWS_1251_RUSSIAN_MODEL from .langthaimodel import TIS_620_THAI_MODEL from .langturkishmodel import ISO_8859_9_TURKISH_MODEL from .sbcharsetprober import SingleByteCharSetProber +# from .langhungarianmodel import (ISO_8859_2_HUNGARIAN_MODEL, +# WINDOWS_1250_HUNGARIAN_MODEL) class SBCSGroupProber(CharSetGroupProber): def __init__(self): - super(SBCSGroupProber, self).__init__() + super().__init__() hebrew_prober = HebrewProber() - logical_hebrew_prober = SingleByteCharSetProber(WINDOWS_1255_HEBREW_MODEL, - False, hebrew_prober) + logical_hebrew_prober = SingleByteCharSetProber( + WINDOWS_1255_HEBREW_MODEL, + False, hebrew_prober, + ) # TODO: See if using ISO-8859-8 Hebrew model works better here, since # it's actually the visual one - visual_hebrew_prober = SingleByteCharSetProber(WINDOWS_1255_HEBREW_MODEL, - True, hebrew_prober) - hebrew_prober.set_model_probers(logical_hebrew_prober, - visual_hebrew_prober) + visual_hebrew_prober = SingleByteCharSetProber( + WINDOWS_1255_HEBREW_MODEL, + True, hebrew_prober, + ) + hebrew_prober.set_model_probers( + logical_hebrew_prober, + visual_hebrew_prober, + ) # TODO: ORDER MATTERS HERE. I changed the order vs what was in master # and several tests failed that did not before. Some thought # should be put into the ordering, and we should consider making diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sjisprober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sjisprober.py index 9e29623..b90c5cd 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sjisprober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/sjisprober.py @@ -24,25 +24,27 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations -from .mbcharsetprober import MultiByteCharSetProber -from .codingstatemachine import CodingStateMachine from .chardistribution import SJISDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .enums import MachineState +from .enums import ProbingState from .jpcntx import SJISContextAnalysis +from .mbcharsetprober import MultiByteCharSetProber from .mbcssm import SJIS_SM_MODEL -from .enums import ProbingState, MachineState class SJISProber(MultiByteCharSetProber): def __init__(self): - super(SJISProber, self).__init__() + super().__init__() self.coding_sm = CodingStateMachine(SJIS_SM_MODEL) self.distribution_analyzer = SJISDistributionAnalysis() self.context_analyzer = SJISContextAnalysis() self.reset() def reset(self): - super(SJISProber, self).reset() + super().reset() self.context_analyzer.reset() @property @@ -51,14 +53,16 @@ class SJISProber(MultiByteCharSetProber): @property def language(self): - return "Japanese" + return 'Japanese' def feed(self, byte_str): for i in range(len(byte_str)): coding_state = self.coding_sm.next_state(byte_str[i]) if coding_state == MachineState.ERROR: - self.logger.debug('%s %s prober hit error at byte %s', - self.charset_name, self.language, i) + self.logger.debug( + '%s %s prober hit error at byte %s', + self.charset_name, self.language, i, + ) self._state = ProbingState.NOT_ME break elif coding_state == MachineState.ITS_ME: @@ -68,20 +72,30 @@ class SJISProber(MultiByteCharSetProber): char_len = self.coding_sm.get_current_charlen() if i == 0: self._last_char[1] = byte_str[0] - self.context_analyzer.feed(self._last_char[2 - char_len:], - char_len) + self.context_analyzer.feed( + self._last_char[2 - char_len:], + char_len, + ) self.distribution_analyzer.feed(self._last_char, char_len) else: - self.context_analyzer.feed(byte_str[i + 1 - char_len:i + 3 - - char_len], char_len) - self.distribution_analyzer.feed(byte_str[i - 1:i + 1], - char_len) + self.context_analyzer.feed( + byte_str[ + i + 1 - char_len:i + 3 - + char_len + ], char_len, + ) + self.distribution_analyzer.feed( + byte_str[i - 1:i + 1], + char_len, + ) self._last_char[0] = byte_str[-1] if self.state == ProbingState.DETECTING: - if (self.context_analyzer.got_enough_data() and - (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + if ( + self.context_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD) + ): self._state = ProbingState.FOUND_IT return self.state diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/universaldetector.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/universaldetector.py index 055a8ac..e234af4 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/universaldetector.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/universaldetector.py @@ -34,21 +34,23 @@ class a user of ``chardet`` should use. :author: Dan Blanchard (major refactoring for 3.0) :author: Ian Cordasco """ - +from __future__ import annotations import codecs import logging import re from .charsetgroupprober import CharSetGroupProber -from .enums import InputState, LanguageFilter, ProbingState +from .enums import InputState +from .enums import LanguageFilter +from .enums import ProbingState from .escprober import EscCharSetProber from .latin1prober import Latin1Prober from .mbcsgroupprober import MBCSGroupProber from .sbcsgroupprober import SBCSGroupProber -class UniversalDetector(object): +class UniversalDetector: """ The ``UniversalDetector`` class underlies the ``chardet.detect`` function and coordinates all of the different charset probers. @@ -69,14 +71,16 @@ class UniversalDetector(object): HIGH_BYTE_DETECTOR = re.compile(b'[\x80-\xFF]') ESC_DETECTOR = re.compile(b'(\033|~{)') WIN_BYTE_DETECTOR = re.compile(b'[\x80-\x9F]') - ISO_WIN_MAP = {'iso-8859-1': 'Windows-1252', - 'iso-8859-2': 'Windows-1250', - 'iso-8859-5': 'Windows-1251', - 'iso-8859-6': 'Windows-1256', - 'iso-8859-7': 'Windows-1253', - 'iso-8859-8': 'Windows-1255', - 'iso-8859-9': 'Windows-1254', - 'iso-8859-13': 'Windows-1257'} + ISO_WIN_MAP = { + 'iso-8859-1': 'Windows-1252', + 'iso-8859-2': 'Windows-1250', + 'iso-8859-5': 'Windows-1251', + 'iso-8859-6': 'Windows-1256', + 'iso-8859-7': 'Windows-1253', + 'iso-8859-8': 'Windows-1255', + 'iso-8859-9': 'Windows-1254', + 'iso-8859-13': 'Windows-1257', + } def __init__(self, lang_filter=LanguageFilter.ALL): self._esc_charset_prober = None @@ -136,32 +140,44 @@ class UniversalDetector(object): # If the data starts with BOM, we know it is UTF if byte_str.startswith(codecs.BOM_UTF8): # EF BB BF UTF-8 with BOM - self.result = {'encoding': "UTF-8-SIG", - 'confidence': 1.0, - 'language': ''} - elif byte_str.startswith((codecs.BOM_UTF32_LE, - codecs.BOM_UTF32_BE)): + self.result = { + 'encoding': 'UTF-8-SIG', + 'confidence': 1.0, + 'language': '', + } + elif byte_str.startswith(( + codecs.BOM_UTF32_LE, + codecs.BOM_UTF32_BE, + )): # FF FE 00 00 UTF-32, little-endian BOM # 00 00 FE FF UTF-32, big-endian BOM - self.result = {'encoding': "UTF-32", - 'confidence': 1.0, - 'language': ''} + self.result = { + 'encoding': 'UTF-32', + 'confidence': 1.0, + 'language': '', + } elif byte_str.startswith(b'\xFE\xFF\x00\x00'): # FE FF 00 00 UCS-4, unusual octet order BOM (3412) - self.result = {'encoding': "X-ISO-10646-UCS-4-3412", - 'confidence': 1.0, - 'language': ''} + self.result = { + 'encoding': 'X-ISO-10646-UCS-4-3412', + 'confidence': 1.0, + 'language': '', + } elif byte_str.startswith(b'\x00\x00\xFF\xFE'): # 00 00 FF FE UCS-4, unusual octet order BOM (2143) - self.result = {'encoding': "X-ISO-10646-UCS-4-2143", - 'confidence': 1.0, - 'language': ''} + self.result = { + 'encoding': 'X-ISO-10646-UCS-4-2143', + 'confidence': 1.0, + 'language': '', + } elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)): # FF FE UTF-16, little endian BOM # FE FF UTF-16, big endian BOM - self.result = {'encoding': "UTF-16", - 'confidence': 1.0, - 'language': ''} + self.result = { + 'encoding': 'UTF-16', + 'confidence': 1.0, + 'language': '', + } self._got_data = True if self.result['encoding'] is not None: @@ -187,12 +203,14 @@ class UniversalDetector(object): if not self._esc_charset_prober: self._esc_charset_prober = EscCharSetProber(self.lang_filter) if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT: - self.result = {'encoding': - self._esc_charset_prober.charset_name, - 'confidence': - self._esc_charset_prober.get_confidence(), - 'language': - self._esc_charset_prober.language} + self.result = { + 'encoding': + self._esc_charset_prober.charset_name, + 'confidence': + self._esc_charset_prober.get_confidence(), + 'language': + self._esc_charset_prober.language, + } self.done = True # If we've seen high bytes (i.e., those with values greater than 127), # we need to do more complicated checks using all our multi-byte and @@ -209,9 +227,11 @@ class UniversalDetector(object): self._charset_probers.append(Latin1Prober()) for prober in self._charset_probers: if prober.feed(byte_str) == ProbingState.FOUND_IT: - self.result = {'encoding': prober.charset_name, - 'confidence': prober.get_confidence(), - 'language': prober.language} + self.result = { + 'encoding': prober.charset_name, + 'confidence': prober.get_confidence(), + 'language': prober.language, + } self.done = True break if self.WIN_BYTE_DETECTOR.search(byte_str): @@ -235,9 +255,11 @@ class UniversalDetector(object): # Default to ASCII if it is all we've seen so far elif self._input_state == InputState.PURE_ASCII: - self.result = {'encoding': 'ascii', - 'confidence': 1.0, - 'language': ''} + self.result = { + 'encoding': 'ascii', + 'confidence': 1.0, + 'language': '', + } # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD elif self._input_state == InputState.HIGH_BYTE: @@ -259,11 +281,15 @@ class UniversalDetector(object): # extra Windows-specific bytes if lower_charset_name.startswith('iso-8859'): if self._has_win_bytes: - charset_name = self.ISO_WIN_MAP.get(lower_charset_name, - charset_name) - self.result = {'encoding': charset_name, - 'confidence': confidence, - 'language': max_prober.language} + charset_name = self.ISO_WIN_MAP.get( + lower_charset_name, + charset_name, + ) + self.result = { + 'encoding': charset_name, + 'confidence': confidence, + 'language': max_prober.language, + } # Log all prober confidences if none met MINIMUM_THRESHOLD if self.logger.getEffectiveLevel() <= logging.DEBUG: @@ -274,13 +300,17 @@ class UniversalDetector(object): continue if isinstance(group_prober, CharSetGroupProber): for prober in group_prober.probers: - self.logger.debug('%s %s confidence = %s', - prober.charset_name, - prober.language, - prober.get_confidence()) + self.logger.debug( + '%s %s confidence = %s', + prober.charset_name, + prober.language, + prober.get_confidence(), + ) else: - self.logger.debug('%s %s confidence = %s', - group_prober.charset_name, - group_prober.language, - group_prober.get_confidence()) + self.logger.debug( + '%s %s confidence = %s', + group_prober.charset_name, + group_prober.language, + group_prober.get_confidence(), + ) return self.result diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/utf8prober.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/utf8prober.py index 6c3196c..9f4a767 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/utf8prober.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/utf8prober.py @@ -24,35 +24,36 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from __future__ import annotations from .charsetprober import CharSetProber -from .enums import ProbingState, MachineState from .codingstatemachine import CodingStateMachine +from .enums import MachineState +from .enums import ProbingState from .mbcssm import UTF8_SM_MODEL - class UTF8Prober(CharSetProber): ONE_CHAR_PROB = 0.5 def __init__(self): - super(UTF8Prober, self).__init__() + super().__init__() self.coding_sm = CodingStateMachine(UTF8_SM_MODEL) self._num_mb_chars = None self.reset() def reset(self): - super(UTF8Prober, self).reset() + super().reset() self.coding_sm.reset() self._num_mb_chars = 0 @property def charset_name(self): - return "utf-8" + return 'utf-8' @property def language(self): - return "" + return '' def feed(self, byte_str): for c in byte_str: diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/version.py b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/version.py index 70369b9..45e881a 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/version.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/chardet/version.py @@ -4,6 +4,7 @@ from within setup.py and from chardet subpackages. :author: Dan Blanchard (dan.blanchard@gmail.com) """ +from __future__ import annotations -__version__ = "4.0.0" +__version__ = '4.0.0' VERSION = __version__.split('.') diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/__init__.py index b149ed7..7927970 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/__init__.py @@ -1,6 +1,14 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -from .initialise import init, deinit, reinit, colorama_text -from .ansi import Fore, Back, Style, Cursor +from __future__ import annotations + +from .ansi import Back +from .ansi import Cursor +from .ansi import Fore +from .ansi import Style from .ansitowin32 import AnsiToWin32 +from .initialise import colorama_text +from .initialise import deinit +from .initialise import init +from .initialise import reinit __version__ = '0.4.4' diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/ansi.py b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/ansi.py index 11ec695..3f232fa 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/ansi.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/ansi.py @@ -3,6 +3,7 @@ This module generates ANSI character codes to printing colors to terminals. See: http://en.wikipedia.org/wiki/ANSI_escape_code ''' +from __future__ import annotations CSI = '\033[' OSC = '\033]' @@ -12,17 +13,20 @@ BEL = '\a' def code_to_chars(code): return CSI + str(code) + 'm' + def set_title(title): return OSC + '2;' + title + BEL + def clear_screen(mode=2): return CSI + str(mode) + 'J' + def clear_line(mode=2): return CSI + str(mode) + 'K' -class AnsiCodes(object): +class AnsiCodes: def __init__(self): # the subclasses declare class attributes which are numbers. # Upon instantiation we define instance attributes, which are the same @@ -33,70 +37,75 @@ class AnsiCodes(object): setattr(self, name, code_to_chars(value)) -class AnsiCursor(object): +class AnsiCursor: def UP(self, n=1): return CSI + str(n) + 'A' + def DOWN(self, n=1): return CSI + str(n) + 'B' + def FORWARD(self, n=1): return CSI + str(n) + 'C' + def BACK(self, n=1): return CSI + str(n) + 'D' + def POS(self, x=1, y=1): return CSI + str(y) + ';' + str(x) + 'H' class AnsiFore(AnsiCodes): - BLACK = 30 - RED = 31 - GREEN = 32 - YELLOW = 33 - BLUE = 34 - MAGENTA = 35 - CYAN = 36 - WHITE = 37 - RESET = 39 + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 # These are fairly well supported, but not part of the standard. - LIGHTBLACK_EX = 90 - LIGHTRED_EX = 91 - LIGHTGREEN_EX = 92 - LIGHTYELLOW_EX = 93 - LIGHTBLUE_EX = 94 + LIGHTBLACK_EX = 90 + LIGHTRED_EX = 91 + LIGHTGREEN_EX = 92 + LIGHTYELLOW_EX = 93 + LIGHTBLUE_EX = 94 LIGHTMAGENTA_EX = 95 - LIGHTCYAN_EX = 96 - LIGHTWHITE_EX = 97 + LIGHTCYAN_EX = 96 + LIGHTWHITE_EX = 97 class AnsiBack(AnsiCodes): - BLACK = 40 - RED = 41 - GREEN = 42 - YELLOW = 43 - BLUE = 44 - MAGENTA = 45 - CYAN = 46 - WHITE = 47 - RESET = 49 + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 # These are fairly well supported, but not part of the standard. - LIGHTBLACK_EX = 100 - LIGHTRED_EX = 101 - LIGHTGREEN_EX = 102 - LIGHTYELLOW_EX = 103 - LIGHTBLUE_EX = 104 + LIGHTBLACK_EX = 100 + LIGHTRED_EX = 101 + LIGHTGREEN_EX = 102 + LIGHTYELLOW_EX = 103 + LIGHTBLUE_EX = 104 LIGHTMAGENTA_EX = 105 - LIGHTCYAN_EX = 106 - LIGHTWHITE_EX = 107 + LIGHTCYAN_EX = 106 + LIGHTWHITE_EX = 107 class AnsiStyle(AnsiCodes): - BRIGHT = 1 - DIM = 2 - NORMAL = 22 + BRIGHT = 1 + DIM = 2 + NORMAL = 22 RESET_ALL = 0 -Fore = AnsiFore() -Back = AnsiBack() -Style = AnsiStyle() + +Fore = AnsiFore() +Back = AnsiBack() +Style = AnsiStyle() Cursor = AnsiCursor() diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/ansitowin32.py b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/ansitowin32.py index 6039a05..38dcc94 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/ansitowin32.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/ansitowin32.py @@ -1,11 +1,20 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from __future__ import annotations + +import os import re import sys -import os -from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL -from .winterm import WinTerm, WinColor, WinStyle -from .win32 import windll, winapi_test +from .ansi import AnsiBack +from .ansi import AnsiFore +from .ansi import AnsiStyle +from .ansi import BEL +from .ansi import Style +from .win32 import winapi_test +from .win32 import windll +from .winterm import WinColor +from .winterm import WinStyle +from .winterm import WinTerm winterm = None @@ -13,12 +22,13 @@ if windll is not None: winterm = WinTerm() -class StreamWrapper(object): +class StreamWrapper: ''' Wraps a stream (such as stdout), acting as a transparent proxy for all attribute access apart from method 'write()', which is delegated to our Converter instance. ''' + def __init__(self, wrapped, converter): # double-underscore everything to prevent clashes with names of # attributes on the wrapped stream object. @@ -61,7 +71,7 @@ class StreamWrapper(object): return True -class AnsiToWin32(object): +class AnsiToWin32: ''' Implements a 'write()' method which, on Windows, will strip ANSI character sequences from the text, and if outputting to a tty, will convert them into @@ -116,7 +126,7 @@ class AnsiToWin32(object): def get_win32_calls(self): if self.convert and winterm: return { - AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.RESET_ALL: (winterm.reset_all,), AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), @@ -128,7 +138,7 @@ class AnsiToWin32(object): AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), AnsiFore.WHITE: (winterm.fore, WinColor.GREY), - AnsiFore.RESET: (winterm.fore, ), + AnsiFore.RESET: (winterm.fore,), AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), @@ -145,7 +155,7 @@ class AnsiToWin32(object): AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), AnsiBack.CYAN: (winterm.back, WinColor.CYAN), AnsiBack.WHITE: (winterm.back, WinColor.GREY), - AnsiBack.RESET: (winterm.back, ), + AnsiBack.RESET: (winterm.back,), AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), @@ -166,14 +176,12 @@ class AnsiToWin32(object): if self.autoreset: self.reset_all() - def reset_all(self): if self.convert: self.call_win32('m', (0,)) elif not self.strip and not self.stream.closed: self.wrapped.write(Style.RESET_ALL) - def write_and_convert(self, text): ''' Write the given text to our wrapped stream, stripping any ANSI @@ -189,19 +197,16 @@ class AnsiToWin32(object): cursor = end self.write_plain_text(text, cursor, len(text)) - def write_plain_text(self, text, start, end): if start < end: self.wrapped.write(text[start:end]) self.wrapped.flush() - def convert_ansi(self, paramstring, command): if self.convert: params = self.extract_params(command, paramstring) self.call_win32(command, params) - def extract_params(self, command, paramstring): if command in 'Hf': params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) @@ -219,7 +224,6 @@ class AnsiToWin32(object): return params - def call_win32(self, command, params): if command == 'm': for param in params: @@ -241,15 +245,14 @@ class AnsiToWin32(object): x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) - def convert_osc(self, text): for match in self.ANSI_OSC_RE.finditer(text): start, end = match.span() text = text[:start] + text[end:] paramstring, command = match.groups() if command == BEL: - if paramstring.count(";") == 1: - params = paramstring.split(";") + if paramstring.count(';') == 1: + params = paramstring.split(';') # 0 - change title and icon (we will only change title) # 1 - change icon (we don't support this) # 2 - change title diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/initialise.py b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/initialise.py index 430d066..64d2bbc 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/initialise.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/initialise.py @@ -1,4 +1,6 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from __future__ import annotations + import atexit import contextlib import sys @@ -73,8 +75,10 @@ def reinit(): def wrap_stream(stream, convert, strip, autoreset, wrap): if wrap: - wrapper = AnsiToWin32(stream, - convert=convert, strip=strip, autoreset=autoreset) + wrapper = AnsiToWin32( + stream, + convert=convert, strip=strip, autoreset=autoreset, + ) if wrapper.should_wrap(): stream = wrapper.stream return stream diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/win32.py b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/win32.py index c2d8360..5b0dcf2 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/win32.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/win32.py @@ -1,6 +1,6 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. - # from winbase.h +from __future__ import annotations STDOUT = -11 STDERR = -12 @@ -21,19 +21,16 @@ else: class CONSOLE_SCREEN_BUFFER_INFO(Structure): """struct in wincon.h.""" _fields_ = [ - ("dwSize", COORD), - ("dwCursorPosition", COORD), - ("wAttributes", wintypes.WORD), - ("srWindow", wintypes.SMALL_RECT), - ("dwMaximumWindowSize", COORD), + ('dwSize', COORD), + ('dwCursorPosition', COORD), + ('wAttributes', wintypes.WORD), + ('srWindow', wintypes.SMALL_RECT), + ('dwMaximumWindowSize', COORD), ] + def __str__(self): return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( - self.dwSize.Y, self.dwSize.X - , self.dwCursorPosition.Y, self.dwCursorPosition.X - , self.wAttributes - , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right - , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + self.dwSize.Y, self.dwSize.X, self.dwCursorPosition.Y, self.dwCursorPosition.X, self.wAttributes, self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right, self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X, ) _GetStdHandle = windll.kernel32.GetStdHandle @@ -85,25 +82,29 @@ else: _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW _SetConsoleTitleW.argtypes = [ - wintypes.LPCWSTR + wintypes.LPCWSTR, ] _SetConsoleTitleW.restype = wintypes.BOOL def _winapi_test(handle): csbi = CONSOLE_SCREEN_BUFFER_INFO() success = _GetConsoleScreenBufferInfo( - handle, byref(csbi)) + handle, byref(csbi), + ) return bool(success) def winapi_test(): - return any(_winapi_test(h) for h in - (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) + return any( + _winapi_test(h) for h in + (_GetStdHandle(STDOUT), _GetStdHandle(STDERR)) + ) def GetConsoleScreenBufferInfo(stream_id=STDOUT): handle = _GetStdHandle(stream_id) csbi = CONSOLE_SCREEN_BUFFER_INFO() success = _GetConsoleScreenBufferInfo( - handle, byref(csbi)) + handle, byref(csbi), + ) return csbi def SetConsoleTextAttribute(stream_id, attrs): @@ -135,7 +136,8 @@ else: num_written = wintypes.DWORD(0) # Note that this is hard-coded for ANSI (vs wide) bytes. success = _FillConsoleOutputCharacterA( - handle, char, length, start, byref(num_written)) + handle, char, length, start, byref(num_written), + ) return num_written.value def FillConsoleOutputAttribute(stream_id, attr, length, start): @@ -146,7 +148,8 @@ else: num_written = wintypes.DWORD(0) # Note that this is hard-coded for ANSI (vs wide) bytes. return _FillConsoleOutputAttribute( - handle, attribute, length, start, byref(num_written)) + handle, attribute, length, start, byref(num_written), + ) def SetConsoleTitle(title): return _SetConsoleTitleW(title) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/winterm.py b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/winterm.py index 0fdb4ec..4e277bf 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/winterm.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/colorama/winterm.py @@ -1,25 +1,30 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from __future__ import annotations + from . import win32 # from wincon.h -class WinColor(object): - BLACK = 0 - BLUE = 1 - GREEN = 2 - CYAN = 3 - RED = 4 +class WinColor: + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 MAGENTA = 5 - YELLOW = 6 - GREY = 7 + YELLOW = 6 + GREY = 7 # from wincon.h -class WinStyle(object): - NORMAL = 0x00 # dim text, dim background - BRIGHT = 0x08 # bright text, dim background - BRIGHT_BACKGROUND = 0x80 # dim text, bright background -class WinTerm(object): + +class WinStyle: + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + + +class WinTerm: def __init__(self): self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/__init__.py index 1154948..20fa0df 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/__init__.py @@ -1,19 +1,22 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012-2019 Vinay Sajip. # Licensed to the Python Software Foundation under a contributor agreement. # See LICENSE.txt and CONTRIBUTORS.txt. # +from __future__ import annotations + import logging __version__ = '0.3.3' + class DistlibException(Exception): pass + try: from logging import NullHandler -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover class NullHandler(logging.Handler): def handle(self, record): pass def emit(self, record): pass diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__init__.py index f7dbf4c..895c46a 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__init__.py @@ -4,3 +4,4 @@ Individual classes and functions are found in d2._backport.misc. Intended usage is to always import things missing from 3.1 from that module: the built-in/stdlib objects will be used if found. """ +from __future__ import annotations diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/misc.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/misc.py index cfb318d..f82f51b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/misc.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/misc.py @@ -1,9 +1,9 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012 The Python Software Foundation. # See LICENSE.txt and CONTRIBUTORS.txt. # """Backports for individual classes and functions.""" +from __future__ import annotations import os import sys @@ -22,7 +22,7 @@ except ImportError: try: callable = callable except NameError: - from collections import Callable + from collections.abc import Callable def callable(obj): return isinstance(obj, Callable) @@ -37,5 +37,7 @@ except AttributeError: elif isinstance(filename, str): return filename.encode(sys.getfilesystemencoding()) else: - raise TypeError("expect bytes or str, not %s" % - type(filename).__name__) + raise TypeError( + 'expect bytes or str, not %s' % + type(filename).__name__, + ) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/shutil.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/shutil.py index 10ed362..3c74815 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/shutil.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/shutil.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012 The Python Software Foundation. # See LICENSE.txt and CONTRIBUTORS.txt. @@ -8,16 +7,17 @@ XXX The functions here don't copy the resource fork or other metadata on Mac. """ +from __future__ import annotations -import os -import sys -import stat -from os.path import abspath import fnmatch +import os +import stat +import sys +from os.path import abspath try: from collections.abc import Callable except ImportError: - from collections import Callable + from collections.abc import Callable import errno from . import tarfile @@ -37,26 +37,33 @@ try: except ImportError: getgrnam = None -__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", - "copytree", "move", "rmtree", "Error", "SpecialFileError", - "ExecError", "make_archive", "get_archive_formats", - "register_archive_format", "unregister_archive_format", - "get_unpack_formats", "register_unpack_format", - "unregister_unpack_format", "unpack_archive", "ignore_patterns"] +__all__ = [ + 'copyfileobj', 'copyfile', 'copymode', 'copystat', 'copy', 'copy2', + 'copytree', 'move', 'rmtree', 'Error', 'SpecialFileError', + 'ExecError', 'make_archive', 'get_archive_formats', + 'register_archive_format', 'unregister_archive_format', + 'get_unpack_formats', 'register_unpack_format', + 'unregister_unpack_format', 'unpack_archive', 'ignore_patterns', +] + class Error(EnvironmentError): pass + class SpecialFileError(EnvironmentError): """Raised when trying to do a kind of operation (e.g. copying) which is not supported on a special file (e.g. a named pipe)""" + class ExecError(EnvironmentError): """Raised when a command could not be executed""" + class ReadError(EnvironmentError): """Raised when an archive cannot be read""" + class RegistryError(Exception): """Raised when a registry operation with the archiving and unpacking registries fails""" @@ -67,7 +74,8 @@ try: except NameError: WindowsError = None -def copyfileobj(fsrc, fdst, length=16*1024): + +def copyfileobj(fsrc, fdst, length=16 * 1024): """copy data from file-like object fsrc to file-like object fdst""" while 1: buf = fsrc.read(length) @@ -75,6 +83,7 @@ def copyfileobj(fsrc, fdst, length=16*1024): break fdst.write(buf) + def _samefile(src, dst): # Macintosh, Unix. if hasattr(os.path, 'samefile'): @@ -84,13 +93,16 @@ def _samefile(src, dst): return False # All other platforms: check for same pathname. - return (os.path.normcase(os.path.abspath(src)) == - os.path.normcase(os.path.abspath(dst))) + return ( + os.path.normcase(os.path.abspath(src)) == + os.path.normcase(os.path.abspath(dst)) + ) + def copyfile(src, dst): """Copy data from src to dst""" if _samefile(src, dst): - raise Error("`%s` and `%s` are the same file" % (src, dst)) + raise Error('`{}` and `{}` are the same file'.format(src, dst)) for fn in [src, dst]: try: @@ -101,12 +113,13 @@ def copyfile(src, dst): else: # XXX What about other special files? (sockets, devices...) if stat.S_ISFIFO(st.st_mode): - raise SpecialFileError("`%s` is a named pipe" % fn) + raise SpecialFileError('`%s` is a named pipe' % fn) with open(src, 'rb') as fsrc: with open(dst, 'wb') as fdst: copyfileobj(fsrc, fdst) + def copymode(src, dst): """Copy mode bits from src to dst""" if hasattr(os, 'chmod'): @@ -114,6 +127,7 @@ def copymode(src, dst): mode = stat.S_IMODE(st.st_mode) os.chmod(dst, mode) + def copystat(src, dst): """Copy all stat info (mode bits, atime, mtime, flags) from src to dst""" st = os.stat(src) @@ -126,10 +140,13 @@ def copystat(src, dst): try: os.chflags(dst, st.st_flags) except OSError as why: - if (not hasattr(errno, 'EOPNOTSUPP') or - why.errno != errno.EOPNOTSUPP): + if ( + not hasattr(errno, 'EOPNOTSUPP') or + why.errno != errno.EOPNOTSUPP + ): raise + def copy(src, dst): """Copy data and mode bits ("cp src dst"). @@ -141,6 +158,7 @@ def copy(src, dst): copyfile(src, dst) copymode(src, dst) + def copy2(src, dst): """Copy data and all stat info ("cp -p src dst"). @@ -152,6 +170,7 @@ def copy2(src, dst): copyfile(src, dst) copystat(src, dst) + def ignore_patterns(*patterns): """Function that can be used as copytree() ignore parameter. @@ -164,8 +183,11 @@ def ignore_patterns(*patterns): return set(ignored_names) return _ignore_patterns -def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, - ignore_dangling_symlinks=False): + +def copytree( + src, dst, symlinks=False, ignore=None, copy_function=copy2, + ignore_dangling_symlinks=False, +): """Recursively copy a directory tree. The destination directory must not already exist. @@ -233,7 +255,7 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, # continue with other files except Error as err: errors.extend(err.args[0]) - except EnvironmentError as why: + except OSError as why: errors.append((srcname, dstname, str(why))) try: copystat(src, dst) @@ -246,6 +268,7 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, if errors: raise Error(errors) + def rmtree(path, ignore_errors=False, onerror=None): """Recursively delete a directory tree. @@ -266,7 +289,7 @@ def rmtree(path, ignore_errors=False, onerror=None): try: if os.path.islink(path): # symlinks to directories are forbidden, see bug #1669 - raise OSError("Cannot call rmtree on a symbolic link") + raise OSError('Cannot call rmtree on a symbolic link') except OSError: onerror(os.path.islink, path, sys.exc_info()) # can't continue even if onerror hook returns @@ -274,24 +297,24 @@ def rmtree(path, ignore_errors=False, onerror=None): names = [] try: names = os.listdir(path) - except os.error: + except OSError: onerror(os.listdir, path, sys.exc_info()) for name in names: fullname = os.path.join(path, name) try: mode = os.lstat(fullname).st_mode - except os.error: + except OSError: mode = 0 if stat.S_ISDIR(mode): rmtree(fullname, ignore_errors, onerror) else: try: os.remove(fullname) - except os.error: + except OSError: onerror(os.remove, fullname, sys.exc_info()) try: os.rmdir(path) - except os.error: + except OSError: onerror(os.rmdir, path, sys.exc_info()) @@ -300,6 +323,7 @@ def _basename(path): # Thus we always get the last component of the path, even for directories. return os.path.basename(path.rstrip(os.path.sep)) + def move(src, dst): """Recursively move a file or directory to another location. This is similar to the Unix "mv" command. @@ -333,13 +357,14 @@ def move(src, dst): except OSError: if os.path.isdir(src): if _destinsrc(src, dst): - raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst)) + raise Error("Cannot move a directory '{}' into itself '{}'.".format(src, dst)) copytree(src, real_dst, symlinks=True) rmtree(src) else: copy2(src, real_dst) os.unlink(src) + def _destinsrc(src, dst): src = abspath(src) dst = abspath(dst) @@ -349,6 +374,7 @@ def _destinsrc(src, dst): dst += os.path.sep return dst.startswith(src) + def _get_gid(name): """Returns a gid, given a group name.""" if getgrnam is None or name is None: @@ -361,6 +387,7 @@ def _get_gid(name): return result[2] return None + def _get_uid(name): """Returns an uid, given a user name.""" if getpwnam is None or name is None: @@ -373,8 +400,11 @@ def _get_uid(name): return result[2] return None -def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, - owner=None, group=None, logger=None): + +def _make_tarball( + base_name, base_dir, compress='gzip', verbose=0, dry_run=0, + owner=None, group=None, logger=None, +): """Create a (possibly compressed) tar file from all the files under 'base_dir'. @@ -398,15 +428,17 @@ def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, # flags for compression program, each element of list will be an argument if compress is not None and compress not in compress_ext: - raise ValueError("bad value for 'compress', or compression format not " - "supported : {0}".format(compress)) + raise ValueError( + "bad value for 'compress', or compression format not " + 'supported : {}'.format(compress), + ) archive_name = base_name + '.tar' + compress_ext.get(compress, '') archive_dir = os.path.dirname(archive_name) if not os.path.exists(archive_dir): if logger is not None: - logger.info("creating %s", archive_dir) + logger.info('creating %s', archive_dir) if not dry_run: os.makedirs(archive_dir) @@ -435,22 +467,26 @@ def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, return archive_name + def _call_external_zip(base_dir, zip_filename, verbose=False, dry_run=False): # XXX see if we want to keep an external call here if verbose: - zipoptions = "-r" + zipoptions = '-r' else: - zipoptions = "-rq" + zipoptions = '-rq' from distutils.errors import DistutilsExecError from distutils.spawn import spawn try: - spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run) + spawn(['zip', zipoptions, zip_filename, base_dir], dry_run=dry_run) except DistutilsExecError: # XXX really should distinguish between "couldn't find # external 'zip' command" and "zip failed". - raise ExecError("unable to create zip file '%s': " + raise ExecError( + "unable to create zip file '%s': " "could neither import the 'zipfile' module nor " - "find a standalone zip utility") % zip_filename + 'find a standalone zip utility', + ) % zip_filename + def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): """Create a zip file from all the files under 'base_dir'. @@ -461,12 +497,12 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): available, raises ExecError. Returns the name of the output zip file. """ - zip_filename = base_name + ".zip" + zip_filename = base_name + '.zip' archive_dir = os.path.dirname(base_name) if not os.path.exists(archive_dir): if logger is not None: - logger.info("creating %s", archive_dir) + logger.info('creating %s', archive_dir) if not dry_run: os.makedirs(archive_dir) @@ -481,12 +517,16 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): _call_external_zip(base_dir, zip_filename, verbose, dry_run) else: if logger is not None: - logger.info("creating '%s' and adding '%s' to it", - zip_filename, base_dir) + logger.info( + "creating '%s' and adding '%s' to it", + zip_filename, base_dir, + ) if not dry_run: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) + zip = zipfile.ZipFile( + zip_filename, 'w', + compression=zipfile.ZIP_DEFLATED, + ) for dirpath, dirnames, filenames in os.walk(base_dir): for name in filenames: @@ -499,27 +539,34 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): return zip_filename + _ARCHIVE_FORMATS = { 'gztar': (_make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), 'bztar': (_make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), - 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"), - 'zip': (_make_zipfile, [], "ZIP file"), - } + 'tar': (_make_tarball, [('compress', None)], 'uncompressed tar file'), + 'zip': (_make_zipfile, [], 'ZIP file'), +} if _BZ2_SUPPORTED: - _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')], - "bzip2'ed tar-file") + _ARCHIVE_FORMATS['bztar'] = ( + _make_tarball, [('compress', 'bzip2')], + "bzip2'ed tar-file", + ) + def get_archive_formats(): """Returns a list of supported formats for archiving and unarchiving. Each element of the returned sequence is a tuple (name, description) """ - formats = [(name, registry[2]) for name, registry in - _ARCHIVE_FORMATS.items()] + formats = [ + (name, registry[2]) for name, registry in + _ARCHIVE_FORMATS.items() + ] formats.sort() return formats + def register_archive_format(name, function, extra_args=None, description=''): """Registers an archive format. @@ -536,16 +583,20 @@ def register_archive_format(name, function, extra_args=None, description=''): if not isinstance(extra_args, (tuple, list)): raise TypeError('extra_args needs to be a sequence') for element in extra_args: - if not isinstance(element, (tuple, list)) or len(element) !=2: + if not isinstance(element, (tuple, list)) or len(element) != 2: raise TypeError('extra_args elements are : (arg_name, value)') _ARCHIVE_FORMATS[name] = (function, extra_args, description) + def unregister_archive_format(name): del _ARCHIVE_FORMATS[name] -def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, - dry_run=0, owner=None, group=None, logger=None): + +def make_archive( + base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0, owner=None, group=None, logger=None, +): """Create an archive file (eg. zip or tar). 'base_name' is the name of the file to create, minus any format-specific @@ -605,11 +656,14 @@ def get_unpack_formats(): Each element of the returned sequence is a tuple (name, extensions, description) """ - formats = [(name, info[0], info[3]) for name, info in - _UNPACK_FORMATS.items()] + formats = [ + (name, info[0], info[3]) for name, info in + _UNPACK_FORMATS.items() + ] formats.sort() return formats + def _check_unpack_options(extensions, function, extra_args): """Checks what gets registered as an unpacker.""" # first make sure no other unpacker is registered for this extension @@ -621,15 +675,21 @@ def _check_unpack_options(extensions, function, extra_args): for extension in extensions: if extension in existing_extensions: msg = '%s is already registered for "%s"' - raise RegistryError(msg % (extension, - existing_extensions[extension])) + raise RegistryError( + msg % ( + extension, + existing_extensions[extension], + ), + ) if not isinstance(function, Callable): raise TypeError('The registered function must be a callable') -def register_unpack_format(name, extensions, function, extra_args=None, - description=''): +def register_unpack_format( + name, extensions, function, extra_args=None, + description='', +): """Registers an unpack format. `name` is the name of the format. `extensions` is a list of extensions @@ -650,16 +710,19 @@ def register_unpack_format(name, extensions, function, extra_args=None, _check_unpack_options(extensions, function, extra_args) _UNPACK_FORMATS[name] = extensions, function, extra_args, description + def unregister_unpack_format(name): """Removes the pack format from the registry.""" del _UNPACK_FORMATS[name] + def _ensure_directory(path): """Ensure that the parent directory of `path` exists""" dirname = os.path.dirname(path) if not os.path.isdir(dirname): os.makedirs(dirname) + def _unpack_zipfile(filename, extract_dir): """Unpack zip `filename` to `extract_dir` """ @@ -669,7 +732,7 @@ def _unpack_zipfile(filename, extract_dir): raise ReadError('zlib not supported, cannot unpack this archive.') if not zipfile.is_zipfile(filename): - raise ReadError("%s is not a zip file" % filename) + raise ReadError('%s is not a zip file' % filename) zip = zipfile.ZipFile(filename) try: @@ -697,6 +760,7 @@ def _unpack_zipfile(filename, extract_dir): finally: zip.close() + def _unpack_tarfile(filename, extract_dir): """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` """ @@ -704,21 +768,26 @@ def _unpack_tarfile(filename, extract_dir): tarobj = tarfile.open(filename) except tarfile.TarError: raise ReadError( - "%s is not a compressed or uncompressed tar file" % filename) + '%s is not a compressed or uncompressed tar file' % filename, + ) try: tarobj.extractall(extract_dir) finally: tarobj.close() + _UNPACK_FORMATS = { 'gztar': (['.tar.gz', '.tgz'], _unpack_tarfile, [], "gzip'ed tar-file"), - 'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"), - 'zip': (['.zip'], _unpack_zipfile, [], "ZIP file") - } + 'tar': (['.tar'], _unpack_tarfile, [], 'uncompressed tar file'), + 'zip': (['.zip'], _unpack_zipfile, [], 'ZIP file'), +} if _BZ2_SUPPORTED: - _UNPACK_FORMATS['bztar'] = (['.bz2'], _unpack_tarfile, [], - "bzip2'ed tar-file") + _UNPACK_FORMATS['bztar'] = ( + ['.bz2'], _unpack_tarfile, [], + "bzip2'ed tar-file", + ) + def _find_unpack_format(filename): for name, info in _UNPACK_FORMATS.items(): @@ -727,6 +796,7 @@ def _find_unpack_format(filename): return name return None + def unpack_archive(filename, extract_dir=None, format=None): """Unpack an archive. @@ -749,7 +819,7 @@ def unpack_archive(filename, extract_dir=None, format=None): try: format_info = _UNPACK_FORMATS[format] except KeyError: - raise ValueError("Unknown unpack format '{0}'".format(format)) + raise ValueError(f"Unknown unpack format '{format}'") func = format_info[1] func(filename, extract_dir, **dict(format_info[2])) @@ -757,7 +827,7 @@ def unpack_archive(filename, extract_dir=None, format=None): # we need to look at the registered unpackers supported extensions format = _find_unpack_format(filename) if format is None: - raise ReadError("Unknown archive format '{0}'".format(filename)) + raise ReadError(f"Unknown archive format '{filename}'") func = _UNPACK_FORMATS[format][1] kwargs = dict(_UNPACK_FORMATS[format][2]) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/sysconfig.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/sysconfig.py index b470a37..549733b 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/sysconfig.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/sysconfig.py @@ -1,15 +1,16 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012 The Python Software Foundation. # See LICENSE.txt and CONTRIBUTORS.txt. # """Access to Python's configuration information.""" +from __future__ import annotations import codecs import os import re import sys -from os.path import pardir, realpath +from os.path import pardir +from os.path import realpath try: import configparser except ImportError: @@ -45,26 +46,28 @@ else: # unable to retrieve the real program name _PROJECT_BASE = _safe_realpath(os.getcwd()) -if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower(): +if os.name == 'nt' and 'pcbuild' in _PROJECT_BASE[-8:].lower(): _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir)) # PC/VS7.1 -if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower(): +if os.name == 'nt' and '\\pc\\v' in _PROJECT_BASE[-10:].lower(): _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) # PC/AMD64 -if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower(): +if os.name == 'nt' and '\\pcbuild\\amd64' in _PROJECT_BASE[-14:].lower(): _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) def is_python_build(): - for fn in ("Setup.dist", "Setup.local"): - if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): + for fn in ('Setup.dist', 'Setup.local'): + if os.path.isfile(os.path.join(_PROJECT_BASE, 'Modules', fn)): return True return False + _PYTHON_BUILD = is_python_build() _cfg_read = False + def _ensure_cfg_read(): global _cfg_read if not _cfg_read: @@ -86,6 +89,7 @@ def _ensure_cfg_read(): _SCHEMES = configparser.RawConfigParser() _VAR_REPL = re.compile(r'\{([^{]*?)\}') + def _expand_globals(config): _ensure_cfg_read() if config.has_section('globals'): @@ -119,6 +123,7 @@ def _expand_globals(config): #_expand_globals(_SCHEMES) + _PY_VERSION = '%s.%s.%s' % sys.version_info[:3] _PY_VERSION_SHORT = '%s.%s' % sys.version_info[:2] _PY_VERSION_SHORT_NO_DOT = '%s%s' % sys.version_info[:2] @@ -182,32 +187,34 @@ def _get_default_scheme(): def _getuserbase(): - env_base = os.environ.get("PYTHONUSERBASE", None) + env_base = os.environ.get('PYTHONUSERBASE', None) def joinuser(*args): return os.path.expanduser(os.path.join(*args)) # what about 'os2emx', 'riscos' ? - if os.name == "nt": - base = os.environ.get("APPDATA") or "~" + if os.name == 'nt': + base = os.environ.get('APPDATA') or '~' if env_base: return env_base else: - return joinuser(base, "Python") + return joinuser(base, 'Python') - if sys.platform == "darwin": - framework = get_config_var("PYTHONFRAMEWORK") + if sys.platform == 'darwin': + framework = get_config_var('PYTHONFRAMEWORK') if framework: if env_base: return env_base else: - return joinuser("~", "Library", framework, "%d.%d" % - sys.version_info[:2]) + return joinuser( + '~', 'Library', framework, '%d.%d' % + sys.version_info[:2], + ) if env_base: return env_base else: - return joinuser("~", ".local") + return joinuser('~', '.local') def _parse_makefile(filename, vars=None): @@ -219,16 +226,16 @@ def _parse_makefile(filename, vars=None): """ # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). - _variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") - _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") - _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + _variable_rx = re.compile(r'([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)') + _findvar1_rx = re.compile(r'\$\(([A-Za-z][A-Za-z0-9_]*)\)') + _findvar2_rx = re.compile(r'\${([A-Za-z][A-Za-z0-9_]*)}') if vars is None: vars = {} done = {} notdone = {} - with codecs.open(filename, encoding='utf-8', errors="surrogateescape") as f: + with codecs.open(filename, encoding='utf-8', errors='surrogateescape') as f: lines = f.readlines() for line in lines: @@ -241,7 +248,7 @@ def _parse_makefile(filename, vars=None): # `$$' is a literal `$' in make tmpv = v.replace('$$', '') - if "$" in tmpv: + if '$' in tmpv: notdone[n] = v else: try: @@ -278,9 +285,11 @@ def _parse_makefile(filename, vars=None): item = os.environ[n] elif n in renamed_variables: - if (name.startswith('PY_') and - name[3:] in renamed_variables): - item = "" + if ( + name.startswith('PY_') and + name[3:] in renamed_variables + ): + item = '' elif 'PY_' + n in notdone: found = False @@ -289,12 +298,12 @@ def _parse_makefile(filename, vars=None): item = str(done['PY_' + n]) else: - done[n] = item = "" + done[n] = item = '' if found: after = value[m.end():] value = value[:m.start()] + item + after - if "$" in after: + if '$' in after: notdone[name] = value else: try: @@ -305,8 +314,10 @@ def _parse_makefile(filename, vars=None): done[name] = value variables.remove(name) - if (name.startswith('PY_') and - name[3:] in renamed_variables): + if ( + name.startswith('PY_') and + name[3:] in renamed_variables + ): name = name[3:] if name not in done: @@ -331,9 +342,9 @@ def _parse_makefile(filename, vars=None): def get_makefile_filename(): """Return the path of the Makefile.""" if _PYTHON_BUILD: - return os.path.join(_PROJECT_BASE, "Makefile") + return os.path.join(_PROJECT_BASE, 'Makefile') if hasattr(sys, 'abiflags'): - config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) + config_dir_name = 'config-{}{}'.format(_PY_VERSION_SHORT, sys.abiflags) else: config_dir_name = 'config' return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') @@ -345,21 +356,21 @@ def _init_posix(vars): makefile = get_makefile_filename() try: _parse_makefile(makefile, vars) - except IOError as e: - msg = "invalid Python installation: unable to open %s" % makefile - if hasattr(e, "strerror"): - msg = msg + " (%s)" % e.strerror - raise IOError(msg) + except OSError as e: + msg = 'invalid Python installation: unable to open %s' % makefile + if hasattr(e, 'strerror'): + msg = msg + ' (%s)' % e.strerror + raise OSError(msg) # load the installed pyconfig.h: config_h = get_config_h_filename() try: with open(config_h) as f: parse_config_h(f, vars) - except IOError as e: - msg = "invalid Python installation: unable to open %s" % config_h - if hasattr(e, "strerror"): - msg = msg + " (%s)" % e.strerror - raise IOError(msg) + except OSError as e: + msg = 'invalid Python installation: unable to open %s' % config_h + if hasattr(e, 'strerror'): + msg = msg + ' (%s)' % e.strerror + raise OSError(msg) # On AIX, there are wrong paths to the linker scripts in the Makefile # -- these paths are relative to the Python source, but when installed # the scripts are in another directory. @@ -392,8 +403,8 @@ def parse_config_h(fp, vars=None): """ if vars is None: vars = {} - define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") - undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + define_rx = re.compile('#define ([A-Z][A-Za-z0-9_]+) (.*)\n') + undef_rx = re.compile('/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n') while True: line = fp.readline() @@ -417,8 +428,8 @@ def parse_config_h(fp, vars=None): def get_config_h_filename(): """Return the path of pyconfig.h.""" if _PYTHON_BUILD: - if os.name == "nt": - inc_dir = os.path.join(_PROJECT_BASE, "PC") + if os.name == 'nt': + inc_dir = os.path.join(_PROJECT_BASE, 'PC') else: inc_dir = _PROJECT_BASE else: @@ -507,14 +518,16 @@ def get_config_vars(*args): # Normally it is relative to the build directory. However, during # testing, for example, we might be running a non-installed python # from a different directory. - if _PYTHON_BUILD and os.name == "posix": + if _PYTHON_BUILD and os.name == 'posix': base = _PROJECT_BASE try: cwd = os.getcwd() except OSError: cwd = None - if (not os.path.isabs(_CONFIG_VARS['srcdir']) and - base != cwd): + if ( + not os.path.isabs(_CONFIG_VARS['srcdir']) and + base != cwd + ): # srcdir is relative and we are not in the same directory # as the executable. Assume executable is in the build # directory and make srcdir absolute. @@ -530,10 +543,12 @@ def get_config_vars(*args): # are in CFLAGS or LDFLAGS and remove them if they are. # This is needed when building extensions on a 10.3 system # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + for key in ( + 'LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED', + ): flags = _CONFIG_VARS[key] flags = re.sub(r'-arch\s+\w+\s', ' ', flags) flags = re.sub('-isysroot [^ \t]*', ' ', flags) @@ -546,10 +561,12 @@ def get_config_vars(*args): # that OS release. if 'ARCHFLAGS' in os.environ: arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', + for key in ( + 'LDFLAGS', 'BASECFLAGS', # a number of derived variables. These need to be # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED', + ): flags = _CONFIG_VARS[key] flags = re.sub(r'-arch\s+\w+\s', ' ', flags) @@ -571,10 +588,12 @@ def get_config_vars(*args): if m is not None: sdk = m.group(1) if not os.path.exists(sdk): - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + for key in ( + 'LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED', + ): flags = _CONFIG_VARS[key] flags = re.sub(r'-isysroot\s+\S+(\s|$)', ' ', flags) @@ -625,19 +644,19 @@ def get_platform(): """ if os.name == 'nt': # sniff sys.version for architecture. - prefix = " bit (" + prefix = ' bit (' i = sys.version.find(prefix) if i == -1: return sys.platform - j = sys.version.find(")", i) - look = sys.version[i+len(prefix):j].lower() + j = sys.version.find(')', i) + look = sys.version[i + len(prefix):j].lower() if look == 'amd64': return 'win-amd64' if look == 'itanium': return 'win-ia64' return sys.platform - if os.name != "posix" or not hasattr(os, 'uname'): + if os.name != 'posix' or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, # Mac OS is M68k or PPC, etc. return sys.platform @@ -651,27 +670,27 @@ def get_platform(): machine = machine.replace(' ', '_') machine = machine.replace('/', '-') - if osname[:5] == "linux": + if osname[:5] == 'linux': # At least on Linux/Intel, 'machine' is the processor -- # i386, etc. # XXX what about Alpha, SPARC, etc? - return "%s-%s" % (osname, machine) - elif osname[:5] == "sunos": - if release[0] >= "5": # SunOS 5 == Solaris 2 - osname = "solaris" - release = "%d.%s" % (int(release[0]) - 3, release[2:]) + return '{}-{}'.format(osname, machine) + elif osname[:5] == 'sunos': + if release[0] >= '5': # SunOS 5 == Solaris 2 + osname = 'solaris' + release = '%d.%s' % (int(release[0]) - 3, release[2:]) # fall through to standard osname-release-machine representation - elif osname[:4] == "irix": # could be "irix64"! - return "%s-%s" % (osname, release) - elif osname[:3] == "aix": - return "%s-%s.%s" % (osname, version, release) - elif osname[:6] == "cygwin": - osname = "cygwin" + elif osname[:4] == 'irix': # could be "irix64"! + return '{}-{}'.format(osname, release) + elif osname[:3] == 'aix': + return '{}-{}.{}'.format(osname, version, release) + elif osname[:6] == 'cygwin': + osname = 'cygwin' rel_re = re.compile(r'[\d.]+') m = rel_re.match(release) if m: release = m.group() - elif osname[:6] == "darwin": + elif osname[:6] == 'darwin': # # For our purposes, we'll assume that the system version from # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set @@ -691,14 +710,16 @@ def get_platform(): # the Gestalt Manager) try: f = open('/System/Library/CoreServices/SystemVersion.plist') - except IOError: + except OSError: # We're on a plain darwin box, fall back to the default # behaviour. pass else: try: - m = re.search(r'ProductUserVisibleVersion\s*' - r'(.*?)', f.read()) + m = re.search( + r'ProductUserVisibleVersion\s*' + r'(.*?)', f.read(), + ) finally: f.close() if m is not None: @@ -710,10 +731,10 @@ def get_platform(): if macver: release = macver - osname = "macosx" + osname = 'macosx' if ((macrelease + '.') >= '10.4.' and - '-arch' in get_config_vars().get('CFLAGS', '').strip()): + '-arch' in get_config_vars().get('CFLAGS', '').strip()): # The universal build will build fat binaries, but not on # systems before 10.4 # @@ -740,7 +761,8 @@ def get_platform(): machine = 'universal' else: raise ValueError( - "Don't know machine value for archs=%r" % (archs,)) + "Don't know machine value for archs={!r}".format(archs), + ) elif machine == 'i386': # On OSX the machine type returned by uname is always the @@ -757,7 +779,7 @@ def get_platform(): else: machine = 'ppc' - return "%s-%s-%s" % (osname, release, machine) + return '{}-{}-{}'.format(osname, release, machine) def get_python_version(): @@ -768,7 +790,7 @@ def _print_dict(title, data): for index, (key, value) in enumerate(sorted(data.items())): if index == 0: print('%s: ' % (title)) - print('\t%s = "%s"' % (key, value)) + print('\t{} = "{}"'.format(key, value)) def _main(): diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/tarfile.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/tarfile.py index d66d856..8d2932f 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/tarfile.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/tarfile.py @@ -25,33 +25,34 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # -from __future__ import print_function +from __future__ import annotations +import re +import copy +import struct +import time +import errno +import stat +import os +import sys """Read from and write to tar format archives. """ -__version__ = "$Revision$" +__version__ = '$Revision$' -version = "0.9.0" -__author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" -__date__ = "$Date: 2011-02-25 17:42:01 +0200 (Fri, 25 Feb 2011) $" -__cvsid__ = "$Id: tarfile.py 88586 2011-02-25 15:42:01Z marc-andre.lemburg $" -__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." +version = '0.9.0' +__author__ = 'Lars Gust\u00e4bel (lars@gustaebel.de)' +__date__ = '$Date: 2011-02-25 17:42:01 +0200 (Fri, 25 Feb 2011) $' +__cvsid__ = '$Id: tarfile.py 88586 2011-02-25 15:42:01Z marc-andre.lemburg $' +__credits__ = 'Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend.' #--------- # Imports #--------- -import sys -import os -import stat -import errno -import time -import struct -import copy -import re try: - import grp, pwd + import grp + import pwd except ImportError: grp = pwd = None @@ -65,7 +66,7 @@ except NameError: pass # from tarfile import * -__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError"] +__all__ = ['TarFile', 'TarInfo', 'is_tarfile', 'TarError'] if sys.version_info[0] < 3: import __builtin__ as builtins @@ -77,33 +78,33 @@ _open = builtins.open # Since 'open' is TarFile.open #--------------------------------------------------------- # tar constants #--------------------------------------------------------- -NUL = b"\0" # the null character +NUL = b'\0' # the null character BLOCKSIZE = 512 # length of processing blocks RECORDSIZE = BLOCKSIZE * 20 # length of records -GNU_MAGIC = b"ustar \0" # magic gnu tar string -POSIX_MAGIC = b"ustar\x0000" # magic posix tar string +GNU_MAGIC = b'ustar \0' # magic gnu tar string +POSIX_MAGIC = b'ustar\x0000' # magic posix tar string LENGTH_NAME = 100 # maximum length of a filename LENGTH_LINK = 100 # maximum length of a linkname LENGTH_PREFIX = 155 # maximum length of the prefix field -REGTYPE = b"0" # regular file -AREGTYPE = b"\0" # regular file -LNKTYPE = b"1" # link (inside tarfile) -SYMTYPE = b"2" # symbolic link -CHRTYPE = b"3" # character special device -BLKTYPE = b"4" # block special device -DIRTYPE = b"5" # directory -FIFOTYPE = b"6" # fifo special device -CONTTYPE = b"7" # contiguous file +REGTYPE = b'0' # regular file +AREGTYPE = b'\0' # regular file +LNKTYPE = b'1' # link (inside tarfile) +SYMTYPE = b'2' # symbolic link +CHRTYPE = b'3' # character special device +BLKTYPE = b'4' # block special device +DIRTYPE = b'5' # directory +FIFOTYPE = b'6' # fifo special device +CONTTYPE = b'7' # contiguous file -GNUTYPE_LONGNAME = b"L" # GNU tar longname -GNUTYPE_LONGLINK = b"K" # GNU tar longlink -GNUTYPE_SPARSE = b"S" # GNU tar sparse file +GNUTYPE_LONGNAME = b'L' # GNU tar longname +GNUTYPE_LONGLINK = b'K' # GNU tar longlink +GNUTYPE_SPARSE = b'S' # GNU tar sparse file -XHDTYPE = b"x" # POSIX.1-2001 extended header -XGLTYPE = b"g" # POSIX.1-2001 global header -SOLARIS_XHDTYPE = b"X" # Solaris extended header +XHDTYPE = b'x' # POSIX.1-2001 extended header +XGLTYPE = b'g' # POSIX.1-2001 global header +SOLARIS_XHDTYPE = b'X' # Solaris extended header USTAR_FORMAT = 0 # POSIX.1-1988 (ustar) format GNU_FORMAT = 1 # GNU tar format @@ -114,36 +115,44 @@ DEFAULT_FORMAT = GNU_FORMAT # tarfile constants #--------------------------------------------------------- # File types that tarfile supports: -SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE, - SYMTYPE, DIRTYPE, FIFOTYPE, - CONTTYPE, CHRTYPE, BLKTYPE, - GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, - GNUTYPE_SPARSE) +SUPPORTED_TYPES = ( + REGTYPE, AREGTYPE, LNKTYPE, + SYMTYPE, DIRTYPE, FIFOTYPE, + CONTTYPE, CHRTYPE, BLKTYPE, + GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE, +) # File types that will be treated as a regular file. -REGULAR_TYPES = (REGTYPE, AREGTYPE, - CONTTYPE, GNUTYPE_SPARSE) +REGULAR_TYPES = ( + REGTYPE, AREGTYPE, + CONTTYPE, GNUTYPE_SPARSE, +) # File types that are part of the GNU tar format. -GNU_TYPES = (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, - GNUTYPE_SPARSE) +GNU_TYPES = ( + GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE, +) # Fields from a pax header that override a TarInfo attribute. -PAX_FIELDS = ("path", "linkpath", "size", "mtime", - "uid", "gid", "uname", "gname") +PAX_FIELDS = ( + 'path', 'linkpath', 'size', 'mtime', + 'uid', 'gid', 'uname', 'gname', +) # Fields from a pax header that are affected by hdrcharset. -PAX_NAME_FIELDS = set(("path", "linkpath", "uname", "gname")) +PAX_NAME_FIELDS = {'path', 'linkpath', 'uname', 'gname'} # Fields in a pax header that are numbers, all other fields # are treated as strings. PAX_NUMBER_FIELDS = { - "atime": float, - "ctime": float, - "mtime": float, - "uid": int, - "gid": int, - "size": int + 'atime': float, + 'ctime': float, + 'mtime': float, + 'uid': int, + 'gid': int, + 'size': int, } #--------------------------------------------------------- @@ -156,25 +165,25 @@ S_IFDIR = 0o040000 # directory S_IFCHR = 0o020000 # character device S_IFIFO = 0o010000 # fifo -TSUID = 0o4000 # set UID on execution -TSGID = 0o2000 # set GID on execution -TSVTX = 0o1000 # reserved +TSUID = 0o4000 # set UID on execution +TSGID = 0o2000 # set GID on execution +TSVTX = 0o1000 # reserved -TUREAD = 0o400 # read by owner +TUREAD = 0o400 # read by owner TUWRITE = 0o200 # write by owner -TUEXEC = 0o100 # execute/search by owner -TGREAD = 0o040 # read by group +TUEXEC = 0o100 # execute/search by owner +TGREAD = 0o040 # read by group TGWRITE = 0o020 # write by group -TGEXEC = 0o010 # execute/search by group -TOREAD = 0o004 # read by other +TGEXEC = 0o010 # execute/search by group +TOREAD = 0o004 # read by other TOWRITE = 0o002 # write by other -TOEXEC = 0o001 # execute/search by other +TOEXEC = 0o001 # execute/search by other #--------------------------------------------------------- # initialization #--------------------------------------------------------- -if os.name in ("nt", "ce"): - ENCODING = "utf-8" +if os.name in ('nt', 'ce'): + ENCODING = 'utf-8' else: ENCODING = sys.getfilesystemencoding() @@ -182,20 +191,23 @@ else: # Some useful functions #--------------------------------------------------------- + def stn(s, length, encoding, errors): """Convert a string to a null-terminated bytes object. """ s = s.encode(encoding, errors) return s[:length] + (length - len(s)) * NUL + def nts(s, encoding, errors): """Convert a null-terminated bytes object to a string. """ - p = s.find(b"\0") + p = s.find(b'\0') if p != -1: s = s[:p] return s.decode(encoding, errors) + def nti(s): """Convert a number field to a python number. """ @@ -203,9 +215,9 @@ def nti(s): # itn() below. if s[0] != chr(0o200): try: - n = int(nts(s, "ascii", "strict") or "0", 8) + n = int(nts(s, 'ascii', 'strict') or '0', 8) except ValueError: - raise InvalidHeaderError("invalid header") + raise InvalidHeaderError('invalid header') else: n = 0 for i in range(len(s) - 1): @@ -213,6 +225,7 @@ def nti(s): n += ord(s[i + 1]) return n + def itn(n, digits=8, format=DEFAULT_FORMAT): """Convert a python number to a number field. """ @@ -223,15 +236,15 @@ def itn(n, digits=8, format=DEFAULT_FORMAT): # encoding, the following digits-1 bytes are a big-endian # representation. This allows values up to (256**(digits-1))-1. if 0 <= n < 8 ** (digits - 1): - s = ("%0*o" % (digits - 1, n)).encode("ascii") + NUL + s = ('%0*o' % (digits - 1, n)).encode('ascii') + NUL else: if format != GNU_FORMAT or n >= 256 ** (digits - 1): - raise ValueError("overflow in number field") + raise ValueError('overflow in number field') if n < 0: # XXX We mimic GNU tar's behaviour with negative numbers, # this could raise OverflowError. - n = struct.unpack("L", struct.pack("l", n))[0] + n = struct.unpack('L', struct.pack('l', n))[0] s = bytearray() for i in range(digits - 1): @@ -240,6 +253,7 @@ def itn(n, digits=8, format=DEFAULT_FORMAT): s.insert(0, 0o200) return s + def calc_chksums(buf): """Calculate the checksum for a member's header by summing up all characters except for the chksum field which is treated as if @@ -249,10 +263,11 @@ def calc_chksums(buf): the high bit set. So we calculate two checksums, unsigned and signed. """ - unsigned_chksum = 256 + sum(struct.unpack("148B", buf[:148]) + struct.unpack("356B", buf[156:512])) - signed_chksum = 256 + sum(struct.unpack("148b", buf[:148]) + struct.unpack("356b", buf[156:512])) + unsigned_chksum = 256 + sum(struct.unpack('148B', buf[:148]) + struct.unpack('356B', buf[156:512])) + signed_chksum = 256 + sum(struct.unpack('148b', buf[:148]) + struct.unpack('356b', buf[156:512])) return unsigned_chksum, signed_chksum + def copyfileobj(src, dst, length=None): """Copy length bytes from fileobj src to fileobj dst. If length is None, copy the entire content. @@ -261,7 +276,7 @@ def copyfileobj(src, dst, length=None): return if length is None: while True: - buf = src.read(16*1024) + buf = src.read(16 * 1024) if not buf: break dst.write(buf) @@ -272,43 +287,53 @@ def copyfileobj(src, dst, length=None): for b in range(blocks): buf = src.read(BUFSIZE) if len(buf) < BUFSIZE: - raise IOError("end of file reached") + raise OSError('end of file reached') dst.write(buf) if remainder != 0: buf = src.read(remainder) if len(buf) < remainder: - raise IOError("end of file reached") + raise OSError('end of file reached') dst.write(buf) return + filemode_table = ( - ((S_IFLNK, "l"), - (S_IFREG, "-"), - (S_IFBLK, "b"), - (S_IFDIR, "d"), - (S_IFCHR, "c"), - (S_IFIFO, "p")), + ( + (S_IFLNK, 'l'), + (S_IFREG, '-'), + (S_IFBLK, 'b'), + (S_IFDIR, 'd'), + (S_IFCHR, 'c'), + (S_IFIFO, 'p'), + ), - ((TUREAD, "r"),), - ((TUWRITE, "w"),), - ((TUEXEC|TSUID, "s"), - (TSUID, "S"), - (TUEXEC, "x")), + ((TUREAD, 'r'),), + ((TUWRITE, 'w'),), + ( + (TUEXEC | TSUID, 's'), + (TSUID, 'S'), + (TUEXEC, 'x'), + ), - ((TGREAD, "r"),), - ((TGWRITE, "w"),), - ((TGEXEC|TSGID, "s"), - (TSGID, "S"), - (TGEXEC, "x")), + ((TGREAD, 'r'),), + ((TGWRITE, 'w'),), + ( + (TGEXEC | TSGID, 's'), + (TSGID, 'S'), + (TGEXEC, 'x'), + ), - ((TOREAD, "r"),), - ((TOWRITE, "w"),), - ((TOEXEC|TSVTX, "t"), - (TSVTX, "T"), - (TOEXEC, "x")) + ((TOREAD, 'r'),), + ((TOWRITE, 'w'),), + ( + (TOEXEC | TSVTX, 't'), + (TSVTX, 'T'), + (TOEXEC, 'x'), + ), ) + def filemode(mode): """Convert a file's mode to a string of the form -rwxrwxrwx. @@ -321,39 +346,60 @@ def filemode(mode): perm.append(char) break else: - perm.append("-") - return "".join(perm) + perm.append('-') + return ''.join(perm) + class TarError(Exception): """Base exception.""" pass + + class ExtractError(TarError): """General exception for extract errors.""" pass + + class ReadError(TarError): """Exception for unreadable tar archives.""" pass + + class CompressionError(TarError): """Exception for unavailable compression methods.""" pass + + class StreamError(TarError): """Exception for unsupported operations on stream-like TarFiles.""" pass + + class HeaderError(TarError): """Base exception for header errors.""" pass + + class EmptyHeaderError(HeaderError): """Exception for empty headers.""" pass + + class TruncatedHeaderError(HeaderError): """Exception for truncated headers.""" pass + + class EOFHeaderError(HeaderError): """Exception for end of file headers.""" pass + + class InvalidHeaderError(HeaderError): """Exception for invalid headers.""" pass + + class SubsequentHeaderError(HeaderError): """Exception for missing and invalid extended headers.""" pass @@ -361,7 +407,9 @@ class SubsequentHeaderError(HeaderError): #--------------------------- # internal stream interface #--------------------------- -class _LowLevelFile(object): + + +class _LowLevelFile: """Low-level file object. Supports reading and writing. It is used instead of a regular file object for streaming access. @@ -369,10 +417,10 @@ class _LowLevelFile(object): def __init__(self, name, mode): mode = { - "r": os.O_RDONLY, - "w": os.O_WRONLY | os.O_CREAT | os.O_TRUNC, + 'r': os.O_RDONLY, + 'w': os.O_WRONLY | os.O_CREAT | os.O_TRUNC, }[mode] - if hasattr(os, "O_BINARY"): + if hasattr(os, 'O_BINARY'): mode |= os.O_BINARY self.fd = os.open(name, mode, 0o666) @@ -385,7 +433,8 @@ class _LowLevelFile(object): def write(self, s): os.write(self.fd, s) -class _Stream(object): + +class _Stream: """Class that serves as an adapter between TarFile and a stream-like object. The stream-like object only needs to have a read() or write() method and is accessed @@ -410,35 +459,35 @@ class _Stream(object): fileobj = _StreamProxy(fileobj) comptype = fileobj.getcomptype() - self.name = name or "" - self.mode = mode + self.name = name or '' + self.mode = mode self.comptype = comptype - self.fileobj = fileobj - self.bufsize = bufsize - self.buf = b"" - self.pos = 0 - self.closed = False + self.fileobj = fileobj + self.bufsize = bufsize + self.buf = b'' + self.pos = 0 + self.closed = False try: - if comptype == "gz": + if comptype == 'gz': try: import zlib except ImportError: - raise CompressionError("zlib module is not available") + raise CompressionError('zlib module is not available') self.zlib = zlib - self.crc = zlib.crc32(b"") - if mode == "r": + self.crc = zlib.crc32(b'') + if mode == 'r': self._init_read_gz() else: self._init_write_gz() - if comptype == "bz2": + if comptype == 'bz2': try: import bz2 except ImportError: - raise CompressionError("bz2 module is not available") - if mode == "r": - self.dbuf = b"" + raise CompressionError('bz2 module is not available') + if mode == 'r': + self.dbuf = b'' self.cmp = bz2.BZ2Decompressor() else: self.cmp = bz2.BZ2Compressor() @@ -449,30 +498,32 @@ class _Stream(object): raise def __del__(self): - if hasattr(self, "closed") and not self.closed: + if hasattr(self, 'closed') and not self.closed: self.close() def _init_write_gz(self): """Initialize for writing with gzip compression. """ - self.cmp = self.zlib.compressobj(9, self.zlib.DEFLATED, - -self.zlib.MAX_WBITS, - self.zlib.DEF_MEM_LEVEL, - 0) - timestamp = struct.pack(" 0: while True: data, start, stop, offset = self.map[self.map_index] @@ -779,24 +834,26 @@ class _FileInFile(object): #class _FileInFile -class ExFileObject(object): +class ExFileObject: """File-like object for reading an archive member. Is returned by TarFile.extractfile(). """ blocksize = 1024 def __init__(self, tarfile, tarinfo): - self.fileobj = _FileInFile(tarfile.fileobj, - tarinfo.offset_data, - tarinfo.size, - tarinfo.sparse) + self.fileobj = _FileInFile( + tarfile.fileobj, + tarinfo.offset_data, + tarinfo.size, + tarinfo.sparse, + ) self.name = tarinfo.name - self.mode = "r" + self.mode = 'r' self.closed = False self.size = tarinfo.size self.position = 0 - self.buffer = b"" + self.buffer = b'' def readable(self): return True @@ -812,13 +869,13 @@ class ExFileObject(object): present or None, read all data until EOF is reached. """ if self.closed: - raise ValueError("I/O operation on closed file") + raise ValueError('I/O operation on closed file') - buf = b"" + buf = b'' if self.buffer: if size is None: buf = self.buffer - self.buffer = b"" + self.buffer = b'' else: buf = self.buffer[:size] self.buffer = self.buffer[size:] @@ -840,16 +897,16 @@ class ExFileObject(object): size, which may be an incomplete line. """ if self.closed: - raise ValueError("I/O operation on closed file") + raise ValueError('I/O operation on closed file') - pos = self.buffer.find(b"\n") + 1 + pos = self.buffer.find(b'\n') + 1 if pos == 0: # no newline found. while True: buf = self.fileobj.read(self.blocksize) self.buffer += buf - if not buf or b"\n" in buf: - pos = self.buffer.find(b"\n") + 1 + if not buf or b'\n' in buf: + pos = self.buffer.find(b'\n') + 1 if pos == 0: # no newline found. pos = len(self.buffer) @@ -869,7 +926,8 @@ class ExFileObject(object): result = [] while True: line = self.readline() - if not line: break + if not line: + break result.append(line) return result @@ -877,7 +935,7 @@ class ExFileObject(object): """Return the current file position. """ if self.closed: - raise ValueError("I/O operation on closed file") + raise ValueError('I/O operation on closed file') return self.position @@ -885,7 +943,7 @@ class ExFileObject(object): """Seek to a position in the file. """ if self.closed: - raise ValueError("I/O operation on closed file") + raise ValueError('I/O operation on closed file') if whence == os.SEEK_SET: self.position = min(max(pos, 0), self.size) @@ -897,9 +955,9 @@ class ExFileObject(object): elif whence == os.SEEK_END: self.position = max(min(self.size + pos, self.size), 0) else: - raise ValueError("Invalid argument") + raise ValueError('Invalid argument') - self.buffer = b"" + self.buffer = b'' self.fileobj.seek(self.position) def close(self): @@ -920,7 +978,9 @@ class ExFileObject(object): #------------------ # Exported Classes #------------------ -class TarInfo(object): + + +class TarInfo: """Informational class which holds the details about an archive member given by a tar header block. TarInfo objects are returned by TarFile.getmember(), @@ -928,13 +988,15 @@ class TarInfo(object): usually created internally. """ - __slots__ = ("name", "mode", "uid", "gid", "size", "mtime", - "chksum", "type", "linkname", "uname", "gname", - "devmajor", "devminor", - "offset", "offset_data", "pax_headers", "sparse", - "tarfile", "_sparse_structs", "_link_target") + __slots__ = ( + 'name', 'mode', 'uid', 'gid', 'size', 'mtime', + 'chksum', 'type', 'linkname', 'uname', 'gname', + 'devmajor', 'devminor', + 'offset', 'offset_data', 'pax_headers', 'sparse', + 'tarfile', '_sparse_structs', '_link_target', + ) - def __init__(self, name=""): + def __init__(self, name=''): """Construct a TarInfo object. name is the optional name of the member. """ @@ -946,9 +1008,9 @@ class TarInfo(object): self.mtime = 0 # modification time self.chksum = 0 # header checksum self.type = REGTYPE # member type - self.linkname = "" # link name - self.uname = "" # user name - self.gname = "" # group name + self.linkname = '' # link name + self.uname = '' # user name + self.gname = '' # group name self.devmajor = 0 # device major number self.devminor = 0 # device minor number @@ -962,44 +1024,46 @@ class TarInfo(object): # "path" and "linkpath". def _getpath(self): return self.name + def _setpath(self, name): self.name = name path = property(_getpath, _setpath) def _getlinkpath(self): return self.linkname + def _setlinkpath(self, linkname): self.linkname = linkname linkpath = property(_getlinkpath, _setlinkpath) def __repr__(self): - return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + return '<{} {!r} at {:#x}>'.format(self.__class__.__name__, self.name, id(self)) def get_info(self): """Return the TarInfo's attributes as a dictionary. """ info = { - "name": self.name, - "mode": self.mode & 0o7777, - "uid": self.uid, - "gid": self.gid, - "size": self.size, - "mtime": self.mtime, - "chksum": self.chksum, - "type": self.type, - "linkname": self.linkname, - "uname": self.uname, - "gname": self.gname, - "devmajor": self.devmajor, - "devminor": self.devminor + 'name': self.name, + 'mode': self.mode & 0o7777, + 'uid': self.uid, + 'gid': self.gid, + 'size': self.size, + 'mtime': self.mtime, + 'chksum': self.chksum, + 'type': self.type, + 'linkname': self.linkname, + 'uname': self.uname, + 'gname': self.gname, + 'devmajor': self.devmajor, + 'devminor': self.devminor, } - if info["type"] == DIRTYPE and not info["name"].endswith("/"): - info["name"] += "/" + if info['type'] == DIRTYPE and not info['name'].endswith('/'): + info['name'] += '/' return info - def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescape"): + def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors='surrogateescape'): """Return a tar header as a string of 512 byte blocks. """ info = self.get_info() @@ -1011,32 +1075,32 @@ class TarInfo(object): elif format == PAX_FORMAT: return self.create_pax_header(info, encoding) else: - raise ValueError("invalid format") + raise ValueError('invalid format') def create_ustar_header(self, info, encoding, errors): """Return the object as a ustar header block. """ - info["magic"] = POSIX_MAGIC + info['magic'] = POSIX_MAGIC - if len(info["linkname"]) > LENGTH_LINK: - raise ValueError("linkname is too long") + if len(info['linkname']) > LENGTH_LINK: + raise ValueError('linkname is too long') - if len(info["name"]) > LENGTH_NAME: - info["prefix"], info["name"] = self._posix_split_name(info["name"]) + if len(info['name']) > LENGTH_NAME: + info['prefix'], info['name'] = self._posix_split_name(info['name']) return self._create_header(info, USTAR_FORMAT, encoding, errors) def create_gnu_header(self, info, encoding, errors): """Return the object as a GNU header block sequence. """ - info["magic"] = GNU_MAGIC + info['magic'] = GNU_MAGIC - buf = b"" - if len(info["linkname"]) > LENGTH_LINK: - buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors) + buf = b'' + if len(info['linkname']) > LENGTH_LINK: + buf += self._create_gnu_long_header(info['linkname'], GNUTYPE_LONGLINK, encoding, errors) - if len(info["name"]) > LENGTH_NAME: - buf += self._create_gnu_long_header(info["name"], GNUTYPE_LONGNAME, encoding, errors) + if len(info['name']) > LENGTH_NAME: + buf += self._create_gnu_long_header(info['name'], GNUTYPE_LONGNAME, encoding, errors) return buf + self._create_header(info, GNU_FORMAT, encoding, errors) @@ -1045,14 +1109,15 @@ class TarInfo(object): represented this way, prepend a pax extended header sequence with supplement information. """ - info["magic"] = POSIX_MAGIC + info['magic'] = POSIX_MAGIC pax_headers = self.pax_headers.copy() # Test string fields for values that exceed the field length or cannot # be represented in ASCII encoding. for name, hname, length in ( - ("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK), - ("uname", "uname", 32), ("gname", "gname", 32)): + ('name', 'path', LENGTH_NAME), ('linkname', 'linkpath', LENGTH_LINK), + ('uname', 'uname', 32), ('gname', 'gname', 32), + ): if hname in pax_headers: # The pax header has priority. @@ -1060,7 +1125,7 @@ class TarInfo(object): # Try to encode the string as ASCII. try: - info[name].encode("ascii", "strict") + info[name].encode('ascii', 'strict') except UnicodeEncodeError: pax_headers[hname] = info[name] continue @@ -1070,7 +1135,7 @@ class TarInfo(object): # Test number fields for values that exceed the field limit or values # that like to be stored as float. - for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)): + for name, digits in (('uid', 8), ('gid', 8), ('size', 12), ('mtime', 12)): if name in pax_headers: # The pax header has priority. Avoid overflow. info[name] = 0 @@ -1085,29 +1150,29 @@ class TarInfo(object): if pax_headers: buf = self._create_pax_generic_header(pax_headers, XHDTYPE, encoding) else: - buf = b"" + buf = b'' - return buf + self._create_header(info, USTAR_FORMAT, "ascii", "replace") + return buf + self._create_header(info, USTAR_FORMAT, 'ascii', 'replace') @classmethod def create_pax_global_header(cls, pax_headers): """Return the object as a pax global header block sequence. """ - return cls._create_pax_generic_header(pax_headers, XGLTYPE, "utf8") + return cls._create_pax_generic_header(pax_headers, XGLTYPE, 'utf8') def _posix_split_name(self, name): """Split a name longer than 100 chars into a prefix and a name part. """ prefix = name[:LENGTH_PREFIX + 1] - while prefix and prefix[-1] != "/": + while prefix and prefix[-1] != '/': prefix = prefix[:-1] name = name[len(prefix):] prefix = prefix[:-1] if not prefix or len(name) > LENGTH_NAME: - raise ValueError("name is too long") + raise ValueError('name is too long') return prefix, name @staticmethod @@ -1116,26 +1181,26 @@ class TarInfo(object): information, format must be one of the *_FORMAT constants. """ parts = [ - stn(info.get("name", ""), 100, encoding, errors), - itn(info.get("mode", 0) & 0o7777, 8, format), - itn(info.get("uid", 0), 8, format), - itn(info.get("gid", 0), 8, format), - itn(info.get("size", 0), 12, format), - itn(info.get("mtime", 0), 12, format), - b" ", # checksum field - info.get("type", REGTYPE), - stn(info.get("linkname", ""), 100, encoding, errors), - info.get("magic", POSIX_MAGIC), - stn(info.get("uname", ""), 32, encoding, errors), - stn(info.get("gname", ""), 32, encoding, errors), - itn(info.get("devmajor", 0), 8, format), - itn(info.get("devminor", 0), 8, format), - stn(info.get("prefix", ""), 155, encoding, errors) + stn(info.get('name', ''), 100, encoding, errors), + itn(info.get('mode', 0) & 0o7777, 8, format), + itn(info.get('uid', 0), 8, format), + itn(info.get('gid', 0), 8, format), + itn(info.get('size', 0), 12, format), + itn(info.get('mtime', 0), 12, format), + b' ', # checksum field + info.get('type', REGTYPE), + stn(info.get('linkname', ''), 100, encoding, errors), + info.get('magic', POSIX_MAGIC), + stn(info.get('uname', ''), 32, encoding, errors), + stn(info.get('gname', ''), 32, encoding, errors), + itn(info.get('devmajor', 0), 8, format), + itn(info.get('devminor', 0), 8, format), + stn(info.get('prefix', ''), 155, encoding, errors), ] - buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts)) + buf = struct.pack('%ds' % BLOCKSIZE, b''.join(parts)) chksum = calc_chksums(buf[-BLOCKSIZE:])[0] - buf = buf[:-364] + ("%06o\0" % chksum).encode("ascii") + buf[-357:] + buf = buf[:-364] + ('%06o\0' % chksum).encode('ascii') + buf[-357:] return buf @staticmethod @@ -1156,14 +1221,14 @@ class TarInfo(object): name = name.encode(encoding, errors) + NUL info = {} - info["name"] = "././@LongLink" - info["type"] = type - info["size"] = len(name) - info["magic"] = GNU_MAGIC + info['name'] = '././@LongLink' + info['type'] = type + info['size'] = len(name) + info['magic'] = GNU_MAGIC # create extended header + name blocks. return cls._create_header(info, USTAR_FORMAT, encoding, errors) + \ - cls._create_payload(name) + cls._create_payload(name) @classmethod def _create_pax_generic_header(cls, pax_headers, type, encoding): @@ -1176,24 +1241,24 @@ class TarInfo(object): binary = False for keyword, value in pax_headers.items(): try: - value.encode("utf8", "strict") + value.encode('utf8', 'strict') except UnicodeEncodeError: binary = True break - records = b"" + records = b'' if binary: # Put the hdrcharset field at the beginning of the header. - records += b"21 hdrcharset=BINARY\n" + records += b'21 hdrcharset=BINARY\n' for keyword, value in pax_headers.items(): - keyword = keyword.encode("utf8") + keyword = keyword.encode('utf8') if binary: # Try to restore the original byte representation of `value'. # Needless to say, that the encoding must match the string. - value = value.encode(encoding, "surrogateescape") + value = value.encode(encoding, 'surrogateescape') else: - value = value.encode("utf8") + value = value.encode('utf8') l = len(keyword) + len(value) + 3 # ' ' + '=' + '\n' n = p = 0 @@ -1202,34 +1267,34 @@ class TarInfo(object): if n == p: break p = n - records += bytes(str(p), "ascii") + b" " + keyword + b"=" + value + b"\n" + records += bytes(str(p), 'ascii') + b' ' + keyword + b'=' + value + b'\n' # We use a hardcoded "././@PaxHeader" name like star does # instead of the one that POSIX recommends. info = {} - info["name"] = "././@PaxHeader" - info["type"] = type - info["size"] = len(records) - info["magic"] = POSIX_MAGIC + info['name'] = '././@PaxHeader' + info['type'] = type + info['size'] = len(records) + info['magic'] = POSIX_MAGIC # Create pax header + record blocks. - return cls._create_header(info, USTAR_FORMAT, "ascii", "replace") + \ - cls._create_payload(records) + return cls._create_header(info, USTAR_FORMAT, 'ascii', 'replace') + \ + cls._create_payload(records) @classmethod def frombuf(cls, buf, encoding, errors): """Construct a TarInfo object from a 512 byte bytes object. """ if len(buf) == 0: - raise EmptyHeaderError("empty header") + raise EmptyHeaderError('empty header') if len(buf) != BLOCKSIZE: - raise TruncatedHeaderError("truncated header") + raise TruncatedHeaderError('truncated header') if buf.count(NUL) == BLOCKSIZE: - raise EOFHeaderError("end of file header") + raise EOFHeaderError('end of file header') chksum = nti(buf[148:156]) if chksum not in calc_chksums(buf): - raise InvalidHeaderError("bad checksum") + raise InvalidHeaderError('bad checksum') obj = cls() obj.name = nts(buf[0:100], encoding, errors) @@ -1249,7 +1314,7 @@ class TarInfo(object): # Old V7 tar format represents a directory as a regular # file with a trailing slash. - if obj.type == AREGTYPE and obj.name.endswith("/"): + if obj.type == AREGTYPE and obj.name.endswith('/'): obj.type = DIRTYPE # The old GNU sparse format occupies some of the unused @@ -1272,11 +1337,11 @@ class TarInfo(object): # Remove redundant slashes from directories. if obj.isdir(): - obj.name = obj.name.rstrip("/") + obj.name = obj.name.rstrip('/') # Reconstruct a ustar longname. if prefix and obj.type not in GNU_TYPES: - obj.name = prefix + "/" + obj.name + obj.name = prefix + '/' + obj.name return obj @classmethod @@ -1340,7 +1405,7 @@ class TarInfo(object): try: next = self.fromtarfile(tarfile) except HeaderError: - raise SubsequentHeaderError("missing or bad subsequent header") + raise SubsequentHeaderError('missing or bad subsequent header') # Patch the TarInfo object from the next header with # the longname information. @@ -1400,24 +1465,24 @@ class TarInfo(object): # these fields are UTF-8 encoded but since POSIX.1-2008 tar # implementations are allowed to store them as raw binary strings if # the translation to UTF-8 fails. - match = re.search(br"\d+ hdrcharset=([^\n]+)\n", buf) + match = re.search(br'\d+ hdrcharset=([^\n]+)\n', buf) if match is not None: - pax_headers["hdrcharset"] = match.group(1).decode("utf8") + pax_headers['hdrcharset'] = match.group(1).decode('utf8') # For the time being, we don't care about anything other than "BINARY". # The only other value that is currently allowed by the standard is # "ISO-IR 10646 2000 UTF-8" in other words UTF-8. - hdrcharset = pax_headers.get("hdrcharset") - if hdrcharset == "BINARY": + hdrcharset = pax_headers.get('hdrcharset') + if hdrcharset == 'BINARY': encoding = tarfile.encoding else: - encoding = "utf8" + encoding = 'utf8' # Parse pax header information. A record looks like that: # "%d %s=%s\n" % (length, keyword, value). length is the size # of the complete record including the length field itself and # the newline. keyword and value are both UTF-8 encoded strings. - regex = re.compile(br"(\d+) ([^=]+)=") + regex = re.compile(br'(\d+) ([^=]+)=') pos = 0 while True: match = regex.match(buf, pos) @@ -1435,14 +1500,20 @@ class TarInfo(object): # hdrcharset=BINARY header). # We first try the strict standard encoding, and if that fails we # fall back on the user's encoding and error handler. - keyword = self._decode_pax_field(keyword, "utf8", "utf8", - tarfile.errors) + keyword = self._decode_pax_field( + keyword, 'utf8', 'utf8', + tarfile.errors, + ) if keyword in PAX_NAME_FIELDS: - value = self._decode_pax_field(value, encoding, tarfile.encoding, - tarfile.errors) + value = self._decode_pax_field( + value, encoding, tarfile.encoding, + tarfile.errors, + ) else: - value = self._decode_pax_field(value, "utf8", "utf8", - tarfile.errors) + value = self._decode_pax_field( + value, 'utf8', 'utf8', + tarfile.errors, + ) pax_headers[keyword] = value pos += length @@ -1451,18 +1522,18 @@ class TarInfo(object): try: next = self.fromtarfile(tarfile) except HeaderError: - raise SubsequentHeaderError("missing or bad subsequent header") + raise SubsequentHeaderError('missing or bad subsequent header') # Process GNU sparse information. - if "GNU.sparse.map" in pax_headers: + if 'GNU.sparse.map' in pax_headers: # GNU extended sparse format version 0.1. self._proc_gnusparse_01(next, pax_headers) - elif "GNU.sparse.size" in pax_headers: + elif 'GNU.sparse.size' in pax_headers: # GNU extended sparse format version 0.0. self._proc_gnusparse_00(next, pax_headers, buf) - elif pax_headers.get("GNU.sparse.major") == "1" and pax_headers.get("GNU.sparse.minor") == "0": + elif pax_headers.get('GNU.sparse.major') == '1' and pax_headers.get('GNU.sparse.minor') == '0': # GNU extended sparse format version 1.0. self._proc_gnusparse_10(next, pax_headers, tarfile) @@ -1471,7 +1542,7 @@ class TarInfo(object): next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors) next.offset = self.offset - if "size" in pax_headers: + if 'size' in pax_headers: # If the extended header replaces the size field, # we need to recalculate the offset where the next # header starts. @@ -1486,17 +1557,17 @@ class TarInfo(object): """Process a GNU tar extended sparse header, version 0.0. """ offsets = [] - for match in re.finditer(br"\d+ GNU.sparse.offset=(\d+)\n", buf): + for match in re.finditer(br'\d+ GNU.sparse.offset=(\d+)\n', buf): offsets.append(int(match.group(1))) numbytes = [] - for match in re.finditer(br"\d+ GNU.sparse.numbytes=(\d+)\n", buf): + for match in re.finditer(br'\d+ GNU.sparse.numbytes=(\d+)\n', buf): numbytes.append(int(match.group(1))) next.sparse = list(zip(offsets, numbytes)) def _proc_gnusparse_01(self, next, pax_headers): """Process a GNU tar extended sparse header, version 0.1. """ - sparse = [int(x) for x in pax_headers["GNU.sparse.map"].split(",")] + sparse = [int(x) for x in pax_headers['GNU.sparse.map'].split(',')] next.sparse = list(zip(sparse[::2], sparse[1::2])) def _proc_gnusparse_10(self, next, pax_headers, tarfile): @@ -1505,12 +1576,12 @@ class TarInfo(object): fields = None sparse = [] buf = tarfile.fileobj.read(BLOCKSIZE) - fields, buf = buf.split(b"\n", 1) + fields, buf = buf.split(b'\n', 1) fields = int(fields) while len(sparse) < fields * 2: - if b"\n" not in buf: + if b'\n' not in buf: buf += tarfile.fileobj.read(BLOCKSIZE) - number, buf = buf.split(b"\n", 1) + number, buf = buf.split(b'\n', 1) sparse.append(int(number)) next.offset_data = tarfile.fileobj.tell() next.sparse = list(zip(sparse[::2], sparse[1::2])) @@ -1520,20 +1591,20 @@ class TarInfo(object): pax extended or global header. """ for keyword, value in pax_headers.items(): - if keyword == "GNU.sparse.name": - setattr(self, "path", value) - elif keyword == "GNU.sparse.size": - setattr(self, "size", int(value)) - elif keyword == "GNU.sparse.realsize": - setattr(self, "size", int(value)) + if keyword == 'GNU.sparse.name': + setattr(self, 'path', value) + elif keyword == 'GNU.sparse.size': + setattr(self, 'size', int(value)) + elif keyword == 'GNU.sparse.realsize': + setattr(self, 'size', int(value)) elif keyword in PAX_FIELDS: if keyword in PAX_NUMBER_FIELDS: try: value = PAX_NUMBER_FIELDS[keyword](value) except ValueError: value = 0 - if keyword == "path": - value = value.rstrip("/") + if keyword == 'path': + value = value.rstrip('/') setattr(self, keyword, value) self.pax_headers = pax_headers.copy() @@ -1542,7 +1613,7 @@ class TarInfo(object): """Decode a single field from a pax record. """ try: - return value.decode(encoding, "strict") + return value.decode(encoding, 'strict') except UnicodeDecodeError: return value.decode(fallback_encoding, fallback_errors) @@ -1557,41 +1628,51 @@ class TarInfo(object): def isreg(self): return self.type in REGULAR_TYPES + def isfile(self): return self.isreg() + def isdir(self): return self.type == DIRTYPE + def issym(self): return self.type == SYMTYPE + def islnk(self): return self.type == LNKTYPE + def ischr(self): return self.type == CHRTYPE + def isblk(self): return self.type == BLKTYPE + def isfifo(self): return self.type == FIFOTYPE + def issparse(self): return self.sparse is not None + def isdev(self): return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE) # class TarInfo -class TarFile(object): + +class TarFile: """The TarFile Class provides an interface to tar archives. """ debug = 0 # May be set from 0 (no msgs) to 3 (all msgs) dereference = False # If true, add content of linked file to the - # tar file, else the link. + # tar file, else the link. ignore_zeros = False # If true, skips empty or invalid blocks and - # continues processing. + # continues processing. errorlevel = 1 # If 0, fatal errors only appear in debug - # messages (if debug >= 0). If > 0, errors - # are passed to the caller as exceptions. + # messages (if debug >= 0). If > 0, errors + # are passed to the caller as exceptions. format = DEFAULT_FORMAT # The format to use when creating an archive. @@ -1603,9 +1684,11 @@ class TarFile(object): fileobject = ExFileObject # The default ExFileObject class to use. - def __init__(self, name=None, mode="r", fileobj=None, format=None, - tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, - errors="surrogateescape", pax_headers=None, debug=None, errorlevel=None): + def __init__( + self, name=None, mode='r', fileobj=None, format=None, + tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, + errors='surrogateescape', pax_headers=None, debug=None, errorlevel=None, + ): """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to read from an existing archive, 'a' to append data to an existing file or 'w' to create a new file overwriting an existing one. `mode' @@ -1614,22 +1697,22 @@ class TarFile(object): can be determined, `mode' is overridden by `fileobj's mode. `fileobj' is not closed, when TarFile is closed. """ - if len(mode) > 1 or mode not in "raw": + if len(mode) > 1 or mode not in 'raw': raise ValueError("mode must be 'r', 'a' or 'w'") self.mode = mode - self._mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] + self._mode = {'r': 'rb', 'a': 'r+b', 'w': 'wb'}[mode] if not fileobj: - if self.mode == "a" and not os.path.exists(name): + if self.mode == 'a' and not os.path.exists(name): # Create nonexistent files in append mode. - self.mode = "w" - self._mode = "wb" + self.mode = 'w' + self._mode = 'wb' fileobj = bltn_open(name, self._mode) self._extfileobj = False else: - if name is None and hasattr(fileobj, "name"): + if name is None and hasattr(fileobj, 'name'): name = fileobj.name - if hasattr(fileobj, "mode"): + if hasattr(fileobj, 'mode'): self._mode = fileobj.mode self._extfileobj = True self.name = os.path.abspath(name) if name else None @@ -1663,16 +1746,16 @@ class TarFile(object): self.members = [] # list of members as TarInfo objects self._loaded = False # flag if all members have been read self.offset = self.fileobj.tell() - # current position in the archive file + # current position in the archive file self.inodes = {} # dictionary caching the inodes of - # archive members already added + # archive members already added try: - if self.mode == "r": + if self.mode == 'r': self.firstmember = None self.firstmember = self.next() - if self.mode == "a": + if self.mode == 'a': # Move to the end of the archive, # before the first empty block. while True: @@ -1686,7 +1769,7 @@ class TarFile(object): except HeaderError as e: raise ReadError(str(e)) - if self.mode in "aw": + if self.mode in 'aw': self._loaded = True if self.pax_headers: @@ -1711,7 +1794,7 @@ class TarFile(object): # by adding it to the mapping in OPEN_METH. @classmethod - def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs): + def open(cls, name=None, mode='r', fileobj=None, bufsize=RECORDSIZE, **kwargs): """Open a tar archive for reading, writing or appending. Return an appropriate TarFile class. @@ -1735,41 +1818,41 @@ class TarFile(object): """ if not name and not fileobj: - raise ValueError("nothing to open") + raise ValueError('nothing to open') - if mode in ("r", "r:*"): + if mode in ('r', 'r:*'): # Find out which *open() is appropriate for opening the file. for comptype in cls.OPEN_METH: func = getattr(cls, cls.OPEN_METH[comptype]) if fileobj is not None: saved_pos = fileobj.tell() try: - return func(name, "r", fileobj, **kwargs) + return func(name, 'r', fileobj, **kwargs) except (ReadError, CompressionError) as e: if fileobj is not None: fileobj.seek(saved_pos) continue - raise ReadError("file could not be opened successfully") + raise ReadError('file could not be opened successfully') - elif ":" in mode: - filemode, comptype = mode.split(":", 1) - filemode = filemode or "r" - comptype = comptype or "tar" + elif ':' in mode: + filemode, comptype = mode.split(':', 1) + filemode = filemode or 'r' + comptype = comptype or 'tar' # Select the *open() function according to # given compression. if comptype in cls.OPEN_METH: func = getattr(cls, cls.OPEN_METH[comptype]) else: - raise CompressionError("unknown compression type %r" % comptype) + raise CompressionError('unknown compression type %r' % comptype) return func(name, filemode, fileobj, **kwargs) - elif "|" in mode: - filemode, comptype = mode.split("|", 1) - filemode = filemode or "r" - comptype = comptype or "tar" + elif '|' in mode: + filemode, comptype = mode.split('|', 1) + filemode = filemode or 'r' + comptype = comptype or 'tar' - if filemode not in "rw": + if filemode not in 'rw': raise ValueError("mode must be 'r' or 'w'") stream = _Stream(name, filemode, comptype, fileobj, bufsize) @@ -1781,43 +1864,43 @@ class TarFile(object): t._extfileobj = False return t - elif mode in "aw": + elif mode in 'aw': return cls.taropen(name, mode, fileobj, **kwargs) - raise ValueError("undiscernible mode") + raise ValueError('undiscernible mode') @classmethod - def taropen(cls, name, mode="r", fileobj=None, **kwargs): + def taropen(cls, name, mode='r', fileobj=None, **kwargs): """Open uncompressed tar archive name for reading or writing. """ - if len(mode) > 1 or mode not in "raw": + if len(mode) > 1 or mode not in 'raw': raise ValueError("mode must be 'r', 'a' or 'w'") return cls(name, mode, fileobj, **kwargs) @classmethod - def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + def gzopen(cls, name, mode='r', fileobj=None, compresslevel=9, **kwargs): """Open gzip compressed tar archive name for reading or writing. Appending is not allowed. """ - if len(mode) > 1 or mode not in "rw": + if len(mode) > 1 or mode not in 'rw': raise ValueError("mode must be 'r' or 'w'") try: import gzip gzip.GzipFile except (ImportError, AttributeError): - raise CompressionError("gzip module is not available") + raise CompressionError('gzip module is not available') extfileobj = fileobj is not None try: - fileobj = gzip.GzipFile(name, mode + "b", compresslevel, fileobj) + fileobj = gzip.GzipFile(name, mode + 'b', compresslevel, fileobj) t = cls.taropen(name, mode, fileobj, **kwargs) - except IOError: + except OSError: if not extfileobj and fileobj is not None: fileobj.close() if fileobj is None: raise - raise ReadError("not a gzip file") + raise ReadError('not a gzip file') except: if not extfileobj and fileobj is not None: fileobj.close() @@ -1826,17 +1909,17 @@ class TarFile(object): return t @classmethod - def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + def bz2open(cls, name, mode='r', fileobj=None, compresslevel=9, **kwargs): """Open bzip2 compressed tar archive name for reading or writing. Appending is not allowed. """ - if len(mode) > 1 or mode not in "rw": + if len(mode) > 1 or mode not in 'rw': raise ValueError("mode must be 'r' or 'w'.") try: import bz2 except ImportError: - raise CompressionError("bz2 module is not available") + raise CompressionError('bz2 module is not available') if fileobj is not None: fileobj = _BZ2Proxy(fileobj, mode) @@ -1845,17 +1928,17 @@ class TarFile(object): try: t = cls.taropen(name, mode, fileobj, **kwargs) - except (IOError, EOFError): + except (OSError, EOFError): fileobj.close() - raise ReadError("not a bzip2 file") + raise ReadError('not a bzip2 file') t._extfileobj = False return t # All *open() methods are registered here. OPEN_METH = { - "tar": "taropen", # uncompressed tar - "gz": "gzopen", # gzip compressed tar - "bz2": "bz2open" # bzip2 compressed tar + 'tar': 'taropen', # uncompressed tar + 'gz': 'gzopen', # gzip compressed tar + 'bz2': 'bz2open', # bzip2 compressed tar } #-------------------------------------------------------------------------- @@ -1868,7 +1951,7 @@ class TarFile(object): if self.closed: return - if self.mode in "aw": + if self.mode in 'aw': self.fileobj.write(NUL * (BLOCKSIZE * 2)) self.offset += (BLOCKSIZE * 2) # fill up the end with zero-blocks @@ -1889,7 +1972,7 @@ class TarFile(object): """ tarinfo = self._getmember(name) if tarinfo is None: - raise KeyError("filename %r not found" % name) + raise KeyError('filename %r not found' % name) return tarinfo def getmembers(self): @@ -1899,7 +1982,7 @@ class TarFile(object): self._check() if not self._loaded: # if we want to obtain a list of self._load() # all members, we first have to - # scan the whole archive. + # scan the whole archive. return self.members def getnames(self): @@ -1915,7 +1998,7 @@ class TarFile(object): addfile(). If given, `arcname' specifies an alternative name for the file in the archive. """ - self._check("aw") + self._check('aw') # When fileobj is given, replace name by # fileobj's real name. @@ -1928,8 +2011,8 @@ class TarFile(object): if arcname is None: arcname = name drv, arcname = os.path.splitdrive(arcname) - arcname = arcname.replace(os.sep, "/") - arcname = arcname.lstrip("/") + arcname = arcname.replace(os.sep, '/') + arcname = arcname.lstrip('/') # Now, fill the TarInfo object with # information specific for the file. @@ -1939,13 +2022,13 @@ class TarFile(object): # Use os.stat or os.lstat, depending on platform # and if symlinks shall be resolved. if fileobj is None: - if hasattr(os, "lstat") and not self.dereference: + if hasattr(os, 'lstat') and not self.dereference: statres = os.lstat(name) else: statres = os.stat(name) else: statres = os.fstat(fileobj.fileno()) - linkname = "" + linkname = '' stmd = statres.st_mode if stat.S_ISREG(stmd): @@ -2001,7 +2084,7 @@ class TarFile(object): pass if type in (CHRTYPE, BLKTYPE): - if hasattr(os, "major") and hasattr(os, "minor"): + if hasattr(os, 'major') and hasattr(os, 'minor'): tarinfo.devmajor = os.major(statres.st_rdev) tarinfo.devminor = os.minor(statres.st_rdev) return tarinfo @@ -2016,23 +2099,33 @@ class TarFile(object): for tarinfo in self: if verbose: print(filemode(tarinfo.mode), end=' ') - print("%s/%s" % (tarinfo.uname or tarinfo.uid, - tarinfo.gname or tarinfo.gid), end=' ') + print( + '{}/{}'.format( + tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid, + ), end=' ', + ) if tarinfo.ischr() or tarinfo.isblk(): - print("%10s" % ("%d,%d" \ - % (tarinfo.devmajor, tarinfo.devminor)), end=' ') + print( + '%10s' % ( + '%d,%d' + % (tarinfo.devmajor, tarinfo.devminor) + ), end=' ', + ) else: - print("%10d" % tarinfo.size, end=' ') - print("%d-%02d-%02d %02d:%02d:%02d" \ - % time.localtime(tarinfo.mtime)[:6], end=' ') + print('%10d' % tarinfo.size, end=' ') + print( + '%d-%02d-%02d %02d:%02d:%02d' + % time.localtime(tarinfo.mtime)[:6], end=' ', + ) - print(tarinfo.name + ("/" if tarinfo.isdir() else ""), end=' ') + print(tarinfo.name + ('/' if tarinfo.isdir() else ''), end=' ') if verbose: if tarinfo.issym(): - print("->", tarinfo.linkname, end=' ') + print('->', tarinfo.linkname, end=' ') if tarinfo.islnk(): - print("link to", tarinfo.linkname, end=' ') + print('link to', tarinfo.linkname, end=' ') print() def add(self, name, arcname=None, recursive=True, exclude=None, filter=None): @@ -2046,7 +2139,7 @@ class TarFile(object): TarInfo object, if it returns None the TarInfo object will be excluded from the archive. """ - self._check("aw") + self._check('aw') if arcname is None: arcname = name @@ -2054,15 +2147,17 @@ class TarFile(object): # Exclude pathnames. if exclude is not None: import warnings - warnings.warn("use the filter argument instead", - DeprecationWarning, 2) + warnings.warn( + 'use the filter argument instead', + DeprecationWarning, 2, + ) if exclude(name): - self._dbg(2, "tarfile: Excluded %r" % name) + self._dbg(2, 'tarfile: Excluded %r' % name) return # Skip if somebody tries to archive the archive... if self.name is not None and os.path.abspath(name) == self.name: - self._dbg(2, "tarfile: Skipped %r" % name) + self._dbg(2, 'tarfile: Skipped %r' % name) return self._dbg(1, name) @@ -2071,19 +2166,19 @@ class TarFile(object): tarinfo = self.gettarinfo(name, arcname) if tarinfo is None: - self._dbg(1, "tarfile: Unsupported type %r" % name) + self._dbg(1, 'tarfile: Unsupported type %r' % name) return # Change or exclude the TarInfo object. if filter is not None: tarinfo = filter(tarinfo) if tarinfo is None: - self._dbg(2, "tarfile: Excluded %r" % name) + self._dbg(2, 'tarfile: Excluded %r' % name) return # Append the tar header and data to the archive. if tarinfo.isreg(): - f = bltn_open(name, "rb") + f = bltn_open(name, 'rb') self.addfile(tarinfo, f) f.close() @@ -2091,8 +2186,10 @@ class TarFile(object): self.addfile(tarinfo) if recursive: for f in os.listdir(name): - self.add(os.path.join(name, f), os.path.join(arcname, f), - recursive, exclude, filter=filter) + self.add( + os.path.join(name, f), os.path.join(arcname, f), + recursive, exclude, filter=filter, + ) else: self.addfile(tarinfo) @@ -2104,7 +2201,7 @@ class TarFile(object): On Windows platforms, `fileobj' should always be opened with mode 'rb' to avoid irritation about the file size. """ - self._check("aw") + self._check('aw') tarinfo = copy.copy(tarinfo) @@ -2123,7 +2220,7 @@ class TarFile(object): self.members.append(tarinfo) - def extractall(self, path=".", members=None): + def extractall(self, path='.', members=None): """Extract all members from the archive to the current working directory and set owner, modification time and permissions on directories afterwards. `path' specifies a different directory @@ -2159,16 +2256,16 @@ class TarFile(object): if self.errorlevel > 1: raise else: - self._dbg(1, "tarfile: %s" % e) + self._dbg(1, 'tarfile: %s' % e) - def extract(self, member, path="", set_attrs=True): + def extract(self, member, path='', set_attrs=True): """Extract a member from the archive to the current working directory, using its full name. Its file information is extracted as accurately as possible. `member' may be a filename or a TarInfo object. You can specify a different directory using `path'. File attributes (owner, mtime, mode) are set unless `set_attrs' is False. """ - self._check("r") + self._check('r') if isinstance(member, str): tarinfo = self.getmember(member) @@ -2180,21 +2277,23 @@ class TarFile(object): tarinfo._link_target = os.path.join(path, tarinfo.linkname) try: - self._extract_member(tarinfo, os.path.join(path, tarinfo.name), - set_attrs=set_attrs) - except EnvironmentError as e: + self._extract_member( + tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs, + ) + except OSError as e: if self.errorlevel > 0: raise else: if e.filename is None: - self._dbg(1, "tarfile: %s" % e.strerror) + self._dbg(1, 'tarfile: %s' % e.strerror) else: - self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) + self._dbg(1, 'tarfile: {} {!r}'.format(e.strerror, e.filename)) except ExtractError as e: if self.errorlevel > 1: raise else: - self._dbg(1, "tarfile: %s" % e) + self._dbg(1, 'tarfile: %s' % e) def extractfile(self, member): """Extract a member from the archive as a file object. `member' may be @@ -2205,7 +2304,7 @@ class TarFile(object): The file-like object is read-only and provides the following methods: read(), readline(), readlines(), seek() and tell() """ - self._check("r") + self._check('r') if isinstance(member, str): tarinfo = self.getmember(member) @@ -2225,7 +2324,7 @@ class TarFile(object): # A small but ugly workaround for the case that someone tries # to extract a (sym)link as a file-object from a non-seekable # stream of tar blocks. - raise StreamError("cannot extract (sym)link as file object") + raise StreamError('cannot extract (sym)link as file object') else: # A (sym)link's file object is its target's file object. return self.extractfile(self._find_link_target(tarinfo)) @@ -2241,8 +2340,8 @@ class TarFile(object): # Fetch the TarInfo object for the given name # and build the destination pathname, replacing # forward slashes to platform specific separators. - targetpath = targetpath.rstrip("/") - targetpath = targetpath.replace("/", os.sep) + targetpath = targetpath.rstrip('/') + targetpath = targetpath.replace('/', os.sep) # Create all upper directories. upperdirs = os.path.dirname(targetpath) @@ -2252,7 +2351,7 @@ class TarFile(object): os.makedirs(upperdirs) if tarinfo.islnk() or tarinfo.issym(): - self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname)) + self._dbg(1, '{} -> {}'.format(tarinfo.name, tarinfo.linkname)) else: self._dbg(1, tarinfo.name) @@ -2289,7 +2388,7 @@ class TarFile(object): # Use a safe mode for the directory, the real mode is set # later in _extract_member(). os.mkdir(targetpath, 0o700) - except EnvironmentError as e: + except OSError as e: if e.errno != errno.EEXIST: raise @@ -2298,7 +2397,7 @@ class TarFile(object): """ source = self.fileobj source.seek(tarinfo.offset_data) - target = bltn_open(targetpath, "wb") + target = bltn_open(targetpath, 'wb') if tarinfo.sparse is not None: for offset, size in tarinfo.sparse: target.seek(offset) @@ -2314,22 +2413,24 @@ class TarFile(object): at targetpath. """ self.makefile(tarinfo, targetpath) - self._dbg(1, "tarfile: Unknown file type %r, " \ - "extracted as regular file." % tarinfo.type) + self._dbg( + 1, 'tarfile: Unknown file type %r, ' + 'extracted as regular file.' % tarinfo.type, + ) def makefifo(self, tarinfo, targetpath): """Make a fifo called targetpath. """ - if hasattr(os, "mkfifo"): + if hasattr(os, 'mkfifo'): os.mkfifo(targetpath) else: - raise ExtractError("fifo not supported by system") + raise ExtractError('fifo not supported by system') def makedev(self, tarinfo, targetpath): """Make a character or block device called targetpath. """ - if not hasattr(os, "mknod") or not hasattr(os, "makedev"): - raise ExtractError("special devices not supported by system") + if not hasattr(os, 'mknod') or not hasattr(os, 'makedev'): + raise ExtractError('special devices not supported by system') mode = tarinfo.mode if tarinfo.isblk(): @@ -2337,8 +2438,10 @@ class TarFile(object): else: mode |= stat.S_IFCHR - os.mknod(targetpath, mode, - os.makedev(tarinfo.devmajor, tarinfo.devminor)) + os.mknod( + targetpath, mode, + os.makedev(tarinfo.devmajor, tarinfo.devminor), + ) def makelink(self, tarinfo, targetpath): """Make a (symbolic) link called targetpath. If it cannot be created @@ -2354,25 +2457,31 @@ class TarFile(object): if os.path.exists(tarinfo._link_target): os.link(tarinfo._link_target, targetpath) else: - self._extract_member(self._find_link_target(tarinfo), - targetpath) + self._extract_member( + self._find_link_target(tarinfo), + targetpath, + ) except symlink_exception: if tarinfo.issym(): - linkpath = os.path.join(os.path.dirname(tarinfo.name), - tarinfo.linkname) + linkpath = os.path.join( + os.path.dirname(tarinfo.name), + tarinfo.linkname, + ) else: linkpath = tarinfo.linkname else: try: - self._extract_member(self._find_link_target(tarinfo), - targetpath) + self._extract_member( + self._find_link_target(tarinfo), + targetpath, + ) except KeyError: - raise ExtractError("unable to resolve link inside archive") + raise ExtractError('unable to resolve link inside archive') def chown(self, tarinfo, targetpath): """Set owner of targetpath according to tarinfo. """ - if pwd and hasattr(os, "geteuid") and os.geteuid() == 0: + if pwd and hasattr(os, 'geteuid') and os.geteuid() == 0: # We have to be root to do so. try: g = grp.getgrnam(tarinfo.gname)[2] @@ -2383,13 +2492,13 @@ class TarFile(object): except KeyError: u = tarinfo.uid try: - if tarinfo.issym() and hasattr(os, "lchown"): + if tarinfo.issym() and hasattr(os, 'lchown'): os.lchown(targetpath, u, g) else: - if sys.platform != "os2emx": + if sys.platform != 'os2emx': os.chown(targetpath, u, g) - except EnvironmentError as e: - raise ExtractError("could not change owner") + except OSError as e: + raise ExtractError('could not change owner') def chmod(self, tarinfo, targetpath): """Set file permissions of targetpath according to tarinfo. @@ -2397,8 +2506,8 @@ class TarFile(object): if hasattr(os, 'chmod'): try: os.chmod(targetpath, tarinfo.mode) - except EnvironmentError as e: - raise ExtractError("could not change mode") + except OSError as e: + raise ExtractError('could not change mode') def utime(self, tarinfo, targetpath): """Set modification time of targetpath according to tarinfo. @@ -2407,8 +2516,8 @@ class TarFile(object): return try: os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) - except EnvironmentError as e: - raise ExtractError("could not change modification time") + except OSError as e: + raise ExtractError('could not change modification time') #-------------------------------------------------------------------------- def next(self): @@ -2416,7 +2525,7 @@ class TarFile(object): TarFile is opened for reading. Return None if there is no more available. """ - self._check("ra") + self._check('ra') if self.firstmember is not None: m = self.firstmember self.firstmember = None @@ -2430,19 +2539,19 @@ class TarFile(object): tarinfo = self.tarinfo.fromtarfile(self) except EOFHeaderError as e: if self.ignore_zeros: - self._dbg(2, "0x%X: %s" % (self.offset, e)) + self._dbg(2, '0x{:X}: {}'.format(self.offset, e)) self.offset += BLOCKSIZE continue except InvalidHeaderError as e: if self.ignore_zeros: - self._dbg(2, "0x%X: %s" % (self.offset, e)) + self._dbg(2, '0x{:X}: {}'.format(self.offset, e)) self.offset += BLOCKSIZE continue elif self.offset == 0: raise ReadError(str(e)) except EmptyHeaderError: if self.offset == 0: - raise ReadError("empty file") + raise ReadError('empty file') except TruncatedHeaderError as e: if self.offset == 0: raise ReadError(str(e)) @@ -2498,9 +2607,9 @@ class TarFile(object): corresponds to TarFile's mode. """ if self.closed: - raise IOError("%s is closed" % self.__class__.__name__) + raise OSError('%s is closed' % self.__class__.__name__) if mode is not None and self.mode not in mode: - raise IOError("bad operation for mode %r" % self.mode) + raise OSError('bad operation for mode %r' % self.mode) def _find_link_target(self, tarinfo): """Find the target member of a symlink or hardlink member in the @@ -2508,7 +2617,7 @@ class TarFile(object): """ if tarinfo.issym(): # Always search the entire archive. - linkname = os.path.dirname(tarinfo.name) + "/" + tarinfo.linkname + linkname = os.path.dirname(tarinfo.name) + '/' + tarinfo.linkname limit = None else: # Search the archive before the link, because a hard link is @@ -2518,7 +2627,7 @@ class TarFile(object): member = self._getmember(linkname, tarinfo=limit, normalize=True) if member is None: - raise KeyError("linkname %r not found" % linkname) + raise KeyError('linkname %r not found' % linkname) return member def __iter__(self): @@ -2550,7 +2659,8 @@ class TarFile(object): self.closed = True # class TarFile -class TarIter(object): + +class TarIter: """Iterator Class. for tarinfo in TarFile(...): @@ -2562,6 +2672,7 @@ class TarIter(object): """ self.tarfile = tarfile self.index = 0 + def __iter__(self): """Return iterator object. """ @@ -2587,11 +2698,13 @@ class TarIter(object): self.index += 1 return tarinfo - next = __next__ # for Python 2.x + next = __next__ # for Python 2.x #-------------------- # exported functions #-------------------- + + def is_tarfile(name): """Return True if name points to a tar archive that we are able to handle, else return False. @@ -2603,5 +2716,6 @@ def is_tarfile(name): except TarError: return False + bltn_open = open open = TarFile.open diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/compat.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/compat.py index e594106..3bebf2e 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/compat.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/compat.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2013-2017 Vinay Sajip. # Licensed to the Python Software Foundation under a contributor agreement. # See LICENSE.txt and CONTRIBUTORS.txt. # -from __future__ import absolute_import +from __future__ import annotations import os import re @@ -24,8 +23,10 @@ if sys.version_info[0] < 3: # pragma: no cover import ConfigParser as configparser from ._backport import shutil from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit - from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, - pathname2url, ContentTooShortError, splittype) + from urllib import ( + urlretrieve, quote as _quote, unquote, url2pathname, + pathname2url, ContentTooShortError, splittype, + ) def quote(s): if isinstance(s, unicode): @@ -33,10 +34,12 @@ if sys.version_info[0] < 3: # pragma: no cover return _quote(s) import urllib2 - from urllib2 import (Request, urlopen, URLError, HTTPError, - HTTPBasicAuthHandler, HTTPPasswordMgr, - HTTPHandler, HTTPRedirectHandler, - build_opener) + from urllib2 import ( + Request, urlopen, URLError, HTTPError, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener, + ) if ssl: from urllib2 import HTTPSHandler import httplib @@ -51,15 +54,15 @@ if sys.version_info[0] < 3: # pragma: no cover # Leaving this around for now, in case it needs resurrecting in some way # _userprog = None # def splituser(host): - # """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" - # global _userprog - # if _userprog is None: - # import re - # _userprog = re.compile('^(.*)@(.*)$') + # """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" + # global _userprog + # if _userprog is None: + # import re + # _userprog = re.compile('^(.*)@(.*)$') - # match = _userprog.match(host) - # if match: return match.group(1, 2) - # return None, host + # match = _userprog.match(host) + # if match: return match.group(1, 2) + # return None, host else: # pragma: no cover from io import StringIO @@ -69,13 +72,17 @@ else: # pragma: no cover import builtins import configparser import shutil - from urllib.parse import (urlparse, urlunparse, urljoin, quote, - unquote, urlsplit, urlunsplit, splittype) - from urllib.request import (urlopen, urlretrieve, Request, url2pathname, - pathname2url, - HTTPBasicAuthHandler, HTTPPasswordMgr, - HTTPHandler, HTTPRedirectHandler, - build_opener) + from urllib.parse import ( + urlparse, urlunparse, urljoin, quote, + unquote, urlsplit, urlunsplit, splittype, + ) + from urllib.request import ( + urlopen, urlretrieve, Request, url2pathname, + pathname2url, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener, + ) if ssl: from urllib.request import HTTPSHandler from urllib.error import HTTPError, URLError, ContentTooShortError @@ -92,11 +99,10 @@ else: # pragma: no cover try: from ssl import match_hostname, CertificateError -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover class CertificateError(ValueError): pass - def _dnsname_match(dn, hostname, max_wildcards=1): """Matching according to RFC 6125, section 6.4.3 @@ -116,7 +122,8 @@ except ImportError: # pragma: no cover # policy among SSL implementations showed it to be a # reasonable choice. raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn)) + 'too many wildcards in certificate DNS name: ' + repr(dn), + ) # speed up common case w/o wildcards if not wildcards: @@ -146,7 +153,6 @@ except ImportError: # pragma: no cover pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) return pat.match(hostname) - def match_hostname(cert, hostname): """Verify that *cert* (in decoded format as returned by SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 @@ -156,9 +162,11 @@ except ImportError: # pragma: no cover returns nothing. """ if not cert: - raise ValueError("empty or no certificate, match_hostname needs a " - "SSL socket or SSL context with either " - "CERT_OPTIONAL or CERT_REQUIRED") + raise ValueError( + 'empty or no certificate, match_hostname needs a ' + 'SSL socket or SSL context with either ' + 'CERT_OPTIONAL or CERT_REQUIRED', + ) dnsnames = [] san = cert.get('subjectAltName', ()) for key, value in san: @@ -178,25 +186,32 @@ except ImportError: # pragma: no cover return dnsnames.append(value) if len(dnsnames) > 1: - raise CertificateError("hostname %r " + raise CertificateError( + 'hostname %r ' "doesn't match either of %s" - % (hostname, ', '.join(map(repr, dnsnames)))) + % (hostname, ', '.join(map(repr, dnsnames))), + ) elif len(dnsnames) == 1: - raise CertificateError("hostname %r " + raise CertificateError( + 'hostname %r ' "doesn't match %r" - % (hostname, dnsnames[0])) + % (hostname, dnsnames[0]), + ) else: - raise CertificateError("no appropriate commonName or " - "subjectAltName fields were found") + raise CertificateError( + 'no appropriate commonName or ' + 'subjectAltName fields were found', + ) try: from types import SimpleNamespace as Container except ImportError: # pragma: no cover - class Container(object): + class Container: """ A generic container for when multiple values need to be returned """ + def __init__(self, **kwargs): self.__dict__.update(kwargs) @@ -219,8 +234,10 @@ except ImportError: # pragma: no cover # Additionally check that `file` is not a directory, as on Windows # directories pass the os.access check. def _access_check(fn, mode): - return (os.path.exists(fn) and os.access(fn, mode) - and not os.path.isdir(fn)) + return ( + os.path.exists(fn) and os.access(fn, mode) and + not os.path.isdir(fn) + ) # If we're given a path with a directory part, look it up directly rather # than referring to PATH directories. This includes checking relative to the @@ -231,18 +248,18 @@ except ImportError: # pragma: no cover return None if path is None: - path = os.environ.get("PATH", os.defpath) + path = os.environ.get('PATH', os.defpath) if not path: return None path = path.split(os.pathsep) - if sys.platform == "win32": + if sys.platform == 'win32': # The current directory takes precedence on Windows. if not os.curdir in path: path.insert(0, os.curdir) # PATHEXT is necessary to check on Windows. - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + pathext = os.environ.get('PATHEXT', '').split(os.pathsep) # See if the given file matches any of the expected path extensions. # This will allow us to short circuit when given "python.exe". # If it does match, only test that one, otherwise we have to try @@ -302,7 +319,7 @@ else: # pragma: no cover try: from platform import python_implementation -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover def python_implementation(): """Return a string identifying the Python implementation.""" if 'PyPy' in sys.version: @@ -315,7 +332,7 @@ except ImportError: # pragma: no cover try: import sysconfig -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover from ._backport import sysconfig try: @@ -349,8 +366,10 @@ except AttributeError: # pragma: no cover elif isinstance(filename, text_type): return filename.encode(_fsencoding, _fserrors) else: - raise TypeError("expect bytes or str, not %s" % - type(filename).__name__) + raise TypeError( + 'expect bytes or str, not %s' % + type(filename).__name__, + ) def fsdecode(filename): if isinstance(filename, text_type): @@ -358,26 +377,28 @@ except AttributeError: # pragma: no cover elif isinstance(filename, bytes): return filename.decode(_fsencoding, _fserrors) else: - raise TypeError("expect bytes or str, not %s" % - type(filename).__name__) + raise TypeError( + 'expect bytes or str, not %s' % + type(filename).__name__, + ) try: from tokenize import detect_encoding -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover from codecs import BOM_UTF8, lookup import re - cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)") + cookie_re = re.compile(r'coding[:=]\s*([-\w.]+)') def _get_normal_name(orig_enc): """Imitates get_normal_name in tokenizer.c.""" # Only care about the first 12 characters. - enc = orig_enc[:12].lower().replace("_", "-") - if enc == "utf-8" or enc.startswith("utf-8-"): - return "utf-8" - if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ - enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): - return "iso-8859-1" + enc = orig_enc[:12].lower().replace('_', '-') + if enc == 'utf-8' or enc.startswith('utf-8-'): + return 'utf-8' + if enc in ('latin-1', 'iso-8859-1', 'iso-latin-1') or \ + enc.startswith(('latin-1-', 'iso-8859-1-', 'iso-latin-1-')): + return 'iso-8859-1' return orig_enc def detect_encoding(readline): @@ -404,6 +425,7 @@ except ImportError: # pragma: no cover bom_found = False encoding = None default = 'utf-8' + def read_or_stop(): try: return readline() @@ -417,9 +439,9 @@ except ImportError: # pragma: no cover # per default encoding. line_string = line.decode('utf-8') except UnicodeDecodeError: - msg = "invalid or missing encoding declaration" + msg = 'invalid or missing encoding declaration' if filename is not None: - msg = '{} for {!r}'.format(msg, filename) + msg = f'{msg} for {filename!r}' raise SyntaxError(msg) matches = cookie_re.findall(line_string) @@ -431,10 +453,12 @@ except ImportError: # pragma: no cover except LookupError: # This behaviour mimics the Python interpreter if filename is None: - msg = "unknown encoding: " + encoding + msg = 'unknown encoding: ' + encoding else: - msg = "unknown encoding for {!r}: {}".format(filename, - encoding) + msg = 'unknown encoding for {!r}: {}'.format( + filename, + encoding, + ) raise SyntaxError(msg) if bom_found: @@ -443,7 +467,7 @@ except ImportError: # pragma: no cover if filename is None: msg = 'encoding problem: utf-8' else: - msg = 'encoding problem for {!r}: utf-8'.format(filename) + msg = f'encoding problem for {filename!r}: utf-8' raise SyntaxError(msg) encoding += '-sig' return encoding @@ -482,8 +506,8 @@ else: try: from collections import ChainMap -except ImportError: # pragma: no cover - from collections import MutableMapping +except ImportError: # pragma: no cover + from collections.abc import MutableMapping try: from reprlib import recursive_repr as _recursive_repr @@ -566,7 +590,8 @@ except ImportError: # pragma: no cover @_recursive_repr() def __repr__(self): return '{0.__class__.__name__}({1})'.format( - self, ', '.join(map(repr, self.maps))) + self, ', '.join(map(repr, self.maps)), + ) @classmethod def fromkeys(cls, iterable, *args): @@ -595,7 +620,7 @@ except ImportError: # pragma: no cover try: del self.maps[0][key] except KeyError: - raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + raise KeyError(f'Key not found in the first mapping: {key!r}') def popitem(self): 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' @@ -609,7 +634,7 @@ except ImportError: # pragma: no cover try: return self.maps[0].pop(key, *args) except KeyError: - raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + raise KeyError(f'Key not found in the first mapping: {key!r}') def clear(self): 'Clear maps[0], leaving maps[1:] intact.' @@ -633,10 +658,10 @@ except ImportError: # pragma: no cover try: from collections import OrderedDict -except ImportError: # pragma: no cover -## {{{ http://code.activestate.com/recipes/576693/ (r9) -# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. -# Passes Python2.7's test suite and incorporates all the latest updates. +except ImportError: # pragma: no cover + # {{{ http://code.activestate.com/recipes/576693/ (r9) + # Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. + # Passes Python2.7's test suite and incorporates all the latest updates. try: from thread import get_ident as _get_ident except ImportError: @@ -647,7 +672,6 @@ except ImportError: # pragma: no cover except ImportError: pass - class OrderedDict(dict): 'Dictionary that remembers insertion order' # An inherited dict maps keys to values. @@ -784,8 +808,10 @@ except ImportError: # pragma: no cover ''' if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) + raise TypeError( + 'update() takes at most 2 positional ' + 'arguments (%d given)' % (len(args),), + ) elif not args: raise TypeError('update() takes at least 1 argument (0 given)') self = args[0] @@ -831,15 +857,16 @@ except ImportError: # pragma: no cover def __repr__(self, _repr_running=None): 'od.__repr__() <==> repr(od)' - if not _repr_running: _repr_running = {} + if not _repr_running: + _repr_running = {} call_key = id(self), _get_ident() if call_key in _repr_running: return '...' _repr_running[call_key] = 1 try: if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) + return '{}()'.format(self.__class__.__name__) + return '{}({!r})'.format(self.__class__.__name__, self.items()) finally: del _repr_running[call_key] @@ -874,7 +901,7 @@ except ImportError: # pragma: no cover ''' if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() + return len(self) == len(other) and self.items() == other.items() return dict.__eq__(self, other) def __ne__(self, other): @@ -896,17 +923,15 @@ except ImportError: # pragma: no cover try: from logging.config import BaseConfigurator, valid_ident -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) - def valid_ident(s): m = IDENTIFIER.match(s) if not m: raise ValueError('Not a valid Python identifier: %r' % s) return True - # The ConvertingXXX classes are wrappers around standard Python containers, # and they serve to convert any suitable values in the container. The # conversion converts base dicts, lists and tuples to their wrapped @@ -925,8 +950,10 @@ except ImportError: # pragma: no cover #If the converted value is different, save for next time if value is not result: self[key] = result - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): + if type(result) in ( + ConvertingDict, ConvertingList, + ConvertingTuple, + ): result.parent = self result.key = key return result @@ -937,8 +964,10 @@ except ImportError: # pragma: no cover #If the converted value is different, save for next time if value is not result: self[key] = result - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): + if type(result) in ( + ConvertingDict, ConvertingList, + ConvertingTuple, + ): result.parent = self result.key = key return result @@ -947,22 +976,27 @@ except ImportError: # pragma: no cover value = dict.pop(self, key, default) result = self.configurator.convert(value) if value is not result: - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): + if type(result) in ( + ConvertingDict, ConvertingList, + ConvertingTuple, + ): result.parent = self result.key = key return result class ConvertingList(list): """A converting list wrapper.""" + def __getitem__(self, key): value = list.__getitem__(self, key) result = self.configurator.convert(value) #If the converted value is different, save for next time if value is not result: self[key] = result - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): + if type(result) in ( + ConvertingDict, ConvertingList, + ConvertingTuple, + ): result.parent = self result.key = key return result @@ -971,24 +1005,29 @@ except ImportError: # pragma: no cover value = list.pop(self, idx) result = self.configurator.convert(value) if value is not result: - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): + if type(result) in ( + ConvertingDict, ConvertingList, + ConvertingTuple, + ): result.parent = self return result class ConvertingTuple(tuple): """A converting tuple wrapper.""" + def __getitem__(self, key): value = tuple.__getitem__(self, key) result = self.configurator.convert(value) if value is not result: - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): + if type(result) in ( + ConvertingDict, ConvertingList, + ConvertingTuple, + ): result.parent = self result.key = key return result - class BaseConfigurator(object): + class BaseConfigurator: """ The configurator base class which defines some useful defaults. """ @@ -1001,8 +1040,8 @@ except ImportError: # pragma: no cover DIGIT_PATTERN = re.compile(r'^\d+$') value_converters = { - 'ext' : 'ext_convert', - 'cfg' : 'cfg_convert', + 'ext': 'ext_convert', + 'cfg': 'cfg_convert', } # We might want to use a different one, e.g. importlib @@ -1031,7 +1070,7 @@ except ImportError: # pragma: no cover return found except ImportError: e, tb = sys.exc_info()[1:] - v = ValueError('Cannot resolve %r: %s' % (s, e)) + v = ValueError('Cannot resolve {!r}: {}'.format(s, e)) v.__cause__, v.__traceback__ = e, tb raise v @@ -1044,7 +1083,7 @@ except ImportError: # pragma: no cover rest = value m = self.WORD_PATTERN.match(rest) if m is None: - raise ValueError("Unable to convert %r" % value) + raise ValueError('Unable to convert %r' % value) else: rest = rest[m.end():] d = self.config[m.groups()[0]] @@ -1061,15 +1100,17 @@ except ImportError: # pragma: no cover d = d[idx] else: try: - n = int(idx) # try as number first (most likely) + n = int(idx) # try as number first (most likely) d = d[n] except TypeError: d = d[idx] if m: rest = rest[m.end():] else: - raise ValueError('Unable to convert ' - '%r at %r' % (value, rest)) + raise ValueError( + 'Unable to convert ' + '%r at %r' % (value, rest), + ) #rest should be empty return d @@ -1086,7 +1127,7 @@ except ImportError: # pragma: no cover value = ConvertingList(value) value.configurator = self elif not isinstance(value, ConvertingTuple) and\ - isinstance(value, tuple): + isinstance(value, tuple): value = ConvertingTuple(value) value.configurator = self elif isinstance(value, string_types): @@ -1108,7 +1149,7 @@ except ImportError: # pragma: no cover c = self.resolve(c) props = config.pop('.', None) # Check for valid identifiers - kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) + kwargs = {k: config[k] for k in config if valid_ident(k)} result = c(**kwargs) if props: for name, value in props.items(): diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/database.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/database.py index 0a90c30..43a6b9e 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/database.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/database.py @@ -1,11 +1,9 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012-2017 The Python Software Foundation. # See LICENSE.txt and CONTRIBUTORS.txt. # """PEP 376 implementation.""" - -from __future__ import unicode_literals +from __future__ import annotations import base64 import codecs @@ -17,18 +15,29 @@ import posixpath import sys import zipimport -from . import DistlibException, resources +from . import DistlibException +from . import resources from .compat import StringIO -from .version import get_scheme, UnsupportedVersionError -from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, - LEGACY_METADATA_FILENAME) -from .util import (parse_requirement, cached_property, parse_name_and_version, - read_exports, write_exports, CSVReader, CSVWriter) +from .metadata import LEGACY_METADATA_FILENAME +from .metadata import Metadata +from .metadata import METADATA_FILENAME +from .metadata import WHEEL_METADATA_FILENAME +from .util import cached_property +from .util import CSVReader +from .util import CSVWriter +from .util import parse_name_and_version +from .util import parse_requirement +from .util import read_exports +from .util import write_exports +from .version import get_scheme +from .version import UnsupportedVersionError -__all__ = ['Distribution', 'BaseInstalledDistribution', - 'InstalledDistribution', 'EggInfoDistribution', - 'DistributionPath'] +__all__ = [ + 'Distribution', 'BaseInstalledDistribution', + 'InstalledDistribution', 'EggInfoDistribution', + 'DistributionPath', +] logger = logging.getLogger(__name__) @@ -36,16 +45,19 @@ logger = logging.getLogger(__name__) EXPORTS_FILENAME = 'pydist-exports.json' COMMANDS_FILENAME = 'pydist-commands.json' -DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED', - 'RESOURCES', EXPORTS_FILENAME, 'SHARED') +DIST_FILES = ( + 'INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED', + 'RESOURCES', EXPORTS_FILENAME, 'SHARED', +) DISTINFO_EXT = '.dist-info' -class _Cache(object): +class _Cache: """ A simple cache mapping names and .dist-info paths to distributions """ + def __init__(self): """ Initialise an instance. There is normally one for each DistributionPath. @@ -72,10 +84,11 @@ class _Cache(object): self.name.setdefault(dist.key, []).append(dist) -class DistributionPath(object): +class DistributionPath: """ Represents a set of distributions installed on a path (typically sys.path). """ + def __init__(self, path=None, include_egg=False): """ Create an instance from a path, optionally including legacy (distutils/ @@ -111,7 +124,6 @@ class DistributionPath(object): self._cache.clear() self._cache_egg.clear() - def _yield_distributions(self): """ Yield .dist-info and/or .egg(-info) distributions. @@ -133,9 +145,11 @@ class DistributionPath(object): if not r or r.path in seen: continue if self._include_dist and entry.endswith(DISTINFO_EXT): - possible_filenames = [METADATA_FILENAME, - WHEEL_METADATA_FILENAME, - LEGACY_METADATA_FILENAME] + possible_filenames = [ + METADATA_FILENAME, + WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME, + ] for metadata_filename in possible_filenames: metadata_path = posixpath.join(entry, metadata_filename) pydist = finder.find(metadata_path) @@ -148,10 +162,14 @@ class DistributionPath(object): metadata = Metadata(fileobj=stream, scheme='legacy') logger.debug('Found %s', r.path) seen.add(r.path) - yield new_dist_class(r.path, metadata=metadata, - env=self) - elif self._include_egg and entry.endswith(('.egg-info', - '.egg')): + yield new_dist_class( + r.path, metadata=metadata, + env=self, + ) + elif self._include_egg and entry.endswith(( + '.egg-info', + '.egg', + )): logger.debug('Found %s', r.path) seen.add(r.path) yield old_dist_class(r.path, self) @@ -207,17 +225,14 @@ class DistributionPath(object): :class:`EggInfoDistribution` instances """ if not self._cache_enabled: - for dist in self._yield_distributions(): - yield dist + yield from self._yield_distributions() else: self._generate_cache() - for dist in self._cache.path.values(): - yield dist + yield from self._cache.path.values() if self._include_egg: - for dist in self._cache_egg.path.values(): - yield dist + yield from self._cache_egg.path.values() def get_distribution(self, name): """ @@ -262,10 +277,12 @@ class DistributionPath(object): matcher = None if version is not None: try: - matcher = self._scheme.matcher('%s (%s)' % (name, version)) + matcher = self._scheme.matcher('{} ({})'.format(name, version)) except ValueError: - raise DistlibException('invalid name or version: %r, %r' % - (name, version)) + raise DistlibException( + 'invalid name or version: %r, %r' % + (name, version), + ) for dist in self.get_distributions(): # We hit a problem on Travis where enum34 was installed and doesn't @@ -310,11 +327,10 @@ class DistributionPath(object): if name in d: yield d[name] else: - for v in d.values(): - yield v + yield from d.values() -class Distribution(object): +class Distribution: """ A base class for distributions, whether installed or from indexes. Either way, it must have some metadata, so that's all that's needed @@ -363,7 +379,7 @@ class Distribution(object): """ A utility property which displays the name and version in parentheses. """ - return '%s (%s)' % (self.name, self.version) + return '{} ({})'.format(self.name, self.version) @property def provides(self): @@ -372,7 +388,7 @@ class Distribution(object): :return: A set of "name (version)" strings. """ plist = self.metadata.provides - s = '%s (%s)' % (self.name, self.version) + s = '{} ({})'.format(self.name, self.version) if s not in plist: plist.append(s) return plist @@ -381,8 +397,12 @@ class Distribution(object): md = self.metadata logger.debug('Getting requirements from metadata %r', md.todict()) reqts = getattr(md, req_attr) - return set(md.get_requirements(reqts, extras=self.extras, - env=self.context)) + return set( + md.get_requirements( + reqts, extras=self.extras, + env=self.context, + ), + ) @property def run_requires(self): @@ -419,8 +439,10 @@ class Distribution(object): matcher = scheme.matcher(r.requirement) except UnsupportedVersionError: # XXX compat-mode if cannot read the version - logger.warning('could not read version %r - using name only', - req) + logger.warning( + 'could not read version %r - using name only', + req, + ) name = req.split()[0] matcher = scheme.matcher(name) @@ -446,7 +468,7 @@ class Distribution(object): suffix = ' [%s]' % self.source_url else: suffix = '' - return '' % (self.name, self.version, suffix) + return ''.format(self.name, self.version, suffix) def __eq__(self, other): """ @@ -459,9 +481,11 @@ class Distribution(object): if type(other) is not type(self): result = False else: - result = (self.name == other.name and - self.version == other.version and - self.source_url == other.source_url) + result = ( + self.name == other.name and + self.version == other.version and + self.source_url == other.source_url + ) return result def __hash__(self): @@ -490,7 +514,7 @@ class BaseInstalledDistribution(Distribution): :param env: This is normally the :class:`DistributionPath` instance where this distribution was found. """ - super(BaseInstalledDistribution, self).__init__(metadata) + super().__init__(metadata) self.path = path self.dist_path = env @@ -523,7 +547,7 @@ class BaseInstalledDistribution(Distribution): prefix = '%s=' % self.hasher digest = hasher(data).digest() digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii') - return '%s%s' % (prefix, digest) + return '{}{}'.format(prefix, digest) class InstalledDistribution(BaseInstalledDistribution): @@ -552,30 +576,35 @@ class InstalledDistribution(BaseInstalledDistribution): if r is None: r = finder.find(LEGACY_METADATA_FILENAME) if r is None: - raise ValueError('no %s found in %s' % (METADATA_FILENAME, - path)) + raise ValueError( + 'no {} found in {}'.format( + METADATA_FILENAME, + path, + ), + ) with contextlib.closing(r.as_stream()) as stream: metadata = Metadata(fileobj=stream, scheme='legacy') - super(InstalledDistribution, self).__init__(metadata, path, env) + super().__init__(metadata, path, env) if env and env._cache_enabled: env._cache.add(self) r = finder.find('REQUESTED') self.requested = r is not None - p = os.path.join(path, 'top_level.txt') + p = os.path.join(path, 'top_level.txt') if os.path.exists(p): with open(p, 'rb') as f: data = f.read().decode('utf-8') self.modules = data.splitlines() def __repr__(self): - return '' % ( - self.name, self.version, self.path) + return ''.format( + self.name, self.version, self.path, + ) def __str__(self): - return "%s %s" % (self.name, self.version) + return '{} {}'.format(self.name, self.version) def _get_records(self): """ @@ -657,8 +686,10 @@ class InstalledDistribution(BaseInstalledDistribution): for relative, destination in resources_reader: if relative == relative_path: return destination - raise KeyError('no resource file with relative path %r ' - 'is installed' % relative_path) + raise KeyError( + 'no resource file with relative path %r ' + 'is installed' % relative_path, + ) def list_installed_files(self): """ @@ -667,8 +698,7 @@ class InstalledDistribution(BaseInstalledDistribution): :returns: iterator of (path, hash, size) """ - for result in self._get_records(): - yield result + yield from self._get_records() def write_installed_files(self, paths, prefix, dry_run=False): """ @@ -694,8 +724,10 @@ class InstalledDistribution(BaseInstalledDistribution): size = '%d' % os.path.getsize(path) with open(path, 'rb') as fp: hash_value = self.get_hash(fp.read()) - if path.startswith(base) or (base_under_prefix and - path.startswith(prefix)): + if path.startswith(base) or ( + base_under_prefix and + path.startswith(prefix) + ): path = os.path.relpath(path, base) writer.writerow((path, hash_value, size)) @@ -784,7 +816,7 @@ class InstalledDistribution(BaseInstalledDistribution): for key in ('prefix', 'lib', 'headers', 'scripts', 'data'): path = paths[key] if os.path.isdir(paths[key]): - lines.append('%s=%s' % (key, path)) + lines.append('{}={}'.format(key, path)) for ns in paths.get('namespace', ()): lines.append('namespace=%s' % ns) @@ -794,8 +826,10 @@ class InstalledDistribution(BaseInstalledDistribution): def get_distinfo_resource(self, path): if path not in DIST_FILES: - raise DistlibException('invalid path for a dist-info file: ' - '%r at %r' % (path, self.path)) + raise DistlibException( + 'invalid path for a dist-info file: ' + '%r at %r' % (path, self.path), + ) finder = resources.finder_for_path(self.path) if finder is None: raise DistlibException('Unable to get a finder for %s' % self.path) @@ -821,12 +855,15 @@ class InstalledDistribution(BaseInstalledDistribution): if distinfo_dirname != self.path.split(os.sep)[-1]: raise DistlibException( 'dist-info file %r does not belong to the %r %s ' - 'distribution' % (path, self.name, self.version)) + 'distribution' % (path, self.name, self.version), + ) # The file must be relative if path not in DIST_FILES: - raise DistlibException('invalid path for a dist-info file: ' - '%r at %r' % (path, self.path)) + raise DistlibException( + 'invalid path for a dist-info file: ' + '%r at %r' % (path, self.path), + ) return os.path.join(self.path, path) @@ -847,8 +884,10 @@ class InstalledDistribution(BaseInstalledDistribution): yield path def __eq__(self, other): - return (isinstance(other, InstalledDistribution) and - self.path == other.path) + return ( + isinstance(other, InstalledDistribution) and + self.path == other.path + ) # See http://docs.python.org/reference/datamodel#object.__hash__ __hash__ = object.__hash__ @@ -882,7 +921,7 @@ class EggInfoDistribution(BaseInstalledDistribution): if env and env._cache_enabled: env._cache_egg.add(self) - super(EggInfoDistribution, self).__init__(metadata, path, env) + super().__init__(metadata, path, env) def _get_metadata(self, path): requires = None @@ -897,21 +936,25 @@ class EggInfoDistribution(BaseInstalledDistribution): for line in lines: line = line.strip() if line.startswith('['): - logger.warning('Unexpected line: quitting requirement scan: %r', - line) + logger.warning( + 'Unexpected line: quitting requirement scan: %r', + line, + ) break r = parse_requirement(line) if not r: logger.warning('Not recognised as a requirement: %r', line) continue if r.extras: - logger.warning('extra requirements in requires.txt are ' - 'not supported') + logger.warning( + 'extra requirements in requires.txt are ' + 'not supported', + ) if not r.constraints: reqs.append(r.name) else: cons = ', '.join('%s%s' % c for c in r.constraints) - reqs.append('%s (%s)' % (r.name, cons)) + reqs.append('{} ({})'.format(r.name, cons)) return reqs def parse_requires_path(req_path): @@ -924,7 +967,7 @@ class EggInfoDistribution(BaseInstalledDistribution): try: with codecs.open(req_path, 'r', 'utf-8') as fp: reqs = parse_requires_data(fp.read()) - except IOError: + except OSError: pass return reqs @@ -941,13 +984,14 @@ class EggInfoDistribution(BaseInstalledDistribution): # FIXME handle the case where zipfile is not available zipf = zipimport.zipimporter(path) fileobj = StringIO( - zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8')) + zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8'), + ) metadata = Metadata(fileobj=fileobj, scheme='legacy') try: data = zipf.get_data('EGG-INFO/requires.txt') tl_data = zipf.get_data('EGG-INFO/top_level.txt').decode('utf-8') requires = parse_requires_data(data.decode('utf-8')) - except IOError: + except OSError: requires = None elif path.endswith('.egg-info'): if os.path.isdir(path): @@ -957,8 +1001,10 @@ class EggInfoDistribution(BaseInstalledDistribution): tl_path = os.path.join(path, 'top_level.txt') metadata = Metadata(path=path, scheme='legacy') else: - raise DistlibException('path must end with .egg-info or .egg, ' - 'got %r' % path) + raise DistlibException( + 'path must end with .egg-info or .egg, ' + 'got %r' % path, + ) if requires: metadata.add_requirements(requires) @@ -975,11 +1021,12 @@ class EggInfoDistribution(BaseInstalledDistribution): return metadata def __repr__(self): - return '' % ( - self.name, self.version, self.path) + return ''.format( + self.name, self.version, self.path, + ) def __str__(self): - return "%s %s" % (self.name, self.version) + return '{} {}'.format(self.name, self.version) def check_installed_files(self): """ @@ -1068,17 +1115,20 @@ class EggInfoDistribution(BaseInstalledDistribution): yield line def __eq__(self, other): - return (isinstance(other, EggInfoDistribution) and - self.path == other.path) + return ( + isinstance(other, EggInfoDistribution) and + self.path == other.path + ) # See http://docs.python.org/reference/datamodel#object.__hash__ __hash__ = object.__hash__ + new_dist_class = InstalledDistribution old_dist_class = EggInfoDistribution -class DependencyGraph(object): +class DependencyGraph: """ Represents a dependency graph between distributions. @@ -1136,7 +1186,7 @@ class DependencyGraph(object): self.missing.setdefault(distribution, []).append(requirement) def _repr_dist(self, dist): - return '%s %s' % (dist.name, dist.version) + return '{} {}'.format(dist.name, dist.version) def repr_node(self, dist, level=1): """Prints only a subgraph""" @@ -1144,7 +1194,7 @@ class DependencyGraph(object): for other, label in self.adjacency_list[dist]: dist = self._repr_dist(other) if label is not None: - dist = '%s [%s]' % (dist, label) + dist = '{} [{}]'.format(dist, label) output.append(' ' * level + str(dist)) suboutput = self.repr_node(other, level + 1) subs = suboutput.split('\n') @@ -1162,16 +1212,18 @@ class DependencyGraph(object): """ disconnected = [] - f.write("digraph dependencies {\n") + f.write('digraph dependencies {\n') for dist, adjs in self.adjacency_list.items(): if len(adjs) == 0 and not skip_disconnected: disconnected.append(dist) for other, label in adjs: if not label is None: - f.write('"%s" -> "%s" [label="%s"]\n' % - (dist.name, other.name, label)) + f.write( + '"%s" -> "%s" [label="%s"]\n' % + (dist.name, other.name, label), + ) else: - f.write('"%s" -> "%s"\n' % (dist.name, other.name)) + f.write('"{}" -> "{}"\n'.format(dist.name, other.name)) if not skip_disconnected and len(disconnected) > 0: f.write('subgraph disconnected {\n') f.write('label = "Disconnected"\n') @@ -1209,8 +1261,10 @@ class DependencyGraph(object): # Remove from the adjacency list of others for k, v in alist.items(): alist[k] = [(d, r) for d, r in v if d not in to_remove] - logger.debug('Moving to result: %s', - ['%s (%s)' % (d.name, d.version) for d in to_remove]) + logger.debug( + 'Moving to result: %s', + ['{} ({})'.format(d.name, d.version) for d in to_remove], + ) result.extend(to_remove) return result, list(alist.keys()) @@ -1245,15 +1299,19 @@ def make_graph(dists, scheme='default'): # now make the edges for dist in dists: - requires = (dist.run_requires | dist.meta_requires | - dist.build_requires | dist.dev_requires) + requires = ( + dist.run_requires | dist.meta_requires | + dist.build_requires | dist.dev_requires + ) for req in requires: try: matcher = scheme.matcher(req) except UnsupportedVersionError: # XXX compat-mode if cannot read the version - logger.warning('could not read version %r - using name only', - req) + logger.warning( + 'could not read version %r - using name only', + req, + ) name = req.split()[0] matcher = scheme.matcher(name) @@ -1284,8 +1342,10 @@ def get_dependent_dists(dists, dist): :param dist: a distribution, member of *dists* for which we are interested """ if dist not in dists: - raise DistlibException('given distribution %r is not a member ' - 'of the list' % dist.name) + raise DistlibException( + 'given distribution %r is not a member ' + 'of the list' % dist.name, + ) graph = make_graph(dists) dep = [dist] # dependent distributions @@ -1310,8 +1370,10 @@ def get_required_dists(dists, dist): :param dist: a distribution, member of *dists* for which we are interested """ if dist not in dists: - raise DistlibException('given distribution %r is not a member ' - 'of the list' % dist.name) + raise DistlibException( + 'given distribution %r is not a member ' + 'of the list' % dist.name, + ) graph = make_graph(dists) req = [] # required distributions diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/index.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/index.py index b1fbbf8..2f7089d 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/index.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/index.py @@ -1,9 +1,10 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2013 Vinay Sajip. # Licensed to the Python Software Foundation under a contributor agreement. # See LICENSE.txt and CONTRIBUTORS.txt. # +from __future__ import annotations + import hashlib import logging import os @@ -16,8 +17,10 @@ except ImportError: from dummy_threading import Thread from . import DistlibException -from .compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr, - urlparse, build_opener, string_types) +from .compat import ( + HTTPBasicAuthHandler, Request, HTTPPasswordMgr, + urlparse, build_opener, string_types, +) from .util import zip_dir, ServerProxy logger = logging.getLogger(__name__) @@ -25,7 +28,8 @@ logger = logging.getLogger(__name__) DEFAULT_INDEX = 'https://pypi.org/pypi' DEFAULT_REALM = 'pypi' -class PackageIndex(object): + +class PackageIndex: """ This class represents a package index compatible with PyPI, the Python Package Index. @@ -54,8 +58,10 @@ class PackageIndex(object): # prompting for passwords for s in ('gpg', 'gpg2'): try: - rc = subprocess.check_call([s, '--version'], stdout=sink, - stderr=sink) + rc = subprocess.check_call( + [s, '--version'], stdout=sink, + stderr=sink, + ) if rc == 0: self.gpg = s break @@ -139,11 +145,13 @@ class PackageIndex(object): break s = s.decode('utf-8').rstrip() outbuf.append(s) - logger.debug('%s: %s' % (name, s)) + logger.debug('{}: {}'.format(name, s)) stream.close() - def get_sign_command(self, filename, signer, sign_password, - keystore=None): + def get_sign_command( + self, filename, signer, sign_password, + keystore=None, + ): """ Return a suitable command for signing a file. @@ -166,8 +174,10 @@ class PackageIndex(object): cmd.extend(['--batch', '--passphrase-fd', '0']) td = tempfile.mkdtemp() sf = os.path.join(td, os.path.basename(filename) + '.asc') - cmd.extend(['--detach-sign', '--armor', '--local-user', - signer, '--output', sf, filename]) + cmd.extend([ + '--detach-sign', '--armor', '--local-user', + signer, '--output', sf, filename, + ]) logger.debug('invoking: %s', ' '.join(cmd)) return cmd, sf @@ -220,17 +230,25 @@ class PackageIndex(object): :return: The absolute pathname of the file where the signature is stored. """ - cmd, sig_file = self.get_sign_command(filename, signer, sign_password, - keystore) - rc, stdout, stderr = self.run_command(cmd, - sign_password.encode('utf-8')) + cmd, sig_file = self.get_sign_command( + filename, signer, sign_password, + keystore, + ) + rc, stdout, stderr = self.run_command( + cmd, + sign_password.encode('utf-8'), + ) if rc != 0: - raise DistlibException('sign command failed with error ' - 'code %s' % rc) + raise DistlibException( + 'sign command failed with error ' + 'code %s' % rc, + ) return sig_file - def upload_file(self, metadata, filename, signer=None, sign_password=None, - filetype='sdist', pyversion='source', keystore=None): + def upload_file( + self, metadata, filename, signer=None, sign_password=None, + filetype='sdist', pyversion='source', keystore=None, + ): """ Upload a release file to the index. @@ -262,8 +280,10 @@ class PackageIndex(object): if not self.gpg: logger.warning('no signing program available - not signed') else: - sig_file = self.sign_file(filename, signer, sign_password, - keystore) + sig_file = self.sign_file( + filename, signer, sign_password, + keystore, + ) with open(filename, 'rb') as f: file_data = f.read() md5_digest = hashlib.md5(file_data).hexdigest() @@ -280,8 +300,10 @@ class PackageIndex(object): if sig_file: with open(sig_file, 'rb') as f: sig_data = f.read() - files.append(('gpg_signature', os.path.basename(sig_file), - sig_data)) + files.append(( + 'gpg_signature', os.path.basename(sig_file), + sig_data, + )) shutil.rmtree(os.path.dirname(sig_file)) request = self.encode_request(d.items(), files) return self.send_request(request) @@ -308,14 +330,18 @@ class PackageIndex(object): metadata.validate() name, version = metadata.name, metadata.version zip_data = zip_dir(doc_dir).getvalue() - fields = [(':action', 'doc_upload'), - ('name', name), ('version', version)] + fields = [ + (':action', 'doc_upload'), + ('name', name), ('version', version), + ] files = [('content', name, zip_data)] request = self.encode_request(fields, files) return self.send_request(request) - def get_verify_command(self, signature_filename, data_filename, - keystore=None): + def get_verify_command( + self, signature_filename, data_filename, + keystore=None, + ): """ Return a suitable command for verifying a file. @@ -338,8 +364,10 @@ class PackageIndex(object): logger.debug('invoking: %s', ' '.join(cmd)) return cmd - def verify_signature(self, signature_filename, data_filename, - keystore=None): + def verify_signature( + self, signature_filename, data_filename, + keystore=None, + ): """ Verify a signature for a file. @@ -353,14 +381,20 @@ class PackageIndex(object): :return: True if the signature was verified, else False. """ if not self.gpg: - raise DistlibException('verification unavailable because gpg ' - 'unavailable') - cmd = self.get_verify_command(signature_filename, data_filename, - keystore) + raise DistlibException( + 'verification unavailable because gpg ' + 'unavailable', + ) + cmd = self.get_verify_command( + signature_filename, data_filename, + keystore, + ) rc, stdout, stderr = self.run_command(cmd) if rc not in (0, 1): - raise DistlibException('verify command failed with error ' - 'code %s' % rc) + raise DistlibException( + 'verify command failed with error ' + 'code %s' % rc, + ) return rc == 0 def download_file(self, url, destfile, digest=None, reporthook=None): @@ -408,8 +442,8 @@ class PackageIndex(object): size = -1 read = 0 blocknum = 0 - if "content-length" in headers: - size = int(headers["Content-Length"]) + if 'content-length' in headers: + size = int(headers['Content-Length']) if reporthook: reporthook(blocknum, blocksize, size) while True: @@ -430,14 +464,19 @@ class PackageIndex(object): if size >= 0 and read < size: raise DistlibException( 'retrieval incomplete: got only %d out of %d bytes' - % (read, size)) + % (read, size), + ) # if we have a digest, it must match. if digester: actual = digester.hexdigest() if digest != actual: - raise DistlibException('%s digest mismatch for %s: expected ' - '%s, got %s' % (hasher, destfile, - digest, actual)) + raise DistlibException( + '%s digest mismatch for %s: expected ' + '%s, got %s' % ( + hasher, destfile, + digest, actual, + ), + ) logger.debug('Digest verified: %s', digest) def send_request(self, req): @@ -477,17 +516,23 @@ class PackageIndex(object): for v in values: parts.extend(( b'--' + boundary, - ('Content-Disposition: form-data; name="%s"' % - k).encode('utf-8'), + ( + 'Content-Disposition: form-data; name="%s"' % + k + ).encode('utf-8'), b'', - v.encode('utf-8'))) + v.encode('utf-8'), + )) for key, filename, value in files: parts.extend(( b'--' + boundary, - ('Content-Disposition: form-data; name="%s"; filename="%s"' % - (key, filename)).encode('utf-8'), + ( + 'Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename) + ).encode('utf-8'), b'', - value)) + value, + )) parts.extend((b'--' + boundary + b'--', b'')) @@ -495,7 +540,7 @@ class PackageIndex(object): ct = b'multipart/form-data; boundary=' + boundary headers = { 'Content-type': ct, - 'Content-length': str(len(body)) + 'Content-length': str(len(body)), } return Request(self.url, body, headers) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/locators.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/locators.py index 0c7d639..9843e1a 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/locators.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/locators.py @@ -1,17 +1,17 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012-2015 Vinay Sajip. # Licensed to the Python Software Foundation under a contributor agreement. # See LICENSE.txt and CONTRIBUTORS.txt. # +from __future__ import annotations import gzip -from io import BytesIO import json import logging import os import posixpath import re +from io import BytesIO try: import threading except ImportError: # pragma: no cover @@ -19,15 +19,19 @@ except ImportError: # pragma: no cover import zlib from . import DistlibException -from .compat import (urljoin, urlparse, urlunparse, url2pathname, pathname2url, - queue, quote, unescape, build_opener, - HTTPRedirectHandler as BaseRedirectHandler, text_type, - Request, HTTPError, URLError) +from .compat import ( + urljoin, urlparse, urlunparse, url2pathname, pathname2url, + queue, quote, unescape, build_opener, + HTTPRedirectHandler as BaseRedirectHandler, text_type, + Request, HTTPError, URLError, +) from .database import Distribution, DistributionPath, make_dist from .metadata import Metadata, MetadataInvalidError -from .util import (cached_property, ensure_slash, split_filename, get_project_data, - parse_requirement, parse_name_and_version, ServerProxy, - normalize_name) +from .util import ( + cached_property, ensure_slash, split_filename, get_project_data, + parse_requirement, parse_name_and_version, ServerProxy, + normalize_name, +) from .version import get_scheme, UnsupportedVersionError from .wheel import Wheel, is_compatible @@ -38,6 +42,7 @@ CHARSET = re.compile(r';\s*charset\s*=\s*(.*)\s*$', re.I) HTML_CONTENT_TYPE = re.compile('text/html|application/x(ht)?ml') DEFAULT_INDEX = 'https://pypi.org/pypi' + def get_all_distribution_names(url=None): """ Return all distribution names known by an index. @@ -52,6 +57,7 @@ def get_all_distribution_names(url=None): finally: client('close')() + class RedirectHandler(BaseRedirectHandler): """ A class to work around a bug in some Python 3.2.x releases. @@ -61,6 +67,7 @@ class RedirectHandler(BaseRedirectHandler): # returns e.g. /abc, it bails because it says the scheme '' # is bogus, when actually it should use the request's # URL for the scheme. See Python issue #13696. + def http_error_302(self, req, fp, code, msg, headers): # Some servers (incorrectly) return multiple Location headers # (so probably same goes for URI). Use first header. @@ -78,12 +85,15 @@ class RedirectHandler(BaseRedirectHandler): headers.replace_header(key, newurl) else: headers[key] = newurl - return BaseRedirectHandler.http_error_302(self, req, fp, code, msg, - headers) + return BaseRedirectHandler.http_error_302( + self, req, fp, code, msg, + headers, + ) http_error_301 = http_error_303 = http_error_307 = http_error_302 -class Locator(object): + +class Locator: """ A base class for locators - things that locate distributions. """ @@ -197,8 +207,10 @@ class Locator(object): is_downloadable = basename.endswith(self.downloadable_extensions) if is_wheel: compatible = is_compatible(Wheel(basename), self.wheel_tags) - return (t.scheme == 'https', 'pypi.org' in t.netloc, - is_downloadable, is_wheel, compatible, basename) + return ( + t.scheme == 'https', 'pypi.org' in t.netloc, + is_downloadable, is_wheel, compatible, basename, + ) def prefer_url(self, url1, url2): """ @@ -242,8 +254,10 @@ class Locator(object): result = None scheme, netloc, path, params, query, frag = urlparse(url) if frag.lower().startswith('egg='): # pragma: no cover - logger.debug('%s: version hint in fragment: %r', - project_name, frag) + logger.debug( + '%s: version hint in fragment: %r', + project_name, frag, + ) m = HASHER_HASH.match(frag) if m: algo, digest = m.groups() @@ -267,10 +281,13 @@ class Locator(object): 'name': wheel.name, 'version': wheel.version, 'filename': wheel.filename, - 'url': urlunparse((scheme, netloc, origpath, - params, query, '')), + 'url': urlunparse(( + scheme, netloc, origpath, + params, query, '', + )), 'python-version': ', '.join( - ['.'.join(list(v[2:])) for v in wheel.pyver]), + ['.'.join(list(v[2:])) for v in wheel.pyver], + ), } except Exception as e: # pragma: no cover logger.warning('invalid path for wheel: %s', path) @@ -291,8 +308,10 @@ class Locator(object): 'name': name, 'version': version, 'filename': filename, - 'url': urlunparse((scheme, netloc, origpath, - params, query, '')), + 'url': urlunparse(( + scheme, netloc, origpath, + params, query, '', + )), #'packagetype': 'sdist', } if pyver: # pragma: no cover @@ -384,10 +403,10 @@ class Locator(object): slist.append(k) # else: # logger.debug('skipping pre-release ' - # 'version %s of %s', k, matcher.name) + # 'version %s of %s', k, matcher.name) except Exception: # pragma: no cover logger.warning('error matching %s with %r', matcher, k) - pass # slist.append(k) + pass # slist.append(k) if len(slist) > 1: slist = sorted(slist, key=scheme.key) if slist: @@ -413,6 +432,7 @@ class PyPIRPCLocator(Locator): This locator uses XML-RPC to locate distributions. It therefore cannot be used with simple mirrors (that only mirror file content). """ + def __init__(self, url, **kwargs): """ Initialise an instance. @@ -420,7 +440,7 @@ class PyPIRPCLocator(Locator): :param url: The URL to use for XML-RPC. :param kwargs: Passed to the superclass constructor. """ - super(PyPIRPCLocator, self).__init__(**kwargs) + super().__init__(**kwargs) self.base_url = url self.client = ServerProxy(url, timeout=3.0) @@ -456,13 +476,15 @@ class PyPIRPCLocator(Locator): result['digests'][url] = digest return result + class PyPIJSONLocator(Locator): """ This locator uses PyPI's JSON interface. It's very limited in functionality and probably not worth using. """ + def __init__(self, url, **kwargs): - super(PyPIJSONLocator, self).__init__(**kwargs) + super().__init__(**kwargs) self.base_url = ensure_slash(url) def get_distribution_names(self): @@ -476,7 +498,7 @@ class PyPIJSONLocator(Locator): url = urljoin(self.base_url, '%s/json' % quote(name)) try: resp = self.opener.open(url) - data = resp.read().decode() # for now + data = resp.read().decode() # for now d = json.loads(data) md = Metadata(scheme=self.scheme) data = d['info'] @@ -525,7 +547,7 @@ class PyPIJSONLocator(Locator): return result -class Page(object): +class Page: """ This class represents a scraped HTML page. """ @@ -534,11 +556,13 @@ class Page(object): # or immediately followed by a "rel" attribute. The attribute values can be # declared with double quotes, single quotes or no quotes - which leads to # the length of the expression. - _href = re.compile(""" + _href = re.compile( + """ (rel\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*))\\s+)? href\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*)) (\\s+rel\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*)))? -""", re.I | re.S | re.X) +""", re.I | re.S | re.X, + ) _base = re.compile(r"""]+)""", re.I | re.S) def __init__(self, data, url): @@ -562,16 +586,20 @@ href\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*)) downloads and which ones to queue for further scraping. """ def clean(url): - "Tidy up an URL." + 'Tidy up an URL.' scheme, netloc, path, params, query, frag = urlparse(url) - return urlunparse((scheme, netloc, quote(path), - params, query, frag)) + return urlunparse(( + scheme, netloc, quote(path), + params, query, frag, + )) result = set() for match in self._href.finditer(self.data): d = match.groupdict('') - rel = (d['rel1'] or d['rel2'] or d['rel3'] or - d['rel4'] or d['rel5'] or d['rel6']) + rel = ( + d['rel1'] or d['rel2'] or d['rel3'] or + d['rel4'] or d['rel5'] or d['rel6'] + ) url = d['url1'] or d['url2'] or d['url3'] url = urljoin(self.base_url, url) url = unescape(url) @@ -607,7 +635,7 @@ class SimpleScrapingLocator(Locator): This defaults to 10. :param kwargs: Passed to the superclass. """ - super(SimpleScrapingLocator, self).__init__(**kwargs) + super().__init__(**kwargs) self.base_url = ensure_slash(url) self.timeout = timeout self._page_cache = {} @@ -668,8 +696,10 @@ class SimpleScrapingLocator(Locator): del self.result return result - platform_dependent = re.compile(r'\b(linux_(i\d86|x86_64|arm\w+)|' - r'win(32|_amd64)|macosx_?\d+)\b', re.I) + platform_dependent = re.compile( + r'\b(linux_(i\d86|x86_64|arm\w+)|' + r'win(32|_amd64)|macosx_?\d+)\b', re.I, + ) def _is_platform_dependent(self, url): """ @@ -703,8 +733,10 @@ class SimpleScrapingLocator(Locator): particular "rel" attribute should be queued for scraping. """ scheme, netloc, path, _, _, _ = urlparse(link) - if path.endswith(self.source_extensions + self.binary_extensions + - self.excluded_extensions): + if path.endswith( + self.source_extensions + self.binary_extensions + + self.excluded_extensions, + ): result = False elif self.skip_externals and not link.startswith(self.base_url): result = False @@ -722,8 +754,10 @@ class SimpleScrapingLocator(Locator): result = False else: result = True - logger.debug('should_queue: %s (%s) from %s -> %s', link, rel, - referrer, result) + logger.debug( + 'should_queue: %s (%s) from %s -> %s', link, rel, + referrer, result, + ) return result def _fetch(self): @@ -744,8 +778,10 @@ class SimpleScrapingLocator(Locator): if link not in self._seen: try: self._seen.add(link) - if (not self._process_download(link) and - self._should_queue(link, url, rel)): + if ( + not self._process_download(link) and + self._should_queue(link, url, rel) + ): logger.debug('Queueing %s from %s', link, url) self._to_fetch.put(link) except MetadataInvalidError: # e.g. invalid versions @@ -832,6 +868,7 @@ class SimpleScrapingLocator(Locator): result.add(match.group(1)) return result + class DirectoryLocator(Locator): """ This class locates distributions in a directory tree. @@ -848,7 +885,7 @@ class DirectoryLocator(Locator): is searched, """ self.recursive = kwargs.pop('recursive', True) - super(DirectoryLocator, self).__init__(**kwargs) + super().__init__(**kwargs) path = os.path.abspath(path) if not os.path.isdir(path): # pragma: no cover raise DistlibException('Not a directory: %r' % path) @@ -868,9 +905,11 @@ class DirectoryLocator(Locator): for fn in files: if self.should_include(fn, root): fn = os.path.join(root, fn) - url = urlunparse(('file', '', - pathname2url(os.path.abspath(fn)), - '', '', '')) + url = urlunparse(( + 'file', '', + pathname2url(os.path.abspath(fn)), + '', '', '', + )) info = self.convert_url_to_download_info(url, name) if info: self._update_version_data(result, info) @@ -887,9 +926,11 @@ class DirectoryLocator(Locator): for fn in files: if self.should_include(fn, root): fn = os.path.join(root, fn) - url = urlunparse(('file', '', - pathname2url(os.path.abspath(fn)), - '', '', '')) + url = urlunparse(( + 'file', '', + pathname2url(os.path.abspath(fn)), + '', '', '', + )) info = self.convert_url_to_download_info(url, None) if info: result.add(info['name']) @@ -897,6 +938,7 @@ class DirectoryLocator(Locator): break return result + class JSONLocator(Locator): """ This locator uses special extended metadata (not available on PyPI) and is @@ -904,6 +946,7 @@ class JSONLocator(Locator): require archive downloads before dependencies can be determined! As you might imagine, that can be slow. """ + def get_distribution_names(self): """ Return all the distribution names known to this locator. @@ -920,10 +963,14 @@ class JSONLocator(Locator): # We don't store summary in project metadata as it makes # the data bigger for no benefit during dependency # resolution - dist = make_dist(data['name'], info['version'], - summary=data.get('summary', - 'Placeholder for summary'), - scheme=self.scheme) + dist = make_dist( + data['name'], info['version'], + summary=data.get( + 'summary', + 'Placeholder for summary', + ), + scheme=self.scheme, + ) md = dist.metadata md.source_url = info['url'] # TODO SHA256 digest @@ -935,18 +982,20 @@ class JSONLocator(Locator): result['urls'].setdefault(dist.version, set()).add(info['url']) return result + class DistPathLocator(Locator): """ This locator finds installed distributions in a path. It can be useful for adding to an :class:`AggregatingLocator`. """ + def __init__(self, distpath, **kwargs): """ Initialise an instance. :param distpath: A :class:`DistributionPath` instance to search. """ - super(DistPathLocator, self).__init__(**kwargs) + super().__init__(**kwargs) assert isinstance(distpath, DistributionPath) self.distpath = distpath @@ -957,8 +1006,8 @@ class DistPathLocator(Locator): else: result = { dist.version: dist, - 'urls': {dist.version: set([dist.source_url])}, - 'digests': {dist.version: set([None])} + 'urls': {dist.version: {dist.source_url}}, + 'digests': {dist.version: {None}}, } return result @@ -967,6 +1016,7 @@ class AggregatingLocator(Locator): """ This class allows you to chain and/or merge a list of locators. """ + def __init__(self, *locators, **kwargs): """ Initialise an instance. @@ -981,10 +1031,10 @@ class AggregatingLocator(Locator): """ self.merge = kwargs.pop('merge', False) self.locators = locators - super(AggregatingLocator, self).__init__(**kwargs) + super().__init__(**kwargs) def clear_cache(self): - super(AggregatingLocator, self).clear_cache() + super().clear_cache() for locator in self.locators: locator.clear_cache() @@ -1055,15 +1105,18 @@ class AggregatingLocator(Locator): # We use a legacy scheme simply because most of the dists on PyPI use legacy # versions which don't conform to PEP 426 / PEP 440. default_locator = AggregatingLocator( - JSONLocator(), - SimpleScrapingLocator('https://pypi.org/simple/', - timeout=3.0), - scheme='legacy') + JSONLocator(), + SimpleScrapingLocator( + 'https://pypi.org/simple/', + timeout=3.0, + ), + scheme='legacy', +) locate = default_locator.locate -class DependencyFinder(object): +class DependencyFinder: """ Locate dependencies for distributions. """ @@ -1176,8 +1229,10 @@ class DependencyFinder(object): unmatched.add(s) if unmatched: # can't replace other with provider - problems.add(('cantreplace', provider, other, - frozenset(unmatched))) + problems.add(( + 'cantreplace', provider, other, + frozenset(unmatched), + )) result = False else: # can replace other with provider @@ -1224,21 +1279,23 @@ class DependencyFinder(object): if ':*:' in meta_extras: meta_extras.remove(':*:') # :meta: and :run: are implicitly included - meta_extras |= set([':test:', ':build:', ':dev:']) + meta_extras |= {':test:', ':build:', ':dev:'} if isinstance(requirement, Distribution): dist = odist = requirement logger.debug('passed %s as requirement', odist) else: - dist = odist = self.locator.locate(requirement, - prereleases=prereleases) + dist = odist = self.locator.locate( + requirement, + prereleases=prereleases, + ) if dist is None: raise DistlibException('Unable to locate %r' % requirement) logger.debug('located %s', odist) dist.requested = True problems = set() - todo = set([dist]) - install_dists = set([odist]) + todo = {dist} + install_dists = {odist} while todo: dist = todo.pop() name = dist.key # case-insensitive @@ -1278,8 +1335,10 @@ class DependencyFinder(object): providers.add(provider) if r in ireqts and dist in install_dists: install_dists.add(provider) - logger.debug('Adding %s to install_dists', - provider.name_and_version) + logger.debug( + 'Adding %s to install_dists', + provider.name_and_version, + ) for p in providers: name = p.key if name not in self.dists_by_name: @@ -1294,7 +1353,9 @@ class DependencyFinder(object): for dist in dists: dist.build_time_dependency = dist not in install_dists if dist.build_time_dependency: - logger.debug('%s is a build-time dependency only.', - dist.name_and_version) + logger.debug( + '%s is a build-time dependency only.', + dist.name_and_version, + ) logger.debug('find done for %s', odist) return dists, problems diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/manifest.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/manifest.py index ca0fe44..897dac9 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/manifest.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/manifest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012-2013 Python Software Foundation. # See LICENSE.txt and CONTRIBUTORS.txt. @@ -8,6 +7,8 @@ Class representing the list of files in a distribution. Equivalent to distutils.filelist, but fixes some problems. """ +from __future__ import annotations + import fnmatch import logging import os @@ -34,7 +35,8 @@ _COMMENTED_LINE = re.compile('#.*?(?=\n)|\n(?=$)', re.M | re.S) # _PYTHON_VERSION = sys.version_info[:2] -class Manifest(object): + +class Manifest: """A list of files built by on exploring the filesystem and filtered by applying various patterns to what we find there. """ @@ -119,8 +121,10 @@ class Manifest(object): for f in result: add_dir(dirs, os.path.dirname(f)) result |= dirs - return [os.path.join(*path_tuple) for path_tuple in - sorted(os.path.split(path) for path in result)] + return [ + os.path.join(*path_tuple) for path_tuple in + sorted(os.path.split(path) for path in result) + ] def clear(self): """Clear all collected files.""" @@ -162,8 +166,10 @@ class Manifest(object): elif action == 'global-include': for pattern in patterns: if not self._include_pattern(pattern, anchor=False): - logger.warning('no files found matching %r ' - 'anywhere in distribution', pattern) + logger.warning( + 'no files found matching %r ' + 'anywhere in distribution', pattern, + ) elif action == 'global-exclude': for pattern in patterns: @@ -176,8 +182,10 @@ class Manifest(object): elif action == 'recursive-include': for pattern in patterns: if not self._include_pattern(pattern, prefix=thedir): - logger.warning('no files found matching %r ' - 'under directory %r', pattern, thedir) + logger.warning( + 'no files found matching %r ' + 'under directory %r', pattern, thedir, + ) elif action == 'recursive-exclude': for pattern in patterns: @@ -189,18 +197,23 @@ class Manifest(object): elif action == 'graft': if not self._include_pattern(None, prefix=dirpattern): - logger.warning('no directories found matching %r', - dirpattern) + logger.warning( + 'no directories found matching %r', + dirpattern, + ) elif action == 'prune': if not self._exclude_pattern(None, prefix=dirpattern): - logger.warning('no previously-included directories found ' - 'matching %r', dirpattern) + logger.warning( + 'no previously-included directories found ' + 'matching %r', dirpattern, + ) else: # pragma: no cover # This should never happen, as it should be caught in # _parse_template_line raise DistlibException( - 'invalid action %r' % action) + 'invalid action %r' % action, + ) # # Private API @@ -213,30 +226,36 @@ class Manifest(object): :return: A tuple of action, patterns, thedir, dir_patterns """ words = directive.split() - if len(words) == 1 and words[0] not in ('include', 'exclude', - 'global-include', - 'global-exclude', - 'recursive-include', - 'recursive-exclude', - 'graft', 'prune'): + if len(words) == 1 and words[0] not in ( + 'include', 'exclude', + 'global-include', + 'global-exclude', + 'recursive-include', + 'recursive-exclude', + 'graft', 'prune', + ): # no action given, let's use the default 'include' words.insert(0, 'include') action = words[0] patterns = thedir = dir_pattern = None - if action in ('include', 'exclude', - 'global-include', 'global-exclude'): + if action in ( + 'include', 'exclude', + 'global-include', 'global-exclude', + ): if len(words) < 2: raise DistlibException( - '%r expects ...' % action) + '%r expects ...' % action, + ) patterns = [convert_path(word) for word in words[1:]] elif action in ('recursive-include', 'recursive-exclude'): if len(words) < 3: raise DistlibException( - '%r expects ...' % action) + '%r expects ...' % action, + ) thedir = convert_path(words[1]) patterns = [convert_path(word) for word in words[2:]] @@ -244,7 +263,8 @@ class Manifest(object): elif action in ('graft', 'prune'): if len(words) != 2: raise DistlibException( - '%r expects a single ' % action) + '%r expects a single ' % action, + ) dir_pattern = convert_path(words[1]) @@ -253,8 +273,10 @@ class Manifest(object): return action, patterns, thedir, dir_pattern - def _include_pattern(self, pattern, anchor=True, prefix=None, - is_regex=False): + def _include_pattern( + self, pattern, anchor=True, prefix=None, + is_regex=False, + ): """Select strings (presumably filenames) from 'self.files' that match 'pattern', a Unix-style wildcard (glob) pattern. @@ -294,8 +316,10 @@ class Manifest(object): found = True return found - def _exclude_pattern(self, pattern, anchor=True, prefix=None, - is_regex=False): + def _exclude_pattern( + self, pattern, anchor=True, prefix=None, + is_regex=False, + ): """Remove strings (presumably filenames) from 'files' that match 'pattern'. @@ -314,8 +338,10 @@ class Manifest(object): found = True return found - def _translate_pattern(self, pattern, anchor=True, prefix=None, - is_regex=False): + def _translate_pattern( + self, pattern, anchor=True, prefix=None, + is_regex=False, + ): """Translate a shell-like wildcard pattern to a compiled regular expression. @@ -354,18 +380,22 @@ class Manifest(object): if os.sep == '\\': sep = r'\\' if _PYTHON_VERSION <= (3, 2): - pattern_re = '^' + base + sep.join((prefix_re, - '.*' + pattern_re)) + pattern_re = '^' + base + sep.join(( + prefix_re, + '.*' + pattern_re, + )) else: pattern_re = pattern_re[len(start): len(pattern_re) - len(end)] - pattern_re = r'%s%s%s%s.*%s%s' % (start, base, prefix_re, sep, - pattern_re, end) + pattern_re = r'{}{}{}{}.*{}{}'.format( + start, base, prefix_re, sep, + pattern_re, end, + ) else: # no prefix -- respect anchor flag if anchor: if _PYTHON_VERSION <= (3, 2): pattern_re = '^' + base + pattern_re else: - pattern_re = r'%s%s%s' % (start, base, pattern_re[len(start):]) + pattern_re = r'{}{}{}'.format(start, base, pattern_re[len(start):]) return re.compile(pattern_re) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/markers.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/markers.py index b43136f..3df85f0 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/markers.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/markers.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012-2017 Vinay Sajip. # Licensed to the Python Software Foundation under a contributor agreement. @@ -7,36 +6,40 @@ """ Parser for the environment markers micro-language defined in PEP 508. """ - # Note: In PEP 345, the micro-language was Python compatible, so the ast # module could be used to parse it. However, PEP 508 introduced operators such # as ~= and === which aren't in Python, necessitating a different approach. +from __future__ import annotations import os +import platform import re import sys -import platform from .compat import string_types -from .util import in_venv, parse_marker +from .util import in_venv +from .util import parse_marker from .version import NormalizedVersion as NV __all__ = ['interpret'] _VERSION_PATTERN = re.compile(r'((\d+(\.\d+)*\w*)|\'(\d+(\.\d+)*\w*)\'|\"(\d+(\.\d+)*\w*)\")') + def _is_literal(o): if not isinstance(o, string_types) or not o: return False return o[0] in '\'"' + def _get_versions(s): result = [] for m in _VERSION_PATTERN.finditer(s): result.append(NV(m.groups()[0])) return set(result) -class Evaluator(object): + +class Evaluator: """ This class is used to evaluate marker expessions. """ @@ -46,10 +49,10 @@ class Evaluator(object): '===': lambda x, y: x == y, '~=': lambda x, y: x == y or x > y, '!=': lambda x, y: x != y, - '<': lambda x, y: x < y, - '<=': lambda x, y: x == y or x < y, - '>': lambda x, y: x > y, - '>=': lambda x, y: x == y or x > y, + '<': lambda x, y: x < y, + '<=': lambda x, y: x == y or x < y, + '>': lambda x, y: x > y, + '>=': lambda x, y: x == y or x > y, 'and': lambda x, y: x and y, 'or': lambda x, y: x or y, 'in': lambda x, y: x in y, @@ -76,12 +79,12 @@ class Evaluator(object): elhs = expr['lhs'] erhs = expr['rhs'] if _is_literal(expr['lhs']) and _is_literal(expr['rhs']): - raise SyntaxError('invalid comparison: %s %s %s' % (elhs, op, erhs)) + raise SyntaxError('invalid comparison: {} {} {}'.format(elhs, op, erhs)) lhs = self.evaluate(elhs, context) rhs = self.evaluate(erhs, context) if ((elhs == 'python_version' or erhs == 'python_version') and - op in ('<', '<=', '>', '>=', '===', '==', '!=', '~=')): + op in ('<', '<=', '>', '>=', '===', '==', '!=', '~=')): lhs = NV(lhs) rhs = NV(rhs) elif elhs == 'python_version' and op in ('in', 'not in'): @@ -90,9 +93,10 @@ class Evaluator(object): result = self.operations[op](lhs, rhs) return result + def default_context(): def format_full_version(info): - version = '%s.%s.%s' % (info.major, info.minor, info.micro) + version = '{}.{}.{}'.format(info.major, info.minor, info.micro) kind = info.releaselevel if kind != 'final': version += kind[0] + str(info.serial) @@ -121,11 +125,13 @@ def default_context(): } return result + DEFAULT_CONTEXT = default_context() del default_context evaluator = Evaluator() + def interpret(marker, execution_context=None): """ Interpret a marker and return a result depending on environment. @@ -138,9 +144,9 @@ def interpret(marker, execution_context=None): try: expr, rest = parse_marker(marker) except Exception as e: - raise SyntaxError('Unable to interpret marker syntax: %s: %s' % (marker, e)) + raise SyntaxError('Unable to interpret marker syntax: {}: {}'.format(marker, e)) if rest and rest[0] != '#': - raise SyntaxError('unexpected trailing data in marker: %s: %s' % (marker, rest)) + raise SyntaxError('unexpected trailing data in marker: {}: {}'.format(marker, rest)) context = dict(DEFAULT_CONTEXT) if execution_context: context.update(execution_context) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/metadata.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/metadata.py index 6a26b0a..185d76e 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/metadata.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/metadata.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012 The Python Software Foundation. # See LICENSE.txt and CONTRIBUTORS.txt. @@ -7,20 +6,24 @@ Supports all metadata formats (1.0, 1.1, 1.2, 1.3/2.1 and withdrawn 2.0). """ -from __future__ import unicode_literals +from __future__ import annotations import codecs -from email import message_from_file import json import logging import re +from email import message_from_file - -from . import DistlibException, __version__ -from .compat import StringIO, string_types, text_type +from . import __version__ +from . import DistlibException +from .compat import string_types +from .compat import StringIO +from .compat import text_type from .markers import interpret -from .util import extract_by_key, get_extras -from .version import get_scheme, PEP440_VERSION_RE +from .util import extract_by_key +from .util import get_extras +from .version import get_scheme +from .version import PEP440_VERSION_RE logger = logging.getLogger(__name__) @@ -40,6 +43,7 @@ class MetadataUnrecognizedVersionError(DistlibException): class MetadataInvalidError(DistlibException): """A metadata value is invalid""" + # public API of this module __all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION'] @@ -52,51 +56,67 @@ PKG_INFO_PREFERRED_VERSION = '1.1' _LINE_PREFIX_1_2 = re.compile('\n \\|') _LINE_PREFIX_PRE_1_2 = re.compile('\n ') -_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', - 'Summary', 'Description', - 'Keywords', 'Home-page', 'Author', 'Author-email', - 'License') +_241_FIELDS = ( + 'Metadata-Version', 'Name', 'Version', 'Platform', + 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License', +) -_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', - 'Supported-Platform', 'Summary', 'Description', - 'Keywords', 'Home-page', 'Author', 'Author-email', - 'License', 'Classifier', 'Download-URL', 'Obsoletes', - 'Provides', 'Requires') +_314_FIELDS = ( + 'Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License', 'Classifier', 'Download-URL', 'Obsoletes', + 'Provides', 'Requires', +) -_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier', - 'Download-URL') +_314_MARKERS = ( + 'Obsoletes', 'Provides', 'Requires', 'Classifier', + 'Download-URL', +) -_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', - 'Supported-Platform', 'Summary', 'Description', - 'Keywords', 'Home-page', 'Author', 'Author-email', - 'Maintainer', 'Maintainer-email', 'License', - 'Classifier', 'Download-URL', 'Obsoletes-Dist', - 'Project-URL', 'Provides-Dist', 'Requires-Dist', - 'Requires-Python', 'Requires-External') +_345_FIELDS = ( + 'Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External', +) -_345_MARKERS = ('Provides-Dist', 'Requires-Dist', 'Requires-Python', - 'Obsoletes-Dist', 'Requires-External', 'Maintainer', - 'Maintainer-email', 'Project-URL') +_345_MARKERS = ( + 'Provides-Dist', 'Requires-Dist', 'Requires-Python', + 'Obsoletes-Dist', 'Requires-External', 'Maintainer', + 'Maintainer-email', 'Project-URL', +) -_426_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', - 'Supported-Platform', 'Summary', 'Description', - 'Keywords', 'Home-page', 'Author', 'Author-email', - 'Maintainer', 'Maintainer-email', 'License', - 'Classifier', 'Download-URL', 'Obsoletes-Dist', - 'Project-URL', 'Provides-Dist', 'Requires-Dist', - 'Requires-Python', 'Requires-External', 'Private-Version', - 'Obsoleted-By', 'Setup-Requires-Dist', 'Extension', - 'Provides-Extra') +_426_FIELDS = ( + 'Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External', 'Private-Version', + 'Obsoleted-By', 'Setup-Requires-Dist', 'Extension', + 'Provides-Extra', +) -_426_MARKERS = ('Private-Version', 'Provides-Extra', 'Obsoleted-By', - 'Setup-Requires-Dist', 'Extension') +_426_MARKERS = ( + 'Private-Version', 'Provides-Extra', 'Obsoleted-By', + 'Setup-Requires-Dist', 'Extension', +) # See issue #106: Sometimes 'Requires' and 'Provides' occur wrongly in # the metadata. Include them in the tuple literal below to allow them # (for now). # Ditto for Obsoletes - see issue #140. -_566_FIELDS = _426_FIELDS + ('Description-Content-Type', - 'Requires', 'Provides', 'Obsoletes') +_566_FIELDS = _426_FIELDS + ( + 'Description-Content-Type', + 'Requires', 'Provides', 'Obsoletes', +) _566_MARKERS = ('Description-Content-Type',) @@ -196,21 +216,24 @@ def _best_version(fields): return '2.0' + # This follows the rules about transforming keys as described in # https://www.python.org/dev/peps/pep-0566/#id17 _ATTR2FIELD = { - name.lower().replace("-", "_"): name for name in _ALL_FIELDS + name.lower().replace('-', '_'): name for name in _ALL_FIELDS } _FIELD2ATTR = {field: attr for attr, field in _ATTR2FIELD.items()} _PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist') _VERSIONS_FIELDS = ('Requires-Python',) _VERSION_FIELDS = ('Version',) -_LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes', - 'Requires', 'Provides', 'Obsoletes-Dist', - 'Provides-Dist', 'Requires-Dist', 'Requires-External', - 'Project-URL', 'Supported-Platform', 'Setup-Requires-Dist', - 'Provides-Extra', 'Extension') +_LISTFIELDS = ( + 'Platform', 'Classifier', 'Obsoletes', + 'Requires', 'Provides', 'Obsoletes-Dist', + 'Provides-Dist', 'Requires-Dist', 'Requires-External', + 'Project-URL', 'Supported-Platform', 'Setup-Requires-Dist', + 'Provides-Extra', 'Extension', +) _LISTTUPLEFIELDS = ('Project-URL',) _ELEMENTSFIELD = ('Keywords',) @@ -232,10 +255,10 @@ def _get_name_and_version(name, version, for_filename=False): # spaces in the version string become '.' name = _FILESAFE.sub('-', name) version = _FILESAFE.sub('-', version.replace(' ', '.')) - return '%s-%s' % (name, version) + return '{}-{}'.format(name, version) -class LegacyMetadata(object): +class LegacyMetadata: """The legacy metadata of a release. Supports versions 1.0, 1.1, 1.2, 2.0 and 1.3/2.1 (auto-detected). You can @@ -247,8 +270,10 @@ class LegacyMetadata(object): """ # TODO document the mapping API and UNKNOWN default key - def __init__(self, path=None, fileobj=None, mapping=None, - scheme='default'): + def __init__( + self, path=None, fileobj=None, mapping=None, + scheme='default', + ): if [path, fileobj, mapping].count(None) < 2: raise TypeError('path, fileobj and mapping are exclusive') self._fields = {} @@ -267,7 +292,7 @@ class LegacyMetadata(object): self._fields['Metadata-Version'] = _best_version(self._fields) def _write_field(self, fileobj, name, value): - fileobj.write('%s: %s\n' % (name, value)) + fileobj.write('{}: {}\n'.format(name, value)) def __getitem__(self, name): return self.get(name) @@ -283,8 +308,10 @@ class LegacyMetadata(object): raise KeyError(name) def __contains__(self, name): - return (name in self._fields or - self._convert_name(name) in self._fields) + return ( + name in self._fields or + self._convert_name(name) in self._fields + ) def _convert_name(self, name): if name in _ALL_FIELDS: @@ -361,7 +388,7 @@ class LegacyMetadata(object): # PEP 566 specifies that the body be used for the description, if # available body = msg.get_payload() - self["Description"] = body if body else self["Description"] + self['Description'] = body if body else self['Description'] # logger.debug('Attempting to set metadata for %s', self) # self.set_metadata_version() @@ -431,13 +458,15 @@ class LegacyMetadata(object): name = self._convert_name(name) if ((name in _ELEMENTSFIELD or name == 'Platform') and - not isinstance(value, (list, tuple))): + not isinstance(value, (list, tuple))): if isinstance(value, string_types): value = [v.strip() for v in value.split(',')] else: value = [] - elif (name in _LISTFIELDS and - not isinstance(value, (list, tuple))): + elif ( + name in _LISTFIELDS and + not isinstance(value, (list, tuple)) + ): if isinstance(value, string_types): value = [value] else: @@ -453,16 +482,21 @@ class LegacyMetadata(object): if not scheme.is_valid_matcher(v.split(';')[0]): logger.warning( "'%s': '%s' is not valid (field '%s')", - project_name, v, name) + project_name, v, name, + ) # FIXME this rejects UNKNOWN, is that right? elif name in _VERSIONS_FIELDS and value is not None: if not scheme.is_valid_constraint_list(value): - logger.warning("'%s': '%s' is not a valid version (field '%s')", - project_name, value, name) + logger.warning( + "'%s': '%s' is not a valid version (field '%s')", + project_name, value, name, + ) elif name in _VERSION_FIELDS and value is not None: if not scheme.is_valid_version(value): - logger.warning("'%s': '%s' is not a valid version (field '%s')", - project_name, value, name) + logger.warning( + "'%s': '%s' is not a valid version (field '%s')", + project_name, value, name, + ) if name in _UNICODEFIELDS: if name == 'Description': @@ -531,15 +565,21 @@ class LegacyMetadata(object): return False return True - for fields, controller in ((_PREDICATE_FIELDS, are_valid_constraints), - (_VERSIONS_FIELDS, - scheme.is_valid_constraint_list), - (_VERSION_FIELDS, - scheme.is_valid_version)): + for fields, controller in ( + (_PREDICATE_FIELDS, are_valid_constraints), + ( + _VERSIONS_FIELDS, + scheme.is_valid_constraint_list, + ), + ( + _VERSION_FIELDS, + scheme.is_valid_version, + ), + ): for field in fields: value = self.get(field, None) if value is not None and not controller(value): - warnings.append("Wrong value for '%s': %s" % (field, value)) + warnings.append("Wrong value for '{}': {}".format(field, value)) return missing, warnings @@ -581,8 +621,7 @@ class LegacyMetadata(object): return list(_version2fieldlist(self['Metadata-Version'])) def __iter__(self): - for key in self.keys(): - yield key + yield from self.keys() def values(self): return [self[key] for key in self.keys()] @@ -591,8 +630,10 @@ class LegacyMetadata(object): return [(key, self[key]) for key in self.keys()] def __repr__(self): - return '<%s %s %s>' % (self.__class__.__name__, self.name, - self.version) + return '<{} {} {}>'.format( + self.__class__.__name__, self.name, + self.version, + ) METADATA_FILENAME = 'pydist.json' @@ -600,7 +641,7 @@ WHEEL_METADATA_FILENAME = 'metadata.json' LEGACY_METADATA_FILENAME = 'METADATA' -class Metadata(object): +class Metadata: """ The metadata of a release. This implementation uses 2.0 (JSON) metadata where possible. If not possible, it wraps a LegacyMetadata @@ -625,13 +666,17 @@ class Metadata(object): 'summary': ('legacy',), } - INDEX_KEYS = ('name version license summary description author ' - 'author_email keywords platform home_page classifiers ' - 'download_url') + INDEX_KEYS = ( + 'name version license summary description author ' + 'author_email keywords platform home_page classifiers ' + 'download_url' + ) - DEPENDENCY_KEYS = ('extras run_requires test_requires build_requires ' - 'dev_requires provides meta_requires obsoleted_by ' - 'supports_environments') + DEPENDENCY_KEYS = ( + 'extras run_requires test_requires build_requires ' + 'dev_requires provides meta_requires obsoleted_by ' + 'supports_environments' + ) SYNTAX_VALIDATORS = { 'metadata_version': (METADATA_VERSION_MATCHER, ()), @@ -642,8 +687,10 @@ class Metadata(object): __slots__ = ('_legacy', '_data', 'scheme') - def __init__(self, path=None, fileobj=None, mapping=None, - scheme='default'): + def __init__( + self, path=None, fileobj=None, mapping=None, + scheme='default', + ): if [path, fileobj, mapping].count(None) < 2: raise TypeError('path, fileobj and mapping are exclusive') self._legacy = None @@ -683,11 +730,13 @@ class Metadata(object): # The ValueError comes from the json.load - if that # succeeds and we get a validation error, we want # that to propagate - self._legacy = LegacyMetadata(fileobj=StringIO(data), - scheme=scheme) + self._legacy = LegacyMetadata( + fileobj=StringIO(data), + scheme=scheme, + ) self.validate() - common_keys = set(('name', 'version', 'license', 'keywords', 'summary')) + common_keys = {'name', 'version', 'license', 'keywords', 'summary'} none_list = (None, list) none_dict = (None, dict) @@ -722,8 +771,10 @@ class Metadata(object): result = self._legacy.get(lk) else: value = None if maker is None else maker() - if key not in ('commands', 'exports', 'modules', 'namespaces', - 'classifiers'): + if key not in ( + 'commands', 'exports', 'modules', 'namespaces', + 'classifiers', + ): result = self._data.get(key, value) else: # special cases for PEP 459 @@ -759,9 +810,13 @@ class Metadata(object): if (scheme or self.scheme) not in exclusions: m = pattern.match(value) if not m: - raise MetadataInvalidError("'%s' is an invalid value for " - "the '%s' property" % (value, - key)) + raise MetadataInvalidError( + "'%s' is an invalid value for " + "the '%s' property" % ( + value, + key, + ), + ) def __setattr__(self, key, value): self._validate_value(key, value) @@ -773,8 +828,10 @@ class Metadata(object): if lk is None: raise NotImplementedError self._legacy[lk] = value - elif key not in ('commands', 'exports', 'modules', 'namespaces', - 'classifiers'): + elif key not in ( + 'commands', 'exports', 'modules', 'namespaces', + 'classifiers', + ): self._data[key] = value else: # special cases for PEP 459 @@ -812,7 +869,7 @@ class Metadata(object): result = self._legacy['Provides-Dist'] else: result = self._data.setdefault('provides', []) - s = '%s (%s)' % (self.name, self.version) + s = '{} ({})'.format(self.name, self.version) if s not in result: result.append(s) return result @@ -862,8 +919,12 @@ class Metadata(object): # A recursive call, but it should terminate since 'test' # has been removed from the extras reqts = self._data.get('%s_requires' % key, []) - result.extend(self.get_requirements(reqts, extras=extras, - env=env)) + result.extend( + self.get_requirements( + reqts, extras=extras, + env=env, + ), + ) return result @property @@ -904,8 +965,10 @@ class Metadata(object): if self._legacy: missing, warnings = self._legacy.check(True) if missing or warnings: - logger.warning('Metadata: missing: %s, warnings: %s', - missing, warnings) + logger.warning( + 'Metadata: missing: %s, warnings: %s', + missing, warnings, + ) else: self._validate_mapping(self._data, self.scheme) @@ -923,8 +986,10 @@ class Metadata(object): 'generator': self.GENERATOR, } lmd = self._legacy.todict(True) # skip missing ones - for k in ('name', 'version', 'license', 'summary', 'description', - 'classifier'): + for k in ( + 'name', 'version', 'license', 'summary', 'description', + 'classifier', + ): if k in lmd: if k == 'classifier': nk = 'classifiers' @@ -935,8 +1000,10 @@ class Metadata(object): if kw == ['']: kw = [] result['keywords'] = kw - keys = (('requires_dist', 'run_requires'), - ('setup_requires_dist', 'build_requires')) + keys = ( + ('requires_dist', 'run_requires'), + ('setup_requires_dist', 'build_requires'), + ) for ok, nk in keys: if ok in lmd and lmd[ok]: result[nk] = [{'requires': lmd[ok]}] @@ -974,7 +1041,7 @@ class Metadata(object): marker = 'extra == "%s"' % extra if env: if marker: - marker = '(%s) and %s' % (env, marker) + marker = '({}) and {}'.format(env, marker) else: marker = env reqts.add(';'.join((r, marker))) @@ -1027,12 +1094,16 @@ class Metadata(object): else: d = self._data if fileobj: - json.dump(d, fileobj, ensure_ascii=True, indent=2, - sort_keys=True) + json.dump( + d, fileobj, ensure_ascii=True, indent=2, + sort_keys=True, + ) else: with codecs.open(path, 'w', 'utf-8') as f: - json.dump(d, f, ensure_ascii=True, indent=2, - sort_keys=True) + json.dump( + d, f, ensure_ascii=True, indent=2, + sort_keys=True, + ) def add_requirements(self, requirements): if self._legacy: @@ -1045,7 +1116,7 @@ class Metadata(object): always = entry break if always is None: - always = { 'requires': requirements } + always = {'requires': requirements} run_requires.insert(0, always) else: rset = set(always['requires']) | set(requirements) @@ -1054,5 +1125,7 @@ class Metadata(object): def __repr__(self): name = self.name or '(no name)' version = self.version or 'no version' - return '<%s %s %s (%s)>' % (self.__class__.__name__, - self.metadata_version, name, version) + return '<{} {} {} ({})>'.format( + self.__class__.__name__, + self.metadata_version, name, version, + ) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/resources.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/resources.py index fef52aa..d76e570 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/resources.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/resources.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2013-2017 Vinay Sajip. # Licensed to the Python Software Foundation under a contributor agreement. # See LICENSE.txt and CONTRIBUTORS.txt. # -from __future__ import unicode_literals +from __future__ import annotations import bisect import io @@ -16,7 +15,9 @@ import types import zipimport from . import DistlibException -from .util import cached_property, get_cache_base, Cache +from .util import Cache +from .util import cached_property +from .util import get_cache_base logger = logging.getLogger(__name__) @@ -28,8 +29,8 @@ class ResourceCache(Cache): def __init__(self, base=None): if base is None: # Use native string to avoid issues on 2.x: see Python #20140. - base = os.path.join(get_cache_base(), str('resource-cache')) - super(ResourceCache, self).__init__(base) + base = os.path.join(get_cache_base(), 'resource-cache') + super().__init__(base) def is_stale(self, resource, path): """ @@ -68,7 +69,7 @@ class ResourceCache(Cache): return result -class ResourceBase(object): +class ResourceBase: def __init__(self, finder, name): self.finder = finder self.name = name @@ -115,7 +116,7 @@ class ResourceContainer(ResourceBase): return self.finder.get_resources(self) -class ResourceFinder(object): +class ResourceFinder: """ Resource finder for file system resources. """ @@ -175,9 +176,11 @@ class ResourceFinder(object): def get_resources(self, resource): def allowed(f): - return (f != '__pycache__' and not - f.endswith(self.skipped_extensions)) - return set([f for f in os.listdir(resource.path) if allowed(f)]) + return ( + f != '__pycache__' and not + f.endswith(self.skipped_extensions) + ) + return {f for f in os.listdir(resource.path) if allowed(f)} def is_container(self, resource): return self._is_directory(resource.path) @@ -209,8 +212,9 @@ class ZipResourceFinder(ResourceFinder): """ Resource finder for resources in .zip files. """ + def __init__(self, module): - super(ZipResourceFinder, self).__init__(module) + super().__init__(module) archive = self.loader.archive self.prefix_len = 1 + len(archive) # PyPy doesn't have a _files attr on zipimporter, and you can't set one @@ -285,7 +289,7 @@ class ZipResourceFinder(ResourceFinder): _finder_registry = { type(None): ResourceFinder, - zipimport.zipimporter: ZipResourceFinder + zipimport.zipimporter: ZipResourceFinder, } try: @@ -324,8 +328,10 @@ def finder(package): module = sys.modules[package] path = getattr(module, '__path__', None) if path is None: - raise DistlibException('You cannot get a finder for a module, ' - 'only for a package') + raise DistlibException( + 'You cannot get a finder for a module, ' + 'only for a package', + ) loader = getattr(module, '__loader__', None) finder_maker = _finder_registry.get(type(loader)) if finder_maker is None: @@ -335,7 +341,7 @@ def finder(package): return result -_dummy_module = types.ModuleType(str('__dummy__')) +_dummy_module = types.ModuleType('__dummy__') def finder_for_path(path): diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/scripts.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/scripts.py index 913912c..e41c583 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/scripts.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/scripts.py @@ -1,20 +1,27 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2013-2015 Vinay Sajip. # Licensed to the Python Software Foundation under a contributor agreement. # See LICENSE.txt and CONTRIBUTORS.txt. # -from io import BytesIO +from __future__ import annotations + import logging import os import re import struct import sys +from io import BytesIO -from .compat import sysconfig, detect_encoding, ZipFile +from .compat import detect_encoding +from .compat import sysconfig +from .compat import ZipFile from .resources import finder -from .util import (FileOperator, get_export_entry, convert_path, - get_executable, get_platform, in_venv) +from .util import convert_path +from .util import FileOperator +from .util import get_executable +from .util import get_export_entry +from .util import get_platform +from .util import in_venv logger = logging.getLogger(__name__) @@ -57,16 +64,18 @@ def enquote_executable(executable): if executable.startswith('/usr/bin/env '): env, _executable = executable.split(' ', 1) if ' ' in _executable and not _executable.startswith('"'): - executable = '%s "%s"' % (env, _executable) + executable = '{} "{}"'.format(env, _executable) else: if not executable.startswith('"'): executable = '"%s"' % executable return executable + # Keep the old name around (for now), as there is at least one project using it! _enquote_executable = enquote_executable -class ScriptMaker(object): + +class ScriptMaker: """ A class to copy or create scripts from source scripts or callable specifications. @@ -75,21 +84,26 @@ class ScriptMaker(object): executable = None # for shebangs - def __init__(self, source_dir, target_dir, add_launchers=True, - dry_run=False, fileop=None): + def __init__( + self, source_dir, target_dir, add_launchers=True, + dry_run=False, fileop=None, + ): self.source_dir = source_dir self.target_dir = target_dir self.add_launchers = add_launchers self.force = False self.clobber = False # It only makes sense to set mode bits on POSIX. - self.set_mode = (os.name == 'posix') or (os.name == 'java' and - os._name == 'posix') - self.variants = set(('', 'X.Y')) + self.set_mode = (os.name == 'posix') or ( + os.name == 'java' and + os._name == 'posix' + ) + self.variants = {'', 'X.Y'} self._fileop = fileop or FileOperator(dry_run) self._is_nt = os.name == 'nt' or ( - os.name == 'java' and os._name == 'nt') + os.name == 'java' and os._name == 'nt' + ) self.version_info = sys.version_info def _get_alternate_executable(self, executable, options): @@ -108,7 +122,7 @@ class ScriptMaker(object): try: with open(executable) as fp: return fp.read(2) == '#!' - except (OSError, IOError): + except OSError: logger.warning('Failed to open %s', executable) return False @@ -163,18 +177,25 @@ class ScriptMaker(object): elif not sysconfig.is_python_build(): executable = get_executable() elif in_venv(): # pragma: no cover - executable = os.path.join(sysconfig.get_path('scripts'), - 'python%s' % sysconfig.get_config_var('EXE')) + executable = os.path.join( + sysconfig.get_path('scripts'), + 'python%s' % sysconfig.get_config_var('EXE'), + ) else: # pragma: no cover executable = os.path.join( sysconfig.get_config_var('BINDIR'), - 'python%s%s' % (sysconfig.get_config_var('VERSION'), - sysconfig.get_config_var('EXE'))) + 'python{}{}'.format( + sysconfig.get_config_var('VERSION'), + sysconfig.get_config_var('EXE'), + ), + ) if not os.path.isfile(executable): # for Python builds from source on Windows, no Python executables with # a version suffix are created, so we use python.exe - executable = os.path.join(sysconfig.get_config_var('BINDIR'), - 'python%s' % (sysconfig.get_config_var('EXE'))) + executable = os.path.join( + sysconfig.get_config_var('BINDIR'), + 'python%s' % (sysconfig.get_config_var('EXE')), + ) if options: executable = self._get_alternate_executable(executable, options) @@ -198,8 +219,10 @@ class ScriptMaker(object): # check that the shebang is decodable using utf-8. executable = executable.encode('utf-8') # in case of IronPython, play safe and enable frames support - if (sys.platform == 'cli' and '-X:Frames' not in post_interp - and '-X:FullFrames' not in post_interp): # pragma: no cover + if ( + sys.platform == 'cli' and '-X:Frames' not in post_interp and + '-X:FullFrames' not in post_interp + ): # pragma: no cover post_interp += b' -X:Frames' shebang = self._build_shebang(executable, post_interp) # Python parser starts to read a script using UTF-8 until @@ -211,7 +234,8 @@ class ScriptMaker(object): shebang.decode('utf-8') except UnicodeDecodeError: # pragma: no cover raise ValueError( - 'The shebang (%r) is not decodable from utf-8' % shebang) + 'The shebang (%r) is not decodable from utf-8' % shebang, + ) # If the script is encoded to a custom encoding (use a # #coding:xxx cookie), the shebang has to be decodable from # the script encoding too. @@ -221,13 +245,16 @@ class ScriptMaker(object): except UnicodeDecodeError: # pragma: no cover raise ValueError( 'The shebang (%r) is not decodable ' - 'from the script encoding (%r)' % (shebang, encoding)) + 'from the script encoding (%r)' % (shebang, encoding), + ) return shebang def _get_script_text(self, entry): - return self.script_template % dict(module=entry.prefix, - import_name=entry.suffix.split('.')[0], - func=entry.suffix) + return self.script_template % dict( + module=entry.prefix, + import_name=entry.suffix.split('.')[0], + func=entry.suffix, + ) manifest = _DEFAULT_MANIFEST @@ -263,22 +290,26 @@ class ScriptMaker(object): self._fileop.write_binary_file(outname, script_bytes) except Exception: # Failed writing an executable - it might be in use. - logger.warning('Failed to write executable - trying to ' - 'use .deleteme logic') + logger.warning( + 'Failed to write executable - trying to ' + 'use .deleteme logic', + ) dfname = '%s.deleteme' % outname if os.path.exists(dfname): os.remove(dfname) # Not allowed to fail here os.rename(outname, dfname) # nor here self._fileop.write_binary_file(outname, script_bytes) - logger.debug('Able to replace executable using ' - '.deleteme logic') + logger.debug( + 'Able to replace executable using ' + '.deleteme logic', + ) try: os.remove(dfname) except Exception: pass # still in use - ignore error else: if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover - outname = '%s.%s' % (outname, ext) + outname = '{}.{}'.format(outname, ext) if os.path.exists(outname) and not self.clobber: logger.warning('Skipping existing file %s', outname) continue @@ -294,10 +325,14 @@ class ScriptMaker(object): if '' in self.variants: result.add(name) if 'X' in self.variants: - result.add('%s%s' % (name, self.version_info[0])) + result.add('{}{}'.format(name, self.version_info[0])) if 'X.Y' in self.variants: - result.add('%s%s%s.%s' % (name, self.variant_separator, - self.version_info[0], self.version_info[1])) + result.add( + '{}{}{}.{}'.format( + name, self.variant_separator, + self.version_info[0], self.version_info[1], + ), + ) return result def _make_script(self, entry, filenames, options=None): @@ -329,7 +364,7 @@ class ScriptMaker(object): # script. try: f = open(script, 'rb') - except IOError: # pragma: no cover + except OSError: # pragma: no cover if not self.dry_run: raise f = None @@ -352,8 +387,10 @@ class ScriptMaker(object): self._fileop.set_executable_mode([outname]) filenames.append(outname) else: - logger.info('copying and adjusting %s -> %s', script, - self.target_dir) + logger.info( + 'copying and adjusting %s -> %s', script, + self.target_dir, + ) if not self._fileop.dry_run: encoding, lines = detect_encoding(f.readline) f.seek(0) @@ -385,14 +422,18 @@ class ScriptMaker(object): else: bits = '32' platform_suffix = '-arm' if get_platform() == 'win-arm64' else '' - name = '%s%s%s.exe' % (kind, bits, platform_suffix) + name = '{}{}{}.exe'.format(kind, bits, platform_suffix) # Issue 31: don't hardcode an absolute package name, but # determine it relative to the current package distlib_package = __name__.rsplit('.', 1)[0] resource = finder(distlib_package).find(name) if not resource: - msg = ('Unable to find resource %s in package %s' % (name, - distlib_package)) + msg = ( + 'Unable to find resource {} in package {}'.format( + name, + distlib_package, + ) + ) raise ValueError(msg) return resource.bytes diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/util.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/util.py index 80bfc86..ed955d8 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/util.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/util.py @@ -2,11 +2,11 @@ # Copyright (C) 2012-2021 The Python Software Foundation. # See LICENSE.txt and CONTRIBUTORS.txt. # +from __future__ import annotations + import codecs -from collections import deque import contextlib import csv -from glob import iglob as std_iglob import io import json import logging @@ -14,6 +14,8 @@ import os import py_compile import re import socket +from collections import deque +from glob import iglob as std_iglob try: import ssl except ImportError: # pragma: no cover @@ -31,11 +33,13 @@ except ImportError: # pragma: no cover import time from . import DistlibException -from .compat import (string_types, text_type, shutil, raw_input, StringIO, - cache_from_source, urlopen, urljoin, httplib, xmlrpclib, - splittype, HTTPHandler, BaseConfigurator, valid_ident, - Container, configparser, URLError, ZipFile, fsdecode, - unquote, urlparse) +from .compat import ( + string_types, text_type, shutil, raw_input, StringIO, + cache_from_source, urlopen, urljoin, httplib, xmlrpclib, + splittype, HTTPHandler, BaseConfigurator, valid_ident, + Container, configparser, URLError, ZipFile, fsdecode, + unquote, urlparse, +) logger = logging.getLogger(__name__) @@ -95,7 +99,7 @@ def parse_marker(marker_string): raise SyntaxError('unterminated string: %s' % s) parts.append(q) result = ''.join(parts) - remaining = remaining[1:].lstrip() # skip past closing quote + remaining = remaining[1:].lstrip() # skip past closing quote return result, remaining def marker_expr(remaining): @@ -262,9 +266,11 @@ def parse_requirement(req): if not versions: rs = distname else: - rs = '%s %s' % (distname, ', '.join(['%s %s' % con for con in versions])) - return Container(name=distname, extras=extras, constraints=versions, - marker=mark_expr, url=uri, requirement=rs) + rs = '{} {}'.format(distname, ', '.join(['%s %s' % con for con in versions])) + return Container( + name=distname, extras=extras, constraints=versions, + marker=mark_expr, url=uri, requirement=rs, + ) def get_resources_dests(resources_root, rules): @@ -304,15 +310,15 @@ def in_venv(): def get_executable(): -# The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as -# changes to the stub launcher mean that sys.executable always points -# to the stub on OS X -# if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__' -# in os.environ): -# result = os.environ['__PYVENV_LAUNCHER__'] -# else: -# result = sys.executable -# return result + # The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as + # changes to the stub launcher mean that sys.executable always points + # to the stub on OS X + # if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__' + # in os.environ): + # result = os.environ['__PYVENV_LAUNCHER__'] + # else: + # result = sys.executable + # return result # Avoid normcasing: see issue #143 # result = os.path.normcase(sys.executable) result = sys.executable @@ -346,6 +352,7 @@ def extract_by_key(d, keys): result[key] = d[key] return result + def read_exports(stream): if sys.version_info[0] >= 3: # needs to be a text stream @@ -358,7 +365,7 @@ def read_exports(stream): result = jdata['extensions']['python.exports']['exports'] for group, entries in result.items(): for k, v in entries.items(): - s = '%s = %s' % (k, v) + s = '{} = {}'.format(k, v) entry = get_export_entry(s) assert entry is not None entries[k] = entry @@ -385,7 +392,7 @@ def read_exports(stream): for key in cp.sections(): result[key] = entries = {} for name, value in cp.items(key): - s = '%s = %s' % (name, value) + s = '{} = {}'.format(name, value) entry = get_export_entry(s) assert entry is not None #entry.dist = self @@ -405,9 +412,9 @@ def write_exports(exports, stream): if entry.suffix is None: s = entry.prefix else: - s = '%s:%s' % (entry.prefix, entry.suffix) + s = '{}:{}'.format(entry.prefix, entry.suffix) if entry.flags: - s = '%s [%s]' % (s, ', '.join(entry.flags)) + s = '{} [{}]'.format(s, ', '.join(entry.flags)) cp.set(k, entry.name, s) cp.write(stream) @@ -420,6 +427,7 @@ def tempdir(): finally: shutil.rmtree(td) + @contextlib.contextmanager def chdir(d): cwd = os.getcwd() @@ -440,7 +448,7 @@ def socket_timeout(seconds=15): socket.setdefaulttimeout(cto) -class cached_property(object): +class cached_property: def __init__(self, func): self.func = func #for attr in ('__name__', '__module__', '__doc__'): @@ -454,6 +462,7 @@ class cached_property(object): #obj.__dict__[self.func.__name__] = value = self.func(obj) return value + def convert_path(pathname): """Return 'pathname' as a name that will work on the native filesystem. @@ -481,7 +490,7 @@ def convert_path(pathname): return os.path.join(*paths) -class FileOperator(object): +class FileOperator: def __init__(self, dry_run=False): self.dry_run = dry_run self.ensured = set() @@ -509,8 +518,10 @@ class FileOperator(object): second will have the same "age". """ if not os.path.exists(source): - raise DistlibException("file '%r' does not exist" % - os.path.abspath(source)) + raise DistlibException( + "file '%r' does not exist" % + os.path.abspath(source), + ) if not os.path.exists(target): return True @@ -566,13 +577,13 @@ class FileOperator(object): # all the files specified. for f in files: if self.dry_run: - logger.info("changing mode of %s", f) + logger.info('changing mode of %s', f) else: mode = (os.stat(f).st_mode | bits) & mask - logger.info("changing mode of %s to %o", f, mode) + logger.info('changing mode of %s to %o', f, mode) os.chmod(f, mode) - set_executable_mode = lambda s, f: s.set_mode(0o555, 0o7777, f) + def set_executable_mode(s, f): return s.set_mode(0o555, 0o7777, f) def ensure_dir(self, path): path = os.path.abspath(path) @@ -664,6 +675,7 @@ class FileOperator(object): os.rmdir(d) # should fail if non-empty self._init_record() + def resolve(module_name, dotted_path): if module_name in sys.modules: mod = sys.modules[module_name] @@ -679,7 +691,7 @@ def resolve(module_name, dotted_path): return result -class ExportEntry(object): +class ExportEntry: def __init__(self, name, prefix, suffix, flags): self.name = name self.prefix = prefix @@ -691,34 +703,43 @@ class ExportEntry(object): return resolve(self.prefix, self.suffix) def __repr__(self): # pragma: no cover - return '' % (self.name, self.prefix, - self.suffix, self.flags) + return ''.format( + self.name, self.prefix, + self.suffix, self.flags, + ) def __eq__(self, other): if not isinstance(other, ExportEntry): result = False else: - result = (self.name == other.name and - self.prefix == other.prefix and - self.suffix == other.suffix and - self.flags == other.flags) + result = ( + self.name == other.name and + self.prefix == other.prefix and + self.suffix == other.suffix and + self.flags == other.flags + ) return result __hash__ = object.__hash__ -ENTRY_RE = re.compile(r'''(?P(\w|[-.+])+) +ENTRY_RE = re.compile( + r'''(?P(\w|[-.+])+) \s*=\s*(?P(\w+)([:\.]\w+)*) \s*(\[\s*(?P[\w-]+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])? - ''', re.VERBOSE) + ''', re.VERBOSE, +) + def get_export_entry(specification): m = ENTRY_RE.search(specification) if not m: result = None if '[' in specification or ']' in specification: - raise DistlibException("Invalid specification " - "'%s'" % specification) + raise DistlibException( + 'Invalid specification ' + "'%s'" % specification, + ) else: d = m.groupdict() name = d['name'] @@ -728,14 +749,18 @@ def get_export_entry(specification): prefix, suffix = path, None else: if colons != 1: - raise DistlibException("Invalid specification " - "'%s'" % specification) + raise DistlibException( + 'Invalid specification ' + "'%s'" % specification, + ) prefix, suffix = path.split(':') flags = d['flags'] if flags is None: if '[' in specification or ']' in specification: - raise DistlibException("Invalid specification " - "'%s'" % specification) + raise DistlibException( + 'Invalid specification ' + "'%s'" % specification, + ) flags = [] else: flags = [f.strip() for f in flags.split(',')] @@ -827,6 +852,7 @@ def get_process_umask(): os.umask(result) return result + def is_string_sequence(seq): result = True i = None @@ -837,8 +863,11 @@ def is_string_sequence(seq): assert i is not None return result -PROJECT_NAME_AND_VERSION = re.compile('([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-' - '([a-z0-9_.+-]+)', re.I) + +PROJECT_NAME_AND_VERSION = re.compile( + '([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-' + '([a-z0-9_.+-]+)', re.I, +) PYTHON_VERSION = re.compile(r'-py(\d\.?\d?)') @@ -866,9 +895,13 @@ def split_filename(filename, project_name=None): result = m.group(1), m.group(3), pyver return result + # Allow spaces in name because of legacy dists like "Twisted Core" -NAME_VERSION_RE = re.compile(r'(?P[\w .-]+)\s*' - r'\(\s*(?P[^\s)]+)\)$') +NAME_VERSION_RE = re.compile( + r'(?P[\w .-]+)\s*' + r'\(\s*(?P[^\s)]+)\)$', +) + def parse_name_and_version(p): """ @@ -885,6 +918,7 @@ def parse_name_and_version(p): d = m.groupdict() return d['name'].strip().lower(), d['ver'] + def get_extras(requested, available): result = set() requested = set(requested or []) @@ -910,6 +944,7 @@ def get_extras(requested, available): # Extended metadata functionality # + def _get_external_data(url): result = {} try: @@ -930,21 +965,24 @@ def _get_external_data(url): logger.exception('Failed to get external data for %s: %s', url, e) return result + _external_data_base_url = 'https://www.red-dove.com/pypi/projects/' + def get_project_data(name): - url = '%s/%s/project.json' % (name[0].upper(), name) + url = '{}/{}/project.json'.format(name[0].upper(), name) url = urljoin(_external_data_base_url, url) result = _get_external_data(url) return result + def get_package_data(name, version): - url = '%s/%s/package-%s.json' % (name[0].upper(), name, version) + url = '{}/{}/package-{}.json'.format(name[0].upper(), name, version) url = urljoin(_external_data_base_url, url) return _get_external_data(url) -class Cache(object): +class Cache: """ A class implementing a cache for resources that need to live in the file system e.g. shared libraries. This class was moved from resources to here because it @@ -988,10 +1026,11 @@ class Cache(object): return not_removed -class EventMixin(object): +class EventMixin: """ A very simple publish/subscribe system. """ + def __init__(self): self._subscribers = {} @@ -1053,14 +1092,18 @@ class EventMixin(object): logger.exception('Exception during event publication') value = None result.append(value) - logger.debug('publish %s: args = %s, kwargs = %s, result = %s', - event, args, kwargs, result) + logger.debug( + 'publish %s: args = %s, kwargs = %s, result = %s', + event, args, kwargs, result, + ) return result # # Simple sequencing # -class Sequencer(object): + + +class Sequencer: def __init__(self): self._preds = {} self._succs = {} @@ -1101,11 +1144,13 @@ class Sequencer(object): preds.remove(pred) succs.remove(succ) except KeyError: # pragma: no cover - raise ValueError('%r not a successor of %r' % (succ, pred)) + raise ValueError('{!r} not a successor of {!r}'.format(succ, pred)) def is_step(self, step): - return (step in self._preds or step in self._succs or - step in self._nodes) + return ( + step in self._preds or step in self._succs or + step in self._nodes + ) def get_steps(self, final): if not self.is_step(final): @@ -1159,11 +1204,11 @@ class Sequencer(object): if successor not in lowlinks: # Successor has not yet been visited strongconnect(successor) - lowlinks[node] = min(lowlinks[node],lowlinks[successor]) + lowlinks[node] = min(lowlinks[node], lowlinks[successor]) elif successor in stack: # the successor is in the stack and hence in the current # strongly connected component (SCC) - lowlinks[node] = min(lowlinks[node],index[successor]) + lowlinks[node] = min(lowlinks[node], index[successor]) # If `node` is a root node, pop the stack and generate an SCC if lowlinks[node] == index[node]: @@ -1172,7 +1217,8 @@ class Sequencer(object): while True: successor = stack.pop() connected_component.append(successor) - if successor == node: break + if successor == node: + break component = tuple(connected_component) # storing the result result.append(component) @@ -1189,7 +1235,7 @@ class Sequencer(object): for succ in self._preds: preds = self._preds[succ] for pred in preds: - result.append(' %s -> %s;' % (pred, succ)) + result.append(' {} -> {};'.format(pred, succ)) for node in self._nodes: result.append(' %s;' % node) result.append('}') @@ -1199,8 +1245,12 @@ class Sequencer(object): # Unarchiving functionality for zip, tar, tgz, tbz, whl # -ARCHIVE_EXTENSIONS = ('.tar.gz', '.tar.bz2', '.tar', '.zip', - '.tgz', '.tbz', '.whl') + +ARCHIVE_EXTENSIONS = ( + '.tar.gz', '.tar.bz2', '.tar', '.zip', + '.tgz', '.tbz', '.whl', +) + def unarchive(archive_filename, dest_dir, format=None, check=True): @@ -1260,7 +1310,7 @@ def zip_dir(directory): """zip a directory tree into a BytesIO object""" result = io.BytesIO() dlen = len(directory) - with ZipFile(result, "w") as zf: + with ZipFile(result, 'w') as zf: for root, dirs, files in os.walk(directory): for name in files: full = os.path.join(root, name) @@ -1273,10 +1323,11 @@ def zip_dir(directory): # Simple progress bar # -UNITS = ('', 'K', 'M', 'G','T','P') + +UNITS = ('', 'K', 'M', 'G', 'T', 'P') -class Progress(object): +class Progress: unknown = 'UNKNOWN' def __init__(self, minval=0, maxval=100): @@ -1351,7 +1402,7 @@ class Progress(object): t = float(self.max - self.min) t /= self.cur - self.min t = (t - 1) * self.elapsed - return '%s: %s' % (prefix, self.format_duration(t)) + return '{}: {}'.format(prefix, self.format_duration(t)) @property def speed(self): @@ -1369,6 +1420,7 @@ class Progress(object): # Glob functionality # + RICH_GLOB = re.compile(r'\{([^}]*)\}') _CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]') _CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$') @@ -1409,21 +1461,24 @@ def _iglob(path_glob): radical = radical.lstrip('\\') for path, dir, files in os.walk(prefix): path = os.path.normpath(path) - for fn in _iglob(os.path.join(path, radical)): - yield fn + yield from _iglob(os.path.join(path, radical)) + if ssl: - from .compat import (HTTPSHandler as BaseHTTPSHandler, match_hostname, - CertificateError) + from .compat import ( + HTTPSHandler as BaseHTTPSHandler, match_hostname, + CertificateError, + ) # # HTTPSConnection which verifies certificates/matches domains # + class HTTPSConnection(httplib.HTTPSConnection): - ca_certs = None # set this to the path to the certs file (.pem) - check_domain = True # only used if ca_certs is not None + ca_certs = None # set this to the path to the certs file (.pem) + check_domain = True # only used if ca_certs is not None # noinspection PyPropertyAccess def connect(self): @@ -1438,10 +1493,12 @@ if ssl: cert_reqs = ssl.CERT_REQUIRED else: cert_reqs = ssl.CERT_NONE - self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, - cert_reqs=cert_reqs, - ssl_version=ssl.PROTOCOL_SSLv23, - ca_certs=self.ca_certs) + self.sock = ssl.wrap_socket( + sock, self.key_file, self.cert_file, + cert_reqs=cert_reqs, + ssl_version=ssl.PROTOCOL_SSLv23, + ca_certs=self.ca_certs, + ) else: # pragma: no cover context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) if hasattr(ssl, 'OP_NO_SSLv2'): @@ -1491,8 +1548,10 @@ if ssl: return self.do_open(self._conn_maker, req) except URLError as e: if 'certificate verify failed' in str(e.reason): - raise CertificateError('Unable to verify server certificate ' - 'for %s' % req.host) + raise CertificateError( + 'Unable to verify server certificate ' + 'for %s' % req.host, + ) else: raise @@ -1507,8 +1566,10 @@ if ssl: # class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler): def http_open(self, req): - raise URLError('Unexpected HTTP request on what should be a secure ' - 'connection: %s' % req) + raise URLError( + 'Unexpected HTTP request on what should be a secure ' + 'connection: %s' % req, + ) # # XML-RPC with timeouts @@ -1523,7 +1584,6 @@ if _ver_info == (2, 6): port = None self._setup(self._connection_class(host, port, **kwargs)) - if ssl: class HTTPS(httplib.HTTPS): def __init__(self, host='', port=None, **kwargs): @@ -1548,6 +1608,7 @@ class Transport(xmlrpclib.Transport): result = self._connection[1] return result + if ssl: class SafeTransport(xmlrpclib.SafeTransport): def __init__(self, timeout, use_datetime=0): @@ -1564,8 +1625,10 @@ if ssl: else: if not self._connection or host != self._connection[0]: self._extra_headers = eh - self._connection = host, httplib.HTTPSConnection(h, None, - **kwargs) + self._connection = host, httplib.HTTPSConnection( + h, None, + **kwargs, + ) result = self._connection[1] return result @@ -1592,6 +1655,7 @@ class ServerProxy(xmlrpclib.ServerProxy): # handle Unicode. However, we need to deal with Unicode in e.g. RECORD files. # + def _csv_open(fn, mode, **kwargs): if sys.version_info[0] < 3: mode += 'b' @@ -1603,11 +1667,11 @@ def _csv_open(fn, mode, **kwargs): return open(fn, mode, **kwargs) -class CSVBase(object): +class CSVBase: defaults = { - 'delimiter': str(','), # The strs are used because we need native - 'quotechar': str('"'), # str in the csv API (2.x won't take - 'lineterminator': str('\n') # Unicode) + 'delimiter': ',', # The strs are used because we need native + 'quotechar': '"', # str in the csv API (2.x won't take + 'lineterminator': '\n', # Unicode) } def __enter__(self): @@ -1642,6 +1706,7 @@ class CSVReader(CSVBase): __next__ = next + class CSVWriter(CSVBase): def __init__(self, fn, **kwargs): self.stream = _csv_open(fn, 'w') @@ -1661,13 +1726,14 @@ class CSVWriter(CSVBase): # Configurator functionality # + class Configurator(BaseConfigurator): value_converters = dict(BaseConfigurator.value_converters) value_converters['inc'] = 'inc_convert' def __init__(self, config, base=None): - super(Configurator, self).__init__(config) + super().__init__(config) self.base = base or os.getcwd() def configure_custom(self, config): @@ -1716,10 +1782,11 @@ class Configurator(BaseConfigurator): return result -class SubprocessMixin(object): +class SubprocessMixin: """ Mixin for running subprocesses and capturing their output """ + def __init__(self, verbose=False, progress=None): self.verbose = verbose self.progress = progress @@ -1746,8 +1813,10 @@ class SubprocessMixin(object): stream.close() def run_command(self, cmd, **kwargs): - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, **kwargs) + p = subprocess.Popen( + cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, **kwargs, + ) t1 = threading.Thread(target=self.reader, args=(p.stdout, 'stdout')) t1.start() t2 = threading.Thread(target=self.reader, args=(p.stderr, 'stderr')) @@ -1777,7 +1846,8 @@ def normalize_name(name): # d = Distribution() # return PyPIRCCommand(d) -class PyPIRCFile(object): + +class PyPIRCFile: DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' DEFAULT_REALM = 'pypi' @@ -1800,9 +1870,11 @@ class PyPIRCFile(object): if 'distutils' in sections: # let's get the list of servers index_servers = config.get('distutils', 'index-servers') - _servers = [server.strip() for server in - index_servers.split('\n') - if server.strip() != ''] + _servers = [ + server.strip() for server in + index_servers.split('\n') + if server.strip() != '' + ] if _servers == []: # nothing set, let's try to get the default pypi if 'pypi' in sections: @@ -1813,9 +1885,11 @@ class PyPIRCFile(object): result['username'] = config.get(server, 'username') # optional params - for key, default in (('repository', self.DEFAULT_REPOSITORY), - ('realm', self.DEFAULT_REALM), - ('password', None)): + for key, default in ( + ('repository', self.DEFAULT_REPOSITORY), + ('realm', self.DEFAULT_REALM), + ('password', None), + ): if config.has_option(server, key): result[key] = config.get(server, key) else: @@ -1824,11 +1898,15 @@ class PyPIRCFile(object): # work around people having "repository" for the "pypi" # section of their config set to the HTTP (rather than # HTTPS) URL - if (server == 'pypi' and - repository in (self.DEFAULT_REPOSITORY, 'pypi')): + if ( + server == 'pypi' and + repository in (self.DEFAULT_REPOSITORY, 'pypi') + ): result['repository'] = self.DEFAULT_REPOSITORY - elif (result['server'] != repository and - result['repository'] != repository): + elif ( + result['server'] != repository and + result['repository'] != repository + ): result = {} elif 'server-login' in sections: # old format @@ -1842,7 +1920,7 @@ class PyPIRCFile(object): 'password': config.get(server, 'password'), 'repository': repository, 'server': server, - 'realm': self.DEFAULT_REALM + 'realm': self.DEFAULT_REALM, } return result @@ -1858,12 +1936,14 @@ class PyPIRCFile(object): with open(fn, 'w') as f: config.write(f) + def _load_pypirc(index): """ Read the PyPI access configuration as supported by distutils. """ return PyPIRCFile(url=index.url).read() + def _store_pypirc(index): PyPIRCFile().update(index.username, index.password) @@ -1872,6 +1952,7 @@ def _store_pypirc(index): # tweaks # + def get_host_platform(): """Return a string that identifies the current platform. This is used mainly to distinguish platform-specific build directories and platform-specific built @@ -1902,8 +1983,8 @@ def get_host_platform(): return sys.platform # Set for cross builds explicitly - if "_PYTHON_HOST_PLATFORM" in os.environ: - return os.environ["_PYTHON_HOST_PLATFORM"] + if '_PYTHON_HOST_PLATFORM' in os.environ: + return os.environ['_PYTHON_HOST_PLATFORM'] if os.name != 'posix' or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, @@ -1923,7 +2004,7 @@ def get_host_platform(): # At least on Linux/Intel, 'machine' is the processor -- # i386, etc. # XXX what about Alpha, SPARC, etc? - return "%s-%s" % (osname, machine) + return '{}-{}'.format(osname, machine) elif osname[:5] == 'sunos': if release[0] >= '5': # SunOS 5 == Solaris 2 @@ -1932,7 +2013,7 @@ def get_host_platform(): # We can't use 'platform.architecture()[0]' because a # bootstrap problem. We use a dict to get an error # if some suspicious happens. - bitness = {2147483647:'32bit', 9223372036854775807:'64bit'} + bitness = {2147483647: '32bit', 9223372036854775807: '64bit'} machine += '.%s' % bitness[sys.maxsize] # fall through to standard osname-release-machine representation elif osname[:3] == 'aix': @@ -1940,23 +2021,25 @@ def get_host_platform(): return aix_platform() elif osname[:6] == 'cygwin': osname = 'cygwin' - rel_re = re.compile (r'[\d.]+', re.ASCII) + rel_re = re.compile(r'[\d.]+', re.ASCII) m = rel_re.match(release) if m: release = m.group() elif osname[:6] == 'darwin': - import _osx_support, distutils.sysconfig + import _osx_support + import distutils.sysconfig osname, release, machine = _osx_support.get_platform_osx( - distutils.sysconfig.get_config_vars(), - osname, release, machine) + distutils.sysconfig.get_config_vars(), + osname, release, machine, + ) - return '%s-%s-%s' % (osname, release, machine) + return '{}-{}-{}'.format(osname, release, machine) _TARGET_TO_PLAT = { - 'x86' : 'win32', - 'x64' : 'win-amd64', - 'arm' : 'win-arm32', + 'x86': 'win32', + 'x64': 'win-amd64', + 'arm': 'win-arm32', } diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/version.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/version.py index c7c8bb6..3bb19c2 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/version.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/version.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2012-2017 The Python Software Foundation. # See LICENSE.txt and CONTRIBUTORS.txt. @@ -7,6 +6,7 @@ Implementation of a flexible versioning scheme providing support for PEP-440, setuptools-compatible and semantic versioning. """ +from __future__ import annotations import logging import re @@ -14,10 +14,12 @@ import re from .compat import string_types from .util import parse_requirement -__all__ = ['NormalizedVersion', 'NormalizedMatcher', - 'LegacyVersion', 'LegacyMatcher', - 'SemanticVersion', 'SemanticMatcher', - 'UnsupportedVersionError', 'get_scheme'] +__all__ = [ + 'NormalizedVersion', 'NormalizedMatcher', + 'LegacyVersion', 'LegacyMatcher', + 'SemanticVersion', 'SemanticMatcher', + 'UnsupportedVersionError', 'get_scheme', +] logger = logging.getLogger(__name__) @@ -27,7 +29,7 @@ class UnsupportedVersionError(ValueError): pass -class Version(object): +class Version: def __init__(self, s): self._string = s = s.strip() self._parts = parts = self.parse(s) @@ -39,7 +41,7 @@ class Version(object): def _check_compatible(self, other): if type(self) != type(other): - raise TypeError('cannot compare %r and %r' % (self, other)) + raise TypeError('cannot compare {!r} and {!r}'.format(self, other)) def __eq__(self, other): self._check_compatible(other) @@ -66,7 +68,7 @@ class Version(object): return hash(self._parts) def __repr__(self): - return "%s('%s')" % (self.__class__.__name__, self._string) + return "{}('{}')".format(self.__class__.__name__, self._string) def __str__(self): return self._string @@ -76,7 +78,7 @@ class Version(object): raise NotImplementedError('Please implement in subclasses.') -class Matcher(object): +class Matcher: version_class = None # value is either a callable or the name of a method @@ -112,8 +114,10 @@ class Matcher(object): for op, s in r.constraints: if s.endswith('.*'): if op not in ('==', '!='): - raise ValueError('\'.*\' not allowed for ' - '%r constraints' % op) + raise ValueError( + '\'.*\' not allowed for ' + '%r constraints' % op, + ) # Could be a partial version (e.g. for '2.*') which # won't parse as a version, so keep it as a string vn, prefix = s[:-2], True @@ -140,8 +144,10 @@ class Matcher(object): if isinstance(f, string_types): f = getattr(self, f) if not f: - msg = ('%r not implemented ' - 'for %s' % (operator, self.__class__.__name__)) + msg = ( + '%r not implemented ' + 'for %s' % (operator, self.__class__.__name__) + ) raise NotImplementedError(msg) if not f(version, constraint, prefix): return False @@ -156,7 +162,7 @@ class Matcher(object): def _check_compatible(self, other): if type(self) != type(other) or self.name != other.name: - raise TypeError('cannot compare %s and %s' % (self, other)) + raise TypeError('cannot compare {} and {}'.format(self, other)) def __eq__(self, other): self._check_compatible(other) @@ -170,15 +176,17 @@ class Matcher(object): return hash(self.key) + hash(self._parts) def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self._string) + return '{}({!r})'.format(self.__class__.__name__, self._string) def __str__(self): return self._string -PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|b|c|rc)(\d+))?' - r'(\.(post)(\d+))?(\.(dev)(\d+))?' - r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$') +PEP440_VERSION_RE = re.compile( + r'^v?(\d+!)?(\d+(\.\d+)*)((a|b|c|rc)(\d+))?' + r'(\.(post)(\d+))?(\.(dev)(\d+))?' + r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$', +) def _pep_440_key(s): @@ -263,6 +271,7 @@ class NormalizedVersion(Version): 1.2a # release level must have a release serial 1.2.3b """ + def parse(self, s): result = _normalized_key(s) # _normalized_key loses trailing zeroes in the release @@ -274,7 +283,7 @@ class NormalizedVersion(Version): self._release_clause = tuple(int(v) for v in groups[1].split('.')) return result - PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev']) + PREREL_TAGS = {'a', 'b', 'c', 'rc', 'dev'} @property def is_prerelease(self): @@ -378,6 +387,7 @@ class NormalizedMatcher(Matcher): pfx = '.'.join([str(i) for i in release_clause]) return _match_prefix(version, pfx) + _REPLACEMENTS = ( (re.compile('[.+-]$'), ''), # remove trailing puncts (re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start @@ -387,8 +397,10 @@ _REPLACEMENTS = ( (re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion) (re.compile('[.]{2,}'), '.'), # multiple runs of '.' (re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha - (re.compile(r'\b(pre-alpha|prealpha)\b'), - 'pre.alpha'), # standardise + ( + re.compile(r'\b(pre-alpha|prealpha)\b'), + 'pre.alpha', + ), # standardise (re.compile(r'\(beta\)$'), 'beta'), # remove parentheses ) @@ -475,29 +487,31 @@ def _suggest_normalized_version(s): rs = s.lower() # part of this could use maketrans - for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'), - ('beta', 'b'), ('rc', 'c'), ('-final', ''), - ('-pre', 'c'), - ('-release', ''), ('.release', ''), ('-stable', ''), - ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''), - ('final', '')): + for orig, repl in ( + ('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'), + ('beta', 'b'), ('rc', 'c'), ('-final', ''), + ('-pre', 'c'), + ('-release', ''), ('.release', ''), ('-stable', ''), + ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''), + ('final', ''), + ): rs = rs.replace(orig, repl) # if something ends with dev or pre, we add a 0 - rs = re.sub(r"pre$", r"pre0", rs) - rs = re.sub(r"dev$", r"dev0", rs) + rs = re.sub(r'pre$', r'pre0', rs) + rs = re.sub(r'dev$', r'dev0', rs) # if we have something like "b-2" or "a.2" at the end of the # version, that is probably beta, alpha, etc # let's remove the dash or dot - rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs) + rs = re.sub(r'([abc]|rc)[\-\.](\d+)$', r'\1\2', rs) # 1.0-dev-r371 -> 1.0.dev371 # 0.1-dev-r79 -> 0.1.dev79 - rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs) + rs = re.sub(r'[\-\.](dev)[\-\.]?r?(\d+)$', r'.\1\2', rs) # Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1 - rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs) + rs = re.sub(r'[.~]?([abc])\.?', r'\1', rs) # Clean: v0.3, v1.0 if rs.startswith('v'): @@ -506,33 +520,33 @@ def _suggest_normalized_version(s): # Clean leading '0's on numbers. #TODO: unintended side-effect on, e.g., "2003.05.09" # PyPI stats: 77 (~2%) better - rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs) + rs = re.sub(r'\b0+(\d+)(?!\d)', r'\1', rs) # Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers # zero. # PyPI stats: 245 (7.56%) better - rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs) + rs = re.sub(r'(\d+[abc])$', r'\g<1>0', rs) # the 'dev-rNNN' tag is a dev tag - rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs) + rs = re.sub(r'\.?(dev-r|dev\.r)\.?(\d+)$', r'.dev\2', rs) # clean the - when used as a pre delimiter - rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs) + rs = re.sub(r'-(a|b|c)(\d+)$', r'\1\2', rs) # a terminal "dev" or "devel" can be changed into ".dev0" - rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs) + rs = re.sub(r'[\.\-](dev|devel)$', r'.dev0', rs) # a terminal "dev" can be changed into ".dev0" - rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs) + rs = re.sub(r'(?![\.\-])dev$', r'.dev0', rs) # a terminal "final" or "stable" can be removed - rs = re.sub(r"(final|stable)$", "", rs) + rs = re.sub(r'(final|stable)$', '', rs) # The 'r' and the '-' tags are post release tags # 0.4a1.r10 -> 0.4a1.post10 # 0.9.33-17222 -> 0.9.33.post17222 # 0.9.33-r17222 -> 0.9.33.post17222 - rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs) + rs = re.sub(r'\.?(r|-|-r)\.?(\d+)$', r'.post\2', rs) # Clean 'r' instead of 'dev' usage: # 0.9.33+r17222 -> 0.9.33.dev17222 @@ -541,17 +555,17 @@ def _suggest_normalized_version(s): # 1.0.bzr123 -> 1.0.dev123 # 0.1a0dev.123 -> 0.1a0.dev123 # PyPI stats: ~150 (~4%) better - rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs) + rs = re.sub(r'\.?(dev|git|bzr)\.?(\d+)$', r'.dev\2', rs) # Clean '.pre' (normalized from '-pre' above) instead of 'c' usage: # 0.2.pre1 -> 0.2c1 # 0.2-c1 -> 0.2c1 # 1.0preview123 -> 1.0c123 # PyPI stats: ~21 (0.62%) better - rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) + rs = re.sub(r'\.?(pre|preview|-c)(\d+)$', r'c\g<2>', rs) # Tcl/Tk uses "px" for their post release markers - rs = re.sub(r"p(\d+)$", r".post\1", rs) + rs = re.sub(r'p(\d+)$', r'.post\1', rs) try: _normalized_key(rs) @@ -563,6 +577,7 @@ def _suggest_normalized_version(s): # Legacy version processing (distribute-compatible) # + _VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I) _VERSION_REPLACE = { 'pre': 'c', @@ -609,8 +624,10 @@ class LegacyVersion(Version): def is_prerelease(self): result = False for x in self._parts: - if (isinstance(x, string_types) and x.startswith('*') and - x < '*final'): + if ( + isinstance(x, string_types) and x.startswith('*') and + x < '*final' + ): result = True break return result @@ -629,8 +646,10 @@ class LegacyMatcher(Matcher): return False m = self.numeric_re.match(str(constraint)) if not m: - logger.warning('Cannot compute compatible match for version %s ' - ' and constraint %s', version, constraint) + logger.warning( + 'Cannot compute compatible match for version %s ' + ' and constraint %s', version, constraint, + ) return True s = m.groups()[0] if '.' in s: @@ -641,9 +660,12 @@ class LegacyMatcher(Matcher): # Semantic versioning # -_SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)' - r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?' - r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I) + +_SEMVER_RE = re.compile( + r'^(\d+)\.(\d+)\.(\d+)' + r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?' + r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I, +) def is_semver(s): @@ -665,7 +687,7 @@ def _semantic_key(s): if not m: raise UnsupportedVersionError(s) groups = m.groups() - major, minor, patch = [int(i) for i in groups[:3]] + major, minor, patch = (int(i) for i in groups[:3]) # choose the '|' and '*' so that versions sort correctly pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*') return (major, minor, patch), pre, build @@ -684,7 +706,7 @@ class SemanticMatcher(Matcher): version_class = SemanticVersion -class VersionScheme(object): +class VersionScheme: def __init__(self, key, matcher, suggester=None): self.key = key self.matcher = matcher @@ -722,12 +744,17 @@ class VersionScheme(object): result = self.suggester(s) return result + _SCHEMES = { - 'normalized': VersionScheme(_normalized_key, NormalizedMatcher, - _suggest_normalized_version), + 'normalized': VersionScheme( + _normalized_key, NormalizedMatcher, + _suggest_normalized_version, + ), 'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s), - 'semantic': VersionScheme(_semantic_key, SemanticMatcher, - _suggest_semantic_version), + 'semantic': VersionScheme( + _semantic_key, SemanticMatcher, + _suggest_semantic_version, + ), } _SCHEMES['default'] = _SCHEMES['normalized'] diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/wheel.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/wheel.py index 48abfde..13410e6 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/wheel.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distlib/wheel.py @@ -1,15 +1,13 @@ -# -*- coding: utf-8 -*- # # Copyright (C) 2013-2020 Vinay Sajip. # Licensed to the Python Software Foundation under a contributor agreement. # See LICENSE.txt and CONTRIBUTORS.txt. # -from __future__ import unicode_literals +from __future__ import annotations import base64 import codecs import datetime -from email import message_from_file import hashlib import imp import json @@ -21,16 +19,32 @@ import shutil import sys import tempfile import zipfile +from email import message_from_file -from . import __version__, DistlibException -from .compat import sysconfig, ZipFile, fsdecode, text_type, filter +from . import __version__ +from . import DistlibException +from .compat import filter +from .compat import fsdecode +from .compat import sysconfig +from .compat import text_type +from .compat import ZipFile from .database import InstalledDistribution -from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, - LEGACY_METADATA_FILENAME) -from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache, - cached_property, get_cache_base, read_exports, tempdir, - get_platform) -from .version import NormalizedVersion, UnsupportedVersionError +from .metadata import LEGACY_METADATA_FILENAME +from .metadata import Metadata +from .metadata import METADATA_FILENAME +from .metadata import WHEEL_METADATA_FILENAME +from .util import Cache +from .util import cached_property +from .util import convert_path +from .util import CSVReader +from .util import CSVWriter +from .util import FileOperator +from .util import get_cache_base +from .util import get_platform +from .util import read_exports +from .util import tempdir +from .version import NormalizedVersion +from .version import UnsupportedVersionError logger = logging.getLogger(__name__) @@ -69,7 +83,8 @@ else: ABI = _derive_abi() del _derive_abi -FILENAME_RE = re.compile(r''' +FILENAME_RE = re.compile( + r''' (?P[^-]+) -(?P\d+[^-]*) (-(?P\d+[^-]*))? @@ -77,13 +92,16 @@ FILENAME_RE = re.compile(r''' -(?P\w+) -(?P\w+(\.\w+)*) \.whl$ -''', re.IGNORECASE | re.VERBOSE) +''', re.IGNORECASE | re.VERBOSE, +) -NAME_VERSION_RE = re.compile(r''' +NAME_VERSION_RE = re.compile( + r''' (?P[^-]+) -(?P\d+[^-]*) (-(?P\d+[^-]*))?$ -''', re.IGNORECASE | re.VERBOSE) +''', re.IGNORECASE | re.VERBOSE, +) SHEBANG_RE = re.compile(br'\s*#![^\r\n]*') SHEBANG_DETAIL_RE = re.compile(br'^(\s*#!("[^"]+"|\S+))\s+(.*)$') @@ -91,12 +109,12 @@ SHEBANG_PYTHON = b'#!python' SHEBANG_PYTHONW = b'#!pythonw' if os.sep == '/': - to_posix = lambda o: o + def to_posix(o): return o else: - to_posix = lambda o: o.replace(os.sep, '/') + def to_posix(o): return o.replace(os.sep, '/') -class Mounter(object): +class Mounter: def __init__(self): self.impure_wheels = {} self.libs = {} @@ -131,10 +149,11 @@ class Mounter(object): result.__package__ = parts[0] return result + _hook = Mounter() -class Wheel(object): +class Wheel: """ Class to build and install from Wheel files (PEP 427). """ @@ -170,8 +189,10 @@ class Wheel(object): dirname, filename = os.path.split(filename) m = FILENAME_RE.match(filename) if not m: - raise DistlibException('Invalid name or ' - 'filename: %r' % filename) + raise DistlibException( + 'Invalid name or ' + 'filename: %r' % filename, + ) if dirname: self.dirname = os.path.abspath(dirname) self._filename = filename @@ -197,8 +218,10 @@ class Wheel(object): arch = '.'.join(self.arch) # replace - with _ as a local version separator version = self.version.replace('-', '_') - return '%s-%s%s-%s-%s-%s.whl' % (self.name, version, buildver, - pyver, abi, arch) + return '{}-{}{}-{}-{}-{}.whl'.format( + self.name, version, buildver, + pyver, abi, arch, + ) @property def exists(self): @@ -215,7 +238,7 @@ class Wheel(object): @cached_property def metadata(self): pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) + name_ver = '{}-{}'.format(self.name, self.version) info_dir = '%s.dist-info' % name_ver wrapper = codecs.getreader('utf-8') with ZipFile(pathname, 'r') as zf: @@ -223,10 +246,10 @@ class Wheel(object): wv = wheel_metadata['Wheel-Version'].split('.', 1) file_version = tuple([int(i) for i in wv]) # if file_version < (1, 1): - # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, - # LEGACY_METADATA_FILENAME] + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, + # LEGACY_METADATA_FILENAME] # else: - # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] fns = [WHEEL_METADATA_FILENAME, LEGACY_METADATA_FILENAME] result = None for fn in fns: @@ -240,12 +263,14 @@ class Wheel(object): except KeyError: pass if not result: - raise ValueError('Invalid wheel, because metadata is ' - 'missing: looked in %s' % ', '.join(fns)) + raise ValueError( + 'Invalid wheel, because metadata is ' + 'missing: looked in %s' % ', '.join(fns), + ) return result def get_wheel_metadata(self, zf): - name_ver = '%s-%s' % (self.name, self.version) + name_ver = '{}-{}'.format(self.name, self.version) info_dir = '%s.dist-info' % name_ver metadata_filename = posixpath.join(info_dir, 'WHEEL') with zf.open(metadata_filename) as bf: @@ -302,7 +327,7 @@ class Wheel(object): return hash_kind, result def write_record(self, records, record_path, base): - records = list(records) # make a copy, as mutated + records = list(records) # make a copy, as mutated p = to_posix(os.path.relpath(record_path, base)) records.append((p, '', '')) with CSVWriter(record_path) as writer: @@ -357,7 +382,7 @@ class Wheel(object): libdir = paths[libkey] - name_ver = '%s-%s' % (self.name, self.version) + name_ver = '{}-{}'.format(self.name, self.version) data_dir = '%s.data' % name_ver info_dir = '%s.dist-info' % name_ver @@ -420,7 +445,7 @@ class Wheel(object): 'Root-Is-Purelib: %s' % is_pure, ] for pyver, abi, arch in self.tags: - wheel_metadata.append('Tag: %s-%s-%s' % (pyver, abi, arch)) + wheel_metadata.append('Tag: {}-{}-{}'.format(pyver, abi, arch)) p = os.path.join(distinfo, 'WHEEL') with open(p, 'w') as f: f.write('\n'.join(wheel_metadata)) @@ -486,7 +511,7 @@ class Wheel(object): bc_hashed_invalidation = kwargs.get('bytecode_hashed_invalidation', False) pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) + name_ver = '{}-{}'.format(self.name, self.version) data_dir = '%s.data' % name_ver info_dir = '%s.dist-info' % name_ver @@ -548,22 +573,28 @@ class Wheel(object): continue row = records[u_arcname] if row[2] and str(zinfo.file_size) != row[2]: - raise DistlibException('size mismatch for ' - '%s' % u_arcname) + raise DistlibException( + 'size mismatch for ' + '%s' % u_arcname, + ) if row[1]: kind, value = row[1].split('=', 1) with zf.open(arcname) as bf: data = bf.read() _, digest = self.get_hash(data, kind) if digest != value: - raise DistlibException('digest mismatch for ' - '%s' % arcname) + raise DistlibException( + 'digest mismatch for ' + '%s' % arcname, + ) if lib_only and u_arcname.startswith((info_pfx, data_pfx)): logger.debug('lib_only: skipping %s', u_arcname) continue - is_script = (u_arcname.startswith(script_pfx) - and not u_arcname.endswith('.exe')) + is_script = ( + u_arcname.startswith(script_pfx) and + not u_arcname.endswith('.exe') + ) if u_arcname.startswith(data_pfx): _, where, rp = u_arcname.split('/', 2) @@ -590,19 +621,25 @@ class Wheel(object): data = bf.read() _, newdigest = self.get_hash(data, kind) if newdigest != digest: - raise DistlibException('digest mismatch ' - 'on write for ' - '%s' % outfile) + raise DistlibException( + 'digest mismatch ' + 'on write for ' + '%s' % outfile, + ) if bc and outfile.endswith('.py'): try: - pyc = fileop.byte_compile(outfile, - hashed_invalidation=bc_hashed_invalidation) + pyc = fileop.byte_compile( + outfile, + hashed_invalidation=bc_hashed_invalidation, + ) outfiles.append(pyc) except Exception: # Don't give up if byte-compilation fails, # but log it and perhaps warn the user - logger.warning('Byte-compilation failed', - exc_info=True) + logger.warning( + 'Byte-compilation failed', + exc_info=True, + ) else: fn = os.path.basename(convert_path(arcname)) workname = os.path.join(workdir, fn) @@ -638,14 +675,16 @@ class Wheel(object): if k in epdata: commands['wrap_%s' % key] = d = {} for v in epdata[k].values(): - s = '%s:%s' % (v.prefix, v.suffix) + s = '{}:{}'.format(v.prefix, v.suffix) if v.flags: s += ' [%s]' % ','.join(v.flags) d[v.name] = s except Exception: - logger.warning('Unable to read legacy script ' - 'metadata, so cannot generate ' - 'scripts') + logger.warning( + 'Unable to read legacy script ' + 'metadata, so cannot generate ' + 'scripts', + ) else: try: with zf.open(metadata_name) as bwf: @@ -654,26 +693,30 @@ class Wheel(object): if commands: commands = commands.get('python.commands') except Exception: - logger.warning('Unable to read JSON metadata, so ' - 'cannot generate scripts') + logger.warning( + 'Unable to read JSON metadata, so ' + 'cannot generate scripts', + ) if commands: console_scripts = commands.get('wrap_console', {}) gui_scripts = commands.get('wrap_gui', {}) if console_scripts or gui_scripts: script_dir = paths.get('scripts', '') if not os.path.isdir(script_dir): - raise ValueError('Valid script path not ' - 'specified') + raise ValueError( + 'Valid script path not ' + 'specified', + ) maker.target_dir = script_dir for k, v in console_scripts.items(): - script = '%s = %s' % (k, v) + script = '{} = {}'.format(k, v) filenames = maker.make(script) fileop.set_executable_mode(filenames) if gui_scripts: - options = {'gui': True } + options = {'gui': True} for k, v in gui_scripts.items(): - script = '%s = %s' % (k, v) + script = '{} = {}'.format(k, v) filenames = maker.make(script, options) fileop.set_executable_mode(filenames) @@ -690,8 +733,10 @@ class Wheel(object): outfiles.append(p) # Write RECORD - dist.write_installed_files(outfiles, paths['prefix'], - dry_run) + dist.write_installed_files( + outfiles, paths['prefix'], + dry_run, + ) return dist except Exception: # pragma: no cover logger.exception('installation failed.') @@ -704,14 +749,16 @@ class Wheel(object): global cache if cache is None: # Use native string to avoid issues on 2.x: see Python #20140. - base = os.path.join(get_cache_base(), str('dylib-cache'), - '%s.%s' % sys.version_info[:2]) + base = os.path.join( + get_cache_base(), 'dylib-cache', + '%s.%s' % sys.version_info[:2], + ) cache = Cache(base) return cache def _get_extensions(self): pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) + name_ver = '{}-{}'.format(self.name, self.version) info_dir = '%s.dist-info' % name_ver arcname = posixpath.join(info_dir, 'EXTENSIONS') wrapper = codecs.getreader('utf-8') @@ -753,7 +800,7 @@ class Wheel(object): """ Determine if a wheel is asserted as mountable by its metadata. """ - return True # for now - metadata details TBD + return True # for now - metadata details TBD def mount(self, append=False): pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) @@ -790,7 +837,7 @@ class Wheel(object): def verify(self): pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) + name_ver = '{}-{}'.format(self.name, self.version) data_dir = '%s.data' % name_ver info_dir = '%s.dist-info' % name_ver @@ -826,23 +873,29 @@ class Wheel(object): # updated to look for .. in the directory portions p = u_arcname.split('/') if '..' in p: - raise DistlibException('invalid entry in ' - 'wheel: %r' % u_arcname) + raise DistlibException( + 'invalid entry in ' + 'wheel: %r' % u_arcname, + ) if self.skip_entry(u_arcname): continue row = records[u_arcname] if row[2] and str(zinfo.file_size) != row[2]: - raise DistlibException('size mismatch for ' - '%s' % u_arcname) + raise DistlibException( + 'size mismatch for ' + '%s' % u_arcname, + ) if row[1]: kind, value = row[1].split('=', 1) with zf.open(arcname) as bf: data = bf.read() _, digest = self.get_hash(data, kind) if digest != value: - raise DistlibException('digest mismatch for ' - '%s' % arcname) + raise DistlibException( + 'digest mismatch for ' + '%s' % arcname, + ) def update(self, modifier, dest_dir=None, **kwargs): """ @@ -863,7 +916,7 @@ class Wheel(object): def get_version(path_map, info_dir): version = path = None - key = '%s/%s' % (info_dir, LEGACY_METADATA_FILENAME) + key = '{}/{}'.format(info_dir, LEGACY_METADATA_FILENAME) if key not in path_map: key = '%s/PKG-INFO' % info_dir if key in path_map: @@ -881,21 +934,27 @@ class Wheel(object): else: parts = [int(s) for s in version[i + 1:].split('.')] parts[-1] += 1 - updated = '%s+%s' % (version[:i], - '.'.join(str(i) for i in parts)) + updated = '{}+{}'.format( + version[:i], + '.'.join(str(i) for i in parts), + ) except UnsupportedVersionError: - logger.debug('Cannot update non-compliant (PEP-440) ' - 'version %r', version) + logger.debug( + 'Cannot update non-compliant (PEP-440) ' + 'version %r', version, + ) if updated: md = Metadata(path=path) md.version = updated legacy = path.endswith(LEGACY_METADATA_FILENAME) md.write(path=path, legacy=legacy) - logger.debug('Version updated from %r to %r', version, - updated) + logger.debug( + 'Version updated from %r to %r', version, + updated, + ) pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) + name_ver = '{}-{}'.format(self.name, self.version) info_dir = '%s.dist-info' % name_ver record_name = posixpath.join(info_dir, 'RECORD') with tempdir() as workdir: @@ -910,8 +969,10 @@ class Wheel(object): if u_arcname == record_name: continue if '..' in u_arcname: - raise DistlibException('invalid entry in ' - 'wheel: %r' % u_arcname) + raise DistlibException( + 'invalid entry in ' + 'wheel: %r' % u_arcname, + ) zf.extract(zinfo, workdir) path = os.path.join(workdir, convert_path(u_arcname)) path_map[u_arcname] = path @@ -928,9 +989,11 @@ class Wheel(object): update_version(current_version, path) # Decide where the new wheel goes. if dest_dir is None: - fd, newpath = tempfile.mkstemp(suffix='.whl', - prefix='wheel-update-', - dir=workdir) + fd, newpath = tempfile.mkstemp( + suffix='.whl', + prefix='wheel-update-', + dir=workdir, + ) os.close(fd) else: if not os.path.isdir(dest_dir): @@ -945,6 +1008,7 @@ class Wheel(object): shutil.copyfile(newpath, pathname) return modified + def _get_glibc_version(): import platform ver = platform.libc_ver() @@ -955,6 +1019,7 @@ def _get_glibc_version(): result = tuple(result) return result + def compatible_tags(): """ Return (pyver, abi, arch) tuples compatible with this Python. @@ -993,7 +1058,7 @@ def compatible_tags(): matches.append('universal') while minor >= 0: for match in matches: - s = '%s_%s_%s_%s' % (name, major, minor, match) + s = '{}_{}_{}_{}'.format(name, major, minor, match) if s != ARCH: # already there arches.append(s) minor -= 1 @@ -1008,17 +1073,27 @@ def compatible_tags(): parts = _get_glibc_version() if len(parts) == 2: if parts >= (2, 5): - result.append((''.join((IMP_PREFIX, versions[0])), abi, - 'manylinux1_%s' % arch)) + result.append(( + ''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux1_%s' % arch, + )) if parts >= (2, 12): - result.append((''.join((IMP_PREFIX, versions[0])), abi, - 'manylinux2010_%s' % arch)) + result.append(( + ''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux2010_%s' % arch, + )) if parts >= (2, 17): - result.append((''.join((IMP_PREFIX, versions[0])), abi, - 'manylinux2014_%s' % arch)) - result.append((''.join((IMP_PREFIX, versions[0])), abi, - 'manylinux_%s_%s_%s' % (parts[0], parts[1], - arch))) + result.append(( + ''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux2014_%s' % arch, + )) + result.append(( + ''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux_{}_{}_{}'.format( + parts[0], parts[1], + arch, + ), + )) # where no ABI / arch dependency, but IMP_PREFIX dependency for i, version in enumerate(versions): diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/distro.py b/.venv/lib/python3.10/site-packages/pip/_vendor/distro.py index 7892741..e5479a3 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/distro.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/distro.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - """ The ``distro`` package (``distro`` stands for Linux Distribution) provides information about the Linux distribution it runs on, such as a reliable @@ -26,6 +25,7 @@ Python 2.6 and removed in Python 3.8. Still, there are many cases in which access to OS distribution information is needed. See `Python issue 1322 `_ for more information. """ +from __future__ import annotations import argparse import json @@ -37,7 +37,7 @@ import subprocess import sys import warnings -__version__ = "1.6.0" +__version__ = '1.6.0' # Use `if False` to avoid an ImportError on Python 2. After dropping Python 2 # support, can use typing.TYPE_CHECKING instead. See: @@ -57,24 +57,22 @@ if False: # pragma: nocover Union, ) - VersionDict = TypedDict( - "VersionDict", {"major": str, "minor": str, "build_number": str} - ) - InfoDict = TypedDict( - "InfoDict", - { - "id": str, - "version": str, - "version_parts": VersionDict, - "like": str, - "codename": str, - }, - ) + class VersionDict(TypedDict): + major: str + minor: str + build_number: str + + class InfoDict(TypedDict): + id: str + version: str + version_parts: VersionDict + like: str + codename: str -_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc") -_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib") -_OS_RELEASE_BASENAME = "os-release" +_UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc') +_UNIXUSRLIBDIR = os.environ.get('UNIXUSRLIBDIR', '/usr/lib') +_OS_RELEASE_BASENAME = 'os-release' #: Translation table for normalizing the "ID" attribute defined in os-release #: files, for use by the :func:`distro.id` method. @@ -84,7 +82,7 @@ _OS_RELEASE_BASENAME = "os-release" #: #: * Value: Normalized value. NORMALIZED_OS_ID = { - "ol": "oracle", # Oracle Linux + 'ol': 'oracle', # Oracle Linux } #: Translation table for normalizing the "Distributor ID" attribute returned by @@ -95,11 +93,11 @@ NORMALIZED_OS_ID = { #: #: * Value: Normalized value. NORMALIZED_LSB_ID = { - "enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4 - "enterpriseenterpriseserver": "oracle", # Oracle Linux 5 - "redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation - "redhatenterpriseserver": "rhel", # RHEL 6, 7 Server - "redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode + 'enterpriseenterpriseas': 'oracle', # Oracle Enterprise Linux 4 + 'enterpriseenterpriseserver': 'oracle', # Oracle Linux 5 + 'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation + 'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server + 'redhatenterprisecomputenode': 'rhel', # RHEL 6 ComputeNode } #: Translation table for normalizing the distro ID derived from the file name @@ -110,26 +108,26 @@ NORMALIZED_LSB_ID = { #: #: * Value: Normalized value. NORMALIZED_DISTRO_ID = { - "redhat": "rhel", # RHEL 6.x, 7.x + 'redhat': 'rhel', # RHEL 6.x, 7.x } # Pattern for content of distro release file (reversed) _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( - r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)" + r'(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)', ) # Pattern for base file name of distro release file -_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$") +_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r'(\w+)[-_](release|version)$') # Base file names to be ignored when searching for distro release file _DISTRO_RELEASE_IGNORE_BASENAMES = ( - "debian_version", - "lsb-release", - "oem-release", + 'debian_version', + 'lsb-release', + 'oem-release', _OS_RELEASE_BASENAME, - "system-release", - "plesk-release", - "iredmail-release", + 'system-release', + 'plesk-release', + 'iredmail-release', ) @@ -167,9 +165,9 @@ def linux_distribution(full_distribution_name=True): for a number of popular OS distributions. """ warnings.warn( - "distro.linux_distribution() is deprecated. It should only be used as a " + 'distro.linux_distribution() is deprecated. It should only be used as a ' "compatibility shim with Python's platform.linux_distribution(). Please use " - "distro.id(), distro.version() and distro.name() instead.", + 'distro.id(), distro.version() and distro.name() instead.', DeprecationWarning, stacklevel=2, ) @@ -615,7 +613,7 @@ try: from functools import cached_property except ImportError: # Python < 3.8 - class cached_property(object): # type: ignore + class cached_property: # type: ignore """A version of @property which caches the value. On access, it calls the underlying function and sets the value in `__dict__` so future accesses will not re-call the property. @@ -628,12 +626,12 @@ except ImportError: def __get__(self, obj, owner): # type: (Any, Type[Any]) -> Any - assert obj is not None, "call {} on an instance".format(self._fname) + assert obj is not None, f'call {self._fname} on an instance' ret = obj.__dict__[self._fname] = self._f(obj) return ret -class LinuxDistribution(object): +class LinuxDistribution: """ Provides information about a OS distribution. @@ -654,8 +652,8 @@ class LinuxDistribution(object): def __init__( self, include_lsb=True, - os_release_file="", - distro_release_file="", + os_release_file='', + distro_release_file='', include_uname=True, root_dir=None, ): @@ -731,9 +729,9 @@ class LinuxDistribution(object): uses an unexpected encoding. """ self.root_dir = root_dir - self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR + self.etc_dir = os.path.join(root_dir, 'etc') if root_dir else _UNIXCONFDIR self.usr_lib_dir = ( - os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR + os.path.join(root_dir, 'usr/lib') if root_dir else _UNIXUSRLIBDIR ) if os_release_file: @@ -741,19 +739,19 @@ class LinuxDistribution(object): else: etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME) usr_lib_os_release_file = os.path.join( - self.usr_lib_dir, _OS_RELEASE_BASENAME + self.usr_lib_dir, _OS_RELEASE_BASENAME, ) # NOTE: The idea is to respect order **and** have it set # at all times for API backwards compatibility. if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile( - usr_lib_os_release_file + usr_lib_os_release_file, ): self.os_release_file = etc_dir_os_release_file else: self.os_release_file = usr_lib_os_release_file - self.distro_release_file = distro_release_file or "" # updated later + self.distro_release_file = distro_release_file or '' # updated later self.include_lsb = include_lsb self.include_uname = include_uname @@ -761,15 +759,15 @@ class LinuxDistribution(object): # type: () -> str """Return repr of all info""" return ( - "LinuxDistribution(" - "os_release_file={self.os_release_file!r}, " - "distro_release_file={self.distro_release_file!r}, " - "include_lsb={self.include_lsb!r}, " - "include_uname={self.include_uname!r}, " - "_os_release_info={self._os_release_info!r}, " - "_lsb_release_info={self._lsb_release_info!r}, " - "_distro_release_info={self._distro_release_info!r}, " - "_uname_info={self._uname_info!r})".format(self=self) + 'LinuxDistribution(' + 'os_release_file={self.os_release_file!r}, ' + 'distro_release_file={self.distro_release_file!r}, ' + 'include_lsb={self.include_lsb!r}, ' + 'include_uname={self.include_uname!r}, ' + '_os_release_info={self._os_release_info!r}, ' + '_lsb_release_info={self._lsb_release_info!r}, ' + '_distro_release_info={self._distro_release_info!r}, ' + '_uname_info={self._uname_info!r})'.format(self=self) ) def linux_distribution(self, full_distribution_name=True): @@ -796,26 +794,26 @@ class LinuxDistribution(object): def normalize(distro_id, table): # type: (str, Dict[str, str]) -> str - distro_id = distro_id.lower().replace(" ", "_") + distro_id = distro_id.lower().replace(' ', '_') return table.get(distro_id, distro_id) - distro_id = self.os_release_attr("id") + distro_id = self.os_release_attr('id') if distro_id: return normalize(distro_id, NORMALIZED_OS_ID) - distro_id = self.lsb_release_attr("distributor_id") + distro_id = self.lsb_release_attr('distributor_id') if distro_id: return normalize(distro_id, NORMALIZED_LSB_ID) - distro_id = self.distro_release_attr("id") + distro_id = self.distro_release_attr('id') if distro_id: return normalize(distro_id, NORMALIZED_DISTRO_ID) - distro_id = self.uname_attr("id") + distro_id = self.uname_attr('id') if distro_id: return normalize(distro_id, NORMALIZED_DISTRO_ID) - return "" + return '' def name(self, pretty=False): # type: (bool) -> str @@ -825,21 +823,21 @@ class LinuxDistribution(object): For details, see :func:`distro.name`. """ name = ( - self.os_release_attr("name") - or self.lsb_release_attr("distributor_id") - or self.distro_release_attr("name") - or self.uname_attr("name") + self.os_release_attr('name') or + self.lsb_release_attr('distributor_id') or + self.distro_release_attr('name') or + self.uname_attr('name') ) if pretty: - name = self.os_release_attr("pretty_name") or self.lsb_release_attr( - "description" + name = self.os_release_attr('pretty_name') or self.lsb_release_attr( + 'description', ) if not name: - name = self.distro_release_attr("name") or self.uname_attr("name") + name = self.distro_release_attr('name') or self.uname_attr('name') version = self.version(pretty=True) if version: - name = name + " " + version - return name or "" + name = name + ' ' + version + return name or '' def version(self, pretty=False, best=False): # type: (bool, bool) -> str @@ -849,33 +847,33 @@ class LinuxDistribution(object): For details, see :func:`distro.version`. """ versions = [ - self.os_release_attr("version_id"), - self.lsb_release_attr("release"), - self.distro_release_attr("version_id"), - self._parse_distro_release_content(self.os_release_attr("pretty_name")).get( - "version_id", "" + self.os_release_attr('version_id'), + self.lsb_release_attr('release'), + self.distro_release_attr('version_id'), + self._parse_distro_release_content(self.os_release_attr('pretty_name')).get( + 'version_id', '', ), self._parse_distro_release_content( - self.lsb_release_attr("description") - ).get("version_id", ""), - self.uname_attr("release"), + self.lsb_release_attr('description'), + ).get('version_id', ''), + self.uname_attr('release'), ] - version = "" + version = '' if best: # This algorithm uses the last version in priority order that has # the best precision. If the versions are not in conflict, that # does not matter; otherwise, using the last one instead of the # first one might be considered a surprise. for v in versions: - if v.count(".") > version.count(".") or version == "": + if v.count('.') > version.count('.') or version == '': version = v else: for v in versions: - if v != "": + if v != '': version = v break if pretty and version and self.codename(): - version = "{0} ({1})".format(version, self.codename()) + version = f'{version} ({self.codename()})' return version def version_parts(self, best=False): @@ -888,12 +886,12 @@ class LinuxDistribution(object): """ version_str = self.version(best=best) if version_str: - version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?") + version_regex = re.compile(r'(\d+)\.?(\d+)?\.?(\d+)?') matches = version_regex.match(version_str) if matches: major, minor, build_number = matches.groups() - return major, minor or "", build_number or "" - return "", "", "" + return major, minor or '', build_number or '' + return '', '', '' def major_version(self, best=False): # type: (bool) -> str @@ -929,7 +927,7 @@ class LinuxDistribution(object): For details, see :func:`distro.like`. """ - return self.os_release_attr("id_like") or "" + return self.os_release_attr('id_like') or '' def codename(self): # type: () -> str @@ -941,12 +939,12 @@ class LinuxDistribution(object): try: # Handle os_release specially since distros might purposefully set # this to empty string to have no codename - return self._os_release_info["codename"] + return self._os_release_info['codename'] except KeyError: return ( - self.lsb_release_attr("codename") - or self.distro_release_attr("codename") - or "" + self.lsb_release_attr('codename') or + self.distro_release_attr('codename') or + '' ) def info(self, pretty=False, best=False): @@ -1019,7 +1017,7 @@ class LinuxDistribution(object): For details, see :func:`distro.os_release_attr`. """ - return self._os_release_info.get(attribute, "") + return self._os_release_info.get(attribute, '') def lsb_release_attr(self, attribute): # type: (str) -> str @@ -1029,7 +1027,7 @@ class LinuxDistribution(object): For details, see :func:`distro.lsb_release_attr`. """ - return self._lsb_release_info.get(attribute, "") + return self._lsb_release_info.get(attribute, '') def distro_release_attr(self, attribute): # type: (str) -> str @@ -1039,7 +1037,7 @@ class LinuxDistribution(object): For details, see :func:`distro.distro_release_attr`. """ - return self._distro_release_info.get(attribute, "") + return self._distro_release_info.get(attribute, '') def uname_attr(self, attribute): # type: (str) -> str @@ -1049,7 +1047,7 @@ class LinuxDistribution(object): For details, see :func:`distro.uname_attr`. """ - return self._uname_info.get(attribute, "") + return self._uname_info.get(attribute, '') @cached_property def _os_release_info(self): @@ -1092,7 +1090,7 @@ class LinuxDistribution(object): # parsed content is a unicode object. The following fix resolves that # (... but it should be fixed in shlex...): if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes): - lexer.wordchars = lexer.wordchars.decode("iso-8859-1") + lexer.wordchars = lexer.wordchars.decode('iso-8859-1') tokens = list(lexer) for token in tokens: @@ -1102,32 +1100,32 @@ class LinuxDistribution(object): # stripped, etc.), so the tokens are now either: # * variable assignments: var=value # * commands or their arguments (not allowed in os-release) - if "=" in token: - k, v = token.split("=", 1) + if '=' in token: + k, v = token.split('=', 1) props[k.lower()] = v else: # Ignore any tokens that are not variable assignments pass - if "version_codename" in props: + if 'version_codename' in props: # os-release added a version_codename field. Use that in # preference to anything else Note that some distros purposefully # do not have code names. They should be setting # version_codename="" - props["codename"] = props["version_codename"] - elif "ubuntu_codename" in props: + props['codename'] = props['version_codename'] + elif 'ubuntu_codename' in props: # Same as above but a non-standard field name used on older Ubuntus - props["codename"] = props["ubuntu_codename"] - elif "version" in props: + props['codename'] = props['ubuntu_codename'] + elif 'version' in props: # If there is no version_codename, parse it from the version - match = re.search(r"(\(\D+\))|,(\s+)?\D+", props["version"]) + match = re.search(r'(\(\D+\))|,(\s+)?\D+', props['version']) if match: codename = match.group() - codename = codename.strip("()") - codename = codename.strip(",") + codename = codename.strip('()') + codename = codename.strip(',') codename = codename.strip() # codename appears within paranthese. - props["codename"] = codename + props['codename'] = codename return props @@ -1142,9 +1140,9 @@ class LinuxDistribution(object): """ if not self.include_lsb: return {} - with open(os.devnull, "wb") as devnull: + with open(os.devnull, 'wb') as devnull: try: - cmd = ("lsb_release", "-a") + cmd = ('lsb_release', '-a') stdout = subprocess.check_output(cmd, stderr=devnull) # Command not found or lsb_release returned error except (OSError, subprocess.CalledProcessError): @@ -1169,20 +1167,20 @@ class LinuxDistribution(object): """ props = {} for line in lines: - kv = line.strip("\n").split(":", 1) + kv = line.strip('\n').split(':', 1) if len(kv) != 2: # Ignore lines without colon. continue k, v = kv - props.update({k.replace(" ", "_").lower(): v.strip()}) + props.update({k.replace(' ', '_').lower(): v.strip()}) return props @cached_property def _uname_info(self): # type: () -> Dict[str, str] - with open(os.devnull, "wb") as devnull: + with open(os.devnull, 'wb') as devnull: try: - cmd = ("uname", "-rs") + cmd = ('uname', '-rs') stdout = subprocess.check_output(cmd, stderr=devnull) except OSError: return {} @@ -1193,25 +1191,25 @@ class LinuxDistribution(object): def _parse_uname_content(lines): # type: (Sequence[str]) -> Dict[str, str] props = {} - match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip()) + match = re.search(r'^([^\s]+)\s+([\d\.]+)', lines[0].strip()) if match: name, version = match.groups() # This is to prevent the Linux kernel version from # appearing as the 'best' version on otherwise # identifiable distributions. - if name == "Linux": + if name == 'Linux': return {} - props["id"] = name.lower() - props["name"] = name - props["release"] = version + props['id'] = name.lower() + props['name'] = name + props['release'] = version return props @staticmethod def _to_str(text): # type: (Union[bytes, str]) -> str encoding = sys.getfilesystemencoding() - encoding = "utf-8" if encoding == "ascii" else encoding + encoding = 'utf-8' if encoding == 'ascii' else encoding if sys.version_info[0] >= 3: if isinstance(text, bytes): @@ -1241,10 +1239,10 @@ class LinuxDistribution(object): # file), because we want to use what was specified as best as # possible. match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) - if "name" in distro_info and "cloudlinux" in distro_info["name"].lower(): - distro_info["id"] = "cloudlinux" + if 'name' in distro_info and 'cloudlinux' in distro_info['name'].lower(): + distro_info['id'] = 'cloudlinux' elif match: - distro_info["id"] = match.group(1) + distro_info['id'] = match.group(1) return distro_info else: try: @@ -1259,21 +1257,21 @@ class LinuxDistribution(object): # /etc for information. If they turn out to not be there the # error is handled in `_parse_distro_release_file()`. basenames = [ - "SuSE-release", - "arch-release", - "base-release", - "centos-release", - "fedora-release", - "gentoo-release", - "mageia-release", - "mandrake-release", - "mandriva-release", - "mandrivalinux-release", - "manjaro-release", - "oracle-release", - "redhat-release", - "sl-release", - "slackware-version", + 'SuSE-release', + 'arch-release', + 'base-release', + 'centos-release', + 'fedora-release', + 'gentoo-release', + 'mageia-release', + 'mandrake-release', + 'mandriva-release', + 'mandrivalinux-release', + 'manjaro-release', + 'oracle-release', + 'redhat-release', + 'sl-release', + 'slackware-version', ] for basename in basenames: if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: @@ -1282,12 +1280,12 @@ class LinuxDistribution(object): if match: filepath = os.path.join(self.etc_dir, basename) distro_info = self._parse_distro_release_file(filepath) - if "name" in distro_info: + if 'name' in distro_info: # The name is always present if the pattern matches self.distro_release_file = filepath - distro_info["id"] = match.group(1) - if "cloudlinux" in distro_info["name"].lower(): - distro_info["id"] = "cloudlinux" + distro_info['id'] = match.group(1) + if 'cloudlinux' in distro_info['name'].lower(): + distro_info['id'] = 'cloudlinux' return distro_info return {} @@ -1308,7 +1306,7 @@ class LinuxDistribution(object): # Only parse the first line. For instance, on SLES there # are multiple lines. We don't want them... return self._parse_distro_release_content(fp.readline()) - except (OSError, IOError): + except OSError: # Ignore not being able to read a specific, seemingly version # related file. # See https://github.com/python-distro/distro/issues/162 @@ -1331,13 +1329,13 @@ class LinuxDistribution(object): distro_info = {} if matches: # regexp ensures non-None - distro_info["name"] = matches.group(3)[::-1] + distro_info['name'] = matches.group(3)[::-1] if matches.group(2): - distro_info["version_id"] = matches.group(2)[::-1] + distro_info['version_id'] = matches.group(2)[::-1] if matches.group(1): - distro_info["codename"] = matches.group(1)[::-1] + distro_info['codename'] = matches.group(1)[::-1] elif line: - distro_info["name"] = line.strip() + distro_info['name'] = line.strip() return distro_info @@ -1350,24 +1348,24 @@ def main(): logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler(sys.stdout)) - parser = argparse.ArgumentParser(description="OS distro info tool") + parser = argparse.ArgumentParser(description='OS distro info tool') parser.add_argument( - "--json", "-j", help="Output in machine readable format", action="store_true" + '--json', '-j', help='Output in machine readable format', action='store_true', ) parser.add_argument( - "--root-dir", - "-r", + '--root-dir', + '-r', type=str, - dest="root_dir", - help="Path to the root filesystem directory (defaults to /)", + dest='root_dir', + help='Path to the root filesystem directory (defaults to /)', ) args = parser.parse_args() if args.root_dir: dist = LinuxDistribution( - include_lsb=False, include_uname=False, root_dir=args.root_dir + include_lsb=False, include_uname=False, root_dir=args.root_dir, ) else: dist = _distro @@ -1375,12 +1373,12 @@ def main(): if args.json: logger.info(json.dumps(dist.info(), indent=4, sort_keys=True)) else: - logger.info("Name: %s", dist.name(pretty=True)) + logger.info('Name: %s', dist.name(pretty=True)) distribution_version = dist.version(pretty=True) - logger.info("Version: %s", distribution_version) + logger.info('Version: %s', distribution_version) distribution_codename = dist.codename() - logger.info("Codename: %s", distribution_codename) + logger.info('Codename: %s', distribution_codename) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/__init__.py index d1d82f1..51861be 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/__init__.py @@ -19,17 +19,20 @@ For convenience, this module re-exports the following names: * :func:`~.treewalkers.getTreeWalker` * :func:`~.serializer.serialize` """ +from __future__ import annotations -from __future__ import absolute_import, division, unicode_literals - -from .html5parser import HTMLParser, parse, parseFragment +from .html5parser import HTMLParser +from .html5parser import parse +from .html5parser import parseFragment +from .serializer import serialize from .treebuilders import getTreeBuilder from .treewalkers import getTreeWalker -from .serializer import serialize -__all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder", - "getTreeWalker", "serialize"] +__all__ = [ + 'HTMLParser', 'parse', 'parseFragment', 'getTreeBuilder', + 'getTreeWalker', 'serialize', +] # this has to be at the top level, see how setup.py parses this #: Distribution version number. -__version__ = "1.1" +__version__ = '1.1' diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_ihatexml.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_ihatexml.py index 3ff803c..da901b1 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_ihatexml.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_ihatexml.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations import re import warnings @@ -89,19 +89,21 @@ extender = """ #x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 | #x0EC6 | #x3005 | #[#x3031-#x3035] | [#x309D-#x309E] | [#x30FC-#x30FE]""" -letter = " | ".join([baseChar, ideographic]) +letter = ' | '.join([baseChar, ideographic]) # Without the -name = " | ".join([letter, digit, ".", "-", "_", combiningCharacter, - extender]) -nameFirst = " | ".join([letter, "_"]) +name = ' | '.join([ + letter, digit, '.', '-', '_', combiningCharacter, + extender, +]) +nameFirst = ' | '.join([letter, '_']) -reChar = re.compile(r"#x([\d|A-F]{4,4})") -reCharRange = re.compile(r"\[#x([\d|A-F]{4,4})-#x([\d|A-F]{4,4})\]") +reChar = re.compile(r'#x([\d|A-F]{4,4})') +reCharRange = re.compile(r'\[#x([\d|A-F]{4,4})-#x([\d|A-F]{4,4})\]') def charStringToList(chars): - charRanges = [item.strip() for item in chars.split(" | ")] + charRanges = [item.strip() for item in chars.split(' | ')] rv = [] for item in charRanges: foundMatch = False @@ -138,7 +140,7 @@ def normaliseCharList(charList): # We don't really support characters above the BMP :( -max_unicode = int("FFFF", 16) +max_unicode = int('FFFF', 16) def missingRanges(charList): @@ -158,9 +160,11 @@ def listToRegexpStr(charList): if item[0] == item[1]: rv.append(escapeRegexp(chr(item[0]))) else: - rv.append(escapeRegexp(chr(item[0])) + "-" + - escapeRegexp(chr(item[1]))) - return "[%s]" % "".join(rv) + rv.append( + escapeRegexp(chr(item[0])) + '-' + + escapeRegexp(chr(item[1])), + ) + return '[%s]' % ''.join(rv) def hexToInt(hex_str): @@ -168,13 +172,16 @@ def hexToInt(hex_str): def escapeRegexp(string): - specialCharacters = (".", "^", "$", "*", "+", "?", "{", "}", - "[", "]", "|", "(", ")", "-") + specialCharacters = ( + '.', '^', '$', '*', '+', '?', '{', '}', + '[', ']', '|', '(', ')', '-', + ) for char in specialCharacters: - string = string.replace(char, "\\" + char) + string = string.replace(char, '\\' + char) return string + # output from the above nonXmlNameBMPRegexp = re.compile('[\x00-,/:-@\\[-\\^`\\{-\xb6\xb8-\xbf\xd7\xf7\u0132-\u0133\u013f-\u0140\u0149\u017f\u01c4-\u01cc\u01f1-\u01f3\u01f6-\u01f9\u0218-\u024f\u02a9-\u02ba\u02c2-\u02cf\u02d2-\u02ff\u0346-\u035f\u0362-\u0385\u038b\u038d\u03a2\u03cf\u03d7-\u03d9\u03db\u03dd\u03df\u03e1\u03f4-\u0400\u040d\u0450\u045d\u0482\u0487-\u048f\u04c5-\u04c6\u04c9-\u04ca\u04cd-\u04cf\u04ec-\u04ed\u04f6-\u04f7\u04fa-\u0530\u0557-\u0558\u055a-\u0560\u0587-\u0590\u05a2\u05ba\u05be\u05c0\u05c3\u05c5-\u05cf\u05eb-\u05ef\u05f3-\u0620\u063b-\u063f\u0653-\u065f\u066a-\u066f\u06b8-\u06b9\u06bf\u06cf\u06d4\u06e9\u06ee-\u06ef\u06fa-\u0900\u0904\u093a-\u093b\u094e-\u0950\u0955-\u0957\u0964-\u0965\u0970-\u0980\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09bb\u09bd\u09c5-\u09c6\u09c9-\u09ca\u09ce-\u09d6\u09d8-\u09db\u09de\u09e4-\u09e5\u09f2-\u0a01\u0a03-\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a3b\u0a3d\u0a43-\u0a46\u0a49-\u0a4a\u0a4e-\u0a58\u0a5d\u0a5f-\u0a65\u0a75-\u0a80\u0a84\u0a8c\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abb\u0ac6\u0aca\u0ace-\u0adf\u0ae1-\u0ae5\u0af0-\u0b00\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34-\u0b35\u0b3a-\u0b3b\u0b44-\u0b46\u0b49-\u0b4a\u0b4e-\u0b55\u0b58-\u0b5b\u0b5e\u0b62-\u0b65\u0b70-\u0b81\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bb6\u0bba-\u0bbd\u0bc3-\u0bc5\u0bc9\u0bce-\u0bd6\u0bd8-\u0be6\u0bf0-\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a-\u0c3d\u0c45\u0c49\u0c4e-\u0c54\u0c57-\u0c5f\u0c62-\u0c65\u0c70-\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cbd\u0cc5\u0cc9\u0cce-\u0cd4\u0cd7-\u0cdd\u0cdf\u0ce2-\u0ce5\u0cf0-\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a-\u0d3d\u0d44-\u0d45\u0d49\u0d4e-\u0d56\u0d58-\u0d5f\u0d62-\u0d65\u0d70-\u0e00\u0e2f\u0e3b-\u0e3f\u0e4f\u0e5a-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eaf\u0eba\u0ebe-\u0ebf\u0ec5\u0ec7\u0ece-\u0ecf\u0eda-\u0f17\u0f1a-\u0f1f\u0f2a-\u0f34\u0f36\u0f38\u0f3a-\u0f3d\u0f48\u0f6a-\u0f70\u0f85\u0f8c-\u0f8f\u0f96\u0f98\u0fae-\u0fb0\u0fb8\u0fba-\u109f\u10c6-\u10cf\u10f7-\u10ff\u1101\u1104\u1108\u110a\u110d\u1113-\u113b\u113d\u113f\u1141-\u114b\u114d\u114f\u1151-\u1153\u1156-\u1158\u115a-\u115e\u1162\u1164\u1166\u1168\u116a-\u116c\u116f-\u1171\u1174\u1176-\u119d\u119f-\u11a7\u11a9-\u11aa\u11ac-\u11ad\u11b0-\u11b6\u11b9\u11bb\u11c3-\u11ea\u11ec-\u11ef\u11f1-\u11f8\u11fa-\u1dff\u1e9c-\u1e9f\u1efa-\u1eff\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fbd\u1fbf-\u1fc1\u1fc5\u1fcd-\u1fcf\u1fd4-\u1fd5\u1fdc-\u1fdf\u1fed-\u1ff1\u1ff5\u1ffd-\u20cf\u20dd-\u20e0\u20e2-\u2125\u2127-\u2129\u212c-\u212d\u212f-\u217f\u2183-\u3004\u3006\u3008-\u3020\u3030\u3036-\u3040\u3095-\u3098\u309b-\u309c\u309f-\u30a0\u30fb\u30ff-\u3104\u312d-\u4dff\u9fa6-\uabff\ud7a4-\uffff]') # noqa @@ -184,16 +191,18 @@ nonXmlNameFirstBMPRegexp = re.compile('[\x00-@\\[-\\^`\\{-\xbf\xd7\xf7\u0132-\u0 nonPubidCharRegexp = re.compile("[^\x20\x0D\x0Aa-zA-Z0-9\\-'()+,./:=?;!*#@$_%]") -class InfosetFilter(object): - replacementRegexp = re.compile(r"U[\dA-F]{5,5}") +class InfosetFilter: + replacementRegexp = re.compile(r'U[\dA-F]{5,5}') - def __init__(self, - dropXmlnsLocalName=False, - dropXmlnsAttrNs=False, - preventDoubleDashComments=False, - preventDashAtCommentEnd=False, - replaceFormFeedCharacters=True, - preventSingleQuotePubid=False): + def __init__( + self, + dropXmlnsLocalName=False, + dropXmlnsAttrNs=False, + preventDoubleDashComments=False, + preventDashAtCommentEnd=False, + replaceFormFeedCharacters=True, + preventSingleQuotePubid=False, + ): self.dropXmlnsLocalName = dropXmlnsLocalName self.dropXmlnsAttrNs = dropXmlnsAttrNs @@ -208,12 +217,14 @@ class InfosetFilter(object): self.replaceCache = {} def coerceAttribute(self, name, namespace=None): - if self.dropXmlnsLocalName and name.startswith("xmlns:"): - warnings.warn("Attributes cannot begin with xmlns", DataLossWarning) + if self.dropXmlnsLocalName and name.startswith('xmlns:'): + warnings.warn('Attributes cannot begin with xmlns', DataLossWarning) return None - elif (self.dropXmlnsAttrNs and - namespace == "http://www.w3.org/2000/xmlns/"): - warnings.warn("Attributes cannot be in the xml namespace", DataLossWarning) + elif ( + self.dropXmlnsAttrNs and + namespace == 'http://www.w3.org/2000/xmlns/' + ): + warnings.warn('Attributes cannot be in the xml namespace', DataLossWarning) return None else: return self.toXmlName(name) @@ -223,30 +234,30 @@ class InfosetFilter(object): def coerceComment(self, data): if self.preventDoubleDashComments: - while "--" in data: - warnings.warn("Comments cannot contain adjacent dashes", DataLossWarning) - data = data.replace("--", "- -") - if data.endswith("-"): - warnings.warn("Comments cannot end in a dash", DataLossWarning) - data += " " + while '--' in data: + warnings.warn('Comments cannot contain adjacent dashes', DataLossWarning) + data = data.replace('--', '- -') + if data.endswith('-'): + warnings.warn('Comments cannot end in a dash', DataLossWarning) + data += ' ' return data def coerceCharacters(self, data): if self.replaceFormFeedCharacters: - for _ in range(data.count("\x0C")): - warnings.warn("Text cannot contain U+000C", DataLossWarning) - data = data.replace("\x0C", " ") + for _ in range(data.count('\x0C')): + warnings.warn('Text cannot contain U+000C', DataLossWarning) + data = data.replace('\x0C', ' ') # Other non-xml characters return data def coercePubid(self, data): dataOutput = data for char in nonPubidCharRegexp.findall(data): - warnings.warn("Coercing non-XML pubid", DataLossWarning) + warnings.warn('Coercing non-XML pubid', DataLossWarning) replacement = self.getReplacementCharacter(char) dataOutput = dataOutput.replace(char, replacement) if self.preventSingleQuotePubid and dataOutput.find("'") >= 0: - warnings.warn("Pubid cannot contain single quote", DataLossWarning) + warnings.warn('Pubid cannot contain single quote', DataLossWarning) dataOutput = dataOutput.replace("'", self.getReplacementCharacter("'")) return dataOutput @@ -255,7 +266,7 @@ class InfosetFilter(object): nameRest = name[1:] m = nonXmlNameFirstBMPRegexp.match(nameFirst) if m: - warnings.warn("Coercing non-XML name: %s" % name, DataLossWarning) + warnings.warn('Coercing non-XML name: %s' % name, DataLossWarning) nameFirstOutput = self.getReplacementCharacter(nameFirst) else: nameFirstOutput = nameFirst @@ -263,7 +274,7 @@ class InfosetFilter(object): nameRestOutput = nameRest replaceChars = set(nonXmlNameBMPRegexp.findall(nameRest)) for char in replaceChars: - warnings.warn("Coercing non-XML name: %s" % name, DataLossWarning) + warnings.warn('Coercing non-XML name: %s' % name, DataLossWarning) replacement = self.getReplacementCharacter(char) nameRestOutput = nameRestOutput.replace(char, replacement) return nameFirstOutput + nameRestOutput @@ -281,7 +292,7 @@ class InfosetFilter(object): return name def escapeChar(self, char): - replacement = "U%05X" % ord(char) + replacement = 'U%05X' % ord(char) self.replaceCache[char] = replacement return replacement diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_inputstream.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_inputstream.py index e0bb376..0d5f845 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_inputstream.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_inputstream.py @@ -1,54 +1,62 @@ -from __future__ import absolute_import, division, unicode_literals - -from pip._vendor.six import text_type -from pip._vendor.six.moves import http_client, urllib +from __future__ import annotations import codecs import re -from io import BytesIO, StringIO +from io import BytesIO +from io import StringIO from pip._vendor import webencodings +from pip._vendor.six import text_type +from pip._vendor.six.moves import http_client +from pip._vendor.six.moves import urllib -from .constants import EOF, spaceCharacters, asciiLetters, asciiUppercase -from .constants import _ReparseException from . import _utils +from .constants import _ReparseException +from .constants import asciiLetters +from .constants import asciiUppercase +from .constants import EOF +from .constants import spaceCharacters # Non-unicode versions of constants for use in the pre-parser -spaceCharactersBytes = frozenset([item.encode("ascii") for item in spaceCharacters]) -asciiLettersBytes = frozenset([item.encode("ascii") for item in asciiLetters]) -asciiUppercaseBytes = frozenset([item.encode("ascii") for item in asciiUppercase]) -spacesAngleBrackets = spaceCharactersBytes | frozenset([b">", b"<"]) +spaceCharactersBytes = frozenset([item.encode('ascii') for item in spaceCharacters]) +asciiLettersBytes = frozenset([item.encode('ascii') for item in asciiLetters]) +asciiUppercaseBytes = frozenset([item.encode('ascii') for item in asciiUppercase]) +spacesAngleBrackets = spaceCharactersBytes | frozenset([b'>', b'<']) -invalid_unicode_no_surrogate = "[\u0001-\u0008\u000B\u000E-\u001F\u007F-\u009F\uFDD0-\uFDEF\uFFFE\uFFFF\U0001FFFE\U0001FFFF\U0002FFFE\U0002FFFF\U0003FFFE\U0003FFFF\U0004FFFE\U0004FFFF\U0005FFFE\U0005FFFF\U0006FFFE\U0006FFFF\U0007FFFE\U0007FFFF\U0008FFFE\U0008FFFF\U0009FFFE\U0009FFFF\U000AFFFE\U000AFFFF\U000BFFFE\U000BFFFF\U000CFFFE\U000CFFFF\U000DFFFE\U000DFFFF\U000EFFFE\U000EFFFF\U000FFFFE\U000FFFFF\U0010FFFE\U0010FFFF]" # noqa +invalid_unicode_no_surrogate = '[\u0001-\u0008\u000B\u000E-\u001F\u007F-\u009F\uFDD0-\uFDEF\uFFFE\uFFFF\U0001FFFE\U0001FFFF\U0002FFFE\U0002FFFF\U0003FFFE\U0003FFFF\U0004FFFE\U0004FFFF\U0005FFFE\U0005FFFF\U0006FFFE\U0006FFFF\U0007FFFE\U0007FFFF\U0008FFFE\U0008FFFF\U0009FFFE\U0009FFFF\U000AFFFE\U000AFFFF\U000BFFFE\U000BFFFF\U000CFFFE\U000CFFFF\U000DFFFE\U000DFFFF\U000EFFFE\U000EFFFF\U000FFFFE\U000FFFFF\U0010FFFE\U0010FFFF]' # noqa if _utils.supports_lone_surrogates: # Use one extra step of indirection and create surrogates with # eval. Not using this indirection would introduce an illegal # unicode literal on platforms not supporting such lone # surrogates. - assert invalid_unicode_no_surrogate[-1] == "]" and invalid_unicode_no_surrogate.count("]") == 1 - invalid_unicode_re = re.compile(invalid_unicode_no_surrogate[:-1] + - eval('"\\uD800-\\uDFFF"') + # pylint:disable=eval-used - "]") + assert invalid_unicode_no_surrogate[-1] == ']' and invalid_unicode_no_surrogate.count(']') == 1 + invalid_unicode_re = re.compile( + invalid_unicode_no_surrogate[:-1] + + eval('"\\uD800-\\uDFFF"') + # pylint:disable=eval-used + ']', + ) else: invalid_unicode_re = re.compile(invalid_unicode_no_surrogate) -non_bmp_invalid_codepoints = {0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, - 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, - 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, - 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, - 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, - 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, - 0x10FFFE, 0x10FFFF} +non_bmp_invalid_codepoints = { + 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, + 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, + 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, + 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, + 0x10FFFE, 0x10FFFF, +} -ascii_punctuation_re = re.compile("[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005C\u005B-\u0060\u007B-\u007E]") +ascii_punctuation_re = re.compile('[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005C\u005B-\u0060\u007B-\u007E]') # Cache for charsUntil() charsUntilRegEx = {} -class BufferedStream(object): +class BufferedStream: """Buffering for streams that do not have buffering of their own The buffer is implemented as a list of chunks on the assumption that @@ -79,8 +87,10 @@ class BufferedStream(object): def read(self, bytes): if not self.buffer: return self._readStream(bytes) - elif (self.position[0] == len(self.buffer) and - self.position[1] == len(self.buffer[-1])): + elif ( + self.position[0] == len(self.buffer) and + self.position[1] == len(self.buffer[-1]) + ): return self._readStream(bytes) else: return self._readFromBuffer(bytes) @@ -119,33 +129,37 @@ class BufferedStream(object): if remainingBytes: rv.append(self._readStream(remainingBytes)) - return b"".join(rv) + return b''.join(rv) def HTMLInputStream(source, **kwargs): # Work around Python bug #20007: read(0) closes the connection. # http://bugs.python.org/issue20007 - if (isinstance(source, http_client.HTTPResponse) or + if ( + isinstance(source, http_client.HTTPResponse) or # Also check for addinfourl wrapping HTTPResponse - (isinstance(source, urllib.response.addbase) and - isinstance(source.fp, http_client.HTTPResponse))): + ( + isinstance(source, urllib.response.addbase) and + isinstance(source.fp, http_client.HTTPResponse) + ) + ): isUnicode = False - elif hasattr(source, "read"): + elif hasattr(source, 'read'): isUnicode = isinstance(source.read(0), text_type) else: isUnicode = isinstance(source, text_type) if isUnicode: - encodings = [x for x in kwargs if x.endswith("_encoding")] + encodings = [x for x in kwargs if x.endswith('_encoding')] if encodings: - raise TypeError("Cannot set an encoding with a unicode input, set %r" % encodings) + raise TypeError('Cannot set an encoding with a unicode input, set %r' % encodings) return HTMLUnicodeInputStream(source, **kwargs) else: return HTMLBinaryInputStream(source, **kwargs) -class HTMLUnicodeInputStream(object): +class HTMLUnicodeInputStream: """Provides a unicode stream of characters to the HTMLTokenizer. This class takes care of character encoding and removing or replacing @@ -174,7 +188,7 @@ class HTMLUnicodeInputStream(object): # Such platforms will have already checked for such # surrogate errors, so no need to do this checking. self.reportCharacterErrors = None - elif len("\U0010FFFF") == 1: + elif len('\U0010FFFF') == 1: self.reportCharacterErrors = self.characterErrorsUCS4 else: self.reportCharacterErrors = self.characterErrorsUCS2 @@ -182,13 +196,13 @@ class HTMLUnicodeInputStream(object): # List of where new lines occur self.newLines = [0] - self.charEncoding = (lookupEncoding("utf-8"), "certain") + self.charEncoding = (lookupEncoding('utf-8'), 'certain') self.dataStream = self.openStream(source) self.reset() def reset(self): - self.chunk = "" + self.chunk = '' self.chunkSize = 0 self.chunkOffset = 0 self.errors = [] @@ -252,7 +266,7 @@ class HTMLUnicodeInputStream(object): self.prevNumLines, self.prevNumCols = self._position(self.chunkSize) - self.chunk = "" + self.chunk = '' self.chunkSize = 0 self.chunkOffset = 0 @@ -276,8 +290,8 @@ class HTMLUnicodeInputStream(object): self.reportCharacterErrors(data) # Replace invalid characters - data = data.replace("\r\n", "\n") - data = data.replace("\r", "\n") + data = data.replace('\r\n', '\n') + data = data.replace('\r', '\n') self.chunk = data self.chunkSize = len(data) @@ -286,7 +300,7 @@ class HTMLUnicodeInputStream(object): def characterErrorsUCS4(self, data): for _ in range(len(invalid_unicode_re.findall(data))): - self.errors.append("invalid-codepoint") + self.errors.append('invalid-codepoint') def characterErrorsUCS2(self, data): # Someone picked the wrong compile option @@ -302,14 +316,16 @@ class HTMLUnicodeInputStream(object): # We have a surrogate pair! char_val = _utils.surrogatePairToCodepoint(data[pos:pos + 2]) if char_val in non_bmp_invalid_codepoints: - self.errors.append("invalid-codepoint") + self.errors.append('invalid-codepoint') skip = True - elif (codepoint >= 0xD800 and codepoint <= 0xDFFF and - pos == len(data) - 1): - self.errors.append("invalid-codepoint") + elif ( + codepoint >= 0xD800 and codepoint <= 0xDFFF and + pos == len(data) - 1 + ): + self.errors.append('invalid-codepoint') else: skip = False - self.errors.append("invalid-codepoint") + self.errors.append('invalid-codepoint') def charsUntil(self, characters, opposite=False): """ Returns a string of characters from the stream up to but not @@ -324,11 +340,11 @@ class HTMLUnicodeInputStream(object): except KeyError: if __debug__: for c in characters: - assert(ord(c) < 128) - regex = "".join(["\\x%02x" % ord(c) for c in characters]) + assert (ord(c) < 128) + regex = ''.join(['\\x%02x' % ord(c) for c in characters]) if not opposite: - regex = "^%s" % regex - chars = charsUntilRegEx[(characters, opposite)] = re.compile("[%s]+" % regex) + regex = '^%s' % regex + chars = charsUntilRegEx[(characters, opposite)] = re.compile('[%s]+' % regex) rv = [] @@ -355,7 +371,7 @@ class HTMLUnicodeInputStream(object): # Reached EOF break - r = "".join(rv) + r = ''.join(rv) return r def unget(self, char): @@ -383,9 +399,11 @@ class HTMLBinaryInputStream(HTMLUnicodeInputStream): """ - def __init__(self, source, override_encoding=None, transport_encoding=None, - same_origin_parent_encoding=None, likely_encoding=None, - default_encoding="windows-1252", useChardet=True): + def __init__( + self, source, override_encoding=None, transport_encoding=None, + same_origin_parent_encoding=None, likely_encoding=None, + default_encoding='windows-1252', useChardet=True, + ): """Initialises the HTMLInputStream. HTMLInputStream(source, [encoding]) -> Normalized stream from source @@ -451,32 +469,32 @@ class HTMLBinaryInputStream(HTMLUnicodeInputStream): def determineEncoding(self, chardet=True): # BOMs take precedence over everything # This will also read past the BOM if present - charEncoding = self.detectBOM(), "certain" + charEncoding = self.detectBOM(), 'certain' if charEncoding[0] is not None: return charEncoding # If we've been overridden, we've been overridden - charEncoding = lookupEncoding(self.override_encoding), "certain" + charEncoding = lookupEncoding(self.override_encoding), 'certain' if charEncoding[0] is not None: return charEncoding # Now check the transport layer - charEncoding = lookupEncoding(self.transport_encoding), "certain" + charEncoding = lookupEncoding(self.transport_encoding), 'certain' if charEncoding[0] is not None: return charEncoding # Look for meta elements with encoding information - charEncoding = self.detectEncodingMeta(), "tentative" + charEncoding = self.detectEncodingMeta(), 'tentative' if charEncoding[0] is not None: return charEncoding # Parent document encoding - charEncoding = lookupEncoding(self.same_origin_parent_encoding), "tentative" - if charEncoding[0] is not None and not charEncoding[0].name.startswith("utf-16"): + charEncoding = lookupEncoding(self.same_origin_parent_encoding), 'tentative' + if charEncoding[0] is not None and not charEncoding[0].name.startswith('utf-16'): return charEncoding # "likely" encoding - charEncoding = lookupEncoding(self.likely_encoding), "tentative" + charEncoding = lookupEncoding(self.likely_encoding), 'tentative' if charEncoding[0] is not None: return charEncoding @@ -500,31 +518,31 @@ class HTMLBinaryInputStream(HTMLUnicodeInputStream): encoding = lookupEncoding(detector.result['encoding']) self.rawStream.seek(0) if encoding is not None: - return encoding, "tentative" + return encoding, 'tentative' # Try the default encoding - charEncoding = lookupEncoding(self.default_encoding), "tentative" + charEncoding = lookupEncoding(self.default_encoding), 'tentative' if charEncoding[0] is not None: return charEncoding # Fallback to html5lib's default if even that hasn't worked - return lookupEncoding("windows-1252"), "tentative" + return lookupEncoding('windows-1252'), 'tentative' def changeEncoding(self, newEncoding): - assert self.charEncoding[1] != "certain" + assert self.charEncoding[1] != 'certain' newEncoding = lookupEncoding(newEncoding) if newEncoding is None: return - if newEncoding.name in ("utf-16be", "utf-16le"): - newEncoding = lookupEncoding("utf-8") + if newEncoding.name in ('utf-16be', 'utf-16le'): + newEncoding = lookupEncoding('utf-8') assert newEncoding is not None elif newEncoding == self.charEncoding[0]: - self.charEncoding = (self.charEncoding[0], "certain") + self.charEncoding = (self.charEncoding[0], 'certain') else: self.rawStream.seek(0) - self.charEncoding = (newEncoding, "certain") + self.charEncoding = (newEncoding, 'certain') self.reset() - raise _ReparseException("Encoding changed from %s to %s" % (self.charEncoding[0], newEncoding)) + raise _ReparseException('Encoding changed from {} to {}'.format(self.charEncoding[0], newEncoding)) def detectBOM(self): """Attempts to detect at BOM at the start of the stream. If @@ -533,7 +551,7 @@ class HTMLBinaryInputStream(HTMLUnicodeInputStream): bomDict = { codecs.BOM_UTF8: 'utf-8', codecs.BOM_UTF16_LE: 'utf-16le', codecs.BOM_UTF16_BE: 'utf-16be', - codecs.BOM_UTF32_LE: 'utf-32le', codecs.BOM_UTF32_BE: 'utf-32be' + codecs.BOM_UTF32_LE: 'utf-32le', codecs.BOM_UTF32_BE: 'utf-32be', } # Go to beginning of file and read in 4 bytes @@ -569,8 +587,8 @@ class HTMLBinaryInputStream(HTMLUnicodeInputStream): self.rawStream.seek(0) encoding = parser.getEncoding() - if encoding is not None and encoding.name in ("utf-16be", "utf-16le"): - encoding = lookupEncoding("utf-8") + if encoding is not None and encoding.name in ('utf-16be', 'utf-16le'): + encoding = lookupEncoding('utf-8') return encoding @@ -579,6 +597,7 @@ class EncodingBytes(bytes): """String-like object with an associated position and various extra methods If the position is ever greater than the string length then an exception is raised""" + def __new__(self, value): assert isinstance(value, bytes) return bytes.__new__(self, value.lower()) @@ -673,7 +692,7 @@ class EncodingBytes(bytes): return True -class EncodingParser(object): +class EncodingParser: """Mini parser for detecting character encoding from meta elements""" def __init__(self, data): @@ -682,20 +701,21 @@ class EncodingParser(object): self.encoding = None def getEncoding(self): - if b"") + return self.data.jumpTo(b'-->') def handleMeta(self): if self.data.currentByte not in spaceCharactersBytes: @@ -728,18 +748,18 @@ class EncodingParser(object): if attr is None: return True else: - if attr[0] == b"http-equiv": - hasPragma = attr[1] == b"content-type" + if attr[0] == b'http-equiv': + hasPragma = attr[1] == b'content-type' if hasPragma and pendingEncoding is not None: self.encoding = pendingEncoding return False - elif attr[0] == b"charset": + elif attr[0] == b'charset': tentativeEncoding = attr[1] codec = lookupEncoding(tentativeEncoding) if codec is not None: self.encoding = codec return False - elif attr[0] == b"content": + elif attr[0] == b'content': contentParser = ContentAttrParser(EncodingBytes(attr[1])) tentativeEncoding = contentParser.parse() if tentativeEncoding is not None: @@ -770,7 +790,7 @@ class EncodingParser(object): return True c = data.skipUntil(spacesAngleBrackets) - if c == b"<": + if c == b'<': # return to the first step in the overall "two step" algorithm # reprocessing the < byte data.previous() @@ -782,31 +802,31 @@ class EncodingParser(object): return True def handleOther(self): - return self.data.jumpTo(b">") + return self.data.jumpTo(b'>') def getAttribute(self): """Return a name,value pair for the next attribute in the stream, if one is found, or None""" data = self.data # Step 1 (skip chars) - c = data.skip(spaceCharactersBytes | frozenset([b"/"])) + c = data.skip(spaceCharactersBytes | frozenset([b'/'])) assert c is None or len(c) == 1 # Step 2 - if c in (b">", None): + if c in (b'>', None): return None # Step 3 attrName = [] attrValue = [] # Step 4 attribute name while True: - if c == b"=" and attrName: + if c == b'=' and attrName: break elif c in spaceCharactersBytes: # Step 6! c = data.skip() break - elif c in (b"/", b">"): - return b"".join(attrName), b"" + elif c in (b'/', b'>'): + return b''.join(attrName), b'' elif c in asciiUppercaseBytes: attrName.append(c.lower()) elif c is None: @@ -816,9 +836,9 @@ class EncodingParser(object): # Step 5 c = next(data) # Step 7 - if c != b"=": + if c != b'=': data.previous() - return b"".join(attrName), b"" + return b''.join(attrName), b'' # Step 8 next(data) # Step 9 @@ -833,15 +853,15 @@ class EncodingParser(object): # 10.3 if c == quoteChar: next(data) - return b"".join(attrName), b"".join(attrValue) + return b''.join(attrName), b''.join(attrValue) # 10.4 elif c in asciiUppercaseBytes: attrValue.append(c.lower()) # 10.5 else: attrValue.append(c) - elif c == b">": - return b"".join(attrName), b"" + elif c == b'>': + return b''.join(attrName), b'' elif c in asciiUppercaseBytes: attrValue.append(c.lower()) elif c is None: @@ -852,7 +872,7 @@ class EncodingParser(object): while True: c = next(data) if c in spacesAngleBrackets: - return b"".join(attrName), b"".join(attrValue) + return b''.join(attrName), b''.join(attrValue) elif c in asciiUppercaseBytes: attrValue.append(c.lower()) elif c is None: @@ -861,7 +881,7 @@ class EncodingParser(object): attrValue.append(c) -class ContentAttrParser(object): +class ContentAttrParser: def __init__(self, data): assert isinstance(data, bytes) self.data = data @@ -870,10 +890,10 @@ class ContentAttrParser(object): try: # Check if the attr name is charset # otherwise return - self.data.jumpTo(b"charset") + self.data.jumpTo(b'charset') self.data.position += 1 self.data.skip() - if not self.data.currentByte == b"=": + if not self.data.currentByte == b'=': # If there is no = sign keep looking for attrs return None self.data.position += 1 @@ -905,7 +925,7 @@ def lookupEncoding(encoding): string doesn't correspond to a valid encoding.""" if isinstance(encoding, bytes): try: - encoding = encoding.decode("ascii") + encoding = encoding.decode('ascii') except UnicodeDecodeError: return None diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_tokenizer.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_tokenizer.py index 5f00253..f3ea91f 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_tokenizer.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_tokenizer.py @@ -1,30 +1,30 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations + +from collections import deque +from collections import OrderedDict +from sys import version_info from pip._vendor.six import unichr as chr -from collections import deque, OrderedDict -from sys import version_info - -from .constants import spaceCharacters -from .constants import entities -from .constants import asciiLetters, asciiUpper2Lower -from .constants import digits, hexDigits, EOF -from .constants import tokenTypes, tagTokenTypes -from .constants import replacementCharacters - from ._inputstream import HTMLInputStream - from ._trie import Trie +from .constants import asciiLetters +from .constants import asciiUpper2Lower +from .constants import digits +from .constants import entities +from .constants import EOF +from .constants import hexDigits +from .constants import replacementCharacters +from .constants import spaceCharacters +from .constants import tagTokenTypes +from .constants import tokenTypes entitiesTrie = Trie(entities) -if version_info >= (3, 7): - attributeMap = dict -else: - attributeMap = OrderedDict +attributeMap = dict -class HTMLTokenizer(object): +class HTMLTokenizer: """ This class takes care of tokenizing HTML. * self.currentToken @@ -50,7 +50,7 @@ class HTMLTokenizer(object): # The current token being created self.currentToken = None - super(HTMLTokenizer, self).__init__() + super().__init__() def __iter__(self): """ This is where the magic happens. @@ -64,7 +64,7 @@ class HTMLTokenizer(object): # instead of True and the loop will terminate. while self.state(): while self.stream.errors: - yield {"type": tokenTypes["ParseError"], "data": self.stream.errors.pop(0)} + yield {'type': tokenTypes['ParseError'], 'data': self.stream.errors.pop(0)} while self.tokenQueue: yield self.tokenQueue.popleft() @@ -90,39 +90,47 @@ class HTMLTokenizer(object): c = self.stream.char() # Convert the set of characters consumed to an int. - charAsInt = int("".join(charStack), radix) + charAsInt = int(''.join(charStack), radix) # Certain characters get replaced with others if charAsInt in replacementCharacters: char = replacementCharacters[charAsInt] - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "illegal-codepoint-for-numeric-entity", - "datavars": {"charAsInt": charAsInt}}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'illegal-codepoint-for-numeric-entity', + 'datavars': {'charAsInt': charAsInt}, + }) elif ((0xD800 <= charAsInt <= 0xDFFF) or (charAsInt > 0x10FFFF)): - char = "\uFFFD" - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "illegal-codepoint-for-numeric-entity", - "datavars": {"charAsInt": charAsInt}}) + char = '\uFFFD' + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'illegal-codepoint-for-numeric-entity', + 'datavars': {'charAsInt': charAsInt}, + }) else: # Should speed up this check somehow (e.g. move the set to a constant) if ((0x0001 <= charAsInt <= 0x0008) or - (0x000E <= charAsInt <= 0x001F) or - (0x007F <= charAsInt <= 0x009F) or - (0xFDD0 <= charAsInt <= 0xFDEF) or - charAsInt in frozenset([0x000B, 0xFFFE, 0xFFFF, 0x1FFFE, - 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, - 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, - 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, - 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, - 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, - 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, - 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, - 0xFFFFF, 0x10FFFE, 0x10FFFF])): - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": - "illegal-codepoint-for-numeric-entity", - "datavars": {"charAsInt": charAsInt}}) + (0x000E <= charAsInt <= 0x001F) or + (0x007F <= charAsInt <= 0x009F) or + (0xFDD0 <= charAsInt <= 0xFDEF) or + charAsInt in frozenset([ + 0x000B, 0xFFFE, 0xFFFF, 0x1FFFE, + 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, + 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, + 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, + 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, + 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, + 0xFFFFF, 0x10FFFE, 0x10FFFF, + ])): + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': + 'illegal-codepoint-for-numeric-entity', + 'datavars': {'charAsInt': charAsInt}, + }) try: # Try/except needed as UCS-2 Python builds' unichar only works # within the BMP. @@ -133,27 +141,31 @@ class HTMLTokenizer(object): # Discard the ; if present. Otherwise, put it back on the queue and # invoke parseError on parser. - if c != ";": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "numeric-entity-without-semicolon"}) + if c != ';': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'numeric-entity-without-semicolon', + }) self.stream.unget(c) return char def consumeEntity(self, allowedChar=None, fromAttribute=False): # Initialise to the default output for when no entity is matched - output = "&" + output = '&' charStack = [self.stream.char()] - if (charStack[0] in spaceCharacters or charStack[0] in (EOF, "<", "&") or - (allowedChar is not None and allowedChar == charStack[0])): + if ( + charStack[0] in spaceCharacters or charStack[0] in (EOF, '<', '&') or + (allowedChar is not None and allowedChar == charStack[0]) + ): self.stream.unget(charStack[0]) - elif charStack[0] == "#": + elif charStack[0] == '#': # Read the next character to see if it's hex or decimal hex = False charStack.append(self.stream.char()) - if charStack[-1] in ("x", "X"): + if charStack[-1] in ('x', 'X'): hex = True charStack.append(self.stream.char()) @@ -165,10 +177,12 @@ class HTMLTokenizer(object): output = self.consumeNumberEntity(hex) else: # No digits found - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "expected-numeric-entity"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'expected-numeric-entity', + }) self.stream.unget(charStack.pop()) - output = "&" + "".join(charStack) + output = '&' + ''.join(charStack) else: # At this point in the process might have named entity. Entities @@ -177,7 +191,7 @@ class HTMLTokenizer(object): # Consume characters and compare to these to a substring of the # entity names in the list until the substring no longer matches. while (charStack[-1] is not EOF): - if not entitiesTrie.has_keys_with_prefix("".join(charStack)): + if not entitiesTrie.has_keys_with_prefix(''.join(charStack)): break charStack.append(self.stream.char()) @@ -186,39 +200,47 @@ class HTMLTokenizer(object): # Try to find the longest entity the string will match to take care # of ¬i for instance. try: - entityName = entitiesTrie.longest_prefix("".join(charStack[:-1])) + entityName = entitiesTrie.longest_prefix(''.join(charStack[:-1])) entityLength = len(entityName) except KeyError: entityName = None if entityName is not None: - if entityName[-1] != ";": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "named-entity-without-semicolon"}) - if (entityName[-1] != ";" and fromAttribute and - (charStack[entityLength] in asciiLetters or - charStack[entityLength] in digits or - charStack[entityLength] == "=")): + if entityName[-1] != ';': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'named-entity-without-semicolon', + }) + if ( + entityName[-1] != ';' and fromAttribute and + ( + charStack[entityLength] in asciiLetters or + charStack[entityLength] in digits or + charStack[entityLength] == '=' + ) + ): self.stream.unget(charStack.pop()) - output = "&" + "".join(charStack) + output = '&' + ''.join(charStack) else: output = entities[entityName] self.stream.unget(charStack.pop()) - output += "".join(charStack[entityLength:]) + output += ''.join(charStack[entityLength:]) else: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-named-entity"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-named-entity', + }) self.stream.unget(charStack.pop()) - output = "&" + "".join(charStack) + output = '&' + ''.join(charStack) if fromAttribute: - self.currentToken["data"][-1][1] += output + self.currentToken['data'][-1][1] += output else: if output in spaceCharacters: - tokenType = "SpaceCharacters" + tokenType = 'SpaceCharacters' else: - tokenType = "Characters" - self.tokenQueue.append({"type": tokenTypes[tokenType], "data": output}) + tokenType = 'Characters' + self.tokenQueue.append({'type': tokenTypes[tokenType], 'data': output}) def processEntityInAttribute(self, allowedChar): """This method replaces the need for "entityInAttributeValueState". @@ -232,38 +254,46 @@ class HTMLTokenizer(object): """ token = self.currentToken # Add token to the queue to be yielded - if (token["type"] in tagTokenTypes): - token["name"] = token["name"].translate(asciiUpper2Lower) - if token["type"] == tokenTypes["StartTag"]: - raw = token["data"] + if (token['type'] in tagTokenTypes): + token['name'] = token['name'].translate(asciiUpper2Lower) + if token['type'] == tokenTypes['StartTag']: + raw = token['data'] data = attributeMap(raw) if len(raw) > len(data): # we had some duplicated attribute, fix so first wins data.update(raw[::-1]) - token["data"] = data + token['data'] = data - if token["type"] == tokenTypes["EndTag"]: - if token["data"]: - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "attributes-in-end-tag"}) - if token["selfClosing"]: - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "self-closing-flag-on-end-tag"}) + if token['type'] == tokenTypes['EndTag']: + if token['data']: + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'attributes-in-end-tag', + }) + if token['selfClosing']: + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'self-closing-flag-on-end-tag', + }) self.tokenQueue.append(token) self.state = self.dataState # Below are the various tokenizer states worked out. def dataState(self): data = self.stream.char() - if data == "&": + if data == '&': self.state = self.entityDataState - elif data == "<": + elif data == '<': self.state = self.tagOpenState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "\u0000"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': '\u0000', + }) elif data is EOF: # Tokenization ends. return False @@ -271,15 +301,19 @@ class HTMLTokenizer(object): # Directly after emitting a token you switch back to the "data # state". At that point spaceCharacters are important so they are # emitted separately. - self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": - data + self.stream.charsUntil(spaceCharacters, True)}) + self.tokenQueue.append({ + 'type': tokenTypes['SpaceCharacters'], 'data': + data + self.stream.charsUntil(spaceCharacters, True), + }) # No need to update lastFourChars here, since the first space will # have already been appended to lastFourChars and will have broken # any sequences else: - chars = self.stream.charsUntil(("&", "<", "\u0000")) - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": - data + chars}) + chars = self.stream.charsUntil(('&', '<', '\u0000')) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], 'data': + data + chars, + }) return True def entityDataState(self): @@ -289,31 +323,39 @@ class HTMLTokenizer(object): def rcdataState(self): data = self.stream.char() - if data == "&": + if data == '&': self.state = self.characterReferenceInRcdata - elif data == "<": + elif data == '<': self.state = self.rcdataLessThanSignState elif data == EOF: # Tokenization ends. return False - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "\uFFFD"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': '\uFFFD', + }) elif data in spaceCharacters: # Directly after emitting a token you switch back to the "data # state". At that point spaceCharacters are important so they are # emitted separately. - self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": - data + self.stream.charsUntil(spaceCharacters, True)}) + self.tokenQueue.append({ + 'type': tokenTypes['SpaceCharacters'], 'data': + data + self.stream.charsUntil(spaceCharacters, True), + }) # No need to update lastFourChars here, since the first space will # have already been appended to lastFourChars and will have broken # any sequences else: - chars = self.stream.charsUntil(("&", "<", "\u0000")) - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": - data + chars}) + chars = self.stream.charsUntil(('&', '<', '\u0000')) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], 'data': + data + chars, + }) return True def characterReferenceInRcdata(self): @@ -323,38 +365,50 @@ class HTMLTokenizer(object): def rawtextState(self): data = self.stream.char() - if data == "<": + if data == '<': self.state = self.rawtextLessThanSignState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "\uFFFD"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': '\uFFFD', + }) elif data == EOF: # Tokenization ends. return False else: - chars = self.stream.charsUntil(("<", "\u0000")) - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": - data + chars}) + chars = self.stream.charsUntil(('<', '\u0000')) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], 'data': + data + chars, + }) return True def scriptDataState(self): data = self.stream.char() - if data == "<": + if data == '<': self.state = self.scriptDataLessThanSignState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "\uFFFD"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': '\uFFFD', + }) elif data == EOF: # Tokenization ends. return False else: - chars = self.stream.charsUntil(("<", "\u0000")) - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": - data + chars}) + chars = self.stream.charsUntil(('<', '\u0000')) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], 'data': + data + chars, + }) return True def plaintextState(self): @@ -362,47 +416,61 @@ class HTMLTokenizer(object): if data == EOF: # Tokenization ends. return False - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "\uFFFD"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': '\uFFFD', + }) else: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": - data + self.stream.charsUntil("\u0000")}) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], 'data': + data + self.stream.charsUntil('\u0000'), + }) return True def tagOpenState(self): data = self.stream.char() - if data == "!": + if data == '!': self.state = self.markupDeclarationOpenState - elif data == "/": + elif data == '/': self.state = self.closeTagOpenState elif data in asciiLetters: - self.currentToken = {"type": tokenTypes["StartTag"], - "name": data, "data": [], - "selfClosing": False, - "selfClosingAcknowledged": False} + self.currentToken = { + 'type': tokenTypes['StartTag'], + 'name': data, 'data': [], + 'selfClosing': False, + 'selfClosingAcknowledged': False, + } self.state = self.tagNameState - elif data == ">": + elif data == '>': # XXX In theory it could be something besides a tag name. But # do we really care? - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-tag-name-but-got-right-bracket"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<>"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-tag-name-but-got-right-bracket', + }) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '<>'}) self.state = self.dataState - elif data == "?": + elif data == '?': # XXX In theory it could be something besides a tag name. But # do we really care? - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-tag-name-but-got-question-mark"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-tag-name-but-got-question-mark', + }) self.stream.unget(data) self.state = self.bogusCommentState else: # XXX - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-tag-name"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-tag-name', + }) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '<'}) self.stream.unget(data) self.state = self.dataState return True @@ -410,23 +478,31 @@ class HTMLTokenizer(object): def closeTagOpenState(self): data = self.stream.char() if data in asciiLetters: - self.currentToken = {"type": tokenTypes["EndTag"], "name": data, - "data": [], "selfClosing": False} + self.currentToken = { + 'type': tokenTypes['EndTag'], 'name': data, + 'data': [], 'selfClosing': False, + } self.state = self.tagNameState - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-closing-tag-but-got-right-bracket"}) + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-closing-tag-but-got-right-bracket', + }) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-closing-tag-but-got-eof"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "": + elif data == '>': self.emitCurrentToken() elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-tag-name"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-tag-name', + }) self.state = self.dataState - elif data == "/": + elif data == '/': self.state = self.selfClosingStartTagState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["name"] += "\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['name'] += '\uFFFD' else: - self.currentToken["name"] += data + self.currentToken['name'] += data # (Don't use charsUntil here, because tag names are # very short and it's faster to not do anything fancy) return True def rcdataLessThanSignState(self): data = self.stream.char() - if data == "/": - self.temporaryBuffer = "" + if data == '/': + self.temporaryBuffer = '' self.state = self.rcdataEndTagOpenState else: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '<'}) self.stream.unget(data) self.state = self.rcdataState return True @@ -470,46 +550,54 @@ class HTMLTokenizer(object): self.temporaryBuffer += data self.state = self.rcdataEndTagNameState else: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "" and appropriate: - self.currentToken = {"type": tokenTypes["EndTag"], - "name": self.temporaryBuffer, - "data": [], "selfClosing": False} + elif data == '>' and appropriate: + self.currentToken = { + 'type': tokenTypes['EndTag'], + 'name': self.temporaryBuffer, + 'data': [], 'selfClosing': False, + } self.emitCurrentToken() self.state = self.dataState elif data in asciiLetters: self.temporaryBuffer += data else: - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "" and appropriate: - self.currentToken = {"type": tokenTypes["EndTag"], - "name": self.temporaryBuffer, - "data": [], "selfClosing": False} + elif data == '>' and appropriate: + self.currentToken = { + 'type': tokenTypes['EndTag'], + 'name': self.temporaryBuffer, + 'data': [], 'selfClosing': False, + } self.emitCurrentToken() self.state = self.dataState elif data in asciiLetters: self.temporaryBuffer += data else: - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "" and appropriate: - self.currentToken = {"type": tokenTypes["EndTag"], - "name": self.temporaryBuffer, - "data": [], "selfClosing": False} + elif data == '>' and appropriate: + self.currentToken = { + 'type': tokenTypes['EndTag'], + 'name': self.temporaryBuffer, + 'data': [], 'selfClosing': False, + } self.emitCurrentToken() self.state = self.dataState elif data in asciiLetters: self.temporaryBuffer += data else: - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "": - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + elif data == '>': + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '>'}) self.state = self.scriptDataState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "\uFFFD"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': '\uFFFD', + }) self.state = self.scriptDataEscapedState elif data == EOF: self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': data}) self.state = self.scriptDataEscapedState return True def scriptDataEscapedLessThanSignState(self): data = self.stream.char() - if data == "/": - self.temporaryBuffer = "" + if data == '/': + self.temporaryBuffer = '' self.state = self.scriptDataEscapedEndTagOpenState elif data in asciiLetters: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<" + data}) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '<' + data}) self.temporaryBuffer = data self.state = self.scriptDataDoubleEscapeStartState else: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '<'}) self.stream.unget(data) self.state = self.scriptDataEscapedState return True @@ -709,49 +827,57 @@ class HTMLTokenizer(object): self.temporaryBuffer = data self.state = self.scriptDataEscapedEndTagNameState else: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "" and appropriate: - self.currentToken = {"type": tokenTypes["EndTag"], - "name": self.temporaryBuffer, - "data": [], "selfClosing": False} + elif data == '>' and appropriate: + self.currentToken = { + 'type': tokenTypes['EndTag'], + 'name': self.temporaryBuffer, + 'data': [], 'selfClosing': False, + } self.emitCurrentToken() self.state = self.dataState elif data in asciiLetters: self.temporaryBuffer += data else: - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": ""))): - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) - if self.temporaryBuffer.lower() == "script": + if data in (spaceCharacters | frozenset(('/', '>'))): + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': data}) + if self.temporaryBuffer.lower() == 'script': self.state = self.scriptDataDoubleEscapedState else: self.state = self.scriptDataEscapedState elif data in asciiLetters: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': data}) self.temporaryBuffer += data else: self.stream.unget(data) @@ -760,78 +886,96 @@ class HTMLTokenizer(object): def scriptDataDoubleEscapedState(self): data = self.stream.char() - if data == "-": - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + if data == '-': + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '-'}) self.state = self.scriptDataDoubleEscapedDashState - elif data == "<": - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + elif data == '<': + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '<'}) self.state = self.scriptDataDoubleEscapedLessThanSignState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "\uFFFD"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': '\uFFFD', + }) elif data == EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-script-in-script"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-script-in-script', + }) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': data}) return True def scriptDataDoubleEscapedDashState(self): data = self.stream.char() - if data == "-": - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + if data == '-': + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '-'}) self.state = self.scriptDataDoubleEscapedDashDashState - elif data == "<": - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + elif data == '<': + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '<'}) self.state = self.scriptDataDoubleEscapedLessThanSignState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "\uFFFD"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': '\uFFFD', + }) self.state = self.scriptDataDoubleEscapedState elif data == EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-script-in-script"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-script-in-script', + }) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': data}) self.state = self.scriptDataDoubleEscapedState return True def scriptDataDoubleEscapedDashDashState(self): data = self.stream.char() - if data == "-": - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) - elif data == "<": - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + if data == '-': + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '-'}) + elif data == '<': + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '<'}) self.state = self.scriptDataDoubleEscapedLessThanSignState - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + elif data == '>': + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '>'}) self.state = self.scriptDataState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": "\uFFFD"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': '\uFFFD', + }) self.state = self.scriptDataDoubleEscapedState elif data == EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-script-in-script"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-script-in-script', + }) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': data}) self.state = self.scriptDataDoubleEscapedState return True def scriptDataDoubleEscapedLessThanSignState(self): data = self.stream.char() - if data == "/": - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "/"}) - self.temporaryBuffer = "" + if data == '/': + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': '/'}) + self.temporaryBuffer = '' self.state = self.scriptDataDoubleEscapeEndState else: self.stream.unget(data) @@ -840,14 +984,14 @@ class HTMLTokenizer(object): def scriptDataDoubleEscapeEndState(self): data = self.stream.char() - if data in (spaceCharacters | frozenset(("/", ">"))): - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) - if self.temporaryBuffer.lower() == "script": + if data in (spaceCharacters | frozenset(('/', '>'))): + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': data}) + if self.temporaryBuffer.lower() == 'script': self.state = self.scriptDataEscapedState else: self.state = self.scriptDataDoubleEscapedState elif data in asciiLetters: - self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.tokenQueue.append({'type': tokenTypes['Characters'], 'data': data}) self.temporaryBuffer += data else: self.stream.unget(data) @@ -859,28 +1003,34 @@ class HTMLTokenizer(object): if data in spaceCharacters: self.stream.charsUntil(spaceCharacters, True) elif data in asciiLetters: - self.currentToken["data"].append([data, ""]) + self.currentToken['data'].append([data, '']) self.state = self.attributeNameState - elif data == ">": + elif data == '>': self.emitCurrentToken() - elif data == "/": + elif data == '/': self.state = self.selfClosingStartTagState - elif data in ("'", '"', "=", "<"): - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "invalid-character-in-attribute-name"}) - self.currentToken["data"].append([data, ""]) + elif data in ("'", '"', '=', '<'): + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'invalid-character-in-attribute-name', + }) + self.currentToken['data'].append([data, '']) self.state = self.attributeNameState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"].append(["\uFFFD", ""]) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'].append(['\uFFFD', '']) self.state = self.attributeNameState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-attribute-name-but-got-eof"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-attribute-name-but-got-eof', + }) self.state = self.dataState else: - self.currentToken["data"].append([data, ""]) + self.currentToken['data'].append([data, '']) self.state = self.attributeNameState return True @@ -888,50 +1038,59 @@ class HTMLTokenizer(object): data = self.stream.char() leavingThisState = True emitToken = False - if data == "=": + if data == '=': self.state = self.beforeAttributeValueState elif data in asciiLetters: - self.currentToken["data"][-1][0] += data +\ + self.currentToken['data'][-1][0] += data +\ self.stream.charsUntil(asciiLetters, True) leavingThisState = False - elif data == ">": + elif data == '>': # XXX If we emit here the attributes are converted to a dict # without being checked and when the code below runs we error # because data is a dict not a list emitToken = True elif data in spaceCharacters: self.state = self.afterAttributeNameState - elif data == "/": + elif data == '/': self.state = self.selfClosingStartTagState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"][-1][0] += "\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'][-1][0] += '\uFFFD' leavingThisState = False - elif data in ("'", '"', "<"): - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": - "invalid-character-in-attribute-name"}) - self.currentToken["data"][-1][0] += data + elif data in ("'", '"', '<'): + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': + 'invalid-character-in-attribute-name', + }) + self.currentToken['data'][-1][0] += data leavingThisState = False elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "eof-in-attribute-name"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'eof-in-attribute-name', + }) self.state = self.dataState else: - self.currentToken["data"][-1][0] += data + self.currentToken['data'][-1][0] += data leavingThisState = False if leavingThisState: # Attributes are not dropped at this stage. That happens when the # start tag token is emitted so values can still be safely appended # to attributes, but we do want to report the parse error in time. - self.currentToken["data"][-1][0] = ( - self.currentToken["data"][-1][0].translate(asciiUpper2Lower)) - for name, _ in self.currentToken["data"][:-1]: - if self.currentToken["data"][-1][0] == name: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "duplicate-attribute"}) + self.currentToken['data'][-1][0] = ( + self.currentToken['data'][-1][0].translate(asciiUpper2Lower) + ) + for name, _ in self.currentToken['data'][:-1]: + if self.currentToken['data'][-1][0] == name: + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'duplicate-attribute', + }) break # XXX Fix for above XXX if emitToken: @@ -942,31 +1101,37 @@ class HTMLTokenizer(object): data = self.stream.char() if data in spaceCharacters: self.stream.charsUntil(spaceCharacters, True) - elif data == "=": + elif data == '=': self.state = self.beforeAttributeValueState - elif data == ">": + elif data == '>': self.emitCurrentToken() elif data in asciiLetters: - self.currentToken["data"].append([data, ""]) + self.currentToken['data'].append([data, '']) self.state = self.attributeNameState - elif data == "/": + elif data == '/': self.state = self.selfClosingStartTagState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"].append(["\uFFFD", ""]) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'].append(['\uFFFD', '']) self.state = self.attributeNameState - elif data in ("'", '"', "<"): - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "invalid-character-after-attribute-name"}) - self.currentToken["data"].append([data, ""]) + elif data in ("'", '"', '<'): + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'invalid-character-after-attribute-name', + }) + self.currentToken['data'].append([data, '']) self.state = self.attributeNameState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-end-of-tag-but-got-eof"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-end-of-tag-but-got-eof', + }) self.state = self.dataState else: - self.currentToken["data"].append([data, ""]) + self.currentToken['data'].append([data, '']) self.state = self.attributeNameState return True @@ -976,31 +1141,39 @@ class HTMLTokenizer(object): self.stream.charsUntil(spaceCharacters, True) elif data == "\"": self.state = self.attributeValueDoubleQuotedState - elif data == "&": + elif data == '&': self.state = self.attributeValueUnQuotedState self.stream.unget(data) elif data == "'": self.state = self.attributeValueSingleQuotedState - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-attribute-value-but-got-right-bracket"}) + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-attribute-value-but-got-right-bracket', + }) self.emitCurrentToken() - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"][-1][1] += "\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'][-1][1] += '\uFFFD' self.state = self.attributeValueUnQuotedState - elif data in ("=", "<", "`"): - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "equals-in-unquoted-attribute-value"}) - self.currentToken["data"][-1][1] += data + elif data in ('=', '<', '`'): + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'equals-in-unquoted-attribute-value', + }) + self.currentToken['data'][-1][1] += data self.state = self.attributeValueUnQuotedState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-attribute-value-but-got-eof"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-attribute-value-but-got-eof', + }) self.state = self.dataState else: - self.currentToken["data"][-1][1] += data + self.currentToken['data'][-1][1] += data self.state = self.attributeValueUnQuotedState return True @@ -1008,99 +1181,122 @@ class HTMLTokenizer(object): data = self.stream.char() if data == "\"": self.state = self.afterAttributeValueState - elif data == "&": + elif data == '&': self.processEntityInAttribute('"') - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"][-1][1] += "\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'][-1][1] += '\uFFFD' elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-attribute-value-double-quote"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-attribute-value-double-quote', + }) self.state = self.dataState else: - self.currentToken["data"][-1][1] += data +\ - self.stream.charsUntil(("\"", "&", "\u0000")) + self.currentToken['data'][-1][1] += data +\ + self.stream.charsUntil(("\"", '&', '\u0000')) return True def attributeValueSingleQuotedState(self): data = self.stream.char() if data == "'": self.state = self.afterAttributeValueState - elif data == "&": + elif data == '&': self.processEntityInAttribute("'") - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"][-1][1] += "\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'][-1][1] += '\uFFFD' elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-attribute-value-single-quote"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-attribute-value-single-quote', + }) self.state = self.dataState else: - self.currentToken["data"][-1][1] += data +\ - self.stream.charsUntil(("'", "&", "\u0000")) + self.currentToken['data'][-1][1] += data +\ + self.stream.charsUntil(("'", '&', '\u0000')) return True def attributeValueUnQuotedState(self): data = self.stream.char() if data in spaceCharacters: self.state = self.beforeAttributeNameState - elif data == "&": - self.processEntityInAttribute(">") - elif data == ">": + elif data == '&': + self.processEntityInAttribute('>') + elif data == '>': self.emitCurrentToken() - elif data in ('"', "'", "=", "<", "`"): - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-character-in-unquoted-attribute-value"}) - self.currentToken["data"][-1][1] += data - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"][-1][1] += "\uFFFD" + elif data in ('"', "'", '=', '<', '`'): + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-character-in-unquoted-attribute-value', + }) + self.currentToken['data'][-1][1] += data + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'][-1][1] += '\uFFFD' elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-attribute-value-no-quotes"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-attribute-value-no-quotes', + }) self.state = self.dataState else: - self.currentToken["data"][-1][1] += data + self.stream.charsUntil( - frozenset(("&", ">", '"', "'", "=", "<", "`", "\u0000")) | spaceCharacters) + self.currentToken['data'][-1][1] += data + self.stream.charsUntil( + frozenset(('&', '>', '"', "'", '=', '<', '`', '\u0000')) | spaceCharacters, + ) return True def afterAttributeValueState(self): data = self.stream.char() if data in spaceCharacters: self.state = self.beforeAttributeNameState - elif data == ">": + elif data == '>': self.emitCurrentToken() - elif data == "/": + elif data == '/': self.state = self.selfClosingStartTagState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-EOF-after-attribute-value"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-EOF-after-attribute-value', + }) self.stream.unget(data) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-character-after-attribute-value"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-character-after-attribute-value', + }) self.stream.unget(data) self.state = self.beforeAttributeNameState return True def selfClosingStartTagState(self): data = self.stream.char() - if data == ">": - self.currentToken["selfClosing"] = True + if data == '>': + self.currentToken['selfClosing'] = True self.emitCurrentToken() elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": - "unexpected-EOF-after-solidus-in-tag"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': + 'unexpected-EOF-after-solidus-in-tag', + }) self.stream.unget(data) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-character-after-solidus-in-tag"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-character-after-solidus-in-tag', + }) self.stream.unget(data) self.state = self.beforeAttributeNameState return True @@ -1109,10 +1305,11 @@ class HTMLTokenizer(object): # Make a new comment token and give it as value all the characters # until the first > or EOF (charsUntil checks for EOF automatically) # and emit it. - data = self.stream.charsUntil(">") - data = data.replace("\u0000", "\uFFFD") + data = self.stream.charsUntil('>') + data = data.replace('\u0000', '\uFFFD') self.tokenQueue.append( - {"type": tokenTypes["Comment"], "data": data}) + {'type': tokenTypes['Comment'], 'data': data}, + ) # Eat the character directly after the bogus comment which is either a # ">" or an EOF. @@ -1122,33 +1319,39 @@ class HTMLTokenizer(object): def markupDeclarationOpenState(self): charStack = [self.stream.char()] - if charStack[-1] == "-": + if charStack[-1] == '-': charStack.append(self.stream.char()) - if charStack[-1] == "-": - self.currentToken = {"type": tokenTypes["Comment"], "data": ""} + if charStack[-1] == '-': + self.currentToken = {'type': tokenTypes['Comment'], 'data': ''} self.state = self.commentStartState return True elif charStack[-1] in ('d', 'D'): matched = True - for expected in (('o', 'O'), ('c', 'C'), ('t', 'T'), - ('y', 'Y'), ('p', 'P'), ('e', 'E')): + for expected in ( + ('o', 'O'), ('c', 'C'), ('t', 'T'), + ('y', 'Y'), ('p', 'P'), ('e', 'E'), + ): charStack.append(self.stream.char()) if charStack[-1] not in expected: matched = False break if matched: - self.currentToken = {"type": tokenTypes["Doctype"], - "name": "", - "publicId": None, "systemId": None, - "correct": True} + self.currentToken = { + 'type': tokenTypes['Doctype'], + 'name': '', + 'publicId': None, 'systemId': None, + 'correct': True, + } self.state = self.doctypeState return True - elif (charStack[-1] == "[" and - self.parser is not None and - self.parser.tree.openElements and - self.parser.tree.openElements[-1].namespace != self.parser.tree.defaultNamespace): + elif ( + charStack[-1] == '[' and + self.parser is not None and + self.parser.tree.openElements and + self.parser.tree.openElements[-1].namespace != self.parser.tree.defaultNamespace + ): matched = True - for expected in ["C", "D", "A", "T", "A", "["]: + for expected in ['C', 'D', 'A', 'T', 'A', '[']: charStack.append(self.stream.char()) if charStack[-1] != expected: matched = False @@ -1157,8 +1360,10 @@ class HTMLTokenizer(object): self.state = self.cdataSectionState return True - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-dashes-or-doctype"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-dashes-or-doctype', + }) while charStack: self.stream.unget(charStack.pop()) @@ -1167,138 +1372,172 @@ class HTMLTokenizer(object): def commentStartState(self): data = self.stream.char() - if data == "-": + if data == '-': self.state = self.commentStartDashState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"] += "\uFFFD" - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "incorrect-comment"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'] += '\uFFFD' + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'incorrect-comment', + }) self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-comment"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-comment', + }) self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["data"] += data + self.currentToken['data'] += data self.state = self.commentState return True def commentStartDashState(self): data = self.stream.char() - if data == "-": + if data == '-': self.state = self.commentEndState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"] += "-\uFFFD" - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "incorrect-comment"}) + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'] += '-\uFFFD' + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'incorrect-comment', + }) self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-comment"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-comment', + }) self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["data"] += "-" + data + self.currentToken['data'] += '-' + data self.state = self.commentState return True def commentState(self): data = self.stream.char() - if data == "-": + if data == '-': self.state = self.commentEndDashState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"] += "\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'] += '\uFFFD' elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "eof-in-comment"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'eof-in-comment', + }) self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["data"] += data + \ - self.stream.charsUntil(("-", "\u0000")) + self.currentToken['data'] += data + \ + self.stream.charsUntil(('-', '\u0000')) return True def commentEndDashState(self): data = self.stream.char() - if data == "-": + if data == '-': self.state = self.commentEndState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"] += "-\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'] += '-\uFFFD' self.state = self.commentState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-comment-end-dash"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-comment-end-dash', + }) self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["data"] += "-" + data + self.currentToken['data'] += '-' + data self.state = self.commentState return True def commentEndState(self): data = self.stream.char() - if data == ">": + if data == '>': self.tokenQueue.append(self.currentToken) self.state = self.dataState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"] += "--\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'] += '--\uFFFD' self.state = self.commentState - elif data == "!": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-bang-after-double-dash-in-comment"}) + elif data == '!': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-bang-after-double-dash-in-comment', + }) self.state = self.commentEndBangState - elif data == "-": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-dash-after-double-dash-in-comment"}) - self.currentToken["data"] += data + elif data == '-': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-dash-after-double-dash-in-comment', + }) + self.currentToken['data'] += data elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-comment-double-dash"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-comment-double-dash', + }) self.tokenQueue.append(self.currentToken) self.state = self.dataState else: # XXX - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-comment"}) - self.currentToken["data"] += "--" + data + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-comment', + }) + self.currentToken['data'] += '--' + data self.state = self.commentState return True def commentEndBangState(self): data = self.stream.char() - if data == ">": + if data == '>': self.tokenQueue.append(self.currentToken) self.state = self.dataState - elif data == "-": - self.currentToken["data"] += "--!" + elif data == '-': + self.currentToken['data'] += '--!' self.state = self.commentEndDashState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["data"] += "--!\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['data'] += '--!\uFFFD' self.state = self.commentState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-comment-end-bang-state"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-comment-end-bang-state', + }) self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["data"] += "--!" + data + self.currentToken['data'] += '--!' + data self.state = self.commentState return True @@ -1307,14 +1546,18 @@ class HTMLTokenizer(object): if data in spaceCharacters: self.state = self.beforeDoctypeNameState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-doctype-name-but-got-eof"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-doctype-name-but-got-eof', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "need-space-after-doctype"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'need-space-after-doctype', + }) self.stream.unget(data) self.state = self.beforeDoctypeNameState return True @@ -1323,72 +1566,86 @@ class HTMLTokenizer(object): data = self.stream.char() if data in spaceCharacters: pass - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-doctype-name-but-got-right-bracket"}) - self.currentToken["correct"] = False + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-doctype-name-but-got-right-bracket', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["name"] = "\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['name'] = '\uFFFD' self.state = self.doctypeNameState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-doctype-name-but-got-eof"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-doctype-name-but-got-eof', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["name"] = data + self.currentToken['name'] = data self.state = self.doctypeNameState return True def doctypeNameState(self): data = self.stream.char() if data in spaceCharacters: - self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.currentToken['name'] = self.currentToken['name'].translate(asciiUpper2Lower) self.state = self.afterDoctypeNameState - elif data == ">": - self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + elif data == '>': + self.currentToken['name'] = self.currentToken['name'].translate(asciiUpper2Lower) self.tokenQueue.append(self.currentToken) self.state = self.dataState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["name"] += "\uFFFD" + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['name'] += '\uFFFD' self.state = self.doctypeNameState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype-name"}) - self.currentToken["correct"] = False - self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype-name', + }) + self.currentToken['correct'] = False + self.currentToken['name'] = self.currentToken['name'].translate(asciiUpper2Lower) self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["name"] += data + self.currentToken['name'] += data return True def afterDoctypeNameState(self): data = self.stream.char() if data in spaceCharacters: pass - elif data == ">": + elif data == '>': self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.currentToken["correct"] = False + self.currentToken['correct'] = False self.stream.unget(data) - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - if data in ("p", "P"): + if data in ('p', 'P'): matched = True - for expected in (("u", "U"), ("b", "B"), ("l", "L"), - ("i", "I"), ("c", "C")): + for expected in ( + ('u', 'U'), ('b', 'B'), ('l', 'L'), + ('i', 'I'), ('c', 'C'), + ): data = self.stream.char() if data not in expected: matched = False @@ -1396,10 +1653,12 @@ class HTMLTokenizer(object): if matched: self.state = self.afterDoctypePublicKeywordState return True - elif data in ("s", "S"): + elif data in ('s', 'S'): matched = True - for expected in (("y", "Y"), ("s", "S"), ("t", "T"), - ("e", "E"), ("m", "M")): + for expected in ( + ('y', 'Y'), ('s', 'S'), ('t', 'T'), + ('e', 'E'), ('m', 'M'), + ): data = self.stream.char() if data not in expected: matched = False @@ -1413,10 +1672,12 @@ class HTMLTokenizer(object): # discarded; only the latest character might be '>' or EOF # and needs to be ungetted self.stream.unget(data) - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "expected-space-or-right-bracket-in-doctype", "datavars": - {"data": data}}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'expected-space-or-right-bracket-in-doctype', 'datavars': + {'data': data}, + }) + self.currentToken['correct'] = False self.state = self.bogusDoctypeState return True @@ -1426,14 +1687,18 @@ class HTMLTokenizer(object): if data in spaceCharacters: self.state = self.beforeDoctypePublicIdentifierState elif data in ("'", '"'): - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) self.stream.unget(data) self.state = self.beforeDoctypePublicIdentifierState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: @@ -1446,27 +1711,33 @@ class HTMLTokenizer(object): if data in spaceCharacters: pass elif data == "\"": - self.currentToken["publicId"] = "" + self.currentToken['publicId'] = '' self.state = self.doctypePublicIdentifierDoubleQuotedState elif data == "'": - self.currentToken["publicId"] = "" + self.currentToken['publicId'] = '' self.state = self.doctypePublicIdentifierSingleQuotedState - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-end-of-doctype"}) - self.currentToken["correct"] = False + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-end-of-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) + self.currentToken['correct'] = False self.state = self.bogusDoctypeState return True @@ -1474,77 +1745,97 @@ class HTMLTokenizer(object): data = self.stream.char() if data == "\"": self.state = self.afterDoctypePublicIdentifierState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["publicId"] += "\uFFFD" - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-end-of-doctype"}) - self.currentToken["correct"] = False + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['publicId'] += '\uFFFD' + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-end-of-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["publicId"] += data + self.currentToken['publicId'] += data return True def doctypePublicIdentifierSingleQuotedState(self): data = self.stream.char() if data == "'": self.state = self.afterDoctypePublicIdentifierState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["publicId"] += "\uFFFD" - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-end-of-doctype"}) - self.currentToken["correct"] = False + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['publicId'] += '\uFFFD' + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-end-of-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["publicId"] += data + self.currentToken['publicId'] += data return True def afterDoctypePublicIdentifierState(self): data = self.stream.char() if data in spaceCharacters: self.state = self.betweenDoctypePublicAndSystemIdentifiersState - elif data == ">": + elif data == '>': self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data == '"': - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) - self.currentToken["systemId"] = "" + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) + self.currentToken['systemId'] = '' self.state = self.doctypeSystemIdentifierDoubleQuotedState elif data == "'": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) - self.currentToken["systemId"] = "" + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) + self.currentToken['systemId'] = '' self.state = self.doctypeSystemIdentifierSingleQuotedState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) + self.currentToken['correct'] = False self.state = self.bogusDoctypeState return True @@ -1552,25 +1843,29 @@ class HTMLTokenizer(object): data = self.stream.char() if data in spaceCharacters: pass - elif data == ">": + elif data == '>': self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data == '"': - self.currentToken["systemId"] = "" + self.currentToken['systemId'] = '' self.state = self.doctypeSystemIdentifierDoubleQuotedState elif data == "'": - self.currentToken["systemId"] = "" + self.currentToken['systemId'] = '' self.state = self.doctypeSystemIdentifierSingleQuotedState elif data == EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) + self.currentToken['correct'] = False self.state = self.bogusDoctypeState return True @@ -1579,14 +1874,18 @@ class HTMLTokenizer(object): if data in spaceCharacters: self.state = self.beforeDoctypeSystemIdentifierState elif data in ("'", '"'): - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) self.stream.unget(data) self.state = self.beforeDoctypeSystemIdentifierState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: @@ -1599,27 +1898,33 @@ class HTMLTokenizer(object): if data in spaceCharacters: pass elif data == "\"": - self.currentToken["systemId"] = "" + self.currentToken['systemId'] = '' self.state = self.doctypeSystemIdentifierDoubleQuotedState elif data == "'": - self.currentToken["systemId"] = "" + self.currentToken['systemId'] = '' self.state = self.doctypeSystemIdentifierSingleQuotedState - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) - self.currentToken["correct"] = False + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) + self.currentToken['correct'] = False self.state = self.bogusDoctypeState return True @@ -1627,72 +1932,88 @@ class HTMLTokenizer(object): data = self.stream.char() if data == "\"": self.state = self.afterDoctypeSystemIdentifierState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["systemId"] += "\uFFFD" - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-end-of-doctype"}) - self.currentToken["correct"] = False + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['systemId'] += '\uFFFD' + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-end-of-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["systemId"] += data + self.currentToken['systemId'] += data return True def doctypeSystemIdentifierSingleQuotedState(self): data = self.stream.char() if data == "'": self.state = self.afterDoctypeSystemIdentifierState - elif data == "\u0000": - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - self.currentToken["systemId"] += "\uFFFD" - elif data == ">": - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-end-of-doctype"}) - self.currentToken["correct"] = False + elif data == '\u0000': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + self.currentToken['systemId'] += '\uFFFD' + elif data == '>': + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-end-of-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.currentToken["systemId"] += data + self.currentToken['systemId'] += data return True def afterDoctypeSystemIdentifierState(self): data = self.stream.char() if data in spaceCharacters: pass - elif data == ">": + elif data == '>': self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "eof-in-doctype"}) - self.currentToken["correct"] = False + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'eof-in-doctype', + }) + self.currentToken['correct'] = False self.tokenQueue.append(self.currentToken) self.state = self.dataState else: - self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": - "unexpected-char-in-doctype"}) + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], 'data': + 'unexpected-char-in-doctype', + }) self.state = self.bogusDoctypeState return True def bogusDoctypeState(self): data = self.stream.char() - if data == ">": + if data == '>': self.tokenQueue.append(self.currentToken) self.state = self.dataState elif data is EOF: @@ -1707,29 +2028,33 @@ class HTMLTokenizer(object): def cdataSectionState(self): data = [] while True: - data.append(self.stream.charsUntil("]")) - data.append(self.stream.charsUntil(">")) + data.append(self.stream.charsUntil(']')) + data.append(self.stream.charsUntil('>')) char = self.stream.char() if char == EOF: break else: - assert char == ">" - if data[-1][-2:] == "]]": + assert char == '>' + if data[-1][-2:] == ']]': data[-1] = data[-1][:-2] break else: data.append(char) - data = "".join(data) # pylint:disable=redefined-variable-type + data = ''.join(data) # pylint:disable=redefined-variable-type # Deal with null here rather than in the parser - nullCount = data.count("\u0000") + nullCount = data.count('\u0000') if nullCount > 0: for _ in range(nullCount): - self.tokenQueue.append({"type": tokenTypes["ParseError"], - "data": "invalid-codepoint"}) - data = data.replace("\u0000", "\uFFFD") + self.tokenQueue.append({ + 'type': tokenTypes['ParseError'], + 'data': 'invalid-codepoint', + }) + data = data.replace('\u0000', '\uFFFD') if data: - self.tokenQueue.append({"type": tokenTypes["Characters"], - "data": data}) + self.tokenQueue.append({ + 'type': tokenTypes['Characters'], + 'data': data, + }) self.state = self.dataState return True diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/__init__.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/__init__.py index 07bad5d..250f1c4 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/__init__.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/__init__.py @@ -1,5 +1,5 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations from .py import Trie -__all__ = ["Trie"] +__all__ = ['Trie'] diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/_base.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/_base.py index 6b71975..64867e9 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/_base.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/_base.py @@ -1,9 +1,9 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations try: from collections.abc import Mapping except ImportError: # Python 2.7 - from collections import Mapping + from collections.abc import Mapping class Trie(Mapping): @@ -11,7 +11,7 @@ class Trie(Mapping): def keys(self, prefix=None): # pylint:disable=arguments-differ - keys = super(Trie, self).keys() + keys = super().keys() if prefix is None: return set(keys) diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/py.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/py.py index c178b21..b54c17a 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/py.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/py.py @@ -1,19 +1,20 @@ -from __future__ import absolute_import, division, unicode_literals -from pip._vendor.six import text_type +from __future__ import annotations from bisect import bisect_left +from pip._vendor.six import text_type + from ._base import Trie as ABCTrie class Trie(ABCTrie): def __init__(self, data): if not all(isinstance(x, text_type) for x in data.keys()): - raise TypeError("All keys must be strings") + raise TypeError('All keys must be strings') self._data = data self._keys = sorted(data.keys()) - self._cachestr = "" + self._cachestr = '' self._cachepoints = (0, len(data)) def __contains__(self, key): @@ -29,7 +30,7 @@ class Trie(ABCTrie): return self._data[key] def keys(self, prefix=None): - if prefix is None or prefix == "" or not self._keys: + if prefix is None or prefix == '' or not self._keys: return set(self._keys) if prefix.startswith(self._cachestr): diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_utils.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_utils.py index d7c4926..bbc7666 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_utils.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/_utils.py @@ -1,11 +1,11 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations from types import ModuleType try: from collections.abc import Mapping except ImportError: - from collections import Mapping + from collections.abc import Mapping from pip._vendor.six import text_type, PY3 @@ -13,14 +13,16 @@ if PY3: import xml.etree.ElementTree as default_etree else: try: - import xml.etree.cElementTree as default_etree + import xml.etree.ElementTree as default_etree except ImportError: import xml.etree.ElementTree as default_etree -__all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair", - "surrogatePairToCodepoint", "moduleFactoryFactory", - "supports_lone_surrogates"] +__all__ = [ + 'default_etree', 'MethodDispatcher', 'isSurrogatePair', + 'surrogatePairToCodepoint', 'moduleFactoryFactory', + 'supports_lone_surrogates', +] # Platforms not supporting lone surrogates (\uD800-\uDFFF) should be @@ -75,6 +77,7 @@ class MethodDispatcher(dict): class BoundMethodDispatcher(Mapping): """Wraps a MethodDispatcher, binding its return values to `instance`""" + def __init__(self, instance, dispatcher): self.instance = instance self.dispatcher = dispatcher @@ -104,14 +107,18 @@ class BoundMethodDispatcher(Mapping): # python builds def isSurrogatePair(data): - return (len(data) == 2 and - ord(data[0]) >= 0xD800 and ord(data[0]) <= 0xDBFF and - ord(data[1]) >= 0xDC00 and ord(data[1]) <= 0xDFFF) + return ( + len(data) == 2 and + ord(data[0]) >= 0xD800 and ord(data[0]) <= 0xDBFF and + ord(data[1]) >= 0xDC00 and ord(data[1]) <= 0xDFFF + ) def surrogatePairToCodepoint(data): - char_val = (0x10000 + (ord(data[0]) - 0xD800) * 0x400 + - (ord(data[1]) - 0xDC00)) + char_val = ( + 0x10000 + (ord(data[0]) - 0xD800) * 0x400 + + (ord(data[1]) - 0xDC00) + ) return char_val # Module Factory Factory (no, this isn't Java, I know) @@ -122,10 +129,10 @@ def moduleFactoryFactory(factory): moduleCache = {} def moduleFactory(baseModule, *args, **kwargs): - if isinstance(ModuleType.__name__, type("")): - name = "_%s_factory" % baseModule.__name__ + if isinstance(ModuleType.__name__, str): + name = '_%s_factory' % baseModule.__name__ else: - name = b"_%s_factory" % baseModule.__name__ + name = b'_%s_factory' % baseModule.__name__ kwargs_tuple = tuple(kwargs.items()) @@ -135,11 +142,11 @@ def moduleFactoryFactory(factory): mod = ModuleType(name) objs = factory(baseModule, *args, **kwargs) mod.__dict__.update(objs) - if "name" not in moduleCache: + if 'name' not in moduleCache: moduleCache[name] = {} - if "args" not in moduleCache[name]: + if 'args' not in moduleCache[name]: moduleCache[name][args] = {} - if "kwargs" not in moduleCache[name][args]: + if 'kwargs' not in moduleCache[name][args]: moduleCache[name][args][kwargs_tuple] = {} moduleCache[name][args][kwargs_tuple] = mod return mod diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/constants.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/constants.py index fe3e237..6a335d9 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/constants.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/constants.py @@ -1,541 +1,543 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations import string EOF = None E = { - "null-character": - "Null character in input stream, replaced with U+FFFD.", - "invalid-codepoint": - "Invalid codepoint in stream.", - "incorrectly-placed-solidus": - "Solidus (/) incorrectly placed in tag.", - "incorrect-cr-newline-entity": - "Incorrect CR newline entity, replaced with LF.", - "illegal-windows-1252-entity": - "Entity used with illegal number (windows-1252 reference).", - "cant-convert-numeric-entity": + 'null-character': + 'Null character in input stream, replaced with U+FFFD.', + 'invalid-codepoint': + 'Invalid codepoint in stream.', + 'incorrectly-placed-solidus': + 'Solidus (/) incorrectly placed in tag.', + 'incorrect-cr-newline-entity': + 'Incorrect CR newline entity, replaced with LF.', + 'illegal-windows-1252-entity': + 'Entity used with illegal number (windows-1252 reference).', + 'cant-convert-numeric-entity': "Numeric entity couldn't be converted to character " - "(codepoint U+%(charAsInt)08x).", - "illegal-codepoint-for-numeric-entity": - "Numeric entity represents an illegal codepoint: " - "U+%(charAsInt)08x.", - "numeric-entity-without-semicolon": + '(codepoint U+%(charAsInt)08x).', + 'illegal-codepoint-for-numeric-entity': + 'Numeric entity represents an illegal codepoint: ' + 'U+%(charAsInt)08x.', + 'numeric-entity-without-semicolon': "Numeric entity didn't end with ';'.", - "expected-numeric-entity-but-got-eof": - "Numeric entity expected. Got end of file instead.", - "expected-numeric-entity": - "Numeric entity expected but none found.", - "named-entity-without-semicolon": + 'expected-numeric-entity-but-got-eof': + 'Numeric entity expected. Got end of file instead.', + 'expected-numeric-entity': + 'Numeric entity expected but none found.', + 'named-entity-without-semicolon': "Named entity didn't end with ';'.", - "expected-named-entity": - "Named entity expected. Got none.", - "attributes-in-end-tag": - "End tag contains unexpected attributes.", + 'expected-named-entity': + 'Named entity expected. Got none.', + 'attributes-in-end-tag': + 'End tag contains unexpected attributes.', 'self-closing-flag-on-end-tag': - "End tag contains unexpected self-closing flag.", - "expected-tag-name-but-got-right-bracket": + 'End tag contains unexpected self-closing flag.', + 'expected-tag-name-but-got-right-bracket': "Expected tag name. Got '>' instead.", - "expected-tag-name-but-got-question-mark": + 'expected-tag-name-but-got-question-mark': "Expected tag name. Got '?' instead. (HTML doesn't " - "support processing instructions.)", - "expected-tag-name": - "Expected tag name. Got something else instead", - "expected-closing-tag-but-got-right-bracket": + 'support processing instructions.)', + 'expected-tag-name': + 'Expected tag name. Got something else instead', + 'expected-closing-tag-but-got-right-bracket': "Expected closing tag. Got '>' instead. Ignoring ''.", - "expected-closing-tag-but-got-eof": - "Expected closing tag. Unexpected end of file.", - "expected-closing-tag-but-got-char": + 'expected-closing-tag-but-got-eof': + 'Expected closing tag. Unexpected end of file.', + 'expected-closing-tag-but-got-char': "Expected closing tag. Unexpected character '%(data)s' found.", - "eof-in-tag-name": - "Unexpected end of file in the tag name.", - "expected-attribute-name-but-got-eof": - "Unexpected end of file. Expected attribute name instead.", - "eof-in-attribute-name": - "Unexpected end of file in attribute name.", - "invalid-character-in-attribute-name": - "Invalid character in attribute name", - "duplicate-attribute": - "Dropped duplicate attribute on tag.", - "expected-end-of-tag-name-but-got-eof": - "Unexpected end of file. Expected = or end of tag.", - "expected-attribute-value-but-got-eof": - "Unexpected end of file. Expected attribute value.", - "expected-attribute-value-but-got-right-bracket": + 'eof-in-tag-name': + 'Unexpected end of file in the tag name.', + 'expected-attribute-name-but-got-eof': + 'Unexpected end of file. Expected attribute name instead.', + 'eof-in-attribute-name': + 'Unexpected end of file in attribute name.', + 'invalid-character-in-attribute-name': + 'Invalid character in attribute name', + 'duplicate-attribute': + 'Dropped duplicate attribute on tag.', + 'expected-end-of-tag-name-but-got-eof': + 'Unexpected end of file. Expected = or end of tag.', + 'expected-attribute-value-but-got-eof': + 'Unexpected end of file. Expected attribute value.', + 'expected-attribute-value-but-got-right-bracket': "Expected attribute value. Got '>' instead.", 'equals-in-unquoted-attribute-value': - "Unexpected = in unquoted attribute", + 'Unexpected = in unquoted attribute', 'unexpected-character-in-unquoted-attribute-value': - "Unexpected character in unquoted attribute", - "invalid-character-after-attribute-name": - "Unexpected character after attribute name.", - "unexpected-character-after-attribute-value": - "Unexpected character after attribute value.", - "eof-in-attribute-value-double-quote": + 'Unexpected character in unquoted attribute', + 'invalid-character-after-attribute-name': + 'Unexpected character after attribute name.', + 'unexpected-character-after-attribute-value': + 'Unexpected character after attribute value.', + 'eof-in-attribute-value-double-quote': "Unexpected end of file in attribute value (\").", - "eof-in-attribute-value-single-quote": + 'eof-in-attribute-value-single-quote': "Unexpected end of file in attribute value (').", - "eof-in-attribute-value-no-quotes": - "Unexpected end of file in attribute value.", - "unexpected-EOF-after-solidus-in-tag": - "Unexpected end of file in tag. Expected >", - "unexpected-character-after-solidus-in-tag": - "Unexpected character after / in tag. Expected >", - "expected-dashes-or-doctype": + 'eof-in-attribute-value-no-quotes': + 'Unexpected end of file in attribute value.', + 'unexpected-EOF-after-solidus-in-tag': + 'Unexpected end of file in tag. Expected >', + 'unexpected-character-after-solidus-in-tag': + 'Unexpected character after / in tag. Expected >', + 'expected-dashes-or-doctype': "Expected '--' or 'DOCTYPE'. Not found.", - "unexpected-bang-after-double-dash-in-comment": - "Unexpected ! after -- in comment", - "unexpected-space-after-double-dash-in-comment": - "Unexpected space after -- in comment", - "incorrect-comment": - "Incorrect comment.", - "eof-in-comment": - "Unexpected end of file in comment.", - "eof-in-comment-end-dash": - "Unexpected end of file in comment (-)", - "unexpected-dash-after-double-dash-in-comment": + 'unexpected-bang-after-double-dash-in-comment': + 'Unexpected ! after -- in comment', + 'unexpected-space-after-double-dash-in-comment': + 'Unexpected space after -- in comment', + 'incorrect-comment': + 'Incorrect comment.', + 'eof-in-comment': + 'Unexpected end of file in comment.', + 'eof-in-comment-end-dash': + 'Unexpected end of file in comment (-)', + 'unexpected-dash-after-double-dash-in-comment': "Unexpected '-' after '--' found in comment.", - "eof-in-comment-double-dash": - "Unexpected end of file in comment (--).", - "eof-in-comment-end-space-state": - "Unexpected end of file in comment.", - "eof-in-comment-end-bang-state": - "Unexpected end of file in comment.", - "unexpected-char-in-comment": - "Unexpected character in comment found.", - "need-space-after-doctype": + 'eof-in-comment-double-dash': + 'Unexpected end of file in comment (--).', + 'eof-in-comment-end-space-state': + 'Unexpected end of file in comment.', + 'eof-in-comment-end-bang-state': + 'Unexpected end of file in comment.', + 'unexpected-char-in-comment': + 'Unexpected character in comment found.', + 'need-space-after-doctype': "No space after literal string 'DOCTYPE'.", - "expected-doctype-name-but-got-right-bracket": - "Unexpected > character. Expected DOCTYPE name.", - "expected-doctype-name-but-got-eof": - "Unexpected end of file. Expected DOCTYPE name.", - "eof-in-doctype-name": - "Unexpected end of file in DOCTYPE name.", - "eof-in-doctype": - "Unexpected end of file in DOCTYPE.", - "expected-space-or-right-bracket-in-doctype": + 'expected-doctype-name-but-got-right-bracket': + 'Unexpected > character. Expected DOCTYPE name.', + 'expected-doctype-name-but-got-eof': + 'Unexpected end of file. Expected DOCTYPE name.', + 'eof-in-doctype-name': + 'Unexpected end of file in DOCTYPE name.', + 'eof-in-doctype': + 'Unexpected end of file in DOCTYPE.', + 'expected-space-or-right-bracket-in-doctype': "Expected space or '>'. Got '%(data)s'", - "unexpected-end-of-doctype": - "Unexpected end of DOCTYPE.", - "unexpected-char-in-doctype": - "Unexpected character in DOCTYPE.", - "eof-in-innerhtml": - "XXX innerHTML EOF", - "unexpected-doctype": - "Unexpected DOCTYPE. Ignored.", - "non-html-root": - "html needs to be the first start tag.", - "expected-doctype-but-got-eof": - "Unexpected End of file. Expected DOCTYPE.", - "unknown-doctype": - "Erroneous DOCTYPE.", - "expected-doctype-but-got-chars": - "Unexpected non-space characters. Expected DOCTYPE.", - "expected-doctype-but-got-start-tag": - "Unexpected start tag (%(name)s). Expected DOCTYPE.", - "expected-doctype-but-got-end-tag": - "Unexpected end tag (%(name)s). Expected DOCTYPE.", - "end-tag-after-implied-root": - "Unexpected end tag (%(name)s) after the (implied) root element.", - "expected-named-closing-tag-but-got-eof": - "Unexpected end of file. Expected end tag (%(name)s).", - "two-heads-are-not-better-than-one": - "Unexpected start tag head in existing head. Ignored.", - "unexpected-end-tag": - "Unexpected end tag (%(name)s). Ignored.", - "unexpected-start-tag-out-of-my-head": - "Unexpected start tag (%(name)s) that can be in head. Moved.", - "unexpected-start-tag": - "Unexpected start tag (%(name)s).", - "missing-end-tag": - "Missing end tag (%(name)s).", - "missing-end-tags": - "Missing end tags (%(name)s).", - "unexpected-start-tag-implies-end-tag": - "Unexpected start tag (%(startName)s) " - "implies end tag (%(endName)s).", - "unexpected-start-tag-treated-as": - "Unexpected start tag (%(originalName)s). Treated as %(newName)s.", - "deprecated-tag": + 'unexpected-end-of-doctype': + 'Unexpected end of DOCTYPE.', + 'unexpected-char-in-doctype': + 'Unexpected character in DOCTYPE.', + 'eof-in-innerhtml': + 'XXX innerHTML EOF', + 'unexpected-doctype': + 'Unexpected DOCTYPE. Ignored.', + 'non-html-root': + 'html needs to be the first start tag.', + 'expected-doctype-but-got-eof': + 'Unexpected End of file. Expected DOCTYPE.', + 'unknown-doctype': + 'Erroneous DOCTYPE.', + 'expected-doctype-but-got-chars': + 'Unexpected non-space characters. Expected DOCTYPE.', + 'expected-doctype-but-got-start-tag': + 'Unexpected start tag (%(name)s). Expected DOCTYPE.', + 'expected-doctype-but-got-end-tag': + 'Unexpected end tag (%(name)s). Expected DOCTYPE.', + 'end-tag-after-implied-root': + 'Unexpected end tag (%(name)s) after the (implied) root element.', + 'expected-named-closing-tag-but-got-eof': + 'Unexpected end of file. Expected end tag (%(name)s).', + 'two-heads-are-not-better-than-one': + 'Unexpected start tag head in existing head. Ignored.', + 'unexpected-end-tag': + 'Unexpected end tag (%(name)s). Ignored.', + 'unexpected-start-tag-out-of-my-head': + 'Unexpected start tag (%(name)s) that can be in head. Moved.', + 'unexpected-start-tag': + 'Unexpected start tag (%(name)s).', + 'missing-end-tag': + 'Missing end tag (%(name)s).', + 'missing-end-tags': + 'Missing end tags (%(name)s).', + 'unexpected-start-tag-implies-end-tag': + 'Unexpected start tag (%(startName)s) ' + 'implies end tag (%(endName)s).', + 'unexpected-start-tag-treated-as': + 'Unexpected start tag (%(originalName)s). Treated as %(newName)s.', + 'deprecated-tag': "Unexpected start tag %(name)s. Don't use it!", - "unexpected-start-tag-ignored": - "Unexpected start tag %(name)s. Ignored.", - "expected-one-end-tag-but-got-another": - "Unexpected end tag (%(gotName)s). " - "Missing end tag (%(expectedName)s).", - "end-tag-too-early": - "End tag (%(name)s) seen too early. Expected other end tag.", - "end-tag-too-early-named": - "Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s).", - "end-tag-too-early-ignored": - "End tag (%(name)s) seen too early. Ignored.", - "adoption-agency-1.1": - "End tag (%(name)s) violates step 1, " - "paragraph 1 of the adoption agency algorithm.", - "adoption-agency-1.2": - "End tag (%(name)s) violates step 1, " - "paragraph 2 of the adoption agency algorithm.", - "adoption-agency-1.3": - "End tag (%(name)s) violates step 1, " - "paragraph 3 of the adoption agency algorithm.", - "adoption-agency-4.4": - "End tag (%(name)s) violates step 4, " - "paragraph 4 of the adoption agency algorithm.", - "unexpected-end-tag-treated-as": - "Unexpected end tag (%(originalName)s). Treated as %(newName)s.", - "no-end-tag": - "This element (%(name)s) has no end tag.", - "unexpected-implied-end-tag-in-table": - "Unexpected implied end tag (%(name)s) in the table phase.", - "unexpected-implied-end-tag-in-table-body": - "Unexpected implied end tag (%(name)s) in the table body phase.", - "unexpected-char-implies-table-voodoo": - "Unexpected non-space characters in " - "table context caused voodoo mode.", - "unexpected-hidden-input-in-table": - "Unexpected input with type hidden in table context.", - "unexpected-form-in-table": - "Unexpected form in table context.", - "unexpected-start-tag-implies-table-voodoo": - "Unexpected start tag (%(name)s) in " - "table context caused voodoo mode.", - "unexpected-end-tag-implies-table-voodoo": - "Unexpected end tag (%(name)s) in " - "table context caused voodoo mode.", - "unexpected-cell-in-table-body": - "Unexpected table cell start tag (%(name)s) " - "in the table body phase.", - "unexpected-cell-end-tag": - "Got table cell end tag (%(name)s) " - "while required end tags are missing.", - "unexpected-end-tag-in-table-body": - "Unexpected end tag (%(name)s) in the table body phase. Ignored.", - "unexpected-implied-end-tag-in-table-row": - "Unexpected implied end tag (%(name)s) in the table row phase.", - "unexpected-end-tag-in-table-row": - "Unexpected end tag (%(name)s) in the table row phase. Ignored.", - "unexpected-select-in-select": - "Unexpected select start tag in the select phase " - "treated as select end tag.", - "unexpected-input-in-select": - "Unexpected input start tag in the select phase.", - "unexpected-start-tag-in-select": - "Unexpected start tag token (%(name)s in the select phase. " - "Ignored.", - "unexpected-end-tag-in-select": - "Unexpected end tag (%(name)s) in the select phase. Ignored.", - "unexpected-table-element-start-tag-in-select-in-table": - "Unexpected table element start tag (%(name)s) in the select in table phase.", - "unexpected-table-element-end-tag-in-select-in-table": - "Unexpected table element end tag (%(name)s) in the select in table phase.", - "unexpected-char-after-body": - "Unexpected non-space characters in the after body phase.", - "unexpected-start-tag-after-body": - "Unexpected start tag token (%(name)s)" - " in the after body phase.", - "unexpected-end-tag-after-body": - "Unexpected end tag token (%(name)s)" - " in the after body phase.", - "unexpected-char-in-frameset": - "Unexpected characters in the frameset phase. Characters ignored.", - "unexpected-start-tag-in-frameset": - "Unexpected start tag token (%(name)s)" - " in the frameset phase. Ignored.", - "unexpected-frameset-in-frameset-innerhtml": - "Unexpected end tag token (frameset) " - "in the frameset phase (innerHTML).", - "unexpected-end-tag-in-frameset": - "Unexpected end tag token (%(name)s)" - " in the frameset phase. Ignored.", - "unexpected-char-after-frameset": - "Unexpected non-space characters in the " - "after frameset phase. Ignored.", - "unexpected-start-tag-after-frameset": - "Unexpected start tag (%(name)s)" - " in the after frameset phase. Ignored.", - "unexpected-end-tag-after-frameset": - "Unexpected end tag (%(name)s)" - " in the after frameset phase. Ignored.", - "unexpected-end-tag-after-body-innerhtml": - "Unexpected end tag after body(innerHtml)", - "expected-eof-but-got-char": - "Unexpected non-space characters. Expected end of file.", - "expected-eof-but-got-start-tag": - "Unexpected start tag (%(name)s)" - ". Expected end of file.", - "expected-eof-but-got-end-tag": - "Unexpected end tag (%(name)s)" - ". Expected end of file.", - "eof-in-table": - "Unexpected end of file. Expected table content.", - "eof-in-select": - "Unexpected end of file. Expected select content.", - "eof-in-frameset": - "Unexpected end of file. Expected frameset content.", - "eof-in-script-in-script": - "Unexpected end of file. Expected script content.", - "eof-in-foreign-lands": - "Unexpected end of file. Expected foreign content", - "non-void-element-with-trailing-solidus": - "Trailing solidus not allowed on element %(name)s", - "unexpected-html-element-in-foreign-content": - "Element %(name)s not allowed in a non-html context", - "unexpected-end-tag-before-html": - "Unexpected end tag (%(name)s) before html.", - "unexpected-inhead-noscript-tag": - "Element %(name)s not allowed in a inhead-noscript context", - "eof-in-head-noscript": - "Unexpected end of file. Expected inhead-noscript content", - "char-in-head-noscript": - "Unexpected non-space character. Expected inhead-noscript content", - "XXX-undefined-error": - "Undefined error (this sucks and should be fixed)", + 'unexpected-start-tag-ignored': + 'Unexpected start tag %(name)s. Ignored.', + 'expected-one-end-tag-but-got-another': + 'Unexpected end tag (%(gotName)s). ' + 'Missing end tag (%(expectedName)s).', + 'end-tag-too-early': + 'End tag (%(name)s) seen too early. Expected other end tag.', + 'end-tag-too-early-named': + 'Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s).', + 'end-tag-too-early-ignored': + 'End tag (%(name)s) seen too early. Ignored.', + 'adoption-agency-1.1': + 'End tag (%(name)s) violates step 1, ' + 'paragraph 1 of the adoption agency algorithm.', + 'adoption-agency-1.2': + 'End tag (%(name)s) violates step 1, ' + 'paragraph 2 of the adoption agency algorithm.', + 'adoption-agency-1.3': + 'End tag (%(name)s) violates step 1, ' + 'paragraph 3 of the adoption agency algorithm.', + 'adoption-agency-4.4': + 'End tag (%(name)s) violates step 4, ' + 'paragraph 4 of the adoption agency algorithm.', + 'unexpected-end-tag-treated-as': + 'Unexpected end tag (%(originalName)s). Treated as %(newName)s.', + 'no-end-tag': + 'This element (%(name)s) has no end tag.', + 'unexpected-implied-end-tag-in-table': + 'Unexpected implied end tag (%(name)s) in the table phase.', + 'unexpected-implied-end-tag-in-table-body': + 'Unexpected implied end tag (%(name)s) in the table body phase.', + 'unexpected-char-implies-table-voodoo': + 'Unexpected non-space characters in ' + 'table context caused voodoo mode.', + 'unexpected-hidden-input-in-table': + 'Unexpected input with type hidden in table context.', + 'unexpected-form-in-table': + 'Unexpected form in table context.', + 'unexpected-start-tag-implies-table-voodoo': + 'Unexpected start tag (%(name)s) in ' + 'table context caused voodoo mode.', + 'unexpected-end-tag-implies-table-voodoo': + 'Unexpected end tag (%(name)s) in ' + 'table context caused voodoo mode.', + 'unexpected-cell-in-table-body': + 'Unexpected table cell start tag (%(name)s) ' + 'in the table body phase.', + 'unexpected-cell-end-tag': + 'Got table cell end tag (%(name)s) ' + 'while required end tags are missing.', + 'unexpected-end-tag-in-table-body': + 'Unexpected end tag (%(name)s) in the table body phase. Ignored.', + 'unexpected-implied-end-tag-in-table-row': + 'Unexpected implied end tag (%(name)s) in the table row phase.', + 'unexpected-end-tag-in-table-row': + 'Unexpected end tag (%(name)s) in the table row phase. Ignored.', + 'unexpected-select-in-select': + 'Unexpected select start tag in the select phase ' + 'treated as select end tag.', + 'unexpected-input-in-select': + 'Unexpected input start tag in the select phase.', + 'unexpected-start-tag-in-select': + 'Unexpected start tag token (%(name)s in the select phase. ' + 'Ignored.', + 'unexpected-end-tag-in-select': + 'Unexpected end tag (%(name)s) in the select phase. Ignored.', + 'unexpected-table-element-start-tag-in-select-in-table': + 'Unexpected table element start tag (%(name)s) in the select in table phase.', + 'unexpected-table-element-end-tag-in-select-in-table': + 'Unexpected table element end tag (%(name)s) in the select in table phase.', + 'unexpected-char-after-body': + 'Unexpected non-space characters in the after body phase.', + 'unexpected-start-tag-after-body': + 'Unexpected start tag token (%(name)s)' + ' in the after body phase.', + 'unexpected-end-tag-after-body': + 'Unexpected end tag token (%(name)s)' + ' in the after body phase.', + 'unexpected-char-in-frameset': + 'Unexpected characters in the frameset phase. Characters ignored.', + 'unexpected-start-tag-in-frameset': + 'Unexpected start tag token (%(name)s)' + ' in the frameset phase. Ignored.', + 'unexpected-frameset-in-frameset-innerhtml': + 'Unexpected end tag token (frameset) ' + 'in the frameset phase (innerHTML).', + 'unexpected-end-tag-in-frameset': + 'Unexpected end tag token (%(name)s)' + ' in the frameset phase. Ignored.', + 'unexpected-char-after-frameset': + 'Unexpected non-space characters in the ' + 'after frameset phase. Ignored.', + 'unexpected-start-tag-after-frameset': + 'Unexpected start tag (%(name)s)' + ' in the after frameset phase. Ignored.', + 'unexpected-end-tag-after-frameset': + 'Unexpected end tag (%(name)s)' + ' in the after frameset phase. Ignored.', + 'unexpected-end-tag-after-body-innerhtml': + 'Unexpected end tag after body(innerHtml)', + 'expected-eof-but-got-char': + 'Unexpected non-space characters. Expected end of file.', + 'expected-eof-but-got-start-tag': + 'Unexpected start tag (%(name)s)' + '. Expected end of file.', + 'expected-eof-but-got-end-tag': + 'Unexpected end tag (%(name)s)' + '. Expected end of file.', + 'eof-in-table': + 'Unexpected end of file. Expected table content.', + 'eof-in-select': + 'Unexpected end of file. Expected select content.', + 'eof-in-frameset': + 'Unexpected end of file. Expected frameset content.', + 'eof-in-script-in-script': + 'Unexpected end of file. Expected script content.', + 'eof-in-foreign-lands': + 'Unexpected end of file. Expected foreign content', + 'non-void-element-with-trailing-solidus': + 'Trailing solidus not allowed on element %(name)s', + 'unexpected-html-element-in-foreign-content': + 'Element %(name)s not allowed in a non-html context', + 'unexpected-end-tag-before-html': + 'Unexpected end tag (%(name)s) before html.', + 'unexpected-inhead-noscript-tag': + 'Element %(name)s not allowed in a inhead-noscript context', + 'eof-in-head-noscript': + 'Unexpected end of file. Expected inhead-noscript content', + 'char-in-head-noscript': + 'Unexpected non-space character. Expected inhead-noscript content', + 'XXX-undefined-error': + 'Undefined error (this sucks and should be fixed)', } namespaces = { - "html": "http://www.w3.org/1999/xhtml", - "mathml": "http://www.w3.org/1998/Math/MathML", - "svg": "http://www.w3.org/2000/svg", - "xlink": "http://www.w3.org/1999/xlink", - "xml": "http://www.w3.org/XML/1998/namespace", - "xmlns": "http://www.w3.org/2000/xmlns/" + 'html': 'http://www.w3.org/1999/xhtml', + 'mathml': 'http://www.w3.org/1998/Math/MathML', + 'svg': 'http://www.w3.org/2000/svg', + 'xlink': 'http://www.w3.org/1999/xlink', + 'xml': 'http://www.w3.org/XML/1998/namespace', + 'xmlns': 'http://www.w3.org/2000/xmlns/', } scopingElements = frozenset([ - (namespaces["html"], "applet"), - (namespaces["html"], "caption"), - (namespaces["html"], "html"), - (namespaces["html"], "marquee"), - (namespaces["html"], "object"), - (namespaces["html"], "table"), - (namespaces["html"], "td"), - (namespaces["html"], "th"), - (namespaces["mathml"], "mi"), - (namespaces["mathml"], "mo"), - (namespaces["mathml"], "mn"), - (namespaces["mathml"], "ms"), - (namespaces["mathml"], "mtext"), - (namespaces["mathml"], "annotation-xml"), - (namespaces["svg"], "foreignObject"), - (namespaces["svg"], "desc"), - (namespaces["svg"], "title"), + (namespaces['html'], 'applet'), + (namespaces['html'], 'caption'), + (namespaces['html'], 'html'), + (namespaces['html'], 'marquee'), + (namespaces['html'], 'object'), + (namespaces['html'], 'table'), + (namespaces['html'], 'td'), + (namespaces['html'], 'th'), + (namespaces['mathml'], 'mi'), + (namespaces['mathml'], 'mo'), + (namespaces['mathml'], 'mn'), + (namespaces['mathml'], 'ms'), + (namespaces['mathml'], 'mtext'), + (namespaces['mathml'], 'annotation-xml'), + (namespaces['svg'], 'foreignObject'), + (namespaces['svg'], 'desc'), + (namespaces['svg'], 'title'), ]) formattingElements = frozenset([ - (namespaces["html"], "a"), - (namespaces["html"], "b"), - (namespaces["html"], "big"), - (namespaces["html"], "code"), - (namespaces["html"], "em"), - (namespaces["html"], "font"), - (namespaces["html"], "i"), - (namespaces["html"], "nobr"), - (namespaces["html"], "s"), - (namespaces["html"], "small"), - (namespaces["html"], "strike"), - (namespaces["html"], "strong"), - (namespaces["html"], "tt"), - (namespaces["html"], "u") + (namespaces['html'], 'a'), + (namespaces['html'], 'b'), + (namespaces['html'], 'big'), + (namespaces['html'], 'code'), + (namespaces['html'], 'em'), + (namespaces['html'], 'font'), + (namespaces['html'], 'i'), + (namespaces['html'], 'nobr'), + (namespaces['html'], 's'), + (namespaces['html'], 'small'), + (namespaces['html'], 'strike'), + (namespaces['html'], 'strong'), + (namespaces['html'], 'tt'), + (namespaces['html'], 'u'), ]) specialElements = frozenset([ - (namespaces["html"], "address"), - (namespaces["html"], "applet"), - (namespaces["html"], "area"), - (namespaces["html"], "article"), - (namespaces["html"], "aside"), - (namespaces["html"], "base"), - (namespaces["html"], "basefont"), - (namespaces["html"], "bgsound"), - (namespaces["html"], "blockquote"), - (namespaces["html"], "body"), - (namespaces["html"], "br"), - (namespaces["html"], "button"), - (namespaces["html"], "caption"), - (namespaces["html"], "center"), - (namespaces["html"], "col"), - (namespaces["html"], "colgroup"), - (namespaces["html"], "command"), - (namespaces["html"], "dd"), - (namespaces["html"], "details"), - (namespaces["html"], "dir"), - (namespaces["html"], "div"), - (namespaces["html"], "dl"), - (namespaces["html"], "dt"), - (namespaces["html"], "embed"), - (namespaces["html"], "fieldset"), - (namespaces["html"], "figure"), - (namespaces["html"], "footer"), - (namespaces["html"], "form"), - (namespaces["html"], "frame"), - (namespaces["html"], "frameset"), - (namespaces["html"], "h1"), - (namespaces["html"], "h2"), - (namespaces["html"], "h3"), - (namespaces["html"], "h4"), - (namespaces["html"], "h5"), - (namespaces["html"], "h6"), - (namespaces["html"], "head"), - (namespaces["html"], "header"), - (namespaces["html"], "hr"), - (namespaces["html"], "html"), - (namespaces["html"], "iframe"), + (namespaces['html'], 'address'), + (namespaces['html'], 'applet'), + (namespaces['html'], 'area'), + (namespaces['html'], 'article'), + (namespaces['html'], 'aside'), + (namespaces['html'], 'base'), + (namespaces['html'], 'basefont'), + (namespaces['html'], 'bgsound'), + (namespaces['html'], 'blockquote'), + (namespaces['html'], 'body'), + (namespaces['html'], 'br'), + (namespaces['html'], 'button'), + (namespaces['html'], 'caption'), + (namespaces['html'], 'center'), + (namespaces['html'], 'col'), + (namespaces['html'], 'colgroup'), + (namespaces['html'], 'command'), + (namespaces['html'], 'dd'), + (namespaces['html'], 'details'), + (namespaces['html'], 'dir'), + (namespaces['html'], 'div'), + (namespaces['html'], 'dl'), + (namespaces['html'], 'dt'), + (namespaces['html'], 'embed'), + (namespaces['html'], 'fieldset'), + (namespaces['html'], 'figure'), + (namespaces['html'], 'footer'), + (namespaces['html'], 'form'), + (namespaces['html'], 'frame'), + (namespaces['html'], 'frameset'), + (namespaces['html'], 'h1'), + (namespaces['html'], 'h2'), + (namespaces['html'], 'h3'), + (namespaces['html'], 'h4'), + (namespaces['html'], 'h5'), + (namespaces['html'], 'h6'), + (namespaces['html'], 'head'), + (namespaces['html'], 'header'), + (namespaces['html'], 'hr'), + (namespaces['html'], 'html'), + (namespaces['html'], 'iframe'), # Note that image is commented out in the spec as "this isn't an # element that can end up on the stack, so it doesn't matter," - (namespaces["html"], "image"), - (namespaces["html"], "img"), - (namespaces["html"], "input"), - (namespaces["html"], "isindex"), - (namespaces["html"], "li"), - (namespaces["html"], "link"), - (namespaces["html"], "listing"), - (namespaces["html"], "marquee"), - (namespaces["html"], "menu"), - (namespaces["html"], "meta"), - (namespaces["html"], "nav"), - (namespaces["html"], "noembed"), - (namespaces["html"], "noframes"), - (namespaces["html"], "noscript"), - (namespaces["html"], "object"), - (namespaces["html"], "ol"), - (namespaces["html"], "p"), - (namespaces["html"], "param"), - (namespaces["html"], "plaintext"), - (namespaces["html"], "pre"), - (namespaces["html"], "script"), - (namespaces["html"], "section"), - (namespaces["html"], "select"), - (namespaces["html"], "style"), - (namespaces["html"], "table"), - (namespaces["html"], "tbody"), - (namespaces["html"], "td"), - (namespaces["html"], "textarea"), - (namespaces["html"], "tfoot"), - (namespaces["html"], "th"), - (namespaces["html"], "thead"), - (namespaces["html"], "title"), - (namespaces["html"], "tr"), - (namespaces["html"], "ul"), - (namespaces["html"], "wbr"), - (namespaces["html"], "xmp"), - (namespaces["svg"], "foreignObject") + (namespaces['html'], 'image'), + (namespaces['html'], 'img'), + (namespaces['html'], 'input'), + (namespaces['html'], 'isindex'), + (namespaces['html'], 'li'), + (namespaces['html'], 'link'), + (namespaces['html'], 'listing'), + (namespaces['html'], 'marquee'), + (namespaces['html'], 'menu'), + (namespaces['html'], 'meta'), + (namespaces['html'], 'nav'), + (namespaces['html'], 'noembed'), + (namespaces['html'], 'noframes'), + (namespaces['html'], 'noscript'), + (namespaces['html'], 'object'), + (namespaces['html'], 'ol'), + (namespaces['html'], 'p'), + (namespaces['html'], 'param'), + (namespaces['html'], 'plaintext'), + (namespaces['html'], 'pre'), + (namespaces['html'], 'script'), + (namespaces['html'], 'section'), + (namespaces['html'], 'select'), + (namespaces['html'], 'style'), + (namespaces['html'], 'table'), + (namespaces['html'], 'tbody'), + (namespaces['html'], 'td'), + (namespaces['html'], 'textarea'), + (namespaces['html'], 'tfoot'), + (namespaces['html'], 'th'), + (namespaces['html'], 'thead'), + (namespaces['html'], 'title'), + (namespaces['html'], 'tr'), + (namespaces['html'], 'ul'), + (namespaces['html'], 'wbr'), + (namespaces['html'], 'xmp'), + (namespaces['svg'], 'foreignObject'), ]) htmlIntegrationPointElements = frozenset([ - (namespaces["mathml"], "annotation-xml"), - (namespaces["svg"], "foreignObject"), - (namespaces["svg"], "desc"), - (namespaces["svg"], "title") + (namespaces['mathml'], 'annotation-xml'), + (namespaces['svg'], 'foreignObject'), + (namespaces['svg'], 'desc'), + (namespaces['svg'], 'title'), ]) mathmlTextIntegrationPointElements = frozenset([ - (namespaces["mathml"], "mi"), - (namespaces["mathml"], "mo"), - (namespaces["mathml"], "mn"), - (namespaces["mathml"], "ms"), - (namespaces["mathml"], "mtext") + (namespaces['mathml'], 'mi'), + (namespaces['mathml'], 'mo'), + (namespaces['mathml'], 'mn'), + (namespaces['mathml'], 'ms'), + (namespaces['mathml'], 'mtext'), ]) adjustSVGAttributes = { - "attributename": "attributeName", - "attributetype": "attributeType", - "basefrequency": "baseFrequency", - "baseprofile": "baseProfile", - "calcmode": "calcMode", - "clippathunits": "clipPathUnits", - "contentscripttype": "contentScriptType", - "contentstyletype": "contentStyleType", - "diffuseconstant": "diffuseConstant", - "edgemode": "edgeMode", - "externalresourcesrequired": "externalResourcesRequired", - "filterres": "filterRes", - "filterunits": "filterUnits", - "glyphref": "glyphRef", - "gradienttransform": "gradientTransform", - "gradientunits": "gradientUnits", - "kernelmatrix": "kernelMatrix", - "kernelunitlength": "kernelUnitLength", - "keypoints": "keyPoints", - "keysplines": "keySplines", - "keytimes": "keyTimes", - "lengthadjust": "lengthAdjust", - "limitingconeangle": "limitingConeAngle", - "markerheight": "markerHeight", - "markerunits": "markerUnits", - "markerwidth": "markerWidth", - "maskcontentunits": "maskContentUnits", - "maskunits": "maskUnits", - "numoctaves": "numOctaves", - "pathlength": "pathLength", - "patterncontentunits": "patternContentUnits", - "patterntransform": "patternTransform", - "patternunits": "patternUnits", - "pointsatx": "pointsAtX", - "pointsaty": "pointsAtY", - "pointsatz": "pointsAtZ", - "preservealpha": "preserveAlpha", - "preserveaspectratio": "preserveAspectRatio", - "primitiveunits": "primitiveUnits", - "refx": "refX", - "refy": "refY", - "repeatcount": "repeatCount", - "repeatdur": "repeatDur", - "requiredextensions": "requiredExtensions", - "requiredfeatures": "requiredFeatures", - "specularconstant": "specularConstant", - "specularexponent": "specularExponent", - "spreadmethod": "spreadMethod", - "startoffset": "startOffset", - "stddeviation": "stdDeviation", - "stitchtiles": "stitchTiles", - "surfacescale": "surfaceScale", - "systemlanguage": "systemLanguage", - "tablevalues": "tableValues", - "targetx": "targetX", - "targety": "targetY", - "textlength": "textLength", - "viewbox": "viewBox", - "viewtarget": "viewTarget", - "xchannelselector": "xChannelSelector", - "ychannelselector": "yChannelSelector", - "zoomandpan": "zoomAndPan" + 'attributename': 'attributeName', + 'attributetype': 'attributeType', + 'basefrequency': 'baseFrequency', + 'baseprofile': 'baseProfile', + 'calcmode': 'calcMode', + 'clippathunits': 'clipPathUnits', + 'contentscripttype': 'contentScriptType', + 'contentstyletype': 'contentStyleType', + 'diffuseconstant': 'diffuseConstant', + 'edgemode': 'edgeMode', + 'externalresourcesrequired': 'externalResourcesRequired', + 'filterres': 'filterRes', + 'filterunits': 'filterUnits', + 'glyphref': 'glyphRef', + 'gradienttransform': 'gradientTransform', + 'gradientunits': 'gradientUnits', + 'kernelmatrix': 'kernelMatrix', + 'kernelunitlength': 'kernelUnitLength', + 'keypoints': 'keyPoints', + 'keysplines': 'keySplines', + 'keytimes': 'keyTimes', + 'lengthadjust': 'lengthAdjust', + 'limitingconeangle': 'limitingConeAngle', + 'markerheight': 'markerHeight', + 'markerunits': 'markerUnits', + 'markerwidth': 'markerWidth', + 'maskcontentunits': 'maskContentUnits', + 'maskunits': 'maskUnits', + 'numoctaves': 'numOctaves', + 'pathlength': 'pathLength', + 'patterncontentunits': 'patternContentUnits', + 'patterntransform': 'patternTransform', + 'patternunits': 'patternUnits', + 'pointsatx': 'pointsAtX', + 'pointsaty': 'pointsAtY', + 'pointsatz': 'pointsAtZ', + 'preservealpha': 'preserveAlpha', + 'preserveaspectratio': 'preserveAspectRatio', + 'primitiveunits': 'primitiveUnits', + 'refx': 'refX', + 'refy': 'refY', + 'repeatcount': 'repeatCount', + 'repeatdur': 'repeatDur', + 'requiredextensions': 'requiredExtensions', + 'requiredfeatures': 'requiredFeatures', + 'specularconstant': 'specularConstant', + 'specularexponent': 'specularExponent', + 'spreadmethod': 'spreadMethod', + 'startoffset': 'startOffset', + 'stddeviation': 'stdDeviation', + 'stitchtiles': 'stitchTiles', + 'surfacescale': 'surfaceScale', + 'systemlanguage': 'systemLanguage', + 'tablevalues': 'tableValues', + 'targetx': 'targetX', + 'targety': 'targetY', + 'textlength': 'textLength', + 'viewbox': 'viewBox', + 'viewtarget': 'viewTarget', + 'xchannelselector': 'xChannelSelector', + 'ychannelselector': 'yChannelSelector', + 'zoomandpan': 'zoomAndPan', } -adjustMathMLAttributes = {"definitionurl": "definitionURL"} +adjustMathMLAttributes = {'definitionurl': 'definitionURL'} adjustForeignAttributes = { - "xlink:actuate": ("xlink", "actuate", namespaces["xlink"]), - "xlink:arcrole": ("xlink", "arcrole", namespaces["xlink"]), - "xlink:href": ("xlink", "href", namespaces["xlink"]), - "xlink:role": ("xlink", "role", namespaces["xlink"]), - "xlink:show": ("xlink", "show", namespaces["xlink"]), - "xlink:title": ("xlink", "title", namespaces["xlink"]), - "xlink:type": ("xlink", "type", namespaces["xlink"]), - "xml:base": ("xml", "base", namespaces["xml"]), - "xml:lang": ("xml", "lang", namespaces["xml"]), - "xml:space": ("xml", "space", namespaces["xml"]), - "xmlns": (None, "xmlns", namespaces["xmlns"]), - "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"]) + 'xlink:actuate': ('xlink', 'actuate', namespaces['xlink']), + 'xlink:arcrole': ('xlink', 'arcrole', namespaces['xlink']), + 'xlink:href': ('xlink', 'href', namespaces['xlink']), + 'xlink:role': ('xlink', 'role', namespaces['xlink']), + 'xlink:show': ('xlink', 'show', namespaces['xlink']), + 'xlink:title': ('xlink', 'title', namespaces['xlink']), + 'xlink:type': ('xlink', 'type', namespaces['xlink']), + 'xml:base': ('xml', 'base', namespaces['xml']), + 'xml:lang': ('xml', 'lang', namespaces['xml']), + 'xml:space': ('xml', 'space', namespaces['xml']), + 'xmlns': (None, 'xmlns', namespaces['xmlns']), + 'xmlns:xlink': ('xmlns', 'xlink', namespaces['xmlns']), } -unadjustForeignAttributes = {(ns, local): qname for qname, (prefix, local, ns) in - adjustForeignAttributes.items()} +unadjustForeignAttributes = { + (ns, local): qname for qname, (prefix, local, ns) in + adjustForeignAttributes.items() +} spaceCharacters = frozenset([ - "\t", - "\n", - "\u000C", - " ", - "\r" + '\t', + '\n', + '\u000C', + ' ', + '\r', ]) tableInsertModeElements = frozenset([ - "table", - "tbody", - "tfoot", - "thead", - "tr" + 'table', + 'tbody', + 'tfoot', + 'thead', + 'tr', ]) asciiLowercase = frozenset(string.ascii_lowercase) @@ -548,30 +550,30 @@ asciiUpper2Lower = {ord(c): ord(c.lower()) for c in string.ascii_uppercase} # Heading elements need to be ordered headingElements = ( - "h1", - "h2", - "h3", - "h4", - "h5", - "h6" + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', ) voidElements = frozenset([ - "base", - "command", - "event-source", - "link", - "meta", - "hr", - "br", - "img", - "embed", - "param", - "area", - "col", - "input", - "source", - "track" + 'base', + 'command', + 'event-source', + 'link', + 'meta', + 'hr', + 'br', + 'img', + 'embed', + 'param', + 'area', + 'col', + 'input', + 'source', + 'track', ]) cdataElements = frozenset(['title', 'textarea']) @@ -583,29 +585,29 @@ rcdataElements = frozenset([ 'iframe', 'noembed', 'noframes', - 'noscript' + 'noscript', ]) booleanAttributes = { - "": frozenset(["irrelevant", "itemscope"]), - "style": frozenset(["scoped"]), - "img": frozenset(["ismap"]), - "audio": frozenset(["autoplay", "controls"]), - "video": frozenset(["autoplay", "controls"]), - "script": frozenset(["defer", "async"]), - "details": frozenset(["open"]), - "datagrid": frozenset(["multiple", "disabled"]), - "command": frozenset(["hidden", "disabled", "checked", "default"]), - "hr": frozenset(["noshade"]), - "menu": frozenset(["autosubmit"]), - "fieldset": frozenset(["disabled", "readonly"]), - "option": frozenset(["disabled", "readonly", "selected"]), - "optgroup": frozenset(["disabled", "readonly"]), - "button": frozenset(["disabled", "autofocus"]), - "input": frozenset(["disabled", "readonly", "required", "autofocus", "checked", "ismap"]), - "select": frozenset(["disabled", "readonly", "autofocus", "multiple"]), - "output": frozenset(["disabled", "readonly"]), - "iframe": frozenset(["seamless"]), + '': frozenset(['irrelevant', 'itemscope']), + 'style': frozenset(['scoped']), + 'img': frozenset(['ismap']), + 'audio': frozenset(['autoplay', 'controls']), + 'video': frozenset(['autoplay', 'controls']), + 'script': frozenset(['defer', 'async']), + 'details': frozenset(['open']), + 'datagrid': frozenset(['multiple', 'disabled']), + 'command': frozenset(['hidden', 'disabled', 'checked', 'default']), + 'hr': frozenset(['noshade']), + 'menu': frozenset(['autosubmit']), + 'fieldset': frozenset(['disabled', 'readonly']), + 'option': frozenset(['disabled', 'readonly', 'selected']), + 'optgroup': frozenset(['disabled', 'readonly']), + 'button': frozenset(['disabled', 'autofocus']), + 'input': frozenset(['disabled', 'readonly', 'required', 'autofocus', 'checked', 'ismap']), + 'select': frozenset(['disabled', 'readonly', 'autofocus', 'multiple']), + 'output': frozenset(['disabled', 'readonly']), + 'iframe': frozenset(['seamless']), } # entitiesWindows1252 has to be _ordered_ and needs to have an index. It @@ -642,2299 +644,2301 @@ entitiesWindows1252 = ( 339, # 0x9C 0x0153 LATIN SMALL LIGATURE OE 65533, # 0x9D UNDEFINED 382, # 0x9E 0x017E LATIN SMALL LETTER Z WITH CARON - 376 # 0x9F 0x0178 LATIN CAPITAL LETTER Y WITH DIAERESIS + 376, # 0x9F 0x0178 LATIN CAPITAL LETTER Y WITH DIAERESIS ) xmlEntities = frozenset(['lt;', 'gt;', 'amp;', 'apos;', 'quot;']) entities = { - "AElig": "\xc6", - "AElig;": "\xc6", - "AMP": "&", - "AMP;": "&", - "Aacute": "\xc1", - "Aacute;": "\xc1", - "Abreve;": "\u0102", - "Acirc": "\xc2", - "Acirc;": "\xc2", - "Acy;": "\u0410", - "Afr;": "\U0001d504", - "Agrave": "\xc0", - "Agrave;": "\xc0", - "Alpha;": "\u0391", - "Amacr;": "\u0100", - "And;": "\u2a53", - "Aogon;": "\u0104", - "Aopf;": "\U0001d538", - "ApplyFunction;": "\u2061", - "Aring": "\xc5", - "Aring;": "\xc5", - "Ascr;": "\U0001d49c", - "Assign;": "\u2254", - "Atilde": "\xc3", - "Atilde;": "\xc3", - "Auml": "\xc4", - "Auml;": "\xc4", - "Backslash;": "\u2216", - "Barv;": "\u2ae7", - "Barwed;": "\u2306", - "Bcy;": "\u0411", - "Because;": "\u2235", - "Bernoullis;": "\u212c", - "Beta;": "\u0392", - "Bfr;": "\U0001d505", - "Bopf;": "\U0001d539", - "Breve;": "\u02d8", - "Bscr;": "\u212c", - "Bumpeq;": "\u224e", - "CHcy;": "\u0427", - "COPY": "\xa9", - "COPY;": "\xa9", - "Cacute;": "\u0106", - "Cap;": "\u22d2", - "CapitalDifferentialD;": "\u2145", - "Cayleys;": "\u212d", - "Ccaron;": "\u010c", - "Ccedil": "\xc7", - "Ccedil;": "\xc7", - "Ccirc;": "\u0108", - "Cconint;": "\u2230", - "Cdot;": "\u010a", - "Cedilla;": "\xb8", - "CenterDot;": "\xb7", - "Cfr;": "\u212d", - "Chi;": "\u03a7", - "CircleDot;": "\u2299", - "CircleMinus;": "\u2296", - "CirclePlus;": "\u2295", - "CircleTimes;": "\u2297", - "ClockwiseContourIntegral;": "\u2232", - "CloseCurlyDoubleQuote;": "\u201d", - "CloseCurlyQuote;": "\u2019", - "Colon;": "\u2237", - "Colone;": "\u2a74", - "Congruent;": "\u2261", - "Conint;": "\u222f", - "ContourIntegral;": "\u222e", - "Copf;": "\u2102", - "Coproduct;": "\u2210", - "CounterClockwiseContourIntegral;": "\u2233", - "Cross;": "\u2a2f", - "Cscr;": "\U0001d49e", - "Cup;": "\u22d3", - "CupCap;": "\u224d", - "DD;": "\u2145", - "DDotrahd;": "\u2911", - "DJcy;": "\u0402", - "DScy;": "\u0405", - "DZcy;": "\u040f", - "Dagger;": "\u2021", - "Darr;": "\u21a1", - "Dashv;": "\u2ae4", - "Dcaron;": "\u010e", - "Dcy;": "\u0414", - "Del;": "\u2207", - "Delta;": "\u0394", - "Dfr;": "\U0001d507", - "DiacriticalAcute;": "\xb4", - "DiacriticalDot;": "\u02d9", - "DiacriticalDoubleAcute;": "\u02dd", - "DiacriticalGrave;": "`", - "DiacriticalTilde;": "\u02dc", - "Diamond;": "\u22c4", - "DifferentialD;": "\u2146", - "Dopf;": "\U0001d53b", - "Dot;": "\xa8", - "DotDot;": "\u20dc", - "DotEqual;": "\u2250", - "DoubleContourIntegral;": "\u222f", - "DoubleDot;": "\xa8", - "DoubleDownArrow;": "\u21d3", - "DoubleLeftArrow;": "\u21d0", - "DoubleLeftRightArrow;": "\u21d4", - "DoubleLeftTee;": "\u2ae4", - "DoubleLongLeftArrow;": "\u27f8", - "DoubleLongLeftRightArrow;": "\u27fa", - "DoubleLongRightArrow;": "\u27f9", - "DoubleRightArrow;": "\u21d2", - "DoubleRightTee;": "\u22a8", - "DoubleUpArrow;": "\u21d1", - "DoubleUpDownArrow;": "\u21d5", - "DoubleVerticalBar;": "\u2225", - "DownArrow;": "\u2193", - "DownArrowBar;": "\u2913", - "DownArrowUpArrow;": "\u21f5", - "DownBreve;": "\u0311", - "DownLeftRightVector;": "\u2950", - "DownLeftTeeVector;": "\u295e", - "DownLeftVector;": "\u21bd", - "DownLeftVectorBar;": "\u2956", - "DownRightTeeVector;": "\u295f", - "DownRightVector;": "\u21c1", - "DownRightVectorBar;": "\u2957", - "DownTee;": "\u22a4", - "DownTeeArrow;": "\u21a7", - "Downarrow;": "\u21d3", - "Dscr;": "\U0001d49f", - "Dstrok;": "\u0110", - "ENG;": "\u014a", - "ETH": "\xd0", - "ETH;": "\xd0", - "Eacute": "\xc9", - "Eacute;": "\xc9", - "Ecaron;": "\u011a", - "Ecirc": "\xca", - "Ecirc;": "\xca", - "Ecy;": "\u042d", - "Edot;": "\u0116", - "Efr;": "\U0001d508", - "Egrave": "\xc8", - "Egrave;": "\xc8", - "Element;": "\u2208", - "Emacr;": "\u0112", - "EmptySmallSquare;": "\u25fb", - "EmptyVerySmallSquare;": "\u25ab", - "Eogon;": "\u0118", - "Eopf;": "\U0001d53c", - "Epsilon;": "\u0395", - "Equal;": "\u2a75", - "EqualTilde;": "\u2242", - "Equilibrium;": "\u21cc", - "Escr;": "\u2130", - "Esim;": "\u2a73", - "Eta;": "\u0397", - "Euml": "\xcb", - "Euml;": "\xcb", - "Exists;": "\u2203", - "ExponentialE;": "\u2147", - "Fcy;": "\u0424", - "Ffr;": "\U0001d509", - "FilledSmallSquare;": "\u25fc", - "FilledVerySmallSquare;": "\u25aa", - "Fopf;": "\U0001d53d", - "ForAll;": "\u2200", - "Fouriertrf;": "\u2131", - "Fscr;": "\u2131", - "GJcy;": "\u0403", - "GT": ">", - "GT;": ">", - "Gamma;": "\u0393", - "Gammad;": "\u03dc", - "Gbreve;": "\u011e", - "Gcedil;": "\u0122", - "Gcirc;": "\u011c", - "Gcy;": "\u0413", - "Gdot;": "\u0120", - "Gfr;": "\U0001d50a", - "Gg;": "\u22d9", - "Gopf;": "\U0001d53e", - "GreaterEqual;": "\u2265", - "GreaterEqualLess;": "\u22db", - "GreaterFullEqual;": "\u2267", - "GreaterGreater;": "\u2aa2", - "GreaterLess;": "\u2277", - "GreaterSlantEqual;": "\u2a7e", - "GreaterTilde;": "\u2273", - "Gscr;": "\U0001d4a2", - "Gt;": "\u226b", - "HARDcy;": "\u042a", - "Hacek;": "\u02c7", - "Hat;": "^", - "Hcirc;": "\u0124", - "Hfr;": "\u210c", - "HilbertSpace;": "\u210b", - "Hopf;": "\u210d", - "HorizontalLine;": "\u2500", - "Hscr;": "\u210b", - "Hstrok;": "\u0126", - "HumpDownHump;": "\u224e", - "HumpEqual;": "\u224f", - "IEcy;": "\u0415", - "IJlig;": "\u0132", - "IOcy;": "\u0401", - "Iacute": "\xcd", - "Iacute;": "\xcd", - "Icirc": "\xce", - "Icirc;": "\xce", - "Icy;": "\u0418", - "Idot;": "\u0130", - "Ifr;": "\u2111", - "Igrave": "\xcc", - "Igrave;": "\xcc", - "Im;": "\u2111", - "Imacr;": "\u012a", - "ImaginaryI;": "\u2148", - "Implies;": "\u21d2", - "Int;": "\u222c", - "Integral;": "\u222b", - "Intersection;": "\u22c2", - "InvisibleComma;": "\u2063", - "InvisibleTimes;": "\u2062", - "Iogon;": "\u012e", - "Iopf;": "\U0001d540", - "Iota;": "\u0399", - "Iscr;": "\u2110", - "Itilde;": "\u0128", - "Iukcy;": "\u0406", - "Iuml": "\xcf", - "Iuml;": "\xcf", - "Jcirc;": "\u0134", - "Jcy;": "\u0419", - "Jfr;": "\U0001d50d", - "Jopf;": "\U0001d541", - "Jscr;": "\U0001d4a5", - "Jsercy;": "\u0408", - "Jukcy;": "\u0404", - "KHcy;": "\u0425", - "KJcy;": "\u040c", - "Kappa;": "\u039a", - "Kcedil;": "\u0136", - "Kcy;": "\u041a", - "Kfr;": "\U0001d50e", - "Kopf;": "\U0001d542", - "Kscr;": "\U0001d4a6", - "LJcy;": "\u0409", - "LT": "<", - "LT;": "<", - "Lacute;": "\u0139", - "Lambda;": "\u039b", - "Lang;": "\u27ea", - "Laplacetrf;": "\u2112", - "Larr;": "\u219e", - "Lcaron;": "\u013d", - "Lcedil;": "\u013b", - "Lcy;": "\u041b", - "LeftAngleBracket;": "\u27e8", - "LeftArrow;": "\u2190", - "LeftArrowBar;": "\u21e4", - "LeftArrowRightArrow;": "\u21c6", - "LeftCeiling;": "\u2308", - "LeftDoubleBracket;": "\u27e6", - "LeftDownTeeVector;": "\u2961", - "LeftDownVector;": "\u21c3", - "LeftDownVectorBar;": "\u2959", - "LeftFloor;": "\u230a", - "LeftRightArrow;": "\u2194", - "LeftRightVector;": "\u294e", - "LeftTee;": "\u22a3", - "LeftTeeArrow;": "\u21a4", - "LeftTeeVector;": "\u295a", - "LeftTriangle;": "\u22b2", - "LeftTriangleBar;": "\u29cf", - "LeftTriangleEqual;": "\u22b4", - "LeftUpDownVector;": "\u2951", - "LeftUpTeeVector;": "\u2960", - "LeftUpVector;": "\u21bf", - "LeftUpVectorBar;": "\u2958", - "LeftVector;": "\u21bc", - "LeftVectorBar;": "\u2952", - "Leftarrow;": "\u21d0", - "Leftrightarrow;": "\u21d4", - "LessEqualGreater;": "\u22da", - "LessFullEqual;": "\u2266", - "LessGreater;": "\u2276", - "LessLess;": "\u2aa1", - "LessSlantEqual;": "\u2a7d", - "LessTilde;": "\u2272", - "Lfr;": "\U0001d50f", - "Ll;": "\u22d8", - "Lleftarrow;": "\u21da", - "Lmidot;": "\u013f", - "LongLeftArrow;": "\u27f5", - "LongLeftRightArrow;": "\u27f7", - "LongRightArrow;": "\u27f6", - "Longleftarrow;": "\u27f8", - "Longleftrightarrow;": "\u27fa", - "Longrightarrow;": "\u27f9", - "Lopf;": "\U0001d543", - "LowerLeftArrow;": "\u2199", - "LowerRightArrow;": "\u2198", - "Lscr;": "\u2112", - "Lsh;": "\u21b0", - "Lstrok;": "\u0141", - "Lt;": "\u226a", - "Map;": "\u2905", - "Mcy;": "\u041c", - "MediumSpace;": "\u205f", - "Mellintrf;": "\u2133", - "Mfr;": "\U0001d510", - "MinusPlus;": "\u2213", - "Mopf;": "\U0001d544", - "Mscr;": "\u2133", - "Mu;": "\u039c", - "NJcy;": "\u040a", - "Nacute;": "\u0143", - "Ncaron;": "\u0147", - "Ncedil;": "\u0145", - "Ncy;": "\u041d", - "NegativeMediumSpace;": "\u200b", - "NegativeThickSpace;": "\u200b", - "NegativeThinSpace;": "\u200b", - "NegativeVeryThinSpace;": "\u200b", - "NestedGreaterGreater;": "\u226b", - "NestedLessLess;": "\u226a", - "NewLine;": "\n", - "Nfr;": "\U0001d511", - "NoBreak;": "\u2060", - "NonBreakingSpace;": "\xa0", - "Nopf;": "\u2115", - "Not;": "\u2aec", - "NotCongruent;": "\u2262", - "NotCupCap;": "\u226d", - "NotDoubleVerticalBar;": "\u2226", - "NotElement;": "\u2209", - "NotEqual;": "\u2260", - "NotEqualTilde;": "\u2242\u0338", - "NotExists;": "\u2204", - "NotGreater;": "\u226f", - "NotGreaterEqual;": "\u2271", - "NotGreaterFullEqual;": "\u2267\u0338", - "NotGreaterGreater;": "\u226b\u0338", - "NotGreaterLess;": "\u2279", - "NotGreaterSlantEqual;": "\u2a7e\u0338", - "NotGreaterTilde;": "\u2275", - "NotHumpDownHump;": "\u224e\u0338", - "NotHumpEqual;": "\u224f\u0338", - "NotLeftTriangle;": "\u22ea", - "NotLeftTriangleBar;": "\u29cf\u0338", - "NotLeftTriangleEqual;": "\u22ec", - "NotLess;": "\u226e", - "NotLessEqual;": "\u2270", - "NotLessGreater;": "\u2278", - "NotLessLess;": "\u226a\u0338", - "NotLessSlantEqual;": "\u2a7d\u0338", - "NotLessTilde;": "\u2274", - "NotNestedGreaterGreater;": "\u2aa2\u0338", - "NotNestedLessLess;": "\u2aa1\u0338", - "NotPrecedes;": "\u2280", - "NotPrecedesEqual;": "\u2aaf\u0338", - "NotPrecedesSlantEqual;": "\u22e0", - "NotReverseElement;": "\u220c", - "NotRightTriangle;": "\u22eb", - "NotRightTriangleBar;": "\u29d0\u0338", - "NotRightTriangleEqual;": "\u22ed", - "NotSquareSubset;": "\u228f\u0338", - "NotSquareSubsetEqual;": "\u22e2", - "NotSquareSuperset;": "\u2290\u0338", - "NotSquareSupersetEqual;": "\u22e3", - "NotSubset;": "\u2282\u20d2", - "NotSubsetEqual;": "\u2288", - "NotSucceeds;": "\u2281", - "NotSucceedsEqual;": "\u2ab0\u0338", - "NotSucceedsSlantEqual;": "\u22e1", - "NotSucceedsTilde;": "\u227f\u0338", - "NotSuperset;": "\u2283\u20d2", - "NotSupersetEqual;": "\u2289", - "NotTilde;": "\u2241", - "NotTildeEqual;": "\u2244", - "NotTildeFullEqual;": "\u2247", - "NotTildeTilde;": "\u2249", - "NotVerticalBar;": "\u2224", - "Nscr;": "\U0001d4a9", - "Ntilde": "\xd1", - "Ntilde;": "\xd1", - "Nu;": "\u039d", - "OElig;": "\u0152", - "Oacute": "\xd3", - "Oacute;": "\xd3", - "Ocirc": "\xd4", - "Ocirc;": "\xd4", - "Ocy;": "\u041e", - "Odblac;": "\u0150", - "Ofr;": "\U0001d512", - "Ograve": "\xd2", - "Ograve;": "\xd2", - "Omacr;": "\u014c", - "Omega;": "\u03a9", - "Omicron;": "\u039f", - "Oopf;": "\U0001d546", - "OpenCurlyDoubleQuote;": "\u201c", - "OpenCurlyQuote;": "\u2018", - "Or;": "\u2a54", - "Oscr;": "\U0001d4aa", - "Oslash": "\xd8", - "Oslash;": "\xd8", - "Otilde": "\xd5", - "Otilde;": "\xd5", - "Otimes;": "\u2a37", - "Ouml": "\xd6", - "Ouml;": "\xd6", - "OverBar;": "\u203e", - "OverBrace;": "\u23de", - "OverBracket;": "\u23b4", - "OverParenthesis;": "\u23dc", - "PartialD;": "\u2202", - "Pcy;": "\u041f", - "Pfr;": "\U0001d513", - "Phi;": "\u03a6", - "Pi;": "\u03a0", - "PlusMinus;": "\xb1", - "Poincareplane;": "\u210c", - "Popf;": "\u2119", - "Pr;": "\u2abb", - "Precedes;": "\u227a", - "PrecedesEqual;": "\u2aaf", - "PrecedesSlantEqual;": "\u227c", - "PrecedesTilde;": "\u227e", - "Prime;": "\u2033", - "Product;": "\u220f", - "Proportion;": "\u2237", - "Proportional;": "\u221d", - "Pscr;": "\U0001d4ab", - "Psi;": "\u03a8", - "QUOT": "\"", - "QUOT;": "\"", - "Qfr;": "\U0001d514", - "Qopf;": "\u211a", - "Qscr;": "\U0001d4ac", - "RBarr;": "\u2910", - "REG": "\xae", - "REG;": "\xae", - "Racute;": "\u0154", - "Rang;": "\u27eb", - "Rarr;": "\u21a0", - "Rarrtl;": "\u2916", - "Rcaron;": "\u0158", - "Rcedil;": "\u0156", - "Rcy;": "\u0420", - "Re;": "\u211c", - "ReverseElement;": "\u220b", - "ReverseEquilibrium;": "\u21cb", - "ReverseUpEquilibrium;": "\u296f", - "Rfr;": "\u211c", - "Rho;": "\u03a1", - "RightAngleBracket;": "\u27e9", - "RightArrow;": "\u2192", - "RightArrowBar;": "\u21e5", - "RightArrowLeftArrow;": "\u21c4", - "RightCeiling;": "\u2309", - "RightDoubleBracket;": "\u27e7", - "RightDownTeeVector;": "\u295d", - "RightDownVector;": "\u21c2", - "RightDownVectorBar;": "\u2955", - "RightFloor;": "\u230b", - "RightTee;": "\u22a2", - "RightTeeArrow;": "\u21a6", - "RightTeeVector;": "\u295b", - "RightTriangle;": "\u22b3", - "RightTriangleBar;": "\u29d0", - "RightTriangleEqual;": "\u22b5", - "RightUpDownVector;": "\u294f", - "RightUpTeeVector;": "\u295c", - "RightUpVector;": "\u21be", - "RightUpVectorBar;": "\u2954", - "RightVector;": "\u21c0", - "RightVectorBar;": "\u2953", - "Rightarrow;": "\u21d2", - "Ropf;": "\u211d", - "RoundImplies;": "\u2970", - "Rrightarrow;": "\u21db", - "Rscr;": "\u211b", - "Rsh;": "\u21b1", - "RuleDelayed;": "\u29f4", - "SHCHcy;": "\u0429", - "SHcy;": "\u0428", - "SOFTcy;": "\u042c", - "Sacute;": "\u015a", - "Sc;": "\u2abc", - "Scaron;": "\u0160", - "Scedil;": "\u015e", - "Scirc;": "\u015c", - "Scy;": "\u0421", - "Sfr;": "\U0001d516", - "ShortDownArrow;": "\u2193", - "ShortLeftArrow;": "\u2190", - "ShortRightArrow;": "\u2192", - "ShortUpArrow;": "\u2191", - "Sigma;": "\u03a3", - "SmallCircle;": "\u2218", - "Sopf;": "\U0001d54a", - "Sqrt;": "\u221a", - "Square;": "\u25a1", - "SquareIntersection;": "\u2293", - "SquareSubset;": "\u228f", - "SquareSubsetEqual;": "\u2291", - "SquareSuperset;": "\u2290", - "SquareSupersetEqual;": "\u2292", - "SquareUnion;": "\u2294", - "Sscr;": "\U0001d4ae", - "Star;": "\u22c6", - "Sub;": "\u22d0", - "Subset;": "\u22d0", - "SubsetEqual;": "\u2286", - "Succeeds;": "\u227b", - "SucceedsEqual;": "\u2ab0", - "SucceedsSlantEqual;": "\u227d", - "SucceedsTilde;": "\u227f", - "SuchThat;": "\u220b", - "Sum;": "\u2211", - "Sup;": "\u22d1", - "Superset;": "\u2283", - "SupersetEqual;": "\u2287", - "Supset;": "\u22d1", - "THORN": "\xde", - "THORN;": "\xde", - "TRADE;": "\u2122", - "TSHcy;": "\u040b", - "TScy;": "\u0426", - "Tab;": "\t", - "Tau;": "\u03a4", - "Tcaron;": "\u0164", - "Tcedil;": "\u0162", - "Tcy;": "\u0422", - "Tfr;": "\U0001d517", - "Therefore;": "\u2234", - "Theta;": "\u0398", - "ThickSpace;": "\u205f\u200a", - "ThinSpace;": "\u2009", - "Tilde;": "\u223c", - "TildeEqual;": "\u2243", - "TildeFullEqual;": "\u2245", - "TildeTilde;": "\u2248", - "Topf;": "\U0001d54b", - "TripleDot;": "\u20db", - "Tscr;": "\U0001d4af", - "Tstrok;": "\u0166", - "Uacute": "\xda", - "Uacute;": "\xda", - "Uarr;": "\u219f", - "Uarrocir;": "\u2949", - "Ubrcy;": "\u040e", - "Ubreve;": "\u016c", - "Ucirc": "\xdb", - "Ucirc;": "\xdb", - "Ucy;": "\u0423", - "Udblac;": "\u0170", - "Ufr;": "\U0001d518", - "Ugrave": "\xd9", - "Ugrave;": "\xd9", - "Umacr;": "\u016a", - "UnderBar;": "_", - "UnderBrace;": "\u23df", - "UnderBracket;": "\u23b5", - "UnderParenthesis;": "\u23dd", - "Union;": "\u22c3", - "UnionPlus;": "\u228e", - "Uogon;": "\u0172", - "Uopf;": "\U0001d54c", - "UpArrow;": "\u2191", - "UpArrowBar;": "\u2912", - "UpArrowDownArrow;": "\u21c5", - "UpDownArrow;": "\u2195", - "UpEquilibrium;": "\u296e", - "UpTee;": "\u22a5", - "UpTeeArrow;": "\u21a5", - "Uparrow;": "\u21d1", - "Updownarrow;": "\u21d5", - "UpperLeftArrow;": "\u2196", - "UpperRightArrow;": "\u2197", - "Upsi;": "\u03d2", - "Upsilon;": "\u03a5", - "Uring;": "\u016e", - "Uscr;": "\U0001d4b0", - "Utilde;": "\u0168", - "Uuml": "\xdc", - "Uuml;": "\xdc", - "VDash;": "\u22ab", - "Vbar;": "\u2aeb", - "Vcy;": "\u0412", - "Vdash;": "\u22a9", - "Vdashl;": "\u2ae6", - "Vee;": "\u22c1", - "Verbar;": "\u2016", - "Vert;": "\u2016", - "VerticalBar;": "\u2223", - "VerticalLine;": "|", - "VerticalSeparator;": "\u2758", - "VerticalTilde;": "\u2240", - "VeryThinSpace;": "\u200a", - "Vfr;": "\U0001d519", - "Vopf;": "\U0001d54d", - "Vscr;": "\U0001d4b1", - "Vvdash;": "\u22aa", - "Wcirc;": "\u0174", - "Wedge;": "\u22c0", - "Wfr;": "\U0001d51a", - "Wopf;": "\U0001d54e", - "Wscr;": "\U0001d4b2", - "Xfr;": "\U0001d51b", - "Xi;": "\u039e", - "Xopf;": "\U0001d54f", - "Xscr;": "\U0001d4b3", - "YAcy;": "\u042f", - "YIcy;": "\u0407", - "YUcy;": "\u042e", - "Yacute": "\xdd", - "Yacute;": "\xdd", - "Ycirc;": "\u0176", - "Ycy;": "\u042b", - "Yfr;": "\U0001d51c", - "Yopf;": "\U0001d550", - "Yscr;": "\U0001d4b4", - "Yuml;": "\u0178", - "ZHcy;": "\u0416", - "Zacute;": "\u0179", - "Zcaron;": "\u017d", - "Zcy;": "\u0417", - "Zdot;": "\u017b", - "ZeroWidthSpace;": "\u200b", - "Zeta;": "\u0396", - "Zfr;": "\u2128", - "Zopf;": "\u2124", - "Zscr;": "\U0001d4b5", - "aacute": "\xe1", - "aacute;": "\xe1", - "abreve;": "\u0103", - "ac;": "\u223e", - "acE;": "\u223e\u0333", - "acd;": "\u223f", - "acirc": "\xe2", - "acirc;": "\xe2", - "acute": "\xb4", - "acute;": "\xb4", - "acy;": "\u0430", - "aelig": "\xe6", - "aelig;": "\xe6", - "af;": "\u2061", - "afr;": "\U0001d51e", - "agrave": "\xe0", - "agrave;": "\xe0", - "alefsym;": "\u2135", - "aleph;": "\u2135", - "alpha;": "\u03b1", - "amacr;": "\u0101", - "amalg;": "\u2a3f", - "amp": "&", - "amp;": "&", - "and;": "\u2227", - "andand;": "\u2a55", - "andd;": "\u2a5c", - "andslope;": "\u2a58", - "andv;": "\u2a5a", - "ang;": "\u2220", - "ange;": "\u29a4", - "angle;": "\u2220", - "angmsd;": "\u2221", - "angmsdaa;": "\u29a8", - "angmsdab;": "\u29a9", - "angmsdac;": "\u29aa", - "angmsdad;": "\u29ab", - "angmsdae;": "\u29ac", - "angmsdaf;": "\u29ad", - "angmsdag;": "\u29ae", - "angmsdah;": "\u29af", - "angrt;": "\u221f", - "angrtvb;": "\u22be", - "angrtvbd;": "\u299d", - "angsph;": "\u2222", - "angst;": "\xc5", - "angzarr;": "\u237c", - "aogon;": "\u0105", - "aopf;": "\U0001d552", - "ap;": "\u2248", - "apE;": "\u2a70", - "apacir;": "\u2a6f", - "ape;": "\u224a", - "apid;": "\u224b", - "apos;": "'", - "approx;": "\u2248", - "approxeq;": "\u224a", - "aring": "\xe5", - "aring;": "\xe5", - "ascr;": "\U0001d4b6", - "ast;": "*", - "asymp;": "\u2248", - "asympeq;": "\u224d", - "atilde": "\xe3", - "atilde;": "\xe3", - "auml": "\xe4", - "auml;": "\xe4", - "awconint;": "\u2233", - "awint;": "\u2a11", - "bNot;": "\u2aed", - "backcong;": "\u224c", - "backepsilon;": "\u03f6", - "backprime;": "\u2035", - "backsim;": "\u223d", - "backsimeq;": "\u22cd", - "barvee;": "\u22bd", - "barwed;": "\u2305", - "barwedge;": "\u2305", - "bbrk;": "\u23b5", - "bbrktbrk;": "\u23b6", - "bcong;": "\u224c", - "bcy;": "\u0431", - "bdquo;": "\u201e", - "becaus;": "\u2235", - "because;": "\u2235", - "bemptyv;": "\u29b0", - "bepsi;": "\u03f6", - "bernou;": "\u212c", - "beta;": "\u03b2", - "beth;": "\u2136", - "between;": "\u226c", - "bfr;": "\U0001d51f", - "bigcap;": "\u22c2", - "bigcirc;": "\u25ef", - "bigcup;": "\u22c3", - "bigodot;": "\u2a00", - "bigoplus;": "\u2a01", - "bigotimes;": "\u2a02", - "bigsqcup;": "\u2a06", - "bigstar;": "\u2605", - "bigtriangledown;": "\u25bd", - "bigtriangleup;": "\u25b3", - "biguplus;": "\u2a04", - "bigvee;": "\u22c1", - "bigwedge;": "\u22c0", - "bkarow;": "\u290d", - "blacklozenge;": "\u29eb", - "blacksquare;": "\u25aa", - "blacktriangle;": "\u25b4", - "blacktriangledown;": "\u25be", - "blacktriangleleft;": "\u25c2", - "blacktriangleright;": "\u25b8", - "blank;": "\u2423", - "blk12;": "\u2592", - "blk14;": "\u2591", - "blk34;": "\u2593", - "block;": "\u2588", - "bne;": "=\u20e5", - "bnequiv;": "\u2261\u20e5", - "bnot;": "\u2310", - "bopf;": "\U0001d553", - "bot;": "\u22a5", - "bottom;": "\u22a5", - "bowtie;": "\u22c8", - "boxDL;": "\u2557", - "boxDR;": "\u2554", - "boxDl;": "\u2556", - "boxDr;": "\u2553", - "boxH;": "\u2550", - "boxHD;": "\u2566", - "boxHU;": "\u2569", - "boxHd;": "\u2564", - "boxHu;": "\u2567", - "boxUL;": "\u255d", - "boxUR;": "\u255a", - "boxUl;": "\u255c", - "boxUr;": "\u2559", - "boxV;": "\u2551", - "boxVH;": "\u256c", - "boxVL;": "\u2563", - "boxVR;": "\u2560", - "boxVh;": "\u256b", - "boxVl;": "\u2562", - "boxVr;": "\u255f", - "boxbox;": "\u29c9", - "boxdL;": "\u2555", - "boxdR;": "\u2552", - "boxdl;": "\u2510", - "boxdr;": "\u250c", - "boxh;": "\u2500", - "boxhD;": "\u2565", - "boxhU;": "\u2568", - "boxhd;": "\u252c", - "boxhu;": "\u2534", - "boxminus;": "\u229f", - "boxplus;": "\u229e", - "boxtimes;": "\u22a0", - "boxuL;": "\u255b", - "boxuR;": "\u2558", - "boxul;": "\u2518", - "boxur;": "\u2514", - "boxv;": "\u2502", - "boxvH;": "\u256a", - "boxvL;": "\u2561", - "boxvR;": "\u255e", - "boxvh;": "\u253c", - "boxvl;": "\u2524", - "boxvr;": "\u251c", - "bprime;": "\u2035", - "breve;": "\u02d8", - "brvbar": "\xa6", - "brvbar;": "\xa6", - "bscr;": "\U0001d4b7", - "bsemi;": "\u204f", - "bsim;": "\u223d", - "bsime;": "\u22cd", - "bsol;": "\\", - "bsolb;": "\u29c5", - "bsolhsub;": "\u27c8", - "bull;": "\u2022", - "bullet;": "\u2022", - "bump;": "\u224e", - "bumpE;": "\u2aae", - "bumpe;": "\u224f", - "bumpeq;": "\u224f", - "cacute;": "\u0107", - "cap;": "\u2229", - "capand;": "\u2a44", - "capbrcup;": "\u2a49", - "capcap;": "\u2a4b", - "capcup;": "\u2a47", - "capdot;": "\u2a40", - "caps;": "\u2229\ufe00", - "caret;": "\u2041", - "caron;": "\u02c7", - "ccaps;": "\u2a4d", - "ccaron;": "\u010d", - "ccedil": "\xe7", - "ccedil;": "\xe7", - "ccirc;": "\u0109", - "ccups;": "\u2a4c", - "ccupssm;": "\u2a50", - "cdot;": "\u010b", - "cedil": "\xb8", - "cedil;": "\xb8", - "cemptyv;": "\u29b2", - "cent": "\xa2", - "cent;": "\xa2", - "centerdot;": "\xb7", - "cfr;": "\U0001d520", - "chcy;": "\u0447", - "check;": "\u2713", - "checkmark;": "\u2713", - "chi;": "\u03c7", - "cir;": "\u25cb", - "cirE;": "\u29c3", - "circ;": "\u02c6", - "circeq;": "\u2257", - "circlearrowleft;": "\u21ba", - "circlearrowright;": "\u21bb", - "circledR;": "\xae", - "circledS;": "\u24c8", - "circledast;": "\u229b", - "circledcirc;": "\u229a", - "circleddash;": "\u229d", - "cire;": "\u2257", - "cirfnint;": "\u2a10", - "cirmid;": "\u2aef", - "cirscir;": "\u29c2", - "clubs;": "\u2663", - "clubsuit;": "\u2663", - "colon;": ":", - "colone;": "\u2254", - "coloneq;": "\u2254", - "comma;": ",", - "commat;": "@", - "comp;": "\u2201", - "compfn;": "\u2218", - "complement;": "\u2201", - "complexes;": "\u2102", - "cong;": "\u2245", - "congdot;": "\u2a6d", - "conint;": "\u222e", - "copf;": "\U0001d554", - "coprod;": "\u2210", - "copy": "\xa9", - "copy;": "\xa9", - "copysr;": "\u2117", - "crarr;": "\u21b5", - "cross;": "\u2717", - "cscr;": "\U0001d4b8", - "csub;": "\u2acf", - "csube;": "\u2ad1", - "csup;": "\u2ad0", - "csupe;": "\u2ad2", - "ctdot;": "\u22ef", - "cudarrl;": "\u2938", - "cudarrr;": "\u2935", - "cuepr;": "\u22de", - "cuesc;": "\u22df", - "cularr;": "\u21b6", - "cularrp;": "\u293d", - "cup;": "\u222a", - "cupbrcap;": "\u2a48", - "cupcap;": "\u2a46", - "cupcup;": "\u2a4a", - "cupdot;": "\u228d", - "cupor;": "\u2a45", - "cups;": "\u222a\ufe00", - "curarr;": "\u21b7", - "curarrm;": "\u293c", - "curlyeqprec;": "\u22de", - "curlyeqsucc;": "\u22df", - "curlyvee;": "\u22ce", - "curlywedge;": "\u22cf", - "curren": "\xa4", - "curren;": "\xa4", - "curvearrowleft;": "\u21b6", - "curvearrowright;": "\u21b7", - "cuvee;": "\u22ce", - "cuwed;": "\u22cf", - "cwconint;": "\u2232", - "cwint;": "\u2231", - "cylcty;": "\u232d", - "dArr;": "\u21d3", - "dHar;": "\u2965", - "dagger;": "\u2020", - "daleth;": "\u2138", - "darr;": "\u2193", - "dash;": "\u2010", - "dashv;": "\u22a3", - "dbkarow;": "\u290f", - "dblac;": "\u02dd", - "dcaron;": "\u010f", - "dcy;": "\u0434", - "dd;": "\u2146", - "ddagger;": "\u2021", - "ddarr;": "\u21ca", - "ddotseq;": "\u2a77", - "deg": "\xb0", - "deg;": "\xb0", - "delta;": "\u03b4", - "demptyv;": "\u29b1", - "dfisht;": "\u297f", - "dfr;": "\U0001d521", - "dharl;": "\u21c3", - "dharr;": "\u21c2", - "diam;": "\u22c4", - "diamond;": "\u22c4", - "diamondsuit;": "\u2666", - "diams;": "\u2666", - "die;": "\xa8", - "digamma;": "\u03dd", - "disin;": "\u22f2", - "div;": "\xf7", - "divide": "\xf7", - "divide;": "\xf7", - "divideontimes;": "\u22c7", - "divonx;": "\u22c7", - "djcy;": "\u0452", - "dlcorn;": "\u231e", - "dlcrop;": "\u230d", - "dollar;": "$", - "dopf;": "\U0001d555", - "dot;": "\u02d9", - "doteq;": "\u2250", - "doteqdot;": "\u2251", - "dotminus;": "\u2238", - "dotplus;": "\u2214", - "dotsquare;": "\u22a1", - "doublebarwedge;": "\u2306", - "downarrow;": "\u2193", - "downdownarrows;": "\u21ca", - "downharpoonleft;": "\u21c3", - "downharpoonright;": "\u21c2", - "drbkarow;": "\u2910", - "drcorn;": "\u231f", - "drcrop;": "\u230c", - "dscr;": "\U0001d4b9", - "dscy;": "\u0455", - "dsol;": "\u29f6", - "dstrok;": "\u0111", - "dtdot;": "\u22f1", - "dtri;": "\u25bf", - "dtrif;": "\u25be", - "duarr;": "\u21f5", - "duhar;": "\u296f", - "dwangle;": "\u29a6", - "dzcy;": "\u045f", - "dzigrarr;": "\u27ff", - "eDDot;": "\u2a77", - "eDot;": "\u2251", - "eacute": "\xe9", - "eacute;": "\xe9", - "easter;": "\u2a6e", - "ecaron;": "\u011b", - "ecir;": "\u2256", - "ecirc": "\xea", - "ecirc;": "\xea", - "ecolon;": "\u2255", - "ecy;": "\u044d", - "edot;": "\u0117", - "ee;": "\u2147", - "efDot;": "\u2252", - "efr;": "\U0001d522", - "eg;": "\u2a9a", - "egrave": "\xe8", - "egrave;": "\xe8", - "egs;": "\u2a96", - "egsdot;": "\u2a98", - "el;": "\u2a99", - "elinters;": "\u23e7", - "ell;": "\u2113", - "els;": "\u2a95", - "elsdot;": "\u2a97", - "emacr;": "\u0113", - "empty;": "\u2205", - "emptyset;": "\u2205", - "emptyv;": "\u2205", - "emsp13;": "\u2004", - "emsp14;": "\u2005", - "emsp;": "\u2003", - "eng;": "\u014b", - "ensp;": "\u2002", - "eogon;": "\u0119", - "eopf;": "\U0001d556", - "epar;": "\u22d5", - "eparsl;": "\u29e3", - "eplus;": "\u2a71", - "epsi;": "\u03b5", - "epsilon;": "\u03b5", - "epsiv;": "\u03f5", - "eqcirc;": "\u2256", - "eqcolon;": "\u2255", - "eqsim;": "\u2242", - "eqslantgtr;": "\u2a96", - "eqslantless;": "\u2a95", - "equals;": "=", - "equest;": "\u225f", - "equiv;": "\u2261", - "equivDD;": "\u2a78", - "eqvparsl;": "\u29e5", - "erDot;": "\u2253", - "erarr;": "\u2971", - "escr;": "\u212f", - "esdot;": "\u2250", - "esim;": "\u2242", - "eta;": "\u03b7", - "eth": "\xf0", - "eth;": "\xf0", - "euml": "\xeb", - "euml;": "\xeb", - "euro;": "\u20ac", - "excl;": "!", - "exist;": "\u2203", - "expectation;": "\u2130", - "exponentiale;": "\u2147", - "fallingdotseq;": "\u2252", - "fcy;": "\u0444", - "female;": "\u2640", - "ffilig;": "\ufb03", - "fflig;": "\ufb00", - "ffllig;": "\ufb04", - "ffr;": "\U0001d523", - "filig;": "\ufb01", - "fjlig;": "fj", - "flat;": "\u266d", - "fllig;": "\ufb02", - "fltns;": "\u25b1", - "fnof;": "\u0192", - "fopf;": "\U0001d557", - "forall;": "\u2200", - "fork;": "\u22d4", - "forkv;": "\u2ad9", - "fpartint;": "\u2a0d", - "frac12": "\xbd", - "frac12;": "\xbd", - "frac13;": "\u2153", - "frac14": "\xbc", - "frac14;": "\xbc", - "frac15;": "\u2155", - "frac16;": "\u2159", - "frac18;": "\u215b", - "frac23;": "\u2154", - "frac25;": "\u2156", - "frac34": "\xbe", - "frac34;": "\xbe", - "frac35;": "\u2157", - "frac38;": "\u215c", - "frac45;": "\u2158", - "frac56;": "\u215a", - "frac58;": "\u215d", - "frac78;": "\u215e", - "frasl;": "\u2044", - "frown;": "\u2322", - "fscr;": "\U0001d4bb", - "gE;": "\u2267", - "gEl;": "\u2a8c", - "gacute;": "\u01f5", - "gamma;": "\u03b3", - "gammad;": "\u03dd", - "gap;": "\u2a86", - "gbreve;": "\u011f", - "gcirc;": "\u011d", - "gcy;": "\u0433", - "gdot;": "\u0121", - "ge;": "\u2265", - "gel;": "\u22db", - "geq;": "\u2265", - "geqq;": "\u2267", - "geqslant;": "\u2a7e", - "ges;": "\u2a7e", - "gescc;": "\u2aa9", - "gesdot;": "\u2a80", - "gesdoto;": "\u2a82", - "gesdotol;": "\u2a84", - "gesl;": "\u22db\ufe00", - "gesles;": "\u2a94", - "gfr;": "\U0001d524", - "gg;": "\u226b", - "ggg;": "\u22d9", - "gimel;": "\u2137", - "gjcy;": "\u0453", - "gl;": "\u2277", - "glE;": "\u2a92", - "gla;": "\u2aa5", - "glj;": "\u2aa4", - "gnE;": "\u2269", - "gnap;": "\u2a8a", - "gnapprox;": "\u2a8a", - "gne;": "\u2a88", - "gneq;": "\u2a88", - "gneqq;": "\u2269", - "gnsim;": "\u22e7", - "gopf;": "\U0001d558", - "grave;": "`", - "gscr;": "\u210a", - "gsim;": "\u2273", - "gsime;": "\u2a8e", - "gsiml;": "\u2a90", - "gt": ">", - "gt;": ">", - "gtcc;": "\u2aa7", - "gtcir;": "\u2a7a", - "gtdot;": "\u22d7", - "gtlPar;": "\u2995", - "gtquest;": "\u2a7c", - "gtrapprox;": "\u2a86", - "gtrarr;": "\u2978", - "gtrdot;": "\u22d7", - "gtreqless;": "\u22db", - "gtreqqless;": "\u2a8c", - "gtrless;": "\u2277", - "gtrsim;": "\u2273", - "gvertneqq;": "\u2269\ufe00", - "gvnE;": "\u2269\ufe00", - "hArr;": "\u21d4", - "hairsp;": "\u200a", - "half;": "\xbd", - "hamilt;": "\u210b", - "hardcy;": "\u044a", - "harr;": "\u2194", - "harrcir;": "\u2948", - "harrw;": "\u21ad", - "hbar;": "\u210f", - "hcirc;": "\u0125", - "hearts;": "\u2665", - "heartsuit;": "\u2665", - "hellip;": "\u2026", - "hercon;": "\u22b9", - "hfr;": "\U0001d525", - "hksearow;": "\u2925", - "hkswarow;": "\u2926", - "hoarr;": "\u21ff", - "homtht;": "\u223b", - "hookleftarrow;": "\u21a9", - "hookrightarrow;": "\u21aa", - "hopf;": "\U0001d559", - "horbar;": "\u2015", - "hscr;": "\U0001d4bd", - "hslash;": "\u210f", - "hstrok;": "\u0127", - "hybull;": "\u2043", - "hyphen;": "\u2010", - "iacute": "\xed", - "iacute;": "\xed", - "ic;": "\u2063", - "icirc": "\xee", - "icirc;": "\xee", - "icy;": "\u0438", - "iecy;": "\u0435", - "iexcl": "\xa1", - "iexcl;": "\xa1", - "iff;": "\u21d4", - "ifr;": "\U0001d526", - "igrave": "\xec", - "igrave;": "\xec", - "ii;": "\u2148", - "iiiint;": "\u2a0c", - "iiint;": "\u222d", - "iinfin;": "\u29dc", - "iiota;": "\u2129", - "ijlig;": "\u0133", - "imacr;": "\u012b", - "image;": "\u2111", - "imagline;": "\u2110", - "imagpart;": "\u2111", - "imath;": "\u0131", - "imof;": "\u22b7", - "imped;": "\u01b5", - "in;": "\u2208", - "incare;": "\u2105", - "infin;": "\u221e", - "infintie;": "\u29dd", - "inodot;": "\u0131", - "int;": "\u222b", - "intcal;": "\u22ba", - "integers;": "\u2124", - "intercal;": "\u22ba", - "intlarhk;": "\u2a17", - "intprod;": "\u2a3c", - "iocy;": "\u0451", - "iogon;": "\u012f", - "iopf;": "\U0001d55a", - "iota;": "\u03b9", - "iprod;": "\u2a3c", - "iquest": "\xbf", - "iquest;": "\xbf", - "iscr;": "\U0001d4be", - "isin;": "\u2208", - "isinE;": "\u22f9", - "isindot;": "\u22f5", - "isins;": "\u22f4", - "isinsv;": "\u22f3", - "isinv;": "\u2208", - "it;": "\u2062", - "itilde;": "\u0129", - "iukcy;": "\u0456", - "iuml": "\xef", - "iuml;": "\xef", - "jcirc;": "\u0135", - "jcy;": "\u0439", - "jfr;": "\U0001d527", - "jmath;": "\u0237", - "jopf;": "\U0001d55b", - "jscr;": "\U0001d4bf", - "jsercy;": "\u0458", - "jukcy;": "\u0454", - "kappa;": "\u03ba", - "kappav;": "\u03f0", - "kcedil;": "\u0137", - "kcy;": "\u043a", - "kfr;": "\U0001d528", - "kgreen;": "\u0138", - "khcy;": "\u0445", - "kjcy;": "\u045c", - "kopf;": "\U0001d55c", - "kscr;": "\U0001d4c0", - "lAarr;": "\u21da", - "lArr;": "\u21d0", - "lAtail;": "\u291b", - "lBarr;": "\u290e", - "lE;": "\u2266", - "lEg;": "\u2a8b", - "lHar;": "\u2962", - "lacute;": "\u013a", - "laemptyv;": "\u29b4", - "lagran;": "\u2112", - "lambda;": "\u03bb", - "lang;": "\u27e8", - "langd;": "\u2991", - "langle;": "\u27e8", - "lap;": "\u2a85", - "laquo": "\xab", - "laquo;": "\xab", - "larr;": "\u2190", - "larrb;": "\u21e4", - "larrbfs;": "\u291f", - "larrfs;": "\u291d", - "larrhk;": "\u21a9", - "larrlp;": "\u21ab", - "larrpl;": "\u2939", - "larrsim;": "\u2973", - "larrtl;": "\u21a2", - "lat;": "\u2aab", - "latail;": "\u2919", - "late;": "\u2aad", - "lates;": "\u2aad\ufe00", - "lbarr;": "\u290c", - "lbbrk;": "\u2772", - "lbrace;": "{", - "lbrack;": "[", - "lbrke;": "\u298b", - "lbrksld;": "\u298f", - "lbrkslu;": "\u298d", - "lcaron;": "\u013e", - "lcedil;": "\u013c", - "lceil;": "\u2308", - "lcub;": "{", - "lcy;": "\u043b", - "ldca;": "\u2936", - "ldquo;": "\u201c", - "ldquor;": "\u201e", - "ldrdhar;": "\u2967", - "ldrushar;": "\u294b", - "ldsh;": "\u21b2", - "le;": "\u2264", - "leftarrow;": "\u2190", - "leftarrowtail;": "\u21a2", - "leftharpoondown;": "\u21bd", - "leftharpoonup;": "\u21bc", - "leftleftarrows;": "\u21c7", - "leftrightarrow;": "\u2194", - "leftrightarrows;": "\u21c6", - "leftrightharpoons;": "\u21cb", - "leftrightsquigarrow;": "\u21ad", - "leftthreetimes;": "\u22cb", - "leg;": "\u22da", - "leq;": "\u2264", - "leqq;": "\u2266", - "leqslant;": "\u2a7d", - "les;": "\u2a7d", - "lescc;": "\u2aa8", - "lesdot;": "\u2a7f", - "lesdoto;": "\u2a81", - "lesdotor;": "\u2a83", - "lesg;": "\u22da\ufe00", - "lesges;": "\u2a93", - "lessapprox;": "\u2a85", - "lessdot;": "\u22d6", - "lesseqgtr;": "\u22da", - "lesseqqgtr;": "\u2a8b", - "lessgtr;": "\u2276", - "lesssim;": "\u2272", - "lfisht;": "\u297c", - "lfloor;": "\u230a", - "lfr;": "\U0001d529", - "lg;": "\u2276", - "lgE;": "\u2a91", - "lhard;": "\u21bd", - "lharu;": "\u21bc", - "lharul;": "\u296a", - "lhblk;": "\u2584", - "ljcy;": "\u0459", - "ll;": "\u226a", - "llarr;": "\u21c7", - "llcorner;": "\u231e", - "llhard;": "\u296b", - "lltri;": "\u25fa", - "lmidot;": "\u0140", - "lmoust;": "\u23b0", - "lmoustache;": "\u23b0", - "lnE;": "\u2268", - "lnap;": "\u2a89", - "lnapprox;": "\u2a89", - "lne;": "\u2a87", - "lneq;": "\u2a87", - "lneqq;": "\u2268", - "lnsim;": "\u22e6", - "loang;": "\u27ec", - "loarr;": "\u21fd", - "lobrk;": "\u27e6", - "longleftarrow;": "\u27f5", - "longleftrightarrow;": "\u27f7", - "longmapsto;": "\u27fc", - "longrightarrow;": "\u27f6", - "looparrowleft;": "\u21ab", - "looparrowright;": "\u21ac", - "lopar;": "\u2985", - "lopf;": "\U0001d55d", - "loplus;": "\u2a2d", - "lotimes;": "\u2a34", - "lowast;": "\u2217", - "lowbar;": "_", - "loz;": "\u25ca", - "lozenge;": "\u25ca", - "lozf;": "\u29eb", - "lpar;": "(", - "lparlt;": "\u2993", - "lrarr;": "\u21c6", - "lrcorner;": "\u231f", - "lrhar;": "\u21cb", - "lrhard;": "\u296d", - "lrm;": "\u200e", - "lrtri;": "\u22bf", - "lsaquo;": "\u2039", - "lscr;": "\U0001d4c1", - "lsh;": "\u21b0", - "lsim;": "\u2272", - "lsime;": "\u2a8d", - "lsimg;": "\u2a8f", - "lsqb;": "[", - "lsquo;": "\u2018", - "lsquor;": "\u201a", - "lstrok;": "\u0142", - "lt": "<", - "lt;": "<", - "ltcc;": "\u2aa6", - "ltcir;": "\u2a79", - "ltdot;": "\u22d6", - "lthree;": "\u22cb", - "ltimes;": "\u22c9", - "ltlarr;": "\u2976", - "ltquest;": "\u2a7b", - "ltrPar;": "\u2996", - "ltri;": "\u25c3", - "ltrie;": "\u22b4", - "ltrif;": "\u25c2", - "lurdshar;": "\u294a", - "luruhar;": "\u2966", - "lvertneqq;": "\u2268\ufe00", - "lvnE;": "\u2268\ufe00", - "mDDot;": "\u223a", - "macr": "\xaf", - "macr;": "\xaf", - "male;": "\u2642", - "malt;": "\u2720", - "maltese;": "\u2720", - "map;": "\u21a6", - "mapsto;": "\u21a6", - "mapstodown;": "\u21a7", - "mapstoleft;": "\u21a4", - "mapstoup;": "\u21a5", - "marker;": "\u25ae", - "mcomma;": "\u2a29", - "mcy;": "\u043c", - "mdash;": "\u2014", - "measuredangle;": "\u2221", - "mfr;": "\U0001d52a", - "mho;": "\u2127", - "micro": "\xb5", - "micro;": "\xb5", - "mid;": "\u2223", - "midast;": "*", - "midcir;": "\u2af0", - "middot": "\xb7", - "middot;": "\xb7", - "minus;": "\u2212", - "minusb;": "\u229f", - "minusd;": "\u2238", - "minusdu;": "\u2a2a", - "mlcp;": "\u2adb", - "mldr;": "\u2026", - "mnplus;": "\u2213", - "models;": "\u22a7", - "mopf;": "\U0001d55e", - "mp;": "\u2213", - "mscr;": "\U0001d4c2", - "mstpos;": "\u223e", - "mu;": "\u03bc", - "multimap;": "\u22b8", - "mumap;": "\u22b8", - "nGg;": "\u22d9\u0338", - "nGt;": "\u226b\u20d2", - "nGtv;": "\u226b\u0338", - "nLeftarrow;": "\u21cd", - "nLeftrightarrow;": "\u21ce", - "nLl;": "\u22d8\u0338", - "nLt;": "\u226a\u20d2", - "nLtv;": "\u226a\u0338", - "nRightarrow;": "\u21cf", - "nVDash;": "\u22af", - "nVdash;": "\u22ae", - "nabla;": "\u2207", - "nacute;": "\u0144", - "nang;": "\u2220\u20d2", - "nap;": "\u2249", - "napE;": "\u2a70\u0338", - "napid;": "\u224b\u0338", - "napos;": "\u0149", - "napprox;": "\u2249", - "natur;": "\u266e", - "natural;": "\u266e", - "naturals;": "\u2115", - "nbsp": "\xa0", - "nbsp;": "\xa0", - "nbump;": "\u224e\u0338", - "nbumpe;": "\u224f\u0338", - "ncap;": "\u2a43", - "ncaron;": "\u0148", - "ncedil;": "\u0146", - "ncong;": "\u2247", - "ncongdot;": "\u2a6d\u0338", - "ncup;": "\u2a42", - "ncy;": "\u043d", - "ndash;": "\u2013", - "ne;": "\u2260", - "neArr;": "\u21d7", - "nearhk;": "\u2924", - "nearr;": "\u2197", - "nearrow;": "\u2197", - "nedot;": "\u2250\u0338", - "nequiv;": "\u2262", - "nesear;": "\u2928", - "nesim;": "\u2242\u0338", - "nexist;": "\u2204", - "nexists;": "\u2204", - "nfr;": "\U0001d52b", - "ngE;": "\u2267\u0338", - "nge;": "\u2271", - "ngeq;": "\u2271", - "ngeqq;": "\u2267\u0338", - "ngeqslant;": "\u2a7e\u0338", - "nges;": "\u2a7e\u0338", - "ngsim;": "\u2275", - "ngt;": "\u226f", - "ngtr;": "\u226f", - "nhArr;": "\u21ce", - "nharr;": "\u21ae", - "nhpar;": "\u2af2", - "ni;": "\u220b", - "nis;": "\u22fc", - "nisd;": "\u22fa", - "niv;": "\u220b", - "njcy;": "\u045a", - "nlArr;": "\u21cd", - "nlE;": "\u2266\u0338", - "nlarr;": "\u219a", - "nldr;": "\u2025", - "nle;": "\u2270", - "nleftarrow;": "\u219a", - "nleftrightarrow;": "\u21ae", - "nleq;": "\u2270", - "nleqq;": "\u2266\u0338", - "nleqslant;": "\u2a7d\u0338", - "nles;": "\u2a7d\u0338", - "nless;": "\u226e", - "nlsim;": "\u2274", - "nlt;": "\u226e", - "nltri;": "\u22ea", - "nltrie;": "\u22ec", - "nmid;": "\u2224", - "nopf;": "\U0001d55f", - "not": "\xac", - "not;": "\xac", - "notin;": "\u2209", - "notinE;": "\u22f9\u0338", - "notindot;": "\u22f5\u0338", - "notinva;": "\u2209", - "notinvb;": "\u22f7", - "notinvc;": "\u22f6", - "notni;": "\u220c", - "notniva;": "\u220c", - "notnivb;": "\u22fe", - "notnivc;": "\u22fd", - "npar;": "\u2226", - "nparallel;": "\u2226", - "nparsl;": "\u2afd\u20e5", - "npart;": "\u2202\u0338", - "npolint;": "\u2a14", - "npr;": "\u2280", - "nprcue;": "\u22e0", - "npre;": "\u2aaf\u0338", - "nprec;": "\u2280", - "npreceq;": "\u2aaf\u0338", - "nrArr;": "\u21cf", - "nrarr;": "\u219b", - "nrarrc;": "\u2933\u0338", - "nrarrw;": "\u219d\u0338", - "nrightarrow;": "\u219b", - "nrtri;": "\u22eb", - "nrtrie;": "\u22ed", - "nsc;": "\u2281", - "nsccue;": "\u22e1", - "nsce;": "\u2ab0\u0338", - "nscr;": "\U0001d4c3", - "nshortmid;": "\u2224", - "nshortparallel;": "\u2226", - "nsim;": "\u2241", - "nsime;": "\u2244", - "nsimeq;": "\u2244", - "nsmid;": "\u2224", - "nspar;": "\u2226", - "nsqsube;": "\u22e2", - "nsqsupe;": "\u22e3", - "nsub;": "\u2284", - "nsubE;": "\u2ac5\u0338", - "nsube;": "\u2288", - "nsubset;": "\u2282\u20d2", - "nsubseteq;": "\u2288", - "nsubseteqq;": "\u2ac5\u0338", - "nsucc;": "\u2281", - "nsucceq;": "\u2ab0\u0338", - "nsup;": "\u2285", - "nsupE;": "\u2ac6\u0338", - "nsupe;": "\u2289", - "nsupset;": "\u2283\u20d2", - "nsupseteq;": "\u2289", - "nsupseteqq;": "\u2ac6\u0338", - "ntgl;": "\u2279", - "ntilde": "\xf1", - "ntilde;": "\xf1", - "ntlg;": "\u2278", - "ntriangleleft;": "\u22ea", - "ntrianglelefteq;": "\u22ec", - "ntriangleright;": "\u22eb", - "ntrianglerighteq;": "\u22ed", - "nu;": "\u03bd", - "num;": "#", - "numero;": "\u2116", - "numsp;": "\u2007", - "nvDash;": "\u22ad", - "nvHarr;": "\u2904", - "nvap;": "\u224d\u20d2", - "nvdash;": "\u22ac", - "nvge;": "\u2265\u20d2", - "nvgt;": ">\u20d2", - "nvinfin;": "\u29de", - "nvlArr;": "\u2902", - "nvle;": "\u2264\u20d2", - "nvlt;": "<\u20d2", - "nvltrie;": "\u22b4\u20d2", - "nvrArr;": "\u2903", - "nvrtrie;": "\u22b5\u20d2", - "nvsim;": "\u223c\u20d2", - "nwArr;": "\u21d6", - "nwarhk;": "\u2923", - "nwarr;": "\u2196", - "nwarrow;": "\u2196", - "nwnear;": "\u2927", - "oS;": "\u24c8", - "oacute": "\xf3", - "oacute;": "\xf3", - "oast;": "\u229b", - "ocir;": "\u229a", - "ocirc": "\xf4", - "ocirc;": "\xf4", - "ocy;": "\u043e", - "odash;": "\u229d", - "odblac;": "\u0151", - "odiv;": "\u2a38", - "odot;": "\u2299", - "odsold;": "\u29bc", - "oelig;": "\u0153", - "ofcir;": "\u29bf", - "ofr;": "\U0001d52c", - "ogon;": "\u02db", - "ograve": "\xf2", - "ograve;": "\xf2", - "ogt;": "\u29c1", - "ohbar;": "\u29b5", - "ohm;": "\u03a9", - "oint;": "\u222e", - "olarr;": "\u21ba", - "olcir;": "\u29be", - "olcross;": "\u29bb", - "oline;": "\u203e", - "olt;": "\u29c0", - "omacr;": "\u014d", - "omega;": "\u03c9", - "omicron;": "\u03bf", - "omid;": "\u29b6", - "ominus;": "\u2296", - "oopf;": "\U0001d560", - "opar;": "\u29b7", - "operp;": "\u29b9", - "oplus;": "\u2295", - "or;": "\u2228", - "orarr;": "\u21bb", - "ord;": "\u2a5d", - "order;": "\u2134", - "orderof;": "\u2134", - "ordf": "\xaa", - "ordf;": "\xaa", - "ordm": "\xba", - "ordm;": "\xba", - "origof;": "\u22b6", - "oror;": "\u2a56", - "orslope;": "\u2a57", - "orv;": "\u2a5b", - "oscr;": "\u2134", - "oslash": "\xf8", - "oslash;": "\xf8", - "osol;": "\u2298", - "otilde": "\xf5", - "otilde;": "\xf5", - "otimes;": "\u2297", - "otimesas;": "\u2a36", - "ouml": "\xf6", - "ouml;": "\xf6", - "ovbar;": "\u233d", - "par;": "\u2225", - "para": "\xb6", - "para;": "\xb6", - "parallel;": "\u2225", - "parsim;": "\u2af3", - "parsl;": "\u2afd", - "part;": "\u2202", - "pcy;": "\u043f", - "percnt;": "%", - "period;": ".", - "permil;": "\u2030", - "perp;": "\u22a5", - "pertenk;": "\u2031", - "pfr;": "\U0001d52d", - "phi;": "\u03c6", - "phiv;": "\u03d5", - "phmmat;": "\u2133", - "phone;": "\u260e", - "pi;": "\u03c0", - "pitchfork;": "\u22d4", - "piv;": "\u03d6", - "planck;": "\u210f", - "planckh;": "\u210e", - "plankv;": "\u210f", - "plus;": "+", - "plusacir;": "\u2a23", - "plusb;": "\u229e", - "pluscir;": "\u2a22", - "plusdo;": "\u2214", - "plusdu;": "\u2a25", - "pluse;": "\u2a72", - "plusmn": "\xb1", - "plusmn;": "\xb1", - "plussim;": "\u2a26", - "plustwo;": "\u2a27", - "pm;": "\xb1", - "pointint;": "\u2a15", - "popf;": "\U0001d561", - "pound": "\xa3", - "pound;": "\xa3", - "pr;": "\u227a", - "prE;": "\u2ab3", - "prap;": "\u2ab7", - "prcue;": "\u227c", - "pre;": "\u2aaf", - "prec;": "\u227a", - "precapprox;": "\u2ab7", - "preccurlyeq;": "\u227c", - "preceq;": "\u2aaf", - "precnapprox;": "\u2ab9", - "precneqq;": "\u2ab5", - "precnsim;": "\u22e8", - "precsim;": "\u227e", - "prime;": "\u2032", - "primes;": "\u2119", - "prnE;": "\u2ab5", - "prnap;": "\u2ab9", - "prnsim;": "\u22e8", - "prod;": "\u220f", - "profalar;": "\u232e", - "profline;": "\u2312", - "profsurf;": "\u2313", - "prop;": "\u221d", - "propto;": "\u221d", - "prsim;": "\u227e", - "prurel;": "\u22b0", - "pscr;": "\U0001d4c5", - "psi;": "\u03c8", - "puncsp;": "\u2008", - "qfr;": "\U0001d52e", - "qint;": "\u2a0c", - "qopf;": "\U0001d562", - "qprime;": "\u2057", - "qscr;": "\U0001d4c6", - "quaternions;": "\u210d", - "quatint;": "\u2a16", - "quest;": "?", - "questeq;": "\u225f", - "quot": "\"", - "quot;": "\"", - "rAarr;": "\u21db", - "rArr;": "\u21d2", - "rAtail;": "\u291c", - "rBarr;": "\u290f", - "rHar;": "\u2964", - "race;": "\u223d\u0331", - "racute;": "\u0155", - "radic;": "\u221a", - "raemptyv;": "\u29b3", - "rang;": "\u27e9", - "rangd;": "\u2992", - "range;": "\u29a5", - "rangle;": "\u27e9", - "raquo": "\xbb", - "raquo;": "\xbb", - "rarr;": "\u2192", - "rarrap;": "\u2975", - "rarrb;": "\u21e5", - "rarrbfs;": "\u2920", - "rarrc;": "\u2933", - "rarrfs;": "\u291e", - "rarrhk;": "\u21aa", - "rarrlp;": "\u21ac", - "rarrpl;": "\u2945", - "rarrsim;": "\u2974", - "rarrtl;": "\u21a3", - "rarrw;": "\u219d", - "ratail;": "\u291a", - "ratio;": "\u2236", - "rationals;": "\u211a", - "rbarr;": "\u290d", - "rbbrk;": "\u2773", - "rbrace;": "}", - "rbrack;": "]", - "rbrke;": "\u298c", - "rbrksld;": "\u298e", - "rbrkslu;": "\u2990", - "rcaron;": "\u0159", - "rcedil;": "\u0157", - "rceil;": "\u2309", - "rcub;": "}", - "rcy;": "\u0440", - "rdca;": "\u2937", - "rdldhar;": "\u2969", - "rdquo;": "\u201d", - "rdquor;": "\u201d", - "rdsh;": "\u21b3", - "real;": "\u211c", - "realine;": "\u211b", - "realpart;": "\u211c", - "reals;": "\u211d", - "rect;": "\u25ad", - "reg": "\xae", - "reg;": "\xae", - "rfisht;": "\u297d", - "rfloor;": "\u230b", - "rfr;": "\U0001d52f", - "rhard;": "\u21c1", - "rharu;": "\u21c0", - "rharul;": "\u296c", - "rho;": "\u03c1", - "rhov;": "\u03f1", - "rightarrow;": "\u2192", - "rightarrowtail;": "\u21a3", - "rightharpoondown;": "\u21c1", - "rightharpoonup;": "\u21c0", - "rightleftarrows;": "\u21c4", - "rightleftharpoons;": "\u21cc", - "rightrightarrows;": "\u21c9", - "rightsquigarrow;": "\u219d", - "rightthreetimes;": "\u22cc", - "ring;": "\u02da", - "risingdotseq;": "\u2253", - "rlarr;": "\u21c4", - "rlhar;": "\u21cc", - "rlm;": "\u200f", - "rmoust;": "\u23b1", - "rmoustache;": "\u23b1", - "rnmid;": "\u2aee", - "roang;": "\u27ed", - "roarr;": "\u21fe", - "robrk;": "\u27e7", - "ropar;": "\u2986", - "ropf;": "\U0001d563", - "roplus;": "\u2a2e", - "rotimes;": "\u2a35", - "rpar;": ")", - "rpargt;": "\u2994", - "rppolint;": "\u2a12", - "rrarr;": "\u21c9", - "rsaquo;": "\u203a", - "rscr;": "\U0001d4c7", - "rsh;": "\u21b1", - "rsqb;": "]", - "rsquo;": "\u2019", - "rsquor;": "\u2019", - "rthree;": "\u22cc", - "rtimes;": "\u22ca", - "rtri;": "\u25b9", - "rtrie;": "\u22b5", - "rtrif;": "\u25b8", - "rtriltri;": "\u29ce", - "ruluhar;": "\u2968", - "rx;": "\u211e", - "sacute;": "\u015b", - "sbquo;": "\u201a", - "sc;": "\u227b", - "scE;": "\u2ab4", - "scap;": "\u2ab8", - "scaron;": "\u0161", - "sccue;": "\u227d", - "sce;": "\u2ab0", - "scedil;": "\u015f", - "scirc;": "\u015d", - "scnE;": "\u2ab6", - "scnap;": "\u2aba", - "scnsim;": "\u22e9", - "scpolint;": "\u2a13", - "scsim;": "\u227f", - "scy;": "\u0441", - "sdot;": "\u22c5", - "sdotb;": "\u22a1", - "sdote;": "\u2a66", - "seArr;": "\u21d8", - "searhk;": "\u2925", - "searr;": "\u2198", - "searrow;": "\u2198", - "sect": "\xa7", - "sect;": "\xa7", - "semi;": ";", - "seswar;": "\u2929", - "setminus;": "\u2216", - "setmn;": "\u2216", - "sext;": "\u2736", - "sfr;": "\U0001d530", - "sfrown;": "\u2322", - "sharp;": "\u266f", - "shchcy;": "\u0449", - "shcy;": "\u0448", - "shortmid;": "\u2223", - "shortparallel;": "\u2225", - "shy": "\xad", - "shy;": "\xad", - "sigma;": "\u03c3", - "sigmaf;": "\u03c2", - "sigmav;": "\u03c2", - "sim;": "\u223c", - "simdot;": "\u2a6a", - "sime;": "\u2243", - "simeq;": "\u2243", - "simg;": "\u2a9e", - "simgE;": "\u2aa0", - "siml;": "\u2a9d", - "simlE;": "\u2a9f", - "simne;": "\u2246", - "simplus;": "\u2a24", - "simrarr;": "\u2972", - "slarr;": "\u2190", - "smallsetminus;": "\u2216", - "smashp;": "\u2a33", - "smeparsl;": "\u29e4", - "smid;": "\u2223", - "smile;": "\u2323", - "smt;": "\u2aaa", - "smte;": "\u2aac", - "smtes;": "\u2aac\ufe00", - "softcy;": "\u044c", - "sol;": "/", - "solb;": "\u29c4", - "solbar;": "\u233f", - "sopf;": "\U0001d564", - "spades;": "\u2660", - "spadesuit;": "\u2660", - "spar;": "\u2225", - "sqcap;": "\u2293", - "sqcaps;": "\u2293\ufe00", - "sqcup;": "\u2294", - "sqcups;": "\u2294\ufe00", - "sqsub;": "\u228f", - "sqsube;": "\u2291", - "sqsubset;": "\u228f", - "sqsubseteq;": "\u2291", - "sqsup;": "\u2290", - "sqsupe;": "\u2292", - "sqsupset;": "\u2290", - "sqsupseteq;": "\u2292", - "squ;": "\u25a1", - "square;": "\u25a1", - "squarf;": "\u25aa", - "squf;": "\u25aa", - "srarr;": "\u2192", - "sscr;": "\U0001d4c8", - "ssetmn;": "\u2216", - "ssmile;": "\u2323", - "sstarf;": "\u22c6", - "star;": "\u2606", - "starf;": "\u2605", - "straightepsilon;": "\u03f5", - "straightphi;": "\u03d5", - "strns;": "\xaf", - "sub;": "\u2282", - "subE;": "\u2ac5", - "subdot;": "\u2abd", - "sube;": "\u2286", - "subedot;": "\u2ac3", - "submult;": "\u2ac1", - "subnE;": "\u2acb", - "subne;": "\u228a", - "subplus;": "\u2abf", - "subrarr;": "\u2979", - "subset;": "\u2282", - "subseteq;": "\u2286", - "subseteqq;": "\u2ac5", - "subsetneq;": "\u228a", - "subsetneqq;": "\u2acb", - "subsim;": "\u2ac7", - "subsub;": "\u2ad5", - "subsup;": "\u2ad3", - "succ;": "\u227b", - "succapprox;": "\u2ab8", - "succcurlyeq;": "\u227d", - "succeq;": "\u2ab0", - "succnapprox;": "\u2aba", - "succneqq;": "\u2ab6", - "succnsim;": "\u22e9", - "succsim;": "\u227f", - "sum;": "\u2211", - "sung;": "\u266a", - "sup1": "\xb9", - "sup1;": "\xb9", - "sup2": "\xb2", - "sup2;": "\xb2", - "sup3": "\xb3", - "sup3;": "\xb3", - "sup;": "\u2283", - "supE;": "\u2ac6", - "supdot;": "\u2abe", - "supdsub;": "\u2ad8", - "supe;": "\u2287", - "supedot;": "\u2ac4", - "suphsol;": "\u27c9", - "suphsub;": "\u2ad7", - "suplarr;": "\u297b", - "supmult;": "\u2ac2", - "supnE;": "\u2acc", - "supne;": "\u228b", - "supplus;": "\u2ac0", - "supset;": "\u2283", - "supseteq;": "\u2287", - "supseteqq;": "\u2ac6", - "supsetneq;": "\u228b", - "supsetneqq;": "\u2acc", - "supsim;": "\u2ac8", - "supsub;": "\u2ad4", - "supsup;": "\u2ad6", - "swArr;": "\u21d9", - "swarhk;": "\u2926", - "swarr;": "\u2199", - "swarrow;": "\u2199", - "swnwar;": "\u292a", - "szlig": "\xdf", - "szlig;": "\xdf", - "target;": "\u2316", - "tau;": "\u03c4", - "tbrk;": "\u23b4", - "tcaron;": "\u0165", - "tcedil;": "\u0163", - "tcy;": "\u0442", - "tdot;": "\u20db", - "telrec;": "\u2315", - "tfr;": "\U0001d531", - "there4;": "\u2234", - "therefore;": "\u2234", - "theta;": "\u03b8", - "thetasym;": "\u03d1", - "thetav;": "\u03d1", - "thickapprox;": "\u2248", - "thicksim;": "\u223c", - "thinsp;": "\u2009", - "thkap;": "\u2248", - "thksim;": "\u223c", - "thorn": "\xfe", - "thorn;": "\xfe", - "tilde;": "\u02dc", - "times": "\xd7", - "times;": "\xd7", - "timesb;": "\u22a0", - "timesbar;": "\u2a31", - "timesd;": "\u2a30", - "tint;": "\u222d", - "toea;": "\u2928", - "top;": "\u22a4", - "topbot;": "\u2336", - "topcir;": "\u2af1", - "topf;": "\U0001d565", - "topfork;": "\u2ada", - "tosa;": "\u2929", - "tprime;": "\u2034", - "trade;": "\u2122", - "triangle;": "\u25b5", - "triangledown;": "\u25bf", - "triangleleft;": "\u25c3", - "trianglelefteq;": "\u22b4", - "triangleq;": "\u225c", - "triangleright;": "\u25b9", - "trianglerighteq;": "\u22b5", - "tridot;": "\u25ec", - "trie;": "\u225c", - "triminus;": "\u2a3a", - "triplus;": "\u2a39", - "trisb;": "\u29cd", - "tritime;": "\u2a3b", - "trpezium;": "\u23e2", - "tscr;": "\U0001d4c9", - "tscy;": "\u0446", - "tshcy;": "\u045b", - "tstrok;": "\u0167", - "twixt;": "\u226c", - "twoheadleftarrow;": "\u219e", - "twoheadrightarrow;": "\u21a0", - "uArr;": "\u21d1", - "uHar;": "\u2963", - "uacute": "\xfa", - "uacute;": "\xfa", - "uarr;": "\u2191", - "ubrcy;": "\u045e", - "ubreve;": "\u016d", - "ucirc": "\xfb", - "ucirc;": "\xfb", - "ucy;": "\u0443", - "udarr;": "\u21c5", - "udblac;": "\u0171", - "udhar;": "\u296e", - "ufisht;": "\u297e", - "ufr;": "\U0001d532", - "ugrave": "\xf9", - "ugrave;": "\xf9", - "uharl;": "\u21bf", - "uharr;": "\u21be", - "uhblk;": "\u2580", - "ulcorn;": "\u231c", - "ulcorner;": "\u231c", - "ulcrop;": "\u230f", - "ultri;": "\u25f8", - "umacr;": "\u016b", - "uml": "\xa8", - "uml;": "\xa8", - "uogon;": "\u0173", - "uopf;": "\U0001d566", - "uparrow;": "\u2191", - "updownarrow;": "\u2195", - "upharpoonleft;": "\u21bf", - "upharpoonright;": "\u21be", - "uplus;": "\u228e", - "upsi;": "\u03c5", - "upsih;": "\u03d2", - "upsilon;": "\u03c5", - "upuparrows;": "\u21c8", - "urcorn;": "\u231d", - "urcorner;": "\u231d", - "urcrop;": "\u230e", - "uring;": "\u016f", - "urtri;": "\u25f9", - "uscr;": "\U0001d4ca", - "utdot;": "\u22f0", - "utilde;": "\u0169", - "utri;": "\u25b5", - "utrif;": "\u25b4", - "uuarr;": "\u21c8", - "uuml": "\xfc", - "uuml;": "\xfc", - "uwangle;": "\u29a7", - "vArr;": "\u21d5", - "vBar;": "\u2ae8", - "vBarv;": "\u2ae9", - "vDash;": "\u22a8", - "vangrt;": "\u299c", - "varepsilon;": "\u03f5", - "varkappa;": "\u03f0", - "varnothing;": "\u2205", - "varphi;": "\u03d5", - "varpi;": "\u03d6", - "varpropto;": "\u221d", - "varr;": "\u2195", - "varrho;": "\u03f1", - "varsigma;": "\u03c2", - "varsubsetneq;": "\u228a\ufe00", - "varsubsetneqq;": "\u2acb\ufe00", - "varsupsetneq;": "\u228b\ufe00", - "varsupsetneqq;": "\u2acc\ufe00", - "vartheta;": "\u03d1", - "vartriangleleft;": "\u22b2", - "vartriangleright;": "\u22b3", - "vcy;": "\u0432", - "vdash;": "\u22a2", - "vee;": "\u2228", - "veebar;": "\u22bb", - "veeeq;": "\u225a", - "vellip;": "\u22ee", - "verbar;": "|", - "vert;": "|", - "vfr;": "\U0001d533", - "vltri;": "\u22b2", - "vnsub;": "\u2282\u20d2", - "vnsup;": "\u2283\u20d2", - "vopf;": "\U0001d567", - "vprop;": "\u221d", - "vrtri;": "\u22b3", - "vscr;": "\U0001d4cb", - "vsubnE;": "\u2acb\ufe00", - "vsubne;": "\u228a\ufe00", - "vsupnE;": "\u2acc\ufe00", - "vsupne;": "\u228b\ufe00", - "vzigzag;": "\u299a", - "wcirc;": "\u0175", - "wedbar;": "\u2a5f", - "wedge;": "\u2227", - "wedgeq;": "\u2259", - "weierp;": "\u2118", - "wfr;": "\U0001d534", - "wopf;": "\U0001d568", - "wp;": "\u2118", - "wr;": "\u2240", - "wreath;": "\u2240", - "wscr;": "\U0001d4cc", - "xcap;": "\u22c2", - "xcirc;": "\u25ef", - "xcup;": "\u22c3", - "xdtri;": "\u25bd", - "xfr;": "\U0001d535", - "xhArr;": "\u27fa", - "xharr;": "\u27f7", - "xi;": "\u03be", - "xlArr;": "\u27f8", - "xlarr;": "\u27f5", - "xmap;": "\u27fc", - "xnis;": "\u22fb", - "xodot;": "\u2a00", - "xopf;": "\U0001d569", - "xoplus;": "\u2a01", - "xotime;": "\u2a02", - "xrArr;": "\u27f9", - "xrarr;": "\u27f6", - "xscr;": "\U0001d4cd", - "xsqcup;": "\u2a06", - "xuplus;": "\u2a04", - "xutri;": "\u25b3", - "xvee;": "\u22c1", - "xwedge;": "\u22c0", - "yacute": "\xfd", - "yacute;": "\xfd", - "yacy;": "\u044f", - "ycirc;": "\u0177", - "ycy;": "\u044b", - "yen": "\xa5", - "yen;": "\xa5", - "yfr;": "\U0001d536", - "yicy;": "\u0457", - "yopf;": "\U0001d56a", - "yscr;": "\U0001d4ce", - "yucy;": "\u044e", - "yuml": "\xff", - "yuml;": "\xff", - "zacute;": "\u017a", - "zcaron;": "\u017e", - "zcy;": "\u0437", - "zdot;": "\u017c", - "zeetrf;": "\u2128", - "zeta;": "\u03b6", - "zfr;": "\U0001d537", - "zhcy;": "\u0436", - "zigrarr;": "\u21dd", - "zopf;": "\U0001d56b", - "zscr;": "\U0001d4cf", - "zwj;": "\u200d", - "zwnj;": "\u200c", + 'AElig': '\xc6', + 'AElig;': '\xc6', + 'AMP': '&', + 'AMP;': '&', + 'Aacute': '\xc1', + 'Aacute;': '\xc1', + 'Abreve;': '\u0102', + 'Acirc': '\xc2', + 'Acirc;': '\xc2', + 'Acy;': '\u0410', + 'Afr;': '\U0001d504', + 'Agrave': '\xc0', + 'Agrave;': '\xc0', + 'Alpha;': '\u0391', + 'Amacr;': '\u0100', + 'And;': '\u2a53', + 'Aogon;': '\u0104', + 'Aopf;': '\U0001d538', + 'ApplyFunction;': '\u2061', + 'Aring': '\xc5', + 'Aring;': '\xc5', + 'Ascr;': '\U0001d49c', + 'Assign;': '\u2254', + 'Atilde': '\xc3', + 'Atilde;': '\xc3', + 'Auml': '\xc4', + 'Auml;': '\xc4', + 'Backslash;': '\u2216', + 'Barv;': '\u2ae7', + 'Barwed;': '\u2306', + 'Bcy;': '\u0411', + 'Because;': '\u2235', + 'Bernoullis;': '\u212c', + 'Beta;': '\u0392', + 'Bfr;': '\U0001d505', + 'Bopf;': '\U0001d539', + 'Breve;': '\u02d8', + 'Bscr;': '\u212c', + 'Bumpeq;': '\u224e', + 'CHcy;': '\u0427', + 'COPY': '\xa9', + 'COPY;': '\xa9', + 'Cacute;': '\u0106', + 'Cap;': '\u22d2', + 'CapitalDifferentialD;': '\u2145', + 'Cayleys;': '\u212d', + 'Ccaron;': '\u010c', + 'Ccedil': '\xc7', + 'Ccedil;': '\xc7', + 'Ccirc;': '\u0108', + 'Cconint;': '\u2230', + 'Cdot;': '\u010a', + 'Cedilla;': '\xb8', + 'CenterDot;': '\xb7', + 'Cfr;': '\u212d', + 'Chi;': '\u03a7', + 'CircleDot;': '\u2299', + 'CircleMinus;': '\u2296', + 'CirclePlus;': '\u2295', + 'CircleTimes;': '\u2297', + 'ClockwiseContourIntegral;': '\u2232', + 'CloseCurlyDoubleQuote;': '\u201d', + 'CloseCurlyQuote;': '\u2019', + 'Colon;': '\u2237', + 'Colone;': '\u2a74', + 'Congruent;': '\u2261', + 'Conint;': '\u222f', + 'ContourIntegral;': '\u222e', + 'Copf;': '\u2102', + 'Coproduct;': '\u2210', + 'CounterClockwiseContourIntegral;': '\u2233', + 'Cross;': '\u2a2f', + 'Cscr;': '\U0001d49e', + 'Cup;': '\u22d3', + 'CupCap;': '\u224d', + 'DD;': '\u2145', + 'DDotrahd;': '\u2911', + 'DJcy;': '\u0402', + 'DScy;': '\u0405', + 'DZcy;': '\u040f', + 'Dagger;': '\u2021', + 'Darr;': '\u21a1', + 'Dashv;': '\u2ae4', + 'Dcaron;': '\u010e', + 'Dcy;': '\u0414', + 'Del;': '\u2207', + 'Delta;': '\u0394', + 'Dfr;': '\U0001d507', + 'DiacriticalAcute;': '\xb4', + 'DiacriticalDot;': '\u02d9', + 'DiacriticalDoubleAcute;': '\u02dd', + 'DiacriticalGrave;': '`', + 'DiacriticalTilde;': '\u02dc', + 'Diamond;': '\u22c4', + 'DifferentialD;': '\u2146', + 'Dopf;': '\U0001d53b', + 'Dot;': '\xa8', + 'DotDot;': '\u20dc', + 'DotEqual;': '\u2250', + 'DoubleContourIntegral;': '\u222f', + 'DoubleDot;': '\xa8', + 'DoubleDownArrow;': '\u21d3', + 'DoubleLeftArrow;': '\u21d0', + 'DoubleLeftRightArrow;': '\u21d4', + 'DoubleLeftTee;': '\u2ae4', + 'DoubleLongLeftArrow;': '\u27f8', + 'DoubleLongLeftRightArrow;': '\u27fa', + 'DoubleLongRightArrow;': '\u27f9', + 'DoubleRightArrow;': '\u21d2', + 'DoubleRightTee;': '\u22a8', + 'DoubleUpArrow;': '\u21d1', + 'DoubleUpDownArrow;': '\u21d5', + 'DoubleVerticalBar;': '\u2225', + 'DownArrow;': '\u2193', + 'DownArrowBar;': '\u2913', + 'DownArrowUpArrow;': '\u21f5', + 'DownBreve;': '\u0311', + 'DownLeftRightVector;': '\u2950', + 'DownLeftTeeVector;': '\u295e', + 'DownLeftVector;': '\u21bd', + 'DownLeftVectorBar;': '\u2956', + 'DownRightTeeVector;': '\u295f', + 'DownRightVector;': '\u21c1', + 'DownRightVectorBar;': '\u2957', + 'DownTee;': '\u22a4', + 'DownTeeArrow;': '\u21a7', + 'Downarrow;': '\u21d3', + 'Dscr;': '\U0001d49f', + 'Dstrok;': '\u0110', + 'ENG;': '\u014a', + 'ETH': '\xd0', + 'ETH;': '\xd0', + 'Eacute': '\xc9', + 'Eacute;': '\xc9', + 'Ecaron;': '\u011a', + 'Ecirc': '\xca', + 'Ecirc;': '\xca', + 'Ecy;': '\u042d', + 'Edot;': '\u0116', + 'Efr;': '\U0001d508', + 'Egrave': '\xc8', + 'Egrave;': '\xc8', + 'Element;': '\u2208', + 'Emacr;': '\u0112', + 'EmptySmallSquare;': '\u25fb', + 'EmptyVerySmallSquare;': '\u25ab', + 'Eogon;': '\u0118', + 'Eopf;': '\U0001d53c', + 'Epsilon;': '\u0395', + 'Equal;': '\u2a75', + 'EqualTilde;': '\u2242', + 'Equilibrium;': '\u21cc', + 'Escr;': '\u2130', + 'Esim;': '\u2a73', + 'Eta;': '\u0397', + 'Euml': '\xcb', + 'Euml;': '\xcb', + 'Exists;': '\u2203', + 'ExponentialE;': '\u2147', + 'Fcy;': '\u0424', + 'Ffr;': '\U0001d509', + 'FilledSmallSquare;': '\u25fc', + 'FilledVerySmallSquare;': '\u25aa', + 'Fopf;': '\U0001d53d', + 'ForAll;': '\u2200', + 'Fouriertrf;': '\u2131', + 'Fscr;': '\u2131', + 'GJcy;': '\u0403', + 'GT': '>', + 'GT;': '>', + 'Gamma;': '\u0393', + 'Gammad;': '\u03dc', + 'Gbreve;': '\u011e', + 'Gcedil;': '\u0122', + 'Gcirc;': '\u011c', + 'Gcy;': '\u0413', + 'Gdot;': '\u0120', + 'Gfr;': '\U0001d50a', + 'Gg;': '\u22d9', + 'Gopf;': '\U0001d53e', + 'GreaterEqual;': '\u2265', + 'GreaterEqualLess;': '\u22db', + 'GreaterFullEqual;': '\u2267', + 'GreaterGreater;': '\u2aa2', + 'GreaterLess;': '\u2277', + 'GreaterSlantEqual;': '\u2a7e', + 'GreaterTilde;': '\u2273', + 'Gscr;': '\U0001d4a2', + 'Gt;': '\u226b', + 'HARDcy;': '\u042a', + 'Hacek;': '\u02c7', + 'Hat;': '^', + 'Hcirc;': '\u0124', + 'Hfr;': '\u210c', + 'HilbertSpace;': '\u210b', + 'Hopf;': '\u210d', + 'HorizontalLine;': '\u2500', + 'Hscr;': '\u210b', + 'Hstrok;': '\u0126', + 'HumpDownHump;': '\u224e', + 'HumpEqual;': '\u224f', + 'IEcy;': '\u0415', + 'IJlig;': '\u0132', + 'IOcy;': '\u0401', + 'Iacute': '\xcd', + 'Iacute;': '\xcd', + 'Icirc': '\xce', + 'Icirc;': '\xce', + 'Icy;': '\u0418', + 'Idot;': '\u0130', + 'Ifr;': '\u2111', + 'Igrave': '\xcc', + 'Igrave;': '\xcc', + 'Im;': '\u2111', + 'Imacr;': '\u012a', + 'ImaginaryI;': '\u2148', + 'Implies;': '\u21d2', + 'Int;': '\u222c', + 'Integral;': '\u222b', + 'Intersection;': '\u22c2', + 'InvisibleComma;': '\u2063', + 'InvisibleTimes;': '\u2062', + 'Iogon;': '\u012e', + 'Iopf;': '\U0001d540', + 'Iota;': '\u0399', + 'Iscr;': '\u2110', + 'Itilde;': '\u0128', + 'Iukcy;': '\u0406', + 'Iuml': '\xcf', + 'Iuml;': '\xcf', + 'Jcirc;': '\u0134', + 'Jcy;': '\u0419', + 'Jfr;': '\U0001d50d', + 'Jopf;': '\U0001d541', + 'Jscr;': '\U0001d4a5', + 'Jsercy;': '\u0408', + 'Jukcy;': '\u0404', + 'KHcy;': '\u0425', + 'KJcy;': '\u040c', + 'Kappa;': '\u039a', + 'Kcedil;': '\u0136', + 'Kcy;': '\u041a', + 'Kfr;': '\U0001d50e', + 'Kopf;': '\U0001d542', + 'Kscr;': '\U0001d4a6', + 'LJcy;': '\u0409', + 'LT': '<', + 'LT;': '<', + 'Lacute;': '\u0139', + 'Lambda;': '\u039b', + 'Lang;': '\u27ea', + 'Laplacetrf;': '\u2112', + 'Larr;': '\u219e', + 'Lcaron;': '\u013d', + 'Lcedil;': '\u013b', + 'Lcy;': '\u041b', + 'LeftAngleBracket;': '\u27e8', + 'LeftArrow;': '\u2190', + 'LeftArrowBar;': '\u21e4', + 'LeftArrowRightArrow;': '\u21c6', + 'LeftCeiling;': '\u2308', + 'LeftDoubleBracket;': '\u27e6', + 'LeftDownTeeVector;': '\u2961', + 'LeftDownVector;': '\u21c3', + 'LeftDownVectorBar;': '\u2959', + 'LeftFloor;': '\u230a', + 'LeftRightArrow;': '\u2194', + 'LeftRightVector;': '\u294e', + 'LeftTee;': '\u22a3', + 'LeftTeeArrow;': '\u21a4', + 'LeftTeeVector;': '\u295a', + 'LeftTriangle;': '\u22b2', + 'LeftTriangleBar;': '\u29cf', + 'LeftTriangleEqual;': '\u22b4', + 'LeftUpDownVector;': '\u2951', + 'LeftUpTeeVector;': '\u2960', + 'LeftUpVector;': '\u21bf', + 'LeftUpVectorBar;': '\u2958', + 'LeftVector;': '\u21bc', + 'LeftVectorBar;': '\u2952', + 'Leftarrow;': '\u21d0', + 'Leftrightarrow;': '\u21d4', + 'LessEqualGreater;': '\u22da', + 'LessFullEqual;': '\u2266', + 'LessGreater;': '\u2276', + 'LessLess;': '\u2aa1', + 'LessSlantEqual;': '\u2a7d', + 'LessTilde;': '\u2272', + 'Lfr;': '\U0001d50f', + 'Ll;': '\u22d8', + 'Lleftarrow;': '\u21da', + 'Lmidot;': '\u013f', + 'LongLeftArrow;': '\u27f5', + 'LongLeftRightArrow;': '\u27f7', + 'LongRightArrow;': '\u27f6', + 'Longleftarrow;': '\u27f8', + 'Longleftrightarrow;': '\u27fa', + 'Longrightarrow;': '\u27f9', + 'Lopf;': '\U0001d543', + 'LowerLeftArrow;': '\u2199', + 'LowerRightArrow;': '\u2198', + 'Lscr;': '\u2112', + 'Lsh;': '\u21b0', + 'Lstrok;': '\u0141', + 'Lt;': '\u226a', + 'Map;': '\u2905', + 'Mcy;': '\u041c', + 'MediumSpace;': '\u205f', + 'Mellintrf;': '\u2133', + 'Mfr;': '\U0001d510', + 'MinusPlus;': '\u2213', + 'Mopf;': '\U0001d544', + 'Mscr;': '\u2133', + 'Mu;': '\u039c', + 'NJcy;': '\u040a', + 'Nacute;': '\u0143', + 'Ncaron;': '\u0147', + 'Ncedil;': '\u0145', + 'Ncy;': '\u041d', + 'NegativeMediumSpace;': '\u200b', + 'NegativeThickSpace;': '\u200b', + 'NegativeThinSpace;': '\u200b', + 'NegativeVeryThinSpace;': '\u200b', + 'NestedGreaterGreater;': '\u226b', + 'NestedLessLess;': '\u226a', + 'NewLine;': '\n', + 'Nfr;': '\U0001d511', + 'NoBreak;': '\u2060', + 'NonBreakingSpace;': '\xa0', + 'Nopf;': '\u2115', + 'Not;': '\u2aec', + 'NotCongruent;': '\u2262', + 'NotCupCap;': '\u226d', + 'NotDoubleVerticalBar;': '\u2226', + 'NotElement;': '\u2209', + 'NotEqual;': '\u2260', + 'NotEqualTilde;': '\u2242\u0338', + 'NotExists;': '\u2204', + 'NotGreater;': '\u226f', + 'NotGreaterEqual;': '\u2271', + 'NotGreaterFullEqual;': '\u2267\u0338', + 'NotGreaterGreater;': '\u226b\u0338', + 'NotGreaterLess;': '\u2279', + 'NotGreaterSlantEqual;': '\u2a7e\u0338', + 'NotGreaterTilde;': '\u2275', + 'NotHumpDownHump;': '\u224e\u0338', + 'NotHumpEqual;': '\u224f\u0338', + 'NotLeftTriangle;': '\u22ea', + 'NotLeftTriangleBar;': '\u29cf\u0338', + 'NotLeftTriangleEqual;': '\u22ec', + 'NotLess;': '\u226e', + 'NotLessEqual;': '\u2270', + 'NotLessGreater;': '\u2278', + 'NotLessLess;': '\u226a\u0338', + 'NotLessSlantEqual;': '\u2a7d\u0338', + 'NotLessTilde;': '\u2274', + 'NotNestedGreaterGreater;': '\u2aa2\u0338', + 'NotNestedLessLess;': '\u2aa1\u0338', + 'NotPrecedes;': '\u2280', + 'NotPrecedesEqual;': '\u2aaf\u0338', + 'NotPrecedesSlantEqual;': '\u22e0', + 'NotReverseElement;': '\u220c', + 'NotRightTriangle;': '\u22eb', + 'NotRightTriangleBar;': '\u29d0\u0338', + 'NotRightTriangleEqual;': '\u22ed', + 'NotSquareSubset;': '\u228f\u0338', + 'NotSquareSubsetEqual;': '\u22e2', + 'NotSquareSuperset;': '\u2290\u0338', + 'NotSquareSupersetEqual;': '\u22e3', + 'NotSubset;': '\u2282\u20d2', + 'NotSubsetEqual;': '\u2288', + 'NotSucceeds;': '\u2281', + 'NotSucceedsEqual;': '\u2ab0\u0338', + 'NotSucceedsSlantEqual;': '\u22e1', + 'NotSucceedsTilde;': '\u227f\u0338', + 'NotSuperset;': '\u2283\u20d2', + 'NotSupersetEqual;': '\u2289', + 'NotTilde;': '\u2241', + 'NotTildeEqual;': '\u2244', + 'NotTildeFullEqual;': '\u2247', + 'NotTildeTilde;': '\u2249', + 'NotVerticalBar;': '\u2224', + 'Nscr;': '\U0001d4a9', + 'Ntilde': '\xd1', + 'Ntilde;': '\xd1', + 'Nu;': '\u039d', + 'OElig;': '\u0152', + 'Oacute': '\xd3', + 'Oacute;': '\xd3', + 'Ocirc': '\xd4', + 'Ocirc;': '\xd4', + 'Ocy;': '\u041e', + 'Odblac;': '\u0150', + 'Ofr;': '\U0001d512', + 'Ograve': '\xd2', + 'Ograve;': '\xd2', + 'Omacr;': '\u014c', + 'Omega;': '\u03a9', + 'Omicron;': '\u039f', + 'Oopf;': '\U0001d546', + 'OpenCurlyDoubleQuote;': '\u201c', + 'OpenCurlyQuote;': '\u2018', + 'Or;': '\u2a54', + 'Oscr;': '\U0001d4aa', + 'Oslash': '\xd8', + 'Oslash;': '\xd8', + 'Otilde': '\xd5', + 'Otilde;': '\xd5', + 'Otimes;': '\u2a37', + 'Ouml': '\xd6', + 'Ouml;': '\xd6', + 'OverBar;': '\u203e', + 'OverBrace;': '\u23de', + 'OverBracket;': '\u23b4', + 'OverParenthesis;': '\u23dc', + 'PartialD;': '\u2202', + 'Pcy;': '\u041f', + 'Pfr;': '\U0001d513', + 'Phi;': '\u03a6', + 'Pi;': '\u03a0', + 'PlusMinus;': '\xb1', + 'Poincareplane;': '\u210c', + 'Popf;': '\u2119', + 'Pr;': '\u2abb', + 'Precedes;': '\u227a', + 'PrecedesEqual;': '\u2aaf', + 'PrecedesSlantEqual;': '\u227c', + 'PrecedesTilde;': '\u227e', + 'Prime;': '\u2033', + 'Product;': '\u220f', + 'Proportion;': '\u2237', + 'Proportional;': '\u221d', + 'Pscr;': '\U0001d4ab', + 'Psi;': '\u03a8', + 'QUOT': "\"", + 'QUOT;': "\"", + 'Qfr;': '\U0001d514', + 'Qopf;': '\u211a', + 'Qscr;': '\U0001d4ac', + 'RBarr;': '\u2910', + 'REG': '\xae', + 'REG;': '\xae', + 'Racute;': '\u0154', + 'Rang;': '\u27eb', + 'Rarr;': '\u21a0', + 'Rarrtl;': '\u2916', + 'Rcaron;': '\u0158', + 'Rcedil;': '\u0156', + 'Rcy;': '\u0420', + 'Re;': '\u211c', + 'ReverseElement;': '\u220b', + 'ReverseEquilibrium;': '\u21cb', + 'ReverseUpEquilibrium;': '\u296f', + 'Rfr;': '\u211c', + 'Rho;': '\u03a1', + 'RightAngleBracket;': '\u27e9', + 'RightArrow;': '\u2192', + 'RightArrowBar;': '\u21e5', + 'RightArrowLeftArrow;': '\u21c4', + 'RightCeiling;': '\u2309', + 'RightDoubleBracket;': '\u27e7', + 'RightDownTeeVector;': '\u295d', + 'RightDownVector;': '\u21c2', + 'RightDownVectorBar;': '\u2955', + 'RightFloor;': '\u230b', + 'RightTee;': '\u22a2', + 'RightTeeArrow;': '\u21a6', + 'RightTeeVector;': '\u295b', + 'RightTriangle;': '\u22b3', + 'RightTriangleBar;': '\u29d0', + 'RightTriangleEqual;': '\u22b5', + 'RightUpDownVector;': '\u294f', + 'RightUpTeeVector;': '\u295c', + 'RightUpVector;': '\u21be', + 'RightUpVectorBar;': '\u2954', + 'RightVector;': '\u21c0', + 'RightVectorBar;': '\u2953', + 'Rightarrow;': '\u21d2', + 'Ropf;': '\u211d', + 'RoundImplies;': '\u2970', + 'Rrightarrow;': '\u21db', + 'Rscr;': '\u211b', + 'Rsh;': '\u21b1', + 'RuleDelayed;': '\u29f4', + 'SHCHcy;': '\u0429', + 'SHcy;': '\u0428', + 'SOFTcy;': '\u042c', + 'Sacute;': '\u015a', + 'Sc;': '\u2abc', + 'Scaron;': '\u0160', + 'Scedil;': '\u015e', + 'Scirc;': '\u015c', + 'Scy;': '\u0421', + 'Sfr;': '\U0001d516', + 'ShortDownArrow;': '\u2193', + 'ShortLeftArrow;': '\u2190', + 'ShortRightArrow;': '\u2192', + 'ShortUpArrow;': '\u2191', + 'Sigma;': '\u03a3', + 'SmallCircle;': '\u2218', + 'Sopf;': '\U0001d54a', + 'Sqrt;': '\u221a', + 'Square;': '\u25a1', + 'SquareIntersection;': '\u2293', + 'SquareSubset;': '\u228f', + 'SquareSubsetEqual;': '\u2291', + 'SquareSuperset;': '\u2290', + 'SquareSupersetEqual;': '\u2292', + 'SquareUnion;': '\u2294', + 'Sscr;': '\U0001d4ae', + 'Star;': '\u22c6', + 'Sub;': '\u22d0', + 'Subset;': '\u22d0', + 'SubsetEqual;': '\u2286', + 'Succeeds;': '\u227b', + 'SucceedsEqual;': '\u2ab0', + 'SucceedsSlantEqual;': '\u227d', + 'SucceedsTilde;': '\u227f', + 'SuchThat;': '\u220b', + 'Sum;': '\u2211', + 'Sup;': '\u22d1', + 'Superset;': '\u2283', + 'SupersetEqual;': '\u2287', + 'Supset;': '\u22d1', + 'THORN': '\xde', + 'THORN;': '\xde', + 'TRADE;': '\u2122', + 'TSHcy;': '\u040b', + 'TScy;': '\u0426', + 'Tab;': '\t', + 'Tau;': '\u03a4', + 'Tcaron;': '\u0164', + 'Tcedil;': '\u0162', + 'Tcy;': '\u0422', + 'Tfr;': '\U0001d517', + 'Therefore;': '\u2234', + 'Theta;': '\u0398', + 'ThickSpace;': '\u205f\u200a', + 'ThinSpace;': '\u2009', + 'Tilde;': '\u223c', + 'TildeEqual;': '\u2243', + 'TildeFullEqual;': '\u2245', + 'TildeTilde;': '\u2248', + 'Topf;': '\U0001d54b', + 'TripleDot;': '\u20db', + 'Tscr;': '\U0001d4af', + 'Tstrok;': '\u0166', + 'Uacute': '\xda', + 'Uacute;': '\xda', + 'Uarr;': '\u219f', + 'Uarrocir;': '\u2949', + 'Ubrcy;': '\u040e', + 'Ubreve;': '\u016c', + 'Ucirc': '\xdb', + 'Ucirc;': '\xdb', + 'Ucy;': '\u0423', + 'Udblac;': '\u0170', + 'Ufr;': '\U0001d518', + 'Ugrave': '\xd9', + 'Ugrave;': '\xd9', + 'Umacr;': '\u016a', + 'UnderBar;': '_', + 'UnderBrace;': '\u23df', + 'UnderBracket;': '\u23b5', + 'UnderParenthesis;': '\u23dd', + 'Union;': '\u22c3', + 'UnionPlus;': '\u228e', + 'Uogon;': '\u0172', + 'Uopf;': '\U0001d54c', + 'UpArrow;': '\u2191', + 'UpArrowBar;': '\u2912', + 'UpArrowDownArrow;': '\u21c5', + 'UpDownArrow;': '\u2195', + 'UpEquilibrium;': '\u296e', + 'UpTee;': '\u22a5', + 'UpTeeArrow;': '\u21a5', + 'Uparrow;': '\u21d1', + 'Updownarrow;': '\u21d5', + 'UpperLeftArrow;': '\u2196', + 'UpperRightArrow;': '\u2197', + 'Upsi;': '\u03d2', + 'Upsilon;': '\u03a5', + 'Uring;': '\u016e', + 'Uscr;': '\U0001d4b0', + 'Utilde;': '\u0168', + 'Uuml': '\xdc', + 'Uuml;': '\xdc', + 'VDash;': '\u22ab', + 'Vbar;': '\u2aeb', + 'Vcy;': '\u0412', + 'Vdash;': '\u22a9', + 'Vdashl;': '\u2ae6', + 'Vee;': '\u22c1', + 'Verbar;': '\u2016', + 'Vert;': '\u2016', + 'VerticalBar;': '\u2223', + 'VerticalLine;': '|', + 'VerticalSeparator;': '\u2758', + 'VerticalTilde;': '\u2240', + 'VeryThinSpace;': '\u200a', + 'Vfr;': '\U0001d519', + 'Vopf;': '\U0001d54d', + 'Vscr;': '\U0001d4b1', + 'Vvdash;': '\u22aa', + 'Wcirc;': '\u0174', + 'Wedge;': '\u22c0', + 'Wfr;': '\U0001d51a', + 'Wopf;': '\U0001d54e', + 'Wscr;': '\U0001d4b2', + 'Xfr;': '\U0001d51b', + 'Xi;': '\u039e', + 'Xopf;': '\U0001d54f', + 'Xscr;': '\U0001d4b3', + 'YAcy;': '\u042f', + 'YIcy;': '\u0407', + 'YUcy;': '\u042e', + 'Yacute': '\xdd', + 'Yacute;': '\xdd', + 'Ycirc;': '\u0176', + 'Ycy;': '\u042b', + 'Yfr;': '\U0001d51c', + 'Yopf;': '\U0001d550', + 'Yscr;': '\U0001d4b4', + 'Yuml;': '\u0178', + 'ZHcy;': '\u0416', + 'Zacute;': '\u0179', + 'Zcaron;': '\u017d', + 'Zcy;': '\u0417', + 'Zdot;': '\u017b', + 'ZeroWidthSpace;': '\u200b', + 'Zeta;': '\u0396', + 'Zfr;': '\u2128', + 'Zopf;': '\u2124', + 'Zscr;': '\U0001d4b5', + 'aacute': '\xe1', + 'aacute;': '\xe1', + 'abreve;': '\u0103', + 'ac;': '\u223e', + 'acE;': '\u223e\u0333', + 'acd;': '\u223f', + 'acirc': '\xe2', + 'acirc;': '\xe2', + 'acute': '\xb4', + 'acute;': '\xb4', + 'acy;': '\u0430', + 'aelig': '\xe6', + 'aelig;': '\xe6', + 'af;': '\u2061', + 'afr;': '\U0001d51e', + 'agrave': '\xe0', + 'agrave;': '\xe0', + 'alefsym;': '\u2135', + 'aleph;': '\u2135', + 'alpha;': '\u03b1', + 'amacr;': '\u0101', + 'amalg;': '\u2a3f', + 'amp': '&', + 'amp;': '&', + 'and;': '\u2227', + 'andand;': '\u2a55', + 'andd;': '\u2a5c', + 'andslope;': '\u2a58', + 'andv;': '\u2a5a', + 'ang;': '\u2220', + 'ange;': '\u29a4', + 'angle;': '\u2220', + 'angmsd;': '\u2221', + 'angmsdaa;': '\u29a8', + 'angmsdab;': '\u29a9', + 'angmsdac;': '\u29aa', + 'angmsdad;': '\u29ab', + 'angmsdae;': '\u29ac', + 'angmsdaf;': '\u29ad', + 'angmsdag;': '\u29ae', + 'angmsdah;': '\u29af', + 'angrt;': '\u221f', + 'angrtvb;': '\u22be', + 'angrtvbd;': '\u299d', + 'angsph;': '\u2222', + 'angst;': '\xc5', + 'angzarr;': '\u237c', + 'aogon;': '\u0105', + 'aopf;': '\U0001d552', + 'ap;': '\u2248', + 'apE;': '\u2a70', + 'apacir;': '\u2a6f', + 'ape;': '\u224a', + 'apid;': '\u224b', + 'apos;': "'", + 'approx;': '\u2248', + 'approxeq;': '\u224a', + 'aring': '\xe5', + 'aring;': '\xe5', + 'ascr;': '\U0001d4b6', + 'ast;': '*', + 'asymp;': '\u2248', + 'asympeq;': '\u224d', + 'atilde': '\xe3', + 'atilde;': '\xe3', + 'auml': '\xe4', + 'auml;': '\xe4', + 'awconint;': '\u2233', + 'awint;': '\u2a11', + 'bNot;': '\u2aed', + 'backcong;': '\u224c', + 'backepsilon;': '\u03f6', + 'backprime;': '\u2035', + 'backsim;': '\u223d', + 'backsimeq;': '\u22cd', + 'barvee;': '\u22bd', + 'barwed;': '\u2305', + 'barwedge;': '\u2305', + 'bbrk;': '\u23b5', + 'bbrktbrk;': '\u23b6', + 'bcong;': '\u224c', + 'bcy;': '\u0431', + 'bdquo;': '\u201e', + 'becaus;': '\u2235', + 'because;': '\u2235', + 'bemptyv;': '\u29b0', + 'bepsi;': '\u03f6', + 'bernou;': '\u212c', + 'beta;': '\u03b2', + 'beth;': '\u2136', + 'between;': '\u226c', + 'bfr;': '\U0001d51f', + 'bigcap;': '\u22c2', + 'bigcirc;': '\u25ef', + 'bigcup;': '\u22c3', + 'bigodot;': '\u2a00', + 'bigoplus;': '\u2a01', + 'bigotimes;': '\u2a02', + 'bigsqcup;': '\u2a06', + 'bigstar;': '\u2605', + 'bigtriangledown;': '\u25bd', + 'bigtriangleup;': '\u25b3', + 'biguplus;': '\u2a04', + 'bigvee;': '\u22c1', + 'bigwedge;': '\u22c0', + 'bkarow;': '\u290d', + 'blacklozenge;': '\u29eb', + 'blacksquare;': '\u25aa', + 'blacktriangle;': '\u25b4', + 'blacktriangledown;': '\u25be', + 'blacktriangleleft;': '\u25c2', + 'blacktriangleright;': '\u25b8', + 'blank;': '\u2423', + 'blk12;': '\u2592', + 'blk14;': '\u2591', + 'blk34;': '\u2593', + 'block;': '\u2588', + 'bne;': '=\u20e5', + 'bnequiv;': '\u2261\u20e5', + 'bnot;': '\u2310', + 'bopf;': '\U0001d553', + 'bot;': '\u22a5', + 'bottom;': '\u22a5', + 'bowtie;': '\u22c8', + 'boxDL;': '\u2557', + 'boxDR;': '\u2554', + 'boxDl;': '\u2556', + 'boxDr;': '\u2553', + 'boxH;': '\u2550', + 'boxHD;': '\u2566', + 'boxHU;': '\u2569', + 'boxHd;': '\u2564', + 'boxHu;': '\u2567', + 'boxUL;': '\u255d', + 'boxUR;': '\u255a', + 'boxUl;': '\u255c', + 'boxUr;': '\u2559', + 'boxV;': '\u2551', + 'boxVH;': '\u256c', + 'boxVL;': '\u2563', + 'boxVR;': '\u2560', + 'boxVh;': '\u256b', + 'boxVl;': '\u2562', + 'boxVr;': '\u255f', + 'boxbox;': '\u29c9', + 'boxdL;': '\u2555', + 'boxdR;': '\u2552', + 'boxdl;': '\u2510', + 'boxdr;': '\u250c', + 'boxh;': '\u2500', + 'boxhD;': '\u2565', + 'boxhU;': '\u2568', + 'boxhd;': '\u252c', + 'boxhu;': '\u2534', + 'boxminus;': '\u229f', + 'boxplus;': '\u229e', + 'boxtimes;': '\u22a0', + 'boxuL;': '\u255b', + 'boxuR;': '\u2558', + 'boxul;': '\u2518', + 'boxur;': '\u2514', + 'boxv;': '\u2502', + 'boxvH;': '\u256a', + 'boxvL;': '\u2561', + 'boxvR;': '\u255e', + 'boxvh;': '\u253c', + 'boxvl;': '\u2524', + 'boxvr;': '\u251c', + 'bprime;': '\u2035', + 'breve;': '\u02d8', + 'brvbar': '\xa6', + 'brvbar;': '\xa6', + 'bscr;': '\U0001d4b7', + 'bsemi;': '\u204f', + 'bsim;': '\u223d', + 'bsime;': '\u22cd', + 'bsol;': '\\', + 'bsolb;': '\u29c5', + 'bsolhsub;': '\u27c8', + 'bull;': '\u2022', + 'bullet;': '\u2022', + 'bump;': '\u224e', + 'bumpE;': '\u2aae', + 'bumpe;': '\u224f', + 'bumpeq;': '\u224f', + 'cacute;': '\u0107', + 'cap;': '\u2229', + 'capand;': '\u2a44', + 'capbrcup;': '\u2a49', + 'capcap;': '\u2a4b', + 'capcup;': '\u2a47', + 'capdot;': '\u2a40', + 'caps;': '\u2229\ufe00', + 'caret;': '\u2041', + 'caron;': '\u02c7', + 'ccaps;': '\u2a4d', + 'ccaron;': '\u010d', + 'ccedil': '\xe7', + 'ccedil;': '\xe7', + 'ccirc;': '\u0109', + 'ccups;': '\u2a4c', + 'ccupssm;': '\u2a50', + 'cdot;': '\u010b', + 'cedil': '\xb8', + 'cedil;': '\xb8', + 'cemptyv;': '\u29b2', + 'cent': '\xa2', + 'cent;': '\xa2', + 'centerdot;': '\xb7', + 'cfr;': '\U0001d520', + 'chcy;': '\u0447', + 'check;': '\u2713', + 'checkmark;': '\u2713', + 'chi;': '\u03c7', + 'cir;': '\u25cb', + 'cirE;': '\u29c3', + 'circ;': '\u02c6', + 'circeq;': '\u2257', + 'circlearrowleft;': '\u21ba', + 'circlearrowright;': '\u21bb', + 'circledR;': '\xae', + 'circledS;': '\u24c8', + 'circledast;': '\u229b', + 'circledcirc;': '\u229a', + 'circleddash;': '\u229d', + 'cire;': '\u2257', + 'cirfnint;': '\u2a10', + 'cirmid;': '\u2aef', + 'cirscir;': '\u29c2', + 'clubs;': '\u2663', + 'clubsuit;': '\u2663', + 'colon;': ':', + 'colone;': '\u2254', + 'coloneq;': '\u2254', + 'comma;': ',', + 'commat;': '@', + 'comp;': '\u2201', + 'compfn;': '\u2218', + 'complement;': '\u2201', + 'complexes;': '\u2102', + 'cong;': '\u2245', + 'congdot;': '\u2a6d', + 'conint;': '\u222e', + 'copf;': '\U0001d554', + 'coprod;': '\u2210', + 'copy': '\xa9', + 'copy;': '\xa9', + 'copysr;': '\u2117', + 'crarr;': '\u21b5', + 'cross;': '\u2717', + 'cscr;': '\U0001d4b8', + 'csub;': '\u2acf', + 'csube;': '\u2ad1', + 'csup;': '\u2ad0', + 'csupe;': '\u2ad2', + 'ctdot;': '\u22ef', + 'cudarrl;': '\u2938', + 'cudarrr;': '\u2935', + 'cuepr;': '\u22de', + 'cuesc;': '\u22df', + 'cularr;': '\u21b6', + 'cularrp;': '\u293d', + 'cup;': '\u222a', + 'cupbrcap;': '\u2a48', + 'cupcap;': '\u2a46', + 'cupcup;': '\u2a4a', + 'cupdot;': '\u228d', + 'cupor;': '\u2a45', + 'cups;': '\u222a\ufe00', + 'curarr;': '\u21b7', + 'curarrm;': '\u293c', + 'curlyeqprec;': '\u22de', + 'curlyeqsucc;': '\u22df', + 'curlyvee;': '\u22ce', + 'curlywedge;': '\u22cf', + 'curren': '\xa4', + 'curren;': '\xa4', + 'curvearrowleft;': '\u21b6', + 'curvearrowright;': '\u21b7', + 'cuvee;': '\u22ce', + 'cuwed;': '\u22cf', + 'cwconint;': '\u2232', + 'cwint;': '\u2231', + 'cylcty;': '\u232d', + 'dArr;': '\u21d3', + 'dHar;': '\u2965', + 'dagger;': '\u2020', + 'daleth;': '\u2138', + 'darr;': '\u2193', + 'dash;': '\u2010', + 'dashv;': '\u22a3', + 'dbkarow;': '\u290f', + 'dblac;': '\u02dd', + 'dcaron;': '\u010f', + 'dcy;': '\u0434', + 'dd;': '\u2146', + 'ddagger;': '\u2021', + 'ddarr;': '\u21ca', + 'ddotseq;': '\u2a77', + 'deg': '\xb0', + 'deg;': '\xb0', + 'delta;': '\u03b4', + 'demptyv;': '\u29b1', + 'dfisht;': '\u297f', + 'dfr;': '\U0001d521', + 'dharl;': '\u21c3', + 'dharr;': '\u21c2', + 'diam;': '\u22c4', + 'diamond;': '\u22c4', + 'diamondsuit;': '\u2666', + 'diams;': '\u2666', + 'die;': '\xa8', + 'digamma;': '\u03dd', + 'disin;': '\u22f2', + 'div;': '\xf7', + 'divide': '\xf7', + 'divide;': '\xf7', + 'divideontimes;': '\u22c7', + 'divonx;': '\u22c7', + 'djcy;': '\u0452', + 'dlcorn;': '\u231e', + 'dlcrop;': '\u230d', + 'dollar;': '$', + 'dopf;': '\U0001d555', + 'dot;': '\u02d9', + 'doteq;': '\u2250', + 'doteqdot;': '\u2251', + 'dotminus;': '\u2238', + 'dotplus;': '\u2214', + 'dotsquare;': '\u22a1', + 'doublebarwedge;': '\u2306', + 'downarrow;': '\u2193', + 'downdownarrows;': '\u21ca', + 'downharpoonleft;': '\u21c3', + 'downharpoonright;': '\u21c2', + 'drbkarow;': '\u2910', + 'drcorn;': '\u231f', + 'drcrop;': '\u230c', + 'dscr;': '\U0001d4b9', + 'dscy;': '\u0455', + 'dsol;': '\u29f6', + 'dstrok;': '\u0111', + 'dtdot;': '\u22f1', + 'dtri;': '\u25bf', + 'dtrif;': '\u25be', + 'duarr;': '\u21f5', + 'duhar;': '\u296f', + 'dwangle;': '\u29a6', + 'dzcy;': '\u045f', + 'dzigrarr;': '\u27ff', + 'eDDot;': '\u2a77', + 'eDot;': '\u2251', + 'eacute': '\xe9', + 'eacute;': '\xe9', + 'easter;': '\u2a6e', + 'ecaron;': '\u011b', + 'ecir;': '\u2256', + 'ecirc': '\xea', + 'ecirc;': '\xea', + 'ecolon;': '\u2255', + 'ecy;': '\u044d', + 'edot;': '\u0117', + 'ee;': '\u2147', + 'efDot;': '\u2252', + 'efr;': '\U0001d522', + 'eg;': '\u2a9a', + 'egrave': '\xe8', + 'egrave;': '\xe8', + 'egs;': '\u2a96', + 'egsdot;': '\u2a98', + 'el;': '\u2a99', + 'elinters;': '\u23e7', + 'ell;': '\u2113', + 'els;': '\u2a95', + 'elsdot;': '\u2a97', + 'emacr;': '\u0113', + 'empty;': '\u2205', + 'emptyset;': '\u2205', + 'emptyv;': '\u2205', + 'emsp13;': '\u2004', + 'emsp14;': '\u2005', + 'emsp;': '\u2003', + 'eng;': '\u014b', + 'ensp;': '\u2002', + 'eogon;': '\u0119', + 'eopf;': '\U0001d556', + 'epar;': '\u22d5', + 'eparsl;': '\u29e3', + 'eplus;': '\u2a71', + 'epsi;': '\u03b5', + 'epsilon;': '\u03b5', + 'epsiv;': '\u03f5', + 'eqcirc;': '\u2256', + 'eqcolon;': '\u2255', + 'eqsim;': '\u2242', + 'eqslantgtr;': '\u2a96', + 'eqslantless;': '\u2a95', + 'equals;': '=', + 'equest;': '\u225f', + 'equiv;': '\u2261', + 'equivDD;': '\u2a78', + 'eqvparsl;': '\u29e5', + 'erDot;': '\u2253', + 'erarr;': '\u2971', + 'escr;': '\u212f', + 'esdot;': '\u2250', + 'esim;': '\u2242', + 'eta;': '\u03b7', + 'eth': '\xf0', + 'eth;': '\xf0', + 'euml': '\xeb', + 'euml;': '\xeb', + 'euro;': '\u20ac', + 'excl;': '!', + 'exist;': '\u2203', + 'expectation;': '\u2130', + 'exponentiale;': '\u2147', + 'fallingdotseq;': '\u2252', + 'fcy;': '\u0444', + 'female;': '\u2640', + 'ffilig;': '\ufb03', + 'fflig;': '\ufb00', + 'ffllig;': '\ufb04', + 'ffr;': '\U0001d523', + 'filig;': '\ufb01', + 'fjlig;': 'fj', + 'flat;': '\u266d', + 'fllig;': '\ufb02', + 'fltns;': '\u25b1', + 'fnof;': '\u0192', + 'fopf;': '\U0001d557', + 'forall;': '\u2200', + 'fork;': '\u22d4', + 'forkv;': '\u2ad9', + 'fpartint;': '\u2a0d', + 'frac12': '\xbd', + 'frac12;': '\xbd', + 'frac13;': '\u2153', + 'frac14': '\xbc', + 'frac14;': '\xbc', + 'frac15;': '\u2155', + 'frac16;': '\u2159', + 'frac18;': '\u215b', + 'frac23;': '\u2154', + 'frac25;': '\u2156', + 'frac34': '\xbe', + 'frac34;': '\xbe', + 'frac35;': '\u2157', + 'frac38;': '\u215c', + 'frac45;': '\u2158', + 'frac56;': '\u215a', + 'frac58;': '\u215d', + 'frac78;': '\u215e', + 'frasl;': '\u2044', + 'frown;': '\u2322', + 'fscr;': '\U0001d4bb', + 'gE;': '\u2267', + 'gEl;': '\u2a8c', + 'gacute;': '\u01f5', + 'gamma;': '\u03b3', + 'gammad;': '\u03dd', + 'gap;': '\u2a86', + 'gbreve;': '\u011f', + 'gcirc;': '\u011d', + 'gcy;': '\u0433', + 'gdot;': '\u0121', + 'ge;': '\u2265', + 'gel;': '\u22db', + 'geq;': '\u2265', + 'geqq;': '\u2267', + 'geqslant;': '\u2a7e', + 'ges;': '\u2a7e', + 'gescc;': '\u2aa9', + 'gesdot;': '\u2a80', + 'gesdoto;': '\u2a82', + 'gesdotol;': '\u2a84', + 'gesl;': '\u22db\ufe00', + 'gesles;': '\u2a94', + 'gfr;': '\U0001d524', + 'gg;': '\u226b', + 'ggg;': '\u22d9', + 'gimel;': '\u2137', + 'gjcy;': '\u0453', + 'gl;': '\u2277', + 'glE;': '\u2a92', + 'gla;': '\u2aa5', + 'glj;': '\u2aa4', + 'gnE;': '\u2269', + 'gnap;': '\u2a8a', + 'gnapprox;': '\u2a8a', + 'gne;': '\u2a88', + 'gneq;': '\u2a88', + 'gneqq;': '\u2269', + 'gnsim;': '\u22e7', + 'gopf;': '\U0001d558', + 'grave;': '`', + 'gscr;': '\u210a', + 'gsim;': '\u2273', + 'gsime;': '\u2a8e', + 'gsiml;': '\u2a90', + 'gt': '>', + 'gt;': '>', + 'gtcc;': '\u2aa7', + 'gtcir;': '\u2a7a', + 'gtdot;': '\u22d7', + 'gtlPar;': '\u2995', + 'gtquest;': '\u2a7c', + 'gtrapprox;': '\u2a86', + 'gtrarr;': '\u2978', + 'gtrdot;': '\u22d7', + 'gtreqless;': '\u22db', + 'gtreqqless;': '\u2a8c', + 'gtrless;': '\u2277', + 'gtrsim;': '\u2273', + 'gvertneqq;': '\u2269\ufe00', + 'gvnE;': '\u2269\ufe00', + 'hArr;': '\u21d4', + 'hairsp;': '\u200a', + 'half;': '\xbd', + 'hamilt;': '\u210b', + 'hardcy;': '\u044a', + 'harr;': '\u2194', + 'harrcir;': '\u2948', + 'harrw;': '\u21ad', + 'hbar;': '\u210f', + 'hcirc;': '\u0125', + 'hearts;': '\u2665', + 'heartsuit;': '\u2665', + 'hellip;': '\u2026', + 'hercon;': '\u22b9', + 'hfr;': '\U0001d525', + 'hksearow;': '\u2925', + 'hkswarow;': '\u2926', + 'hoarr;': '\u21ff', + 'homtht;': '\u223b', + 'hookleftarrow;': '\u21a9', + 'hookrightarrow;': '\u21aa', + 'hopf;': '\U0001d559', + 'horbar;': '\u2015', + 'hscr;': '\U0001d4bd', + 'hslash;': '\u210f', + 'hstrok;': '\u0127', + 'hybull;': '\u2043', + 'hyphen;': '\u2010', + 'iacute': '\xed', + 'iacute;': '\xed', + 'ic;': '\u2063', + 'icirc': '\xee', + 'icirc;': '\xee', + 'icy;': '\u0438', + 'iecy;': '\u0435', + 'iexcl': '\xa1', + 'iexcl;': '\xa1', + 'iff;': '\u21d4', + 'ifr;': '\U0001d526', + 'igrave': '\xec', + 'igrave;': '\xec', + 'ii;': '\u2148', + 'iiiint;': '\u2a0c', + 'iiint;': '\u222d', + 'iinfin;': '\u29dc', + 'iiota;': '\u2129', + 'ijlig;': '\u0133', + 'imacr;': '\u012b', + 'image;': '\u2111', + 'imagline;': '\u2110', + 'imagpart;': '\u2111', + 'imath;': '\u0131', + 'imof;': '\u22b7', + 'imped;': '\u01b5', + 'in;': '\u2208', + 'incare;': '\u2105', + 'infin;': '\u221e', + 'infintie;': '\u29dd', + 'inodot;': '\u0131', + 'int;': '\u222b', + 'intcal;': '\u22ba', + 'integers;': '\u2124', + 'intercal;': '\u22ba', + 'intlarhk;': '\u2a17', + 'intprod;': '\u2a3c', + 'iocy;': '\u0451', + 'iogon;': '\u012f', + 'iopf;': '\U0001d55a', + 'iota;': '\u03b9', + 'iprod;': '\u2a3c', + 'iquest': '\xbf', + 'iquest;': '\xbf', + 'iscr;': '\U0001d4be', + 'isin;': '\u2208', + 'isinE;': '\u22f9', + 'isindot;': '\u22f5', + 'isins;': '\u22f4', + 'isinsv;': '\u22f3', + 'isinv;': '\u2208', + 'it;': '\u2062', + 'itilde;': '\u0129', + 'iukcy;': '\u0456', + 'iuml': '\xef', + 'iuml;': '\xef', + 'jcirc;': '\u0135', + 'jcy;': '\u0439', + 'jfr;': '\U0001d527', + 'jmath;': '\u0237', + 'jopf;': '\U0001d55b', + 'jscr;': '\U0001d4bf', + 'jsercy;': '\u0458', + 'jukcy;': '\u0454', + 'kappa;': '\u03ba', + 'kappav;': '\u03f0', + 'kcedil;': '\u0137', + 'kcy;': '\u043a', + 'kfr;': '\U0001d528', + 'kgreen;': '\u0138', + 'khcy;': '\u0445', + 'kjcy;': '\u045c', + 'kopf;': '\U0001d55c', + 'kscr;': '\U0001d4c0', + 'lAarr;': '\u21da', + 'lArr;': '\u21d0', + 'lAtail;': '\u291b', + 'lBarr;': '\u290e', + 'lE;': '\u2266', + 'lEg;': '\u2a8b', + 'lHar;': '\u2962', + 'lacute;': '\u013a', + 'laemptyv;': '\u29b4', + 'lagran;': '\u2112', + 'lambda;': '\u03bb', + 'lang;': '\u27e8', + 'langd;': '\u2991', + 'langle;': '\u27e8', + 'lap;': '\u2a85', + 'laquo': '\xab', + 'laquo;': '\xab', + 'larr;': '\u2190', + 'larrb;': '\u21e4', + 'larrbfs;': '\u291f', + 'larrfs;': '\u291d', + 'larrhk;': '\u21a9', + 'larrlp;': '\u21ab', + 'larrpl;': '\u2939', + 'larrsim;': '\u2973', + 'larrtl;': '\u21a2', + 'lat;': '\u2aab', + 'latail;': '\u2919', + 'late;': '\u2aad', + 'lates;': '\u2aad\ufe00', + 'lbarr;': '\u290c', + 'lbbrk;': '\u2772', + 'lbrace;': '{', + 'lbrack;': '[', + 'lbrke;': '\u298b', + 'lbrksld;': '\u298f', + 'lbrkslu;': '\u298d', + 'lcaron;': '\u013e', + 'lcedil;': '\u013c', + 'lceil;': '\u2308', + 'lcub;': '{', + 'lcy;': '\u043b', + 'ldca;': '\u2936', + 'ldquo;': '\u201c', + 'ldquor;': '\u201e', + 'ldrdhar;': '\u2967', + 'ldrushar;': '\u294b', + 'ldsh;': '\u21b2', + 'le;': '\u2264', + 'leftarrow;': '\u2190', + 'leftarrowtail;': '\u21a2', + 'leftharpoondown;': '\u21bd', + 'leftharpoonup;': '\u21bc', + 'leftleftarrows;': '\u21c7', + 'leftrightarrow;': '\u2194', + 'leftrightarrows;': '\u21c6', + 'leftrightharpoons;': '\u21cb', + 'leftrightsquigarrow;': '\u21ad', + 'leftthreetimes;': '\u22cb', + 'leg;': '\u22da', + 'leq;': '\u2264', + 'leqq;': '\u2266', + 'leqslant;': '\u2a7d', + 'les;': '\u2a7d', + 'lescc;': '\u2aa8', + 'lesdot;': '\u2a7f', + 'lesdoto;': '\u2a81', + 'lesdotor;': '\u2a83', + 'lesg;': '\u22da\ufe00', + 'lesges;': '\u2a93', + 'lessapprox;': '\u2a85', + 'lessdot;': '\u22d6', + 'lesseqgtr;': '\u22da', + 'lesseqqgtr;': '\u2a8b', + 'lessgtr;': '\u2276', + 'lesssim;': '\u2272', + 'lfisht;': '\u297c', + 'lfloor;': '\u230a', + 'lfr;': '\U0001d529', + 'lg;': '\u2276', + 'lgE;': '\u2a91', + 'lhard;': '\u21bd', + 'lharu;': '\u21bc', + 'lharul;': '\u296a', + 'lhblk;': '\u2584', + 'ljcy;': '\u0459', + 'll;': '\u226a', + 'llarr;': '\u21c7', + 'llcorner;': '\u231e', + 'llhard;': '\u296b', + 'lltri;': '\u25fa', + 'lmidot;': '\u0140', + 'lmoust;': '\u23b0', + 'lmoustache;': '\u23b0', + 'lnE;': '\u2268', + 'lnap;': '\u2a89', + 'lnapprox;': '\u2a89', + 'lne;': '\u2a87', + 'lneq;': '\u2a87', + 'lneqq;': '\u2268', + 'lnsim;': '\u22e6', + 'loang;': '\u27ec', + 'loarr;': '\u21fd', + 'lobrk;': '\u27e6', + 'longleftarrow;': '\u27f5', + 'longleftrightarrow;': '\u27f7', + 'longmapsto;': '\u27fc', + 'longrightarrow;': '\u27f6', + 'looparrowleft;': '\u21ab', + 'looparrowright;': '\u21ac', + 'lopar;': '\u2985', + 'lopf;': '\U0001d55d', + 'loplus;': '\u2a2d', + 'lotimes;': '\u2a34', + 'lowast;': '\u2217', + 'lowbar;': '_', + 'loz;': '\u25ca', + 'lozenge;': '\u25ca', + 'lozf;': '\u29eb', + 'lpar;': '(', + 'lparlt;': '\u2993', + 'lrarr;': '\u21c6', + 'lrcorner;': '\u231f', + 'lrhar;': '\u21cb', + 'lrhard;': '\u296d', + 'lrm;': '\u200e', + 'lrtri;': '\u22bf', + 'lsaquo;': '\u2039', + 'lscr;': '\U0001d4c1', + 'lsh;': '\u21b0', + 'lsim;': '\u2272', + 'lsime;': '\u2a8d', + 'lsimg;': '\u2a8f', + 'lsqb;': '[', + 'lsquo;': '\u2018', + 'lsquor;': '\u201a', + 'lstrok;': '\u0142', + 'lt': '<', + 'lt;': '<', + 'ltcc;': '\u2aa6', + 'ltcir;': '\u2a79', + 'ltdot;': '\u22d6', + 'lthree;': '\u22cb', + 'ltimes;': '\u22c9', + 'ltlarr;': '\u2976', + 'ltquest;': '\u2a7b', + 'ltrPar;': '\u2996', + 'ltri;': '\u25c3', + 'ltrie;': '\u22b4', + 'ltrif;': '\u25c2', + 'lurdshar;': '\u294a', + 'luruhar;': '\u2966', + 'lvertneqq;': '\u2268\ufe00', + 'lvnE;': '\u2268\ufe00', + 'mDDot;': '\u223a', + 'macr': '\xaf', + 'macr;': '\xaf', + 'male;': '\u2642', + 'malt;': '\u2720', + 'maltese;': '\u2720', + 'map;': '\u21a6', + 'mapsto;': '\u21a6', + 'mapstodown;': '\u21a7', + 'mapstoleft;': '\u21a4', + 'mapstoup;': '\u21a5', + 'marker;': '\u25ae', + 'mcomma;': '\u2a29', + 'mcy;': '\u043c', + 'mdash;': '\u2014', + 'measuredangle;': '\u2221', + 'mfr;': '\U0001d52a', + 'mho;': '\u2127', + 'micro': '\xb5', + 'micro;': '\xb5', + 'mid;': '\u2223', + 'midast;': '*', + 'midcir;': '\u2af0', + 'middot': '\xb7', + 'middot;': '\xb7', + 'minus;': '\u2212', + 'minusb;': '\u229f', + 'minusd;': '\u2238', + 'minusdu;': '\u2a2a', + 'mlcp;': '\u2adb', + 'mldr;': '\u2026', + 'mnplus;': '\u2213', + 'models;': '\u22a7', + 'mopf;': '\U0001d55e', + 'mp;': '\u2213', + 'mscr;': '\U0001d4c2', + 'mstpos;': '\u223e', + 'mu;': '\u03bc', + 'multimap;': '\u22b8', + 'mumap;': '\u22b8', + 'nGg;': '\u22d9\u0338', + 'nGt;': '\u226b\u20d2', + 'nGtv;': '\u226b\u0338', + 'nLeftarrow;': '\u21cd', + 'nLeftrightarrow;': '\u21ce', + 'nLl;': '\u22d8\u0338', + 'nLt;': '\u226a\u20d2', + 'nLtv;': '\u226a\u0338', + 'nRightarrow;': '\u21cf', + 'nVDash;': '\u22af', + 'nVdash;': '\u22ae', + 'nabla;': '\u2207', + 'nacute;': '\u0144', + 'nang;': '\u2220\u20d2', + 'nap;': '\u2249', + 'napE;': '\u2a70\u0338', + 'napid;': '\u224b\u0338', + 'napos;': '\u0149', + 'napprox;': '\u2249', + 'natur;': '\u266e', + 'natural;': '\u266e', + 'naturals;': '\u2115', + 'nbsp': '\xa0', + 'nbsp;': '\xa0', + 'nbump;': '\u224e\u0338', + 'nbumpe;': '\u224f\u0338', + 'ncap;': '\u2a43', + 'ncaron;': '\u0148', + 'ncedil;': '\u0146', + 'ncong;': '\u2247', + 'ncongdot;': '\u2a6d\u0338', + 'ncup;': '\u2a42', + 'ncy;': '\u043d', + 'ndash;': '\u2013', + 'ne;': '\u2260', + 'neArr;': '\u21d7', + 'nearhk;': '\u2924', + 'nearr;': '\u2197', + 'nearrow;': '\u2197', + 'nedot;': '\u2250\u0338', + 'nequiv;': '\u2262', + 'nesear;': '\u2928', + 'nesim;': '\u2242\u0338', + 'nexist;': '\u2204', + 'nexists;': '\u2204', + 'nfr;': '\U0001d52b', + 'ngE;': '\u2267\u0338', + 'nge;': '\u2271', + 'ngeq;': '\u2271', + 'ngeqq;': '\u2267\u0338', + 'ngeqslant;': '\u2a7e\u0338', + 'nges;': '\u2a7e\u0338', + 'ngsim;': '\u2275', + 'ngt;': '\u226f', + 'ngtr;': '\u226f', + 'nhArr;': '\u21ce', + 'nharr;': '\u21ae', + 'nhpar;': '\u2af2', + 'ni;': '\u220b', + 'nis;': '\u22fc', + 'nisd;': '\u22fa', + 'niv;': '\u220b', + 'njcy;': '\u045a', + 'nlArr;': '\u21cd', + 'nlE;': '\u2266\u0338', + 'nlarr;': '\u219a', + 'nldr;': '\u2025', + 'nle;': '\u2270', + 'nleftarrow;': '\u219a', + 'nleftrightarrow;': '\u21ae', + 'nleq;': '\u2270', + 'nleqq;': '\u2266\u0338', + 'nleqslant;': '\u2a7d\u0338', + 'nles;': '\u2a7d\u0338', + 'nless;': '\u226e', + 'nlsim;': '\u2274', + 'nlt;': '\u226e', + 'nltri;': '\u22ea', + 'nltrie;': '\u22ec', + 'nmid;': '\u2224', + 'nopf;': '\U0001d55f', + 'not': '\xac', + 'not;': '\xac', + 'notin;': '\u2209', + 'notinE;': '\u22f9\u0338', + 'notindot;': '\u22f5\u0338', + 'notinva;': '\u2209', + 'notinvb;': '\u22f7', + 'notinvc;': '\u22f6', + 'notni;': '\u220c', + 'notniva;': '\u220c', + 'notnivb;': '\u22fe', + 'notnivc;': '\u22fd', + 'npar;': '\u2226', + 'nparallel;': '\u2226', + 'nparsl;': '\u2afd\u20e5', + 'npart;': '\u2202\u0338', + 'npolint;': '\u2a14', + 'npr;': '\u2280', + 'nprcue;': '\u22e0', + 'npre;': '\u2aaf\u0338', + 'nprec;': '\u2280', + 'npreceq;': '\u2aaf\u0338', + 'nrArr;': '\u21cf', + 'nrarr;': '\u219b', + 'nrarrc;': '\u2933\u0338', + 'nrarrw;': '\u219d\u0338', + 'nrightarrow;': '\u219b', + 'nrtri;': '\u22eb', + 'nrtrie;': '\u22ed', + 'nsc;': '\u2281', + 'nsccue;': '\u22e1', + 'nsce;': '\u2ab0\u0338', + 'nscr;': '\U0001d4c3', + 'nshortmid;': '\u2224', + 'nshortparallel;': '\u2226', + 'nsim;': '\u2241', + 'nsime;': '\u2244', + 'nsimeq;': '\u2244', + 'nsmid;': '\u2224', + 'nspar;': '\u2226', + 'nsqsube;': '\u22e2', + 'nsqsupe;': '\u22e3', + 'nsub;': '\u2284', + 'nsubE;': '\u2ac5\u0338', + 'nsube;': '\u2288', + 'nsubset;': '\u2282\u20d2', + 'nsubseteq;': '\u2288', + 'nsubseteqq;': '\u2ac5\u0338', + 'nsucc;': '\u2281', + 'nsucceq;': '\u2ab0\u0338', + 'nsup;': '\u2285', + 'nsupE;': '\u2ac6\u0338', + 'nsupe;': '\u2289', + 'nsupset;': '\u2283\u20d2', + 'nsupseteq;': '\u2289', + 'nsupseteqq;': '\u2ac6\u0338', + 'ntgl;': '\u2279', + 'ntilde': '\xf1', + 'ntilde;': '\xf1', + 'ntlg;': '\u2278', + 'ntriangleleft;': '\u22ea', + 'ntrianglelefteq;': '\u22ec', + 'ntriangleright;': '\u22eb', + 'ntrianglerighteq;': '\u22ed', + 'nu;': '\u03bd', + 'num;': '#', + 'numero;': '\u2116', + 'numsp;': '\u2007', + 'nvDash;': '\u22ad', + 'nvHarr;': '\u2904', + 'nvap;': '\u224d\u20d2', + 'nvdash;': '\u22ac', + 'nvge;': '\u2265\u20d2', + 'nvgt;': '>\u20d2', + 'nvinfin;': '\u29de', + 'nvlArr;': '\u2902', + 'nvle;': '\u2264\u20d2', + 'nvlt;': '<\u20d2', + 'nvltrie;': '\u22b4\u20d2', + 'nvrArr;': '\u2903', + 'nvrtrie;': '\u22b5\u20d2', + 'nvsim;': '\u223c\u20d2', + 'nwArr;': '\u21d6', + 'nwarhk;': '\u2923', + 'nwarr;': '\u2196', + 'nwarrow;': '\u2196', + 'nwnear;': '\u2927', + 'oS;': '\u24c8', + 'oacute': '\xf3', + 'oacute;': '\xf3', + 'oast;': '\u229b', + 'ocir;': '\u229a', + 'ocirc': '\xf4', + 'ocirc;': '\xf4', + 'ocy;': '\u043e', + 'odash;': '\u229d', + 'odblac;': '\u0151', + 'odiv;': '\u2a38', + 'odot;': '\u2299', + 'odsold;': '\u29bc', + 'oelig;': '\u0153', + 'ofcir;': '\u29bf', + 'ofr;': '\U0001d52c', + 'ogon;': '\u02db', + 'ograve': '\xf2', + 'ograve;': '\xf2', + 'ogt;': '\u29c1', + 'ohbar;': '\u29b5', + 'ohm;': '\u03a9', + 'oint;': '\u222e', + 'olarr;': '\u21ba', + 'olcir;': '\u29be', + 'olcross;': '\u29bb', + 'oline;': '\u203e', + 'olt;': '\u29c0', + 'omacr;': '\u014d', + 'omega;': '\u03c9', + 'omicron;': '\u03bf', + 'omid;': '\u29b6', + 'ominus;': '\u2296', + 'oopf;': '\U0001d560', + 'opar;': '\u29b7', + 'operp;': '\u29b9', + 'oplus;': '\u2295', + 'or;': '\u2228', + 'orarr;': '\u21bb', + 'ord;': '\u2a5d', + 'order;': '\u2134', + 'orderof;': '\u2134', + 'ordf': '\xaa', + 'ordf;': '\xaa', + 'ordm': '\xba', + 'ordm;': '\xba', + 'origof;': '\u22b6', + 'oror;': '\u2a56', + 'orslope;': '\u2a57', + 'orv;': '\u2a5b', + 'oscr;': '\u2134', + 'oslash': '\xf8', + 'oslash;': '\xf8', + 'osol;': '\u2298', + 'otilde': '\xf5', + 'otilde;': '\xf5', + 'otimes;': '\u2297', + 'otimesas;': '\u2a36', + 'ouml': '\xf6', + 'ouml;': '\xf6', + 'ovbar;': '\u233d', + 'par;': '\u2225', + 'para': '\xb6', + 'para;': '\xb6', + 'parallel;': '\u2225', + 'parsim;': '\u2af3', + 'parsl;': '\u2afd', + 'part;': '\u2202', + 'pcy;': '\u043f', + 'percnt;': '%', + 'period;': '.', + 'permil;': '\u2030', + 'perp;': '\u22a5', + 'pertenk;': '\u2031', + 'pfr;': '\U0001d52d', + 'phi;': '\u03c6', + 'phiv;': '\u03d5', + 'phmmat;': '\u2133', + 'phone;': '\u260e', + 'pi;': '\u03c0', + 'pitchfork;': '\u22d4', + 'piv;': '\u03d6', + 'planck;': '\u210f', + 'planckh;': '\u210e', + 'plankv;': '\u210f', + 'plus;': '+', + 'plusacir;': '\u2a23', + 'plusb;': '\u229e', + 'pluscir;': '\u2a22', + 'plusdo;': '\u2214', + 'plusdu;': '\u2a25', + 'pluse;': '\u2a72', + 'plusmn': '\xb1', + 'plusmn;': '\xb1', + 'plussim;': '\u2a26', + 'plustwo;': '\u2a27', + 'pm;': '\xb1', + 'pointint;': '\u2a15', + 'popf;': '\U0001d561', + 'pound': '\xa3', + 'pound;': '\xa3', + 'pr;': '\u227a', + 'prE;': '\u2ab3', + 'prap;': '\u2ab7', + 'prcue;': '\u227c', + 'pre;': '\u2aaf', + 'prec;': '\u227a', + 'precapprox;': '\u2ab7', + 'preccurlyeq;': '\u227c', + 'preceq;': '\u2aaf', + 'precnapprox;': '\u2ab9', + 'precneqq;': '\u2ab5', + 'precnsim;': '\u22e8', + 'precsim;': '\u227e', + 'prime;': '\u2032', + 'primes;': '\u2119', + 'prnE;': '\u2ab5', + 'prnap;': '\u2ab9', + 'prnsim;': '\u22e8', + 'prod;': '\u220f', + 'profalar;': '\u232e', + 'profline;': '\u2312', + 'profsurf;': '\u2313', + 'prop;': '\u221d', + 'propto;': '\u221d', + 'prsim;': '\u227e', + 'prurel;': '\u22b0', + 'pscr;': '\U0001d4c5', + 'psi;': '\u03c8', + 'puncsp;': '\u2008', + 'qfr;': '\U0001d52e', + 'qint;': '\u2a0c', + 'qopf;': '\U0001d562', + 'qprime;': '\u2057', + 'qscr;': '\U0001d4c6', + 'quaternions;': '\u210d', + 'quatint;': '\u2a16', + 'quest;': '?', + 'questeq;': '\u225f', + 'quot': "\"", + 'quot;': "\"", + 'rAarr;': '\u21db', + 'rArr;': '\u21d2', + 'rAtail;': '\u291c', + 'rBarr;': '\u290f', + 'rHar;': '\u2964', + 'race;': '\u223d\u0331', + 'racute;': '\u0155', + 'radic;': '\u221a', + 'raemptyv;': '\u29b3', + 'rang;': '\u27e9', + 'rangd;': '\u2992', + 'range;': '\u29a5', + 'rangle;': '\u27e9', + 'raquo': '\xbb', + 'raquo;': '\xbb', + 'rarr;': '\u2192', + 'rarrap;': '\u2975', + 'rarrb;': '\u21e5', + 'rarrbfs;': '\u2920', + 'rarrc;': '\u2933', + 'rarrfs;': '\u291e', + 'rarrhk;': '\u21aa', + 'rarrlp;': '\u21ac', + 'rarrpl;': '\u2945', + 'rarrsim;': '\u2974', + 'rarrtl;': '\u21a3', + 'rarrw;': '\u219d', + 'ratail;': '\u291a', + 'ratio;': '\u2236', + 'rationals;': '\u211a', + 'rbarr;': '\u290d', + 'rbbrk;': '\u2773', + 'rbrace;': '}', + 'rbrack;': ']', + 'rbrke;': '\u298c', + 'rbrksld;': '\u298e', + 'rbrkslu;': '\u2990', + 'rcaron;': '\u0159', + 'rcedil;': '\u0157', + 'rceil;': '\u2309', + 'rcub;': '}', + 'rcy;': '\u0440', + 'rdca;': '\u2937', + 'rdldhar;': '\u2969', + 'rdquo;': '\u201d', + 'rdquor;': '\u201d', + 'rdsh;': '\u21b3', + 'real;': '\u211c', + 'realine;': '\u211b', + 'realpart;': '\u211c', + 'reals;': '\u211d', + 'rect;': '\u25ad', + 'reg': '\xae', + 'reg;': '\xae', + 'rfisht;': '\u297d', + 'rfloor;': '\u230b', + 'rfr;': '\U0001d52f', + 'rhard;': '\u21c1', + 'rharu;': '\u21c0', + 'rharul;': '\u296c', + 'rho;': '\u03c1', + 'rhov;': '\u03f1', + 'rightarrow;': '\u2192', + 'rightarrowtail;': '\u21a3', + 'rightharpoondown;': '\u21c1', + 'rightharpoonup;': '\u21c0', + 'rightleftarrows;': '\u21c4', + 'rightleftharpoons;': '\u21cc', + 'rightrightarrows;': '\u21c9', + 'rightsquigarrow;': '\u219d', + 'rightthreetimes;': '\u22cc', + 'ring;': '\u02da', + 'risingdotseq;': '\u2253', + 'rlarr;': '\u21c4', + 'rlhar;': '\u21cc', + 'rlm;': '\u200f', + 'rmoust;': '\u23b1', + 'rmoustache;': '\u23b1', + 'rnmid;': '\u2aee', + 'roang;': '\u27ed', + 'roarr;': '\u21fe', + 'robrk;': '\u27e7', + 'ropar;': '\u2986', + 'ropf;': '\U0001d563', + 'roplus;': '\u2a2e', + 'rotimes;': '\u2a35', + 'rpar;': ')', + 'rpargt;': '\u2994', + 'rppolint;': '\u2a12', + 'rrarr;': '\u21c9', + 'rsaquo;': '\u203a', + 'rscr;': '\U0001d4c7', + 'rsh;': '\u21b1', + 'rsqb;': ']', + 'rsquo;': '\u2019', + 'rsquor;': '\u2019', + 'rthree;': '\u22cc', + 'rtimes;': '\u22ca', + 'rtri;': '\u25b9', + 'rtrie;': '\u22b5', + 'rtrif;': '\u25b8', + 'rtriltri;': '\u29ce', + 'ruluhar;': '\u2968', + 'rx;': '\u211e', + 'sacute;': '\u015b', + 'sbquo;': '\u201a', + 'sc;': '\u227b', + 'scE;': '\u2ab4', + 'scap;': '\u2ab8', + 'scaron;': '\u0161', + 'sccue;': '\u227d', + 'sce;': '\u2ab0', + 'scedil;': '\u015f', + 'scirc;': '\u015d', + 'scnE;': '\u2ab6', + 'scnap;': '\u2aba', + 'scnsim;': '\u22e9', + 'scpolint;': '\u2a13', + 'scsim;': '\u227f', + 'scy;': '\u0441', + 'sdot;': '\u22c5', + 'sdotb;': '\u22a1', + 'sdote;': '\u2a66', + 'seArr;': '\u21d8', + 'searhk;': '\u2925', + 'searr;': '\u2198', + 'searrow;': '\u2198', + 'sect': '\xa7', + 'sect;': '\xa7', + 'semi;': ';', + 'seswar;': '\u2929', + 'setminus;': '\u2216', + 'setmn;': '\u2216', + 'sext;': '\u2736', + 'sfr;': '\U0001d530', + 'sfrown;': '\u2322', + 'sharp;': '\u266f', + 'shchcy;': '\u0449', + 'shcy;': '\u0448', + 'shortmid;': '\u2223', + 'shortparallel;': '\u2225', + 'shy': '\xad', + 'shy;': '\xad', + 'sigma;': '\u03c3', + 'sigmaf;': '\u03c2', + 'sigmav;': '\u03c2', + 'sim;': '\u223c', + 'simdot;': '\u2a6a', + 'sime;': '\u2243', + 'simeq;': '\u2243', + 'simg;': '\u2a9e', + 'simgE;': '\u2aa0', + 'siml;': '\u2a9d', + 'simlE;': '\u2a9f', + 'simne;': '\u2246', + 'simplus;': '\u2a24', + 'simrarr;': '\u2972', + 'slarr;': '\u2190', + 'smallsetminus;': '\u2216', + 'smashp;': '\u2a33', + 'smeparsl;': '\u29e4', + 'smid;': '\u2223', + 'smile;': '\u2323', + 'smt;': '\u2aaa', + 'smte;': '\u2aac', + 'smtes;': '\u2aac\ufe00', + 'softcy;': '\u044c', + 'sol;': '/', + 'solb;': '\u29c4', + 'solbar;': '\u233f', + 'sopf;': '\U0001d564', + 'spades;': '\u2660', + 'spadesuit;': '\u2660', + 'spar;': '\u2225', + 'sqcap;': '\u2293', + 'sqcaps;': '\u2293\ufe00', + 'sqcup;': '\u2294', + 'sqcups;': '\u2294\ufe00', + 'sqsub;': '\u228f', + 'sqsube;': '\u2291', + 'sqsubset;': '\u228f', + 'sqsubseteq;': '\u2291', + 'sqsup;': '\u2290', + 'sqsupe;': '\u2292', + 'sqsupset;': '\u2290', + 'sqsupseteq;': '\u2292', + 'squ;': '\u25a1', + 'square;': '\u25a1', + 'squarf;': '\u25aa', + 'squf;': '\u25aa', + 'srarr;': '\u2192', + 'sscr;': '\U0001d4c8', + 'ssetmn;': '\u2216', + 'ssmile;': '\u2323', + 'sstarf;': '\u22c6', + 'star;': '\u2606', + 'starf;': '\u2605', + 'straightepsilon;': '\u03f5', + 'straightphi;': '\u03d5', + 'strns;': '\xaf', + 'sub;': '\u2282', + 'subE;': '\u2ac5', + 'subdot;': '\u2abd', + 'sube;': '\u2286', + 'subedot;': '\u2ac3', + 'submult;': '\u2ac1', + 'subnE;': '\u2acb', + 'subne;': '\u228a', + 'subplus;': '\u2abf', + 'subrarr;': '\u2979', + 'subset;': '\u2282', + 'subseteq;': '\u2286', + 'subseteqq;': '\u2ac5', + 'subsetneq;': '\u228a', + 'subsetneqq;': '\u2acb', + 'subsim;': '\u2ac7', + 'subsub;': '\u2ad5', + 'subsup;': '\u2ad3', + 'succ;': '\u227b', + 'succapprox;': '\u2ab8', + 'succcurlyeq;': '\u227d', + 'succeq;': '\u2ab0', + 'succnapprox;': '\u2aba', + 'succneqq;': '\u2ab6', + 'succnsim;': '\u22e9', + 'succsim;': '\u227f', + 'sum;': '\u2211', + 'sung;': '\u266a', + 'sup1': '\xb9', + 'sup1;': '\xb9', + 'sup2': '\xb2', + 'sup2;': '\xb2', + 'sup3': '\xb3', + 'sup3;': '\xb3', + 'sup;': '\u2283', + 'supE;': '\u2ac6', + 'supdot;': '\u2abe', + 'supdsub;': '\u2ad8', + 'supe;': '\u2287', + 'supedot;': '\u2ac4', + 'suphsol;': '\u27c9', + 'suphsub;': '\u2ad7', + 'suplarr;': '\u297b', + 'supmult;': '\u2ac2', + 'supnE;': '\u2acc', + 'supne;': '\u228b', + 'supplus;': '\u2ac0', + 'supset;': '\u2283', + 'supseteq;': '\u2287', + 'supseteqq;': '\u2ac6', + 'supsetneq;': '\u228b', + 'supsetneqq;': '\u2acc', + 'supsim;': '\u2ac8', + 'supsub;': '\u2ad4', + 'supsup;': '\u2ad6', + 'swArr;': '\u21d9', + 'swarhk;': '\u2926', + 'swarr;': '\u2199', + 'swarrow;': '\u2199', + 'swnwar;': '\u292a', + 'szlig': '\xdf', + 'szlig;': '\xdf', + 'target;': '\u2316', + 'tau;': '\u03c4', + 'tbrk;': '\u23b4', + 'tcaron;': '\u0165', + 'tcedil;': '\u0163', + 'tcy;': '\u0442', + 'tdot;': '\u20db', + 'telrec;': '\u2315', + 'tfr;': '\U0001d531', + 'there4;': '\u2234', + 'therefore;': '\u2234', + 'theta;': '\u03b8', + 'thetasym;': '\u03d1', + 'thetav;': '\u03d1', + 'thickapprox;': '\u2248', + 'thicksim;': '\u223c', + 'thinsp;': '\u2009', + 'thkap;': '\u2248', + 'thksim;': '\u223c', + 'thorn': '\xfe', + 'thorn;': '\xfe', + 'tilde;': '\u02dc', + 'times': '\xd7', + 'times;': '\xd7', + 'timesb;': '\u22a0', + 'timesbar;': '\u2a31', + 'timesd;': '\u2a30', + 'tint;': '\u222d', + 'toea;': '\u2928', + 'top;': '\u22a4', + 'topbot;': '\u2336', + 'topcir;': '\u2af1', + 'topf;': '\U0001d565', + 'topfork;': '\u2ada', + 'tosa;': '\u2929', + 'tprime;': '\u2034', + 'trade;': '\u2122', + 'triangle;': '\u25b5', + 'triangledown;': '\u25bf', + 'triangleleft;': '\u25c3', + 'trianglelefteq;': '\u22b4', + 'triangleq;': '\u225c', + 'triangleright;': '\u25b9', + 'trianglerighteq;': '\u22b5', + 'tridot;': '\u25ec', + 'trie;': '\u225c', + 'triminus;': '\u2a3a', + 'triplus;': '\u2a39', + 'trisb;': '\u29cd', + 'tritime;': '\u2a3b', + 'trpezium;': '\u23e2', + 'tscr;': '\U0001d4c9', + 'tscy;': '\u0446', + 'tshcy;': '\u045b', + 'tstrok;': '\u0167', + 'twixt;': '\u226c', + 'twoheadleftarrow;': '\u219e', + 'twoheadrightarrow;': '\u21a0', + 'uArr;': '\u21d1', + 'uHar;': '\u2963', + 'uacute': '\xfa', + 'uacute;': '\xfa', + 'uarr;': '\u2191', + 'ubrcy;': '\u045e', + 'ubreve;': '\u016d', + 'ucirc': '\xfb', + 'ucirc;': '\xfb', + 'ucy;': '\u0443', + 'udarr;': '\u21c5', + 'udblac;': '\u0171', + 'udhar;': '\u296e', + 'ufisht;': '\u297e', + 'ufr;': '\U0001d532', + 'ugrave': '\xf9', + 'ugrave;': '\xf9', + 'uharl;': '\u21bf', + 'uharr;': '\u21be', + 'uhblk;': '\u2580', + 'ulcorn;': '\u231c', + 'ulcorner;': '\u231c', + 'ulcrop;': '\u230f', + 'ultri;': '\u25f8', + 'umacr;': '\u016b', + 'uml': '\xa8', + 'uml;': '\xa8', + 'uogon;': '\u0173', + 'uopf;': '\U0001d566', + 'uparrow;': '\u2191', + 'updownarrow;': '\u2195', + 'upharpoonleft;': '\u21bf', + 'upharpoonright;': '\u21be', + 'uplus;': '\u228e', + 'upsi;': '\u03c5', + 'upsih;': '\u03d2', + 'upsilon;': '\u03c5', + 'upuparrows;': '\u21c8', + 'urcorn;': '\u231d', + 'urcorner;': '\u231d', + 'urcrop;': '\u230e', + 'uring;': '\u016f', + 'urtri;': '\u25f9', + 'uscr;': '\U0001d4ca', + 'utdot;': '\u22f0', + 'utilde;': '\u0169', + 'utri;': '\u25b5', + 'utrif;': '\u25b4', + 'uuarr;': '\u21c8', + 'uuml': '\xfc', + 'uuml;': '\xfc', + 'uwangle;': '\u29a7', + 'vArr;': '\u21d5', + 'vBar;': '\u2ae8', + 'vBarv;': '\u2ae9', + 'vDash;': '\u22a8', + 'vangrt;': '\u299c', + 'varepsilon;': '\u03f5', + 'varkappa;': '\u03f0', + 'varnothing;': '\u2205', + 'varphi;': '\u03d5', + 'varpi;': '\u03d6', + 'varpropto;': '\u221d', + 'varr;': '\u2195', + 'varrho;': '\u03f1', + 'varsigma;': '\u03c2', + 'varsubsetneq;': '\u228a\ufe00', + 'varsubsetneqq;': '\u2acb\ufe00', + 'varsupsetneq;': '\u228b\ufe00', + 'varsupsetneqq;': '\u2acc\ufe00', + 'vartheta;': '\u03d1', + 'vartriangleleft;': '\u22b2', + 'vartriangleright;': '\u22b3', + 'vcy;': '\u0432', + 'vdash;': '\u22a2', + 'vee;': '\u2228', + 'veebar;': '\u22bb', + 'veeeq;': '\u225a', + 'vellip;': '\u22ee', + 'verbar;': '|', + 'vert;': '|', + 'vfr;': '\U0001d533', + 'vltri;': '\u22b2', + 'vnsub;': '\u2282\u20d2', + 'vnsup;': '\u2283\u20d2', + 'vopf;': '\U0001d567', + 'vprop;': '\u221d', + 'vrtri;': '\u22b3', + 'vscr;': '\U0001d4cb', + 'vsubnE;': '\u2acb\ufe00', + 'vsubne;': '\u228a\ufe00', + 'vsupnE;': '\u2acc\ufe00', + 'vsupne;': '\u228b\ufe00', + 'vzigzag;': '\u299a', + 'wcirc;': '\u0175', + 'wedbar;': '\u2a5f', + 'wedge;': '\u2227', + 'wedgeq;': '\u2259', + 'weierp;': '\u2118', + 'wfr;': '\U0001d534', + 'wopf;': '\U0001d568', + 'wp;': '\u2118', + 'wr;': '\u2240', + 'wreath;': '\u2240', + 'wscr;': '\U0001d4cc', + 'xcap;': '\u22c2', + 'xcirc;': '\u25ef', + 'xcup;': '\u22c3', + 'xdtri;': '\u25bd', + 'xfr;': '\U0001d535', + 'xhArr;': '\u27fa', + 'xharr;': '\u27f7', + 'xi;': '\u03be', + 'xlArr;': '\u27f8', + 'xlarr;': '\u27f5', + 'xmap;': '\u27fc', + 'xnis;': '\u22fb', + 'xodot;': '\u2a00', + 'xopf;': '\U0001d569', + 'xoplus;': '\u2a01', + 'xotime;': '\u2a02', + 'xrArr;': '\u27f9', + 'xrarr;': '\u27f6', + 'xscr;': '\U0001d4cd', + 'xsqcup;': '\u2a06', + 'xuplus;': '\u2a04', + 'xutri;': '\u25b3', + 'xvee;': '\u22c1', + 'xwedge;': '\u22c0', + 'yacute': '\xfd', + 'yacute;': '\xfd', + 'yacy;': '\u044f', + 'ycirc;': '\u0177', + 'ycy;': '\u044b', + 'yen': '\xa5', + 'yen;': '\xa5', + 'yfr;': '\U0001d536', + 'yicy;': '\u0457', + 'yopf;': '\U0001d56a', + 'yscr;': '\U0001d4ce', + 'yucy;': '\u044e', + 'yuml': '\xff', + 'yuml;': '\xff', + 'zacute;': '\u017a', + 'zcaron;': '\u017e', + 'zcy;': '\u0437', + 'zdot;': '\u017c', + 'zeetrf;': '\u2128', + 'zeta;': '\u03b6', + 'zfr;': '\U0001d537', + 'zhcy;': '\u0436', + 'zigrarr;': '\u21dd', + 'zopf;': '\U0001d56b', + 'zscr;': '\U0001d4cf', + 'zwj;': '\u200d', + 'zwnj;': '\u200c', } replacementCharacters = { - 0x0: "\uFFFD", - 0x0d: "\u000D", - 0x80: "\u20AC", - 0x81: "\u0081", - 0x82: "\u201A", - 0x83: "\u0192", - 0x84: "\u201E", - 0x85: "\u2026", - 0x86: "\u2020", - 0x87: "\u2021", - 0x88: "\u02C6", - 0x89: "\u2030", - 0x8A: "\u0160", - 0x8B: "\u2039", - 0x8C: "\u0152", - 0x8D: "\u008D", - 0x8E: "\u017D", - 0x8F: "\u008F", - 0x90: "\u0090", - 0x91: "\u2018", - 0x92: "\u2019", - 0x93: "\u201C", - 0x94: "\u201D", - 0x95: "\u2022", - 0x96: "\u2013", - 0x97: "\u2014", - 0x98: "\u02DC", - 0x99: "\u2122", - 0x9A: "\u0161", - 0x9B: "\u203A", - 0x9C: "\u0153", - 0x9D: "\u009D", - 0x9E: "\u017E", - 0x9F: "\u0178", + 0x0: '\uFFFD', + 0x0d: '\u000D', + 0x80: '\u20AC', + 0x81: '\u0081', + 0x82: '\u201A', + 0x83: '\u0192', + 0x84: '\u201E', + 0x85: '\u2026', + 0x86: '\u2020', + 0x87: '\u2021', + 0x88: '\u02C6', + 0x89: '\u2030', + 0x8A: '\u0160', + 0x8B: '\u2039', + 0x8C: '\u0152', + 0x8D: '\u008D', + 0x8E: '\u017D', + 0x8F: '\u008F', + 0x90: '\u0090', + 0x91: '\u2018', + 0x92: '\u2019', + 0x93: '\u201C', + 0x94: '\u201D', + 0x95: '\u2022', + 0x96: '\u2013', + 0x97: '\u2014', + 0x98: '\u02DC', + 0x99: '\u2122', + 0x9A: '\u0161', + 0x9B: '\u203A', + 0x9C: '\u0153', + 0x9D: '\u009D', + 0x9E: '\u017E', + 0x9F: '\u0178', } tokenTypes = { - "Doctype": 0, - "Characters": 1, - "SpaceCharacters": 2, - "StartTag": 3, - "EndTag": 4, - "EmptyTag": 5, - "Comment": 6, - "ParseError": 7 + 'Doctype': 0, + 'Characters': 1, + 'SpaceCharacters': 2, + 'StartTag': 3, + 'EndTag': 4, + 'EmptyTag': 5, + 'Comment': 6, + 'ParseError': 7, } -tagTokenTypes = frozenset([tokenTypes["StartTag"], tokenTypes["EndTag"], - tokenTypes["EmptyTag"]]) +tagTokenTypes = frozenset([ + tokenTypes['StartTag'], tokenTypes['EndTag'], + tokenTypes['EmptyTag'], +]) prefixes = {v: k for k, v in namespaces.items()} -prefixes["http://www.w3.org/1998/Math/MathML"] = "math" +prefixes['http://www.w3.org/1998/Math/MathML'] = 'math' class DataLossWarning(UserWarning): diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py index 5ba926e..65933f3 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py @@ -1,9 +1,9 @@ -from __future__ import absolute_import, division, unicode_literals - -from . import base +from __future__ import annotations from collections import OrderedDict +from . import base + def _attr_key(attr): """Return an appropriate key for an attribute for sorting @@ -18,12 +18,15 @@ def _attr_key(attr): class Filter(base.Filter): """Alphabetizes attributes for elements""" + def __iter__(self): for token in base.Filter.__iter__(self): - if token["type"] in ("StartTag", "EmptyTag"): + if token['type'] in ('StartTag', 'EmptyTag'): attrs = OrderedDict() - for name, value in sorted(token["data"].items(), - key=_attr_key): + for name, value in sorted( + token['data'].items(), + key=_attr_key, + ): attrs[name] = value - token["data"] = attrs + token['data'] = attrs yield token diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/base.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/base.py index c7dbaed..f174ae2 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/base.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/base.py @@ -1,7 +1,7 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations -class Filter(object): +class Filter: def __init__(self, source): self.source = source diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py index aefb5c8..ae67a94 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py @@ -1,10 +1,11 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations from . import base class Filter(base.Filter): """Injects ```` tag into head of document""" + def __init__(self, source, encoding): """Creates a Filter @@ -17,57 +18,63 @@ class Filter(base.Filter): self.encoding = encoding def __iter__(self): - state = "pre_head" + state = 'pre_head' meta_found = (self.encoding is None) pending = [] for token in base.Filter.__iter__(self): - type = token["type"] - if type == "StartTag": - if token["name"].lower() == "head": - state = "in_head" + type = token['type'] + if type == 'StartTag': + if token['name'].lower() == 'head': + state = 'in_head' - elif type == "EmptyTag": - if token["name"].lower() == "meta": + elif type == 'EmptyTag': + if token['name'].lower() == 'meta': # replace charset with actual encoding has_http_equiv_content_type = False - for (namespace, name), value in token["data"].items(): + for (namespace, name), value in token['data'].items(): if namespace is not None: continue elif name.lower() == 'charset': - token["data"][(namespace, name)] = self.encoding + token['data'][(namespace, name)] = self.encoding meta_found = True break elif name == 'http-equiv' and value.lower() == 'content-type': has_http_equiv_content_type = True else: - if has_http_equiv_content_type and (None, "content") in token["data"]: - token["data"][(None, "content")] = 'text/html; charset=%s' % self.encoding + if has_http_equiv_content_type and (None, 'content') in token['data']: + token['data'][(None, 'content')] = 'text/html; charset=%s' % self.encoding meta_found = True - elif token["name"].lower() == "head" and not meta_found: + elif token['name'].lower() == 'head' and not meta_found: # insert meta into empty head - yield {"type": "StartTag", "name": "head", - "data": token["data"]} - yield {"type": "EmptyTag", "name": "meta", - "data": {(None, "charset"): self.encoding}} - yield {"type": "EndTag", "name": "head"} + yield { + 'type': 'StartTag', 'name': 'head', + 'data': token['data'], + } + yield { + 'type': 'EmptyTag', 'name': 'meta', + 'data': {(None, 'charset'): self.encoding}, + } + yield {'type': 'EndTag', 'name': 'head'} meta_found = True continue - elif type == "EndTag": - if token["name"].lower() == "head" and pending: + elif type == 'EndTag': + if token['name'].lower() == 'head' and pending: # insert meta into head (if necessary) and flush pending queue yield pending.pop(0) if not meta_found: - yield {"type": "EmptyTag", "name": "meta", - "data": {(None, "charset"): self.encoding}} + yield { + 'type': 'EmptyTag', 'name': 'meta', + 'data': {(None, 'charset'): self.encoding}, + } while pending: yield pending.pop(0) meta_found = True - state = "post_head" + state = 'post_head' - if state == "in_head": + if state == 'in_head': pending.append(token) else: yield token diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/lint.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/lint.py index fcc07ee..ee41a37 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/lint.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/lint.py @@ -1,12 +1,12 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations from pip._vendor.six import text_type from . import base -from ..constants import namespaces, voidElements - +from ..constants import namespaces from ..constants import spaceCharacters -spaceCharacters = "".join(spaceCharacters) +from ..constants import voidElements +spaceCharacters = ''.join(spaceCharacters) class Filter(base.Filter): @@ -15,6 +15,7 @@ class Filter(base.Filter): If it finds any errors, it'll raise an ``AssertionError``. """ + def __init__(self, source, require_matching_tags=True): """Creates a Filter @@ -23,71 +24,71 @@ class Filter(base.Filter): :arg require_matching_tags: whether or not to require matching tags """ - super(Filter, self).__init__(source) + super().__init__(source) self.require_matching_tags = require_matching_tags def __iter__(self): open_elements = [] for token in base.Filter.__iter__(self): - type = token["type"] - if type in ("StartTag", "EmptyTag"): - namespace = token["namespace"] - name = token["name"] + type = token['type'] + if type in ('StartTag', 'EmptyTag'): + namespace = token['namespace'] + name = token['name'] assert namespace is None or isinstance(namespace, text_type) - assert namespace != "" + assert namespace != '' assert isinstance(name, text_type) - assert name != "" - assert isinstance(token["data"], dict) - if (not namespace or namespace == namespaces["html"]) and name in voidElements: - assert type == "EmptyTag" + assert name != '' + assert isinstance(token['data'], dict) + if (not namespace or namespace == namespaces['html']) and name in voidElements: + assert type == 'EmptyTag' else: - assert type == "StartTag" - if type == "StartTag" and self.require_matching_tags: + assert type == 'StartTag' + if type == 'StartTag' and self.require_matching_tags: open_elements.append((namespace, name)) - for (namespace, name), value in token["data"].items(): + for (namespace, name), value in token['data'].items(): assert namespace is None or isinstance(namespace, text_type) - assert namespace != "" + assert namespace != '' assert isinstance(name, text_type) - assert name != "" + assert name != '' assert isinstance(value, text_type) - elif type == "EndTag": - namespace = token["namespace"] - name = token["name"] + elif type == 'EndTag': + namespace = token['namespace'] + name = token['name'] assert namespace is None or isinstance(namespace, text_type) - assert namespace != "" + assert namespace != '' assert isinstance(name, text_type) - assert name != "" - if (not namespace or namespace == namespaces["html"]) and name in voidElements: - assert False, "Void element reported as EndTag token: %(tag)s" % {"tag": name} + assert name != '' + if (not namespace or namespace == namespaces['html']) and name in voidElements: + assert False, 'Void element reported as EndTag token: {tag}'.format(tag=name) elif self.require_matching_tags: start = open_elements.pop() assert start == (namespace, name) - elif type == "Comment": - data = token["data"] + elif type == 'Comment': + data = token['data'] assert isinstance(data, text_type) - elif type in ("Characters", "SpaceCharacters"): - data = token["data"] + elif type in ('Characters', 'SpaceCharacters'): + data = token['data'] assert isinstance(data, text_type) - assert data != "" - if type == "SpaceCharacters": - assert data.strip(spaceCharacters) == "" + assert data != '' + if type == 'SpaceCharacters': + assert data.strip(spaceCharacters) == '' - elif type == "Doctype": - name = token["name"] + elif type == 'Doctype': + name = token['name'] assert name is None or isinstance(name, text_type) - assert token["publicId"] is None or isinstance(name, text_type) - assert token["systemId"] is None or isinstance(name, text_type) + assert token['publicId'] is None or isinstance(name, text_type) + assert token['systemId'] is None or isinstance(name, text_type) - elif type == "Entity": - assert isinstance(token["name"], text_type) + elif type == 'Entity': + assert isinstance(token['name'], text_type) - elif type == "SerializerError": - assert isinstance(token["data"], text_type) + elif type == 'SerializerError': + assert isinstance(token['data'], text_type) else: - assert False, "Unknown token type: %(type)s" % {"type": type} + assert False, 'Unknown token type: {type}'.format(type=type) yield token diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/optionaltags.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/optionaltags.py index 4a86501..bb9b1bf 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/optionaltags.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/optionaltags.py @@ -1,10 +1,11 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations from . import base class Filter(base.Filter): """Removes optional tags from the token stream""" + def slider(self): previous1 = previous2 = None for token in self.source: @@ -17,44 +18,46 @@ class Filter(base.Filter): def __iter__(self): for previous, token, next in self.slider(): - type = token["type"] - if type == "StartTag": - if (token["data"] or - not self.is_optional_start(token["name"], previous, next)): + type = token['type'] + if type == 'StartTag': + if ( + token['data'] or + not self.is_optional_start(token['name'], previous, next) + ): yield token - elif type == "EndTag": - if not self.is_optional_end(token["name"], next): + elif type == 'EndTag': + if not self.is_optional_end(token['name'], next): yield token else: yield token def is_optional_start(self, tagname, previous, next): - type = next and next["type"] or None + type = next and next['type'] or None if tagname in 'html': # An html element's start tag may be omitted if the first thing # inside the html element is not a space character or a comment. - return type not in ("Comment", "SpaceCharacters") + return type not in ('Comment', 'SpaceCharacters') elif tagname == 'head': # A head element's start tag may be omitted if the first thing # inside the head element is an element. # XXX: we also omit the start tag if the head element is empty - if type in ("StartTag", "EmptyTag"): + if type in ('StartTag', 'EmptyTag'): return True - elif type == "EndTag": - return next["name"] == "head" + elif type == 'EndTag': + return next['name'] == 'head' elif tagname == 'body': # A body element's start tag may be omitted if the first thing # inside the body element is not a space character or a comment, # except if the first thing inside the body element is a script # or style element and the node immediately preceding the body # element is a head element whose end tag has been omitted. - if type in ("Comment", "SpaceCharacters"): + if type in ('Comment', 'SpaceCharacters'): return False - elif type == "StartTag": + elif type == 'StartTag': # XXX: we do not look at the preceding event, so we never omit # the body element's start tag if it's followed by a script or # a style element. - return next["name"] not in ('script', 'style') + return next['name'] not in ('script', 'style') else: return True elif tagname == 'colgroup': @@ -62,11 +65,11 @@ class Filter(base.Filter): # inside the colgroup element is a col element, and if the element # is not immediately preceded by another colgroup element whose # end tag has been omitted. - if type in ("StartTag", "EmptyTag"): + if type in ('StartTag', 'EmptyTag'): # XXX: we do not look at the preceding event, so instead we never # omit the colgroup element's end tag when it is immediately # followed by another colgroup element. See is_optional_end. - return next["name"] == "col" + return next['name'] == 'col' else: return False elif tagname == 'tbody': @@ -74,23 +77,23 @@ class Filter(base.Filter): # inside the tbody element is a tr element, and if the element is # not immediately preceded by a tbody, thead, or tfoot element # whose end tag has been omitted. - if type == "StartTag": + if type == 'StartTag': # omit the thead and tfoot elements' end tag when they are # immediately followed by a tbody element. See is_optional_end. if previous and previous['type'] == 'EndTag' and \ previous['name'] in ('tbody', 'thead', 'tfoot'): return False - return next["name"] == 'tr' + return next['name'] == 'tr' else: return False return False def is_optional_end(self, tagname, next): - type = next and next["type"] or None + type = next and next['type'] or None if tagname in ('html', 'head', 'body'): # An html element's end tag may be omitted if the html element # is not immediately followed by a space character or a comment. - return type not in ("Comment", "SpaceCharacters") + return type not in ('Comment', 'SpaceCharacters') elif tagname in ('li', 'optgroup', 'tr'): # A li element's end tag may be omitted if the li element is # immediately followed by another li element or if there is @@ -101,20 +104,20 @@ class Filter(base.Filter): # A tr element's end tag may be omitted if the tr element is # immediately followed by another tr element, or if there is # no more content in the parent element. - if type == "StartTag": - return next["name"] == tagname + if type == 'StartTag': + return next['name'] == tagname else: - return type == "EndTag" or type is None + return type == 'EndTag' or type is None elif tagname in ('dt', 'dd'): # A dt element's end tag may be omitted if the dt element is # immediately followed by another dt element or a dd element. # A dd element's end tag may be omitted if the dd element is # immediately followed by another dd element or a dt element, # or if there is no more content in the parent element. - if type == "StartTag": - return next["name"] in ('dt', 'dd') + if type == 'StartTag': + return next['name'] in ('dt', 'dd') elif tagname == 'dd': - return type == "EndTag" or type is None + return type == 'EndTag' or type is None else: return False elif tagname == 'p': @@ -124,25 +127,27 @@ class Filter(base.Filter): # footer, form, h1, h2, h3, h4, h5, h6, header, hr, menu, # nav, ol, p, pre, section, table, or ul, element, or if # there is no more content in the parent element. - if type in ("StartTag", "EmptyTag"): - return next["name"] in ('address', 'article', 'aside', - 'blockquote', 'datagrid', 'dialog', - 'dir', 'div', 'dl', 'fieldset', 'footer', - 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', - 'header', 'hr', 'menu', 'nav', 'ol', - 'p', 'pre', 'section', 'table', 'ul') + if type in ('StartTag', 'EmptyTag'): + return next['name'] in ( + 'address', 'article', 'aside', + 'blockquote', 'datagrid', 'dialog', + 'dir', 'div', 'dl', 'fieldset', 'footer', + 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'header', 'hr', 'menu', 'nav', 'ol', + 'p', 'pre', 'section', 'table', 'ul', + ) else: - return type == "EndTag" or type is None + return type == 'EndTag' or type is None elif tagname == 'option': # An option element's end tag may be omitted if the option # element is immediately followed by another option element, # or if it is immediately followed by an optgroup # element, or if there is no more content in the parent # element. - if type == "StartTag": - return next["name"] in ('option', 'optgroup') + if type == 'StartTag': + return next['name'] in ('option', 'optgroup') else: - return type == "EndTag" or type is None + return type == 'EndTag' or type is None elif tagname in ('rt', 'rp'): # An rt element's end tag may be omitted if the rt element is # immediately followed by an rt or rp element, or if there is @@ -150,20 +155,20 @@ class Filter(base.Filter): # An rp element's end tag may be omitted if the rp element is # immediately followed by an rt or rp element, or if there is # no more content in the parent element. - if type == "StartTag": - return next["name"] in ('rt', 'rp') + if type == 'StartTag': + return next['name'] in ('rt', 'rp') else: - return type == "EndTag" or type is None + return type == 'EndTag' or type is None elif tagname == 'colgroup': # A colgroup element's end tag may be omitted if the colgroup # element is not immediately followed by a space character or # a comment. - if type in ("Comment", "SpaceCharacters"): + if type in ('Comment', 'SpaceCharacters'): return False - elif type == "StartTag": + elif type == 'StartTag': # XXX: we also look for an immediately following colgroup # element. See is_optional_start. - return next["name"] != 'colgroup' + return next['name'] != 'colgroup' else: return True elif tagname in ('thead', 'tbody'): @@ -177,10 +182,10 @@ class Filter(base.Filter): # more content in the parent element. # XXX: we never omit the end tag when the following element is # a tbody. See is_optional_start. - if type == "StartTag": - return next["name"] in ['tbody', 'tfoot'] + if type == 'StartTag': + return next['name'] in ['tbody', 'tfoot'] elif tagname == 'tbody': - return type == "EndTag" or type is None + return type == 'EndTag' or type is None else: return False elif tagname == 'tfoot': @@ -189,10 +194,10 @@ class Filter(base.Filter): # more content in the parent element. # XXX: we never omit the end tag when the following element is # a tbody. See is_optional_start. - if type == "StartTag": - return next["name"] == 'tbody' + if type == 'StartTag': + return next['name'] == 'tbody' else: - return type == "EndTag" or type is None + return type == 'EndTag' or type is None elif tagname in ('td', 'th'): # A td element's end tag may be omitted if the td element is # immediately followed by a td or th element, or if there is @@ -200,8 +205,8 @@ class Filter(base.Filter): # A th element's end tag may be omitted if the th element is # immediately followed by a td or th element, or if there is # no more content in the parent element. - if type == "StartTag": - return next["name"] in ('td', 'th') + if type == 'StartTag': + return next['name'] in ('td', 'th') else: - return type == "EndTag" or type is None + return type == 'EndTag' or type is None return False diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/sanitizer.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/sanitizer.py index aa7431d..fbb5ed5 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/sanitizer.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/sanitizer.py @@ -6,24 +6,26 @@ is recommended as a replacement. Please let us know in the aforementioned issue if Bleach is unsuitable for your needs. """ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations import re import warnings -from xml.sax.saxutils import escape, unescape +from xml.sax.saxutils import escape +from xml.sax.saxutils import unescape from pip._vendor.six.moves import urllib_parse as urlparse from . import base -from ..constants import namespaces, prefixes +from ..constants import namespaces +from ..constants import prefixes -__all__ = ["Filter"] +__all__ = ['Filter'] _deprecation_msg = ( "html5lib's sanitizer is deprecated; see " + - "https://github.com/html5lib/html5lib-python/issues/443 and please let " + - "us know if Bleach is unsuitable for your needs" + 'https://github.com/html5lib/html5lib-python/issues/443 and please let ' + + 'us know if Bleach is unsuitable for your needs' ) warnings.warn(_deprecation_msg, DeprecationWarning) @@ -566,7 +568,7 @@ svg_allow_local_href = frozenset(( (None, 'textpath'), (None, 'tref'), (None, 'set'), - (None, 'use') + (None, 'use'), )) allowed_css_properties = frozenset(( @@ -707,7 +709,8 @@ allowed_content_types = frozenset(( )) -data_content_type = re.compile(r''' +data_content_type = re.compile( + r''' ^ # Match a content type / (?P[-a-zA-Z0-9.]+/[-a-zA-Z0-9.]+) @@ -718,23 +721,27 @@ data_content_type = re.compile(r''' ,.* $ ''', - re.VERBOSE) + re.VERBOSE, +) class Filter(base.Filter): """Sanitizes token stream of XHTML+MathML+SVG and of inline style attributes""" - def __init__(self, - source, - allowed_elements=allowed_elements, - allowed_attributes=allowed_attributes, - allowed_css_properties=allowed_css_properties, - allowed_css_keywords=allowed_css_keywords, - allowed_svg_properties=allowed_svg_properties, - allowed_protocols=allowed_protocols, - allowed_content_types=allowed_content_types, - attr_val_is_uri=attr_val_is_uri, - svg_attr_val_allows_ref=svg_attr_val_allows_ref, - svg_allow_local_href=svg_allow_local_href): + + def __init__( + self, + source, + allowed_elements=allowed_elements, + allowed_attributes=allowed_attributes, + allowed_css_properties=allowed_css_properties, + allowed_css_keywords=allowed_css_keywords, + allowed_svg_properties=allowed_svg_properties, + allowed_protocols=allowed_protocols, + allowed_content_types=allowed_content_types, + attr_val_is_uri=attr_val_is_uri, + svg_attr_val_allows_ref=svg_attr_val_allows_ref, + svg_allow_local_href=svg_allow_local_href, + ): """Creates a Filter :arg allowed_elements: set of elements to allow--everything else will @@ -766,7 +773,7 @@ class Filter(base.Filter): hrefs--these are removed """ - super(Filter, self).__init__(source) + super().__init__(source) warnings.warn(_deprecation_msg, DeprecationWarning) @@ -801,29 +808,31 @@ class Filter(base.Filter): def sanitize_token(self, token): # accommodate filters which use token_type differently - token_type = token["type"] - if token_type in ("StartTag", "EndTag", "EmptyTag"): - name = token["name"] - namespace = token["namespace"] + token_type = token['type'] + if token_type in ('StartTag', 'EndTag', 'EmptyTag'): + name = token['name'] + namespace = token['namespace'] if ((namespace, name) in self.allowed_elements or - (namespace is None and - (namespaces["html"], name) in self.allowed_elements)): + ( + namespace is None and + (namespaces['html'], name) in self.allowed_elements + )): return self.allowed_token(token) else: return self.disallowed_token(token) - elif token_type == "Comment": + elif token_type == 'Comment': pass else: return token def allowed_token(self, token): - if "data" in token: - attrs = token["data"] + if 'data' in token: + attrs = token['data'] attr_names = set(attrs.keys()) # Remove forbidden attributes for to_remove in (attr_names - self.allowed_attributes): - del token["data"][to_remove] + del token['data'][to_remove] attr_names.remove(to_remove) # Remove attributes with disallowed URL values @@ -833,10 +842,12 @@ class Filter(base.Filter): # characters, nor why we call unescape. I just know it's always been here. # Should you be worried by this comment in a sanitizer? Yes. On the other hand, all # this will do is remove *more* than it otherwise would. - val_unescaped = re.sub("[`\x00-\x20\x7f-\xa0\\s]+", '', - unescape(attrs[attr])).lower() + val_unescaped = re.sub( + '[`\x00-\x20\x7f-\xa0\\s]+', '', + unescape(attrs[attr]), + ).lower() # remove replacement characters from unescaped characters - val_unescaped = val_unescaped.replace("\ufffd", "") + val_unescaped = val_unescaped.replace('\ufffd', '') try: uri = urlparse.urlparse(val_unescaped) except ValueError: @@ -854,36 +865,42 @@ class Filter(base.Filter): for attr in self.svg_attr_val_allows_ref: if attr in attrs: - attrs[attr] = re.sub(r'url\s*\(\s*[^#\s][^)]+?\)', - ' ', - unescape(attrs[attr])) - if (token["name"] in self.svg_allow_local_href and - (namespaces['xlink'], 'href') in attrs and re.search(r'^\s*[^#\s].*', - attrs[(namespaces['xlink'], 'href')])): + attrs[attr] = re.sub( + r'url\s*\(\s*[^#\s][^)]+?\)', + ' ', + unescape(attrs[attr]), + ) + if ( + token['name'] in self.svg_allow_local_href and + (namespaces['xlink'], 'href') in attrs and re.search( + r'^\s*[^#\s].*', + attrs[(namespaces['xlink'], 'href')], + ) + ): del attrs[(namespaces['xlink'], 'href')] if (None, 'style') in attrs: attrs[(None, 'style')] = self.sanitize_css(attrs[(None, 'style')]) - token["data"] = attrs + token['data'] = attrs return token def disallowed_token(self, token): - token_type = token["type"] - if token_type == "EndTag": - token["data"] = "" % token["name"] - elif token["data"]: - assert token_type in ("StartTag", "EmptyTag") + token_type = token['type'] + if token_type == 'EndTag': + token['data'] = '' % token['name'] + elif token['data']: + assert token_type in ('StartTag', 'EmptyTag') attrs = [] - for (ns, name), v in token["data"].items(): - attrs.append(' %s="%s"' % (name if ns is None else "%s:%s" % (prefixes[ns], name), escape(v))) - token["data"] = "<%s%s>" % (token["name"], ''.join(attrs)) + for (ns, name), v in token['data'].items(): + attrs.append(' {}="{}"'.format(name if ns is None else '{}:{}'.format(prefixes[ns], name), escape(v))) + token['data'] = '<{}{}>'.format(token['name'], ''.join(attrs)) else: - token["data"] = "<%s>" % token["name"] - if token.get("selfClosing"): - token["data"] = token["data"][:-1] + "/>" + token['data'] = '<%s>' % token['name'] + if token.get('selfClosing'): + token['data'] = token['data'][:-1] + '/>' - token["type"] = "Characters" + token['type'] = 'Characters' - del token["name"] + del token['name'] return token def sanitize_css(self, style): @@ -893,20 +910,22 @@ class Filter(base.Filter): # gauntlet if not re.match(r"""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): return '' - if not re.match(r"^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$", style): + if not re.match(r'^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$', style): return '' clean = [] - for prop, value in re.findall(r"([-\w]+)\s*:\s*([^:;]*)", style): + for prop, value in re.findall(r'([-\w]+)\s*:\s*([^:;]*)', style): if not value: continue if prop.lower() in self.allowed_css_properties: clean.append(prop + ': ' + value + ';') - elif prop.split('-')[0].lower() in ['background', 'border', 'margin', - 'padding']: + elif prop.split('-')[0].lower() in [ + 'background', 'border', 'margin', + 'padding', + ]: for keyword in value.split(): if keyword not in self.allowed_css_keywords and \ - not re.match(r"^(#[0-9a-fA-F]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$", keyword): # noqa + not re.match(r'^(#[0-9a-fA-F]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$', keyword): # noqa break else: clean.append(prop + ': ' + value + ';') diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/whitespace.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/whitespace.py index 0d12584..7932882 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/whitespace.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/whitespace.py @@ -1,35 +1,36 @@ -from __future__ import absolute_import, division, unicode_literals +from __future__ import annotations import re from . import base -from ..constants import rcdataElements, spaceCharacters -spaceCharacters = "".join(spaceCharacters) +from ..constants import rcdataElements +from ..constants import spaceCharacters +spaceCharacters = ''.join(spaceCharacters) -SPACES_REGEX = re.compile("[%s]+" % spaceCharacters) +SPACES_REGEX = re.compile('[%s]+' % spaceCharacters) class Filter(base.Filter): """Collapses whitespace except in pre, textarea, and script elements""" - spacePreserveElements = frozenset(["pre", "textarea"] + list(rcdataElements)) + spacePreserveElements = frozenset(['pre', 'textarea'] + list(rcdataElements)) def __iter__(self): preserve = 0 for token in base.Filter.__iter__(self): - type = token["type"] - if type == "StartTag" \ - and (preserve or token["name"] in self.spacePreserveElements): + type = token['type'] + if type == 'StartTag' \ + and (preserve or token['name'] in self.spacePreserveElements): preserve += 1 - elif type == "EndTag" and preserve: + elif type == 'EndTag' and preserve: preserve -= 1 - elif not preserve and type == "SpaceCharacters" and token["data"]: + elif not preserve and type == 'SpaceCharacters' and token['data']: # Test on token["data"] above to not introduce spaces where there were not - token["data"] = " " + token['data'] = ' ' - elif not preserve and type == "Characters": - token["data"] = collapse_spaces(token["data"]) + elif not preserve and type == 'Characters': + token['data'] = collapse_spaces(token['data']) yield token diff --git a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/html5parser.py b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/html5parser.py index d06784f..2b191b6 100644 --- a/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/html5parser.py +++ b/.venv/lib/python3.10/site-packages/pip/_vendor/html5lib/html5parser.py @@ -1,29 +1,34 @@ -from __future__ import absolute_import, division, unicode_literals -from pip._vendor.six import with_metaclass, viewkeys +from __future__ import annotations import types +from pip._vendor.six import viewkeys +from pip._vendor.six import with_metaclass + from . import _inputstream from . import _tokenizer - +from . import _utils from . import treebuilders +from .constants import _ReparseException +from .constants import adjustForeignAttributes as adjustForeignAttributesMap +from .constants import adjustMathMLAttributes +from .constants import adjustSVGAttributes +from .constants import asciiUpper2Lower +from .constants import cdataElements +from .constants import E +from .constants import headingElements +from .constants import htmlIntegrationPointElements +from .constants import mathmlTextIntegrationPointElements +from .constants import namespaces +from .constants import rcdataElements +from .constants import spaceCharacters +from .constants import specialElements +from .constants import tagTokenTypes +from .constants import tokenTypes from .treebuilders.base import Marker -from . import _utils -from .constants import ( - spaceCharacters, asciiUpper2Lower, - specialElements, headingElements, cdataElements, rcdataElements, - tokenTypes, tagTokenTypes, - namespaces, - htmlIntegrationPointElements, mathmlTextIntegrationPointElements, - adjustForeignAttributes as adjustForeignAttributesMap, - adjustMathMLAttributes, adjustSVGAttributes, - E, - _ReparseException -) - -def parse(doc, treebuilder="etree", namespaceHTMLElements=True, **kwargs): +def parse(doc, treebuilder='etree', namespaceHTMLElements=True, **kwargs): """Parse an HTML document as a string or file-like object into a tree :arg doc: the document to parse as a string or file-like object @@ -46,7 +51,7 @@ def parse(doc, treebuilder="etree", namespaceHTMLElements=True, **kwargs): return p.parse(doc, **kwargs) -def parseFragment(doc, container="div", treebuilder="etree", namespaceHTMLElements=True, **kwargs): +def parseFragment(doc, container='div', treebuilder='etree', namespaceHTMLElements=True, **kwargs): """Parse an HTML fragment as a string or file-like object into a tree :arg doc: the fragment to parse as a string or file-like object @@ -83,7 +88,7 @@ def method_decorator_metaclass(function): return Decorated -class HTMLParser(object): +class HTMLParser: """HTML parser Generates a tree structure from a stream of (possibly malformed) HTML. @@ -114,14 +119,16 @@ class HTMLParser(object): self.strict = strict if tree is None: - tree = treebuilders.getTreeBuilder("etree") + tree = treebuilders.getTreeBuilder('etree') self.tree = tree(namespaceHTMLElements) self.errors = [] - self.phases = {name: cls(self, self.tree) for name, cls in - getPhases(debug).items()} + self.phases = { + name: cls(self, self.tree) for name, cls in + getPhases(debug).items() + } - def _parse(self, stream, innerHTML=False, container="div", scripting=False, **kwargs): + def _parse(self, stream, innerHTML=False, container='div', scripting=False, **kwargs): self.innerHTMLMode = innerHTML self.container = container @@ -141,7 +148,7 @@ class HTMLParser(object): self.errors = [] self.log = [] # only used with debug mode # "quirks" / "limited quirks" / "no quirks" - self.compatMode = "no quirks" + self.compatMode = 'no quirks' if self.innerHTMLMode: self.innerHTML = self.container.lower() @@ -156,12 +163,12 @@ class HTMLParser(object): # state already is data state # self.tokenizer.state = self.tokenizer.dataState pass - self.phase = self.phases["beforeHtml"] + self.phase = self.phases['beforeHtml'] self.phase.insertHtmlElement() self.resetInsertionMode() else: self.innerHTML = False # pylint:disable=redefined-variable-type - self.phase = self.phases["initial"] + self.phase = self.phases['initial'] self.lastPhase = None @@ -180,12 +187,17 @@ class HTMLParser(object): return self.tokenizer.stream.charEncoding[0].name def isHTMLIntegrationPoint(self, element): - if (element.name == "annotation-xml" and - element.namespace == namespaces["mathml"]): - return ("encoding" in element.attributes and - element.attributes["encoding"].translate( - asciiUpper2Lower) in - ("text/html", "application/xhtml+xml")) + if ( + element.name == 'annotation-xml' and + element.namespace == namespaces['mathml'] + ): + return ( + 'encoding' in element.attributes and + element.attributes['encoding'].translate( + asciiUpper2Lower, + ) in + ('text/html', 'application/xhtml+xml') + ) else: return (element.namespace, element.name) in htmlIntegrationPointElements @@ -193,13 +205,13 @@ class HTMLParser(object): return (element.namespace, element.name) in mathmlTextIntegrationPointElements def mainLoop(self): - CharactersToken = tokenTypes["Characters"] - SpaceCharactersToken = tokenTypes["SpaceCharacters"] - StartTagToken = tokenTypes["StartTag"] - EndTagToken = tokenTypes["EndTag"] - CommentToken = tokenTypes["Comment"] - DoctypeToken = tokenTypes["Doctype"] - ParseErrorToken = tokenTypes["ParseError"] + CharactersToken = tokenTypes['Characters'] + SpaceCharactersToken = tokenTypes['SpaceCharacters'] + StartTagToken = tokenTypes['StartTag'] + EndTagToken = tokenTypes['EndTag'] + CommentToken = tokenTypes['Comment'] + DoctypeToken = tokenTypes['Doctype'] + ParseErrorToken = tokenTypes['ParseError'] for token in self.tokenizer: prev_token = None @@ -210,27 +222,37 @@ class HTMLParser(object): currentNodeNamespace = currentNode.namespace if currentNode else None currentNodeName = currentNode.name if currentNode else None - type = new_token["type"] + type = new_token['type'] if type == ParseErrorToken: - self.parseError(new_token["data"], new_token.get("datavars", {})) + self.parseError(new_token['data'], new_token.get('datavars', {})) new_token = None else: - if (len(self.tree.openElements) == 0 or + if ( + len(self.tree.openElements) == 0 or currentNodeNamespace == self.tree.defaultNamespace or - (self.isMathMLTextIntegrationPoint(currentNode) and - ((type == StartTagToken and - token["name"] not in frozenset(["mglyph", "malignmark"])) or - type in (CharactersToken, SpaceCharactersToken))) or - (currentNodeNamespace == namespaces["mathml"] and - currentNodeName == "annotation-xml" and - type == StartTagToken and - token["name"] == "svg") or - (self.isHTMLIntegrationPoint(currentNode) and - type in (StartTagToken, CharactersToken, SpaceCharactersToken))): + ( + self.isMathMLTextIntegrationPoint(currentNode) and + (( + type == StartTagToken and + token['name'] not in frozenset(['mglyph', 'malignmark']) + ) or + type in (CharactersToken, SpaceCharactersToken)) + ) or + ( + currentNodeNamespace == namespaces['mathml'] and + currentNodeName == 'annotation-xml' and + type == StartTagToken and + token['name'] == 'svg' + ) or + ( + self.isHTMLIntegrationPoint(currentNode) and + type in (StartTagToken, CharactersToken, SpaceCharactersToken) + ) + ): phase = self.phase else: - phase = self.phases["inForeignContent"] + phase = self.phases['inForeignContent'] if type == CharactersToken: new_token = phase.processCharacters(new_token) @@ -245,10 +267,14 @@ class HTMLParser(object): elif type == DoctypeToken: new_token = phase.processDoctype(new_token) - if (type == StartTagToken and prev_token["selfClosing"] and - not prev_token["selfClosingAcknowledged"]): - self.parseError("non-void-element-with-trailing-solidus", - {"name": prev_token["name"]}) + if ( + type == StartTagToken and prev_token['selfClosing'] and + not prev_token['selfClosingAcknowledged'] + ): + self.parseError( + 'non-void-element-with-trailing-solidus', + {'name': prev_token['name']}, + ) # When the loop finishes it's EOF reprocess = True @@ -312,7 +338,7 @@ class HTMLParser(object): self._parse(stream, True, *args, **kwargs) return self.tree.getFragment() - def parseError(self, errorcode="XXX-undefined-error", datavars=None): + def parseError(self, errorcode='XXX-undefined-error', datavars=None): # XXX The idea is to make errorcode mandatory. if datavars is None: datavars = {} @@ -338,20 +364,20 @@ class HTMLParser(object): # specification.) last = False newModes = { - "select": "inSelect", - "td": "inCell", - "th": "inCell", - "tr": "inRow", - "tbody": "inTableBody", - "thead": "inTableBody", - "tfoot": "inTableBody", - "caption": "inCaption", - "colgroup": "inColumnGroup", - "table": "inTable", - "head": "inBody", - "body": "inBody", - "frameset": "inFrameset", - "html": "beforeHead" + 'select': 'inSelect', + 'td': 'inCell', + 'th': 'inCell', + 'tr': 'inRow', + 'tbody': 'inTableBody', + 'thead': 'inTableBody', + 'tfoot': 'inTableBody', + 'caption': 'inCaption', + 'colgroup': 'inColumnGroup', + 'table': 'inTable', + 'head': 'inBody', + 'body': 'inBody', + 'frameset': 'inFrameset', + 'html': 'beforeHead', } for node in self.tree.openElements[::-1]: nodeName = node.name @@ -362,7 +388,7 @@ class HTMLParser(object): nodeName = self.innerHTML # Check for conditions that should only happen in the innerHTML # case - if nodeName in ("select", "colgroup", "head", "html"): + if nodeName in ('select', 'colgroup', 'head', 'html'): assert self.innerHTML if not last and node.namespace != self.tree.defaultNamespace: @@ -372,25 +398,25 @@ class HTMLParser(object): new_phase = self.phases[newModes[nodeName]] break elif last: - new_phase = self.phases["inBody"] + new_phase = self.phases['inBody'] break self.phase = new_phase def parseRCDataRawtext(self, token, contentType): # Generic RCDATA/RAWTEXT Parsing algorithm - assert contentType in ("RAWTEXT", "RCDATA") + assert contentType in ('RAWTEXT', 'RCDATA') self.tree.insertElement(token) - if contentType == "RAWTEXT": + if contentType == 'RAWTEXT': self.tokenizer.state = self.tokenizer.rawtextState else: self.tokenizer.state = self.tokenizer.rcdataState self.originalPhase = self.phase - self.phase = self.phases["text"] + self.phase = self.phases['text'] @_utils.memoize @@ -400,17 +426,19 @@ def getPhases(debug): type_names = {value: key for key, value in tokenTypes.items()} def wrapped(self, *args, **kwargs): - if function.__name__.startswith("process") and len(args) > 0: + if function.__name__.startswith('process') and len(args) > 0: token = args[0] - info = {"type": type_names[token['type']]} + info = {'type': type_names[token['type']]} if token['type'] in tagTokenTypes: - info["name"] = token['name'] + info['name'] = token['name'] - self.parser.log.append((self.parser.tokenizer.state.__name__, - self.parser.phase.__class__.__name__, - self.__class__.__name__, - function.__name__, - info)) + self.parser.log.append(( + self.parser.tokenizer.state.__name__, + self.parser.phase.__class__.__name__, + self.__class__.__name__, + function.__name__, + info, + )) return function(self, *args, **kwargs) else: return function(self, *args, **kwargs) @@ -426,7 +454,7 @@ def getPhases(debug): class Phase(with_metaclass(getMetaclass(debug, log))): """Base class for helper object that implements each phase of processing """ - __slots__ = ("parser", "tree", "__startTagCache", "__endTagCache") + __slots__ = ('parser', 'tree', '__startTagCache', '__endTagCache') def __init__(self, parser, tree): self.parser = parser @@ -443,19 +471,19 @@ def getPhases(debug): self.tree.insertComment(token, self.tree.openElements[-1]) def processDoctype(self, token): - self.parser.parseError("unexpected-doctype") + self.parser.parseError('unexpected-doctype') def processCharacters(self, token): - self.tree.insertText(token["data"]) + self.tree.insertText(token['data']) def processSpaceCharacters(self, token): - self.tree.insertText(token["data"]) + self.tree.insertText(token['data']) def processStartTag(self, token): # Note the caching is done here rather than BoundMethodDispatcher as doing it there # requires a circular reference to the Phase, and this ends up with a significant # (CPython 2.7, 3.8) GC cost when parsing many short inputs - name = token["name"] + name = token['name'] # In Py2, using `in` is quicker in general than try/except KeyError # In Py3, `in` is quicker when there are few cache hits (typically short inputs) if name in self.__startTagCache: @@ -469,11 +497,11 @@ def getPhases(debug): return func(token) def startTagHtml(self, token): - if not self.parser.firstStartTag and token["name"] == "html": - self.parser.parseError("non-html-root") + if not self.parser.firstStartTag and token['name'] == 'html': + self.parser.parseError('non-html-root') # XXX Need a check here to see if the first start tag token emitted is # this token... If it's not, invoke self.parser.parseError(). - for attr, value in token["data"].items(): + for attr, value in token['data'].items(): if attr not in self.tree.openElements[0].attributes: self.tree.openElements[0].attributes[attr] = value self.parser.firstStartTag = False @@ -482,7 +510,7 @@ def getPhases(debug): # Note the caching is done here rather than BoundMethodDispatcher as doing it there # requires a circular reference to the Phase, and this ends up with a significant # (CPython 2.7, 3.8) GC cost when parsing many short inputs - name = token["name"] + name = token['name'] # In Py2, using `in` is quicker in general than try/except KeyError # In Py3, `in` is quicker when there are few cache hits (typically short inputs) if name in self.__endTagCache: @@ -505,123 +533,147 @@ def getPhases(debug): self.tree.insertComment(token, self.tree.document) def processDoctype(self, token): - name = token["name"] - publicId = token["publicId"] - systemId = token["systemId"] - correct = token["correct"] + name = token['name'] + publicId = token['publicId'] + systemId = token['systemId'] + correct = token['correct'] - if (name != "html" or publicId is not None or - systemId is not None and systemId != "about:legacy-compat"): - self.parser.parseError("unknown-doctype") + if ( + name != 'html' or publicId is not None or + systemId is not None and systemId != 'about:legacy-compat' + ): + self.parser.parseError('unknown-doctype') if publicId is None: - publicId = "" + publicId = '' self.tree.insertDoctype(token) - if publicId != "": + if publicId != '': publicId = publicId.translate(asciiUpper2Lower) - if (not correct or token["name"] != "html" or - publicId.startswith( - ("+//silmaril//dtd html pro v0r11 19970101//", - "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", - "-//as//dtd html 3.0 aswedit + extensions//", - "-//ietf//dtd html 2.0 level 1//", - "-//ietf//dtd html 2.0 level 2//", - "-//ietf//dtd html 2.0 strict level 1//", - "-//ietf//dtd html 2.0 strict level 2//", - "-//ietf//dtd html 2.0 strict//", - "-//ietf//dtd html 2.0//", - "-//ietf//dtd html 2.1e//", - "-//ietf//dtd html 3.0//", - "-//ietf//dtd html 3.2 final//", - "-//ietf//dtd html 3.2//", - "-//ietf//dtd html 3//", - "-//ietf//dtd html level 0//", - "-//ietf//dtd html level 1//", - "-//ietf//dtd html level 2//", - "-//ietf//dtd html level 3//", - "-//ietf//dtd html strict level 0//", - "-//ietf//dtd html strict level 1//", - "-//ietf//dtd html strict level 2//", - "-//ietf//dtd html strict level 3//", - "-//ietf//dtd html strict//", - "-//ietf//dtd html//", - "-//metrius//dtd metrius presentational//", - "-//microsoft//dtd internet explorer 2.0 html strict//", - "-//microsoft//dtd internet explorer 2.0 html//", - "-//microsoft//dtd internet explorer 2.0 tables//", - "-//microsoft//dtd internet explorer 3.0 html strict//", - "-//microsoft//dtd internet explorer 3.0 html//", - "-//microsoft//dtd internet explorer 3.0 tables//", - "-//netscape comm. corp.//dtd html//", - "-//netscape comm. corp.//dtd strict html//", - "-//o'reilly and associates//dtd html 2.0//", - "-//o'reilly and associates//dtd html extended 1.0//", - "-//o'reilly and associates//dtd html extended relaxed 1.0//", - "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", - "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", - "-//spyglass//dtd html 2.0 extended//", - "-//sq//dtd html 2.0 hotmetal + extensions//", - "-//sun microsystems corp.//dtd hotjava html//", - "-//sun microsystems corp.//dtd hotjava strict html//", - "-//w3c//dtd html 3 1995-03-24//", - "-//w3c//dtd html 3.2 draft//", - "-//w3c//dtd html 3.2 final//", - "-//w3c//dtd html 3.2//", - "-//w3c//dtd html 3.2s draft//", - "-//w3c//dtd html 4.0 frameset//", - "-//w3c//dtd html 4.0 transitional//", - "-//w3c//dtd html experimental 19960712//", - "-//w3c//dtd html experimental 970421//", - "-//w3c//dtd w3 html//", - "-//w3o//dtd w3 html 3.0//", - "-//webtechs//dtd mozilla html 2.0//", - "-//webtechs//dtd mozilla html//")) or - publicId in ("-//w3o//dtd w3 html strict 3.0//en//", - "-/w3c/dtd html 4.0 transitional/en", - "html") or - publicId.startswith( - ("-//w3c//dtd html 4.01 frameset//", - "-//w3c//dtd html 4.01 transitional//")) and - systemId is None or - systemId and systemId.lower() == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"): - self.parser.compatMode = "quirks" - elif (publicId.startswith( - ("-//w3c//dtd xhtml 1.0 frameset//", - "-//w3c//dtd xhtml 1.0 transitional//")) or - publicId.startswith( - ("-//w3c//dtd html 4.01 frameset//", - "-//w3c//dtd html 4.01 transitional//")) and - systemId is not None): - self.parser.compatMode = "limited quirks" + if ( + not correct or token['name'] != 'html' or + publicId.startswith( + ( + '+//silmaril//dtd html pro v0r11 19970101//', + '-//advasoft ltd//dtd html 3.0 aswedit + extensions//', + '-//as//dtd html 3.0 aswedit + extensions//', + '-//ietf//dtd html 2.0 level 1//', + '-//ietf//dtd html 2.0 level 2//', + '-//ietf//dtd html 2.0 strict level 1//', + '-//ietf//dtd html 2.0 strict level 2//', + '-//ietf//dtd html 2.0 strict//', + '-//ietf//dtd html 2.0//', + '-//ietf//dtd html 2.1e//', + '-//ietf//dtd html 3.0//', + '-//ietf//dtd html 3.2 final//', + '-//ietf//dtd html 3.2//', + '-//ietf//dtd html 3//', + '-//ietf//dtd html level 0//', + '-//ietf//dtd html level 1//', + '-//ietf//dtd html level 2//', + '-//ietf//dtd html level 3//', + '-//ietf//dtd html strict level 0//', + '-//ietf//dtd html strict level 1//', + '-//ietf//dtd html strict level 2//', + '-//ietf//dtd html strict level 3//', + '-//ietf//dtd html strict//', + '-//ietf//dtd html//', + '-//metrius//dtd metrius presentational//', + '-//microsoft//dtd internet explorer 2.0 html strict//', + '-//microsoft//dtd internet explorer 2.0 html//', + '-//microsoft//dtd internet explorer 2.0 tables//', + '-//microsoft//dtd internet explorer 3.0 html strict//', + '-//microsoft//dtd internet explorer 3.0 html//', + '-//microsoft//dtd internet explorer 3.0 tables//', + '-//netscape comm. corp.//dtd html//', + '-//netscape comm. corp.//dtd strict html//', + "-//o'reilly and associates//dtd html 2.0//", + "-//o'reilly and associates//dtd html extended 1.0//", + "-//o'reilly and associates//dtd html extended relaxed 1.0//", + '-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//', + '-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//', + '-//spyglass//dtd html 2.0 extended//', + '-//sq//dtd html 2.0 hotmetal + extensions//', + '-//sun microsystems corp.//dtd hotjava html//', + '-//sun microsystems corp.//dtd hotjava strict html//', + '-//w3c//dtd html 3 1995-03-24//', + '-//w3c//dtd html 3.2 draft//', + '-//w3c//dtd html 3.2 final//', + '-//w3c//dtd html 3.2//', + '-//w3c//dtd html 3.2s draft//', + '-//w3c//dtd html 4.0 frameset//', + '-//w3c//dtd html 4.0 transitional//', + '-//w3c//dtd html experimental 19960712//', + '-//w3c//dtd html experimental 970421//', + '-//w3c//dtd w3 html//', + '-//w3o//dtd w3 html 3.0//', + '-//webtechs//dtd mozilla html 2.0//', + '-//webtechs//dtd mozilla html//', + ), + ) or + publicId in ( + '-//w3o//dtd w3 html strict 3.0//en//', + '-/w3c/dtd html 4.0 transitional/en', + 'html', + ) or + publicId.startswith( + ( + '-//w3c//dtd html 4.01 frameset//', + '-//w3c//dtd html 4.01 transitional//', + ), + ) and + systemId is None or + systemId and systemId.lower() == 'http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd' + ): + self.parser.compatMode = 'quirks' + elif ( + publicId.startswith( + ( + '-//w3c//dtd xhtml 1.0 frameset//', + '-//w3c//dtd xhtml 1.0 transitional//', + ), + ) or + publicId.startswith( + ( + '-//w3c//dtd html 4.01 frameset//', + '-//w3c//dtd html 4.01 transitional//', + ), + ) and + systemId is not None + ): + self.parser.compatMode = 'limited quirks' - self.parser.phase = self.parser.phases["beforeHtml"] + self.parser.phase = self.parser.phases['beforeHtml'] def anythingElse(self): - self.parser.compatMode = "quirks" - self.parser.phase = self.parser.phases["beforeHtml"] + self.parser.compatMode = 'quirks' + self.parser.phase = self.parser.phases['beforeHtml'] def processCharacters(self, token): - self.parser.parseError("expected-doctype-but-got-chars") + self.parser.parseError('expected-doctype-but-got-chars') self.anythingElse() return token def processStartTag(self, token): - self.parser.parseError("expected-doctype-but-got-start-tag", - {"name": token["name"]}) + self.parser.parseError( + 'expected-doctype-but-got-start-tag', + {'name': token['name']}, + ) self.anythingElse() return token def processEndTag(self, token): - self.parser.parseError("expected-doctype-but-got-end-tag", - {"name": token["name"]}) + self.parser.parseError( + 'expected-doctype-but-got-end-tag', + {'name': token['name']}, + ) self.anythingElse() return token def processEOF(self): - self.parser.parseError("expected-doctype-but-got-eof") + self.parser.parseError('expected-doctype-but-got-eof') self.anythingElse() return True @@ -630,8 +682,8 @@ def getPhases(debug): # helper methods def insertHtmlElement(self): - self.tree.insertRoot(impliedTagToken("html", "StartTag")) - self.parser.phase = self.parser.phases["beforeHead"] + self.tree.insertRoot(impliedTagToken('html', 'StartTag')) + self.parser.phase = self.parser.phases['beforeHead'] # other def processEOF(self): @@ -649,15 +701,17 @@ def getPhases(debug): return token def processStartTag(self, token): - if token["name"] == "html": + if token['name'] == 'html': self.parser.firstStartTag = True self.insertHtmlElement() return token def processEndTag(self, token): - if token["name"] not in ("head", "body", "html", "br"): - self.parser.parseError("unexpected-end-tag-before-html", - {"name": token["name"]}) + if token['name'] not in ('head', 'body', 'html', 'br'): + self.parser.parseError( + 'unexpected-end-tag-before-html', + {'name': token['name']}, + ) else: self.insertHtmlElement() return token @@ -666,44 +720,46 @@ def getPhases(debug): __slots__ = tuple() def processEOF(self): - self.startTagHead(impliedTagToken("head", "StartTag")) + self.startTagHead(impliedTagToken('head', 'StartTag')) return True def processSpaceCharacters(self, token): pass def processCharacters(self, token): - self.startTagHead(impliedTagToken("head", "StartTag")) + self.startTagHead(impliedTagToken('head', 'StartTag')) return token def startTagHtml(self, token): - return self.parser.phases["inBody"].processStartTag(token) + return self.parser.phases['inBody'].processStartTag(token) def startTagHead(self, token): self.tree.insertElement(token) self.tree.headPointer = self.tree.openElements[-1] - self.parser.phase = self.parser.phases["inHead"] + self.parser.phase = self.parser.phases['inHead'] def startTagOther(self, token): - self.startTagHead(impliedTagToken("head", "StartTag")) + self.startTagHead(impliedTagToken('head', 'StartTag')) return token def endTagImplyHead(self, token): - self.startTagHead(impliedTagToken("head", "StartTag")) + self.startTagHead(impliedTagToken('head', 'StartTag')) return token def endTagOther(self, token): - self.parser.parseError("end-tag-after-implied-root", - {"name": token["name"]}) + self.parser.parseError( + 'end-tag-after-implied-root', + {'name': token['name']}, + ) startTagHandler = _utils.MethodDispatcher([ - ("html", startTagHtml), - ("head", startTagHead) + ('html', startTagHtml), + ('head', startTagHead), ]) startTagHandler.default = startTagOther endTagHandler = _utils.MethodDispatcher([ - (("head", "body", "html", "br"), endTagImplyHead) + (('head', 'body', 'html', 'br'), endTagImplyHead), ]) endTagHandler.default = endTagOther @@ -720,56 +776,58 @@ def getPhases(debug): return token def startTagHtml(self, token): - return self.parser.phases["inBody"].processStartTag(token) + return self.parser.phases['inBody'].processStartTag(token) def startTagHead(self, token): - self.parser.parseError("two-heads-are-not-better-than-one") + self.parser.parseError('two-heads-are-not-better-than-one') def startTagBaseLinkCommand(self, token): self.tree.insertElement(token) self.tree.openElements.pop() - token["selfClosingAcknowledged"] = True + token['selfClosingAcknowledged'] = True def startTagMeta(self, token): self.tree.insertElement(token) self.tree.openElements.pop() - token["selfClosingAcknowledged"] = True + token['selfClosingAcknowledged'] = True - attributes = token["data"] - if self.parser.tokenizer.stream.charEncoding[1] == "tentative": - if "charset" in attributes: - self.parser.tokenizer.stream.changeEncoding(attributes["charset"]) - elif ("content" in attributes and - "http-equiv" in attributes and - attributes["http-equiv"].lower() == "content-type"): + attributes = token['data'] + if self.parser.tokenizer.stream.charEncoding[1] == 'tentative': + if 'charset' in attributes: + self.parser.tokenizer.stream.changeEncoding(attributes['charset']) + elif ( + 'content' in attributes and + 'http-equiv' in attributes and + attributes['http-equiv'].lower() == 'content-type' + ): # Encoding it as UTF-8 here is a hack, as really we should pass # the abstract Unicode string, and just use the # ContentAttrParser on that, but using UTF-8 allows all chars # to be encoded and as a ASCII-superset works. - data = _inputstream.EncodingBytes(attributes["content"].encode("utf-8")) + data = _inputstream.EncodingBytes(attributes['content'].encode('utf-8')) parser = _inputstream.ContentAttrParser(data) codec = parser.parse() self.parser.tokenizer.stream.changeEncoding(codec) def startTagTitle(self, token): - self.parser.parseRCDataRawtext(token, "RCDATA") + self.parser.parseRCDataRawtext(token, 'RCDATA') def startTagNoFramesStyle(self, token): # Need to decide whether to implement the scripting-disabled case - self.parser.parseRCDataRawtext(token, "RAWTEXT") + self.parser.parseRCDataRawtext(token, 'RAWTEXT') def startTagNoscript(self, token): if self.parser.scripting: - self.parser.parseRCDataRawtext(token, "RAWTEXT") + self.parser.parseRCDataRawtext(token, 'RAWTEXT') else: self.tree.insertElement(token) - self.parser.phase = self.parser.phases["inHeadNoscript"] + self.parser.phase = self.parser.phases['inHeadNoscript'] def startTagScript(self, token): self.tree.insertElement(token) self.parser.tokenizer.state = self.parser.tokenizer.scriptDataState self.parser.originalPhase = self.parser.phase - self.parser.phase = self.parser.phases["text"] + self.parser.phase = self.parser.phases['text'] def startTagOther(self, token): self.anythingElse() @@ -777,35 +835,37 @@ def getPhases(debug): def endTagHead(self, token): node = self.parser.tree.openElements.pop() - assert node.name == "head", "Expected head got %s" % node.name - self.parser.phase = self.parser.phases["afterHead"] + assert node.name == 'head', 'Expected head got %s' % node.name + self.parser.phase = self.parser.phases['afterHead'] def endTagHtmlBodyBr(self, token): self.anythingElse() return token def endTagOther(self, token): - self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + self.parser.parseError('unexpected-end-tag', {'name': token['name']}) def anythingElse(self): - self.endTagHead(impliedTagToken("head")) + self.endTagHead(impliedTagToken('head')) startTagHandler = _utils.MethodDispatcher([ - ("html", startTagHtml), - ("title", startTagTitle), - (("noframes", "style"), startTagNoFramesStyle), - ("noscript", startTagNoscript), - ("script", startTagScript), - (("base", "basefont", "bgsound", "command", "link"), - startTagBaseLinkCommand), - ("meta", startTagMeta), - ("head", startTagHead) + ('html', startTagHtml), + ('title', startTagTitle), + (('noframes', 'style'), startTagNoFramesStyle), + ('noscript', startTagNoscript), + ('script', startTagScript), + ( + ('base', 'basefont', 'bgsound', 'command', 'link'), + startTagBaseLinkCommand, + ), + ('meta', startTagMeta), + ('head', startTagHead), ]) startTagHandler.default = startTagOther endTagHandler = _utils.MethodDispatcher([ - ("head", endTagHead), - (("br", "html", "body"), endTagHtmlBodyBr) + ('head', endTagHead), + (('br', 'html', 'body'), endTagHtmlBodyBr), ]) endTagHandler.default = endTagOther @@ -813,62 +873,62 @@ def getPhases(debug): __slots__ = tuple() def processEOF(self): - self.parser.parseError("eof-in-head-noscript") + self.parser.parseError('eof-in-head-noscript') self.anythingElse() return True def processComment(self, token): - return self.parser.phases["inHead"].processComment(token) + return self.parser.phases['inHead'].processComment(token) def processCharacters(self, token): - self.parser.parseError("char-in-head-noscript") + self.parser.parseError('char-in-head-noscript') self.anythingElse() return token def processSpaceCharacters(self, token): - return self.parser.phases["inHead"].processSpaceCharacters(token) + return self.parser.phases['inHead'].processSpaceCharacters(token) def startTagHtml(self, token): - return self.parser.phases["inBody"].processStartTag(token) + return self.parser.phases['inBody'].processStartTag(token) def startTagBaseLinkCommand(self, token): - return self.parser.phases["inHead"].processStartTag(token) + return self.parser.phases['inHead'].processStartTag(token) def startTagHeadNoscript(self, token): - self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + self.parser.parseError('unexpected-start-tag', {'name': token['name']}) def startTagOther(self, token): - self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.parser.parseError('unexpected-inhead-noscript-tag', {'name': token['name']}) self.anythingElse() return token def endTagNoscript(self, token): node = self.parser.tree.openElements.pop() - assert node.name == "noscript", "Expected noscript got %s" % node.name - self.parser.phase = self.parser.phases["inHead"] + assert node.name == 'noscript', 'Expected noscript got %s' % node.name + self.parser.phase = self.parser.phases['inHead'] def endTagBr(self, token): - self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.parser.parseError('unexpected-inhead-noscript-tag', {'name': token['name']}) self.anythingElse() return token def endTagOther(self, token): - self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + self.parser.parseError('unexpected-end-tag', {'name': token['name']}) def anythingElse(self): # Caller must raise parse error first! - self.endTagNoscript(impliedTagToken("noscript")) + self.endTagNoscript(impliedTagToken('noscript')) startTagHandler = _utils.MethodDispatcher([ - ("html", startTagHtml), - (("basefont", "bgsound", "link", "meta", "noframes", "style"), startTagBaseLinkCommand), - (("head", "noscript"), startTagHeadNoscript), + ('html', startTagHtml), + (('basefont', 'bgsound', 'link', 'meta', 'noframes', 'style'), startTagBaseLinkCommand), + (('head', 'noscript'), startTagHeadNoscript), ]) startTagHandler.default = startTagOther endTagHandler = _utils.MethodDispatcher([ - ("noscript", endTagNoscript), - ("br", endTagBr), + ('noscript', endTagNoscript), + ('br', endTagBr), ]) endTagHandler.default = endTagOther @@ -884,29 +944,31 @@ def getPhases(debug): return token def startTagHtml(self, token): - return self.parser.phases["inBody"].processStartTag(token) + return self.parser.phases['inBody'].processStartTag(token) def startTagBody(self, token): self.parser.framesetOK = False self.tree.insertElement(token) - self.parser.phase = self.parser.phases["inBody"] + self.parser.phase = self.parser.phases['inBody'] def startTagFrameset(self, token): self.tree.insertElement(token) - self.parser.phase = self.parser.phases["inFrameset"] + self.parser.phase = self.parser.phases['inFrameset'] def startTagFromHead(self, token): - self.parser.parseError("unexpected-start-tag-out-of-my-head", - {"name": token["name"]}) + self.parser.parseError( + 'unexpected-start-tag-out-of-my-head', + {'name': token['name']}, + ) self.tree.openElements.append(self.tree.headPointer) - self.parser.phases["inHead"].processStartTag(token) + self.parser.phases['inHead'].processStartTag(token) for node in self.tree.openElements[::-1]: - if node.name == "head": + if node.name == 'head': self.tree.openElements.remove(node) break def startTagHead(self, token): - self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + self.parser.parseError('unexpected-start-tag', {'name': token['name']}) def startTagOther(self, token): self.anythingElse() @@ -917,41 +979,49 @@ def getPhases(debug): return token def endTagOther(self, token): - self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + self.parser.parseError('unexpected-end-tag', {'name': token['name']}) def anythingElse(self): - self.tree.insertElement(impliedTagToken("body", "StartTag")) - self.parser.phase = self.parser.phases["inBody"] + self.tree.insertElement(impliedTagToken('body', 'StartTag')) + self.parser.phase = self.parser.phases['inBody'] self.parser.framesetOK = True startTagHandler = _utils.MethodDispatcher([ - ("html", startTagHtml), - ("body", startTagBody), - ("frameset", startTagFrameset), - (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", - "style", "title"), - startTagFromHead), - ("head", startTagHead) + ('html', startTagHtml), + ('body', startTagBody), + ('frameset', startTagFrameset), + ( + ( + 'base', 'basefont', 'bgsound', 'link', 'meta', 'noframes', 'script', + 'style', 'title', + ), + startTagFromHead, + ), + ('head', startTagHead), ]) startTagHandler.default = startTagOther - endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), - endTagHtmlBodyBr)]) + endTagHandler = _utils.MethodDispatcher([( + ('body', 'html', 'br'), + endTagHtmlBodyBr, + )]) endTagHandler.default = endTagOther class InBodyPhase(Phase): # http://www.whatwg.org/specs/web-apps/current-work/#parsing-main-inbody # the really-really-really-very crazy mode - __slots__ = ("processSpaceCharacters",) + __slots__ = ('processSpaceCharacters',) def __init__(self, *args, **kwargs): - super(InBodyPhase, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Set this to the default handler self.processSpaceCharacters = self.processSpaceCharactersNonPre def isMatchingFormattingElement(self, node1, node2): - return (node1.name == node2.name and - node1.namespace == node2.namespace and - node1.attributes == node2.attributes) + return ( + node1.name == node2.name and + node1.namespace == node2.namespace and + node1.attributes == node2.attributes + ) # helper def addFormattingElement(self, token): @@ -972,135 +1042,153 @@ def getPhases(debug): # the real deal def processEOF(self): - allowed_elements = frozenset(("dd", "dt", "li", "p", "tbody", "td", - "tfoot", "th", "thead", "tr", "body", - "html")) + allowed_elements = frozenset(( + 'dd', 'dt', 'li', 'p', 'tbody', 'td', + 'tfoot', 'th', 'thead', 'tr', 'body', + 'html', + )) for node in self.tree.openElements[::-1]: if node.name not in allowed_elements: - self.parser.parseError("expected-closing-tag-but-got-eof") + self.parser.parseError('expected-closing-tag-but-got-eof') break # Stop parsing def processSpaceCharactersDropNewline(self, token): # Sometimes (start of
, , and