diff --git a/.gitignore b/.gitignore index d5470cfce5d5..ffd0f9faf145 100644 --- a/.gitignore +++ b/.gitignore @@ -14,16 +14,11 @@ ID !id/ .DS_Store* *.pdb +*.egg-info .eslintcache # Filesystem temporaries .fuse_hidden* -# Ignore Python .egg-info directories for first-party modules (but, -# still add vendored packages' .egg-info directories) -*.egg-info -!third_party/python/**/*.egg-info -!testing/web-platform/tests/tools/third_party/**/*.egg-info - # Vim swap files. .*.sw[a-z] .sw[a-z] diff --git a/.hgignore b/.hgignore index 5dff49ad47b3..e5d248a0175b 100644 --- a/.hgignore +++ b/.hgignore @@ -7,16 +7,13 @@ (^|/)ID$ (^|/)\.DS_Store$ \.pdb +\.egg-info \.eslintcache \.gcda \.gcno \.gcov compile_commands\.json -# Ignore Python .egg-info directories for first-party modules (but, -# still add vendored packages' .egg-info directories) -^(?=.*\.egg-info/)(?!^third_party/python/)(?!^testing/web-platform/tests/tools/third_party/) - # Vim swap files. ^\.sw.$ .[^/]*\.sw.$ diff --git a/build/build_virtualenv_packages.txt b/build/build_virtualenv_packages.txt index ad065a4590ff..a1fb43265d37 100644 --- a/build/build_virtualenv_packages.txt +++ b/build/build_virtualenv_packages.txt @@ -1,2 +1,2 @@ packages.txt:build/common_virtualenv_packages.txt -vendored:third_party/python/glean_parser +pth:third_party/python/glean_parser diff --git a/build/common_virtualenv_packages.txt b/build/common_virtualenv_packages.txt index a4244e205f48..0b1f4f9dbd97 100644 --- a/build/common_virtualenv_packages.txt +++ b/build/common_virtualenv_packages.txt @@ -44,82 +44,82 @@ pth:testing/mozbase/mozversion pth:testing/raptor pth:testing/talos pth:testing/web-platform -vendored:testing/web-platform/tests/tools/third_party/h2 -vendored:testing/web-platform/tests/tools/third_party/hpack -vendored:testing/web-platform/tests/tools/third_party/html5lib -vendored:testing/web-platform/tests/tools/third_party/hyperframe -vendored:testing/web-platform/tests/tools/third_party/pywebsocket3 -vendored:testing/web-platform/tests/tools/third_party/webencodings -vendored:testing/web-platform/tests/tools/wptserve -vendored:testing/web-platform/tests/tools/wptrunner +pth:testing/web-platform/tests/tools/third_party/certifi +pth:testing/web-platform/tests/tools/third_party/h2 +pth:testing/web-platform/tests/tools/third_party/hpack +pth:testing/web-platform/tests/tools/third_party/html5lib +pth:testing/web-platform/tests/tools/third_party/hyperframe +pth:testing/web-platform/tests/tools/third_party/pywebsocket3 +pth:testing/web-platform/tests/tools/third_party/webencodings +pth:testing/web-platform/tests/tools/wptserve +pth:testing/web-platform/tests/tools/wptrunner pth:testing/xpcshell -vendored:third_party/python/aiohttp -vendored:third_party/python/appdirs -vendored:third_party/python/async_timeout -vendored:third_party/python/atomicwrites -vendored:third_party/python/attrs -vendored:third_party/python/blessings -vendored:third_party/python/cbor2 -vendored:third_party/python/certifi -vendored:third_party/python/chardet -vendored:third_party/python/Click -vendored:third_party/python/compare_locales -vendored:third_party/python/cookies -vendored:third_party/python/cram -vendored:third_party/python/diskcache -vendored:third_party/python/distro -vendored:third_party/python/dlmanager -vendored:third_party/python/ecdsa -vendored:third_party/python/esprima -vendored:third_party/python/fluent.migrate -vendored:third_party/python/fluent.syntax -vendored:third_party/python/funcsigs -vendored:third_party/python/gyp/pylib -vendored:third_party/python/idna -vendored:third_party/python/idna-ssl -vendored:third_party/python/importlib_metadata -vendored:third_party/python/iso8601 -vendored:third_party/python/Jinja2 -vendored:third_party/python/jsmin -vendored:third_party/python/json-e -vendored:third_party/python/jsonschema -vendored:third_party/python/MarkupSafe/src -vendored:third_party/python/mohawk -vendored:third_party/python/more_itertools -vendored:third_party/python/mozilla_version -vendored:third_party/python/multidict -vendored:third_party/python/packaging -vendored:third_party/python/pathspec -vendored:third_party/python/pip_tools -vendored:third_party/python/pluggy -vendored:third_party/python/ply -vendored:third_party/python/py -vendored:third_party/python/pyasn1 -vendored:third_party/python/pyasn1_modules -vendored:third_party/python/pylru -vendored:third_party/python/pyparsing -vendored:third_party/python/pyrsistent -vendored:third_party/python/pystache -vendored:third_party/python/pytest -vendored:third_party/python/python-hglib -vendored:third_party/python/pytoml -vendored:third_party/python/PyYAML/lib3/ -vendored:third_party/python/redo -vendored:third_party/python/requests -vendored:third_party/python/requests_unixsocket -vendored:third_party/python/responses -vendored:third_party/python/rsa -vendored:third_party/python/sentry_sdk -vendored:third_party/python/six -vendored:third_party/python/slugid -vendored:third_party/python/taskcluster -vendored:third_party/python/taskcluster_urls -vendored:third_party/python/typing_extensions -vendored:third_party/python/urllib3 -vendored:third_party/python/voluptuous -vendored:third_party/python/yamllint -vendored:third_party/python/yarl -vendored:third_party/python/zipp +pth:third_party/python/aiohttp +pth:third_party/python/appdirs +pth:third_party/python/async_timeout +pth:third_party/python/atomicwrites +pth:third_party/python/attrs +pth:third_party/python/blessings +pth:third_party/python/cbor2 +pth:third_party/python/chardet +pth:third_party/python/Click +pth:third_party/python/compare_locales +pth:third_party/python/cookies +pth:third_party/python/cram +pth:third_party/python/diskcache +pth:third_party/python/distro +pth:third_party/python/dlmanager +pth:third_party/python/ecdsa +pth:third_party/python/esprima +pth:third_party/python/fluent.migrate +pth:third_party/python/fluent.syntax +pth:third_party/python/funcsigs +pth:third_party/python/gyp/pylib +pth:third_party/python/idna +pth:third_party/python/idna-ssl +pth:third_party/python/importlib_metadata +pth:third_party/python/iso8601 +pth:third_party/python/Jinja2 +pth:third_party/python/jsmin +pth:third_party/python/json-e +pth:third_party/python/jsonschema +pth:third_party/python/MarkupSafe/src +pth:third_party/python/mohawk +pth:third_party/python/more_itertools +pth:third_party/python/mozilla_version +pth:third_party/python/multidict +pth:third_party/python/packaging +pth:third_party/python/pathspec +pth:third_party/python/pip_tools +pth:third_party/python/pluggy +pth:third_party/python/ply +pth:third_party/python/py +pth:third_party/python/pyasn1 +pth:third_party/python/pyasn1_modules +pth:third_party/python/pylru +pth:third_party/python/pyparsing +pth:third_party/python/pyrsistent +pth:third_party/python/pystache +pth:third_party/python/pytest +pth:third_party/python/python-hglib +pth:third_party/python/pytoml +pth:third_party/python/PyYAML/lib3/ +pth:third_party/python/redo +pth:third_party/python/requests +pth:third_party/python/requests_unixsocket +pth:third_party/python/responses +pth:third_party/python/rsa +pth:third_party/python/sentry_sdk +pth:third_party/python/six +pth:third_party/python/slugid +pth:third_party/python/taskcluster +pth:third_party/python/taskcluster_urls +pth:third_party/python/typing_extensions +pth:third_party/python/urllib3 +pth:third_party/python/voluptuous +pth:third_party/python/yamllint +pth:third_party/python/yarl +pth:third_party/python/zipp pth:toolkit/components/telemetry/tests/marionette/harness pth:tools pth:tools/moztreedocs diff --git a/build/mach_initialize.py b/build/mach_initialize.py index e4616d2847c3..0c79d881348a 100644 --- a/build/mach_initialize.py +++ b/build/mach_initialize.py @@ -9,7 +9,6 @@ import os import platform import shutil import site -import subprocess import sys if sys.version_info[0] < 3: @@ -142,6 +141,35 @@ CATEGORIES = { }, } + +def search_path(mozilla_dir, packages_txt): + with open(os.path.join(mozilla_dir, packages_txt)) as f: + packages = [ + line.strip().split(":", maxsplit=1) + for line in f + if not line.lstrip().startswith("#") + ] + + def handle_package(action, package): + if action == "packages.txt": + for p in search_path(mozilla_dir, package): + yield os.path.join(mozilla_dir, p) + + if action == "pth": + yield os.path.join(mozilla_dir, package) + + for current_action, current_package in packages: + for path in handle_package(current_action, current_package): + yield path + + +def mach_sys_path(mozilla_dir): + return [ + os.path.join(mozilla_dir, path) + for path in search_path(mozilla_dir, "build/mach_virtualenv_packages.txt") + ] + + INSTALL_PYTHON_GUIDANCE_LINUX = """ See https://firefox-source-docs.mozilla.org/setup/linux_build.html#installingpython for guidance on how to install Python on your system. @@ -168,102 +196,6 @@ install a recent enough Python 3. """.strip() -def _scrub_system_site_packages(): - site_paths = set(site.getsitepackages() + [site.getusersitepackages()]) - sys.path = [path for path in sys.path if path not in site_paths] - - -def _activate_python_environment(topsrcdir): - # We need the "mach" module to access the logic to parse virtualenv - # requirements. Since that depends on "packaging" (and, transitively, - # "pyparsing"), we add those to the path too. - sys.path[0:0] = [ - os.path.join(topsrcdir, module) - for module in ( - os.path.join("python", "mach"), - os.path.join("third_party", "python", "packaging"), - os.path.join("third_party", "python", "pyparsing"), - ) - ] - - from mach.requirements import MachEnvRequirements - - thunderbird_dir = os.path.join(topsrcdir, "comm") - is_thunderbird = os.path.exists(thunderbird_dir) and bool( - os.listdir(thunderbird_dir) - ) - - requirements = MachEnvRequirements.from_requirements_definition( - topsrcdir, - is_thunderbird, - True, - os.path.join(topsrcdir, "build", "mach_virtualenv_packages.txt"), - ) - - if os.environ.get("MACH_USE_SYSTEM_PYTHON") or os.environ.get("MOZ_AUTOMATION"): - env_var = ( - "MOZ_AUTOMATION" - if os.environ.get("MOZ_AUTOMATION") - else "MACH_USE_SYSTEM_PYTHON" - ) - - has_pip = ( - subprocess.run( - [sys.executable, "-c", "import pip"], stderr=subprocess.DEVNULL - ).returncode - == 0 - ) - # There are environments in CI that aren't prepared to provide any Mach dependency - # packages. Changing this is a nontrivial endeavour, so guard against having - # non-optional Mach requirements. - assert ( - not requirements.pypi_requirements - ), "Mach pip package requirements must be optional." - if has_pip: - pip = [sys.executable, "-m", "pip"] - check_result = subprocess.run( - pip + ["check"], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True, - ) - if check_result.returncode: - print(check_result.stdout, file=sys.stderr) - subprocess.check_call(pip + ["list", "-v"], stdout=sys.stderr) - raise Exception( - 'According to "pip check", the current Python ' - "environment has package-compatibility issues." - ) - - package_result = requirements.validate_environment_packages(pip) - if not package_result.has_all_packages: - print( - "Skipping automatic management of Python dependencies since " - f"the '{env_var}' environment variable is set.\n" - "The following issues were found while validating your Python " - "environment:" - ) - print(package_result.report()) - sys.exit(1) - else: - # Pip isn't installed to the system Python environment, so we can't use - # it to verify compatibility with Mach. Remove the system site-packages - # from the import scope so that Mach behaves as though all of its - # (optional) dependencies are not installed. - _scrub_system_site_packages() - - elif sys.prefix == sys.base_prefix: - # We're in an environment where we normally use the Mach virtualenv, - # but we're running a "nativecmd" such as "create-mach-environment". - # Remove global site packages from sys.path to improve isolation accordingly. - _scrub_system_site_packages() - - sys.path[0:0] = [ - os.path.join(topsrcdir, pth.path) - for pth in requirements.pth_requirements + requirements.vendored_requirements - ] - - def initialize(topsrcdir): # Ensure we are running Python 3.6+. We run this check as soon as # possible to avoid a cryptic import/usage error. @@ -287,9 +219,15 @@ def initialize(topsrcdir): if os.path.exists(deleted_dir): shutil.rmtree(deleted_dir, ignore_errors=True) - state_dir = _create_state_dir() - _activate_python_environment(topsrcdir) + if sys.prefix == sys.base_prefix: + # We are not in a virtualenv. Remove global site packages + # from sys.path. + site_paths = set(site.getsitepackages() + [site.getusersitepackages()]) + sys.path = [path for path in sys.path if path not in site_paths] + state_dir = _create_state_dir() + + sys.path[0:0] = mach_sys_path(topsrcdir) import mach.base import mach.main from mach.util import setenv diff --git a/build/mach_virtualenv_packages.txt b/build/mach_virtualenv_packages.txt index 1c4f31dd3095..85011f15d60f 100644 --- a/build/mach_virtualenv_packages.txt +++ b/build/mach_virtualenv_packages.txt @@ -3,7 +3,5 @@ packages.txt:build/common_virtualenv_packages.txt # and it has to be built from source. pypi-optional:glean-sdk==40.0.0:telemetry will not be collected # Mach gracefully handles the case where `psutil` is unavailable. -# We aren't (yet) able to pin packages in automation, so we have to -# support down to the oldest locally-installed version (5.4.2). -pypi-optional:psutil>=5.4.2,<=5.8.0:telemetry will be missing some data -pypi-optional:zstandard>=0.11.1,<=0.15.2:zstd archives will not be possible to extract +pypi-optional:psutil==5.8.0:telemetry will be missing some data +pypi:zstandard==0.15.2 diff --git a/build/python-test_virtualenv_packages.txt b/build/python-test_virtualenv_packages.txt index 4d01049a8e84..a1fb43265d37 100644 --- a/build/python-test_virtualenv_packages.txt +++ b/build/python-test_virtualenv_packages.txt @@ -1,3 +1,2 @@ packages.txt:build/common_virtualenv_packages.txt -vendored:third_party/python/glean_parser - +pth:third_party/python/glean_parser diff --git a/configure.py b/configure.py index e08ac9c96e0d..fb3a1f976469 100644 --- a/configure.py +++ b/configure.py @@ -21,11 +21,8 @@ except ImportError: base_dir = os.path.abspath(os.path.dirname(__file__)) -sys.path.insert(0, os.path.join(base_dir, "python", "mach")) sys.path.insert(0, os.path.join(base_dir, "python", "mozboot")) sys.path.insert(0, os.path.join(base_dir, "python", "mozbuild")) -sys.path.insert(0, os.path.join(base_dir, "third_party", "python", "packaging")) -sys.path.insert(0, os.path.join(base_dir, "third_party", "python", "pyparsing")) sys.path.insert(0, os.path.join(base_dir, "third_party", "python", "six")) from mozbuild.configure import ( ConfigureSandbox, diff --git a/python/mach/mach/requirements.py b/python/mach/mach/requirements.py deleted file mode 100644 index 2be2d5f1cd2d..000000000000 --- a/python/mach/mach/requirements.py +++ /dev/null @@ -1,242 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -import json -import os -from pathlib import Path -import subprocess - -from packaging.requirements import Requirement - - -THUNDERBIRD_PYPI_ERROR = """ -Thunderbird requirements definitions cannot include PyPI packages. -""".strip() - - -class EnvironmentPackageValidationResult: - def __init__(self): - self._package_discrepancies = [] - self.has_all_packages = True - - def add_discrepancy(self, requirement, found): - self._package_discrepancies.append((requirement, found)) - self.has_all_packages = False - - def report(self): - lines = [] - for requirement, found in self._package_discrepancies: - if found: - error = f'Installed with unexpected version "{found}"' - else: - error = "Not installed" - lines.append(f"{requirement}: {error}") - return "\n".join(lines) - - -class PthSpecifier: - def __init__(self, path): - self.path = path - - -class PypiSpecifier: - def __init__(self, requirement): - self.requirement = requirement - - -class PypiOptionalSpecifier(PypiSpecifier): - def __init__(self, repercussion, requirement): - super().__init__(requirement) - self.repercussion = repercussion - - -class MachEnvRequirements: - """Requirements associated with a "virtualenv_packages.txt" definition - - Represents the dependencies of a virtualenv. The source files consist - of colon-delimited fields. The first field - specifies the action. The remaining fields are arguments to that - action. The following actions are supported: - - pth -- Adds the path given as argument to "mach.pth" under - the virtualenv site packages directory. - - pypi -- Fetch the package, plus dependencies, from PyPI. - - pypi-optional -- Attempt to install the package and dependencies from PyPI. - Continue using the virtualenv, even if the package could not be installed. - - packages.txt -- Denotes that the specified path is a child manifest. It - will be read and processed as if its contents were concatenated - into the manifest being read. - - thunderbird-packages.txt -- Denotes a Thunderbird child manifest. - Thunderbird child manifests are only activated when working on Thunderbird, - and they can cannot have "pypi" or "pypi-optional" entries. - """ - - def __init__(self): - self.requirements_paths = [] - self.pth_requirements = [] - self.pypi_requirements = [] - self.pypi_optional_requirements = [] - self.vendored_requirements = [] - - def validate_environment_packages(self, pip_command): - result = EnvironmentPackageValidationResult() - if not self.pypi_requirements and not self.pypi_optional_requirements: - return result - - pip_json = subprocess.check_output( - pip_command + ["list", "--format", "json"], universal_newlines=True - ) - - installed_packages = json.loads(pip_json) - installed_packages = { - package["name"]: package["version"] for package in installed_packages - } - for pkg in self.pypi_requirements: - installed_version = installed_packages.get(pkg.requirement.name) - if not installed_version or not pkg.requirement.specifier.contains( - installed_version - ): - result.add_discrepancy(pkg.requirement, installed_version) - - for pkg in self.pypi_optional_requirements: - installed_version = installed_packages.get(pkg.requirement.name) - if installed_version and not pkg.requirement.specifier.contains( - installed_version - ): - result.add_discrepancy(pkg.requirement, installed_version) - - return result - - @classmethod - def from_requirements_definition( - cls, - topsrcdir, - is_thunderbird, - is_mach_or_build_virtualenv, - requirements_definition, - ): - requirements = cls() - _parse_mach_env_requirements( - requirements, - requirements_definition, - topsrcdir, - is_thunderbird, - is_mach_or_build_virtualenv, - ) - return requirements - - -def _parse_mach_env_requirements( - requirements_output, - root_requirements_path, - topsrcdir, - is_thunderbird, - is_mach_or_build_virtualenv, -): - topsrcdir = Path(topsrcdir) - - def _parse_requirements_line( - current_requirements_path, line, line_number, is_thunderbird_packages_txt - ): - line = line.strip() - if not line or line.startswith("#"): - return - - action, params = line.rstrip().split(":", maxsplit=1) - if action == "pth": - path = topsrcdir / params - if not path.exists(): - # In sparse checkouts, not all paths will be populated. - return - - for child in path.iterdir(): - if child.name.endswith(".dist-info"): - raise Exception( - f'The "pth:" pointing to "{path}" has a ".dist-info" file.\n' - f'Perhaps "{current_requirements_path}:{line_number}" ' - 'should change to start with "vendored:" instead of "pth:".' - ) - if child.name == "PKG-INFO": - raise Exception( - f'The "pth:" pointing to "{path}" has a "PKG-INFO" file.\n' - f'Perhaps "{current_requirements_path}:{line_number}" ' - 'should change to start with "vendored:" instead of "pth:".' - ) - - requirements_output.pth_requirements.append(PthSpecifier(params)) - elif action == "vendored": - requirements_output.vendored_requirements.append(PthSpecifier(params)) - elif action == "packages.txt": - _parse_requirements_definition_file( - os.path.join(topsrcdir, params), - is_thunderbird_packages_txt, - ) - elif action == "pypi": - if is_thunderbird_packages_txt: - raise Exception(THUNDERBIRD_PYPI_ERROR) - - requirements_output.pypi_requirements.append( - PypiSpecifier( - _parse_package_specifier(params, is_mach_or_build_virtualenv) - ) - ) - elif action == "pypi-optional": - if is_thunderbird_packages_txt: - raise Exception(THUNDERBIRD_PYPI_ERROR) - - if len(params.split(":", maxsplit=1)) != 2: - raise Exception( - "Expected pypi-optional package to have a repercussion " - 'description in the format "package:fallback explanation", ' - 'found "{}"'.format(params) - ) - raw_requirement, repercussion = params.split(":") - requirements_output.pypi_optional_requirements.append( - PypiOptionalSpecifier( - repercussion, - _parse_package_specifier( - raw_requirement, is_mach_or_build_virtualenv - ), - ) - ) - elif action == "thunderbird-packages.txt": - if is_thunderbird: - _parse_requirements_definition_file( - os.path.join(topsrcdir, params), is_thunderbird_packages_txt=True - ) - else: - raise Exception("Unknown requirements definition action: %s" % action) - - def _parse_requirements_definition_file( - requirements_path, is_thunderbird_packages_txt - ): - """Parse requirements file into list of requirements""" - assert os.path.isfile(requirements_path) - requirements_output.requirements_paths.append(requirements_path) - - with open(requirements_path, "r") as requirements_file: - lines = [line for line in requirements_file] - - for number, line in enumerate(lines, start=1): - _parse_requirements_line( - requirements_path, line, number, is_thunderbird_packages_txt - ) - - _parse_requirements_definition_file(root_requirements_path, False) - - -def _parse_package_specifier(raw_requirement, is_mach_or_build_virtualenv): - requirement = Requirement(raw_requirement) - - if not is_mach_or_build_virtualenv and [ - s for s in requirement.specifier if s.operator != "==" - ]: - raise Exception( - 'All virtualenvs except for "mach" and "build" must pin pypi package ' - f'versions in the format "package==version", found "{raw_requirement}"' - ) - return requirement diff --git a/python/mach/mach/test/python.ini b/python/mach/mach/test/python.ini index b66dc1033dc9..891d292036c8 100644 --- a/python/mach/mach/test/python.ini +++ b/python/mach/mach/test/python.ini @@ -12,9 +12,3 @@ skip-if = python == 3 skip-if = python == 3 [test_logger.py] [test_mach.py] -[test_virtualenv_compatibility.py] -# The Windows and Mac workers only use the internal PyPI mirror, -# which will be missing packages required for this test. -skip-if = - os == "win" - os == "mac" diff --git a/python/mach/mach/test/test_virtualenv_compatibility.py b/python/mach/mach/test/test_virtualenv_compatibility.py deleted file mode 100644 index 6e4bd2ae9a8c..000000000000 --- a/python/mach/mach/test/test_virtualenv_compatibility.py +++ /dev/null @@ -1,136 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import os -import shutil -import subprocess -import sys -from pathlib import Path - -import mozunit -from buildconfig import topsrcdir -from mach.requirements import MachEnvRequirements - - -def _resolve_command_virtualenv_names(): - virtualenv_names = [] - for child in (Path(topsrcdir) / "build").iterdir(): - if not child.name.endswith("_virtualenv_packages.txt"): - continue - - if child.name == "mach_virtualenv_packages.txt": - continue - - virtualenv_names.append(child.name[: -len("_virtualenv_packages.txt")]) - return virtualenv_names - - -def _requirement_definition_to_pip_format(virtualenv_name, cache, is_mach_or_build_env): - """Convert from parsed requirements object to pip-consumable format""" - path = Path(topsrcdir) / "build" / f"{virtualenv_name}_virtualenv_packages.txt" - requirements = MachEnvRequirements.from_requirements_definition( - topsrcdir, False, is_mach_or_build_env, path - ) - - lines = [] - for pypi in ( - requirements.pypi_requirements + requirements.pypi_optional_requirements - ): - lines.append(str(pypi.requirement)) - - for vendored in requirements.vendored_requirements: - lines.append(cache.package_for_vendor_dir(Path(vendored.path))) - - return "\n".join(lines) - - -class PackageCache: - def __init__(self, storage_dir: Path): - self._cache = {} - self._storage_dir = storage_dir - - def package_for_vendor_dir(self, vendor_path: Path): - if vendor_path in self._cache: - return self._cache[vendor_path] - - if not any((p for p in vendor_path.iterdir() if p.name.endswith(".dist-info"))): - # This vendored package is not a wheel. It may be a source package (with - # a setup.py), or just some Python code that was manually copied into the - # tree. If it's a source package, the setup.py file may be up a few levels - # from the referenced Python module path. - package_dir = vendor_path - while True: - if (package_dir / "setup.py").exists(): - break - elif package_dir.parent == package_dir: - raise Exception( - f'Package "{vendor_path}" is not a wheel and does not have a ' - 'setup.py file. Perhaps it should be "pth:" instead of ' - '"vendored:"?' - ) - package_dir = package_dir.parent - - self._cache[vendor_path] = str(package_dir) - return str(package_dir) - - # Pip requires that wheels have a version number in their name, even if - # it ignores it. We should parse out the version and put it in here - # so that failure debugging is easier, but that's non-trivial work. - # So, this "0" satisfies pip's naming requirement while being relatively - # obvious that it's a placeholder. - output_path = str(self._storage_dir / f"{vendor_path.name}-0-py3-none-any") - shutil.make_archive(output_path, "zip", vendor_path) - whl_path = output_path + ".whl" - os.rename(output_path + ".zip", whl_path) - self._cache[vendor_path] = whl_path - return whl_path - - -def test_virtualenvs_compatible(tmpdir): - command_virtualenv_names = _resolve_command_virtualenv_names() - work_dir = Path(tmpdir) - cache = PackageCache(work_dir) - mach_requirements = _requirement_definition_to_pip_format("mach", cache, True) - - # Create virtualenv to try to install all dependencies into. - subprocess.check_call( - [ - sys.executable, - os.path.join( - topsrcdir, - "third_party", - "python", - "virtualenv", - "virtualenv.py", - ), - "--no-download", - str(work_dir / "env"), - ] - ) - - for name in command_virtualenv_names: - print(f'Checking compatibility of "{name}" virtualenv') - command_requirements = _requirement_definition_to_pip_format( - name, cache, name == "build" - ) - with open(work_dir / "requirements.txt", "w") as requirements_txt: - requirements_txt.write(mach_requirements) - requirements_txt.write("\n") - requirements_txt.write(command_requirements) - - # Attempt to install combined set of dependencies (global Mach + current - # command) - subprocess.check_call( - [ - str(work_dir / "env" / "bin" / "pip"), - "install", - "-r", - str(work_dir / "requirements.txt"), - ], - cwd=topsrcdir, - ) - - -if __name__ == "__main__": - mozunit.main() diff --git a/python/mach_commands.py b/python/mach_commands.py index f3293a2cc44a..cd39c2f3e159 100644 --- a/python/mach_commands.py +++ b/python/mach_commands.py @@ -22,7 +22,6 @@ from manifestparser import filters as mpf from mach.decorators import CommandArgument, Command -from mach.requirements import MachEnvRequirements from mach.util import UserError here = os.path.abspath(os.path.dirname(__file__)) @@ -69,20 +68,11 @@ def python( raise UserError("Cannot pass both --requirements and --no-virtualenv.") if no_virtualenv: - python_path = sys.executable - requirements = MachEnvRequirements.from_requirements_definition( - command_context.topsrcdir, - False, - True, - os.path.join( - command_context.topsrcdir, "build", "mach_virtualenv_packages.txt" - ), - ) + from mach_initialize import mach_sys_path + python_path = sys.executable append_env["PYTHONPATH"] = os.pathsep.join( - os.path.join(command_context.topsrcdir, pth.path) - for pth in requirements.pth_requirements - + requirements.vendored_requirements + mach_sys_path(command_context.topsrcdir) ) else: command_context.virtualenv_manager.ensure() diff --git a/python/mozbuild/mozbuild/requirements.py b/python/mozbuild/mozbuild/requirements.py new file mode 100644 index 000000000000..34dc90ddfe25 --- /dev/null +++ b/python/mozbuild/mozbuild/requirements.py @@ -0,0 +1,144 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os + + +THUNDERBIRD_PYPI_ERROR = """ +Thunderbird requirements definitions cannot include PyPI packages. +""".strip() + + +class PthSpecifier: + def __init__(self, path): + self.path = path + + +class PypiSpecifier: + def __init__(self, package_name, version, full_specifier): + self.package_name = package_name + self.version = version + self.full_specifier = full_specifier + + +class PypiOptionalSpecifier: + def __init__(self, repercussion, package_name, version, full_specifier): + self.repercussion = repercussion + self.package_name = package_name + self.version = version + self.full_specifier = full_specifier + + +class MachEnvRequirements: + """Requirements associated with a "virtualenv_packages.txt" definition + + Represents the dependencies of a virtualenv. The source files consist + of colon-delimited fields. The first field + specifies the action. The remaining fields are arguments to that + action. The following actions are supported: + + pth -- Adds the path given as argument to "mach.pth" under + the virtualenv site packages directory. + + pypi -- Fetch the package, plus dependencies, from PyPI. + + pypi-optional -- Attempt to install the package and dependencies from PyPI. + Continue using the virtualenv, even if the package could not be installed. + + packages.txt -- Denotes that the specified path is a child manifest. It + will be read and processed as if its contents were concatenated + into the manifest being read. + + thunderbird-packages.txt -- Denotes a Thunderbird child manifest. + Thunderbird child manifests are only activated when working on Thunderbird, + and they can cannot have "pypi" or "pypi-optional" entries. + """ + + def __init__(self): + self.requirements_paths = [] + self.pth_requirements = [] + self.pypi_requirements = [] + self.pypi_optional_requirements = [] + + @classmethod + def from_requirements_definition( + cls, topsrcdir, is_thunderbird, requirements_definition + ): + requirements = cls() + _parse_mach_env_requirements( + requirements, requirements_definition, topsrcdir, is_thunderbird + ) + return requirements + + +def _parse_mach_env_requirements( + requirements_output, root_requirements_path, topsrcdir, is_thunderbird +): + def _parse_requirements_line(line, is_thunderbird_packages_txt): + line = line.strip() + if not line or line.startswith("#"): + return + + action, params = line.rstrip().split(":", maxsplit=1) + if action == "pth": + requirements_output.pth_requirements.append(PthSpecifier(params)) + elif action == "packages.txt": + _parse_requirements_definition_file( + os.path.join(topsrcdir, params), + is_thunderbird_packages_txt, + ) + elif action == "pypi": + if is_thunderbird_packages_txt: + raise Exception(THUNDERBIRD_PYPI_ERROR) + + package_name, version = _parse_package_specifier(params) + requirements_output.pypi_requirements.append( + PypiSpecifier(package_name, version, params) + ) + elif action == "pypi-optional": + if is_thunderbird_packages_txt: + raise Exception(THUNDERBIRD_PYPI_ERROR) + + if len(params.split(":", maxsplit=1)) != 2: + raise Exception( + "Expected pypi-optional package to have a repercussion " + 'description in the format "package:fallback explanation", ' + 'found "{}"'.format(params) + ) + package, repercussion = params.split(":") + package_name, version = _parse_package_specifier(package) + requirements_output.pypi_optional_requirements.append( + PypiOptionalSpecifier(repercussion, package_name, version, package) + ) + elif action == "thunderbird-packages.txt": + if is_thunderbird: + _parse_requirements_definition_file( + os.path.join(topsrcdir, params), is_thunderbird_packages_txt=True + ) + else: + raise Exception("Unknown requirements definition action: %s" % action) + + def _parse_requirements_definition_file( + requirements_path, is_thunderbird_packages_txt + ): + """Parse requirements file into list of requirements""" + assert os.path.isfile(requirements_path) + requirements_output.requirements_paths.append(requirements_path) + + with open(requirements_path, "r") as requirements_file: + lines = [line for line in requirements_file] + + for line in lines: + _parse_requirements_line(line, is_thunderbird_packages_txt) + + _parse_requirements_definition_file(root_requirements_path, False) + + +def _parse_package_specifier(specifier): + if len(specifier.split("==")) != 2: + raise Exception( + "Expected pypi package version to be pinned in the " + 'format "package==version", found "{}"'.format(specifier) + ) + return specifier.split("==") diff --git a/python/mozbuild/mozbuild/test/test_vendor.py b/python/mozbuild/mozbuild/test/test_vendor.py index e1241577da82..860da7d21e6f 100644 --- a/python/mozbuild/mozbuild/test/test_vendor.py +++ b/python/mozbuild/mozbuild/test/test_vendor.py @@ -29,21 +29,14 @@ def test_up_to_date_vendor(): # it will use its associated virtualenv and package configuration. # Since it uses "pip-tools" within, and "pip-tools" needs # the "Click" library, we need to make them available. - file.write("vendored:third_party/python/Click\n") - file.write("vendored:third_party/python/pip_tools\n") + file.write("pth:third_party/python/Click\n") + file.write("pth:third_party/python/pip_tools\n") # Copy existing "third_party/python/" vendored files existing_vendored = os.path.join(topsrcdir, "third_party", "python") work_vendored = os.path.join(work_dir, "third_party", "python") shutil.copytree(existing_vendored, work_vendored) - # Copy "mach" module so that `VirtualenvManager` can populate itself. - # This is needed because "topsrcdir" is used in this test both for determining - # import paths and for acting as a "work dir". - existing_mach = os.path.join(topsrcdir, "python", "mach") - work_mach = os.path.join(work_dir, "python", "mach") - shutil.copytree(existing_mach, work_mach) - # Run the vendoring process vendor = VendorPython( work_dir, None, Mock(), topobjdir=os.path.join(work_dir, "obj") @@ -60,6 +53,7 @@ def test_up_to_date_vendor(): existing_vendored, work_vendored, "--exclude=__pycache__", + "--exclude=*.egg-info", ] ) diff --git a/python/mozbuild/mozbuild/virtualenv.py b/python/mozbuild/mozbuild/virtualenv.py index 96c2ccbcdb4a..293ebdc48383 100644 --- a/python/mozbuild/mozbuild/virtualenv.py +++ b/python/mozbuild/mozbuild/virtualenv.py @@ -193,9 +193,7 @@ class VirtualenvManager(VirtualenvHelper): if existing_metadata != self._metadata: return False - if ( - env_requirements.pth_requirements or env_requirements.vendored_requirements - ) and self.populate_local_paths: + if env_requirements.pth_requirements and self.populate_local_paths: try: with open( os.path.join(self._site_packages_dir(), PTH_FILENAME) @@ -216,16 +214,35 @@ class VirtualenvManager(VirtualenvHelper): os.path.abspath(os.path.join(self.topsrcdir, pth.path)) ) for pth in env_requirements.pth_requirements - + env_requirements.vendored_requirements ] if current_paths != required_paths: return False - pip = os.path.join(self.bin_path, "pip") - package_result = env_requirements.validate_environment_packages([pip]) - if not package_result.has_all_packages: - return False + if ( + env_requirements.pypi_requirements + or env_requirements.pypi_optional_requirements + ): + pip_json = self._run_pip( + ["list", "--format", "json"], stdout=subprocess.PIPE + ).stdout + installed_packages = json.loads(pip_json) + installed_packages = { + package["name"]: package["version"] for package in installed_packages + } + for requirement in env_requirements.pypi_requirements: + if ( + installed_packages.get(requirement.package_name, None) + != requirement.version + ): + return False + + for requirement in env_requirements.pypi_optional_requirements: + installed_version = installed_packages.get( + requirement.package_name, None + ) + if installed_version and installed_version != requirement.version: + return False return True @@ -292,7 +309,15 @@ class VirtualenvManager(VirtualenvHelper): return self.virtualenv_root def _requirements(self): - from mach.requirements import MachEnvRequirements + try: + # When `virtualenv.py` is invoked from an existing Mach process, + # import MachEnvRequirements in the expected way. + from mozbuild.requirements import MachEnvRequirements + except ImportError: + # When `virtualenv.py` is invoked standalone, import + # MachEnvRequirements from the adjacent "standalone" + # requirements module. + from requirements import MachEnvRequirements if not os.path.exists(self._manifest_path): raise Exception( @@ -306,10 +331,7 @@ class VirtualenvManager(VirtualenvHelper): os.listdir(thunderbird_dir) ) return MachEnvRequirements.from_requirements_definition( - self.topsrcdir, - is_thunderbird, - self._virtualenv_name in ("mach", "build"), - self._manifest_path, + self.topsrcdir, is_thunderbird, self._manifest_path ) def populate(self): @@ -341,10 +363,7 @@ class VirtualenvManager(VirtualenvHelper): if self.populate_local_paths: python_lib = distutils.sysconfig.get_python_lib() with open(os.path.join(python_lib, PTH_FILENAME), "a") as f: - for pth_requirement in ( - env_requirements.pth_requirements - + env_requirements.vendored_requirements - ): + for pth_requirement in env_requirements.pth_requirements: path = os.path.join(self.topsrcdir, pth_requirement.path) # This path is relative to the .pth file. Using a # relative path allows the srcdir/objdir combination @@ -353,14 +372,14 @@ class VirtualenvManager(VirtualenvHelper): f.write("{}\n".format(os.path.relpath(path, python_lib))) for pypi_requirement in env_requirements.pypi_requirements: - self.install_pip_package(str(pypi_requirement.requirement)) + self.install_pip_package(pypi_requirement.full_specifier) for requirement in env_requirements.pypi_optional_requirements: try: - self.install_pip_package(str(requirement.requirement)) + self.install_pip_package(requirement.full_specifier) except subprocess.CalledProcessError: print( - f"Could not install {requirement.requirement.name}, so " + f"Could not install {requirement.package_name}, so " f"{requirement.repercussion}. Continuing." ) @@ -584,12 +603,6 @@ if __name__ == "__main__": populate = False opts = parser.parse_args(sys.argv[1:]) - # We want to be able to import the "mach.requirements" module. - sys.path.append(os.path.join(opts.topsrcdir, "python", "mach")) - # Virtualenv logic needs access to the vendored "packaging" library. - sys.path.append(os.path.join(opts.topsrcdir, "third_party", "python", "pyparsing")) - sys.path.append(os.path.join(opts.topsrcdir, "third_party", "python", "packaging")) - manager = VirtualenvManager( opts.topsrcdir, opts.virtualenvs_dir, diff --git a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/PKG-INFO b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/PKG-INFO deleted file mode 100644 index 9defab7de904..000000000000 --- a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/PKG-INFO +++ /dev/null @@ -1,101 +0,0 @@ -Metadata-Version: 1.2 -Name: MarkupSafe -Version: 1.1.1 -Summary: Safely add untrusted strings to HTML/XML markup. -Home-page: https://palletsprojects.com/p/markupsafe/ -Author: Armin Ronacher -Author-email: armin.ronacher@active-4.com -Maintainer: The Pallets Team -Maintainer-email: contact@palletsprojects.com -License: BSD-3-Clause -Project-URL: Documentation, https://markupsafe.palletsprojects.com/ -Project-URL: Code, https://github.com/pallets/markupsafe -Project-URL: Issue tracker, https://github.com/pallets/markupsafe/issues -Description: MarkupSafe - ========== - - MarkupSafe implements a text object that escapes characters so it is - safe to use in HTML and XML. Characters that have special meanings are - replaced so that they display as the actual characters. This mitigates - injection attacks, meaning untrusted user input can safely be displayed - on a page. - - - Installing - ---------- - - Install and update using `pip`_: - - .. code-block:: text - - pip install -U MarkupSafe - - .. _pip: https://pip.pypa.io/en/stable/quickstart/ - - - Examples - -------- - - .. code-block:: pycon - - >>> from markupsafe import Markup, escape - >>> # escape replaces special characters and wraps in Markup - >>> escape('') - Markup(u'<script>alert(document.cookie);</script>') - >>> # wrap in Markup to mark text "safe" and prevent escaping - >>> Markup('Hello') - Markup('hello') - >>> escape(Markup('Hello')) - Markup('hello') - >>> # Markup is a text subclass (str on Python 3, unicode on Python 2) - >>> # methods and operators escape their arguments - >>> template = Markup("Hello %s") - >>> template % '"World"' - Markup('Hello "World"') - - - Donate - ------ - - The Pallets organization develops and supports MarkupSafe and other - libraries that use it. In order to grow the community of contributors - and users, and allow the maintainers to devote more time to the - projects, `please donate today`_. - - .. _please donate today: https://palletsprojects.com/donate - - - Links - ----- - - * Website: https://palletsprojects.com/p/markupsafe/ - * Documentation: https://markupsafe.palletsprojects.com/ - * License: `BSD-3-Clause `_ - * Releases: https://pypi.org/project/MarkupSafe/ - * Code: https://github.com/pallets/markupsafe - * Issue tracker: https://github.com/pallets/markupsafe/issues - * Test status: - - * Linux, Mac: https://travis-ci.org/pallets/markupsafe - * Windows: https://ci.appveyor.com/project/pallets/markupsafe - - * Test coverage: https://codecov.io/gh/pallets/markupsafe - -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Web Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Text Processing :: Markup :: HTML -Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* diff --git a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/SOURCES.txt b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/SOURCES.txt deleted file mode 100644 index 70108af00ee6..000000000000 --- a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/SOURCES.txt +++ /dev/null @@ -1,31 +0,0 @@ -CHANGES.rst -LICENSE.rst -MANIFEST.in -README.rst -setup.cfg -setup.py -tox.ini -docs/Makefile -docs/changes.rst -docs/conf.py -docs/escaping.rst -docs/formatting.rst -docs/html.rst -docs/index.rst -docs/license.rst -docs/make.bat -docs/requirements.txt -src/MarkupSafe.egg-info/PKG-INFO -src/MarkupSafe.egg-info/SOURCES.txt -src/MarkupSafe.egg-info/dependency_links.txt -src/MarkupSafe.egg-info/top_level.txt -src/markupsafe/__init__.py -src/markupsafe/_compat.py -src/markupsafe/_constants.py -src/markupsafe/_native.py -src/markupsafe/_speedups.c -tests/conftest.py -tests/test_escape.py -tests/test_exception_custom_html.py -tests/test_leak.py -tests/test_markupsafe.py \ No newline at end of file diff --git a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/dependency_links.txt b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/top_level.txt b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/top_level.txt deleted file mode 100644 index 75bf729258f9..000000000000 --- a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -markupsafe diff --git a/third_party/python/PyYAML/lib3/PyYAML.egg-info/PKG-INFO b/third_party/python/PyYAML/lib3/PyYAML.egg-info/PKG-INFO deleted file mode 100644 index 04d0abf6e5f7..000000000000 --- a/third_party/python/PyYAML/lib3/PyYAML.egg-info/PKG-INFO +++ /dev/null @@ -1,44 +0,0 @@ -Metadata-Version: 1.2 -Name: PyYAML -Version: 5.4.1 -Summary: YAML parser and emitter for Python -Home-page: https://pyyaml.org/ -Author: Kirill Simonov -Author-email: xi@resolvent.net -License: MIT -Download-URL: https://pypi.org/project/PyYAML/ -Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues -Project-URL: CI, https://github.com/yaml/pyyaml/actions -Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation -Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core -Project-URL: Source Code, https://github.com/yaml/pyyaml -Description: YAML is a data serialization format designed for human readability - and interaction with scripting languages. PyYAML is a YAML parser - and emitter for Python. - - PyYAML features a complete YAML 1.1 parser, Unicode support, pickle - support, capable extension API, and sensible error messages. PyYAML - supports standard YAML tags and provides Python-specific tags that - allow to represent an arbitrary Python object. - - PyYAML is applicable for a broad range of tasks from complex - configuration files to object serialization and persistence. -Platform: Any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Cython -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Text Processing :: Markup -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.* diff --git a/third_party/python/PyYAML/lib3/PyYAML.egg-info/SOURCES.txt b/third_party/python/PyYAML/lib3/PyYAML.egg-info/SOURCES.txt deleted file mode 100644 index 89f5a63c0928..000000000000 --- a/third_party/python/PyYAML/lib3/PyYAML.egg-info/SOURCES.txt +++ /dev/null @@ -1,670 +0,0 @@ -CHANGES -LICENSE -MANIFEST.in -Makefile -README -pyproject.toml -setup.cfg -setup.py -examples/pygments-lexer/example.yaml -examples/pygments-lexer/yaml.py -examples/yaml-highlight/yaml_hl.cfg -examples/yaml-highlight/yaml_hl.py -lib/_yaml/__init__.py -lib/yaml/__init__.py -lib/yaml/composer.py -lib/yaml/constructor.py -lib/yaml/cyaml.py -lib/yaml/dumper.py -lib/yaml/emitter.py -lib/yaml/error.py -lib/yaml/events.py -lib/yaml/loader.py -lib/yaml/nodes.py -lib/yaml/parser.py -lib/yaml/reader.py -lib/yaml/representer.py -lib/yaml/resolver.py -lib/yaml/scanner.py -lib/yaml/serializer.py -lib/yaml/tokens.py -lib3/PyYAML.egg-info/PKG-INFO -lib3/PyYAML.egg-info/SOURCES.txt -lib3/PyYAML.egg-info/dependency_links.txt -lib3/PyYAML.egg-info/top_level.txt -lib3/_yaml/__init__.py -lib3/yaml/__init__.py -lib3/yaml/composer.py -lib3/yaml/constructor.py -lib3/yaml/cyaml.py -lib3/yaml/dumper.py -lib3/yaml/emitter.py -lib3/yaml/error.py -lib3/yaml/events.py -lib3/yaml/loader.py -lib3/yaml/nodes.py -lib3/yaml/parser.py -lib3/yaml/reader.py -lib3/yaml/representer.py -lib3/yaml/resolver.py -lib3/yaml/scanner.py -lib3/yaml/serializer.py -lib3/yaml/tokens.py -tests/data/a-nasty-libyaml-bug.loader-error -tests/data/aliases-cdumper-bug.code -tests/data/aliases.events -tests/data/bool.data -tests/data/bool.detect -tests/data/construct-binary-py2.code -tests/data/construct-binary-py2.data -tests/data/construct-binary-py3.code -tests/data/construct-binary-py3.data -tests/data/construct-bool.code -tests/data/construct-bool.data -tests/data/construct-custom.code -tests/data/construct-custom.data -tests/data/construct-float.code -tests/data/construct-float.data -tests/data/construct-int.code -tests/data/construct-int.data -tests/data/construct-map.code -tests/data/construct-map.data -tests/data/construct-merge.code -tests/data/construct-merge.data -tests/data/construct-null.code -tests/data/construct-null.data -tests/data/construct-omap.code -tests/data/construct-omap.data -tests/data/construct-pairs.code -tests/data/construct-pairs.data -tests/data/construct-python-bool.code -tests/data/construct-python-bool.data -tests/data/construct-python-bytes-py3.code -tests/data/construct-python-bytes-py3.data -tests/data/construct-python-complex.code -tests/data/construct-python-complex.data -tests/data/construct-python-float.code -tests/data/construct-python-float.data -tests/data/construct-python-int.code -tests/data/construct-python-int.data -tests/data/construct-python-long-short-py2.code -tests/data/construct-python-long-short-py2.data -tests/data/construct-python-long-short-py3.code -tests/data/construct-python-long-short-py3.data -tests/data/construct-python-name-module.code -tests/data/construct-python-name-module.data -tests/data/construct-python-none.code -tests/data/construct-python-none.data -tests/data/construct-python-object.code -tests/data/construct-python-object.data -tests/data/construct-python-str-ascii.code -tests/data/construct-python-str-ascii.data -tests/data/construct-python-str-utf8-py2.code -tests/data/construct-python-str-utf8-py2.data -tests/data/construct-python-str-utf8-py3.code -tests/data/construct-python-str-utf8-py3.data -tests/data/construct-python-tuple-list-dict.code -tests/data/construct-python-tuple-list-dict.data -tests/data/construct-python-unicode-ascii-py2.code -tests/data/construct-python-unicode-ascii-py2.data -tests/data/construct-python-unicode-ascii-py3.code -tests/data/construct-python-unicode-ascii-py3.data -tests/data/construct-python-unicode-utf8-py2.code -tests/data/construct-python-unicode-utf8-py2.data -tests/data/construct-python-unicode-utf8-py3.code -tests/data/construct-python-unicode-utf8-py3.data -tests/data/construct-seq.code -tests/data/construct-seq.data -tests/data/construct-set.code -tests/data/construct-set.data -tests/data/construct-str-ascii.code -tests/data/construct-str-ascii.data -tests/data/construct-str-utf8-py2.code -tests/data/construct-str-utf8-py2.data -tests/data/construct-str-utf8-py3.code -tests/data/construct-str-utf8-py3.data -tests/data/construct-str.code -tests/data/construct-str.data -tests/data/construct-timestamp.code -tests/data/construct-timestamp.data -tests/data/construct-value.code -tests/data/construct-value.data -tests/data/document-separator-in-quoted-scalar.loader-error -tests/data/documents.events -tests/data/duplicate-anchor-1.loader-error -tests/data/duplicate-anchor-2.loader-error -tests/data/duplicate-key.former-loader-error.code -tests/data/duplicate-key.former-loader-error.data -tests/data/duplicate-mapping-key.former-loader-error.code -tests/data/duplicate-mapping-key.former-loader-error.data -tests/data/duplicate-merge-key.former-loader-error.code -tests/data/duplicate-merge-key.former-loader-error.data -tests/data/duplicate-tag-directive.loader-error -tests/data/duplicate-value-key.former-loader-error.code -tests/data/duplicate-value-key.former-loader-error.data -tests/data/duplicate-yaml-directive.loader-error -tests/data/emit-block-scalar-in-simple-key-context-bug.canonical -tests/data/emit-block-scalar-in-simple-key-context-bug.data -tests/data/emitting-unacceptable-unicode-character-bug-py3.code -tests/data/emitting-unacceptable-unicode-character-bug-py3.data -tests/data/emitting-unacceptable-unicode-character-bug-py3.skip-ext -tests/data/emitting-unacceptable-unicode-character-bug.code -tests/data/emitting-unacceptable-unicode-character-bug.data -tests/data/emitting-unacceptable-unicode-character-bug.skip-ext -tests/data/emoticons.unicode -tests/data/emoticons2.unicode -tests/data/empty-anchor.emitter-error -tests/data/empty-document-bug.canonical -tests/data/empty-document-bug.data -tests/data/empty-document-bug.empty -tests/data/empty-documents.single-loader-error -tests/data/empty-python-module.loader-error -tests/data/empty-python-name.loader-error -tests/data/empty-tag-handle.emitter-error -tests/data/empty-tag-prefix.emitter-error -tests/data/empty-tag.emitter-error -tests/data/expected-document-end.emitter-error -tests/data/expected-document-start.emitter-error -tests/data/expected-mapping.loader-error -tests/data/expected-node-1.emitter-error -tests/data/expected-node-2.emitter-error -tests/data/expected-nothing.emitter-error -tests/data/expected-scalar.loader-error -tests/data/expected-sequence.loader-error -tests/data/expected-stream-start.emitter-error -tests/data/explicit-document.single-loader-error -tests/data/fetch-complex-value-bug.loader-error -tests/data/float-representer-2.3-bug.code -tests/data/float-representer-2.3-bug.data -tests/data/float.data -tests/data/float.detect -tests/data/forbidden-entry.loader-error -tests/data/forbidden-key.loader-error -tests/data/forbidden-value.loader-error -tests/data/implicit-document.single-loader-error -tests/data/int.data -tests/data/int.detect -tests/data/invalid-anchor-1.loader-error -tests/data/invalid-anchor-2.loader-error -tests/data/invalid-anchor.emitter-error -tests/data/invalid-base64-data-2.loader-error -tests/data/invalid-base64-data.loader-error -tests/data/invalid-block-scalar-indicator.loader-error -tests/data/invalid-character.loader-error -tests/data/invalid-character.stream-error -tests/data/invalid-directive-line.loader-error -tests/data/invalid-directive-name-1.loader-error -tests/data/invalid-directive-name-2.loader-error -tests/data/invalid-escape-character.loader-error -tests/data/invalid-escape-numbers.loader-error -tests/data/invalid-indentation-indicator-1.loader-error -tests/data/invalid-indentation-indicator-2.loader-error -tests/data/invalid-item-without-trailing-break.loader-error -tests/data/invalid-merge-1.loader-error -tests/data/invalid-merge-2.loader-error -tests/data/invalid-omap-1.loader-error -tests/data/invalid-omap-2.loader-error -tests/data/invalid-omap-3.loader-error -tests/data/invalid-pairs-1.loader-error -tests/data/invalid-pairs-2.loader-error -tests/data/invalid-pairs-3.loader-error -tests/data/invalid-python-bytes-2-py3.loader-error -tests/data/invalid-python-bytes-py3.loader-error -tests/data/invalid-python-module-kind.loader-error -tests/data/invalid-python-module-value.loader-error -tests/data/invalid-python-module.loader-error -tests/data/invalid-python-name-kind.loader-error -tests/data/invalid-python-name-module.loader-error -tests/data/invalid-python-name-object.loader-error -tests/data/invalid-python-name-value.loader-error -tests/data/invalid-simple-key.loader-error -tests/data/invalid-single-quote-bug.code -tests/data/invalid-single-quote-bug.data -tests/data/invalid-starting-character.loader-error -tests/data/invalid-tag-1.loader-error -tests/data/invalid-tag-2.loader-error -tests/data/invalid-tag-directive-handle.loader-error -tests/data/invalid-tag-directive-prefix.loader-error -tests/data/invalid-tag-handle-1.emitter-error -tests/data/invalid-tag-handle-1.loader-error -tests/data/invalid-tag-handle-2.emitter-error -tests/data/invalid-tag-handle-2.loader-error -tests/data/invalid-uri-escapes-1.loader-error -tests/data/invalid-uri-escapes-2.loader-error -tests/data/invalid-uri-escapes-3.loader-error -tests/data/invalid-uri.loader-error -tests/data/invalid-utf8-byte.loader-error -tests/data/invalid-utf8-byte.stream-error -tests/data/invalid-yaml-directive-version-1.loader-error -tests/data/invalid-yaml-directive-version-2.loader-error -tests/data/invalid-yaml-directive-version-3.loader-error -tests/data/invalid-yaml-directive-version-4.loader-error -tests/data/invalid-yaml-directive-version-5.loader-error -tests/data/invalid-yaml-directive-version-6.loader-error -tests/data/invalid-yaml-version.loader-error -tests/data/latin.unicode -tests/data/mapping.sort -tests/data/mapping.sorted -tests/data/mappings.events -tests/data/merge.data -tests/data/merge.detect -tests/data/more-floats.code -tests/data/more-floats.data -tests/data/multi-constructor.code -tests/data/multi-constructor.multi -tests/data/myfullloader.subclass_blacklist -tests/data/negative-float-bug.code -tests/data/negative-float-bug.data -tests/data/no-alias-anchor.emitter-error -tests/data/no-alias-anchor.skip-ext -tests/data/no-block-collection-end.loader-error -tests/data/no-block-mapping-end-2.loader-error -tests/data/no-block-mapping-end.loader-error -tests/data/no-document-start.loader-error -tests/data/no-flow-mapping-end.loader-error -tests/data/no-flow-sequence-end.loader-error -tests/data/no-node-1.loader-error -tests/data/no-node-2.loader-error -tests/data/no-tag.emitter-error -tests/data/null.data -tests/data/null.detect -tests/data/odd-utf16.stream-error -tests/data/overwrite-state-new-constructor.loader-error -tests/data/recursive-anchor.former-loader-error -tests/data/recursive-dict.recursive -tests/data/recursive-list.recursive -tests/data/recursive-set.recursive -tests/data/recursive-state.recursive -tests/data/recursive-tuple.recursive -tests/data/recursive.former-dumper-error -tests/data/remove-possible-simple-key-bug.loader-error -tests/data/resolver.data -tests/data/resolver.path -tests/data/run-parser-crash-bug.data -tests/data/scalars.events -tests/data/scan-document-end-bug.canonical -tests/data/scan-document-end-bug.data -tests/data/scan-line-break-bug.canonical -tests/data/scan-line-break-bug.data -tests/data/sequences.events -tests/data/serializer-is-already-opened.dumper-error -tests/data/serializer-is-closed-1.dumper-error -tests/data/serializer-is-closed-2.dumper-error -tests/data/serializer-is-not-opened-1.dumper-error -tests/data/serializer-is-not-opened-2.dumper-error -tests/data/single-dot-is-not-float-bug.code -tests/data/single-dot-is-not-float-bug.data -tests/data/sloppy-indentation.canonical -tests/data/sloppy-indentation.data -tests/data/spec-02-01.data -tests/data/spec-02-01.structure -tests/data/spec-02-01.tokens -tests/data/spec-02-02.data -tests/data/spec-02-02.structure -tests/data/spec-02-02.tokens -tests/data/spec-02-03.data -tests/data/spec-02-03.structure -tests/data/spec-02-03.tokens -tests/data/spec-02-04.data -tests/data/spec-02-04.structure -tests/data/spec-02-04.tokens -tests/data/spec-02-05.data -tests/data/spec-02-05.structure -tests/data/spec-02-05.tokens -tests/data/spec-02-06.data -tests/data/spec-02-06.structure -tests/data/spec-02-06.tokens -tests/data/spec-02-07.data -tests/data/spec-02-07.structure -tests/data/spec-02-07.tokens -tests/data/spec-02-08.data -tests/data/spec-02-08.structure -tests/data/spec-02-08.tokens -tests/data/spec-02-09.data -tests/data/spec-02-09.structure -tests/data/spec-02-09.tokens -tests/data/spec-02-10.data -tests/data/spec-02-10.structure -tests/data/spec-02-10.tokens -tests/data/spec-02-11.data -tests/data/spec-02-11.structure -tests/data/spec-02-11.tokens -tests/data/spec-02-12.data -tests/data/spec-02-12.structure -tests/data/spec-02-12.tokens -tests/data/spec-02-13.data -tests/data/spec-02-13.structure -tests/data/spec-02-13.tokens -tests/data/spec-02-14.data -tests/data/spec-02-14.structure -tests/data/spec-02-14.tokens -tests/data/spec-02-15.data -tests/data/spec-02-15.structure -tests/data/spec-02-15.tokens -tests/data/spec-02-16.data -tests/data/spec-02-16.structure -tests/data/spec-02-16.tokens -tests/data/spec-02-17.data -tests/data/spec-02-17.structure -tests/data/spec-02-17.tokens -tests/data/spec-02-18.data -tests/data/spec-02-18.structure -tests/data/spec-02-18.tokens -tests/data/spec-02-19.data -tests/data/spec-02-19.structure -tests/data/spec-02-19.tokens -tests/data/spec-02-20.data -tests/data/spec-02-20.structure -tests/data/spec-02-20.tokens -tests/data/spec-02-21.data -tests/data/spec-02-21.structure -tests/data/spec-02-21.tokens -tests/data/spec-02-22.data -tests/data/spec-02-22.structure -tests/data/spec-02-22.tokens -tests/data/spec-02-23.data -tests/data/spec-02-23.structure -tests/data/spec-02-23.tokens -tests/data/spec-02-24.data -tests/data/spec-02-24.structure -tests/data/spec-02-24.tokens -tests/data/spec-02-25.data -tests/data/spec-02-25.structure -tests/data/spec-02-25.tokens -tests/data/spec-02-26.data -tests/data/spec-02-26.structure -tests/data/spec-02-26.tokens -tests/data/spec-02-27.data -tests/data/spec-02-27.structure -tests/data/spec-02-27.tokens -tests/data/spec-02-28.data -tests/data/spec-02-28.structure -tests/data/spec-02-28.tokens -tests/data/spec-05-01-utf16be.data -tests/data/spec-05-01-utf16be.empty -tests/data/spec-05-01-utf16le.data -tests/data/spec-05-01-utf16le.empty -tests/data/spec-05-01-utf8.data -tests/data/spec-05-01-utf8.empty -tests/data/spec-05-02-utf16be.data -tests/data/spec-05-02-utf16be.error -tests/data/spec-05-02-utf16le.data -tests/data/spec-05-02-utf16le.error -tests/data/spec-05-02-utf8.data -tests/data/spec-05-02-utf8.error -tests/data/spec-05-03.canonical -tests/data/spec-05-03.data -tests/data/spec-05-04.canonical -tests/data/spec-05-04.data -tests/data/spec-05-05.data -tests/data/spec-05-05.empty -tests/data/spec-05-06.canonical -tests/data/spec-05-06.data -tests/data/spec-05-07.canonical -tests/data/spec-05-07.data -tests/data/spec-05-08.canonical -tests/data/spec-05-08.data -tests/data/spec-05-09.canonical -tests/data/spec-05-09.data -tests/data/spec-05-10.data -tests/data/spec-05-10.error -tests/data/spec-05-11.canonical -tests/data/spec-05-11.data -tests/data/spec-05-12.data -tests/data/spec-05-12.error -tests/data/spec-05-13.canonical -tests/data/spec-05-13.data -tests/data/spec-05-14.canonical -tests/data/spec-05-14.data -tests/data/spec-05-15.data -tests/data/spec-05-15.error -tests/data/spec-06-01.canonical -tests/data/spec-06-01.data -tests/data/spec-06-02.data -tests/data/spec-06-02.empty -tests/data/spec-06-03.canonical -tests/data/spec-06-03.data -tests/data/spec-06-04.canonical -tests/data/spec-06-04.data -tests/data/spec-06-05.canonical -tests/data/spec-06-05.data -tests/data/spec-06-06.canonical -tests/data/spec-06-06.data -tests/data/spec-06-07.canonical -tests/data/spec-06-07.data -tests/data/spec-06-08.canonical -tests/data/spec-06-08.data -tests/data/spec-07-01.canonical -tests/data/spec-07-01.data -tests/data/spec-07-01.skip-ext -tests/data/spec-07-02.canonical -tests/data/spec-07-02.data -tests/data/spec-07-02.skip-ext -tests/data/spec-07-03.data -tests/data/spec-07-03.error -tests/data/spec-07-04.canonical -tests/data/spec-07-04.data -tests/data/spec-07-05.data -tests/data/spec-07-05.error -tests/data/spec-07-06.canonical -tests/data/spec-07-06.data -tests/data/spec-07-07a.canonical -tests/data/spec-07-07a.data -tests/data/spec-07-07b.canonical -tests/data/spec-07-07b.data -tests/data/spec-07-08.canonical -tests/data/spec-07-08.data -tests/data/spec-07-09.canonical -tests/data/spec-07-09.data -tests/data/spec-07-10.canonical -tests/data/spec-07-10.data -tests/data/spec-07-11.data -tests/data/spec-07-11.empty -tests/data/spec-07-12a.canonical -tests/data/spec-07-12a.data -tests/data/spec-07-12b.canonical -tests/data/spec-07-12b.data -tests/data/spec-07-13.canonical -tests/data/spec-07-13.data -tests/data/spec-08-01.canonical -tests/data/spec-08-01.data -tests/data/spec-08-02.canonical -tests/data/spec-08-02.data -tests/data/spec-08-03.canonical -tests/data/spec-08-03.data -tests/data/spec-08-04.data -tests/data/spec-08-04.error -tests/data/spec-08-05.canonical -tests/data/spec-08-05.data -tests/data/spec-08-06.data -tests/data/spec-08-06.error -tests/data/spec-08-07.canonical -tests/data/spec-08-07.data -tests/data/spec-08-08.canonical -tests/data/spec-08-08.data -tests/data/spec-08-09.canonical -tests/data/spec-08-09.data -tests/data/spec-08-10.canonical -tests/data/spec-08-10.data -tests/data/spec-08-11.canonical -tests/data/spec-08-11.data -tests/data/spec-08-12.canonical -tests/data/spec-08-12.data -tests/data/spec-08-13.canonical -tests/data/spec-08-13.data -tests/data/spec-08-13.skip-ext -tests/data/spec-08-14.canonical -tests/data/spec-08-14.data -tests/data/spec-08-15.canonical -tests/data/spec-08-15.data -tests/data/spec-09-01.canonical -tests/data/spec-09-01.data -tests/data/spec-09-02.canonical -tests/data/spec-09-02.data -tests/data/spec-09-03.canonical -tests/data/spec-09-03.data -tests/data/spec-09-04.canonical -tests/data/spec-09-04.data -tests/data/spec-09-05.canonical -tests/data/spec-09-05.data -tests/data/spec-09-06.canonical -tests/data/spec-09-06.data -tests/data/spec-09-07.canonical -tests/data/spec-09-07.data -tests/data/spec-09-08.canonical -tests/data/spec-09-08.data -tests/data/spec-09-09.canonical -tests/data/spec-09-09.data -tests/data/spec-09-10.canonical -tests/data/spec-09-10.data -tests/data/spec-09-11.canonical -tests/data/spec-09-11.data -tests/data/spec-09-12.canonical -tests/data/spec-09-12.data -tests/data/spec-09-13.canonical -tests/data/spec-09-13.data -tests/data/spec-09-14.data -tests/data/spec-09-14.error -tests/data/spec-09-15.canonical -tests/data/spec-09-15.data -tests/data/spec-09-16.canonical -tests/data/spec-09-16.data -tests/data/spec-09-17.canonical -tests/data/spec-09-17.data -tests/data/spec-09-18.canonical -tests/data/spec-09-18.data -tests/data/spec-09-19.canonical -tests/data/spec-09-19.data -tests/data/spec-09-20.canonical -tests/data/spec-09-20.data -tests/data/spec-09-20.skip-ext -tests/data/spec-09-21.data -tests/data/spec-09-21.error -tests/data/spec-09-22.canonical -tests/data/spec-09-22.data -tests/data/spec-09-23.canonical -tests/data/spec-09-23.data -tests/data/spec-09-24.canonical -tests/data/spec-09-24.data -tests/data/spec-09-25.canonical -tests/data/spec-09-25.data -tests/data/spec-09-26.canonical -tests/data/spec-09-26.data -tests/data/spec-09-27.canonical -tests/data/spec-09-27.data -tests/data/spec-09-28.canonical -tests/data/spec-09-28.data -tests/data/spec-09-29.canonical -tests/data/spec-09-29.data -tests/data/spec-09-30.canonical -tests/data/spec-09-30.data -tests/data/spec-09-31.canonical -tests/data/spec-09-31.data -tests/data/spec-09-32.canonical -tests/data/spec-09-32.data -tests/data/spec-09-33.canonical -tests/data/spec-09-33.data -tests/data/spec-10-01.canonical -tests/data/spec-10-01.data -tests/data/spec-10-02.canonical -tests/data/spec-10-02.data -tests/data/spec-10-03.canonical -tests/data/spec-10-03.data -tests/data/spec-10-04.canonical -tests/data/spec-10-04.data -tests/data/spec-10-05.canonical -tests/data/spec-10-05.data -tests/data/spec-10-06.canonical -tests/data/spec-10-06.data -tests/data/spec-10-07.canonical -tests/data/spec-10-07.data -tests/data/spec-10-08.data -tests/data/spec-10-08.error -tests/data/spec-10-09.canonical -tests/data/spec-10-09.data -tests/data/spec-10-10.canonical -tests/data/spec-10-10.data -tests/data/spec-10-11.canonical -tests/data/spec-10-11.data -tests/data/spec-10-12.canonical -tests/data/spec-10-12.data -tests/data/spec-10-13.canonical -tests/data/spec-10-13.data -tests/data/spec-10-14.canonical -tests/data/spec-10-14.data -tests/data/spec-10-15.canonical -tests/data/spec-10-15.data -tests/data/str.data -tests/data/str.detect -tests/data/tags.events -tests/data/test_mark.marks -tests/data/timestamp-bugs.code -tests/data/timestamp-bugs.data -tests/data/timestamp.data -tests/data/timestamp.detect -tests/data/unacceptable-key.loader-error -tests/data/unclosed-bracket.loader-error -tests/data/unclosed-quoted-scalar.loader-error -tests/data/undefined-anchor.loader-error -tests/data/undefined-constructor.loader-error -tests/data/undefined-tag-handle.loader-error -tests/data/unknown.dumper-error -tests/data/unsupported-version.emitter-error -tests/data/utf16be.code -tests/data/utf16be.data -tests/data/utf16le.code -tests/data/utf16le.data -tests/data/utf8-implicit.code -tests/data/utf8-implicit.data -tests/data/utf8.code -tests/data/utf8.data -tests/data/value.data -tests/data/value.detect -tests/data/yaml.data -tests/data/yaml.detect -tests/lib/canonical.py -tests/lib/test_all.py -tests/lib/test_appliance.py -tests/lib/test_build.py -tests/lib/test_build_ext.py -tests/lib/test_canonical.py -tests/lib/test_constructor.py -tests/lib/test_emitter.py -tests/lib/test_errors.py -tests/lib/test_input_output.py -tests/lib/test_mark.py -tests/lib/test_multi_constructor.py -tests/lib/test_reader.py -tests/lib/test_recursive.py -tests/lib/test_representer.py -tests/lib/test_resolver.py -tests/lib/test_sort_keys.py -tests/lib/test_structure.py -tests/lib/test_tokens.py -tests/lib/test_yaml.py -tests/lib/test_yaml_ext.py -tests/lib3/canonical.py -tests/lib3/test_all.py -tests/lib3/test_appliance.py -tests/lib3/test_build.py -tests/lib3/test_build_ext.py -tests/lib3/test_canonical.py -tests/lib3/test_constructor.py -tests/lib3/test_emitter.py -tests/lib3/test_errors.py -tests/lib3/test_input_output.py -tests/lib3/test_mark.py -tests/lib3/test_multi_constructor.py -tests/lib3/test_reader.py -tests/lib3/test_recursive.py -tests/lib3/test_representer.py -tests/lib3/test_resolver.py -tests/lib3/test_sort_keys.py -tests/lib3/test_structure.py -tests/lib3/test_tokens.py -tests/lib3/test_yaml.py -tests/lib3/test_yaml_ext.py -yaml/__init__.pxd -yaml/_yaml.h -yaml/_yaml.pxd -yaml/_yaml.pyx \ No newline at end of file diff --git a/third_party/python/PyYAML/lib3/PyYAML.egg-info/dependency_links.txt b/third_party/python/PyYAML/lib3/PyYAML.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/PyYAML/lib3/PyYAML.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/PyYAML/lib3/PyYAML.egg-info/top_level.txt b/third_party/python/PyYAML/lib3/PyYAML.egg-info/top_level.txt deleted file mode 100644 index e6475e911f62..000000000000 --- a/third_party/python/PyYAML/lib3/PyYAML.egg-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -_yaml -yaml diff --git a/third_party/python/aiohttp/aiohttp.egg-info/PKG-INFO b/third_party/python/aiohttp/aiohttp.egg-info/PKG-INFO deleted file mode 100644 index a0c00158c790..000000000000 --- a/third_party/python/aiohttp/aiohttp.egg-info/PKG-INFO +++ /dev/null @@ -1,966 +0,0 @@ -Metadata-Version: 2.1 -Name: aiohttp -Version: 3.7.4.post0 -Summary: Async http client/server framework (asyncio) -Home-page: https://github.com/aio-libs/aiohttp -Author: Nikolay Kim -Author-email: fafhrd91@gmail.com -Maintainer: Nikolay Kim , Andrew Svetlov -Maintainer-email: aio-libs@googlegroups.com -License: Apache 2 -Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby -Project-URL: CI: Azure Pipelines, https://dev.azure.com/aio-libs/aiohttp/_build -Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/aiohttp -Project-URL: Docs: RTD, https://docs.aiohttp.org -Project-URL: GitHub: issues, https://github.com/aio-libs/aiohttp/issues -Project-URL: GitHub: repo, https://github.com/aio-libs/aiohttp -Description: ================================== - Async http client/server framework - ================================== - - .. image:: https://raw.githubusercontent.com/aio-libs/aiohttp/master/docs/_static/aiohttp-icon-128x128.png - :height: 64px - :width: 64px - :alt: aiohttp logo - - | - - .. image:: https://github.com/aio-libs/aiohttp/workflows/CI/badge.svg - :target: https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI - :alt: GitHub Actions status for master branch - - .. image:: https://codecov.io/gh/aio-libs/aiohttp/branch/master/graph/badge.svg - :target: https://codecov.io/gh/aio-libs/aiohttp - :alt: codecov.io status for master branch - - .. image:: https://badge.fury.io/py/aiohttp.svg - :target: https://pypi.org/project/aiohttp - :alt: Latest PyPI package version - - .. image:: https://readthedocs.org/projects/aiohttp/badge/?version=latest - :target: https://docs.aiohttp.org/ - :alt: Latest Read The Docs - - .. image:: https://img.shields.io/discourse/status?server=https%3A%2F%2Faio-libs.discourse.group - :target: https://aio-libs.discourse.group - :alt: Discourse status - - .. image:: https://badges.gitter.im/Join%20Chat.svg - :target: https://gitter.im/aio-libs/Lobby - :alt: Chat on Gitter - - - Key Features - ============ - - - Supports both client and server side of HTTP protocol. - - Supports both client and server Web-Sockets out-of-the-box and avoids - Callback Hell. - - Provides Web-server with middlewares and plugable routing. - - - Getting started - =============== - - Client - ------ - - To get something from the web: - - .. code-block:: python - - import aiohttp - import asyncio - - async def main(): - - async with aiohttp.ClientSession() as session: - async with session.get('http://python.org') as response: - - print("Status:", response.status) - print("Content-type:", response.headers['content-type']) - - html = await response.text() - print("Body:", html[:15], "...") - - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) - - This prints: - - .. code-block:: - - Status: 200 - Content-type: text/html; charset=utf-8 - Body: ... - - Coming from `requests `_ ? Read `why we need so many lines `_. - - Server - ------ - - An example using a simple server: - - .. code-block:: python - - # examples/server_simple.py - from aiohttp import web - - async def handle(request): - name = request.match_info.get('name', "Anonymous") - text = "Hello, " + name - return web.Response(text=text) - - async def wshandle(request): - ws = web.WebSocketResponse() - await ws.prepare(request) - - async for msg in ws: - if msg.type == web.WSMsgType.text: - await ws.send_str("Hello, {}".format(msg.data)) - elif msg.type == web.WSMsgType.binary: - await ws.send_bytes(msg.data) - elif msg.type == web.WSMsgType.close: - break - - return ws - - - app = web.Application() - app.add_routes([web.get('/', handle), - web.get('/echo', wshandle), - web.get('/{name}', handle)]) - - if __name__ == '__main__': - web.run_app(app) - - - Documentation - ============= - - https://aiohttp.readthedocs.io/ - - - Demos - ===== - - https://github.com/aio-libs/aiohttp-demos - - - External links - ============== - - * `Third party libraries - `_ - * `Built with aiohttp - `_ - * `Powered by aiohttp - `_ - - Feel free to make a Pull Request for adding your link to these pages! - - - Communication channels - ====================== - - *aio-libs discourse group*: https://aio-libs.discourse.group - - *gitter chat* https://gitter.im/aio-libs/Lobby - - We support `Stack Overflow - `_. - Please add *aiohttp* tag to your question there. - - Requirements - ============ - - - Python >= 3.6 - - async-timeout_ - - attrs_ - - chardet_ - - multidict_ - - yarl_ - - Optionally you may install the cChardet_ and aiodns_ libraries (highly - recommended for sake of speed). - - .. _chardet: https://pypi.python.org/pypi/chardet - .. _aiodns: https://pypi.python.org/pypi/aiodns - .. _attrs: https://github.com/python-attrs/attrs - .. _multidict: https://pypi.python.org/pypi/multidict - .. _yarl: https://pypi.python.org/pypi/yarl - .. _async-timeout: https://pypi.python.org/pypi/async_timeout - .. _cChardet: https://pypi.python.org/pypi/cchardet - - License - ======= - - ``aiohttp`` is offered under the Apache 2 license. - - - Keepsafe - ======== - - The aiohttp community would like to thank Keepsafe - (https://www.getkeepsafe.com) for its support in the early days of - the project. - - - Source code - =========== - - The latest developer version is available in a GitHub repository: - https://github.com/aio-libs/aiohttp - - Benchmarks - ========== - - If you are interested in efficiency, the AsyncIO community maintains a - list of benchmarks on the official wiki: - https://github.com/python/asyncio/wiki/Benchmarks - - ========= - Changelog - ========= - - .. - You should *NOT* be adding new change log entries to this file, this - file is managed by towncrier. You *may* edit previous change logs to - fix problems like typo corrections or such. - To add a new change log entry, please see - https://pip.pypa.io/en/latest/development/#adding-a-news-entry - we named the news folder "changes". - - WARNING: Don't drop the next directive! - - .. towncrier release notes start - - 3.7.4.post0 (2021-03-06) - ======================== - - Misc - ---- - - - Bumped upper bound of the ``chardet`` runtime dependency - to allow their v4.0 version stream. - `#5366 `_ - - - ---- - - - 3.7.4 (2021-02-25) - ================== - - Bugfixes - -------- - - - **(SECURITY BUG)** Started preventing open redirects in the - ``aiohttp.web.normalize_path_middleware`` middleware. For - more details, see - https://github.com/aio-libs/aiohttp/security/advisories/GHSA-v6wp-4m6f-gcjg. - - Thanks to `Beast Glatisant `__ for - finding the first instance of this issue and `Jelmer Vernooij - `__ for reporting and tracking it down - in aiohttp. - `#5497 `_ - - Fix interpretation difference of the pure-Python and the Cython-based - HTTP parsers construct a ``yarl.URL`` object for HTTP request-target. - - Before this fix, the Python parser would turn the URI's absolute-path - for ``//some-path`` into ``/`` while the Cython code preserved it as - ``//some-path``. Now, both do the latter. - `#5498 `_ - - - ---- - - - 3.7.3 (2020-11-18) - ================== - - Features - -------- - - - Use Brotli instead of brotlipy - `#3803 `_ - - Made exceptions pickleable. Also changed the repr of some exceptions. - `#4077 `_ - - - Bugfixes - -------- - - - Raise a ClientResponseError instead of an AssertionError for a blank - HTTP Reason Phrase. - `#3532 `_ - - Fix ``web_middlewares.normalize_path_middleware`` behavior for patch without slash. - `#3669 `_ - - Fix overshadowing of overlapped sub-applications prefixes. - `#3701 `_ - - Make `BaseConnector.close()` a coroutine and wait until the client closes all connections. Drop deprecated "with Connector():" syntax. - `#3736 `_ - - Reset the ``sock_read`` timeout each time data is received for a ``aiohttp.client`` response. - `#3808 `_ - - Fixed type annotation for add_view method of UrlDispatcher to accept any subclass of View - `#3880 `_ - - Fixed querying the address families from DNS that the current host supports. - `#5156 `_ - - Change return type of MultipartReader.__aiter__() and BodyPartReader.__aiter__() to AsyncIterator. - `#5163 `_ - - Provide x86 Windows wheels. - `#5230 `_ - - - Improved Documentation - ---------------------- - - - Add documentation for ``aiohttp.web.FileResponse``. - `#3958 `_ - - Removed deprecation warning in tracing example docs - `#3964 `_ - - Fixed wrong "Usage" docstring of ``aiohttp.client.request``. - `#4603 `_ - - Add aiohttp-pydantic to third party libraries - `#5228 `_ - - - Misc - ---- - - - `#4102 `_ - - - ---- - - - 3.7.2 (2020-10-27) - ================== - - Bugfixes - -------- - - - Fixed static files handling for loops without ``.sendfile()`` support - `#5149 `_ - - - ---- - - - 3.7.1 (2020-10-25) - ================== - - Bugfixes - -------- - - - Fixed a type error caused by the conditional import of `Protocol`. - `#5111 `_ - - Server doesn't send Content-Length for 1xx or 204 - `#4901 `_ - - Fix run_app typing - `#4957 `_ - - Always require ``typing_extensions`` library. - `#5107 `_ - - Fix a variable-shadowing bug causing `ThreadedResolver.resolve` to - return the resolved IP as the ``hostname`` in each record, which prevented - validation of HTTPS connections. - `#5110 `_ - - Added annotations to all public attributes. - `#5115 `_ - - Fix flaky test_when_timeout_smaller_second - `#5116 `_ - - Ensure sending a zero byte file does not throw an exception - `#5124 `_ - - Fix a bug in ``web.run_app()`` about Python version checking on Windows - `#5127 `_ - - - ---- - - - 3.7.0 (2020-10-24) - ================== - - Features - -------- - - - Response headers are now prepared prior to running ``on_response_prepare`` hooks, directly before headers are sent to the client. - `#1958 `_ - - Add a ``quote_cookie`` option to ``CookieJar``, a way to skip quotation wrapping of cookies containing special characters. - `#2571 `_ - - Call ``AccessLogger.log`` with the current exception available from ``sys.exc_info()``. - `#3557 `_ - - `web.UrlDispatcher.add_routes` and `web.Application.add_routes` return a list - of registered `AbstractRoute` instances. `AbstractRouteDef.register` (and all - subclasses) return a list of registered resources registered resource. - `#3866 `_ - - Added properties of default ClientSession params to ClientSession class so it is available for introspection - `#3882 `_ - - Don't cancel web handler on peer disconnection, raise `OSError` on reading/writing instead. - `#4080 `_ - - Implement BaseRequest.get_extra_info() to access a protocol transports' extra info. - `#4189 `_ - - Added `ClientSession.timeout` property. - `#4191 `_ - - allow use of SameSite in cookies. - `#4224 `_ - - Use ``loop.sendfile()`` instead of custom implementation if available. - `#4269 `_ - - Apply SO_REUSEADDR to test server's socket. - `#4393 `_ - - Use .raw_host instead of slower .host in client API - `#4402 `_ - - Allow configuring the buffer size of input stream by passing ``read_bufsize`` argument. - `#4453 `_ - - Pass tests on Python 3.8 for Windows. - `#4513 `_ - - Add `method` and `url` attributes to `TraceRequestChunkSentParams` and `TraceResponseChunkReceivedParams`. - `#4674 `_ - - Add ClientResponse.ok property for checking status code under 400. - `#4711 `_ - - Don't ceil timeouts that are smaller than 5 seconds. - `#4850 `_ - - TCPSite now listens by default on all interfaces instead of just IPv4 when `None` is passed in as the host. - `#4894 `_ - - Bump ``http_parser`` to 2.9.4 - `#5070 `_ - - - Bugfixes - -------- - - - Fix keepalive connections not being closed in time - `#3296 `_ - - Fix failed websocket handshake leaving connection hanging. - `#3380 `_ - - Fix tasks cancellation order on exit. The run_app task needs to be cancelled first for cleanup hooks to run with all tasks intact. - `#3805 `_ - - Don't start heartbeat until _writer is set - `#4062 `_ - - Fix handling of multipart file uploads without a content type. - `#4089 `_ - - Preserve view handler function attributes across middlewares - `#4174 `_ - - Fix the string representation of ``ServerDisconnectedError``. - `#4175 `_ - - Raising RuntimeError when trying to get encoding from not read body - `#4214 `_ - - Remove warning messages from noop. - `#4282 `_ - - Raise ClientPayloadError if FormData re-processed. - `#4345 `_ - - Fix a warning about unfinished task in ``web_protocol.py`` - `#4408 `_ - - Fixed 'deflate' compression. According to RFC 2616 now. - `#4506 `_ - - Fixed OverflowError on platforms with 32-bit time_t - `#4515 `_ - - Fixed request.body_exists returns wrong value for methods without body. - `#4528 `_ - - Fix connecting to link-local IPv6 addresses. - `#4554 `_ - - Fix a problem with connection waiters that are never awaited. - `#4562 `_ - - Always make sure transport is not closing before reuse a connection. - - Reuse a protocol based on keepalive in headers is unreliable. - For example, uWSGI will not support keepalive even it serves a - HTTP 1.1 request, except explicitly configure uWSGI with a - ``--http-keepalive`` option. - - Servers designed like uWSGI could cause aiohttp intermittently - raise a ConnectionResetException when the protocol poll runs - out and some protocol is reused. - `#4587 `_ - - Handle the last CRLF correctly even if it is received via separate TCP segment. - `#4630 `_ - - Fix the register_resource function to validate route name before splitting it so that route name can include python keywords. - `#4691 `_ - - Improve typing annotations for ``web.Request``, ``aiohttp.ClientResponse`` and - ``multipart`` module. - `#4736 `_ - - Fix resolver task is not awaited when connector is cancelled - `#4795 `_ - - Fix a bug "Aiohttp doesn't return any error on invalid request methods" - `#4798 `_ - - Fix HEAD requests for static content. - `#4809 `_ - - Fix incorrect size calculation for memoryview - `#4890 `_ - - Add HTTPMove to _all__. - `#4897 `_ - - Fixed the type annotations in the ``tracing`` module. - `#4912 `_ - - Fix typing for multipart ``__aiter__``. - `#4931 `_ - - Fix for race condition on connections in BaseConnector that leads to exceeding the connection limit. - `#4936 `_ - - Add forced UTF-8 encoding for ``application/rdap+json`` responses. - `#4938 `_ - - Fix inconsistency between Python and C http request parsers in parsing pct-encoded URL. - `#4972 `_ - - Fix connection closing issue in HEAD request. - `#5012 `_ - - Fix type hint on BaseRunner.addresses (from ``List[str]`` to ``List[Any]``) - `#5086 `_ - - Make `web.run_app()` more responsive to Ctrl+C on Windows for Python < 3.8. It slightly - increases CPU load as a side effect. - `#5098 `_ - - - Improved Documentation - ---------------------- - - - Fix example code in client quick-start - `#3376 `_ - - Updated the docs so there is no contradiction in ``ttl_dns_cache`` default value - `#3512 `_ - - Add 'Deploy with SSL' to docs. - `#4201 `_ - - Change typing of the secure argument on StreamResponse.set_cookie from ``Optional[str]`` to ``Optional[bool]`` - `#4204 `_ - - Changes ``ttl_dns_cache`` type from int to Optional[int]. - `#4270 `_ - - Simplify README hello word example and add a documentation page for people coming from requests. - `#4272 `_ - - Improve some code examples in the documentation involving websockets and starting a simple HTTP site with an AppRunner. - `#4285 `_ - - Fix typo in code example in Multipart docs - `#4312 `_ - - Fix code example in Multipart section. - `#4314 `_ - - Update contributing guide so new contributors read the most recent version of that guide. Update command used to create test coverage reporting. - `#4810 `_ - - Spelling: Change "canonize" to "canonicalize". - `#4986 `_ - - Add ``aiohttp-sse-client`` library to third party usage list. - `#5084 `_ - - - Misc - ---- - - - `#2856 `_, `#4218 `_, `#4250 `_ - - - ---- - - - 3.6.3 (2020-10-12) - ================== - - Bugfixes - -------- - - - Pin yarl to ``<1.6.0`` to avoid buggy behavior that will be fixed by the next aiohttp - release. - - 3.6.2 (2019-10-09) - ================== - - Features - -------- - - - Made exceptions pickleable. Also changed the repr of some exceptions. - `#4077 `_ - - Use ``Iterable`` type hint instead of ``Sequence`` for ``Application`` *middleware* - parameter. `#4125 `_ - - - Bugfixes - -------- - - - Reset the ``sock_read`` timeout each time data is received for a - ``aiohttp.ClientResponse``. `#3808 - `_ - - Fix handling of expired cookies so they are not stored in CookieJar. - `#4063 `_ - - Fix misleading message in the string representation of ``ClientConnectorError``; - ``self.ssl == None`` means default SSL context, not SSL disabled `#4097 - `_ - - Don't clobber HTTP status when using FileResponse. - `#4106 `_ - - - Improved Documentation - ---------------------- - - - Added minimal required logging configuration to logging documentation. - `#2469 `_ - - Update docs to reflect proxy support. - `#4100 `_ - - Fix typo in code example in testing docs. - `#4108 `_ - - - Misc - ---- - - - `#4102 `_ - - - ---- - - - 3.6.1 (2019-09-19) - ================== - - Features - -------- - - - Compatibility with Python 3.8. - `#4056 `_ - - - Bugfixes - -------- - - - correct some exception string format - `#4068 `_ - - Emit a warning when ``ssl.OP_NO_COMPRESSION`` is - unavailable because the runtime is built against - an outdated OpenSSL. - `#4052 `_ - - Update multidict requirement to >= 4.5 - `#4057 `_ - - - Improved Documentation - ---------------------- - - - Provide pytest-aiohttp namespace for pytest fixtures in docs. - `#3723 `_ - - - ---- - - - 3.6.0 (2019-09-06) - ================== - - Features - -------- - - - Add support for Named Pipes (Site and Connector) under Windows. This feature requires - Proactor event loop to work. `#3629 - `_ - - Removed ``Transfer-Encoding: chunked`` header from websocket responses to be - compatible with more http proxy servers. `#3798 - `_ - - Accept non-GET request for starting websocket handshake on server side. - `#3980 `_ - - - Bugfixes - -------- - - - Raise a ClientResponseError instead of an AssertionError for a blank - HTTP Reason Phrase. - `#3532 `_ - - Fix an issue where cookies would sometimes not be set during a redirect. - `#3576 `_ - - Change normalize_path_middleware to use 308 redirect instead of 301. - - This behavior should prevent clients from being unable to use PUT/POST - methods on endpoints that are redirected because of a trailing slash. - `#3579 `_ - - Drop the processed task from ``all_tasks()`` list early. It prevents logging about a - task with unhandled exception when the server is used in conjunction with - ``asyncio.run()``. `#3587 `_ - - ``Signal`` type annotation changed from ``Signal[Callable[['TraceConfig'], - Awaitable[None]]]`` to ``Signal[Callable[ClientSession, SimpleNamespace, ...]``. - `#3595 `_ - - Use sanitized URL as Location header in redirects - `#3614 `_ - - Improve typing annotations for multipart.py along with changes required - by mypy in files that references multipart.py. - `#3621 `_ - - Close session created inside ``aiohttp.request`` when unhandled exception occurs - `#3628 `_ - - Cleanup per-chunk data in generic data read. Memory leak fixed. - `#3631 `_ - - Use correct type for add_view and family - `#3633 `_ - - Fix _keepalive field in __slots__ of ``RequestHandler``. - `#3644 `_ - - Properly handle ConnectionResetError, to silence the "Cannot write to closing - transport" exception when clients disconnect uncleanly. - `#3648 `_ - - Suppress pytest warnings due to ``test_utils`` classes - `#3660 `_ - - Fix overshadowing of overlapped sub-application prefixes. - `#3701 `_ - - Fixed return type annotation for WSMessage.json() - `#3720 `_ - - Properly expose TooManyRedirects publicly as documented. - `#3818 `_ - - Fix missing brackets for IPv6 in proxy CONNECT request - `#3841 `_ - - Make the signature of ``aiohttp.test_utils.TestClient.request`` match - ``asyncio.ClientSession.request`` according to the docs `#3852 - `_ - - Use correct style for re-exported imports, makes mypy ``--strict`` mode happy. - `#3868 `_ - - Fixed type annotation for add_view method of UrlDispatcher to accept any subclass of - View `#3880 `_ - - Made cython HTTP parser set Reason-Phrase of the response to an empty string if it is - missing. `#3906 `_ - - Add URL to the string representation of ClientResponseError. - `#3959 `_ - - Accept ``istr`` keys in ``LooseHeaders`` type hints. - `#3976 `_ - - Fixed race conditions in _resolve_host caching and throttling when tracing is enabled. - `#4013 `_ - - For URLs like "unix://localhost/..." set Host HTTP header to "localhost" instead of - "localhost:None". `#4039 `_ - - - Improved Documentation - ---------------------- - - - Modify documentation for Background Tasks to remove deprecated usage of event loop. - `#3526 `_ - - use ``if __name__ == '__main__':`` in server examples. - `#3775 `_ - - Update documentation reference to the default access logger. - `#3783 `_ - - Improve documentation for ``web.BaseRequest.path`` and ``web.BaseRequest.raw_path``. - `#3791 `_ - - Removed deprecation warning in tracing example docs - `#3964 `_ - - - ---- - - - 3.5.4 (2019-01-12) - ================== - - Bugfixes - -------- - - - Fix stream ``.read()`` / ``.readany()`` / ``.iter_any()`` which used to return a - partial content only in case of compressed content - `#3525 `_ - - - 3.5.3 (2019-01-10) - ================== - - Bugfixes - -------- - - - Fix type stubs for ``aiohttp.web.run_app(access_log=True)`` and fix edge case of - ``access_log=True`` and the event loop being in debug mode. `#3504 - `_ - - Fix ``aiohttp.ClientTimeout`` type annotations to accept ``None`` for fields - `#3511 `_ - - Send custom per-request cookies even if session jar is empty - `#3515 `_ - - Restore Linux binary wheels publishing on PyPI - - ---- - - - 3.5.2 (2019-01-08) - ================== - - Features - -------- - - - ``FileResponse`` from ``web_fileresponse.py`` uses a ``ThreadPoolExecutor`` to work - with files asynchronously. I/O based payloads from ``payload.py`` uses a - ``ThreadPoolExecutor`` to work with I/O objects asynchronously. `#3313 - `_ - - Internal Server Errors in plain text if the browser does not support HTML. - `#3483 `_ - - - Bugfixes - -------- - - - Preserve MultipartWriter parts headers on write. Refactor the way how - ``Payload.headers`` are handled. Payload instances now always have headers and - Content-Type defined. Fix Payload Content-Disposition header reset after initial - creation. `#3035 `_ - - Log suppressed exceptions in ``GunicornWebWorker``. - `#3464 `_ - - Remove wildcard imports. - `#3468 `_ - - Use the same task for app initialization and web server handling in gunicorn workers. - It allows to use Python3.7 context vars smoothly. - `#3471 `_ - - Fix handling of chunked+gzipped response when first chunk does not give uncompressed - data `#3477 `_ - - Replace ``collections.MutableMapping`` with ``collections.abc.MutableMapping`` to - avoid a deprecation warning. `#3480 - `_ - - ``Payload.size`` type annotation changed from ``Optional[float]`` to - ``Optional[int]``. `#3484 `_ - - Ignore done tasks when cancels pending activities on ``web.run_app`` finalization. - `#3497 `_ - - - Improved Documentation - ---------------------- - - - Add documentation for ``aiohttp.web.HTTPException``. - `#3490 `_ - - - Misc - ---- - - - `#3487 `_ - - - ---- - - - 3.5.1 (2018-12-24) - ==================== - - - Fix a regression about ``ClientSession._requote_redirect_url`` modification in debug - mode. - - 3.5.0 (2018-12-22) - ==================== - - Features - -------- - - - The library type annotations are checked in strict mode now. - - Add support for setting cookies for individual request (`#2387 - `_) - - Application.add_domain implementation (`#2809 - `_) - - The default ``app`` in the request returned by ``test_utils.make_mocked_request`` can - now have objects assigned to it and retrieved using the ``[]`` operator. (`#3174 - `_) - - Make ``request.url`` accessible when transport is closed. (`#3177 - `_) - - Add ``zlib_executor_size`` argument to ``Response`` constructor to allow compression - to run in a background executor to avoid blocking the main thread and potentially - triggering health check failures. (`#3205 - `_) - - Enable users to set ``ClientTimeout`` in ``aiohttp.request`` (`#3213 - `_) - - Don't raise a warning if ``NETRC`` environment variable is not set and ``~/.netrc`` - file doesn't exist. (`#3267 `_) - - Add default logging handler to web.run_app If the ``Application.debug``` flag is set - and the default logger ``aiohttp.access`` is used, access logs will now be output - using a *stderr* ``StreamHandler`` if no handlers are attached. Furthermore, if the - default logger has no log level set, the log level will be set to ``DEBUG``. (`#3324 - `_) - - Add method argument to ``session.ws_connect()``. Sometimes server API requires a - different HTTP method for WebSocket connection establishment. For example, ``Docker - exec`` needs POST. (`#3378 `_) - - Create a task per request handling. (`#3406 - `_) - - - Bugfixes - -------- - - - Enable passing ``access_log_class`` via ``handler_args`` (`#3158 - `_) - - Return empty bytes with end-of-chunk marker in empty stream reader. (`#3186 - `_) - - Accept ``CIMultiDictProxy`` instances for ``headers`` argument in ``web.Response`` - constructor. (`#3207 `_) - - Don't uppercase HTTP method in parser (`#3233 - `_) - - Make method match regexp RFC-7230 compliant (`#3235 - `_) - - Add ``app.pre_frozen`` state to properly handle startup signals in - sub-applications. (`#3237 `_) - - Enhanced parsing and validation of helpers.BasicAuth.decode. (`#3239 - `_) - - Change imports from collections module in preparation for 3.8. (`#3258 - `_) - - Ensure Host header is added first to ClientRequest to better replicate browser (`#3265 - `_) - - Fix forward compatibility with Python 3.8: importing ABCs directly from the - collections module will not be supported anymore. (`#3273 - `_) - - Keep the query string by ``normalize_path_middleware``. (`#3278 - `_) - - Fix missing parameter ``raise_for_status`` for aiohttp.request() (`#3290 - `_) - - Bracket IPv6 addresses in the HOST header (`#3304 - `_) - - Fix default message for server ping and pong frames. (`#3308 - `_) - - Fix tests/test_connector.py typo and tests/autobahn/server.py duplicate loop - def. (`#3337 `_) - - Fix false-negative indicator end_of_HTTP_chunk in StreamReader.readchunk function - (`#3361 `_) - - Release HTTP response before raising status exception (`#3364 - `_) - - Fix task cancellation when ``sendfile()`` syscall is used by static file - handling. (`#3383 `_) - - Fix stack trace for ``asyncio.TimeoutError`` which was not logged, when it is caught - in the handler. (`#3414 `_) - - - Improved Documentation - ---------------------- - - - Improve documentation of ``Application.make_handler`` parameters. (`#3152 - `_) - - Fix BaseRequest.raw_headers doc. (`#3215 - `_) - - Fix typo in TypeError exception reason in ``web.Application._handle`` (`#3229 - `_) - - Make server access log format placeholder %b documentation reflect - behavior and docstring. (`#3307 `_) - - - Deprecations and Removals - ------------------------- - - - Deprecate modification of ``session.requote_redirect_url`` (`#2278 - `_) - - Deprecate ``stream.unread_data()`` (`#3260 - `_) - - Deprecated use of boolean in ``resp.enable_compression()`` (`#3318 - `_) - - Encourage creation of aiohttp public objects inside a coroutine (`#3331 - `_) - - Drop dead ``Connection.detach()`` and ``Connection.writer``. Both methods were broken - for more than 2 years. (`#3358 `_) - - Deprecate ``app.loop``, ``request.loop``, ``client.loop`` and ``connector.loop`` - properties. (`#3374 `_) - - Deprecate explicit debug argument. Use asyncio debug mode instead. (`#3381 - `_) - - Deprecate body parameter in HTTPException (and derived classes) constructor. (`#3385 - `_) - - Deprecate bare connector close, use ``async with connector:`` and ``await - connector.close()`` instead. (`#3417 - `_) - - Deprecate obsolete ``read_timeout`` and ``conn_timeout`` in ``ClientSession`` - constructor. (`#3438 `_) - - - Misc - ---- - - - #3341, #3351 -Platform: UNKNOWN -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Intended Audience :: Developers -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Development Status :: 5 - Production/Stable -Classifier: Operating System :: POSIX -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: Microsoft :: Windows -Classifier: Topic :: Internet :: WWW/HTTP -Classifier: Framework :: AsyncIO -Requires-Python: >=3.6 -Provides-Extra: speedups diff --git a/third_party/python/aiohttp/aiohttp.egg-info/SOURCES.txt b/third_party/python/aiohttp/aiohttp.egg-info/SOURCES.txt deleted file mode 100644 index e006e2a0d816..000000000000 --- a/third_party/python/aiohttp/aiohttp.egg-info/SOURCES.txt +++ /dev/null @@ -1,246 +0,0 @@ -CHANGES.rst -CONTRIBUTORS.txt -LICENSE.txt -MANIFEST.in -Makefile -README.rst -pyproject.toml -setup.cfg -setup.py -aiohttp/__init__.py -aiohttp/_cparser.pxd -aiohttp/_find_header.c -aiohttp/_find_header.h -aiohttp/_find_header.pxd -aiohttp/_frozenlist.c -aiohttp/_frozenlist.pyx -aiohttp/_headers.pxi -aiohttp/_helpers.c -aiohttp/_helpers.pyi -aiohttp/_helpers.pyx -aiohttp/_http_parser.c -aiohttp/_http_parser.pyx -aiohttp/_http_writer.c -aiohttp/_http_writer.pyx -aiohttp/_websocket.c -aiohttp/_websocket.pyx -aiohttp/abc.py -aiohttp/base_protocol.py -aiohttp/client.py -aiohttp/client_exceptions.py -aiohttp/client_proto.py -aiohttp/client_reqrep.py -aiohttp/client_ws.py -aiohttp/connector.py -aiohttp/cookiejar.py -aiohttp/formdata.py -aiohttp/frozenlist.py -aiohttp/frozenlist.pyi -aiohttp/hdrs.py -aiohttp/helpers.py -aiohttp/http.py -aiohttp/http_exceptions.py -aiohttp/http_parser.py -aiohttp/http_websocket.py -aiohttp/http_writer.py -aiohttp/locks.py -aiohttp/log.py -aiohttp/multipart.py -aiohttp/payload.py -aiohttp/payload_streamer.py -aiohttp/py.typed -aiohttp/pytest_plugin.py -aiohttp/resolver.py -aiohttp/signals.py -aiohttp/signals.pyi -aiohttp/streams.py -aiohttp/tcp_helpers.py -aiohttp/test_utils.py -aiohttp/tracing.py -aiohttp/typedefs.py -aiohttp/web.py -aiohttp/web_app.py -aiohttp/web_exceptions.py -aiohttp/web_fileresponse.py -aiohttp/web_log.py -aiohttp/web_middlewares.py -aiohttp/web_protocol.py -aiohttp/web_request.py -aiohttp/web_response.py -aiohttp/web_routedef.py -aiohttp/web_runner.py -aiohttp/web_server.py -aiohttp/web_urldispatcher.py -aiohttp/web_ws.py -aiohttp/worker.py -aiohttp.egg-info/PKG-INFO -aiohttp.egg-info/SOURCES.txt -aiohttp.egg-info/dependency_links.txt -aiohttp.egg-info/requires.txt -aiohttp.egg-info/top_level.txt -aiohttp/.hash/_cparser.pxd.hash -aiohttp/.hash/_find_header.pxd.hash -aiohttp/.hash/_frozenlist.pyx.hash -aiohttp/.hash/_helpers.pyi.hash -aiohttp/.hash/_helpers.pyx.hash -aiohttp/.hash/_http_parser.pyx.hash -aiohttp/.hash/_http_writer.pyx.hash -aiohttp/.hash/_websocket.pyx.hash -aiohttp/.hash/frozenlist.pyi.hash -aiohttp/.hash/hdrs.py.hash -aiohttp/.hash/signals.pyi.hash -docs/Makefile -docs/abc.rst -docs/aiohttp-icon.svg -docs/aiohttp-plain.svg -docs/built_with.rst -docs/changes.rst -docs/client.rst -docs/client_advanced.rst -docs/client_quickstart.rst -docs/client_reference.rst -docs/conf.py -docs/contributing.rst -docs/deployment.rst -docs/essays.rst -docs/external.rst -docs/faq.rst -docs/favicon.ico -docs/glossary.rst -docs/http_request_lifecycle.rst -docs/index.rst -docs/logging.rst -docs/make.bat -docs/migration_to_2xx.rst -docs/misc.rst -docs/multipart.rst -docs/multipart_reference.rst -docs/new_router.rst -docs/old-logo.png -docs/old-logo.svg -docs/powered_by.rst -docs/signals.rst -docs/spelling_wordlist.txt -docs/streams.rst -docs/structures.rst -docs/testing.rst -docs/third_party.rst -docs/tracing_reference.rst -docs/utilities.rst -docs/web.rst -docs/web_advanced.rst -docs/web_lowlevel.rst -docs/web_quickstart.rst -docs/web_reference.rst -docs/websocket_utilities.rst -docs/whats_new_1_1.rst -docs/whats_new_3_0.rst -docs/_static/aiohttp-icon-128x128.png -examples/background_tasks.py -examples/cli_app.py -examples/client_auth.py -examples/client_json.py -examples/client_ws.py -examples/curl.py -examples/fake_server.py -examples/lowlevel_srv.py -examples/server.crt -examples/server.csr -examples/server.key -examples/server_simple.py -examples/static_files.py -examples/web_classview.py -examples/web_cookies.py -examples/web_rewrite_headers_middleware.py -examples/web_srv.py -examples/web_srv_route_deco.py -examples/web_srv_route_table.py -examples/web_ws.py -examples/websocket.html -examples/legacy/crawl.py -examples/legacy/srv.py -examples/legacy/tcp_protocol_parser.py -tests/aiohttp.jpg -tests/aiohttp.png -tests/conftest.py -tests/data.unknown_mime_type -tests/data.zero_bytes -tests/hello.txt.gz -tests/test_base_protocol.py -tests/test_classbasedview.py -tests/test_client_connection.py -tests/test_client_exceptions.py -tests/test_client_fingerprint.py -tests/test_client_functional.py -tests/test_client_proto.py -tests/test_client_request.py -tests/test_client_response.py -tests/test_client_session.py -tests/test_client_ws.py -tests/test_client_ws_functional.py -tests/test_connector.py -tests/test_cookiejar.py -tests/test_flowcontrol_streams.py -tests/test_formdata.py -tests/test_frozenlist.py -tests/test_helpers.py -tests/test_http_exceptions.py -tests/test_http_parser.py -tests/test_http_writer.py -tests/test_locks.py -tests/test_loop.py -tests/test_multipart.py -tests/test_multipart_helpers.py -tests/test_payload.py -tests/test_proxy.py -tests/test_proxy_functional.py -tests/test_pytest_plugin.py -tests/test_resolver.py -tests/test_route_def.py -tests/test_run_app.py -tests/test_signals.py -tests/test_streams.py -tests/test_tcp_helpers.py -tests/test_test_utils.py -tests/test_tracing.py -tests/test_urldispatch.py -tests/test_web_app.py -tests/test_web_cli.py -tests/test_web_exceptions.py -tests/test_web_functional.py -tests/test_web_log.py -tests/test_web_middleware.py -tests/test_web_protocol.py -tests/test_web_request.py -tests/test_web_request_handler.py -tests/test_web_response.py -tests/test_web_runner.py -tests/test_web_sendfile.py -tests/test_web_sendfile_functional.py -tests/test_web_server.py -tests/test_web_urldispatcher.py -tests/test_web_websocket.py -tests/test_web_websocket_functional.py -tests/test_websocket_handshake.py -tests/test_websocket_parser.py -tests/test_websocket_writer.py -tests/test_worker.py -tests/autobahn/client.py -tests/autobahn/fuzzingclient.json -tests/autobahn/fuzzingserver.json -tests/autobahn/server.py -vendor/http-parser/.git -vendor/http-parser/.gitignore -vendor/http-parser/.mailmap -vendor/http-parser/.travis.yml -vendor/http-parser/AUTHORS -vendor/http-parser/LICENSE-MIT -vendor/http-parser/Makefile -vendor/http-parser/README.md -vendor/http-parser/bench.c -vendor/http-parser/http_parser.c -vendor/http-parser/http_parser.gyp -vendor/http-parser/http_parser.h -vendor/http-parser/test.c -vendor/http-parser/contrib/parsertrace.c -vendor/http-parser/contrib/url_parser.c \ No newline at end of file diff --git a/third_party/python/aiohttp/aiohttp.egg-info/dependency_links.txt b/third_party/python/aiohttp/aiohttp.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/aiohttp/aiohttp.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/aiohttp/aiohttp.egg-info/requires.txt b/third_party/python/aiohttp/aiohttp.egg-info/requires.txt deleted file mode 100644 index 746f3f8655e4..000000000000 --- a/third_party/python/aiohttp/aiohttp.egg-info/requires.txt +++ /dev/null @@ -1,14 +0,0 @@ -attrs>=17.3.0 -chardet<5.0,>=2.0 -multidict<7.0,>=4.5 -async_timeout<4.0,>=3.0 -yarl<2.0,>=1.0 -typing_extensions>=3.6.5 - -[:python_version < "3.7"] -idna-ssl>=1.0 - -[speedups] -aiodns -brotlipy -cchardet diff --git a/third_party/python/aiohttp/aiohttp.egg-info/top_level.txt b/third_party/python/aiohttp/aiohttp.egg-info/top_level.txt deleted file mode 100644 index ee4ba4f3d739..000000000000 --- a/third_party/python/aiohttp/aiohttp.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -aiohttp diff --git a/third_party/python/coverage/coverage.egg-info/PKG-INFO b/third_party/python/coverage/coverage.egg-info/PKG-INFO deleted file mode 100644 index 181e84b15c42..000000000000 --- a/third_party/python/coverage/coverage.egg-info/PKG-INFO +++ /dev/null @@ -1,187 +0,0 @@ -Metadata-Version: 2.1 -Name: coverage -Version: 5.1 -Summary: Code coverage measurement for Python -Home-page: https://github.com/nedbat/coveragepy -Author: Ned Batchelder and 131 others -Author-email: ned@nedbatchelder.com -License: Apache 2.0 -Project-URL: Documentation, https://coverage.readthedocs.io -Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=pypi -Project-URL: Issues, https://github.com/nedbat/coveragepy/issues -Description: .. 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 - =========== - - Code coverage testing for Python. - - | |license| |versions| |status| - | |ci-status| |win-ci-status| |docs| |codecov| - | |kit| |format| |repos| - | |stars| |forks| |contributors| - | |tidelift| |twitter-coveragepy| |twitter-nedbat| - - Coverage.py measures code coverage, typically during test execution. It uses - the code analysis tools and tracing hooks provided in the Python standard - library to determine which lines are executable, and which have been executed. - - Coverage.py runs on many versions of Python: - - * CPython 2.7. - * CPython 3.5 through 3.9 alpha 4. - * PyPy2 7.3.0 and PyPy3 7.3.0. - - Documentation is on `Read the Docs`_. Code repository and issue tracker are on - `GitHub`_. - - .. _Read the Docs: https://coverage.readthedocs.io/ - .. _GitHub: https://github.com/nedbat/coveragepy - - - **New in 5.0:** SQLite data storage, JSON report, contexts, relative filenames, - dropped support for Python 2.6, 3.3 and 3.4. - - - For Enterprise - -------------- - - .. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png - :width: 75 - :alt: Tidelift - :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme - - .. list-table:: - :widths: 10 100 - - * - |tideliftlogo| - - `Available as part of the Tidelift Subscription. `_ - Coverage and thousands of other packages are working with - Tidelift to deliver one enterprise subscription that covers all of the open - source you use. If you want the flexibility of open source and the confidence - of commercial-grade software, this is for you. - `Learn more. `_ - - - Getting Started - --------------- - - See the `Quick Start section`_ of the docs. - - .. _Quick Start section: https://coverage.readthedocs.io/#quick-start - - - Change history - -------------- - - The complete history of changes is on the `change history page`_. - - .. _change history page: https://coverage.readthedocs.io/en/latest/changes.html - - - Contributing - ------------ - - See the `Contributing section`_ of the docs. - - .. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html - - - Security - -------- - - To report a security vulnerability, please use the `Tidelift security - contact`_. Tidelift will coordinate the fix and disclosure. - - .. _Tidelift security contact: https://tidelift.com/security - - - License - ------- - - Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_. - - .. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 - .. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - - - .. |ci-status| image:: https://travis-ci.com/nedbat/coveragepy.svg?branch=master - :target: https://travis-ci.com/nedbat/coveragepy - :alt: Build status - .. |win-ci-status| image:: https://ci.appveyor.com/api/projects/status/kmeqpdje7h9r6vsf/branch/master?svg=true - :target: https://ci.appveyor.com/project/nedbat/coveragepy - :alt: Windows build status - .. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat - :target: https://coverage.readthedocs.io/ - :alt: Documentation - .. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master - :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master - :alt: Requirements status - .. |kit| image:: https://badge.fury.io/py/coverage.svg - :target: https://pypi.org/project/coverage/ - :alt: PyPI status - .. |format| image:: https://img.shields.io/pypi/format/coverage.svg - :target: https://pypi.org/project/coverage/ - :alt: Kit format - .. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg - :target: https://pypi.org/project/coverage/ - :alt: Weekly PyPI downloads - .. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072 - :target: https://pypi.org/project/coverage/ - :alt: Python versions supported - .. |status| image:: https://img.shields.io/pypi/status/coverage.svg - :target: https://pypi.org/project/coverage/ - :alt: Package stability - .. |license| image:: https://img.shields.io/pypi/l/coverage.svg - :target: https://pypi.org/project/coverage/ - :alt: License - .. |codecov| image:: https://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2 - :target: https://codecov.io/github/nedbat/coveragepy?branch=master - :alt: Coverage! - .. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg - :target: https://repology.org/metapackage/python:coverage/versions - :alt: Packaging status - .. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage - :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme - :alt: Tidelift - .. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github - :target: https://github.com/nedbat/coveragepy/stargazers - :alt: Github stars - .. |forks| image:: https://img.shields.io/github/forks/nedbat/coveragepy.svg?logo=github - :target: https://github.com/nedbat/coveragepy/network/members - :alt: Github forks - .. |contributors| image:: https://img.shields.io/github/contributors/nedbat/coveragepy.svg?logo=github - :target: https://github.com/nedbat/coveragepy/graphs/contributors - :alt: Contributors - .. |twitter-coveragepy| image:: https://img.shields.io/twitter/follow/coveragepy.svg?label=coveragepy&style=flat&logo=twitter&logoColor=4FADFF - :target: https://twitter.com/coveragepy - :alt: coverage.py on Twitter - .. |twitter-nedbat| image:: https://img.shields.io/twitter/follow/nedbat.svg?label=nedbat&style=flat&logo=twitter&logoColor=4FADFF - :target: https://twitter.com/nedbat - :alt: nedbat on Twitter - -Keywords: code coverage testing -Platform: UNKNOWN -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Quality Assurance -Classifier: Topic :: Software Development :: Testing -Classifier: Development Status :: 5 - Production/Stable -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4 -Description-Content-Type: text/x-rst -Provides-Extra: toml diff --git a/third_party/python/coverage/coverage.egg-info/SOURCES.txt b/third_party/python/coverage/coverage.egg-info/SOURCES.txt deleted file mode 100644 index fb09ab389f9b..000000000000 --- a/third_party/python/coverage/coverage.egg-info/SOURCES.txt +++ /dev/null @@ -1,287 +0,0 @@ -.editorconfig -.readthedocs.yml -.travis.yml -CHANGES.rst -CONTRIBUTORS.txt -LICENSE.txt -MANIFEST.in -Makefile -NOTICE.txt -README.rst -__main__.py -appveyor.yml -howto.txt -igor.py -metacov.ini -pylintrc -setup.cfg -setup.py -tox.ini -tox_wheels.ini -ci/README.txt -ci/download_appveyor.py -ci/install.ps1 -ci/manylinux.sh -ci/run_with_env.cmd -ci/upload_relnotes.py -coverage/__init__.py -coverage/__main__.py -coverage/annotate.py -coverage/backunittest.py -coverage/backward.py -coverage/bytecode.py -coverage/cmdline.py -coverage/collector.py -coverage/config.py -coverage/context.py -coverage/control.py -coverage/data.py -coverage/debug.py -coverage/disposition.py -coverage/env.py -coverage/execfile.py -coverage/files.py -coverage/html.py -coverage/inorout.py -coverage/jsonreport.py -coverage/misc.py -coverage/multiproc.py -coverage/numbits.py -coverage/optional.py -coverage/parser.py -coverage/phystokens.py -coverage/plugin.py -coverage/plugin_support.py -coverage/python.py -coverage/pytracer.py -coverage/report.py -coverage/results.py -coverage/sqldata.py -coverage/summary.py -coverage/templite.py -coverage/tomlconfig.py -coverage/version.py -coverage/xmlreport.py -coverage.egg-info/PKG-INFO -coverage.egg-info/SOURCES.txt -coverage.egg-info/dependency_links.txt -coverage.egg-info/entry_points.txt -coverage.egg-info/not-zip-safe -coverage.egg-info/requires.txt -coverage.egg-info/top_level.txt -coverage/ctracer/datastack.c -coverage/ctracer/datastack.h -coverage/ctracer/filedisp.c -coverage/ctracer/filedisp.h -coverage/ctracer/module.c -coverage/ctracer/stats.h -coverage/ctracer/tracer.c -coverage/ctracer/tracer.h -coverage/ctracer/util.h -coverage/fullcoverage/encodings.py -coverage/htmlfiles/coverage_html.js -coverage/htmlfiles/index.html -coverage/htmlfiles/jquery.ba-throttle-debounce.min.js -coverage/htmlfiles/jquery.hotkeys.js -coverage/htmlfiles/jquery.isonscreen.js -coverage/htmlfiles/jquery.min.js -coverage/htmlfiles/jquery.tablesorter.min.js -coverage/htmlfiles/keybd_closed.png -coverage/htmlfiles/keybd_open.png -coverage/htmlfiles/pyfile.html -coverage/htmlfiles/style.css -coverage/htmlfiles/style.scss -doc/api.rst -doc/api_coverage.rst -doc/api_coveragedata.rst -doc/api_module.rst -doc/api_plugin.rst -doc/branch.rst -doc/changes.rst -doc/check_copied_from.py -doc/cmd.rst -doc/conf.py -doc/config.rst -doc/contexts.rst -doc/contributing.rst -doc/dbschema.rst -doc/dict.txt -doc/excluding.rst -doc/faq.rst -doc/howitworks.rst -doc/index.rst -doc/install.rst -doc/plugins.rst -doc/python-coverage.1.txt -doc/requirements.pip -doc/sleepy.rst -doc/source.rst -doc/subprocess.rst -doc/trouble.rst -doc/whatsnew5x.rst -doc/_static/coverage.css -doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png -doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png -doc/media/sleepy-snake-600.png -doc/media/sleepy-snake-circle-150.png -doc/sample_html/keybd_closed.png -doc/sample_html/keybd_open.png -requirements/ci.pip -requirements/dev.pip -requirements/pytest.pip -requirements/tox.pip -requirements/wheel.pip -tests/__init__.py -tests/conftest.py -tests/coveragetest.py -tests/covmodzip1.py -tests/goldtest.py -tests/helpers.py -tests/osinfo.py -tests/plugin1.py -tests/plugin2.py -tests/plugin_config.py -tests/stress_phystoken.tok -tests/stress_phystoken_dos.tok -tests/test_annotate.py -tests/test_api.py -tests/test_arcs.py -tests/test_backward.py -tests/test_cmdline.py -tests/test_collector.py -tests/test_concurrency.py -tests/test_config.py -tests/test_context.py -tests/test_coverage.py -tests/test_data.py -tests/test_debug.py -tests/test_execfile.py -tests/test_filereporter.py -tests/test_files.py -tests/test_html.py -tests/test_json.py -tests/test_misc.py -tests/test_numbits.py -tests/test_oddball.py -tests/test_parser.py -tests/test_phystokens.py -tests/test_plugins.py -tests/test_process.py -tests/test_python.py -tests/test_results.py -tests/test_setup.py -tests/test_summary.py -tests/test_templite.py -tests/test_testing.py -tests/test_version.py -tests/test_xml.py -tests/eggsrc/setup.py -tests/eggsrc/egg1/__init__.py -tests/eggsrc/egg1/egg1.py -tests/gold/README.rst -tests/gold/annotate/anno_dir/a___init__.py,cover -tests/gold/annotate/anno_dir/a_a.py,cover -tests/gold/annotate/anno_dir/b___init__.py,cover -tests/gold/annotate/anno_dir/b_b.py,cover -tests/gold/annotate/anno_dir/multi.py,cover -tests/gold/annotate/annotate/white.py,cover -tests/gold/annotate/encodings/utf8.py,cover -tests/gold/annotate/multi/multi.py,cover -tests/gold/annotate/multi/a/__init__.py,cover -tests/gold/annotate/multi/a/a.py,cover -tests/gold/annotate/multi/b/__init__.py,cover -tests/gold/annotate/multi/b/b.py,cover -tests/gold/html/Makefile -tests/gold/html/a/a_py.html -tests/gold/html/a/index.html -tests/gold/html/b_branch/b_py.html -tests/gold/html/b_branch/index.html -tests/gold/html/bom/bom_py.html -tests/gold/html/bom/index.html -tests/gold/html/bom/2/bom_py.html -tests/gold/html/bom/2/index.html -tests/gold/html/isolatin1/index.html -tests/gold/html/isolatin1/isolatin1_py.html -tests/gold/html/omit_1/index.html -tests/gold/html/omit_1/m1_py.html -tests/gold/html/omit_1/m2_py.html -tests/gold/html/omit_1/m3_py.html -tests/gold/html/omit_1/main_py.html -tests/gold/html/omit_2/index.html -tests/gold/html/omit_2/m2_py.html -tests/gold/html/omit_2/m3_py.html -tests/gold/html/omit_2/main_py.html -tests/gold/html/omit_3/index.html -tests/gold/html/omit_3/m3_py.html -tests/gold/html/omit_3/main_py.html -tests/gold/html/omit_4/index.html -tests/gold/html/omit_4/m1_py.html -tests/gold/html/omit_4/m3_py.html -tests/gold/html/omit_4/main_py.html -tests/gold/html/omit_5/index.html -tests/gold/html/omit_5/m1_py.html -tests/gold/html/omit_5/main_py.html -tests/gold/html/other/blah_blah_other_py.html -tests/gold/html/other/here_py.html -tests/gold/html/other/index.html -tests/gold/html/partial/index.html -tests/gold/html/partial/partial_py.html -tests/gold/html/styled/a_py.html -tests/gold/html/styled/extra.css -tests/gold/html/styled/index.html -tests/gold/html/styled/style.css -tests/gold/html/support/coverage_html.js -tests/gold/html/support/jquery.ba-throttle-debounce.min.js -tests/gold/html/support/jquery.hotkeys.js -tests/gold/html/support/jquery.isonscreen.js -tests/gold/html/support/jquery.min.js -tests/gold/html/support/jquery.tablesorter.min.js -tests/gold/html/support/keybd_closed.png -tests/gold/html/support/keybd_open.png -tests/gold/html/support/style.css -tests/gold/html/unicode/index.html -tests/gold/html/unicode/unicode_py.html -tests/gold/xml/x_xml/coverage.xml -tests/gold/xml/y_xml_branch/coverage.xml -tests/js/index.html -tests/js/tests.js -tests/modules/covmod1.py -tests/modules/runmod1.py -tests/modules/usepkgs.py -tests/modules/aa/__init__.py -tests/modules/aa/afile.odd.py -tests/modules/aa/afile.py -tests/modules/aa/zfile.py -tests/modules/aa/bb/__init__.py -tests/modules/aa/bb/bfile.odd.py -tests/modules/aa/bb/bfile.py -tests/modules/aa/bb.odd/bfile.py -tests/modules/aa/bb/cc/__init__.py -tests/modules/aa/bb/cc/cfile.py -tests/modules/namespace_420/sub1/__init__.py -tests/modules/pkg1/__init__.py -tests/modules/pkg1/__main__.py -tests/modules/pkg1/p1a.py -tests/modules/pkg1/p1b.py -tests/modules/pkg1/p1c.py -tests/modules/pkg1/runmod2.py -tests/modules/pkg1/sub/__init__.py -tests/modules/pkg1/sub/__main__.py -tests/modules/pkg1/sub/ps1a.py -tests/modules/pkg1/sub/runmod3.py -tests/modules/pkg2/__init__.py -tests/modules/pkg2/p2a.py -tests/modules/pkg2/p2b.py -tests/modules/plugins/__init__.py -tests/modules/plugins/a_plugin.py -tests/modules/plugins/another.py -tests/modules/process_test/__init__.py -tests/modules/process_test/try_execfile.py -tests/moremodules/namespace_420/sub2/__init__.py -tests/moremodules/othermods/__init__.py -tests/moremodules/othermods/othera.py -tests/moremodules/othermods/otherb.py -tests/moremodules/othermods/sub/__init__.py -tests/moremodules/othermods/sub/osa.py -tests/moremodules/othermods/sub/osb.py -tests/qunit/jquery.tmpl.min.js \ No newline at end of file diff --git a/third_party/python/coverage/coverage.egg-info/dependency_links.txt b/third_party/python/coverage/coverage.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/coverage/coverage.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/coverage/coverage.egg-info/entry_points.txt b/third_party/python/coverage/coverage.egg-info/entry_points.txt deleted file mode 100644 index 58d31949dedd..000000000000 --- a/third_party/python/coverage/coverage.egg-info/entry_points.txt +++ /dev/null @@ -1,5 +0,0 @@ -[console_scripts] -coverage = coverage.cmdline:main -coverage-3.8 = coverage.cmdline:main -coverage3 = coverage.cmdline:main - diff --git a/third_party/python/coverage/coverage.egg-info/not-zip-safe b/third_party/python/coverage/coverage.egg-info/not-zip-safe deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/coverage/coverage.egg-info/not-zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/coverage/coverage.egg-info/requires.txt b/third_party/python/coverage/coverage.egg-info/requires.txt deleted file mode 100644 index c694f11c9d76..000000000000 --- a/third_party/python/coverage/coverage.egg-info/requires.txt +++ /dev/null @@ -1,3 +0,0 @@ - -[toml] -toml diff --git a/third_party/python/coverage/coverage.egg-info/top_level.txt b/third_party/python/coverage/coverage.egg-info/top_level.txt deleted file mode 100644 index 4ebc8aea50e0..000000000000 --- a/third_party/python/coverage/coverage.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -coverage diff --git a/third_party/python/esprima/esprima.egg-info/PKG-INFO b/third_party/python/esprima/esprima.egg-info/PKG-INFO deleted file mode 100644 index c2fee6ace6f6..000000000000 --- a/third_party/python/esprima/esprima.egg-info/PKG-INFO +++ /dev/null @@ -1,143 +0,0 @@ -Metadata-Version: 1.1 -Name: esprima -Version: 4.0.1 -Summary: ECMAScript parsing infrastructure for multipurpose analysis in Python -Home-page: https://github.com/Kronuz/esprima-python -Author: German M. Bravo (Kronuz) -Author-email: german.mb@gmail.com -License: BSD License -Description: |Donate| |PyPI Version| |PyPI License| |PyPI Format| |PyPI Status| - - **Esprima** (`esprima.org `__, BSD license) is a - high performance, standard-compliant - `ECMAScript `__ - parser officially written in ECMAScript (also popularly known as - `JavaScript `__) and ported to - Python. Esprima is created and maintained by `Ariya - Hidayat `__, with the help of `many - contributors `__. - - Python port is a line-by-line manual translation and was created and is - maintained by `German Mendez Bravo - (Kronuz) `__. - - Features - ~~~~~~~~ - - - Full support for ECMAScript 2017 (`ECMA-262 8th - Edition `__) - - Sensible `syntax tree - format `__ as - standardized by `ESTree project `__ - - Experimental support for `JSX `__, a - syntax extension for `React `__ - - Optional tracking of syntax node location (index-based and - line-column) - - `Heavily tested `__ (~1500 `unit - tests `__ - with `full code - coverage `__) - - Installation - ~~~~~~~~~~~~ - - .. code:: shell - - pip install esprima - - API - ~~~ - - Esprima can be used to perform `lexical - analysis `__ - (tokenization) or `syntactic - analysis `__ (parsing) of a - JavaScript program. - - A simple example: - - .. code:: javascript - - >>> import esprima - >>> program = 'const answer = 42' - - >>> esprima.tokenize(program) - [{ - type: "Keyword", - value: "const" - }, { - type: "Identifier", - value: "answer" - }, { - type: "Punctuator", - value: "=" - }, { - type: "Numeric", - value: "42" - }] - - >>> esprima.parseScript(program) - { - body: [ - { - kind: "const", - declarations: [ - { - init: { - raw: "42", - type: "Literal", - value: 42 - }, - type: "VariableDeclarator", - id: { - type: "Identifier", - name: "answer" - } - } - ], - type: "VariableDeclaration" - } - ], - type: "Program", - sourceType: "script" - } - - For more information, please read the `complete - documentation `__. - - .. |Donate| image:: https://img.shields.io/badge/Donate-PayPal-green.svg - :target: https://www.paypal.me/Kronuz/25 - .. |PyPI Version| image:: https://img.shields.io/pypi/v/esprima.svg - :target: https://pypi.python.org/pypi/esprima - .. |PyPI License| image:: https://img.shields.io/pypi/l/esprima.svg - :target: https://pypi.python.org/pypi/esprima - .. |PyPI Wheel| image:: https://img.shields.io/pypi/wheel/esprima.svg - :target: https://pypi.python.org/pypi/esprima - .. |PyPI Format| image:: https://img.shields.io/pypi/format/esprima.svg - :target: https://pypi.python.org/pypi/esprima - .. |PyPI Python Version| image:: https://img.shields.io/pypi/pyversions/esprima.svg - :target: https://pypi.python.org/pypi/esprima - .. |PyPI Implementation| image:: https://img.shields.io/pypi/implementation/esprima.svg - :target: https://pypi.python.org/pypi/esprima - .. |PyPI Status| image:: https://img.shields.io/pypi/status/esprima.svg - :target: https://pypi.python.org/pypi/esprima - .. |PyPI Downloads| image:: https://img.shields.io/pypi/dm/esprima.svg - :target: https://pypi.python.org/pypi/esprima -Keywords: esprima ecmascript javascript parser ast -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Topic :: Software Development :: Code Generators -Classifier: Topic :: Software Development :: Compilers -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Text Processing :: General -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 diff --git a/third_party/python/esprima/esprima.egg-info/SOURCES.txt b/third_party/python/esprima/esprima.egg-info/SOURCES.txt deleted file mode 100644 index 16bea37b8d2e..000000000000 --- a/third_party/python/esprima/esprima.egg-info/SOURCES.txt +++ /dev/null @@ -1,29 +0,0 @@ -README -setup.py -esprima/__init__.py -esprima/__main__.py -esprima/character.py -esprima/comment_handler.py -esprima/compat.py -esprima/error_handler.py -esprima/esprima.py -esprima/jsx_nodes.py -esprima/jsx_parser.py -esprima/jsx_syntax.py -esprima/messages.py -esprima/nodes.py -esprima/objects.py -esprima/parser.py -esprima/scanner.py -esprima/syntax.py -esprima/token.py -esprima/tokenizer.py -esprima/utils.py -esprima/visitor.py -esprima/xhtml_entities.py -esprima.egg-info/PKG-INFO -esprima.egg-info/SOURCES.txt -esprima.egg-info/dependency_links.txt -esprima.egg-info/entry_points.txt -esprima.egg-info/pbr.json -esprima.egg-info/top_level.txt \ No newline at end of file diff --git a/third_party/python/esprima/esprima.egg-info/dependency_links.txt b/third_party/python/esprima/esprima.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/esprima/esprima.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/esprima/esprima.egg-info/entry_points.txt b/third_party/python/esprima/esprima.egg-info/entry_points.txt deleted file mode 100644 index 0170557792e4..000000000000 --- a/third_party/python/esprima/esprima.egg-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -esprima = esprima.__main__:main - diff --git a/third_party/python/esprima/esprima.egg-info/pbr.json b/third_party/python/esprima/esprima.egg-info/pbr.json deleted file mode 100644 index d8e931d7dd88..000000000000 --- a/third_party/python/esprima/esprima.egg-info/pbr.json +++ /dev/null @@ -1 +0,0 @@ -{"is_release": false, "git_version": "ac65290"} \ No newline at end of file diff --git a/third_party/python/esprima/esprima.egg-info/top_level.txt b/third_party/python/esprima/esprima.egg-info/top_level.txt deleted file mode 100644 index c0ba54881edf..000000000000 --- a/third_party/python/esprima/esprima.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -esprima diff --git a/third_party/python/fluent.migrate/fluent.migrate.egg-info/PKG-INFO b/third_party/python/fluent.migrate/fluent.migrate.egg-info/PKG-INFO deleted file mode 100644 index e15a76d1d684..000000000000 --- a/third_party/python/fluent.migrate/fluent.migrate.egg-info/PKG-INFO +++ /dev/null @@ -1,18 +0,0 @@ -Metadata-Version: 2.1 -Name: fluent.migrate -Version: 0.11 -Summary: Toolchain to migrate legacy translation to Fluent. -Home-page: https://hg.mozilla.org/l10n/fluent-migration/ -Author: Mozilla -Author-email: l10n-drivers@mozilla.org -License: APL 2 -Description: UNKNOWN -Keywords: fluent,localization,l10n -Platform: UNKNOWN -Classifier: Development Status :: 3 - Alpha -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.7 -Description-Content-Type: text/markdown -Provides-Extra: hg diff --git a/third_party/python/fluent.migrate/fluent.migrate.egg-info/SOURCES.txt b/third_party/python/fluent.migrate/fluent.migrate.egg-info/SOURCES.txt deleted file mode 100644 index 6d51c427d1e6..000000000000 --- a/third_party/python/fluent.migrate/fluent.migrate.egg-info/SOURCES.txt +++ /dev/null @@ -1,23 +0,0 @@ -README.md -setup.cfg -setup.py -fluent/__init__.py -fluent.migrate.egg-info/PKG-INFO -fluent.migrate.egg-info/SOURCES.txt -fluent.migrate.egg-info/dependency_links.txt -fluent.migrate.egg-info/entry_points.txt -fluent.migrate.egg-info/requires.txt -fluent.migrate.egg-info/top_level.txt -fluent/migrate/__init__.py -fluent/migrate/_context.py -fluent/migrate/blame.py -fluent/migrate/changesets.py -fluent/migrate/context.py -fluent/migrate/errors.py -fluent/migrate/evaluator.py -fluent/migrate/helpers.py -fluent/migrate/merge.py -fluent/migrate/tool.py -fluent/migrate/transforms.py -fluent/migrate/util.py -fluent/migrate/validator.py \ No newline at end of file diff --git a/third_party/python/fluent.migrate/fluent.migrate.egg-info/dependency_links.txt b/third_party/python/fluent.migrate/fluent.migrate.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/fluent.migrate/fluent.migrate.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/fluent.migrate/fluent.migrate.egg-info/entry_points.txt b/third_party/python/fluent.migrate/fluent.migrate.egg-info/entry_points.txt deleted file mode 100644 index 7a1ebbf874ae..000000000000 --- a/third_party/python/fluent.migrate/fluent.migrate.egg-info/entry_points.txt +++ /dev/null @@ -1,4 +0,0 @@ -[console_scripts] -migrate-l10n = fluent.migrate.tool:cli -validate-l10n-recipe = fluent.migrate.validator:cli - diff --git a/third_party/python/fluent.migrate/fluent.migrate.egg-info/requires.txt b/third_party/python/fluent.migrate/fluent.migrate.egg-info/requires.txt deleted file mode 100644 index 84a38c8fe23f..000000000000 --- a/third_party/python/fluent.migrate/fluent.migrate.egg-info/requires.txt +++ /dev/null @@ -1,6 +0,0 @@ -compare-locales<9.0,>=8.1 -fluent.syntax<0.19,>=0.18.0 -six - -[hg] -python-hglib diff --git a/third_party/python/fluent.migrate/fluent.migrate.egg-info/top_level.txt b/third_party/python/fluent.migrate/fluent.migrate.egg-info/top_level.txt deleted file mode 100644 index a3582d405aef..000000000000 --- a/third_party/python/fluent.migrate/fluent.migrate.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -fluent diff --git a/third_party/python/idna-ssl/idna_ssl.egg-info/PKG-INFO b/third_party/python/idna-ssl/idna_ssl.egg-info/PKG-INFO deleted file mode 100644 index 54ea22146d07..000000000000 --- a/third_party/python/idna-ssl/idna_ssl.egg-info/PKG-INFO +++ /dev/null @@ -1,81 +0,0 @@ -Metadata-Version: 1.1 -Name: idna-ssl -Version: 1.1.0 -Summary: Patch ssl.match_hostname for Unicode(idna) domains support -Home-page: https://github.com/aio-libs/idna-ssl -Author: Victor Kovtun -Author-email: hellysmile@gmail.com -License: UNKNOWN -Description: idna-ssl - ======== - - :info: Patch ssl.match_hostname for Unicode(idna) domains support - - .. image:: https://travis-ci.com/aio-libs/idna-ssl.svg?branch=master - :target: https://travis-ci.com/aio-libs/idna-ssl - - .. image:: https://img.shields.io/pypi/v/idna_ssl.svg - :target: https://pypi.python.org/pypi/idna_ssl - - .. image:: https://codecov.io/gh/aio-libs/idna-ssl/branch/master/graph/badge.svg - :target: https://codecov.io/gh/aio-libs/idna-ssl - - Installation - ------------ - - .. code-block:: shell - - pip install idna-ssl - - Usage - ----- - - .. code-block:: python - - from idna_ssl import patch_match_hostname # noqa isort:skip - patch_match_hostname() # noqa isort:skip - - import asyncio - - import aiohttp - - URL = 'https://цфоут.мвд.рф/news/item/8065038/' - - - async def main(): - async with aiohttp.ClientSession() as session: - async with session.get(URL) as response: - print(response) - - - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) - - Motivation - ---------- - - * Here is 100% backward capability - * Related aiohttp `issue `_ - * Related Python `bug `_ - * Related Python `pull request `_ - * It is fixed (by January 27 2018) in upcoming Python 3.7, but `IDNA2008 `_ is still broken - - Thanks - ------ - - The library was donated by `Ocean S.A. `_ - - Thanks to the company for contribution. - -Keywords: ssl,Unicode,idna,match_hostname -Platform: UNKNOWN -Classifier: Development Status :: 4 - Beta -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 diff --git a/third_party/python/idna-ssl/idna_ssl.egg-info/SOURCES.txt b/third_party/python/idna-ssl/idna_ssl.egg-info/SOURCES.txt deleted file mode 100644 index 0ed8815e2859..000000000000 --- a/third_party/python/idna-ssl/idna_ssl.egg-info/SOURCES.txt +++ /dev/null @@ -1,12 +0,0 @@ -LICENSE -MANIFEST.in -README.rst -idna_ssl.py -setup.cfg -setup.py -idna_ssl.egg-info/PKG-INFO -idna_ssl.egg-info/SOURCES.txt -idna_ssl.egg-info/dependency_links.txt -idna_ssl.egg-info/not-zip-safe -idna_ssl.egg-info/requires.txt -idna_ssl.egg-info/top_level.txt \ No newline at end of file diff --git a/third_party/python/idna-ssl/idna_ssl.egg-info/dependency_links.txt b/third_party/python/idna-ssl/idna_ssl.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/idna-ssl/idna_ssl.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/idna-ssl/idna_ssl.egg-info/not-zip-safe b/third_party/python/idna-ssl/idna_ssl.egg-info/not-zip-safe deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/idna-ssl/idna_ssl.egg-info/not-zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/idna-ssl/idna_ssl.egg-info/requires.txt b/third_party/python/idna-ssl/idna_ssl.egg-info/requires.txt deleted file mode 100644 index af5772da9934..000000000000 --- a/third_party/python/idna-ssl/idna_ssl.egg-info/requires.txt +++ /dev/null @@ -1 +0,0 @@ -idna>=2.0 diff --git a/third_party/python/idna-ssl/idna_ssl.egg-info/top_level.txt b/third_party/python/idna-ssl/idna_ssl.egg-info/top_level.txt deleted file mode 100644 index 15da36aac121..000000000000 --- a/third_party/python/idna-ssl/idna_ssl.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -idna_ssl diff --git a/third_party/python/jsmin/jsmin.egg-info/PKG-INFO b/third_party/python/jsmin/jsmin.egg-info/PKG-INFO deleted file mode 100644 index 27f88212e575..000000000000 --- a/third_party/python/jsmin/jsmin.egg-info/PKG-INFO +++ /dev/null @@ -1,117 +0,0 @@ -Metadata-Version: 1.1 -Name: jsmin -Version: 2.1.0 -Summary: JavaScript minifier. -PLEASE UPDATE TO VERSION >= 2.0.6. Older versions have a serious bug related to comments. -Home-page: https://bitbucket.org/dcs/jsmin/ -Author: Tikitu de Jager -Author-email: tikitu+jsmin@logophile.org -License: MIT License -Description: ===== - jsmin - ===== - - JavaScript minifier. - - Usage - ===== - - .. code:: python - - from jsmin import jsmin - with open('myfile.js') as js_file: - minified = jsmin(js_file.read()) - - You can run it as a commandline tool also:: - - python -m jsmin myfile.js - - As yet, ``jsmin`` makes no attempt to be compatible with - `ECMAScript 6 / ES.next / Harmony `_. - If you're using it on Harmony code, though, you might find the ``quote_chars`` - parameter useful: - - .. code:: python - - from jsmin import jsmin - with open('myfile.js') as js_file: - minified = jsmin(js_file.read(), quote_chars="'\"`") - - - Where to get it - =============== - - * install the package `from pypi `_ - * get the latest release `from the stable branch on bitbucket `_ - * get the development version `from the default branch on bitbucket `_ - - Contributing - ============ - - `Issues `_ and `Pull requests `_ - will be gratefully received on Bitbucket. Pull requests on github are great too, but the issue tracker lives on - bitbucket. - - If possible, please make separate pull requests for tests and for code: tests will be committed on the stable branch - (which tracks the latest released version) while code will go to default by, erm, default. - - Unless you request otherwise, your Bitbucket identity will be added to the contributor's list below; if you prefer a - different name feel free to add it in your pull request instead. (If you prefer not to be mentioned you'll have to let - the maintainer know somehow.) - - Build/test status - ================= - - Both default and stable branches are tested with Travis: https://travis-ci.org/tikitu/jsmin - - Stable (latest released version plus any new tests) is tested against CPython 2.6, 2.7, 3.2, and 3.3. - Currently: - - .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=ghstable - - If stable is failing that means there's a new test that fails on *the latest released version on pypi*, with no fix yet - released. - - Default (development version, might be ahead of latest released version) is tested against CPython 2.6, 2.7, 3.2, and - 3.3. Currently: - - .. image:: https://travis-ci.org/tikitu/jsmin.png?branch=master - - If default is failing don't use it, but as long as stable is passing the pypi release should be ok. - - Contributors (chronological commit order) - ========================================= - - * `Dave St.Germain `_ (original author) - * `Hans weltar `_ - * `Tikitu de Jager `_ (current maintainer) - * https://bitbucket.org/rennat - * `Nick Alexander `_ - - Changelog - ========= - - v2.1.0 (2014-12-24) Tikitu de Jager - ----------------------------------- - - * First changelog entries; see README.rst for prior contributors. - - * Expose quote_chars parameter to provide just enough unofficial Harmony - support to be useful. - - -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Web Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Software Development :: Pre-processors -Classifier: Topic :: Text Processing :: Filters diff --git a/third_party/python/jsmin/jsmin.egg-info/SOURCES.txt b/third_party/python/jsmin/jsmin.egg-info/SOURCES.txt deleted file mode 100644 index ae208f4b6065..000000000000 --- a/third_party/python/jsmin/jsmin.egg-info/SOURCES.txt +++ /dev/null @@ -1,13 +0,0 @@ -CHANGELOG.txt -LICENSE.txt -MANIFEST.in -README.rst -setup.cfg -setup.py -jsmin/__init__.py -jsmin/__main__.py -jsmin/test.py -jsmin.egg-info/PKG-INFO -jsmin.egg-info/SOURCES.txt -jsmin.egg-info/dependency_links.txt -jsmin.egg-info/top_level.txt \ No newline at end of file diff --git a/third_party/python/jsmin/jsmin.egg-info/dependency_links.txt b/third_party/python/jsmin/jsmin.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/jsmin/jsmin.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/jsmin/jsmin.egg-info/top_level.txt b/third_party/python/jsmin/jsmin.egg-info/top_level.txt deleted file mode 100644 index 79abaa99eece..000000000000 --- a/third_party/python/jsmin/jsmin.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -jsmin diff --git a/third_party/python/json-e/json_e.egg-info/PKG-INFO b/third_party/python/json-e/json_e.egg-info/PKG-INFO deleted file mode 100644 index bf41f8216701..000000000000 --- a/third_party/python/json-e/json_e.egg-info/PKG-INFO +++ /dev/null @@ -1,11 +0,0 @@ -Metadata-Version: 2.1 -Name: json-e -Version: 2.7.0 -Summary: A data-structure parameterization system written for embedding context in JSON objects -Home-page: https://taskcluster.github.io/json-e/ -Author: Dustin J. Mitchell -Author-email: dustin@mozilla.com -License: MPL2 -Description: UNKNOWN -Platform: UNKNOWN -Provides-Extra: release diff --git a/third_party/python/json-e/json_e.egg-info/SOURCES.txt b/third_party/python/json-e/json_e.egg-info/SOURCES.txt deleted file mode 100644 index ec2e4f666bf7..000000000000 --- a/third_party/python/json-e/json_e.egg-info/SOURCES.txt +++ /dev/null @@ -1,17 +0,0 @@ -MANIFEST.in -README.md -package.json -setup.cfg -setup.py -json_e.egg-info/PKG-INFO -json_e.egg-info/SOURCES.txt -json_e.egg-info/dependency_links.txt -json_e.egg-info/requires.txt -json_e.egg-info/top_level.txt -jsone/__init__.py -jsone/builtins.py -jsone/interpreter.py -jsone/prattparser.py -jsone/render.py -jsone/shared.py -jsone/six.py \ No newline at end of file diff --git a/third_party/python/json-e/json_e.egg-info/dependency_links.txt b/third_party/python/json-e/json_e.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/json-e/json_e.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/json-e/json_e.egg-info/requires.txt b/third_party/python/json-e/json_e.egg-info/requires.txt deleted file mode 100644 index f25a637d715a..000000000000 --- a/third_party/python/json-e/json_e.egg-info/requires.txt +++ /dev/null @@ -1,3 +0,0 @@ - -[release] -towncrier diff --git a/third_party/python/json-e/json_e.egg-info/top_level.txt b/third_party/python/json-e/json_e.egg-info/top_level.txt deleted file mode 100644 index afe8caa74cad..000000000000 --- a/third_party/python/json-e/json_e.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -jsone diff --git a/third_party/python/mohawk/mohawk.egg-info/PKG-INFO b/third_party/python/mohawk/mohawk.egg-info/PKG-INFO deleted file mode 100644 index 131f03cfc50d..000000000000 --- a/third_party/python/mohawk/mohawk.egg-info/PKG-INFO +++ /dev/null @@ -1,19 +0,0 @@ -Metadata-Version: 1.1 -Name: mohawk -Version: 0.3.4 -Summary: Library for Hawk HTTP authorization -Home-page: https://github.com/kumar303/mohawk -Author: Kumar McMillan, Austin King -Author-email: kumar.mcmillan@gmail.com -License: MPL 2.0 (Mozilla Public License) -Description: UNKNOWN -Platform: UNKNOWN -Classifier: Intended Audience :: Developers -Classifier: Natural Language :: English -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Topic :: Internet :: WWW/HTTP diff --git a/third_party/python/mohawk/mohawk.egg-info/SOURCES.txt b/third_party/python/mohawk/mohawk.egg-info/SOURCES.txt deleted file mode 100644 index 880beddbc40f..000000000000 --- a/third_party/python/mohawk/mohawk.egg-info/SOURCES.txt +++ /dev/null @@ -1,15 +0,0 @@ -README.rst -setup.py -mohawk/__init__.py -mohawk/base.py -mohawk/bewit.py -mohawk/exc.py -mohawk/receiver.py -mohawk/sender.py -mohawk/tests.py -mohawk/util.py -mohawk.egg-info/PKG-INFO -mohawk.egg-info/SOURCES.txt -mohawk.egg-info/dependency_links.txt -mohawk.egg-info/requires.txt -mohawk.egg-info/top_level.txt \ No newline at end of file diff --git a/third_party/python/mohawk/mohawk.egg-info/dependency_links.txt b/third_party/python/mohawk/mohawk.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/mohawk/mohawk.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/mohawk/mohawk.egg-info/requires.txt b/third_party/python/mohawk/mohawk.egg-info/requires.txt deleted file mode 100644 index ffe2fce49895..000000000000 --- a/third_party/python/mohawk/mohawk.egg-info/requires.txt +++ /dev/null @@ -1 +0,0 @@ -six diff --git a/third_party/python/mohawk/mohawk.egg-info/top_level.txt b/third_party/python/mohawk/mohawk.egg-info/top_level.txt deleted file mode 100644 index 2b859fd06c73..000000000000 --- a/third_party/python/mohawk/mohawk.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -mohawk diff --git a/third_party/python/multidict/multidict.egg-info/PKG-INFO b/third_party/python/multidict/multidict.egg-info/PKG-INFO deleted file mode 100644 index bbd48649479a..000000000000 --- a/third_party/python/multidict/multidict.egg-info/PKG-INFO +++ /dev/null @@ -1,128 +0,0 @@ -Metadata-Version: 1.2 -Name: multidict -Version: 5.1.0 -Summary: multidict implementation -Home-page: https://github.com/aio-libs/multidict -Author: Andrew Svetlov -Author-email: andrew.svetlov@gmail.com -License: Apache 2 -Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby -Project-URL: CI: Azure Pipelines, https://dev.azure.com/aio-libs/multidict/_build -Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/multidict -Project-URL: Docs: RTD, https://multidict.readthedocs.io -Project-URL: GitHub: issues, https://github.com/aio-libs/multidict/issues -Project-URL: GitHub: repo, https://github.com/aio-libs/multidict -Description: ========= - multidict - ========= - - .. image:: https://github.com/aio-libs/multidict/workflows/CI/badge.svg - :target: https://github.com/aio-libs/multidict/actions?query=workflow%3ACI - :alt: GitHub status for master branch - - .. image:: https://codecov.io/gh/aio-libs/multidict/branch/master/graph/badge.svg - :target: https://codecov.io/gh/aio-libs/multidict - :alt: Coverage metrics - - .. image:: https://img.shields.io/pypi/v/multidict.svg - :target: https://pypi.org/project/multidict - :alt: PyPI - - .. image:: https://readthedocs.org/projects/multidict/badge/?version=latest - :target: http://multidict.readthedocs.org/en/latest/?badge=latest - :alt: Documentationb - - .. image:: https://img.shields.io/pypi/pyversions/multidict.svg - :target: https://pypi.org/project/multidict - :alt: Python versions - - .. image:: https://badges.gitter.im/Join%20Chat.svg - :target: https://gitter.im/aio-libs/Lobby - :alt: Chat on Gitter - - Multidict is dict-like collection of *key-value pairs* where key - might be occurred more than once in the container. - - Introduction - ------------ - - *HTTP Headers* and *URL query string* require specific data structure: - *multidict*. It behaves mostly like a regular ``dict`` but it may have - several *values* for the same *key* and *preserves insertion ordering*. - - The *key* is ``str`` (or ``istr`` for case-insensitive dictionaries). - - ``multidict`` has four multidict classes: - ``MultiDict``, ``MultiDictProxy``, ``CIMultiDict`` - and ``CIMultiDictProxy``. - - Immutable proxies (``MultiDictProxy`` and - ``CIMultiDictProxy``) provide a dynamic view for the - proxied multidict, the view reflects underlying collection changes. They - implement the ``collections.abc.Mapping`` interface. - - Regular mutable (``MultiDict`` and ``CIMultiDict``) classes - implement ``collections.abc.MutableMapping`` and allows to change - their own content. - - - *Case insensitive* (``CIMultiDict`` and - ``CIMultiDictProxy``) ones assume the *keys* are case - insensitive, e.g.:: - - >>> dct = CIMultiDict(key='val') - >>> 'Key' in dct - True - >>> dct['Key'] - 'val' - - *Keys* should be ``str`` or ``istr`` instances. - - The library has optional C Extensions for sake of speed. - - - License - ------- - - Apache 2 - - Library Installation - -------------------- - - .. code-block:: bash - - $ pip install multidict - - The library is Python 3 only! - - PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install - ``multidict`` on another operation system (or *Alpine Linux* inside a Docker) the - Tarball will be used to compile the library from sources. It requires C compiler and - Python headers installed. - - To skip the compilation please use `MULTIDICT_NO_EXTENSIONS` environment variable, - e.g.: - - .. code-block:: bash - - $ MULTIDICT_NO_EXTENSIONS=1 pip install multidict - - Please note, Pure Python (uncompiled) version is about 20-50 times slower depending on - the usage scenario!!! - - - - Changelog - --------- - See `RTD page `_. -Platform: UNKNOWN -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Intended Audience :: Developers -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Development Status :: 5 - Production/Stable -Requires-Python: >=3.6 diff --git a/third_party/python/multidict/multidict.egg-info/SOURCES.txt b/third_party/python/multidict/multidict.egg-info/SOURCES.txt deleted file mode 100644 index 6c6257ea9b0b..000000000000 --- a/third_party/python/multidict/multidict.egg-info/SOURCES.txt +++ /dev/null @@ -1,71 +0,0 @@ -CHANGES.rst -LICENSE -MANIFEST.in -Makefile -README.rst -pyproject.toml -setup.cfg -setup.py -docs/Makefile -docs/benchmark.rst -docs/changes.rst -docs/conf.py -docs/index.rst -docs/make.bat -docs/multidict.rst -docs/spelling_wordlist.txt -multidict/__init__.py -multidict/__init__.pyi -multidict/_abc.py -multidict/_compat.py -multidict/_multidict.c -multidict/_multidict_base.py -multidict/_multidict_py.py -multidict/py.typed -multidict.egg-info/PKG-INFO -multidict.egg-info/SOURCES.txt -multidict.egg-info/dependency_links.txt -multidict.egg-info/top_level.txt -multidict/_multilib/defs.h -multidict/_multilib/dict.h -multidict/_multilib/istr.h -multidict/_multilib/iter.h -multidict/_multilib/pair_list.h -multidict/_multilib/views.h -tests/cimultidict.pickle.0 -tests/cimultidict.pickle.1 -tests/cimultidict.pickle.2 -tests/cimultidict.pickle.3 -tests/cimultidict.pickle.4 -tests/cimultidict.pickle.5 -tests/conftest.py -tests/gen_pickles.py -tests/multidict.pickle.0 -tests/multidict.pickle.1 -tests/multidict.pickle.2 -tests/multidict.pickle.3 -tests/multidict.pickle.4 -tests/multidict.pickle.5 -tests/pycimultidict.pickle.0 -tests/pycimultidict.pickle.1 -tests/pycimultidict.pickle.2 -tests/pycimultidict.pickle.3 -tests/pycimultidict.pickle.4 -tests/pycimultidict.pickle.5 -tests/pymultidict.pickle.0 -tests/pymultidict.pickle.1 -tests/pymultidict.pickle.2 -tests/pymultidict.pickle.3 -tests/pymultidict.pickle.4 -tests/pymultidict.pickle.5 -tests/test_abc.py -tests/test_copy.py -tests/test_guard.py -tests/test_istr.py -tests/test_multidict.py -tests/test_mutable_multidict.py -tests/test_mypy.py -tests/test_pickle.py -tests/test_types.py -tests/test_update.py -tests/test_version.py \ No newline at end of file diff --git a/third_party/python/multidict/multidict.egg-info/dependency_links.txt b/third_party/python/multidict/multidict.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/multidict/multidict.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/multidict/multidict.egg-info/top_level.txt b/third_party/python/multidict/multidict.egg-info/top_level.txt deleted file mode 100644 index afcecdff0822..000000000000 --- a/third_party/python/multidict/multidict.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -multidict diff --git a/third_party/python/packaging/packaging-21.0.dist-info/LICENSE b/third_party/python/packaging/packaging-21.0.dist-info/LICENSE deleted file mode 100644 index 6f62d44e4ef7..000000000000 --- a/third_party/python/packaging/packaging-21.0.dist-info/LICENSE +++ /dev/null @@ -1,3 +0,0 @@ -This software is made available under the terms of *either* of the licenses -found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made -under the terms of *both* these licenses. diff --git a/third_party/python/packaging/packaging-21.0.dist-info/LICENSE.APACHE b/third_party/python/packaging/packaging-21.0.dist-info/LICENSE.APACHE deleted file mode 100644 index f433b1a53f5b..000000000000 --- a/third_party/python/packaging/packaging-21.0.dist-info/LICENSE.APACHE +++ /dev/null @@ -1,177 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/third_party/python/packaging/packaging-21.0.dist-info/LICENSE.BSD b/third_party/python/packaging/packaging-21.0.dist-info/LICENSE.BSD deleted file mode 100644 index 42ce7b75c92f..000000000000 --- a/third_party/python/packaging/packaging-21.0.dist-info/LICENSE.BSD +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) Donald Stufft and individual contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/python/packaging/packaging-21.0.dist-info/METADATA b/third_party/python/packaging/packaging-21.0.dist-info/METADATA deleted file mode 100644 index 4d4fae12157a..000000000000 --- a/third_party/python/packaging/packaging-21.0.dist-info/METADATA +++ /dev/null @@ -1,425 +0,0 @@ -Metadata-Version: 2.1 -Name: packaging -Version: 21.0 -Summary: Core utilities for Python packages -Home-page: https://github.com/pypa/packaging -Author: Donald Stufft and individual contributors -Author-email: donald@stufft.io -License: BSD-2-Clause or Apache-2.0 -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Apache Software License -Classifier: License :: OSI Approved :: BSD License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Python: >=3.6 -Description-Content-Type: text/x-rst -License-File: LICENSE -License-File: LICENSE.APACHE -License-File: LICENSE.BSD -Requires-Dist: pyparsing (>=2.0.2) - -packaging -========= - -.. start-intro - -Reusable core utilities for various Python Packaging -`interoperability specifications `_. - -This library provides utilities that implement the interoperability -specifications which have clearly one correct behaviour (eg: :pep:`440`) -or benefit greatly from having a single shared implementation (eg: :pep:`425`). - -.. end-intro - -The ``packaging`` project includes the following: version handling, specifiers, -markers, requirements, tags, utilities. - -Documentation -------------- - -The `documentation`_ provides information and the API for the following: - -- Version Handling -- Specifiers -- Markers -- Requirements -- Tags -- Utilities - -Installation ------------- - -Use ``pip`` to install these utilities:: - - pip install packaging - -Discussion ----------- - -If you run into bugs, you can file them in our `issue tracker`_. - -You can also join ``#pypa`` on Freenode to ask questions or get involved. - - -.. _`documentation`: https://packaging.pypa.io/ -.. _`issue tracker`: https://github.com/pypa/packaging/issues - - -Code of Conduct ---------------- - -Everyone interacting in the packaging project's codebases, issue trackers, chat -rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. - -.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md - -Contributing ------------- - -The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as -well as how to report a potential security issue. The documentation for this -project also covers information about `project development`_ and `security`_. - -.. _`project development`: https://packaging.pypa.io/en/latest/development/ -.. _`security`: https://packaging.pypa.io/en/latest/security/ - -Project History ---------------- - -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/ - -Changelog ---------- - -21.0 - 2021-07-03 -~~~~~~~~~~~~~~~~~ - -* `packaging` is now only compatible with Python 3.6 and above. -* Add support for zip files in ``parse_sdist_filename`` (`#429 `__) - -20.9 - 2021-01-29 -~~~~~~~~~~~~~~~~~ - -* Run `isort `_ over the code base (`#377 `__) -* Add support for the ``macosx_10_*_universal2`` platform tags (`#379 `__) -* Introduce ``packaging.utils.parse_wheel_filename()`` and ``parse_sdist_filename()`` - (`#387 `__ and `#389 `__) - -20.8 - 2020-12-11 -~~~~~~~~~~~~~~~~~ - -* Revert back to setuptools for compatibility purposes for some Linux distros (`#363 `__) -* Do not insert an underscore in wheel tags when the interpreter version number - is more than 2 digits (`#372 `__) - -20.7 - 2020-11-28 -~~~~~~~~~~~~~~~~~ - -No unreleased changes. - -20.6 - 2020-11-28 -~~~~~~~~~~~~~~~~~ - -.. note:: This release was subsequently yanked, and these changes were included in 20.7. - -* Fix flit configuration, to include LICENSE files (`#357 `__) -* Make `intel` a recognized CPU architecture for the `universal` macOS platform tag (`#361 `__) -* Add some missing type hints to `packaging.requirements` (issue:`350`) - -20.5 - 2020-11-27 -~~~~~~~~~~~~~~~~~ - -* Officially support Python 3.9 (`#343 `__) -* Deprecate the ``LegacyVersion`` and ``LegacySpecifier`` classes (`#321 `__) -* Handle ``OSError`` on non-dynamic executables when attempting to resolve - the glibc version string. - -20.4 - 2020-05-19 -~~~~~~~~~~~~~~~~~ - -* Canonicalize version before comparing specifiers. (`#282 `__) -* Change type hint for ``canonicalize_name`` to return - ``packaging.utils.NormalizedName``. - This enables the use of static typing tools (like mypy) to detect mixing of - normalized and un-normalized names. - -20.3 - 2020-03-05 -~~~~~~~~~~~~~~~~~ - -* Fix changelog for 20.2. - -20.2 - 2020-03-05 -~~~~~~~~~~~~~~~~~ - -* Fix a bug that caused a 32-bit OS that runs on a 64-bit ARM CPU (e.g. ARM-v8, - aarch64), to report the wrong bitness. - -20.1 - 2020-01-24 -~~~~~~~~~~~~~~~~~~~ - -* Fix a bug caused by reuse of an exhausted iterator. (`#257 `__) - -20.0 - 2020-01-06 -~~~~~~~~~~~~~~~~~ - -* Add type hints (`#191 `__) - -* Add proper trove classifiers for PyPy support (`#198 `__) - -* Scale back depending on ``ctypes`` for manylinux support detection (`#171 `__) - -* Use ``sys.implementation.name`` where appropriate for ``packaging.tags`` (`#193 `__) - -* Expand upon the API provided by ``packaging.tags``: ``interpreter_name()``, ``mac_platforms()``, ``compatible_tags()``, ``cpython_tags()``, ``generic_tags()`` (`#187 `__) - -* Officially support Python 3.8 (`#232 `__) - -* Add ``major``, ``minor``, and ``micro`` aliases to ``packaging.version.Version`` (`#226 `__) - -* Properly mark ``packaging`` has being fully typed by adding a `py.typed` file (`#226 `__) - -19.2 - 2019-09-18 -~~~~~~~~~~~~~~~~~ - -* Remove dependency on ``attrs`` (`#178 `__, `#179 `__) - -* Use appropriate fallbacks for CPython ABI tag (`#181 `__, `#185 `__) - -* Add manylinux2014 support (`#186 `__) - -* Improve ABI detection (`#181 `__) - -* Properly handle debug wheels for Python 3.8 (`#172 `__) - -* Improve detection of debug builds on Windows (`#194 `__) - -19.1 - 2019-07-30 -~~~~~~~~~~~~~~~~~ - -* Add the ``packaging.tags`` module. (`#156 `__) - -* Correctly handle two-digit versions in ``python_version`` (`#119 `__) - - -19.0 - 2019-01-20 -~~~~~~~~~~~~~~~~~ - -* Fix string representation of PEP 508 direct URL requirements with markers. - -* Better handling of file URLs - - This allows for using ``file:///absolute/path``, which was previously - prevented due to the missing ``netloc``. - - This allows for all file URLs that ``urlunparse`` turns back into the - original URL to be valid. - - -18.0 - 2018-09-26 -~~~~~~~~~~~~~~~~~ - -* Improve error messages when invalid requirements are given. (`#129 `__) - - -17.1 - 2017-02-28 -~~~~~~~~~~~~~~~~~ - -* Fix ``utils.canonicalize_version`` when supplying non PEP 440 versions. - - -17.0 - 2017-02-28 -~~~~~~~~~~~~~~~~~ - -* Drop support for python 2.6, 3.2, and 3.3. - -* Define minimal pyparsing version to 2.0.2 (`#91 `__). - -* Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` attributes to - ``Version`` and ``LegacyVersion`` (`#34 `__). - -* Add ``Version().is_devrelease`` and ``LegacyVersion().is_devrelease`` to - make it easy to determine if a release is a development release. - -* Add ``utils.canonicalize_version`` to canonicalize version strings or - ``Version`` instances (`#121 `__). - - -16.8 - 2016-10-29 -~~~~~~~~~~~~~~~~~ - -* Fix markers that utilize ``in`` so that they render correctly. - -* Fix an erroneous test on Python RC releases. - - -16.7 - 2016-04-23 -~~~~~~~~~~~~~~~~~ - -* Add support for the deprecated ``python_implementation`` marker which was - an undocumented setuptools marker in addition to the newer markers. - - -16.6 - 2016-03-29 -~~~~~~~~~~~~~~~~~ - -* Add support for the deprecated, PEP 345 environment markers in addition to - the newer markers. - - -16.5 - 2016-02-26 -~~~~~~~~~~~~~~~~~ - -* Fix a regression in parsing requirements with whitespaces between the comma - separators. - - -16.4 - 2016-02-22 -~~~~~~~~~~~~~~~~~ - -* Fix a regression in parsing requirements like ``foo (==4)``. - - -16.3 - 2016-02-21 -~~~~~~~~~~~~~~~~~ - -* Fix a bug where ``packaging.requirements:Requirement`` was overly strict when - matching legacy requirements. - - -16.2 - 2016-02-09 -~~~~~~~~~~~~~~~~~ - -* Add a function that implements the name canonicalization from PEP 503. - - -16.1 - 2016-02-07 -~~~~~~~~~~~~~~~~~ - -* Implement requirement specifiers from PEP 508. - - -16.0 - 2016-01-19 -~~~~~~~~~~~~~~~~~ - -* Relicense so that packaging is available under *either* the Apache License, - Version 2.0 or a 2 Clause BSD license. - -* Support installation of packaging when only distutils is available. - -* Fix ``==`` comparison when there is a prefix and a local version in play. - (`#41 `__). - -* Implement environment markers from PEP 508. - - -15.3 - 2015-08-01 -~~~~~~~~~~~~~~~~~ - -* Normalize post-release spellings for rev/r prefixes. `#35 `__ - - -15.2 - 2015-05-13 -~~~~~~~~~~~~~~~~~ - -* Fix an error where the arbitrary specifier (``===``) was not correctly - allowing pre-releases when it was being used. - -* Expose the specifier and version parts through properties on the - ``Specifier`` classes. - -* Allow iterating over the ``SpecifierSet`` to get access to all of the - ``Specifier`` instances. - -* Allow testing if a version is contained within a specifier via the ``in`` - operator. - - -15.1 - 2015-04-13 -~~~~~~~~~~~~~~~~~ - -* Fix a logic error that was causing inconsistent answers about whether or not - a pre-release was contained within a ``SpecifierSet`` or not. - - -15.0 - 2015-01-02 -~~~~~~~~~~~~~~~~~ - -* Add ``Version().is_postrelease`` and ``LegacyVersion().is_postrelease`` to - make it easy to determine if a release is a post release. - -* Add ``Version().base_version`` and ``LegacyVersion().base_version`` to make - it easy to get the public version without any pre or post release markers. - -* Support the update to PEP 440 which removed the implied ``!=V.*`` when using - either ``>V`` or ``V`` or ````) operator. - - -14.3 - 2014-11-19 -~~~~~~~~~~~~~~~~~ - -* **BACKWARDS INCOMPATIBLE** Refactor specifier support so that it can sanely - handle legacy specifiers as well as PEP 440 specifiers. - -* **BACKWARDS INCOMPATIBLE** Move the specifier support out of - ``packaging.version`` into ``packaging.specifiers``. - - -14.2 - 2014-09-10 -~~~~~~~~~~~~~~~~~ - -* Add prerelease support to ``Specifier``. -* Remove the ability to do ``item in Specifier()`` and replace it with - ``Specifier().contains(item)`` in order to allow flags that signal if a - prerelease should be accepted or not. -* Add a method ``Specifier().filter()`` which will take an iterable and returns - an iterable with items that do not match the specifier filtered out. - - -14.1 - 2014-09-08 -~~~~~~~~~~~~~~~~~ - -* Allow ``LegacyVersion`` and ``Version`` to be sorted together. -* Add ``packaging.version.parse()`` to enable easily parsing a version string - as either a ``Version`` or a ``LegacyVersion`` depending on it's PEP 440 - validity. - - -14.0 - 2014-09-05 -~~~~~~~~~~~~~~~~~ - -* Initial release. - - -.. _`master`: https://github.com/pypa/packaging/ - - diff --git a/third_party/python/packaging/packaging-21.0.dist-info/RECORD b/third_party/python/packaging/packaging-21.0.dist-info/RECORD deleted file mode 100644 index 19dd3954b50d..000000000000 --- a/third_party/python/packaging/packaging-21.0.dist-info/RECORD +++ /dev/null @@ -1,19 +0,0 @@ -packaging/__about__.py,sha256=p_OQloqH2saadcbUQmWEsWK857dI6_ff5E3aSiCqGFA,661 -packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497 -packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488 -packaging/_musllinux.py,sha256=z5yeG1ygOPx4uUyLdqj-p8Dk5UBb5H_b0NIjW9yo8oA,4378 -packaging/_structures.py,sha256=TMiAgFbdUOPmIfDIfiHc3KFhSJ8kMjof2QS5I-2NyQ8,1629 -packaging/markers.py,sha256=Fygi3_eZnjQ-3VJizW5AhI5wvo0Hb6RMk4DidsKpOC0,8475 -packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -packaging/requirements.py,sha256=rjaGRCMepZS1mlYMjJ5Qh6rfq3gtsCRQUQmftGZ_bu8,4664 -packaging/specifiers.py,sha256=MZ-fYcNL3u7pNrt-6g2EQO7AbRXkjc-SPEYwXMQbLmc,30964 -packaging/tags.py,sha256=akIerYw8W0sz4OW9HHozgawWnbt2GGOPm3sviW0jowY,15714 -packaging/utils.py,sha256=dJjeat3BS-TYn1RrUFVwufUMasbtzLfYRoy_HXENeFQ,4200 -packaging/version.py,sha256=_fLRNrFrxYcHVfyo8vk9j8s6JM8N_xsSxVFr6RJyco8,14665 -packaging-21.0.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 -packaging-21.0.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 -packaging-21.0.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 -packaging-21.0.dist-info/METADATA,sha256=ZV4MesCjT-YxFEJvLzsJ31kKmmj4ltiMUl3JvqxJfqI,13418 -packaging-21.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 -packaging-21.0.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10 -packaging-21.0.dist-info/RECORD,, diff --git a/third_party/python/packaging/packaging-21.0.dist-info/WHEEL b/third_party/python/packaging/packaging-21.0.dist-info/WHEEL deleted file mode 100644 index 385faab0525c..000000000000 --- a/third_party/python/packaging/packaging-21.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.36.2) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/third_party/python/packaging/packaging-21.0.dist-info/top_level.txt b/third_party/python/packaging/packaging-21.0.dist-info/top_level.txt deleted file mode 100644 index 748809f75c47..000000000000 --- a/third_party/python/packaging/packaging-21.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -packaging diff --git a/third_party/python/packaging/packaging/__about__.py b/third_party/python/packaging/packaging/__about__.py deleted file mode 100644 index e70d692c85a1..000000000000 --- a/third_party/python/packaging/packaging/__about__.py +++ /dev/null @@ -1,26 +0,0 @@ -# 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. - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "21.0" - -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" - -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "2014-2019 %s" % __author__ diff --git a/third_party/python/packaging/packaging/__init__.py b/third_party/python/packaging/packaging/__init__.py deleted file mode 100644 index 3c50c5dcfeed..000000000000 --- a/third_party/python/packaging/packaging/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# 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 .__about__ import ( - __author__, - __copyright__, - __email__, - __license__, - __summary__, - __title__, - __uri__, - __version__, -) - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] diff --git a/third_party/python/packaging/packaging/_manylinux.py b/third_party/python/packaging/packaging/_manylinux.py deleted file mode 100644 index 4c379aa6f69f..000000000000 --- a/third_party/python/packaging/packaging/_manylinux.py +++ /dev/null @@ -1,301 +0,0 @@ -import collections -import functools -import os -import re -import struct -import sys -import warnings -from typing import IO, Dict, Iterator, NamedTuple, Optional, Tuple - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader: - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file: IO[bytes]) -> None: - def unpack(fmt: str) -> int: - try: - data = file.read(struct.calcsize(fmt)) - result: Tuple[int, ...] = struct.unpack(fmt, data) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result[0] - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "H" - format_i = "I" - format_q = "Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header() -> Optional[_ELFFileHeader]: - try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header - - -def _is_linux_armhf() -> bool: - # hard-float ABI can be detected from the ELF header of the running - # process - # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686() -> bool: - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result - - -def _have_compatible_abi(arch: str) -> bool: - if arch == "armv7l": - return _is_linux_armhf() - if arch == "i686": - return _is_linux_i686() - return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} - - -# If glibc ever changes its major version, we need to know what the last -# minor version was, so we can build the complete list of all versions. -# 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) - - -class _GLibCVersion(NamedTuple): - major: int - minor: int - - -def _glibc_version_string_confstr() -> Optional[str]: - """ - 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/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 - try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr("CS_GNU_LIBC_VERSION") - assert version_string is not None - _, version = version_string.split() - except (AssertionError, 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. - """ - try: - import ctypes - except ImportError: - return None - - # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen - # manpage says, "If filename is NULL, then the returned handle is for the - # main program". This way we can let the linker do the work to figure out - # which libc our process is actually using. - # - # We must also handle the special case where the executable is not a - # dynamically linked executable. This can occur when using musl libc, - # for example. In this situation, dlopen() will error, leading to an - # OSError. Interestingly, at least in the case of musl, there is no - # errno set on the OSError. The single string argument used to construct - # OSError comes from libc itself and is therefore not portable to - # hard code here. In any case, failure to call dlopen() means we - # can proceed, so we bail on our attempt. - try: - process_namespace = ctypes.CDLL(None) - except OSError: - return None - - try: - gnu_get_libc_version = process_namespace.gnu_get_libc_version - except AttributeError: - # Symbol doesn't exist -> therefore, we are not linked to - # glibc. - return None - - # Call gnu_get_libc_version, which returns a string like "2.5" - gnu_get_libc_version.restype = ctypes.c_char_p - version_str: str = gnu_get_libc_version() - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -def _glibc_version_string() -> Optional[str]: - """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]: - """Parse glibc version. - - We use a regexp instead of str.split because we want to discard any - random junk that might come after the minor version -- this might happen - 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) - if not m: - warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, - RuntimeWarning, - ) - return -1, -1 - return int(m.group("major")), int(m.group("minor")) - - -@functools.lru_cache() -def _get_glibc_version() -> Tuple[int, int]: - version_str = _glibc_version_string() - if version_str is None: - return (-1, -1) - return _parse_glibc_version(version_str) - - -# From PEP 513, PEP 600 -def _is_compatible(name: str, arch: str, version: _GLibCVersion) -> bool: - sys_glibc = _get_glibc_version() - if sys_glibc < version: - return False - # Check for presence of _manylinux module. - try: - import _manylinux # noqa - except ImportError: - return True - 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"): - return bool(_manylinux.manylinux1_compatible) - if version == _GLibCVersion(2, 12): - if hasattr(_manylinux, "manylinux2010_compatible"): - return bool(_manylinux.manylinux2010_compatible) - if version == _GLibCVersion(2, 17): - 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", - # CentOS 6 w/ glibc 2.12 (PEP 571) - (2, 12): "manylinux2010", - # CentOS 5 w/ glibc 2.5 (PEP 513) - (2, 5): "manylinux1", -} - - -def platform_tags(linux: str, arch: str) -> Iterator[str]: - if not _have_compatible_abi(arch): - return - # Oldest glibc to be supported regardless of architecture is (2, 17). - too_old_glibc2 = _GLibCVersion(2, 16) - if arch in {"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()) - glibc_max_list = [current_glibc] - # We can assume compatibility across glibc major versions. - # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 - # - # Build a list of maximum glibc versions so that we can - # output the canonical list of all glibc from current_glibc - # down to too_old_glibc2, including all intermediary versions. - for glibc_major in range(current_glibc.major - 1, 1, -1): - glibc_minor = _LAST_GLIBC_MINOR[glibc_major] - glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) - for glibc_max in glibc_max_list: - if glibc_max.major == too_old_glibc2.major: - min_minor = too_old_glibc2.minor - else: - # For other glibc major versions oldest supported is (x, 0). - 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) - if _is_compatible(tag, arch, glibc_version): - yield linux.replace("linux", tag) - # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. - if glibc_version in _LEGACY_MANYLINUX_MAP: - legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] - if _is_compatible(legacy_tag, arch, glibc_version): - yield linux.replace("linux", legacy_tag) diff --git a/third_party/python/packaging/packaging/_musllinux.py b/third_party/python/packaging/packaging/_musllinux.py deleted file mode 100644 index 85450fafa347..000000000000 --- a/third_party/python/packaging/packaging/_musllinux.py +++ /dev/null @@ -1,136 +0,0 @@ -"""PEP 656 support. - -This module implements logic to detect if the currently running Python is -linked against musl, and what musl version is used. -""" - -import contextlib -import functools -import operator -import os -import re -import struct -import subprocess -import sys -from typing import IO, Iterator, NamedTuple, Optional, Tuple - - -def _read_unpacked(f: IO[bytes], fmt: str) -> Tuple[int, ...]: - return struct.unpack(fmt, f.read(struct.calcsize(fmt))) - - -def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]: - """Detect musl libc location by parsing the Python executable. - - Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca - ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html - """ - f.seek(0) - try: - ident = _read_unpacked(f, "16B") - except struct.error: - return None - if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF. - return None - f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version. - - try: - # e_fmt: Format for program header. - # p_fmt: Format for section header. - # p_idx: Indexes to find p_type, p_offset, and p_filesz. - e_fmt, p_fmt, p_idx = { - 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit. - 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit. - }[ident[4]] - except KeyError: - return None - else: - p_get = operator.itemgetter(*p_idx) - - # Find the interpreter section and return its content. - try: - _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt) - except struct.error: - return None - for i in range(e_phnum + 1): - f.seek(e_phoff + e_phentsize * i) - try: - p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt)) - except struct.error: - return None - if p_type != 3: # Not PT_INTERP. - continue - f.seek(p_offset) - interpreter = os.fsdecode(f.read(p_filesz)).strip("\0") - if "musl" not in interpreter: - return None - return interpreter - return None - - -class _MuslVersion(NamedTuple): - major: int - minor: int - - -def _parse_musl_version(output: str) -> Optional[_MuslVersion]: - lines = [n for n in (n.strip() for n in output.splitlines()) if n] - if len(lines) < 2 or lines[0][:4] != "musl": - return None - 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]: - """Detect currently-running musl runtime version. - - This is done by checking the specified executable's dynamic linking - information, and invoking the loader to parse its output for a version - string. If the loader is musl, the output would be something like:: - - musl libc (x86_64) - Version 1.2.2 - Dynamic Program Loader - """ - with contextlib.ExitStack() as stack: - try: - f = stack.enter_context(open(executable, "rb")) - except IOError: - return None - ld = _parse_ld_musl_from_elf(f) - if not ld: - return None - proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True) - return _parse_musl_version(proc.stderr) - - -def platform_tags(arch: str) -> Iterator[str]: - """Generate musllinux tags compatible to the current platform. - - :param arch: Should be the part of platform tag after the ``linux_`` - prefix, e.g. ``x86_64``. The ``linux_`` prefix is assumed as a - prerequisite for the current platform to be musllinux-compatible. - - :returns: An iterator of compatible musllinux tags. - """ - sys_musl = _get_musl_version(sys.executable) - if sys_musl is None: # Python not dynamically linked against musl. - return - for minor in range(sys_musl.minor, -1, -1): - yield f"musllinux_{sys_musl.major}_{minor}_{arch}" - - -if __name__ == "__main__": # pragma: no cover - import sysconfig - - plat = sysconfig.get_platform() - 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 ") diff --git a/third_party/python/packaging/packaging/_structures.py b/third_party/python/packaging/packaging/_structures.py deleted file mode 100644 index 951549753afa..000000000000 --- a/third_party/python/packaging/packaging/_structures.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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. - - -class InfinityType: - def __repr__(self) -> str: - return "Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: object) -> bool: - return False - - def __le__(self, other: object) -> bool: - return False - - def __eq__(self, other: object) -> bool: - return isinstance(other, self.__class__) - - def __ne__(self, other: object) -> bool: - return not isinstance(other, self.__class__) - - def __gt__(self, other: object) -> bool: - return True - - def __ge__(self, other: object) -> bool: - return True - - def __neg__(self: object) -> "NegativeInfinityType": - return NegativeInfinity - - -Infinity = InfinityType() - - -class NegativeInfinityType: - def __repr__(self) -> str: - return "-Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: object) -> bool: - return True - - def __le__(self, other: object) -> bool: - return True - - def __eq__(self, other: object) -> bool: - return isinstance(other, self.__class__) - - def __ne__(self, other: object) -> bool: - return not isinstance(other, self.__class__) - - def __gt__(self, other: object) -> bool: - return False - - def __ge__(self, other: object) -> bool: - return False - - def __neg__(self: object) -> InfinityType: - return Infinity - - -NegativeInfinity = NegativeInfinityType() diff --git a/third_party/python/packaging/packaging/markers.py b/third_party/python/packaging/packaging/markers.py deleted file mode 100644 index cb640e8f9b80..000000000000 --- a/third_party/python/packaging/packaging/markers.py +++ /dev/null @@ -1,304 +0,0 @@ -# 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. - -import operator -import os -import platform -import sys -from typing import Any, Callable, Dict, List, Optional, Tuple, Union - -from pyparsing import ( # noqa: N817 - Forward, - Group, - Literal as L, - ParseException, - ParseResults, - QuotedString, - ZeroOrMore, - stringEnd, - stringStart, -) - -from .specifiers import InvalidSpecifier, Specifier - -__all__ = [ - "InvalidMarker", - "UndefinedComparison", - "UndefinedEnvironmentName", - "Marker", - "default_environment", -] - -Operator = Callable[[str, str], bool] - - -class InvalidMarker(ValueError): - """ - An invalid marker was found, users should refer to PEP 508. - """ - - -class UndefinedComparison(ValueError): - """ - An invalid operation was attempted on a value that doesn't support it. - """ - - -class UndefinedEnvironmentName(ValueError): - """ - A name was attempted to be used that does not exist inside of the - environment. - """ - - -class Node: - def __init__(self, value: Any) -> None: - self.value = value - - def __str__(self) -> str: - return str(self.value) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__}('{self}')>" - - def serialize(self) -> str: - raise NotImplementedError - - -class Variable(Node): - def serialize(self) -> str: - return str(self) - - -class Value(Node): - def serialize(self) -> str: - return f'"{self}"' - - -class Op(Node): - def serialize(self) -> str: - return str(self) - - -VARIABLE = ( - L("implementation_version") - | L("platform_python_implementation") - | L("implementation_name") - | L("python_full_version") - | L("platform_release") - | L("platform_version") - | L("platform_machine") - | L("platform_system") - | L("python_version") - | L("sys_platform") - | L("os_name") - | L("os.name") # PEP-345 - | L("sys.platform") # PEP-345 - | L("platform.version") # PEP-345 - | L("platform.machine") # PEP-345 - | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # undocumented setuptools legacy - | L("extra") # PEP-508 -) -ALIASES = { - "os.name": "os_name", - "sys.platform": "sys_platform", - "platform.version": "platform_version", - "platform.machine": "platform_machine", - "platform.python_implementation": "platform_python_implementation", - "python_implementation": "platform_python_implementation", -} -VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) - -VERSION_CMP = ( - L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") -) - -MARKER_OP = VERSION_CMP | L("not in") | L("in") -MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) - -MARKER_VALUE = QuotedString("'") | QuotedString('"') -MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) - -BOOLOP = L("and") | L("or") - -MARKER_VAR = VARIABLE | MARKER_VALUE - -MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) -MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) - -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() - -MARKER_EXPR = Forward() -MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) -MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) - -MARKER = stringStart + MARKER_EXPR + stringEnd - - -def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]: - if isinstance(results, ParseResults): - return [_coerce_parse_result(i) for i in results] - else: - return results - - -def _format_marker( - marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True -) -> str: - - assert isinstance(marker, (list, tuple, str)) - - # Sometimes we have a structure like [[...]] which is a single item list - # where the single item is itself it's own list. In that case we want skip - # 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)) - ): - return _format_marker(marker[0]) - - if isinstance(marker, list): - inner = (_format_marker(m, first=False) for m in marker) - if first: - return " ".join(inner) - else: - return "(" + " ".join(inner) + ")" - elif isinstance(marker, tuple): - 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, -} - - -def _eval_op(lhs: str, op: Op, rhs: str) -> bool: - try: - spec = Specifier("".join([op.serialize(), rhs])) - except InvalidSpecifier: - pass - else: - return spec.contains(lhs) - - oper: Optional[Operator] = _operators.get(op.serialize()) - if oper is None: - raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") - - return oper(lhs, rhs) - - -class Undefined: - pass - - -_undefined = Undefined() - - -def _get_env(environment: Dict[str, str], name: str) -> str: - value: Union[str, Undefined] = environment.get(name, _undefined) - - if isinstance(value, Undefined): - raise UndefinedEnvironmentName( - f"{name!r} does not exist in evaluation environment." - ) - - return value - - -def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: - groups: List[List[bool]] = [[]] - - for marker in markers: - assert isinstance(marker, (list, tuple, str)) - - if isinstance(marker, list): - groups[-1].append(_evaluate_markers(marker, environment)) - elif isinstance(marker, tuple): - lhs, op, rhs = marker - - if isinstance(lhs, Variable): - lhs_value = _get_env(environment, lhs.value) - rhs_value = rhs.value - else: - lhs_value = lhs.value - rhs_value = _get_env(environment, rhs.value) - - groups[-1].append(_eval_op(lhs_value, op, rhs_value)) - else: - 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) - kind = info.releaselevel - if kind != "final": - version += kind[0] + str(info.serial) - return version - - -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, - } - - -class Marker: - def __init__(self, marker: str) -> None: - try: - self._markers = _coerce_parse_result(MARKER.parseString(marker)) - except ParseException as e: - raise InvalidMarker( - f"Invalid marker: {marker!r}, parse error at " - f"{marker[e.loc : e.loc + 8]!r}" - ) - - def __str__(self) -> str: - return _format_marker(self._markers) - - def __repr__(self) -> str: - return f"" - - def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: - """Evaluate a marker. - - Return the boolean from evaluating the given marker against the - environment. environment is an optional argument to override all or - part of the determined environment. - - The environment is determined from the current Python process. - """ - current_environment = default_environment() - if environment is not None: - current_environment.update(environment) - - return _evaluate_markers(self._markers, current_environment) diff --git a/third_party/python/packaging/packaging/py.typed b/third_party/python/packaging/packaging/py.typed deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/third_party/python/packaging/packaging/requirements.py b/third_party/python/packaging/packaging/requirements.py deleted file mode 100644 index 53f9a3aa42b8..000000000000 --- a/third_party/python/packaging/packaging/requirements.py +++ /dev/null @@ -1,146 +0,0 @@ -# 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. - -import re -import string -import urllib.parse -from typing import List, Optional as TOptional, Set - -from pyparsing import ( # noqa - Combine, - Literal as L, - Optional, - ParseException, - Regex, - Word, - ZeroOrMore, - originalTextFor, - stringEnd, - stringStart, -) - -from .markers import MARKER_EXPR, Marker -from .specifiers import LegacySpecifier, Specifier, SpecifierSet - - -class InvalidRequirement(ValueError): - """ - An invalid requirement was found, users should refer to PEP 508. - """ - - -ALPHANUM = Word(string.ascii_letters + string.digits) - -LBRACKET = L("[").suppress() -RBRACKET = L("]").suppress() -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() -COMMA = L(",").suppress() -SEMICOLON = L(";").suppress() -AT = L("@").suppress() - -PUNCTUATION = Word("-_.") -IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) -IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) - -NAME = IDENTIFIER("name") -EXTRA = IDENTIFIER - -URI = Regex(r"[^ ]+")("url") -URL = AT + URI - -EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) -EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") - -VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) -VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) - -VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY -VERSION_MANY = Combine( - VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False -)("_raw_spec") -_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY) -_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") - -VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") -VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) - -MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") -MARKER_EXPR.setParseAction( - lambda s, l, t: Marker(s[t._original_start : t._original_end]) -) -MARKER_SEPARATOR = SEMICOLON -MARKER = MARKER_SEPARATOR + MARKER_EXPR - -VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) -URL_AND_MARKER = URL + Optional(MARKER) - -NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) - -REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd -# pyparsing isn't thread safe during initialization, so we do it eagerly, see -# issue #104 -REQUIREMENT.parseString("x[]") - - -class Requirement: - """Parse a requirement. - - Parse a given requirement string into its parts, such as name, specifier, - URL, and extras. Raises InvalidRequirement on a badly-formed requirement - string. - """ - - # TODO: Can we test whether something is contained within a requirement? - # If so how do we do that? Do we need to test against the _name_ of - # the thing as well as the version? What about the markers? - # TODO: Can we normalize the name and extra name? - - def __init__(self, requirement_string: str) -> None: - try: - req = REQUIREMENT.parseString(requirement_string) - except ParseException as e: - raise InvalidRequirement( - f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}' - ) - - self.name: str = req.name - if req.url: - parsed_url = urllib.parse.urlparse(req.url) - if parsed_url.scheme == "file": - if urllib.parse.urlunparse(parsed_url) != req.url: - raise InvalidRequirement("Invalid URL given") - elif not (parsed_url.scheme and parsed_url.netloc) or ( - not parsed_url.scheme and not parsed_url.netloc - ): - raise InvalidRequirement(f"Invalid URL: {req.url}") - self.url: TOptional[str] = req.url - else: - self.url = None - self.extras: Set[str] = set(req.extras.asList() if req.extras else []) - self.specifier: SpecifierSet = SpecifierSet(req.specifier) - self.marker: TOptional[Marker] = req.marker if req.marker else None - - def __str__(self) -> str: - parts: List[str] = [self.name] - - if self.extras: - formatted_extras = ",".join(sorted(self.extras)) - parts.append(f"[{formatted_extras}]") - - if self.specifier: - parts.append(str(self.specifier)) - - if self.url: - parts.append(f"@ {self.url}") - if self.marker: - parts.append(" ") - - if self.marker: - parts.append(f"; {self.marker}") - - return "".join(parts) - - def __repr__(self) -> str: - return f"" diff --git a/third_party/python/packaging/packaging/specifiers.py b/third_party/python/packaging/packaging/specifiers.py deleted file mode 100644 index ce66bd4addbd..000000000000 --- a/third_party/python/packaging/packaging/specifiers.py +++ /dev/null @@ -1,828 +0,0 @@ -# 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. - -import abc -import functools -import itertools -import re -import warnings -from typing import ( - Callable, - Dict, - Iterable, - Iterator, - List, - Optional, - Pattern, - Set, - Tuple, - TypeVar, - Union, -) - -from .utils import canonicalize_version -from .version import LegacyVersion, Version, parse - -ParsedVersion = Union[Version, LegacyVersion] -UnparsedVersion = Union[Version, LegacyVersion, str] -VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion) -CallableOperator = Callable[[ParsedVersion, str], bool] - - -class InvalidSpecifier(ValueError): - """ - An invalid specifier was found, users should refer to PEP 440. - """ - - -class BaseSpecifier(metaclass=abc.ABCMeta): - @abc.abstractmethod - def __str__(self) -> str: - """ - Returns the str representation of this Specifier like object. This - should be representative of the Specifier itself. - """ - - @abc.abstractmethod - def __hash__(self) -> int: - """ - Returns a hash value for this Specifier like object. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Returns a boolean representing whether or not the two Specifier like - objects are equal. - """ - - @abc.abstractmethod - def __ne__(self, other: object) -> bool: - """ - Returns a boolean representing whether or not the two Specifier like - objects are not equal. - """ - - @abc.abstractproperty - def prereleases(self) -> Optional[bool]: - """ - Returns whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @prereleases.setter - def prereleases(self, value: bool) -> None: - """ - Sets whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @abc.abstractmethod - def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: - """ - Determines if the given item is contained within this specifier. - """ - - @abc.abstractmethod - def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - """ - Takes an iterable of items and filters them so that only items which - are contained within this specifier are allowed in it. - """ - - -class _IndividualSpecifier(BaseSpecifier): - - _operators: Dict[str, str] = {} - _regex: Pattern[str] - - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier(f"Invalid specifier: '{spec}'") - - self._spec: Tuple[str, str] = ( - match.group("operator").strip(), - match.group("version").strip(), - ) - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - def __repr__(self) -> str: - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return "<{}({!r}{})>".format(self.__class__.__name__, str(self), pre) - - def __str__(self) -> str: - return "{}{}".format(*self._spec) - - @property - def _canonical_spec(self) -> Tuple[str, str]: - return self._spec[0], canonicalize_version(self._spec[1]) - - def __hash__(self) -> int: - return hash(self._canonical_spec) - - def __eq__(self, other: object) -> bool: - if isinstance(other, str): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._canonical_spec == other._canonical_spec - - def __ne__(self, other: object) -> bool: - if isinstance(other, str): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._spec != other._spec - - def _get_operator(self, op: str) -> CallableOperator: - operator_callable: CallableOperator = getattr( - self, f"_compare_{self._operators[op]}" - ) - return operator_callable - - def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion: - if not isinstance(version, (LegacyVersion, Version)): - version = parse(version) - return version - - @property - def operator(self) -> str: - return self._spec[0] - - @property - def version(self) -> str: - return self._spec[1] - - @property - def prereleases(self) -> Optional[bool]: - return self._prereleases - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - def __contains__(self, item: str) -> bool: - return self.contains(item) - - def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None - ) -> bool: - - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version or LegacyVersion, this allows us to have - # a shortcut for ``"2.0" in Specifier(">=2") - normalized_item = self._coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if normalized_item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - operator_callable: CallableOperator = self._get_operator(self.operator) - return operator_callable(normalized_item, self.version) - - def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - - yielded = False - found_prereleases = [] - - 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. - for version in iterable: - parsed_version = self._coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later in case nothing - # else matches this specifier. - if parsed_version.is_prerelease and not ( - prereleases or self.prereleases - ): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the beginning. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -class LegacySpecifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(==|!=|<=|>=|<|>)) - \s* - (?P - [^,;\s)]* # Since this is a "legacy" specifier, and the version - # string can be just about anything, we match everything - # except for whitespace, a semi-colon for marker support, - # a closing paren since versions can be enclosed in - # them, and a comma since it's a version separator. - ) - """ - - _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) - - _operators = { - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - } - - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: - super().__init__(spec, prereleases) - - warnings.warn( - "Creating a LegacyVersion has been deprecated and will be " - "removed in the next major release", - DeprecationWarning, - ) - - def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion: - if not isinstance(version, LegacyVersion): - version = LegacyVersion(str(version)) - return version - - def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective == self._coerce_version(spec) - - def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective != self._coerce_version(spec) - - def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective <= self._coerce_version(spec) - - def _compare_greater_than_equal( - self, prospective: LegacyVersion, spec: str - ) -> bool: - return prospective >= self._coerce_version(spec) - - def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective < self._coerce_version(spec) - - def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective > self._coerce_version(spec) - - -def _require_version_compare( - fn: Callable[["Specifier", ParsedVersion, str], bool] -) -> Callable[["Specifier", ParsedVersion, str], bool]: - @functools.wraps(fn) - def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool: - if not isinstance(prospective, Version): - return False - return fn(self, prospective, spec) - - return wrapped - - -class Specifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(~=|==|!=|<=|>=|<|>|===)) - (?P - (?: - # The identity operators allow for an escape hatch that will - # do an exact string match of the version you wish to install. - # This will not be parsed by PEP 440 and we cannot determine - # any semantic meaning from it. This operator is discouraged - # but included entirely as an escape hatch. - (?<====) # Only match for the identity operator - \s* - [^\s]* # We just match everything, except for whitespace - # since we are only testing for strict identity. - ) - | - (?: - # The (non)equality operators allow for wild card and local - # versions to be specified so we have to define these two - # operators separately to enable that. - (?<===|!=) # Only match for equals and not equals - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - - # You cannot use a wild card and a dev or local version - # together so group them with a | and make them optional. - (?: - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - | - \.\* # Wild card syntax of .* - )? - ) - | - (?: - # The compatible operator requires at least two digits in the - # release segment. - (?<=~=) # Only match for the compatible operator - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - | - (?: - # All other operators only allow a sub set of what the - # (non)equality operators do. Specifically they do not allow - # local versions to be specified nor do they allow the prefix - # matching wild cards. - (?=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", - } - - @_require_version_compare - def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: - - # Compatible releases have an equivalent combination of >= and ==. That - # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to - # implement this in terms of the other specifiers instead of - # implementing it ourselves. The only thing we need to do is construct - # the other specifiers. - - # We want everything but the last item in the version, but we want to - # ignore suffix segments. - prefix = ".".join( - list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] - ) - - # Add the prefix notation to the end of our string - prefix += ".*" - - return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( - prospective, prefix - ) - - @_require_version_compare - def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: - - # We need special logic to handle prefix matching - if spec.endswith(".*"): - # In the case of prefix matching we want to ignore local segment. - prospective = Version(prospective.public) - # Split the spec out by dots, and pretend that there is an implicit - # dot in between a release segment and a pre-release segment. - split_spec = _version_split(spec[:-2]) # Remove the trailing .* - - # Split the prospective version out by dots, and pretend that there - # is an implicit dot in between a release segment and a pre-release - # segment. - split_prospective = _version_split(str(prospective)) - - # Shorten the prospective version to be the same length as the spec - # so that we can determine if the specifier is a prefix of the - # prospective version or not. - shortened_prospective = split_prospective[: len(split_spec)] - - # Pad out our two sides with zeros so that they both equal the same - # length. - padded_spec, padded_prospective = _pad_version( - split_spec, shortened_prospective - ) - - return padded_prospective == padded_spec - else: - # Convert our spec string into a Version - spec_version = Version(spec) - - # If the specifier does not have a local segment, then we want to - # act as if the prospective version also does not have a local - # segment. - if not spec_version.local: - prospective = Version(prospective.public) - - return prospective == spec_version - - @_require_version_compare - def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool: - return not self._compare_equal(prospective, spec) - - @_require_version_compare - def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool: - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) <= Version(spec) - - @_require_version_compare - def _compare_greater_than_equal( - self, prospective: ParsedVersion, spec: str - ) -> bool: - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) >= Version(spec) - - @_require_version_compare - def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is less than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective < spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a pre-release version, that we do not accept pre-release - # versions for the version mentioned in the specifier (e.g. <3.1 should - # not match 3.1.dev0, but should match 3.0.dev0). - if not spec.is_prerelease and prospective.is_prerelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # less than the spec version *and* it's not a pre-release of the same - # version in the spec. - return True - - @_require_version_compare - def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool: - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is greater than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective > spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a post-release version, that we do not accept - # post-release versions for the version mentioned in the specifier - # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). - if not spec.is_postrelease and prospective.is_postrelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is technically greater than, to match. - if prospective.local is not None: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # greater than the spec version *and* it's not a pre-release of the - # same version in the spec. - return True - - def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: - return str(prospective).lower() == str(spec).lower() - - @property - def prereleases(self) -> bool: - - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases - - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] - - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if parse(version).is_prerelease: - return True - - return False - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - -_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") - - -def _version_split(version: str) -> List[str]: - result: List[str] = [] - for item in version.split("."): - match = _prefix_regex.search(item) - if match: - result.extend(match.groups()) - else: - result.append(item) - return result - - -def _is_not_suffix(segment: str) -> bool: - return not any( - 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]]: - left_split, right_split = [], [] - - # Get the release segment of our versions - left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) - 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]) :]) - - # 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]))) - - return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) - - -class SpecifierSet(BaseSpecifier): - def __init__( - self, specifiers: str = "", prereleases: Optional[bool] = None - ) -> None: - - # 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()] - - # Parsed each individual specifier, attempting first to make it a - # Specifier and falling back to a LegacySpecifier. - parsed: Set[_IndividualSpecifier] = set() - for specifier in split_specifiers: - try: - parsed.add(Specifier(specifier)) - except InvalidSpecifier: - parsed.add(LegacySpecifier(specifier)) - - # Turn our parsed specifiers into a frozen set and save them for later. - self._specs = frozenset(parsed) - - # Store our prereleases value so we can use it later to determine if - # we accept prereleases or not. - self._prereleases = prereleases - - def __repr__(self) -> str: - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return "".format(str(self), pre) - - def __str__(self) -> str: - 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": - if isinstance(other, str): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - - if self._prereleases is None and other._prereleases is not None: - specifier._prereleases = other._prereleases - elif self._prereleases is not None and other._prereleases is None: - specifier._prereleases = self._prereleases - elif self._prereleases == other._prereleases: - specifier._prereleases = self._prereleases - else: - raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease " - "overrides." - ) - - return specifier - - def __eq__(self, other: object) -> bool: - if isinstance(other, (str, _IndividualSpecifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs == other._specs - - def __ne__(self, other: object) -> bool: - if isinstance(other, (str, _IndividualSpecifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs != other._specs - - def __len__(self) -> int: - return len(self._specs) - - def __iter__(self) -> Iterator[_IndividualSpecifier]: - return iter(self._specs) - - @property - def prereleases(self) -> Optional[bool]: - - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - def __contains__(self, item: UnparsedVersion) -> bool: - return self.contains(item) - - def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None - ) -> bool: - - # Ensure that our item is a Version or LegacyVersion instance. - if not isinstance(item, (LegacyVersion, Version)): - item = parse(item) - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # We can determine if we're going to allow pre-releases by looking to - # see if any of the underlying items supports them. If none of them do - # and this item is a pre-release then we do not allow it and we can - # short circuit that here. - # Note: This means that 1.0.dev1 would not be contained in something - # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 - if not prereleases and item.is_prerelease: - return False - - # We simply dispatch to the underlying specs here to make sure that the - # given version is contained within all of them. - # Note: This use of all() here means that an empty set of specifiers - # will always return True, this is an explicit design decision. - return all(s.contains(item, prereleases=prereleases) for s in self._specs) - - def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # If we have any specifiers, then we want to wrap our iterable in the - # filter method for each one, this will act as a logical AND amongst - # each specifier. - if self._specs: - for spec in self._specs: - iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iterable - # If we do not have any specifiers, then we need to have a rough filter - # which will filter out any pre-releases, unless there are no final - # releases, and which will filter out LegacyVersion in general. - else: - filtered: List[VersionTypeVar] = [] - found_prereleases: List[VersionTypeVar] = [] - - item: UnparsedVersion - parsed_version: Union[Version, LegacyVersion] - - for item in iterable: - # Ensure that we some kind of Version class for this item. - if not isinstance(item, (LegacyVersion, Version)): - parsed_version = parse(item) - else: - parsed_version = item - - # Filter out any item which is parsed as a LegacyVersion - if isinstance(parsed_version, LegacyVersion): - continue - - # Store any item which is a pre-release for later unless we've - # already found a final version or we are accepting prereleases - if parsed_version.is_prerelease and not prereleases: - if not filtered: - found_prereleases.append(item) - else: - filtered.append(item) - - # If we've found no items except for pre-releases, then we'll go - # ahead and use the pre-releases - if not filtered and found_prereleases and prereleases is None: - return found_prereleases - - return filtered diff --git a/third_party/python/packaging/packaging/tags.py b/third_party/python/packaging/packaging/tags.py deleted file mode 100644 index 82a47cdae8b3..000000000000 --- a/third_party/python/packaging/packaging/tags.py +++ /dev/null @@ -1,484 +0,0 @@ -# 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. - -import logging -import platform -import sys -import sysconfig -from importlib.machinery import EXTENSION_SUFFIXES -from typing import ( - Dict, - FrozenSet, - Iterable, - Iterator, - List, - Optional, - Sequence, - Tuple, - Union, - cast, -) - -from . import _manylinux, _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", -} - - -_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 - - -class Tag: - """ - A representation of the tag triple for a wheel. - - Instances are considered immutable and thus are hashable. Equality checking - is also supported. - """ - - __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] - - def __init__(self, interpreter: str, abi: str, platform: str) -> None: - self._interpreter = interpreter.lower() - self._abi = abi.lower() - self._platform = platform.lower() - # The __hash__ of every single element in a Set[Tag] will be evaluated each time - # that a set calls its `.disjoint()` method, which may be called hundreds of - # times when scanning a page of links for packages with tags matching that - # Set[Tag]. Pre-computing the value here produces significant speedups for - # downstream consumers. - self._hash = hash((self._interpreter, self._abi, self._platform)) - - @property - def interpreter(self) -> str: - return self._interpreter - - @property - def abi(self) -> str: - return self._abi - - @property - def platform(self) -> str: - return self._platform - - def __eq__(self, other: object) -> bool: - if not isinstance(other, 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) - ) - - def __hash__(self) -> int: - return self._hash - - def __str__(self) -> str: - return f"{self._interpreter}-{self._abi}-{self._platform}" - - def __repr__(self) -> str: - return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) - - -def parse_tag(tag: str) -> FrozenSet[Tag]: - """ - Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. - - Returning a set is required due to the possibility that the tag is a - 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("."): - tags.add(Tag(interpreter, abi, platform_)) - return frozenset(tags) - - -def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: - value = 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 - ) - return value - - -def _normalize_string(string: str) -> str: - return string.replace(".", "_").replace("-", "_") - - -def _abi3_applies(python_version: PythonVersion) -> bool: - """ - Determine if the Python version supports abi3. - - PEP 384 was first implemented in Python 3.2. - """ - return len(python_version) > 1 and tuple(python_version) >= (3, 2) - - -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]) - 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 - if with_debug or (with_debug is None and (has_refcount or has_ext)): - debug = "d" - if py_version < (3, 8): - with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) - if with_pymalloc or with_pymalloc is None: - pymalloc = "m" - if py_version < (3, 3): - unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) - if unicode_size == 4 or ( - unicode_size is None and sys.maxunicode == 0x10FFFF - ): - 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}") - abis.insert( - 0, - "cp{version}{debug}{pymalloc}{ucs4}".format( - version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 - ), - ) - return abis - - -def cpython_tags( - python_version: Optional[PythonVersion] = None, - abis: Optional[Iterable[str]] = None, - platforms: Optional[Iterable[str]] = None, - *, - warn: bool = False, -) -> Iterator[Tag]: - """ - Yields the tags for a CPython interpreter. - - The tags consist of: - - cp-- - - cp-abi3- - - cp-none- - - cp-abi3- # Older Python versions down to 3.2. - - If python_version only specifies a major version then user-provided ABIs and - the 'none' ABItag will be used. - - If 'abi3' or 'none' are specified in 'abis' then they will be yielded at - their normal position and not at the beginning. - """ - if not python_version: - python_version = sys.version_info[:2] - - interpreter = "cp{}".format(_version_nodot(python_version[:2])) - - if abis is None: - if len(python_version) > 1: - abis = _cpython_abis(python_version, warn) - else: - abis = [] - abis = list(abis) - # 'abi3' and 'none' are explicitly handled later. - for explicit_abi in ("abi3", "none"): - try: - abis.remove(explicit_abi) - except ValueError: - pass - - platforms = list(platforms or _platform_tags()) - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - if _abi3_applies(python_version): - yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) - yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) - - if _abi3_applies(python_version): - 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)) - ) - yield Tag(interpreter, "abi3", platform_) - - -def _generic_abi() -> Iterator[str]: - abi = sysconfig.get_config_var("SOABI") - if abi: - yield _normalize_string(abi) - - -def generic_tags( - interpreter: Optional[str] = None, - abis: Optional[Iterable[str]] = None, - platforms: Optional[Iterable[str]] = None, - *, - warn: bool = False, -) -> Iterator[Tag]: - """ - Yields the tags for a generic interpreter. - - The tags consist of: - - -- - - The "none" ABI will be added if it was not explicitly provided. - """ - if not interpreter: - interp_name = interpreter_name() - interp_version = interpreter_version(warn=warn) - interpreter = "".join([interp_name, interp_version]) - if abis is None: - abis = _generic_abi() - platforms = list(platforms or _platform_tags()) - abis = list(abis) - if "none" not in abis: - abis.append("none") - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - - -def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: - """ - Yields Python versions in descending order. - - After the latest version, the major-only version will be yielded, and then - all previous versions of that major version. - """ - if len(py_version) > 1: - yield "py{version}".format(version=_version_nodot(py_version[:2])) - yield "py{major}".format(major=py_version[0]) - if len(py_version) > 1: - for minor in range(py_version[1] - 1, -1, -1): - yield "py{version}".format(version=_version_nodot((py_version[0], minor))) - - -def compatible_tags( - python_version: Optional[PythonVersion] = None, - interpreter: Optional[str] = None, - platforms: Optional[Iterable[str]] = None, -) -> Iterator[Tag]: - """ - Yields the sequence of tags that are compatible with a specific version of Python. - - The tags consist of: - - py*-none- - - -none-any # ... if `interpreter` is provided. - - py*-none-any - """ - if not python_version: - python_version = sys.version_info[:2] - platforms = list(platforms or _platform_tags()) - for version in _py_interpreter_range(python_version): - for platform_ in platforms: - yield Tag(version, "none", platform_) - if interpreter: - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(python_version): - 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" - - return "i386" - - -def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: - formats = [cpu_arch] - if cpu_arch == "x86_64": - if version < (10, 4): - return [] - formats.extend(["intel", "fat64", "fat32"]) - - elif cpu_arch == "i386": - if version < (10, 4): - return [] - formats.extend(["intel", "fat32", "fat"]) - - 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") - - elif cpu_arch == "ppc": - if version > (10, 6): - return [] - formats.extend(["fat32", "fat"]) - - if cpu_arch in {"arm64", "x86_64"}: - formats.append("universal2") - - 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 -) -> Iterator[str]: - """ - Yields the platform tags for a macOS system. - - The `version` parameter is a two-item tuple specifying the macOS version to - generate platform tags for. The `arch` parameter is the CPU architecture to - generate platform tags for. Both parameters default to the appropriate value - for the current system. - """ - version_str, _, cpu_arch = platform.mac_ver() - if version is None: - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) - else: - version = version - if arch is None: - arch = _mac_arch(cpu_arch) - else: - arch = arch - - if (10, 0) <= version and version < (11, 0): - # Prior to Mac OS 11, each yearly release of Mac OS bumped the - # "minor" version number. The major version was always 10. - for minor_version in range(version[1], -1, -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( - major=10, minor=minor_version, binary_format=binary_format - ) - - if version >= (11, 0): - # Starting with Mac OS 11, each yearly release bumps the major version - # number. The minor versions are now the midyear updates. - for major_version in range(version[0], 10, -1): - 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 - ) - - if version >= (11, 0): - # Mac OS 11 on x86_64 is compatible with binaries from previous releases. - # Arm64 support was introduced in 11.0, so no Arm binaries from previous - # releases exist. - # - # 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": - 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( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) - else: - for minor_version in range(16, 3, -1): - compat_version = 10, minor_version - binary_format = "universal2" - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) - - -def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: - linux = _normalize_string(sysconfig.get_platform()) - if is_32bit: - if linux == "linux_x86_64": - linux = "linux_i686" - elif linux == "linux_aarch64": - linux = "linux_armv7l" - _, arch = linux.split("_", 1) - yield from _manylinux.platform_tags(linux, arch) - yield from _musllinux.platform_tags(arch) - yield linux - - -def _generic_platforms() -> Iterator[str]: - yield _normalize_string(sysconfig.get_platform()) - - -def _platform_tags() -> Iterator[str]: - """ - Provides the platform tags for this installation. - """ - if platform.system() == "Darwin": - return mac_platforms() - elif platform.system() == "Linux": - return _linux_platforms() - else: - return _generic_platforms() - - -def interpreter_name() -> str: - """ - Returns the name of the running interpreter. - """ - name = sys.implementation.name - return INTERPRETER_SHORT_NAMES.get(name) or name - - -def interpreter_version(*, warn: bool = False) -> str: - """ - Returns the version of the running interpreter. - """ - version = _get_config_var("py_version_nodot", warn=warn) - if version: - version = str(version) - else: - version = _version_nodot(sys.version_info[:2]) - return version - - -def _version_nodot(version: PythonVersion) -> str: - return "".join(map(str, version)) - - -def sys_tags(*, warn: bool = False) -> Iterator[Tag]: - """ - Returns the sequence of tag triples for the running interpreter. - - The order of the sequence corresponds to priority order for the - interpreter, from most to least important. - """ - - interp_name = interpreter_name() - if interp_name == "cp": - yield from cpython_tags(warn=warn) - else: - yield from generic_tags() - - yield from compatible_tags() diff --git a/third_party/python/packaging/packaging/utils.py b/third_party/python/packaging/packaging/utils.py deleted file mode 100644 index bab11b80c60f..000000000000 --- a/third_party/python/packaging/packaging/utils.py +++ /dev/null @@ -1,136 +0,0 @@ -# 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. - -import re -from typing import FrozenSet, NewType, Tuple, Union, cast - -from .tags import Tag, parse_tag -from .version import InvalidVersion, Version - -BuildTag = Union[Tuple[()], Tuple[int, str]] -NormalizedName = NewType("NormalizedName", str) - - -class InvalidWheelFilename(ValueError): - """ - An invalid wheel filename was found, users should refer to PEP 427. - """ - - -class InvalidSdistFilename(ValueError): - """ - An invalid sdist filename was found, users should refer to the packaging user guide. - """ - - -_canonicalize_regex = re.compile(r"[-_.]+") -# PEP 427: The build number must start with a digit. -_build_tag_regex = re.compile(r"(\d+)(.*)") - - -def canonicalize_name(name: str) -> NormalizedName: - # This is taken from PEP 503. - value = _canonicalize_regex.sub("-", name).lower() - return cast(NormalizedName, value) - - -def canonicalize_version(version: Union[Version, str]) -> str: - """ - This is very similar to Version.__str__, but has one subtle difference - with the way it handles the release segment. - """ - if isinstance(version, str): - try: - parsed = Version(version) - except InvalidVersion: - # Legacy versions cannot be normalized - return version - else: - parsed = version - - parts = [] - - # Epoch - if parsed.epoch != 0: - parts.append(f"{parsed.epoch}!") - - # Release segment - # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release))) - - # Pre-release - if parsed.pre is not None: - parts.append("".join(str(x) for x in parsed.pre)) - - # Post-release - if parsed.post is not None: - parts.append(f".post{parsed.post}") - - # Development release - if parsed.dev is not None: - parts.append(f".dev{parsed.dev}") - - # Local version segment - if parsed.local is not None: - parts.append(f"+{parsed.local}") - - return "".join(parts) - - -def parse_wheel_filename( - filename: str, -) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: - if not filename.endswith(".whl"): - raise InvalidWheelFilename( - f"Invalid wheel filename (extension must be '.whl'): {filename}" - ) - - filename = filename[:-4] - dashes = filename.count("-") - if dashes not in (4, 5): - raise InvalidWheelFilename( - f"Invalid wheel filename (wrong number of parts): {filename}" - ) - - 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}") - name = canonicalize_name(name_part) - version = Version(parts[1]) - if dashes == 5: - build_part = parts[2] - build_match = _build_tag_regex.match(build_part) - if build_match is None: - raise InvalidWheelFilename( - f"Invalid build number: {build_part} in '{filename}'" - ) - build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) - else: - build = () - tags = parse_tag(parts[-1]) - 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")] - else: - raise InvalidSdistFilename( - f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" - 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("-") - if not sep: - raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") - - name = canonicalize_name(name_part) - version = Version(version_part) - return (name, version) diff --git a/third_party/python/packaging/packaging/version.py b/third_party/python/packaging/packaging/version.py deleted file mode 100644 index de9a09a4ed3b..000000000000 --- a/third_party/python/packaging/packaging/version.py +++ /dev/null @@ -1,504 +0,0 @@ -# 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. - -import collections -import itertools -import re -import warnings -from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union - -from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType - -__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] - -InfiniteTypes = Union[InfinityType, NegativeInfinityType] -PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] -SubLocalType = Union[InfiniteTypes, int, str] -LocalType = Union[ - NegativeInfinityType, - Tuple[ - Union[ - SubLocalType, - Tuple[SubLocalType, str], - Tuple[NegativeInfinityType, SubLocalType], - ], - ..., - ], -] -CmpKey = Tuple[ - int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType -] -LegacyCmpKey = Tuple[int, Tuple[str, ...]] -VersionComparisonMethod = Callable[ - [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool -] - -_Version = collections.namedtuple( - "_Version", ["epoch", "release", "dev", "pre", "post", "local"] -) - - -def parse(version: str) -> Union["LegacyVersion", "Version"]: - """ - Parse the given version string and return either a :class:`Version` object - or a :class:`LegacyVersion` object depending on if the given version is - a valid PEP 440 version or a legacy version. - """ - try: - return Version(version) - except InvalidVersion: - return LegacyVersion(version) - - -class InvalidVersion(ValueError): - """ - An invalid version was found, users should refer to PEP 440. - """ - - -class _BaseVersion: - _key: Union[CmpKey, LegacyCmpKey] - - def __hash__(self) -> int: - return hash(self._key) - - # 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: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key < other._key - - def __le__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key <= other._key - - def __eq__(self, other: object) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key == other._key - - def __ge__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key >= other._key - - def __gt__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key > other._key - - def __ne__(self, other: object) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key != other._key - - -class LegacyVersion(_BaseVersion): - def __init__(self, version: str) -> None: - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - warnings.warn( - "Creating a LegacyVersion has been deprecated and will be " - "removed in the next major release", - DeprecationWarning, - ) - - def __str__(self) -> str: - return self._version - - def __repr__(self) -> str: - return f"" - - @property - def public(self) -> str: - return self._version - - @property - def base_version(self) -> str: - return self._version - - @property - def epoch(self) -> int: - return -1 - - @property - def release(self) -> None: - return None - - @property - def pre(self) -> None: - return None - - @property - def post(self) -> None: - return None - - @property - def dev(self) -> None: - return None - - @property - def local(self) -> None: - return None - - @property - def is_prerelease(self) -> bool: - return False - - @property - def is_postrelease(self) -> bool: - return False - - @property - def is_devrelease(self) -> bool: - return False - - -_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) - -_legacy_version_replacement_map = { - "pre": "c", - "preview": "c", - "-": "final-", - "rc": "c", - "dev": "@", -} - - -def _parse_version_parts(s: str) -> Iterator[str]: - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version: str) -> LegacyCmpKey: - - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts: List[str] = [] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - - return epoch, tuple(parts) - - -# Deliberately not anchored to the start and end of the string, to make it -# easier for 3rd party code to reuse -VERSION_PATTERN = r""" - v? - (?: - (?:(?P[0-9]+)!)? # epoch - (?P[0-9]+(?:\.[0-9]+)*) # release segment - (?P
                                          # pre-release
-            [-_\.]?
-            (?P(a|b|c|rc|alpha|beta|pre|preview))
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-        (?P                                         # post release
-            (?:-(?P[0-9]+))
-            |
-            (?:
-                [-_\.]?
-                (?Ppost|rev|r)
-                [-_\.]?
-                (?P[0-9]+)?
-            )
-        )?
-        (?P                                          # dev release
-            [-_\.]?
-            (?Pdev)
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-    )
-    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
-"""
-
-
-class Version(_BaseVersion):
-
-    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
-
-    def __init__(self, version: str) -> None:
-
-        # Validate the version and parse it into pieces
-        match = self._regex.search(version)
-        if not match:
-            raise InvalidVersion(f"Invalid version: '{version}'")
-
-        # 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")),
-            post=_parse_letter_version(
-                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")),
-        )
-
-        # Generate a key which will be used for sorting
-        self._key = _cmpkey(
-            self._version.epoch,
-            self._version.release,
-            self._version.pre,
-            self._version.post,
-            self._version.dev,
-            self._version.local,
-        )
-
-    def __repr__(self) -> str:
-        return f""
-
-    def __str__(self) -> str:
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append(f"{self.epoch}!")
-
-        # Release segment
-        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))
-
-        # Post-release
-        if self.post is not None:
-            parts.append(f".post{self.post}")
-
-        # Development release
-        if self.dev is not None:
-            parts.append(f".dev{self.dev}")
-
-        # Local version segment
-        if self.local is not None:
-            parts.append(f"+{self.local}")
-
-        return "".join(parts)
-
-    @property
-    def epoch(self) -> int:
-        _epoch: int = self._version.epoch
-        return _epoch
-
-    @property
-    def release(self) -> Tuple[int, ...]:
-        _release: Tuple[int, ...] = self._version.release
-        return _release
-
-    @property
-    def pre(self) -> Optional[Tuple[str, int]]:
-        _pre: Optional[Tuple[str, int]] = self._version.pre
-        return _pre
-
-    @property
-    def post(self) -> Optional[int]:
-        return self._version.post[1] if self._version.post else None
-
-    @property
-    def dev(self) -> Optional[int]:
-        return self._version.dev[1] if self._version.dev else None
-
-    @property
-    def local(self) -> Optional[str]:
-        if self._version.local:
-            return ".".join(str(x) for x in self._version.local)
-        else:
-            return None
-
-    @property
-    def public(self) -> str:
-        return str(self).split("+", 1)[0]
-
-    @property
-    def base_version(self) -> str:
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append(f"{self.epoch}!")
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        return "".join(parts)
-
-    @property
-    def is_prerelease(self) -> bool:
-        return self.dev is not None or self.pre is not None
-
-    @property
-    def is_postrelease(self) -> bool:
-        return self.post is not None
-
-    @property
-    def is_devrelease(self) -> bool:
-        return self.dev is not None
-
-    @property
-    def major(self) -> int:
-        return self.release[0] if len(self.release) >= 1 else 0
-
-    @property
-    def minor(self) -> int:
-        return self.release[1] if len(self.release) >= 2 else 0
-
-    @property
-    def micro(self) -> int:
-        return self.release[2] if len(self.release) >= 3 else 0
-
-
-def _parse_letter_version(
-    letter: str, number: Union[str, bytes, SupportsInt]
-) -> Optional[Tuple[str, int]]:
-
-    if letter:
-        # We consider there to be an implicit 0 in a pre-release if there is
-        # not a numeral associated with it.
-        if number is None:
-            number = 0
-
-        # We normalize any letters to their lower case form
-        letter = letter.lower()
-
-        # 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"
-
-        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"
-
-        return letter, int(number)
-
-    return None
-
-
-_local_version_separators = re.compile(r"[\._-]")
-
-
-def _parse_local_version(local: str) -> Optional[LocalType]:
-    """
-    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
-    """
-    if local is not None:
-        return tuple(
-            part.lower() if not part.isdigit() else int(part)
-            for part in _local_version_separators.split(local)
-        )
-    return None
-
-
-def _cmpkey(
-    epoch: int,
-    release: Tuple[int, ...],
-    pre: Optional[Tuple[str, int]],
-    post: Optional[Tuple[str, int]],
-    dev: Optional[Tuple[str, int]],
-    local: Optional[Tuple[SubLocalType]],
-) -> CmpKey:
-
-    # When we compare a release version, we want to compare it with all of the
-    # trailing zeros removed. So we'll use a reverse the list, drop all the now
-    # leading zeros until we come to something non zero, then take the rest
-    # 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))))
-    )
-
-    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
-    # We'll do this by abusing the pre segment, but we _only_ want to do this
-    # if there is not a pre or a post segment. If we have one of those then
-    # the normal sorting rules will handle this case correctly.
-    if pre is None and post is None and dev is not None:
-        _pre: PrePostDevType = NegativeInfinity
-    # Versions without a pre-release (except as noted above) should sort after
-    # those with one.
-    elif pre is None:
-        _pre = Infinity
-    else:
-        _pre = pre
-
-    # Versions without a post segment should sort before those with one.
-    if post is None:
-        _post: PrePostDevType = NegativeInfinity
-
-    else:
-        _post = post
-
-    # Versions without a development segment should sort after those with one.
-    if dev is None:
-        _dev: PrePostDevType = Infinity
-
-    else:
-        _dev = dev
-
-    if local is None:
-        # Versions without a local segment should sort before those with one.
-        _local: LocalType = NegativeInfinity
-    else:
-        # Versions with a local segment need that segment parsed to implement
-        # the sorting rules in PEP440.
-        # - Alpha numeric segments sort before numeric segments
-        # - Alpha numeric segments sort lexicographically
-        # - Numeric segments sort numerically
-        # - 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
-        )
-
-    return epoch, _release, _pre, _post, _dev, _local
diff --git a/third_party/python/ply/ply.egg-info/PKG-INFO b/third_party/python/ply/ply.egg-info/PKG-INFO
deleted file mode 100644
index 6eedf4259539..000000000000
--- a/third_party/python/ply/ply.egg-info/PKG-INFO
+++ /dev/null
@@ -1,22 +0,0 @@
-Metadata-Version: 1.1
-Name: ply
-Version: 3.10
-Summary: Python Lex & Yacc
-Home-page: http://www.dabeaz.com/ply/
-Author: David Beazley
-Author-email: dave@dabeaz.com
-License: BSD
-Description: 
-        PLY is yet another implementation of lex and yacc for Python. Some notable
-        features include the fact that its implemented entirely in Python and it
-        uses LALR(1) parsing which is efficient and well suited for larger grammars.
-        
-        PLY provides most of the standard lex/yacc features including support for empty 
-        productions, precedence rules, error recovery, and support for ambiguous grammars. 
-        
-        PLY is extremely easy to use and provides very extensive error checking. 
-        It is compatible with both Python 2 and Python 3.
-        
-Platform: UNKNOWN
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 2
diff --git a/third_party/python/ply/ply.egg-info/SOURCES.txt b/third_party/python/ply/ply.egg-info/SOURCES.txt
deleted file mode 100644
index 2dff7dd29b8f..000000000000
--- a/third_party/python/ply/ply.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,172 +0,0 @@
-ANNOUNCE
-CHANGES
-MANIFEST.in
-README.md
-TODO
-setup.cfg
-setup.py
-doc/internal.html
-doc/makedoc.py
-doc/ply.html
-example/README
-example/cleanup.sh
-example/BASIC/README
-example/BASIC/basic.py
-example/BASIC/basiclex.py
-example/BASIC/basiclog.py
-example/BASIC/basinterp.py
-example/BASIC/basparse.py
-example/BASIC/dim.bas
-example/BASIC/func.bas
-example/BASIC/gcd.bas
-example/BASIC/gosub.bas
-example/BASIC/hello.bas
-example/BASIC/linear.bas
-example/BASIC/maxsin.bas
-example/BASIC/powers.bas
-example/BASIC/rand.bas
-example/BASIC/sales.bas
-example/BASIC/sears.bas
-example/BASIC/sqrt1.bas
-example/BASIC/sqrt2.bas
-example/GardenSnake/GardenSnake.py
-example/GardenSnake/README
-example/ansic/README
-example/ansic/clex.py
-example/ansic/cparse.py
-example/calc/calc.py
-example/calcdebug/calc.py
-example/calceof/calc.py
-example/classcalc/calc.py
-example/closurecalc/calc.py
-example/hedit/hedit.py
-example/newclasscalc/calc.py
-example/optcalc/README
-example/optcalc/calc.py
-example/unicalc/calc.py
-example/yply/README
-example/yply/ylex.py
-example/yply/yparse.py
-example/yply/yply.py
-ply/__init__.py
-ply/cpp.py
-ply/ctokens.py
-ply/lex.py
-ply/yacc.py
-ply/ygen.py
-ply.egg-info/PKG-INFO
-ply.egg-info/SOURCES.txt
-ply.egg-info/dependency_links.txt
-ply.egg-info/top_level.txt
-test/README
-test/calclex.py
-test/cleanup.sh
-test/lex_closure.py
-test/lex_doc1.py
-test/lex_dup1.py
-test/lex_dup2.py
-test/lex_dup3.py
-test/lex_empty.py
-test/lex_error1.py
-test/lex_error2.py
-test/lex_error3.py
-test/lex_error4.py
-test/lex_hedit.py
-test/lex_ignore.py
-test/lex_ignore2.py
-test/lex_literal1.py
-test/lex_literal2.py
-test/lex_literal3.py
-test/lex_many_tokens.py
-test/lex_module.py
-test/lex_module_import.py
-test/lex_object.py
-test/lex_opt_alias.py
-test/lex_optimize.py
-test/lex_optimize2.py
-test/lex_optimize3.py
-test/lex_re1.py
-test/lex_re2.py
-test/lex_re3.py
-test/lex_rule1.py
-test/lex_rule2.py
-test/lex_rule3.py
-test/lex_state1.py
-test/lex_state2.py
-test/lex_state3.py
-test/lex_state4.py
-test/lex_state5.py
-test/lex_state_noerror.py
-test/lex_state_norule.py
-test/lex_state_try.py
-test/lex_token1.py
-test/lex_token2.py
-test/lex_token3.py
-test/lex_token4.py
-test/lex_token5.py
-test/lex_token_dup.py
-test/testlex.py
-test/testyacc.py
-test/yacc_badargs.py
-test/yacc_badid.py
-test/yacc_badprec.py
-test/yacc_badprec2.py
-test/yacc_badprec3.py
-test/yacc_badrule.py
-test/yacc_badtok.py
-test/yacc_dup.py
-test/yacc_error1.py
-test/yacc_error2.py
-test/yacc_error3.py
-test/yacc_error4.py
-test/yacc_error5.py
-test/yacc_error6.py
-test/yacc_error7.py
-test/yacc_inf.py
-test/yacc_literal.py
-test/yacc_misplaced.py
-test/yacc_missing1.py
-test/yacc_nested.py
-test/yacc_nodoc.py
-test/yacc_noerror.py
-test/yacc_nop.py
-test/yacc_notfunc.py
-test/yacc_notok.py
-test/yacc_prec1.py
-test/yacc_rr.py
-test/yacc_rr_unused.py
-test/yacc_simple.py
-test/yacc_sr.py
-test/yacc_term1.py
-test/yacc_unicode_literals.py
-test/yacc_unused.py
-test/yacc_unused_rule.py
-test/yacc_uprec.py
-test/yacc_uprec2.py
-test/pkg_test1/__init__.py
-test/pkg_test1/parsing/__init__.py
-test/pkg_test1/parsing/calclex.py
-test/pkg_test1/parsing/calcparse.py
-test/pkg_test2/__init__.py
-test/pkg_test2/parsing/__init__.py
-test/pkg_test2/parsing/calclex.py
-test/pkg_test2/parsing/calcparse.py
-test/pkg_test3/__init__.py
-test/pkg_test3/generated/__init__.py
-test/pkg_test3/parsing/__init__.py
-test/pkg_test3/parsing/calclex.py
-test/pkg_test3/parsing/calcparse.py
-test/pkg_test4/__init__.py
-test/pkg_test4/parsing/__init__.py
-test/pkg_test4/parsing/calclex.py
-test/pkg_test4/parsing/calcparse.py
-test/pkg_test5/__init__.py
-test/pkg_test5/parsing/__init__.py
-test/pkg_test5/parsing/calclex.py
-test/pkg_test5/parsing/calcparse.py
-test/pkg_test6/__init__.py
-test/pkg_test6/parsing/__init__.py
-test/pkg_test6/parsing/calclex.py
-test/pkg_test6/parsing/calcparse.py
-test/pkg_test6/parsing/expression.py
-test/pkg_test6/parsing/statement.py
\ No newline at end of file
diff --git a/third_party/python/ply/ply.egg-info/dependency_links.txt b/third_party/python/ply/ply.egg-info/dependency_links.txt
deleted file mode 100644
index 8b137891791f..000000000000
--- a/third_party/python/ply/ply.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/third_party/python/ply/ply.egg-info/top_level.txt b/third_party/python/ply/ply.egg-info/top_level.txt
deleted file mode 100644
index 90412f06833c..000000000000
--- a/third_party/python/ply/ply.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-ply
diff --git a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/LICENSE b/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/LICENSE
deleted file mode 100644
index 1bf98523e331..000000000000
--- a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/METADATA b/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/METADATA
deleted file mode 100644
index 2206ad94ed99..000000000000
--- a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/METADATA
+++ /dev/null
@@ -1,104 +0,0 @@
-Metadata-Version: 2.1
-Name: pyparsing
-Version: 2.4.7
-Summary: Python parsing module
-Home-page: https://github.com/pyparsing/pyparsing/
-Author: Paul McGuire
-Author-email: ptmcg@users.sourceforge.net
-License: MIT License
-Download-URL: https://pypi.org/project/pyparsing/
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: Intended Audience :: Information Technology
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
-
-PyParsing -- A Python Parsing Module
-====================================
-
-|Build Status|
-
-Introduction
-============
-
-The pyparsing module is an alternative approach to creating and
-executing simple grammars, vs. the traditional lex/yacc approach, or the
-use of regular expressions. The pyparsing module provides a library of
-classes that client code uses to construct the grammar directly in
-Python code.
-
-*[Since first writing this description of pyparsing in late 2003, this
-technique for developing parsers has become more widespread, under the
-name Parsing Expression Grammars - PEGs. See more information on PEGs at*
-https://en.wikipedia.org/wiki/Parsing_expression_grammar *.]*
-
-Here is a program to parse ``"Hello, World!"`` (or any greeting of the form
-``"salutation, addressee!"``):
-
-.. code:: python
-
-    from pyparsing import Word, alphas
-    greet = Word(alphas) + "," + Word(alphas) + "!"
-    hello = "Hello, World!"
-    print(hello, "->", greet.parseString(hello))
-
-The program outputs the following::
-
-    Hello, World! -> ['Hello', ',', 'World', '!']
-
-The Python representation of the grammar is quite readable, owing to the
-self-explanatory class names, and the use of '+', '|' and '^' operator
-definitions.
-
-The parsed results returned from ``parseString()`` can be accessed as a
-nested list, a dictionary, or an object with named attributes.
-
-The pyparsing module handles some of the problems that are typically
-vexing when writing text parsers:
-
-- extra or missing whitespace (the above program will also handle ``"Hello,World!"``, ``"Hello , World !"``, etc.)
-- quoted strings
-- embedded comments
-
-The examples directory includes a simple SQL parser, simple CORBA IDL
-parser, a config file parser, a chemical formula parser, and a four-
-function algebraic notation parser, among many others.
-
-Documentation
-=============
-
-There are many examples in the online docstrings of the classes
-and methods in pyparsing. You can find them compiled into online docs
-at https://pyparsing-docs.readthedocs.io/en/latest/. Additional
-documentation resources and project info are listed in the online
-GitHub wiki, at https://github.com/pyparsing/pyparsing/wiki. An
-entire directory of examples is at
-https://github.com/pyparsing/pyparsing/tree/master/examples.
-
-License
-=======
-
-MIT License. See header of pyparsing.py
-
-History
-=======
-
-See CHANGES file.
-
-.. |Build Status| image:: https://travis-ci.org/pyparsing/pyparsing.svg?branch=master
-   :target: https://travis-ci.org/pyparsing/pyparsing
-
-
diff --git a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/RECORD b/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/RECORD
deleted file mode 100644
index 39a2bc593719..000000000000
--- a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/RECORD
+++ /dev/null
@@ -1,6 +0,0 @@
-pyparsing.py,sha256=oxX_ZOz8t-eros-UWY7nJgcdUgD-rQ53Ck0qp7_v3Ig,273365
-pyparsing-2.4.7.dist-info/LICENSE,sha256=ENUSChaAWAT_2otojCIL-06POXQbVzIGBNRVowngGXI,1023
-pyparsing-2.4.7.dist-info/METADATA,sha256=Ry40soZZiZrAkSMQT_KU1_1REe6FKa5UWzbT6YA8Mxs,3636
-pyparsing-2.4.7.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
-pyparsing-2.4.7.dist-info/top_level.txt,sha256=eUOjGzJVhlQ3WS2rFAy2mN3LX_7FKTM5GSJ04jfnLmU,10
-pyparsing-2.4.7.dist-info/RECORD,,
diff --git a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/WHEEL b/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/WHEEL
deleted file mode 100644
index ef99c6cf3283..000000000000
--- a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/WHEEL
+++ /dev/null
@@ -1,6 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.34.2)
-Root-Is-Purelib: true
-Tag: py2-none-any
-Tag: py3-none-any
-
diff --git a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/top_level.txt b/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/top_level.txt
deleted file mode 100644
index 210dfec50b01..000000000000
--- a/third_party/python/pyparsing/pyparsing-2.4.7.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-pyparsing
diff --git a/third_party/python/pyparsing/pyparsing.py b/third_party/python/pyparsing/pyparsing.py
deleted file mode 100644
index 581d5bbb8a95..000000000000
--- a/third_party/python/pyparsing/pyparsing.py
+++ /dev/null
@@ -1,7107 +0,0 @@
-# -*- coding: utf-8 -*-
-# module pyparsing.py
-#
-# Copyright (c) 2003-2019  Paul T. McGuire
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__doc__ = \
-"""
-pyparsing module - Classes and methods to define and execute parsing grammars
-=============================================================================
-
-The pyparsing module is an alternative approach to creating and
-executing simple grammars, vs. the traditional lex/yacc approach, or the
-use of regular expressions.  With pyparsing, you don't need to learn
-a new syntax for defining grammars or matching expressions - the parsing
-module provides a library of classes that you use to construct the
-grammar directly in Python.
-
-Here is a program to parse "Hello, World!" (or any greeting of the form
-``", !"``), built up using :class:`Word`,
-:class:`Literal`, and :class:`And` elements
-(the :class:`'+'` operators create :class:`And` expressions,
-and the strings are auto-converted to :class:`Literal` expressions)::
-
-    from pyparsing import Word, alphas
-
-    # define grammar of a greeting
-    greet = Word(alphas) + "," + Word(alphas) + "!"
-
-    hello = "Hello, World!"
-    print (hello, "->", greet.parseString(hello))
-
-The program outputs the following::
-
-    Hello, World! -> ['Hello', ',', 'World', '!']
-
-The Python representation of the grammar is quite readable, owing to the
-self-explanatory class names, and the use of '+', '|' and '^' operators.
-
-The :class:`ParseResults` object returned from
-:class:`ParserElement.parseString` can be
-accessed as a nested list, a dictionary, or an object with named
-attributes.
-
-The pyparsing module handles some of the problems that are typically
-vexing when writing text parsers:
-
-  - extra or missing whitespace (the above program will also handle
-    "Hello,World!", "Hello  ,  World  !", etc.)
-  - quoted strings
-  - embedded comments
-
-
-Getting Started -
------------------
-Visit the classes :class:`ParserElement` and :class:`ParseResults` to
-see the base classes that most other pyparsing
-classes inherit from. Use the docstrings for examples of how to:
-
- - construct literal match expressions from :class:`Literal` and
-   :class:`CaselessLiteral` classes
- - construct character word-group expressions using the :class:`Word`
-   class
- - see how to create repetitive expressions using :class:`ZeroOrMore`
-   and :class:`OneOrMore` classes
- - use :class:`'+'`, :class:`'|'`, :class:`'^'`,
-   and :class:`'&'` operators to combine simple expressions into
-   more complex ones
- - associate names with your parsed results using
-   :class:`ParserElement.setResultsName`
- - access the parsed data, which is returned as a :class:`ParseResults`
-   object
- - find some helpful expression short-cuts like :class:`delimitedList`
-   and :class:`oneOf`
- - find more useful common expressions in the :class:`pyparsing_common`
-   namespace class
-"""
-
-__version__ = "2.4.7"
-__versionTime__ = "30 Mar 2020 00:43 UTC"
-__author__ = "Paul McGuire "
-
-import string
-from weakref import ref as wkref
-import copy
-import sys
-import warnings
-import re
-import sre_constants
-import collections
-import pprint
-import traceback
-import types
-from datetime import datetime
-from operator import itemgetter
-import itertools
-from functools import wraps
-from contextlib import contextmanager
-
-try:
-    # Python 3
-    from itertools import filterfalse
-except ImportError:
-    from itertools import ifilterfalse as filterfalse
-
-try:
-    from _thread import RLock
-except ImportError:
-    from threading import RLock
-
-try:
-    # Python 3
-    from collections.abc import Iterable
-    from collections.abc import MutableMapping, Mapping
-except ImportError:
-    # Python 2.7
-    from collections import Iterable
-    from collections import MutableMapping, Mapping
-
-try:
-    from collections import OrderedDict as _OrderedDict
-except ImportError:
-    try:
-        from ordereddict import OrderedDict as _OrderedDict
-    except ImportError:
-        _OrderedDict = None
-
-try:
-    from types import SimpleNamespace
-except ImportError:
-    class SimpleNamespace: pass
-
-# version compatibility configuration
-__compat__ = SimpleNamespace()
-__compat__.__doc__ = """
-    A cross-version compatibility configuration for pyparsing features that will be
-    released in a future version. By setting values in this configuration to True,
-    those features can be enabled in prior versions for compatibility development
-    and testing.
-
-     - collect_all_And_tokens - flag to enable fix for Issue #63 that fixes erroneous grouping
-       of results names when an And expression is nested within an Or or MatchFirst; set to
-       True to enable bugfix released in pyparsing 2.3.0, or False to preserve
-       pre-2.3.0 handling of named results
-"""
-__compat__.collect_all_And_tokens = True
-
-__diag__ = SimpleNamespace()
-__diag__.__doc__ = """
-Diagnostic configuration (all default to False)
-     - warn_multiple_tokens_in_named_alternation - flag to enable warnings when a results
-       name is defined on a MatchFirst or Or expression with one or more And subexpressions
-       (only warns if __compat__.collect_all_And_tokens is False)
-     - warn_ungrouped_named_tokens_in_collection - flag to enable warnings when a results
-       name is defined on a containing expression with ungrouped subexpressions that also
-       have results names
-     - warn_name_set_on_empty_Forward - flag to enable warnings whan a Forward is defined
-       with a results name, but has no contents defined
-     - warn_on_multiple_string_args_to_oneof - flag to enable warnings whan oneOf is
-       incorrectly called with multiple str arguments
-     - enable_debug_on_named_expressions - flag to auto-enable debug on all subsequent
-       calls to ParserElement.setName()
-"""
-__diag__.warn_multiple_tokens_in_named_alternation = False
-__diag__.warn_ungrouped_named_tokens_in_collection = False
-__diag__.warn_name_set_on_empty_Forward = False
-__diag__.warn_on_multiple_string_args_to_oneof = False
-__diag__.enable_debug_on_named_expressions = False
-__diag__._all_names = [nm for nm in vars(__diag__) if nm.startswith("enable_") or nm.startswith("warn_")]
-
-def _enable_all_warnings():
-    __diag__.warn_multiple_tokens_in_named_alternation = True
-    __diag__.warn_ungrouped_named_tokens_in_collection = True
-    __diag__.warn_name_set_on_empty_Forward = True
-    __diag__.warn_on_multiple_string_args_to_oneof = True
-__diag__.enable_all_warnings = _enable_all_warnings
-
-
-__all__ = ['__version__', '__versionTime__', '__author__', '__compat__', '__diag__',
-           'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
-           'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
-           'PrecededBy', 'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
-           'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
-           'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
-           'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter',
-           'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', 'Char',
-           'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
-           'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
-           'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
-           'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
-           'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
-           'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
-           'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity',
-           'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
-           'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
-           'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation', 'locatedExpr', 'withClass',
-           'CloseMatch', 'tokenMap', 'pyparsing_common', 'pyparsing_unicode', 'unicode_set',
-           'conditionAsParseAction', 're',
-           ]
-
-system_version = tuple(sys.version_info)[:3]
-PY_3 = system_version[0] == 3
-if PY_3:
-    _MAX_INT = sys.maxsize
-    basestring = str
-    unichr = chr
-    unicode = str
-    _ustr = str
-
-    # build list of single arg builtins, that can be used as parse actions
-    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
-
-else:
-    _MAX_INT = sys.maxint
-    range = xrange
-
-    def _ustr(obj):
-        """Drop-in replacement for str(obj) that tries to be Unicode
-        friendly. It first tries str(obj). If that fails with
-        a UnicodeEncodeError, then it tries unicode(obj). It then
-        < returns the unicode object | encodes it with the default
-        encoding | ... >.
-        """
-        if isinstance(obj, unicode):
-            return obj
-
-        try:
-            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
-            # it won't break any existing code.
-            return str(obj)
-
-        except UnicodeEncodeError:
-            # Else encode it
-            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
-            xmlcharref = Regex(r'&#\d+;')
-            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
-            return xmlcharref.transformString(ret)
-
-    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
-    singleArgBuiltins = []
-    import __builtin__
-
-    for fname in "sum len sorted reversed list tuple set any all min max".split():
-        try:
-            singleArgBuiltins.append(getattr(__builtin__, fname))
-        except AttributeError:
-            continue
-
-_generatorType = type((y for y in range(1)))
-
-def _xml_escape(data):
-    """Escape &, <, >, ", ', etc. in a string of data."""
-
-    # ampersand must be replaced first
-    from_symbols = '&><"\''
-    to_symbols = ('&' + s + ';' for s in "amp gt lt quot apos".split())
-    for from_, to_ in zip(from_symbols, to_symbols):
-        data = data.replace(from_, to_)
-    return data
-
-alphas = string.ascii_uppercase + string.ascii_lowercase
-nums = "0123456789"
-hexnums = nums + "ABCDEFabcdef"
-alphanums = alphas + nums
-_bslash = chr(92)
-printables = "".join(c for c in string.printable if c not in string.whitespace)
-
-
-def conditionAsParseAction(fn, message=None, fatal=False):
-    msg = message if message is not None else "failed user-defined condition"
-    exc_type = ParseFatalException if fatal else ParseException
-    fn = _trim_arity(fn)
-
-    @wraps(fn)
-    def pa(s, l, t):
-        if not bool(fn(s, l, t)):
-            raise exc_type(s, l, msg)
-
-    return pa
-
-class ParseBaseException(Exception):
-    """base exception class for all parsing runtime exceptions"""
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__(self, pstr, loc=0, msg=None, elem=None):
-        self.loc = loc
-        if msg is None:
-            self.msg = pstr
-            self.pstr = ""
-        else:
-            self.msg = msg
-            self.pstr = pstr
-        self.parserElement = elem
-        self.args = (pstr, loc, msg)
-
-    @classmethod
-    def _from_exception(cls, pe):
-        """
-        internal factory method to simplify creating one type of ParseException
-        from another - avoids having __init__ signature conflicts among subclasses
-        """
-        return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
-
-    def __getattr__(self, aname):
-        """supported attributes by name are:
-           - lineno - returns the line number of the exception text
-           - col - returns the column number of the exception text
-           - line - returns the line containing the exception text
-        """
-        if aname == "lineno":
-            return lineno(self.loc, self.pstr)
-        elif aname in ("col", "column"):
-            return col(self.loc, self.pstr)
-        elif aname == "line":
-            return line(self.loc, self.pstr)
-        else:
-            raise AttributeError(aname)
-
-    def __str__(self):
-        if self.pstr:
-            if self.loc >= len(self.pstr):
-                foundstr = ', found end of text'
-            else:
-                foundstr = (', found %r' % self.pstr[self.loc:self.loc + 1]).replace(r'\\', '\\')
-        else:
-            foundstr = ''
-        return ("%s%s  (at char %d), (line:%d, col:%d)" %
-                   (self.msg, foundstr, self.loc, self.lineno, self.column))
-    def __repr__(self):
-        return _ustr(self)
-    def markInputline(self, markerString=">!<"):
-        """Extracts the exception line from the input string, and marks
-           the location of the exception with a special symbol.
-        """
-        line_str = self.line
-        line_column = self.column - 1
-        if markerString:
-            line_str = "".join((line_str[:line_column],
-                                markerString, line_str[line_column:]))
-        return line_str.strip()
-    def __dir__(self):
-        return "lineno col line".split() + dir(type(self))
-
-class ParseException(ParseBaseException):
-    """
-    Exception thrown when parse expressions don't match class;
-    supported attributes by name are:
-    - lineno - returns the line number of the exception text
-    - col - returns the column number of the exception text
-    - line - returns the line containing the exception text
-
-    Example::
-
-        try:
-            Word(nums).setName("integer").parseString("ABC")
-        except ParseException as pe:
-            print(pe)
-            print("column: {}".format(pe.col))
-
-    prints::
-
-       Expected integer (at char 0), (line:1, col:1)
-        column: 1
-
-    """
-
-    @staticmethod
-    def explain(exc, depth=16):
-        """
-        Method to take an exception and translate the Python internal traceback into a list
-        of the pyparsing expressions that caused the exception to be raised.
-
-        Parameters:
-
-         - exc - exception raised during parsing (need not be a ParseException, in support
-           of Python exceptions that might be raised in a parse action)
-         - depth (default=16) - number of levels back in the stack trace to list expression
-           and function names; if None, the full stack trace names will be listed; if 0, only
-           the failing input line, marker, and exception string will be shown
-
-        Returns a multi-line string listing the ParserElements and/or function names in the
-        exception's stack trace.
-
-        Note: the diagnostic output will include string representations of the expressions
-        that failed to parse. These representations will be more helpful if you use `setName` to
-        give identifiable names to your expressions. Otherwise they will use the default string
-        forms, which may be cryptic to read.
-
-        explain() is only supported under Python 3.
-        """
-        import inspect
-
-        if depth is None:
-            depth = sys.getrecursionlimit()
-        ret = []
-        if isinstance(exc, ParseBaseException):
-            ret.append(exc.line)
-            ret.append(' ' * (exc.col - 1) + '^')
-        ret.append("{0}: {1}".format(type(exc).__name__, exc))
-
-        if depth > 0:
-            callers = inspect.getinnerframes(exc.__traceback__, context=depth)
-            seen = set()
-            for i, ff in enumerate(callers[-depth:]):
-                frm = ff[0]
-
-                f_self = frm.f_locals.get('self', None)
-                if isinstance(f_self, ParserElement):
-                    if frm.f_code.co_name not in ('parseImpl', '_parseNoCache'):
-                        continue
-                    if f_self in seen:
-                        continue
-                    seen.add(f_self)
-
-                    self_type = type(f_self)
-                    ret.append("{0}.{1} - {2}".format(self_type.__module__,
-                                                      self_type.__name__,
-                                                      f_self))
-                elif f_self is not None:
-                    self_type = type(f_self)
-                    ret.append("{0}.{1}".format(self_type.__module__,
-                                                self_type.__name__))
-                else:
-                    code = frm.f_code
-                    if code.co_name in ('wrapper', ''):
-                        continue
-
-                    ret.append("{0}".format(code.co_name))
-
-                depth -= 1
-                if not depth:
-                    break
-
-        return '\n'.join(ret)
-
-
-class ParseFatalException(ParseBaseException):
-    """user-throwable exception thrown when inconsistent parse content
-       is found; stops all parsing immediately"""
-    pass
-
-class ParseSyntaxException(ParseFatalException):
-    """just like :class:`ParseFatalException`, but thrown internally
-    when an :class:`ErrorStop` ('-' operator) indicates
-    that parsing is to stop immediately because an unbacktrackable
-    syntax error has been found.
-    """
-    pass
-
-#~ class ReparseException(ParseBaseException):
-    #~ """Experimental class - parse actions can raise this exception to cause
-       #~ pyparsing to reparse the input string:
-        #~ - with a modified input string, and/or
-        #~ - with a modified start location
-       #~ Set the values of the ReparseException in the constructor, and raise the
-       #~ exception in a parse action to cause pyparsing to use the new string/location.
-       #~ Setting the values as None causes no change to be made.
-       #~ """
-    #~ def __init_( self, newstring, restartLoc ):
-        #~ self.newParseText = newstring
-        #~ self.reparseLoc = restartLoc
-
-class RecursiveGrammarException(Exception):
-    """exception thrown by :class:`ParserElement.validate` if the
-    grammar could be improperly recursive
-    """
-    def __init__(self, parseElementList):
-        self.parseElementTrace = parseElementList
-
-    def __str__(self):
-        return "RecursiveGrammarException: %s" % self.parseElementTrace
-
-class _ParseResultsWithOffset(object):
-    def __init__(self, p1, p2):
-        self.tup = (p1, p2)
-    def __getitem__(self, i):
-        return self.tup[i]
-    def __repr__(self):
-        return repr(self.tup[0])
-    def setOffset(self, i):
-        self.tup = (self.tup[0], i)
-
-class ParseResults(object):
-    """Structured parse results, to provide multiple means of access to
-    the parsed data:
-
-       - as a list (``len(results)``)
-       - by list index (``results[0], results[1]``, etc.)
-       - by attribute (``results.`` - see :class:`ParserElement.setResultsName`)
-
-    Example::
-
-        integer = Word(nums)
-        date_str = (integer.setResultsName("year") + '/'
-                        + integer.setResultsName("month") + '/'
-                        + integer.setResultsName("day"))
-        # equivalent form:
-        # date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-        # parseString returns a ParseResults object
-        result = date_str.parseString("1999/12/31")
-
-        def test(s, fn=repr):
-            print("%s -> %s" % (s, fn(eval(s))))
-        test("list(result)")
-        test("result[0]")
-        test("result['month']")
-        test("result.day")
-        test("'month' in result")
-        test("'minutes' in result")
-        test("result.dump()", str)
-
-    prints::
-
-        list(result) -> ['1999', '/', '12', '/', '31']
-        result[0] -> '1999'
-        result['month'] -> '12'
-        result.day -> '31'
-        'month' in result -> True
-        'minutes' in result -> False
-        result.dump() -> ['1999', '/', '12', '/', '31']
-        - day: 31
-        - month: 12
-        - year: 1999
-    """
-    def __new__(cls, toklist=None, name=None, asList=True, modal=True):
-        if isinstance(toklist, cls):
-            return toklist
-        retobj = object.__new__(cls)
-        retobj.__doinit = True
-        return retobj
-
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__(self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance):
-        if self.__doinit:
-            self.__doinit = False
-            self.__name = None
-            self.__parent = None
-            self.__accumNames = {}
-            self.__asList = asList
-            self.__modal = modal
-            if toklist is None:
-                toklist = []
-            if isinstance(toklist, list):
-                self.__toklist = toklist[:]
-            elif isinstance(toklist, _generatorType):
-                self.__toklist = list(toklist)
-            else:
-                self.__toklist = [toklist]
-            self.__tokdict = dict()
-
-        if name is not None and name:
-            if not modal:
-                self.__accumNames[name] = 0
-            if isinstance(name, int):
-                name = _ustr(name)  # will always return a str, but use _ustr for consistency
-            self.__name = name
-            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None, '', [])):
-                if isinstance(toklist, basestring):
-                    toklist = [toklist]
-                if asList:
-                    if isinstance(toklist, ParseResults):
-                        self[name] = _ParseResultsWithOffset(ParseResults(toklist.__toklist), 0)
-                    else:
-                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]), 0)
-                    self[name].__name = name
-                else:
-                    try:
-                        self[name] = toklist[0]
-                    except (KeyError, TypeError, IndexError):
-                        self[name] = toklist
-
-    def __getitem__(self, i):
-        if isinstance(i, (int, slice)):
-            return self.__toklist[i]
-        else:
-            if i not in self.__accumNames:
-                return self.__tokdict[i][-1][0]
-            else:
-                return ParseResults([v[0] for v in self.__tokdict[i]])
-
-    def __setitem__(self, k, v, isinstance=isinstance):
-        if isinstance(v, _ParseResultsWithOffset):
-            self.__tokdict[k] = self.__tokdict.get(k, list()) + [v]
-            sub = v[0]
-        elif isinstance(k, (int, slice)):
-            self.__toklist[k] = v
-            sub = v
-        else:
-            self.__tokdict[k] = self.__tokdict.get(k, list()) + [_ParseResultsWithOffset(v, 0)]
-            sub = v
-        if isinstance(sub, ParseResults):
-            sub.__parent = wkref(self)
-
-    def __delitem__(self, i):
-        if isinstance(i, (int, slice)):
-            mylen = len(self.__toklist)
-            del self.__toklist[i]
-
-            # convert int to slice
-            if isinstance(i, int):
-                if i < 0:
-                    i += mylen
-                i = slice(i, i + 1)
-            # get removed indices
-            removed = list(range(*i.indices(mylen)))
-            removed.reverse()
-            # fixup indices in token dictionary
-            for name, occurrences in self.__tokdict.items():
-                for j in removed:
-                    for k, (value, position) in enumerate(occurrences):
-                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
-        else:
-            del self.__tokdict[i]
-
-    def __contains__(self, k):
-        return k in self.__tokdict
-
-    def __len__(self):
-        return len(self.__toklist)
-
-    def __bool__(self):
-        return (not not self.__toklist)
-    __nonzero__ = __bool__
-
-    def __iter__(self):
-        return iter(self.__toklist)
-
-    def __reversed__(self):
-        return iter(self.__toklist[::-1])
-
-    def _iterkeys(self):
-        if hasattr(self.__tokdict, "iterkeys"):
-            return self.__tokdict.iterkeys()
-        else:
-            return iter(self.__tokdict)
-
-    def _itervalues(self):
-        return (self[k] for k in self._iterkeys())
-
-    def _iteritems(self):
-        return ((k, self[k]) for k in self._iterkeys())
-
-    if PY_3:
-        keys = _iterkeys
-        """Returns an iterator of all named result keys."""
-
-        values = _itervalues
-        """Returns an iterator of all named result values."""
-
-        items = _iteritems
-        """Returns an iterator of all named result key-value tuples."""
-
-    else:
-        iterkeys = _iterkeys
-        """Returns an iterator of all named result keys (Python 2.x only)."""
-
-        itervalues = _itervalues
-        """Returns an iterator of all named result values (Python 2.x only)."""
-
-        iteritems = _iteritems
-        """Returns an iterator of all named result key-value tuples (Python 2.x only)."""
-
-        def keys(self):
-            """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iterkeys())
-
-        def values(self):
-            """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.itervalues())
-
-        def items(self):
-            """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iteritems())
-
-    def haskeys(self):
-        """Since keys() returns an iterator, this method is helpful in bypassing
-           code that looks for the existence of any defined results names."""
-        return bool(self.__tokdict)
-
-    def pop(self, *args, **kwargs):
-        """
-        Removes and returns item at specified index (default= ``last``).
-        Supports both ``list`` and ``dict`` semantics for ``pop()``. If
-        passed no argument or an integer argument, it will use ``list``
-        semantics and pop tokens from the list of parsed tokens. If passed
-        a non-integer argument (most likely a string), it will use ``dict``
-        semantics and pop the corresponding value from any defined results
-        names. A second default return value argument is supported, just as in
-        ``dict.pop()``.
-
-        Example::
-
-            def remove_first(tokens):
-                tokens.pop(0)
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-            print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321']
-
-            label = Word(alphas)
-            patt = label("LABEL") + OneOrMore(Word(nums))
-            print(patt.parseString("AAB 123 321").dump())
-
-            # Use pop() in a parse action to remove named result (note that corresponding value is not
-            # removed from list form of results)
-            def remove_LABEL(tokens):
-                tokens.pop("LABEL")
-                return tokens
-            patt.addParseAction(remove_LABEL)
-            print(patt.parseString("AAB 123 321").dump())
-
-        prints::
-
-            ['AAB', '123', '321']
-            - LABEL: AAB
-
-            ['AAB', '123', '321']
-        """
-        if not args:
-            args = [-1]
-        for k, v in kwargs.items():
-            if k == 'default':
-                args = (args[0], v)
-            else:
-                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
-        if (isinstance(args[0], int)
-                or len(args) == 1
-                or args[0] in self):
-            index = args[0]
-            ret = self[index]
-            del self[index]
-            return ret
-        else:
-            defaultvalue = args[1]
-            return defaultvalue
-
-    def get(self, key, defaultValue=None):
-        """
-        Returns named result matching the given key, or if there is no
-        such name, then returns the given ``defaultValue`` or ``None`` if no
-        ``defaultValue`` is specified.
-
-        Similar to ``dict.get()``.
-
-        Example::
-
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-            result = date_str.parseString("1999/12/31")
-            print(result.get("year")) # -> '1999'
-            print(result.get("hour", "not specified")) # -> 'not specified'
-            print(result.get("hour")) # -> None
-        """
-        if key in self:
-            return self[key]
-        else:
-            return defaultValue
-
-    def insert(self, index, insStr):
-        """
-        Inserts new element at location index in the list of parsed tokens.
-
-        Similar to ``list.insert()``.
-
-        Example::
-
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-
-            # use a parse action to insert the parse location in the front of the parsed results
-            def insert_locn(locn, tokens):
-                tokens.insert(0, locn)
-            print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321']
-        """
-        self.__toklist.insert(index, insStr)
-        # fixup indices in token dictionary
-        for name, occurrences in self.__tokdict.items():
-            for k, (value, position) in enumerate(occurrences):
-                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
-
-    def append(self, item):
-        """
-        Add single element to end of ParseResults list of elements.
-
-        Example::
-
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-
-            # use a parse action to compute the sum of the parsed integers, and add it to the end
-            def append_sum(tokens):
-                tokens.append(sum(map(int, tokens)))
-            print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444]
-        """
-        self.__toklist.append(item)
-
-    def extend(self, itemseq):
-        """
-        Add sequence of elements to end of ParseResults list of elements.
-
-        Example::
-
-            patt = OneOrMore(Word(alphas))
-
-            # use a parse action to append the reverse of the matched strings, to make a palindrome
-            def make_palindrome(tokens):
-                tokens.extend(reversed([t[::-1] for t in tokens]))
-                return ''.join(tokens)
-            print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
-        """
-        if isinstance(itemseq, ParseResults):
-            self.__iadd__(itemseq)
-        else:
-            self.__toklist.extend(itemseq)
-
-    def clear(self):
-        """
-        Clear all elements and results names.
-        """
-        del self.__toklist[:]
-        self.__tokdict.clear()
-
-    def __getattr__(self, name):
-        try:
-            return self[name]
-        except KeyError:
-            return ""
-
-    def __add__(self, other):
-        ret = self.copy()
-        ret += other
-        return ret
-
-    def __iadd__(self, other):
-        if other.__tokdict:
-            offset = len(self.__toklist)
-            addoffset = lambda a: offset if a < 0 else a + offset
-            otheritems = other.__tokdict.items()
-            otherdictitems = [(k, _ParseResultsWithOffset(v[0], addoffset(v[1])))
-                              for k, vlist in otheritems for v in vlist]
-            for k, v in otherdictitems:
-                self[k] = v
-                if isinstance(v[0], ParseResults):
-                    v[0].__parent = wkref(self)
-
-        self.__toklist += other.__toklist
-        self.__accumNames.update(other.__accumNames)
-        return self
-
-    def __radd__(self, other):
-        if isinstance(other, int) and other == 0:
-            # useful for merging many ParseResults using sum() builtin
-            return self.copy()
-        else:
-            # this may raise a TypeError - so be it
-            return other + self
-
-    def __repr__(self):
-        return "(%s, %s)" % (repr(self.__toklist), repr(self.__tokdict))
-
-    def __str__(self):
-        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
-
-    def _asStringList(self, sep=''):
-        out = []
-        for item in self.__toklist:
-            if out and sep:
-                out.append(sep)
-            if isinstance(item, ParseResults):
-                out += item._asStringList()
-            else:
-                out.append(_ustr(item))
-        return out
-
-    def asList(self):
-        """
-        Returns the parse results as a nested list of matching tokens, all converted to strings.
-
-        Example::
-
-            patt = OneOrMore(Word(alphas))
-            result = patt.parseString("sldkj lsdkj sldkj")
-            # even though the result prints in string-like form, it is actually a pyparsing ParseResults
-            print(type(result), result) # ->  ['sldkj', 'lsdkj', 'sldkj']
-
-            # Use asList() to create an actual list
-            result_list = result.asList()
-            print(type(result_list), result_list) # ->  ['sldkj', 'lsdkj', 'sldkj']
-        """
-        return [res.asList() if isinstance(res, ParseResults) else res for res in self.__toklist]
-
-    def asDict(self):
-        """
-        Returns the named parse results as a nested dictionary.
-
-        Example::
-
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-            result = date_str.parseString('12/31/1999')
-            print(type(result), repr(result)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
-
-            result_dict = result.asDict()
-            print(type(result_dict), repr(result_dict)) # ->  {'day': '1999', 'year': '12', 'month': '31'}
-
-            # even though a ParseResults supports dict-like access, sometime you just need to have a dict
-            import json
-            print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
-            print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"}
-        """
-        if PY_3:
-            item_fn = self.items
-        else:
-            item_fn = self.iteritems
-
-        def toItem(obj):
-            if isinstance(obj, ParseResults):
-                if obj.haskeys():
-                    return obj.asDict()
-                else:
-                    return [toItem(v) for v in obj]
-            else:
-                return obj
-
-        return dict((k, toItem(v)) for k, v in item_fn())
-
-    def copy(self):
-        """
-        Returns a new copy of a :class:`ParseResults` object.
-        """
-        ret = ParseResults(self.__toklist)
-        ret.__tokdict = dict(self.__tokdict.items())
-        ret.__parent = self.__parent
-        ret.__accumNames.update(self.__accumNames)
-        ret.__name = self.__name
-        return ret
-
-    def asXML(self, doctag=None, namedItemsOnly=False, indent="", formatted=True):
-        """
-        (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.
-        """
-        nl = "\n"
-        out = []
-        namedItems = dict((v[1], k) for (k, vlist) in self.__tokdict.items()
-                          for v in vlist)
-        nextLevelIndent = indent + "  "
-
-        # collapse out indents if formatting is not desired
-        if not formatted:
-            indent = ""
-            nextLevelIndent = ""
-            nl = ""
-
-        selfTag = None
-        if doctag is not None:
-            selfTag = doctag
-        else:
-            if self.__name:
-                selfTag = self.__name
-
-        if not selfTag:
-            if namedItemsOnly:
-                return ""
-            else:
-                selfTag = "ITEM"
-
-        out += [nl, indent, "<", selfTag, ">"]
-
-        for i, res in enumerate(self.__toklist):
-            if isinstance(res, ParseResults):
-                if i in namedItems:
-                    out += [res.asXML(namedItems[i],
-                                      namedItemsOnly and doctag is None,
-                                      nextLevelIndent,
-                                      formatted)]
-                else:
-                    out += [res.asXML(None,
-                                      namedItemsOnly and doctag is None,
-                                      nextLevelIndent,
-                                      formatted)]
-            else:
-                # individual token, see if there is a name for it
-                resTag = None
-                if i in namedItems:
-                    resTag = namedItems[i]
-                if not resTag:
-                    if namedItemsOnly:
-                        continue
-                    else:
-                        resTag = "ITEM"
-                xmlBodyText = _xml_escape(_ustr(res))
-                out += [nl, nextLevelIndent, "<", resTag, ">",
-                        xmlBodyText,
-                                                ""]
-
-        out += [nl, indent, ""]
-        return "".join(out)
-
-    def __lookup(self, sub):
-        for k, vlist in self.__tokdict.items():
-            for v, loc in vlist:
-                if sub is v:
-                    return k
-        return None
-
-    def getName(self):
-        r"""
-        Returns the results name for this token expression. Useful when several
-        different expressions might match at a particular location.
-
-        Example::
-
-            integer = Word(nums)
-            ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
-            house_number_expr = Suppress('#') + Word(nums, alphanums)
-            user_data = (Group(house_number_expr)("house_number")
-                        | Group(ssn_expr)("ssn")
-                        | Group(integer)("age"))
-            user_info = OneOrMore(user_data)
-
-            result = user_info.parseString("22 111-22-3333 #221B")
-            for item in result:
-                print(item.getName(), ':', item[0])
-
-        prints::
-
-            age : 22
-            ssn : 111-22-3333
-            house_number : 221B
-        """
-        if self.__name:
-            return self.__name
-        elif self.__parent:
-            par = self.__parent()
-            if par:
-                return par.__lookup(self)
-            else:
-                return None
-        elif (len(self) == 1
-              and len(self.__tokdict) == 1
-              and next(iter(self.__tokdict.values()))[0][1] in (0, -1)):
-            return next(iter(self.__tokdict.keys()))
-        else:
-            return None
-
-    def dump(self, indent='', full=True, include_list=True, _depth=0):
-        """
-        Diagnostic method for listing out the contents of
-        a :class:`ParseResults`. Accepts an optional ``indent`` argument so
-        that this string can be embedded in a nested display of other data.
-
-        Example::
-
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-            result = date_str.parseString('12/31/1999')
-            print(result.dump())
-
-        prints::
-
-            ['12', '/', '31', '/', '1999']
-            - day: 1999
-            - month: 31
-            - year: 12
-        """
-        out = []
-        NL = '\n'
-        if include_list:
-            out.append(indent + _ustr(self.asList()))
-        else:
-            out.append('')
-
-        if full:
-            if self.haskeys():
-                items = sorted((str(k), v) for k, v in self.items())
-                for k, v in items:
-                    if out:
-                        out.append(NL)
-                    out.append("%s%s- %s: " % (indent, ('  ' * _depth), k))
-                    if isinstance(v, ParseResults):
-                        if v:
-                            out.append(v.dump(indent=indent, full=full, include_list=include_list, _depth=_depth + 1))
-                        else:
-                            out.append(_ustr(v))
-                    else:
-                        out.append(repr(v))
-            elif any(isinstance(vv, ParseResults) for vv in self):
-                v = self
-                for i, vv in enumerate(v):
-                    if isinstance(vv, ParseResults):
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,
-                                                            ('  ' * (_depth)),
-                                                            i,
-                                                            indent,
-                                                            ('  ' * (_depth + 1)),
-                                                            vv.dump(indent=indent,
-                                                                    full=full,
-                                                                    include_list=include_list,
-                                                                    _depth=_depth + 1)))
-                    else:
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,
-                                                            ('  ' * (_depth)),
-                                                            i,
-                                                            indent,
-                                                            ('  ' * (_depth + 1)),
-                                                            _ustr(vv)))
-
-        return "".join(out)
-
-    def pprint(self, *args, **kwargs):
-        """
-        Pretty-printer for parsed results as a list, using the
-        `pprint `_ module.
-        Accepts additional positional or keyword args as defined for
-        `pprint.pprint `_ .
-
-        Example::
-
-            ident = Word(alphas, alphanums)
-            num = Word(nums)
-            func = Forward()
-            term = ident | num | Group('(' + func + ')')
-            func <<= ident + Group(Optional(delimitedList(term)))
-            result = func.parseString("fna a,b,(fnb c,d,200),100")
-            result.pprint(width=40)
-
-        prints::
-
-            ['fna',
-             ['a',
-              'b',
-              ['(', 'fnb', ['c', 'd', '200'], ')'],
-              '100']]
-        """
-        pprint.pprint(self.asList(), *args, **kwargs)
-
-    # add support for pickle protocol
-    def __getstate__(self):
-        return (self.__toklist,
-                (self.__tokdict.copy(),
-                 self.__parent is not None and self.__parent() or None,
-                 self.__accumNames,
-                 self.__name))
-
-    def __setstate__(self, state):
-        self.__toklist = state[0]
-        self.__tokdict, par, inAccumNames, self.__name = state[1]
-        self.__accumNames = {}
-        self.__accumNames.update(inAccumNames)
-        if par is not None:
-            self.__parent = wkref(par)
-        else:
-            self.__parent = None
-
-    def __getnewargs__(self):
-        return self.__toklist, self.__name, self.__asList, self.__modal
-
-    def __dir__(self):
-        return dir(type(self)) + list(self.keys())
-
-    @classmethod
-    def from_dict(cls, other, name=None):
-        """
-        Helper classmethod to construct a ParseResults from a dict, preserving the
-        name-value relations as results names. If an optional 'name' argument is
-        given, a nested ParseResults will be returned
-        """
-        def is_iterable(obj):
-            try:
-                iter(obj)
-            except Exception:
-                return False
-            else:
-                if PY_3:
-                    return not isinstance(obj, (str, bytes))
-                else:
-                    return not isinstance(obj, basestring)
-
-        ret = cls([])
-        for k, v in other.items():
-            if isinstance(v, Mapping):
-                ret += cls.from_dict(v, name=k)
-            else:
-                ret += cls([v], name=k, asList=is_iterable(v))
-        if name is not None:
-            ret = cls([ret], name=name)
-        return ret
-
-MutableMapping.register(ParseResults)
-
-def col (loc, strg):
-    """Returns current column within a string, counting newlines as line separators.
-   The first column is number 1.
-
-   Note: the default parsing behavior is to expand tabs in the input string
-   before starting the parsing process.  See
-   :class:`ParserElement.parseString` for more
-   information on parsing strings containing ```` s, and suggested
-   methods to maintain a consistent view of the parsed string, the parse
-   location, and line and column positions within the parsed string.
-   """
-    s = strg
-    return 1 if 0 < loc < len(s) and s[loc-1] == '\n' else loc - s.rfind("\n", 0, loc)
-
-def lineno(loc, strg):
-    """Returns current line number within a string, counting newlines as line separators.
-    The first line is number 1.
-
-    Note - the default parsing behavior is to expand tabs in the input string
-    before starting the parsing process.  See :class:`ParserElement.parseString`
-    for more information on parsing strings containing ```` s, and
-    suggested methods to maintain a consistent view of the parsed string, the
-    parse location, and line and column positions within the parsed string.
-    """
-    return strg.count("\n", 0, loc) + 1
-
-def line(loc, strg):
-    """Returns the line of text containing loc within a string, counting newlines as line separators.
-       """
-    lastCR = strg.rfind("\n", 0, loc)
-    nextCR = strg.find("\n", loc)
-    if nextCR >= 0:
-        return strg[lastCR + 1:nextCR]
-    else:
-        return strg[lastCR + 1:]
-
-def _defaultStartDebugAction(instring, loc, expr):
-    print(("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % (lineno(loc, instring), col(loc, instring))))
-
-def _defaultSuccessDebugAction(instring, startloc, endloc, expr, toks):
-    print("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
-
-def _defaultExceptionDebugAction(instring, loc, expr, exc):
-    print("Exception raised:" + _ustr(exc))
-
-def nullDebugAction(*args):
-    """'Do-nothing' debug action, to suppress debugging output during parsing."""
-    pass
-
-# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
-#~ 'decorator to trim function calls to match the arity of the target'
-#~ def _trim_arity(func, maxargs=3):
-    #~ if func in singleArgBuiltins:
-        #~ return lambda s,l,t: func(t)
-    #~ limit = 0
-    #~ foundArity = False
-    #~ def wrapper(*args):
-        #~ nonlocal limit,foundArity
-        #~ while 1:
-            #~ try:
-                #~ ret = func(*args[limit:])
-                #~ foundArity = True
-                #~ return ret
-            #~ except TypeError:
-                #~ if limit == maxargs or foundArity:
-                    #~ raise
-                #~ limit += 1
-                #~ continue
-    #~ return wrapper
-
-# this version is Python 2.x-3.x cross-compatible
-'decorator to trim function calls to match the arity of the target'
-def _trim_arity(func, maxargs=2):
-    if func in singleArgBuiltins:
-        return lambda s, l, t: func(t)
-    limit = [0]
-    foundArity = [False]
-
-    # traceback return data structure changed in Py3.5 - normalize back to plain tuples
-    if system_version[:2] >= (3, 5):
-        def extract_stack(limit=0):
-            # special handling for Python 3.5.0 - extra deep call stack by 1
-            offset = -3 if system_version == (3, 5, 0) else -2
-            frame_summary = traceback.extract_stack(limit=-offset + limit - 1)[offset]
-            return [frame_summary[:2]]
-        def extract_tb(tb, limit=0):
-            frames = traceback.extract_tb(tb, limit=limit)
-            frame_summary = frames[-1]
-            return [frame_summary[:2]]
-    else:
-        extract_stack = traceback.extract_stack
-        extract_tb = traceback.extract_tb
-
-    # synthesize what would be returned by traceback.extract_stack at the call to
-    # user's parse action 'func', so that we don't incur call penalty at parse time
-
-    LINE_DIFF = 6
-    # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND
-    # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!!
-    this_line = extract_stack(limit=2)[-1]
-    pa_call_line_synth = (this_line[0], this_line[1] + LINE_DIFF)
-
-    def wrapper(*args):
-        while 1:
-            try:
-                ret = func(*args[limit[0]:])
-                foundArity[0] = True
-                return ret
-            except TypeError:
-                # re-raise TypeErrors if they did not come from our arity testing
-                if foundArity[0]:
-                    raise
-                else:
-                    try:
-                        tb = sys.exc_info()[-1]
-                        if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth:
-                            raise
-                    finally:
-                        try:
-                            del tb
-                        except NameError:
-                            pass
-
-                if limit[0] <= maxargs:
-                    limit[0] += 1
-                    continue
-                raise
-
-    # copy func name to wrapper for sensible debug output
-    func_name = ""
-    try:
-        func_name = getattr(func, '__name__',
-                            getattr(func, '__class__').__name__)
-    except Exception:
-        func_name = str(func)
-    wrapper.__name__ = func_name
-
-    return wrapper
-
-
-class ParserElement(object):
-    """Abstract base level parser element class."""
-    DEFAULT_WHITE_CHARS = " \n\t\r"
-    verbose_stacktrace = False
-
-    @staticmethod
-    def setDefaultWhitespaceChars(chars):
-        r"""
-        Overrides the default whitespace chars
-
-        Example::
-
-            # default whitespace chars are space,  and newline
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def', 'ghi', 'jkl']
-
-            # change to just treat newline as significant
-            ParserElement.setDefaultWhitespaceChars(" \t")
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def']
-        """
-        ParserElement.DEFAULT_WHITE_CHARS = chars
-
-    @staticmethod
-    def inlineLiteralsUsing(cls):
-        """
-        Set class to be used for inclusion of string literals into a parser.
-
-        Example::
-
-            # default literal class used is Literal
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-
-            # change to Suppress
-            ParserElement.inlineLiteralsUsing(Suppress)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '12', '31']
-        """
-        ParserElement._literalStringClass = cls
-
-    @classmethod
-    def _trim_traceback(cls, tb):
-        while tb.tb_next:
-            tb = tb.tb_next
-        return tb
-
-    def __init__(self, savelist=False):
-        self.parseAction = list()
-        self.failAction = None
-        # ~ self.name = ""  # don't define self.name, let subclasses try/except upcall
-        self.strRepr = None
-        self.resultsName = None
-        self.saveAsList = savelist
-        self.skipWhitespace = True
-        self.whiteChars = set(ParserElement.DEFAULT_WHITE_CHARS)
-        self.copyDefaultWhiteChars = True
-        self.mayReturnEmpty = False # used when checking for left-recursion
-        self.keepTabs = False
-        self.ignoreExprs = list()
-        self.debug = False
-        self.streamlined = False
-        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
-        self.errmsg = ""
-        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
-        self.debugActions = (None, None, None)  # custom debug actions
-        self.re = None
-        self.callPreparse = True # used to avoid redundant calls to preParse
-        self.callDuringTry = False
-
-    def copy(self):
-        """
-        Make a copy of this :class:`ParserElement`.  Useful for defining
-        different parse actions for the same parsing pattern, using copies of
-        the original parse element.
-
-        Example::
-
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            integerK = integer.copy().addParseAction(lambda toks: toks[0] * 1024) + Suppress("K")
-            integerM = integer.copy().addParseAction(lambda toks: toks[0] * 1024 * 1024) + Suppress("M")
-
-            print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M"))
-
-        prints::
-
-            [5120, 100, 655360, 268435456]
-
-        Equivalent form of ``expr.copy()`` is just ``expr()``::
-
-            integerM = integer().addParseAction(lambda toks: toks[0] * 1024 * 1024) + Suppress("M")
-        """
-        cpy = copy.copy(self)
-        cpy.parseAction = self.parseAction[:]
-        cpy.ignoreExprs = self.ignoreExprs[:]
-        if self.copyDefaultWhiteChars:
-            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
-        return cpy
-
-    def setName(self, name):
-        """
-        Define name for this expression, makes debugging and exception messages clearer.
-
-        Example::
-
-            Word(nums).parseString("ABC")  # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1)
-            Word(nums).setName("integer").parseString("ABC")  # -> Exception: Expected integer (at char 0), (line:1, col:1)
-        """
-        self.name = name
-        self.errmsg = "Expected " + self.name
-        if __diag__.enable_debug_on_named_expressions:
-            self.setDebug()
-        return self
-
-    def setResultsName(self, name, listAllMatches=False):
-        """
-        Define name for referencing matching tokens as a nested attribute
-        of the returned parse results.
-        NOTE: this returns a *copy* of the original :class:`ParserElement` object;
-        this is so that the client can define a basic element, such as an
-        integer, and reference it in multiple places with different names.
-
-        You can also set results names using the abbreviated syntax,
-        ``expr("name")`` in place of ``expr.setResultsName("name")``
-        - see :class:`__call__`.
-
-        Example::
-
-            date_str = (integer.setResultsName("year") + '/'
-                        + integer.setResultsName("month") + '/'
-                        + integer.setResultsName("day"))
-
-            # equivalent form:
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-        """
-        return self._setResultsName(name, listAllMatches)
-
-    def _setResultsName(self, name, listAllMatches=False):
-        newself = self.copy()
-        if name.endswith("*"):
-            name = name[:-1]
-            listAllMatches = True
-        newself.resultsName = name
-        newself.modalResults = not listAllMatches
-        return newself
-
-    def setBreak(self, breakFlag=True):
-        """Method to invoke the Python pdb debugger when this element is
-           about to be parsed. Set ``breakFlag`` to True to enable, False to
-           disable.
-        """
-        if breakFlag:
-            _parseMethod = self._parse
-            def breaker(instring, loc, doActions=True, callPreParse=True):
-                import pdb
-                # this call to pdb.set_trace() is intentional, not a checkin error
-                pdb.set_trace()
-                return _parseMethod(instring, loc, doActions, callPreParse)
-            breaker._originalParseMethod = _parseMethod
-            self._parse = breaker
-        else:
-            if hasattr(self._parse, "_originalParseMethod"):
-                self._parse = self._parse._originalParseMethod
-        return self
-
-    def setParseAction(self, *fns, **kwargs):
-        """
-        Define one or more actions to perform when successfully matching parse element definition.
-        Parse action fn is a callable method with 0-3 arguments, called as ``fn(s, loc, toks)`` ,
-        ``fn(loc, toks)`` , ``fn(toks)`` , or just ``fn()`` , where:
-
-        - s   = the original string being parsed (see note below)
-        - loc = the location of the matching substring
-        - toks = a list of the matched tokens, packaged as a :class:`ParseResults` object
-
-        If the functions in fns modify the tokens, they can return them as the return
-        value from fn, and the modified list of tokens will replace the original.
-        Otherwise, fn does not need to return any value.
-
-        If None is passed as the parse action, all previously added parse actions for this
-        expression are cleared.
-
-        Optional keyword arguments:
-        - callDuringTry = (default= ``False``) indicate if parse action should be run during lookaheads and alternate testing
-
-        Note: the default parsing behavior is to expand tabs in the input string
-        before starting the parsing process.  See :class:`parseString for more
-        information on parsing strings containing ```` s, and suggested
-        methods to maintain a consistent view of the parsed string, the parse
-        location, and line and column positions within the parsed string.
-
-        Example::
-
-            integer = Word(nums)
-            date_str = integer + '/' + integer + '/' + integer
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-            # use parse action to convert to ints at parse time
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            date_str = integer + '/' + integer + '/' + integer
-
-            # note that integer fields are now ints, not strings
-            date_str.parseString("1999/12/31")  # -> [1999, '/', 12, '/', 31]
-        """
-        if list(fns) == [None,]:
-            self.parseAction = []
-        else:
-            if not all(callable(fn) for fn in fns):
-                raise TypeError("parse actions must be callable")
-            self.parseAction = list(map(_trim_arity, list(fns)))
-            self.callDuringTry = kwargs.get("callDuringTry", False)
-        return self
-
-    def addParseAction(self, *fns, **kwargs):
-        """
-        Add one or more parse actions to expression's list of parse actions. See :class:`setParseAction`.
-
-        See examples in :class:`copy`.
-        """
-        self.parseAction += list(map(_trim_arity, list(fns)))
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def addCondition(self, *fns, **kwargs):
-        """Add a boolean predicate function to expression's list of parse actions. See
-        :class:`setParseAction` for function call signatures. Unlike ``setParseAction``,
-        functions passed to ``addCondition`` need to return boolean success/fail of the condition.
-
-        Optional keyword arguments:
-        - message = define a custom message to be used in the raised exception
-        - fatal   = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException
-
-        Example::
-
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            year_int = integer.copy()
-            year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
-            date_str = year_int + '/' + integer + '/' + integer
-
-            result = date_str.parseString("1999/12/31")  # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1)
-        """
-        for fn in fns:
-            self.parseAction.append(conditionAsParseAction(fn, message=kwargs.get('message'),
-                                                           fatal=kwargs.get('fatal', False)))
-
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def setFailAction(self, fn):
-        """Define action to perform if parsing fails at this expression.
-           Fail acton fn is a callable function that takes the arguments
-           ``fn(s, loc, expr, err)`` where:
-           - s = string being parsed
-           - loc = location where expression match was attempted and failed
-           - expr = the parse expression that failed
-           - err = the exception thrown
-           The function returns no value.  It may throw :class:`ParseFatalException`
-           if it is desired to stop parsing immediately."""
-        self.failAction = fn
-        return self
-
-    def _skipIgnorables(self, instring, loc):
-        exprsFound = True
-        while exprsFound:
-            exprsFound = False
-            for e in self.ignoreExprs:
-                try:
-                    while 1:
-                        loc, dummy = e._parse(instring, loc)
-                        exprsFound = True
-                except ParseException:
-                    pass
-        return loc
-
-    def preParse(self, instring, loc):
-        if self.ignoreExprs:
-            loc = self._skipIgnorables(instring, loc)
-
-        if self.skipWhitespace:
-            wt = self.whiteChars
-            instrlen = len(instring)
-            while loc < instrlen and instring[loc] in wt:
-                loc += 1
-
-        return loc
-
-    def parseImpl(self, instring, loc, doActions=True):
-        return loc, []
-
-    def postParse(self, instring, loc, tokenlist):
-        return tokenlist
-
-    # ~ @profile
-    def _parseNoCache(self, instring, loc, doActions=True, callPreParse=True):
-        TRY, MATCH, FAIL = 0, 1, 2
-        debugging = (self.debug)  # and doActions)
-
-        if debugging or self.failAction:
-            # ~ print ("Match", self, "at loc", loc, "(%d, %d)" % (lineno(loc, instring), col(loc, instring)))
-            if self.debugActions[TRY]:
-                self.debugActions[TRY](instring, loc, self)
-            try:
-                if callPreParse and self.callPreparse:
-                    preloc = self.preParse(instring, loc)
-                else:
-                    preloc = loc
-                tokensStart = preloc
-                if self.mayIndexError or preloc >= len(instring):
-                    try:
-                        loc, tokens = self.parseImpl(instring, preloc, doActions)
-                    except IndexError:
-                        raise ParseException(instring, len(instring), self.errmsg, self)
-                else:
-                    loc, tokens = self.parseImpl(instring, preloc, doActions)
-            except Exception as err:
-                # ~ print ("Exception raised:", err)
-                if self.debugActions[FAIL]:
-                    self.debugActions[FAIL](instring, tokensStart, self, err)
-                if self.failAction:
-                    self.failAction(instring, tokensStart, self, err)
-                raise
-        else:
-            if callPreParse and self.callPreparse:
-                preloc = self.preParse(instring, loc)
-            else:
-                preloc = loc
-            tokensStart = preloc
-            if self.mayIndexError or preloc >= len(instring):
-                try:
-                    loc, tokens = self.parseImpl(instring, preloc, doActions)
-                except IndexError:
-                    raise ParseException(instring, len(instring), self.errmsg, self)
-            else:
-                loc, tokens = self.parseImpl(instring, preloc, doActions)
-
-        tokens = self.postParse(instring, loc, tokens)
-
-        retTokens = ParseResults(tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults)
-        if self.parseAction and (doActions or self.callDuringTry):
-            if debugging:
-                try:
-                    for fn in self.parseAction:
-                        try:
-                            tokens = fn(instring, tokensStart, retTokens)
-                        except IndexError as parse_action_exc:
-                            exc = ParseException("exception raised in parse action")
-                            exc.__cause__ = parse_action_exc
-                            raise exc
-
-                        if tokens is not None and tokens is not retTokens:
-                            retTokens = ParseResults(tokens,
-                                                      self.resultsName,
-                                                      asList=self.saveAsList and isinstance(tokens, (ParseResults, list)),
-                                                      modal=self.modalResults)
-                except Exception as err:
-                    # ~ print "Exception raised in user parse action:", err
-                    if self.debugActions[FAIL]:
-                        self.debugActions[FAIL](instring, tokensStart, self, err)
-                    raise
-            else:
-                for fn in self.parseAction:
-                    try:
-                        tokens = fn(instring, tokensStart, retTokens)
-                    except IndexError as parse_action_exc:
-                        exc = ParseException("exception raised in parse action")
-                        exc.__cause__ = parse_action_exc
-                        raise exc
-
-                    if tokens is not None and tokens is not retTokens:
-                        retTokens = ParseResults(tokens,
-                                                  self.resultsName,
-                                                  asList=self.saveAsList and isinstance(tokens, (ParseResults, list)),
-                                                  modal=self.modalResults)
-        if debugging:
-            # ~ print ("Matched", self, "->", retTokens.asList())
-            if self.debugActions[MATCH]:
-                self.debugActions[MATCH](instring, tokensStart, loc, self, retTokens)
-
-        return loc, retTokens
-
-    def tryParse(self, instring, loc):
-        try:
-            return self._parse(instring, loc, doActions=False)[0]
-        except ParseFatalException:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-    def canParseNext(self, instring, loc):
-        try:
-            self.tryParse(instring, loc)
-        except (ParseException, IndexError):
-            return False
-        else:
-            return True
-
-    class _UnboundedCache(object):
-        def __init__(self):
-            cache = {}
-            self.not_in_cache = not_in_cache = object()
-
-            def get(self, key):
-                return cache.get(key, not_in_cache)
-
-            def set(self, key, value):
-                cache[key] = value
-
-            def clear(self):
-                cache.clear()
-
-            def cache_len(self):
-                return len(cache)
-
-            self.get = types.MethodType(get, self)
-            self.set = types.MethodType(set, self)
-            self.clear = types.MethodType(clear, self)
-            self.__len__ = types.MethodType(cache_len, self)
-
-    if _OrderedDict is not None:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = _OrderedDict()
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(cache) > size:
-                        try:
-                            cache.popitem(False)
-                        except KeyError:
-                            pass
-
-                def clear(self):
-                    cache.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    else:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = {}
-                key_fifo = collections.deque([], size)
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(key_fifo) > size:
-                        cache.pop(key_fifo.popleft(), None)
-                    key_fifo.append(key)
-
-                def clear(self):
-                    cache.clear()
-                    key_fifo.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    # argument cache for optimizing repeated calls when backtracking through recursive expressions
-    packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
-    packrat_cache_lock = RLock()
-    packrat_cache_stats = [0, 0]
-
-    # this method gets repeatedly called during backtracking with the same arguments -
-    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
-    def _parseCache(self, instring, loc, doActions=True, callPreParse=True):
-        HIT, MISS = 0, 1
-        lookup = (self, instring, loc, callPreParse, doActions)
-        with ParserElement.packrat_cache_lock:
-            cache = ParserElement.packrat_cache
-            value = cache.get(lookup)
-            if value is cache.not_in_cache:
-                ParserElement.packrat_cache_stats[MISS] += 1
-                try:
-                    value = self._parseNoCache(instring, loc, doActions, callPreParse)
-                except ParseBaseException as pe:
-                    # cache a copy of the exception, without the traceback
-                    cache.set(lookup, pe.__class__(*pe.args))
-                    raise
-                else:
-                    cache.set(lookup, (value[0], value[1].copy()))
-                    return value
-            else:
-                ParserElement.packrat_cache_stats[HIT] += 1
-                if isinstance(value, Exception):
-                    raise value
-                return value[0], value[1].copy()
-
-    _parse = _parseNoCache
-
-    @staticmethod
-    def resetCache():
-        ParserElement.packrat_cache.clear()
-        ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats)
-
-    _packratEnabled = False
-    @staticmethod
-    def enablePackrat(cache_size_limit=128):
-        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
-           Repeated parse attempts at the same string location (which happens
-           often in many complex grammars) can immediately return a cached value,
-           instead of re-executing parsing/validating code.  Memoizing is done of
-           both valid results and parsing exceptions.
-
-           Parameters:
-
-           - cache_size_limit - (default= ``128``) - if an integer value is provided
-             will limit the size of the packrat cache; if None is passed, then
-             the cache size will be unbounded; if 0 is passed, the cache will
-             be effectively disabled.
-
-           This speedup may break existing programs that use parse actions that
-           have side-effects.  For this reason, packrat parsing is disabled when
-           you first import pyparsing.  To activate the packrat feature, your
-           program must call the class method :class:`ParserElement.enablePackrat`.
-           For best results, call ``enablePackrat()`` immediately after
-           importing pyparsing.
-
-           Example::
-
-               import pyparsing
-               pyparsing.ParserElement.enablePackrat()
-        """
-        if not ParserElement._packratEnabled:
-            ParserElement._packratEnabled = True
-            if cache_size_limit is None:
-                ParserElement.packrat_cache = ParserElement._UnboundedCache()
-            else:
-                ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit)
-            ParserElement._parse = ParserElement._parseCache
-
-    def parseString(self, instring, parseAll=False):
-        """
-        Execute the parse expression with the given string.
-        This is the main interface to the client code, once the complete
-        expression has been built.
-
-        Returns the parsed data as a :class:`ParseResults` object, which may be
-        accessed as a list, or as a dict or object with attributes if the given parser
-        includes results names.
-
-        If you want the grammar to require that the entire input string be
-        successfully parsed, then set ``parseAll`` to True (equivalent to ending
-        the grammar with ``StringEnd()``).
-
-        Note: ``parseString`` implicitly calls ``expandtabs()`` on the input string,
-        in order to report proper column numbers in parse actions.
-        If the input string contains tabs and
-        the grammar uses parse actions that use the ``loc`` argument to index into the
-        string being parsed, you can ensure you have a consistent view of the input
-        string by:
-
-        - calling ``parseWithTabs`` on your grammar before calling ``parseString``
-          (see :class:`parseWithTabs`)
-        - define your parse action using the full ``(s, loc, toks)`` signature, and
-          reference the input string using the parse action's ``s`` argument
-        - explictly expand the tabs in your input string before calling
-          ``parseString``
-
-        Example::
-
-            Word('a').parseString('aaaaabaaa')  # -> ['aaaaa']
-            Word('a').parseString('aaaaabaaa', parseAll=True)  # -> Exception: Expected end of text
-        """
-        ParserElement.resetCache()
-        if not self.streamlined:
-            self.streamline()
-            # ~ self.saveAsList = True
-        for e in self.ignoreExprs:
-            e.streamline()
-        if not self.keepTabs:
-            instring = instring.expandtabs()
-        try:
-            loc, tokens = self._parse(instring, 0)
-            if parseAll:
-                loc = self.preParse(instring, loc)
-                se = Empty() + StringEnd()
-                se._parse(instring, loc)
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clearing out pyparsing internal stack trace
-                if getattr(exc, '__traceback__', None) is not None:
-                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
-                raise exc
-        else:
-            return tokens
-
-    def scanString(self, instring, maxMatches=_MAX_INT, overlap=False):
-        """
-        Scan the input string for expression matches.  Each match will return the
-        matching tokens, start location, and end location.  May be called with optional
-        ``maxMatches`` argument, to clip scanning after 'n' matches are found.  If
-        ``overlap`` is specified, then overlapping matches will be reported.
-
-        Note that the start and end locations are reported relative to the string
-        being parsed.  See :class:`parseString` for more information on parsing
-        strings with embedded tabs.
-
-        Example::
-
-            source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
-            print(source)
-            for tokens, start, end in Word(alphas).scanString(source):
-                print(' '*start + '^'*(end-start))
-                print(' '*start + tokens[0])
-
-        prints::
-
-            sldjf123lsdjjkf345sldkjf879lkjsfd987
-            ^^^^^
-            sldjf
-                    ^^^^^^^
-                    lsdjjkf
-                              ^^^^^^
-                              sldkjf
-                                       ^^^^^^
-                                       lkjsfd
-        """
-        if not self.streamlined:
-            self.streamline()
-        for e in self.ignoreExprs:
-            e.streamline()
-
-        if not self.keepTabs:
-            instring = _ustr(instring).expandtabs()
-        instrlen = len(instring)
-        loc = 0
-        preparseFn = self.preParse
-        parseFn = self._parse
-        ParserElement.resetCache()
-        matches = 0
-        try:
-            while loc <= instrlen and matches < maxMatches:
-                try:
-                    preloc = preparseFn(instring, loc)
-                    nextLoc, tokens = parseFn(instring, preloc, callPreParse=False)
-                except ParseException:
-                    loc = preloc + 1
-                else:
-                    if nextLoc > loc:
-                        matches += 1
-                        yield tokens, preloc, nextLoc
-                        if overlap:
-                            nextloc = preparseFn(instring, loc)
-                            if nextloc > loc:
-                                loc = nextLoc
-                            else:
-                                loc += 1
-                        else:
-                            loc = nextLoc
-                    else:
-                        loc = preloc + 1
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clearing out pyparsing internal stack trace
-                if getattr(exc, '__traceback__', None) is not None:
-                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
-                raise exc
-
-    def transformString(self, instring):
-        """
-        Extension to :class:`scanString`, to modify matching text with modified tokens that may
-        be returned from a parse action.  To use ``transformString``, define a grammar and
-        attach a parse action to it that modifies the returned token list.
-        Invoking ``transformString()`` on a target string will then scan for matches,
-        and replace the matched text patterns according to the logic in the parse
-        action.  ``transformString()`` returns the resulting transformed string.
-
-        Example::
-
-            wd = Word(alphas)
-            wd.setParseAction(lambda toks: toks[0].title())
-
-            print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york."))
-
-        prints::
-
-            Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
-        """
-        out = []
-        lastE = 0
-        # force preservation of s, to minimize unwanted transformation of string, and to
-        # keep string locs straight between transformString and scanString
-        self.keepTabs = True
-        try:
-            for t, s, e in self.scanString(instring):
-                out.append(instring[lastE:s])
-                if t:
-                    if isinstance(t, ParseResults):
-                        out += t.asList()
-                    elif isinstance(t, list):
-                        out += t
-                    else:
-                        out.append(t)
-                lastE = e
-            out.append(instring[lastE:])
-            out = [o for o in out if o]
-            return "".join(map(_ustr, _flatten(out)))
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clearing out pyparsing internal stack trace
-                if getattr(exc, '__traceback__', None) is not None:
-                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
-                raise exc
-
-    def searchString(self, instring, maxMatches=_MAX_INT):
-        """
-        Another extension to :class:`scanString`, simplifying the access to the tokens found
-        to match the given parse expression.  May be called with optional
-        ``maxMatches`` argument, to clip searching after 'n' matches are found.
-
-        Example::
-
-            # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
-            cap_word = Word(alphas.upper(), alphas.lower())
-
-            print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
-
-            # the sum() builtin can be used to merge results into a single ParseResults object
-            print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
-
-        prints::
-
-            [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
-            ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
-        """
-        try:
-            return ParseResults([t for t, s, e in self.scanString(instring, maxMatches)])
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clearing out pyparsing internal stack trace
-                if getattr(exc, '__traceback__', None) is not None:
-                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
-                raise exc
-
-    def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
-        """
-        Generator method to split a string using the given expression as a separator.
-        May be called with optional ``maxsplit`` argument, to limit the number of splits;
-        and the optional ``includeSeparators`` argument (default= ``False``), if the separating
-        matching text should be included in the split results.
-
-        Example::
-
-            punc = oneOf(list(".,;:/-!?"))
-            print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
-
-        prints::
-
-            ['This', ' this', '', ' this sentence', ' is badly punctuated', '']
-        """
-        splits = 0
-        last = 0
-        for t, s, e in self.scanString(instring, maxMatches=maxsplit):
-            yield instring[last:s]
-            if includeSeparators:
-                yield t[0]
-            last = e
-        yield instring[last:]
-
-    def __add__(self, other):
-        """
-        Implementation of + operator - returns :class:`And`. Adding strings to a ParserElement
-        converts them to :class:`Literal`s by default.
-
-        Example::
-
-            greet = Word(alphas) + "," + Word(alphas) + "!"
-            hello = "Hello, World!"
-            print (hello, "->", greet.parseString(hello))
-
-        prints::
-
-            Hello, World! -> ['Hello', ',', 'World', '!']
-
-        ``...`` may be used as a parse expression as a short form of :class:`SkipTo`.
-
-            Literal('start') + ... + Literal('end')
-
-        is equivalent to:
-
-            Literal('start') + SkipTo('end')("_skipped*") + Literal('end')
-
-        Note that the skipped text is returned with '_skipped' as a results name,
-        and to support having multiple skips in the same parser, the value returned is
-        a list of all skipped text.
-        """
-        if other is Ellipsis:
-            return _PendingSkip(self)
-
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return And([self, other])
-
-    def __radd__(self, other):
-        """
-        Implementation of + operator when left operand is not a :class:`ParserElement`
-        """
-        if other is Ellipsis:
-            return SkipTo(self)("_skipped*") + self
-
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return other + self
-
-    def __sub__(self, other):
-        """
-        Implementation of - operator, returns :class:`And` with error stop
-        """
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return self + And._ErrorStop() + other
-
-    def __rsub__(self, other):
-        """
-        Implementation of - operator when left operand is not a :class:`ParserElement`
-        """
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return other - self
-
-    def __mul__(self, other):
-        """
-        Implementation of * operator, allows use of ``expr * 3`` in place of
-        ``expr + expr + expr``.  Expressions may also me multiplied by a 2-integer
-        tuple, similar to ``{min, max}`` multipliers in regular expressions.  Tuples
-        may also include ``None`` as in:
-         - ``expr*(n, None)`` or ``expr*(n, )`` is equivalent
-              to ``expr*n + ZeroOrMore(expr)``
-              (read as "at least n instances of ``expr``")
-         - ``expr*(None, n)`` is equivalent to ``expr*(0, n)``
-              (read as "0 to n instances of ``expr``")
-         - ``expr*(None, None)`` is equivalent to ``ZeroOrMore(expr)``
-         - ``expr*(1, None)`` is equivalent to ``OneOrMore(expr)``
-
-        Note that ``expr*(None, n)`` does not raise an exception if
-        more than n exprs exist in the input stream; that is,
-        ``expr*(None, n)`` does not enforce a maximum number of expr
-        occurrences.  If this behavior is desired, then write
-        ``expr*(None, n) + ~expr``
-        """
-        if other is Ellipsis:
-            other = (0, None)
-        elif isinstance(other, tuple) and other[:1] == (Ellipsis,):
-            other = ((0, ) + other[1:] + (None,))[:2]
-
-        if isinstance(other, int):
-            minElements, optElements = other, 0
-        elif isinstance(other, tuple):
-            other = tuple(o if o is not Ellipsis else None for o in other)
-            other = (other + (None, None))[:2]
-            if other[0] is None:
-                other = (0, other[1])
-            if isinstance(other[0], int) and other[1] is None:
-                if other[0] == 0:
-                    return ZeroOrMore(self)
-                if other[0] == 1:
-                    return OneOrMore(self)
-                else:
-                    return self * other[0] + ZeroOrMore(self)
-            elif isinstance(other[0], int) and isinstance(other[1], int):
-                minElements, optElements = other
-                optElements -= minElements
-            else:
-                raise TypeError("cannot multiply 'ParserElement' and ('%s', '%s') objects", type(other[0]), type(other[1]))
-        else:
-            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
-
-        if minElements < 0:
-            raise ValueError("cannot multiply ParserElement by negative value")
-        if optElements < 0:
-            raise ValueError("second tuple value must be greater or equal to first tuple value")
-        if minElements == optElements == 0:
-            raise ValueError("cannot multiply ParserElement by 0 or (0, 0)")
-
-        if optElements:
-            def makeOptionalList(n):
-                if n > 1:
-                    return Optional(self + makeOptionalList(n - 1))
-                else:
-                    return Optional(self)
-            if minElements:
-                if minElements == 1:
-                    ret = self + makeOptionalList(optElements)
-                else:
-                    ret = And([self] * minElements) + makeOptionalList(optElements)
-            else:
-                ret = makeOptionalList(optElements)
-        else:
-            if minElements == 1:
-                ret = self
-            else:
-                ret = And([self] * minElements)
-        return ret
-
-    def __rmul__(self, other):
-        return self.__mul__(other)
-
-    def __or__(self, other):
-        """
-        Implementation of | operator - returns :class:`MatchFirst`
-        """
-        if other is Ellipsis:
-            return _PendingSkip(self, must_skip=True)
-
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return MatchFirst([self, other])
-
-    def __ror__(self, other):
-        """
-        Implementation of | operator when left operand is not a :class:`ParserElement`
-        """
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return other | self
-
-    def __xor__(self, other):
-        """
-        Implementation of ^ operator - returns :class:`Or`
-        """
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return Or([self, other])
-
-    def __rxor__(self, other):
-        """
-        Implementation of ^ operator when left operand is not a :class:`ParserElement`
-        """
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return other ^ self
-
-    def __and__(self, other):
-        """
-        Implementation of & operator - returns :class:`Each`
-        """
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return Each([self, other])
-
-    def __rand__(self, other):
-        """
-        Implementation of & operator when left operand is not a :class:`ParserElement`
-        """
-        if isinstance(other, basestring):
-            other = self._literalStringClass(other)
-        if not isinstance(other, ParserElement):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                          SyntaxWarning, stacklevel=2)
-            return None
-        return other & self
-
-    def __invert__(self):
-        """
-        Implementation of ~ operator - returns :class:`NotAny`
-        """
-        return NotAny(self)
-
-    def __iter__(self):
-        # must implement __iter__ to override legacy use of sequential access to __getitem__ to
-        # iterate over a sequence
-        raise TypeError('%r object is not iterable' % self.__class__.__name__)
-
-    def __getitem__(self, key):
-        """
-        use ``[]`` indexing notation as a short form for expression repetition:
-         - ``expr[n]`` is equivalent to ``expr*n``
-         - ``expr[m, n]`` is equivalent to ``expr*(m, n)``
-         - ``expr[n, ...]`` or ``expr[n,]`` is equivalent
-              to ``expr*n + ZeroOrMore(expr)``
-              (read as "at least n instances of ``expr``")
-         - ``expr[..., n]`` is equivalent to ``expr*(0, n)``
-              (read as "0 to n instances of ``expr``")
-         - ``expr[...]`` and ``expr[0, ...]`` are equivalent to ``ZeroOrMore(expr)``
-         - ``expr[1, ...]`` is equivalent to ``OneOrMore(expr)``
-         ``None`` may be used in place of ``...``.
-
-        Note that ``expr[..., n]`` and ``expr[m, n]``do not raise an exception
-        if more than ``n`` ``expr``s exist in the input stream.  If this behavior is
-        desired, then write ``expr[..., n] + ~expr``.
-       """
-
-        # convert single arg keys to tuples
-        try:
-            if isinstance(key, str):
-                key = (key,)
-            iter(key)
-        except TypeError:
-            key = (key, key)
-
-        if len(key) > 2:
-            warnings.warn("only 1 or 2 index arguments supported ({0}{1})".format(key[:5],
-                                                                                '... [{0}]'.format(len(key))
-                                                                                if len(key) > 5 else ''))
-
-        # clip to 2 elements
-        ret = self * tuple(key[:2])
-        return ret
-
-    def __call__(self, name=None):
-        """
-        Shortcut for :class:`setResultsName`, with ``listAllMatches=False``.
-
-        If ``name`` is given with a trailing ``'*'`` character, then ``listAllMatches`` will be
-        passed as ``True``.
-
-        If ``name` is omitted, same as calling :class:`copy`.
-
-        Example::
-
-            # these are equivalent
-            userdata = Word(alphas).setResultsName("name") + Word(nums + "-").setResultsName("socsecno")
-            userdata = Word(alphas)("name") + Word(nums + "-")("socsecno")
-        """
-        if name is not None:
-            return self._setResultsName(name)
-        else:
-            return self.copy()
-
-    def suppress(self):
-        """
-        Suppresses the output of this :class:`ParserElement`; useful to keep punctuation from
-        cluttering up returned output.
-        """
-        return Suppress(self)
-
-    def leaveWhitespace(self):
-        """
-        Disables the skipping of whitespace before matching the characters in the
-        :class:`ParserElement`'s defined pattern.  This is normally only used internally by
-        the pyparsing module, but may be needed in some whitespace-sensitive grammars.
-        """
-        self.skipWhitespace = False
-        return self
-
-    def setWhitespaceChars(self, chars):
-        """
-        Overrides the default whitespace chars
-        """
-        self.skipWhitespace = True
-        self.whiteChars = chars
-        self.copyDefaultWhiteChars = False
-        return self
-
-    def parseWithTabs(self):
-        """
-        Overrides default behavior to expand ````s to spaces before parsing the input string.
-        Must be called before ``parseString`` when the input grammar contains elements that
-        match ```` characters.
-        """
-        self.keepTabs = True
-        return self
-
-    def ignore(self, other):
-        """
-        Define expression to be ignored (e.g., comments) while doing pattern
-        matching; may be called repeatedly, to define multiple comment or other
-        ignorable patterns.
-
-        Example::
-
-            patt = OneOrMore(Word(alphas))
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj']
-
-            patt.ignore(cStyleComment)
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd']
-        """
-        if isinstance(other, basestring):
-            other = Suppress(other)
-
-        if isinstance(other, Suppress):
-            if other not in self.ignoreExprs:
-                self.ignoreExprs.append(other)
-        else:
-            self.ignoreExprs.append(Suppress(other.copy()))
-        return self
-
-    def setDebugActions(self, startAction, successAction, exceptionAction):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        """
-        self.debugActions = (startAction or _defaultStartDebugAction,
-                             successAction or _defaultSuccessDebugAction,
-                             exceptionAction or _defaultExceptionDebugAction)
-        self.debug = True
-        return self
-
-    def setDebug(self, flag=True):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        Set ``flag`` to True to enable, False to disable.
-
-        Example::
-
-            wd = Word(alphas).setName("alphaword")
-            integer = Word(nums).setName("numword")
-            term = wd | integer
-
-            # turn on debugging for wd
-            wd.setDebug()
-
-            OneOrMore(term).parseString("abc 123 xyz 890")
-
-        prints::
-
-            Match alphaword at loc 0(1,1)
-            Matched alphaword -> ['abc']
-            Match alphaword at loc 3(1,4)
-            Exception raised:Expected alphaword (at char 4), (line:1, col:5)
-            Match alphaword at loc 7(1,8)
-            Matched alphaword -> ['xyz']
-            Match alphaword at loc 11(1,12)
-            Exception raised:Expected alphaword (at char 12), (line:1, col:13)
-            Match alphaword at loc 15(1,16)
-            Exception raised:Expected alphaword (at char 15), (line:1, col:16)
-
-        The output shown is that produced by the default debug actions - custom debug actions can be
-        specified using :class:`setDebugActions`. Prior to attempting
-        to match the ``wd`` expression, the debugging message ``"Match  at loc (,)"``
-        is shown. Then if the parse succeeds, a ``"Matched"`` message is shown, or an ``"Exception raised"``
-        message is shown. Also note the use of :class:`setName` to assign a human-readable name to the expression,
-        which makes debugging and exception messages easier to understand - for instance, the default
-        name created for the :class:`Word` expression without calling ``setName`` is ``"W:(ABCD...)"``.
-        """
-        if flag:
-            self.setDebugActions(_defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction)
-        else:
-            self.debug = False
-        return self
-
-    def __str__(self):
-        return self.name
-
-    def __repr__(self):
-        return _ustr(self)
-
-    def streamline(self):
-        self.streamlined = True
-        self.strRepr = None
-        return self
-
-    def checkRecursion(self, parseElementList):
-        pass
-
-    def validate(self, validateTrace=None):
-        """
-        Check defined expressions for valid structure, check for infinite recursive definitions.
-        """
-        self.checkRecursion([])
-
-    def parseFile(self, file_or_filename, parseAll=False):
-        """
-        Execute the parse expression on the given file or filename.
-        If a filename is specified (instead of a file object),
-        the entire file is opened, read, and closed before parsing.
-        """
-        try:
-            file_contents = file_or_filename.read()
-        except AttributeError:
-            with open(file_or_filename, "r") as f:
-                file_contents = f.read()
-        try:
-            return self.parseString(file_contents, parseAll)
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clearing out pyparsing internal stack trace
-                if getattr(exc, '__traceback__', None) is not None:
-                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
-                raise exc
-
-    def __eq__(self, other):
-        if self is other:
-            return True
-        elif isinstance(other, basestring):
-            return self.matches(other)
-        elif isinstance(other, ParserElement):
-            return vars(self) == vars(other)
-        return False
-
-    def __ne__(self, other):
-        return not (self == other)
-
-    def __hash__(self):
-        return id(self)
-
-    def __req__(self, other):
-        return self == other
-
-    def __rne__(self, other):
-        return not (self == other)
-
-    def matches(self, testString, parseAll=True):
-        """
-        Method for quick testing of a parser against a test string. Good for simple
-        inline microtests of sub expressions while building up larger parser.
-
-        Parameters:
-         - testString - to test against this expression for a match
-         - parseAll - (default= ``True``) - flag to pass to :class:`parseString` when running tests
-
-        Example::
-
-            expr = Word(nums)
-            assert expr.matches("100")
-        """
-        try:
-            self.parseString(_ustr(testString), parseAll=parseAll)
-            return True
-        except ParseBaseException:
-            return False
-
-    def runTests(self, tests, parseAll=True, comment='#',
-                 fullDump=True, printResults=True, failureTests=False, postParse=None,
-                 file=None):
-        """
-        Execute the parse expression on a series of test strings, showing each
-        test, the parsed results or where the parse failed. Quick and easy way to
-        run a parse expression against a list of sample strings.
-
-        Parameters:
-         - tests - a list of separate test strings, or a multiline string of test strings
-         - parseAll - (default= ``True``) - flag to pass to :class:`parseString` when running tests
-         - comment - (default= ``'#'``) - expression for indicating embedded comments in the test
-              string; pass None to disable comment filtering
-         - fullDump - (default= ``True``) - dump results as list followed by results names in nested outline;
-              if False, only dump nested list
-         - printResults - (default= ``True``) prints test output to stdout
-         - failureTests - (default= ``False``) indicates if these tests are expected to fail parsing
-         - postParse - (default= ``None``) optional callback for successful parse results; called as
-              `fn(test_string, parse_results)` and returns a string to be added to the test output
-         - file - (default=``None``) optional file-like object to which test output will be written;
-              if None, will default to ``sys.stdout``
-
-        Returns: a (success, results) tuple, where success indicates that all tests succeeded
-        (or failed if ``failureTests`` is True), and the results contain a list of lines of each
-        test's output
-
-        Example::
-
-            number_expr = pyparsing_common.number.copy()
-
-            result = number_expr.runTests('''
-                # unsigned integer
-                100
-                # negative integer
-                -100
-                # float with scientific notation
-                6.02e23
-                # integer with scientific notation
-                1e-12
-                ''')
-            print("Success" if result[0] else "Failed!")
-
-            result = number_expr.runTests('''
-                # stray character
-                100Z
-                # missing leading digit before '.'
-                -.100
-                # too many '.'
-                3.14.159
-                ''', failureTests=True)
-            print("Success" if result[0] else "Failed!")
-
-        prints::
-
-            # unsigned integer
-            100
-            [100]
-
-            # negative integer
-            -100
-            [-100]
-
-            # float with scientific notation
-            6.02e23
-            [6.02e+23]
-
-            # integer with scientific notation
-            1e-12
-            [1e-12]
-
-            Success
-
-            # stray character
-            100Z
-               ^
-            FAIL: Expected end of text (at char 3), (line:1, col:4)
-
-            # missing leading digit before '.'
-            -.100
-            ^
-            FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
-
-            # too many '.'
-            3.14.159
-                ^
-            FAIL: Expected end of text (at char 4), (line:1, col:5)
-
-            Success
-
-        Each test string must be on a single line. If you want to test a string that spans multiple
-        lines, create a test like this::
-
-            expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")
-
-        (Note that this is a raw string literal, you must include the leading 'r'.)
-        """
-        if isinstance(tests, basestring):
-            tests = list(map(str.strip, tests.rstrip().splitlines()))
-        if isinstance(comment, basestring):
-            comment = Literal(comment)
-        if file is None:
-            file = sys.stdout
-        print_ = file.write
-
-        allResults = []
-        comments = []
-        success = True
-        NL = Literal(r'\n').addParseAction(replaceWith('\n')).ignore(quotedString)
-        BOM = u'\ufeff'
-        for t in tests:
-            if comment is not None and comment.matches(t, False) or comments and not t:
-                comments.append(t)
-                continue
-            if not t:
-                continue
-            out = ['\n' + '\n'.join(comments) if comments else '', t]
-            comments = []
-            try:
-                # convert newline marks to actual newlines, and strip leading BOM if present
-                t = NL.transformString(t.lstrip(BOM))
-                result = self.parseString(t, parseAll=parseAll)
-            except ParseBaseException as pe:
-                fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
-                if '\n' in t:
-                    out.append(line(pe.loc, t))
-                    out.append(' ' * (col(pe.loc, t) - 1) + '^' + fatal)
-                else:
-                    out.append(' ' * pe.loc + '^' + fatal)
-                out.append("FAIL: " + str(pe))
-                success = success and failureTests
-                result = pe
-            except Exception as exc:
-                out.append("FAIL-EXCEPTION: " + str(exc))
-                success = success and failureTests
-                result = exc
-            else:
-                success = success and not failureTests
-                if postParse is not None:
-                    try:
-                        pp_value = postParse(t, result)
-                        if pp_value is not None:
-                            if isinstance(pp_value, ParseResults):
-                                out.append(pp_value.dump())
-                            else:
-                                out.append(str(pp_value))
-                        else:
-                            out.append(result.dump())
-                    except Exception as e:
-                        out.append(result.dump(full=fullDump))
-                        out.append("{0} failed: {1}: {2}".format(postParse.__name__, type(e).__name__, e))
-                else:
-                    out.append(result.dump(full=fullDump))
-
-            if printResults:
-                if fullDump:
-                    out.append('')
-                print_('\n'.join(out))
-
-            allResults.append((t, result))
-
-        return success, allResults
-
-
-class _PendingSkip(ParserElement):
-    # internal placeholder class to hold a place were '...' is added to a parser element,
-    # once another ParserElement is added, this placeholder will be replaced with a SkipTo
-    def __init__(self, expr, must_skip=False):
-        super(_PendingSkip, self).__init__()
-        self.strRepr = str(expr + Empty()).replace('Empty', '...')
-        self.name = self.strRepr
-        self.anchor = expr
-        self.must_skip = must_skip
-
-    def __add__(self, other):
-        skipper = SkipTo(other).setName("...")("_skipped*")
-        if self.must_skip:
-            def must_skip(t):
-                if not t._skipped or t._skipped.asList() == ['']:
-                    del t[0]
-                    t.pop("_skipped", None)
-            def show_skip(t):
-                if t._skipped.asList()[-1:] == ['']:
-                    skipped = t.pop('_skipped')
-                    t['_skipped'] = 'missing <' + repr(self.anchor) + '>'
-            return (self.anchor + skipper().addParseAction(must_skip)
-                    | skipper().addParseAction(show_skip)) + other
-
-        return self.anchor + skipper + other
-
-    def __repr__(self):
-        return self.strRepr
-
-    def parseImpl(self, *args):
-        raise Exception("use of `...` expression without following SkipTo target expression")
-
-
-class Token(ParserElement):
-    """Abstract :class:`ParserElement` subclass, for defining atomic
-    matching patterns.
-    """
-    def __init__(self):
-        super(Token, self).__init__(savelist=False)
-
-
-class Empty(Token):
-    """An empty token, will always match.
-    """
-    def __init__(self):
-        super(Empty, self).__init__()
-        self.name = "Empty"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-
-
-class NoMatch(Token):
-    """A token that will never match.
-    """
-    def __init__(self):
-        super(NoMatch, self).__init__()
-        self.name = "NoMatch"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-        self.errmsg = "Unmatchable token"
-
-    def parseImpl(self, instring, loc, doActions=True):
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Literal(Token):
-    """Token to exactly match a specified string.
-
-    Example::
-
-        Literal('blah').parseString('blah')  # -> ['blah']
-        Literal('blah').parseString('blahfooblah')  # -> ['blah']
-        Literal('blah').parseString('bla')  # -> Exception: Expected "blah"
-
-    For case-insensitive matching, use :class:`CaselessLiteral`.
-
-    For keyword matching (force word break before and after the matched string),
-    use :class:`Keyword` or :class:`CaselessKeyword`.
-    """
-    def __init__(self, matchString):
-        super(Literal, self).__init__()
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Literal; use Empty() instead",
-                            SyntaxWarning, stacklevel=2)
-            self.__class__ = Empty
-        self.name = '"%s"' % _ustr(self.match)
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-
-        # Performance tuning: modify __class__ to select
-        # a parseImpl optimized for single-character check
-        if self.matchLen == 1 and type(self) is Literal:
-            self.__class__ = _SingleCharLiteral
-
-    def parseImpl(self, instring, loc, doActions=True):
-        if instring[loc] == self.firstMatchChar and instring.startswith(self.match, loc):
-            return loc + self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class _SingleCharLiteral(Literal):
-    def parseImpl(self, instring, loc, doActions=True):
-        if instring[loc] == self.firstMatchChar:
-            return loc + 1, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-_L = Literal
-ParserElement._literalStringClass = Literal
-
-class Keyword(Token):
-    """Token to exactly match a specified string as a keyword, that is,
-    it must be immediately followed by a non-keyword character.  Compare
-    with :class:`Literal`:
-
-     - ``Literal("if")`` will match the leading ``'if'`` in
-       ``'ifAndOnlyIf'``.
-     - ``Keyword("if")`` will not; it will only match the leading
-       ``'if'`` in ``'if x=1'``, or ``'if(y==2)'``
-
-    Accepts two optional constructor arguments in addition to the
-    keyword string:
-
-     - ``identChars`` is a string of characters that would be valid
-       identifier characters, defaulting to all alphanumerics + "_" and
-       "$"
-     - ``caseless`` allows case-insensitive matching, default is ``False``.
-
-    Example::
-
-        Keyword("start").parseString("start")  # -> ['start']
-        Keyword("start").parseString("starting")  # -> Exception
-
-    For case-insensitive matching, use :class:`CaselessKeyword`.
-    """
-    DEFAULT_KEYWORD_CHARS = alphanums + "_$"
-
-    def __init__(self, matchString, identChars=None, caseless=False):
-        super(Keyword, self).__init__()
-        if identChars is None:
-            identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Keyword; use Empty() instead",
-                          SyntaxWarning, stacklevel=2)
-        self.name = '"%s"' % self.match
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-        self.caseless = caseless
-        if caseless:
-            self.caselessmatch = matchString.upper()
-            identChars = identChars.upper()
-        self.identChars = set(identChars)
-
-    def parseImpl(self, instring, loc, doActions=True):
-        if self.caseless:
-            if ((instring[loc:loc + self.matchLen].upper() == self.caselessmatch)
-                    and (loc >= len(instring) - self.matchLen
-                         or instring[loc + self.matchLen].upper() not in self.identChars)
-                    and (loc == 0
-                         or instring[loc - 1].upper() not in self.identChars)):
-                return loc + self.matchLen, self.match
-
-        else:
-            if instring[loc] == self.firstMatchChar:
-                if ((self.matchLen == 1 or instring.startswith(self.match, loc))
-                        and (loc >= len(instring) - self.matchLen
-                             or instring[loc + self.matchLen] not in self.identChars)
-                        and (loc == 0 or instring[loc - 1] not in self.identChars)):
-                    return loc + self.matchLen, self.match
-
-        raise ParseException(instring, loc, self.errmsg, self)
-
-    def copy(self):
-        c = super(Keyword, self).copy()
-        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        return c
-
-    @staticmethod
-    def setDefaultKeywordChars(chars):
-        """Overrides the default Keyword chars
-        """
-        Keyword.DEFAULT_KEYWORD_CHARS = chars
-
-class CaselessLiteral(Literal):
-    """Token to match a specified string, ignoring case of letters.
-    Note: the matched results will always be in the case of the given
-    match string, NOT the case of the input text.
-
-    Example::
-
-        OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD']
-
-    (Contrast with example for :class:`CaselessKeyword`.)
-    """
-    def __init__(self, matchString):
-        super(CaselessLiteral, self).__init__(matchString.upper())
-        # Preserve the defining literal.
-        self.returnString = matchString
-        self.name = "'%s'" % self.returnString
-        self.errmsg = "Expected " + self.name
-
-    def parseImpl(self, instring, loc, doActions=True):
-        if instring[loc:loc + self.matchLen].upper() == self.match:
-            return loc + self.matchLen, self.returnString
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class CaselessKeyword(Keyword):
-    """
-    Caseless version of :class:`Keyword`.
-
-    Example::
-
-        OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD']
-
-    (Contrast with example for :class:`CaselessLiteral`.)
-    """
-    def __init__(self, matchString, identChars=None):
-        super(CaselessKeyword, self).__init__(matchString, identChars, caseless=True)
-
-class CloseMatch(Token):
-    """A variation on :class:`Literal` which matches "close" matches,
-    that is, strings with at most 'n' mismatching characters.
-    :class:`CloseMatch` takes parameters:
-
-     - ``match_string`` - string to be matched
-     - ``maxMismatches`` - (``default=1``) maximum number of
-       mismatches allowed to count as a match
-
-    The results from a successful parse will contain the matched text
-    from the input string and the following named results:
-
-     - ``mismatches`` - a list of the positions within the
-       match_string where mismatches were found
-     - ``original`` - the original match_string used to compare
-       against the input string
-
-    If ``mismatches`` is an empty list, then the match was an exact
-    match.
-
-    Example::
-
-        patt = CloseMatch("ATCATCGAATGGA")
-        patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
-        patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
-
-        # exact match
-        patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
-
-        # close match allowing up to 2 mismatches
-        patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2)
-        patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
-    """
-    def __init__(self, match_string, maxMismatches=1):
-        super(CloseMatch, self).__init__()
-        self.name = match_string
-        self.match_string = match_string
-        self.maxMismatches = maxMismatches
-        self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches)
-        self.mayIndexError = False
-        self.mayReturnEmpty = False
-
-    def parseImpl(self, instring, loc, doActions=True):
-        start = loc
-        instrlen = len(instring)
-        maxloc = start + len(self.match_string)
-
-        if maxloc <= instrlen:
-            match_string = self.match_string
-            match_stringloc = 0
-            mismatches = []
-            maxMismatches = self.maxMismatches
-
-            for match_stringloc, s_m in enumerate(zip(instring[loc:maxloc], match_string)):
-                src, mat = s_m
-                if src != mat:
-                    mismatches.append(match_stringloc)
-                    if len(mismatches) > maxMismatches:
-                        break
-            else:
-                loc = match_stringloc + 1
-                results = ParseResults([instring[start:loc]])
-                results['original'] = match_string
-                results['mismatches'] = mismatches
-                return loc, results
-
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Word(Token):
-    """Token for matching words composed of allowed character sets.
-    Defined with string containing all allowed initial characters, an
-    optional string containing allowed body characters (if omitted,
-    defaults to the initial character set), and an optional minimum,
-    maximum, and/or exact length.  The default value for ``min`` is
-    1 (a minimum value < 1 is not valid); the default values for
-    ``max`` and ``exact`` are 0, meaning no maximum or exact
-    length restriction. An optional ``excludeChars`` parameter can
-    list characters that might be found in the input ``bodyChars``
-    string; useful to define a word of all printables except for one or
-    two characters, for instance.
-
-    :class:`srange` is useful for defining custom character set strings
-    for defining ``Word`` expressions, using range notation from
-    regular expression character sets.
-
-    A common mistake is to use :class:`Word` to match a specific literal
-    string, as in ``Word("Address")``. Remember that :class:`Word`
-    uses the string argument to define *sets* of matchable characters.
-    This expression would match "Add", "AAA", "dAred", or any other word
-    made up of the characters 'A', 'd', 'r', 'e', and 's'. To match an
-    exact literal string, use :class:`Literal` or :class:`Keyword`.
-
-    pyparsing includes helper strings for building Words:
-
-     - :class:`alphas`
-     - :class:`nums`
-     - :class:`alphanums`
-     - :class:`hexnums`
-     - :class:`alphas8bit` (alphabetic characters in ASCII range 128-255
-       - accented, tilded, umlauted, etc.)
-     - :class:`punc8bit` (non-alphabetic characters in ASCII range
-       128-255 - currency, symbols, superscripts, diacriticals, etc.)
-     - :class:`printables` (any non-whitespace character)
-
-    Example::
-
-        # a word composed of digits
-        integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
-
-        # a word with a leading capital, and zero or more lowercase
-        capital_word = Word(alphas.upper(), alphas.lower())
-
-        # hostnames are alphanumeric, with leading alpha, and '-'
-        hostname = Word(alphas, alphanums + '-')
-
-        # roman numeral (not a strict parser, accepts invalid mix of characters)
-        roman = Word("IVXLCDM")
-
-        # any string of non-whitespace characters, except for ','
-        csv_value = Word(printables, excludeChars=",")
-    """
-    def __init__(self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None):
-        super(Word, self).__init__()
-        if excludeChars:
-            excludeChars = set(excludeChars)
-            initChars = ''.join(c for c in initChars if c not in excludeChars)
-            if bodyChars:
-                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
-        self.initCharsOrig = initChars
-        self.initChars = set(initChars)
-        if bodyChars:
-            self.bodyCharsOrig = bodyChars
-            self.bodyChars = set(bodyChars)
-        else:
-            self.bodyCharsOrig = initChars
-            self.bodyChars = set(initChars)
-
-        self.maxSpecified = max > 0
-
-        if min < 1:
-            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.asKeyword = asKeyword
-
-        if ' ' not in self.initCharsOrig + self.bodyCharsOrig and (min == 1 and max == 0 and exact == 0):
-            if self.bodyCharsOrig == self.initCharsOrig:
-                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
-            elif len(self.initCharsOrig) == 1:
-                self.reString = "%s[%s]*" % (re.escape(self.initCharsOrig),
-                                             _escapeRegexRangeChars(self.bodyCharsOrig),)
-            else:
-                self.reString = "[%s][%s]*" % (_escapeRegexRangeChars(self.initCharsOrig),
-                                               _escapeRegexRangeChars(self.bodyCharsOrig),)
-            if self.asKeyword:
-                self.reString = r"\b" + self.reString + r"\b"
-
-            try:
-                self.re = re.compile(self.reString)
-            except Exception:
-                self.re = None
-            else:
-                self.re_match = self.re.match
-                self.__class__ = _WordRegex
-
-    def parseImpl(self, instring, loc, doActions=True):
-        if instring[loc] not in self.initChars:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        start = loc
-        loc += 1
-        instrlen = len(instring)
-        bodychars = self.bodyChars
-        maxloc = start + self.maxLen
-        maxloc = min(maxloc, instrlen)
-        while loc < maxloc and instring[loc] in bodychars:
-            loc += 1
-
-        throwException = False
-        if loc - start < self.minLen:
-            throwException = True
-        elif self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
-            throwException = True
-        elif self.asKeyword:
-            if (start > 0 and instring[start - 1] in bodychars
-                    or loc < instrlen and instring[loc] in bodychars):
-                throwException = True
-
-        if throwException:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        return loc, instring[start:loc]
-
-    def __str__(self):
-        try:
-            return super(Word, self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-
-            def charsAsStr(s):
-                if len(s) > 4:
-                    return s[:4] + "..."
-                else:
-                    return s
-
-            if self.initCharsOrig != self.bodyCharsOrig:
-                self.strRepr = "W:(%s, %s)" % (charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig))
-            else:
-                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
-
-        return self.strRepr
-
-class _WordRegex(Word):
-    def parseImpl(self, instring, loc, doActions=True):
-        result = self.re_match(instring, loc)
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        return loc, result.group()
-
-
-class Char(_WordRegex):
-    """A short-cut class for defining ``Word(characters, exact=1)``,
-    when defining a match of any single character in a string of
-    characters.
-    """
-    def __init__(self, charset, asKeyword=False, excludeChars=None):
-        super(Char, self).__init__(charset, exact=1, asKeyword=asKeyword, excludeChars=excludeChars)
-        self.reString = "[%s]" % _escapeRegexRangeChars(''.join(self.initChars))
-        if asKeyword:
-            self.reString = r"\b%s\b" % self.reString
-        self.re = re.compile(self.reString)
-        self.re_match = self.re.match
-
-
-class Regex(Token):
-    r"""Token for matching strings that match a given regular
-    expression. Defined with string specifying the regular expression in
-    a form recognized by the stdlib Python  `re module `_.
-    If the given regex contains named groups (defined using ``(?P...)``),
-    these will be preserved as named parse results.
-
-    If instead of the Python stdlib re module you wish to use a different RE module
-    (such as the `regex` module), you can replace it by either building your
-    Regex object with a compiled RE that was compiled using regex:
-
-    Example::
-
-        realnum = Regex(r"[+-]?\d+\.\d*")
-        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)')
-        # ref: https://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
-        roman = Regex(r"M{0,4}(CM|CD|D?{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})")
-
-        # use regex module instead of stdlib re module to construct a Regex using
-        # a compiled regular expression
-        import regex
-        parser = pp.Regex(regex.compile(r'[0-9]'))
-
-    """
-    def __init__(self, pattern, flags=0, asGroupList=False, asMatch=False):
-        """The parameters ``pattern`` and ``flags`` are passed
-        to the ``re.compile()`` function as-is. See the Python
-        `re module `_ module for an
-        explanation of the acceptable patterns and flags.
-        """
-        super(Regex, self).__init__()
-
-        if isinstance(pattern, basestring):
-            if not pattern:
-                warnings.warn("null string passed to Regex; use Empty() instead",
-                              SyntaxWarning, stacklevel=2)
-
-            self.pattern = pattern
-            self.flags = flags
-
-            try:
-                self.re = re.compile(self.pattern, self.flags)
-                self.reString = self.pattern
-            except sre_constants.error:
-                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
-                              SyntaxWarning, stacklevel=2)
-                raise
-
-        elif hasattr(pattern, 'pattern') and hasattr(pattern, 'match'):
-            self.re = pattern
-            self.pattern = self.reString = pattern.pattern
-            self.flags = flags
-
-        else:
-            raise TypeError("Regex may only be constructed with a string or a compiled RE object")
-
-        self.re_match = self.re.match
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.mayReturnEmpty = self.re_match("") is not None
-        self.asGroupList = asGroupList
-        self.asMatch = asMatch
-        if self.asGroupList:
-            self.parseImpl = self.parseImplAsGroupList
-        if self.asMatch:
-            self.parseImpl = self.parseImplAsMatch
-
-    def parseImpl(self, instring, loc, doActions=True):
-        result = self.re_match(instring, loc)
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        ret = ParseResults(result.group())
-        d = result.groupdict()
-        if d:
-            for k, v in d.items():
-                ret[k] = v
-        return loc, ret
-
-    def parseImplAsGroupList(self, instring, loc, doActions=True):
-        result = self.re_match(instring, loc)
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        ret = result.groups()
-        return loc, ret
-
-    def parseImplAsMatch(self, instring, loc, doActions=True):
-        result = self.re_match(instring, loc)
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        ret = result
-        return loc, ret
-
-    def __str__(self):
-        try:
-            return super(Regex, self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            self.strRepr = "Re:(%s)" % repr(self.pattern)
-
-        return self.strRepr
-
-    def sub(self, repl):
-        r"""
-        Return Regex with an attached parse action to transform the parsed
-        result as if called using `re.sub(expr, repl, string) `_.
-
-        Example::
-
-            make_html = Regex(r"(\w+):(.*?):").sub(r"<\1>\2")
-            print(make_html.transformString("h1:main title:"))
-            # prints "

main title

" - """ - if self.asGroupList: - warnings.warn("cannot use sub() with Regex(asGroupList=True)", - SyntaxWarning, stacklevel=2) - raise SyntaxError() - - if self.asMatch and callable(repl): - warnings.warn("cannot use sub() with a callable with Regex(asMatch=True)", - SyntaxWarning, stacklevel=2) - raise SyntaxError() - - if self.asMatch: - def pa(tokens): - return tokens[0].expand(repl) - else: - def pa(tokens): - return self.re.sub(repl, tokens[0]) - return self.addParseAction(pa) - -class QuotedString(Token): - r""" - Token for matching strings that are delimited by quoting characters. - - Defined with the following parameters: - - - quoteChar - string of one or more characters defining the - quote delimiting string - - escChar - character to escape quotes, typically backslash - (default= ``None``) - - escQuote - special quote sequence to escape an embedded quote - string (such as SQL's ``""`` to escape an embedded ``"``) - (default= ``None``) - - multiline - boolean indicating whether quotes can span - multiple lines (default= ``False``) - - unquoteResults - boolean indicating whether the matched text - should be unquoted (default= ``True``) - - endQuoteChar - string of one or more characters defining the - end of the quote delimited string (default= ``None`` => same as - quoteChar) - - convertWhitespaceEscapes - convert escaped whitespace - (``'\t'``, ``'\n'``, etc.) to actual whitespace - (default= ``True``) - - Example:: - - qs = QuotedString('"') - print(qs.searchString('lsjdf "This is the quote" sldjf')) - complex_qs = QuotedString('{{', endQuoteChar='}}') - print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) - sql_qs = QuotedString('"', escQuote='""') - print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) - - prints:: - - [['This is the quote']] - [['This is the "quote"']] - [['This is the quote with "embedded" quotes']] - """ - def __init__(self, quoteChar, escChar=None, escQuote=None, multiline=False, - unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): - super(QuotedString, self).__init__() - - # remove white space from quote chars - wont work anyway - quoteChar = quoteChar.strip() - if not quoteChar: - warnings.warn("quoteChar cannot be the empty string", SyntaxWarning, stacklevel=2) - raise SyntaxError() - - if endQuoteChar is None: - endQuoteChar = quoteChar - else: - endQuoteChar = endQuoteChar.strip() - if not endQuoteChar: - warnings.warn("endQuoteChar cannot be the empty string", SyntaxWarning, stacklevel=2) - raise SyntaxError() - - self.quoteChar = quoteChar - self.quoteCharLen = len(quoteChar) - self.firstQuoteChar = quoteChar[0] - self.endQuoteChar = endQuoteChar - self.endQuoteCharLen = len(endQuoteChar) - self.escChar = escChar - self.escQuote = escQuote - self.unquoteResults = unquoteResults - self.convertWhitespaceEscapes = convertWhitespaceEscapes - - if multiline: - self.flags = re.MULTILINE | re.DOTALL - self.pattern = r'%s(?:[^%s%s]' % (re.escape(self.quoteChar), - _escapeRegexRangeChars(self.endQuoteChar[0]), - (escChar is not None and _escapeRegexRangeChars(escChar) or '')) - else: - self.flags = 0 - self.pattern = r'%s(?:[^%s\n\r%s]' % (re.escape(self.quoteChar), - _escapeRegexRangeChars(self.endQuoteChar[0]), - (escChar is not None and _escapeRegexRangeChars(escChar) or '')) - if len(self.endQuoteChar) > 1: - self.pattern += ( - '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]), - _escapeRegexRangeChars(self.endQuoteChar[i])) - for i in range(len(self.endQuoteChar) - 1, 0, -1)) + ')') - - if escQuote: - self.pattern += (r'|(?:%s)' % re.escape(escQuote)) - if escChar: - self.pattern += (r'|(?:%s.)' % re.escape(escChar)) - self.escCharReplacePattern = re.escape(self.escChar) + "(.)" - self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) - - try: - self.re = re.compile(self.pattern, self.flags) - self.reString = self.pattern - self.re_match = self.re.match - except sre_constants.error: - warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, - SyntaxWarning, stacklevel=2) - raise - - self.name = _ustr(self) - self.errmsg = "Expected " + self.name - self.mayIndexError = False - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - result = instring[loc] == self.firstQuoteChar and self.re_match(instring, loc) or None - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - ret = result.group() - - if self.unquoteResults: - - # strip off quotes - ret = ret[self.quoteCharLen: -self.endQuoteCharLen] - - if isinstance(ret, basestring): - # replace escaped whitespace - if '\\' in ret and self.convertWhitespaceEscapes: - ws_map = { - r'\t': '\t', - r'\n': '\n', - r'\f': '\f', - r'\r': '\r', - } - for wslit, wschar in ws_map.items(): - ret = ret.replace(wslit, wschar) - - # replace escaped characters - if self.escChar: - ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret) - - # replace escaped quotes - if self.escQuote: - ret = ret.replace(self.escQuote, self.endQuoteChar) - - return loc, ret - - def __str__(self): - try: - return super(QuotedString, self).__str__() - except Exception: - pass - - if self.strRepr is None: - self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) - - return self.strRepr - - -class CharsNotIn(Token): - """Token for matching words composed of characters *not* in a given - set (will include whitespace in matched characters if not listed in - the provided exclusion set - see example). Defined with string - containing all disallowed characters, and an optional minimum, - maximum, and/or exact length. The default value for ``min`` is - 1 (a minimum value < 1 is not valid); the default values for - ``max`` and ``exact`` are 0, meaning no maximum or exact - length restriction. - - Example:: - - # define a comma-separated-value as anything that is not a ',' - csv_value = CharsNotIn(',') - print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) - - prints:: - - ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] - """ - def __init__(self, notChars, min=1, max=0, exact=0): - super(CharsNotIn, self).__init__() - self.skipWhitespace = False - self.notChars = notChars - - if min < 1: - raise ValueError("cannot specify a minimum length < 1; use " - "Optional(CharsNotIn()) if zero-length char group is permitted") - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - self.name = _ustr(self) - self.errmsg = "Expected " + self.name - self.mayReturnEmpty = (self.minLen == 0) - self.mayIndexError = False - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] in self.notChars: - raise ParseException(instring, loc, self.errmsg, self) - - start = loc - loc += 1 - notchars = self.notChars - maxlen = min(start + self.maxLen, len(instring)) - while loc < maxlen and instring[loc] not in notchars: - loc += 1 - - if loc - start < self.minLen: - raise ParseException(instring, loc, self.errmsg, self) - - return loc, instring[start:loc] - - def __str__(self): - try: - return super(CharsNotIn, self).__str__() - except Exception: - pass - - if self.strRepr is None: - if len(self.notChars) > 4: - self.strRepr = "!W:(%s...)" % self.notChars[:4] - else: - self.strRepr = "!W:(%s)" % self.notChars - - return self.strRepr - -class White(Token): - """Special matching class for matching whitespace. Normally, - whitespace is ignored by pyparsing grammars. This class is included - when some whitespace structures are significant. Define with - a string containing the whitespace characters to be matched; default - is ``" \\t\\r\\n"``. Also takes optional ``min``, - ``max``, and ``exact`` arguments, as defined for the - :class:`Word` class. - """ - whiteStrs = { - ' ' : '', - '\t': '', - '\n': '', - '\r': '', - '\f': '', - u'\u00A0': '', - u'\u1680': '', - u'\u180E': '', - u'\u2000': '', - u'\u2001': '', - u'\u2002': '', - u'\u2003': '', - u'\u2004': '', - u'\u2005': '', - u'\u2006': '', - u'\u2007': '', - u'\u2008': '', - u'\u2009': '', - u'\u200A': '', - u'\u200B': '', - u'\u202F': '', - u'\u205F': '', - u'\u3000': '', - } - def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): - super(White, self).__init__() - self.matchWhite = ws - self.setWhitespaceChars("".join(c for c in self.whiteChars if c not in self.matchWhite)) - # ~ self.leaveWhitespace() - self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite)) - self.mayReturnEmpty = True - self.errmsg = "Expected " + self.name - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] not in self.matchWhite: - raise ParseException(instring, loc, self.errmsg, self) - start = loc - loc += 1 - maxloc = start + self.maxLen - maxloc = min(maxloc, len(instring)) - while loc < maxloc and instring[loc] in self.matchWhite: - loc += 1 - - if loc - start < self.minLen: - raise ParseException(instring, loc, self.errmsg, self) - - return loc, instring[start:loc] - - -class _PositionToken(Token): - def __init__(self): - super(_PositionToken, self).__init__() - self.name = self.__class__.__name__ - self.mayReturnEmpty = True - self.mayIndexError = False - -class GoToColumn(_PositionToken): - """Token to advance to a specific column of input text; useful for - tabular report scraping. - """ - def __init__(self, colno): - super(GoToColumn, self).__init__() - self.col = colno - - def preParse(self, instring, loc): - if col(loc, instring) != self.col: - instrlen = len(instring) - if self.ignoreExprs: - loc = self._skipIgnorables(instring, loc) - while loc < instrlen and instring[loc].isspace() and col(loc, instring) != self.col: - loc += 1 - return loc - - def parseImpl(self, instring, loc, doActions=True): - thiscol = col(loc, instring) - if thiscol > self.col: - raise ParseException(instring, loc, "Text not in expected column", self) - newloc = loc + self.col - thiscol - ret = instring[loc: newloc] - return newloc, ret - - -class LineStart(_PositionToken): - r"""Matches if current position is at the beginning of a line within - the parse string - - Example:: - - test = '''\ - AAA this line - AAA and this line - AAA but not this one - B AAA and definitely not this one - ''' - - for t in (LineStart() + 'AAA' + restOfLine).searchString(test): - print(t) - - prints:: - - ['AAA', ' this line'] - ['AAA', ' and this line'] - - """ - def __init__(self): - super(LineStart, self).__init__() - self.errmsg = "Expected start of line" - - def parseImpl(self, instring, loc, doActions=True): - if col(loc, instring) == 1: - return loc, [] - raise ParseException(instring, loc, self.errmsg, self) - -class LineEnd(_PositionToken): - """Matches if current position is at the end of a line within the - parse string - """ - def __init__(self): - super(LineEnd, self).__init__() - self.setWhitespaceChars(ParserElement.DEFAULT_WHITE_CHARS.replace("\n", "")) - self.errmsg = "Expected end of line" - - def parseImpl(self, instring, loc, doActions=True): - if loc < len(instring): - if instring[loc] == "\n": - return loc + 1, "\n" - else: - raise ParseException(instring, loc, self.errmsg, self) - elif loc == len(instring): - return loc + 1, [] - else: - raise ParseException(instring, loc, self.errmsg, self) - -class StringStart(_PositionToken): - """Matches if current position is at the beginning of the parse - string - """ - def __init__(self): - super(StringStart, self).__init__() - self.errmsg = "Expected start of text" - - def parseImpl(self, instring, loc, doActions=True): - if loc != 0: - # see if entire string up to here is just whitespace and ignoreables - if loc != self.preParse(instring, 0): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - -class StringEnd(_PositionToken): - """Matches if current position is at the end of the parse string - """ - def __init__(self): - super(StringEnd, self).__init__() - self.errmsg = "Expected end of text" - - def parseImpl(self, instring, loc, doActions=True): - if loc < len(instring): - raise ParseException(instring, loc, self.errmsg, self) - elif loc == len(instring): - return loc + 1, [] - elif loc > len(instring): - return loc, [] - else: - raise ParseException(instring, loc, self.errmsg, self) - -class WordStart(_PositionToken): - """Matches if the current position is at the beginning of a Word, - and is not preceded by any character in a given set of - ``wordChars`` (default= ``printables``). To emulate the - ``\b`` behavior of regular expressions, use - ``WordStart(alphanums)``. ``WordStart`` will also match at - the beginning of the string being parsed, or at the beginning of - a line. - """ - def __init__(self, wordChars=printables): - super(WordStart, self).__init__() - self.wordChars = set(wordChars) - self.errmsg = "Not at the start of a word" - - def parseImpl(self, instring, loc, doActions=True): - if loc != 0: - if (instring[loc - 1] in self.wordChars - or instring[loc] not in self.wordChars): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - -class WordEnd(_PositionToken): - """Matches if the current position is at the end of a Word, and is - not followed by any character in a given set of ``wordChars`` - (default= ``printables``). To emulate the ``\b`` behavior of - regular expressions, use ``WordEnd(alphanums)``. ``WordEnd`` - will also match at the end of the string being parsed, or at the end - of a line. - """ - def __init__(self, wordChars=printables): - super(WordEnd, self).__init__() - self.wordChars = set(wordChars) - self.skipWhitespace = False - self.errmsg = "Not at the end of a word" - - def parseImpl(self, instring, loc, doActions=True): - instrlen = len(instring) - if instrlen > 0 and loc < instrlen: - if (instring[loc] in self.wordChars or - instring[loc - 1] not in self.wordChars): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - -class ParseExpression(ParserElement): - """Abstract subclass of ParserElement, for combining and - post-processing parsed tokens. - """ - def __init__(self, exprs, savelist=False): - super(ParseExpression, self).__init__(savelist) - if isinstance(exprs, _generatorType): - exprs = list(exprs) - - if isinstance(exprs, basestring): - self.exprs = [self._literalStringClass(exprs)] - elif isinstance(exprs, ParserElement): - self.exprs = [exprs] - elif isinstance(exprs, Iterable): - exprs = list(exprs) - # if sequence of strings provided, wrap with Literal - if any(isinstance(expr, basestring) for expr in exprs): - exprs = (self._literalStringClass(e) if isinstance(e, basestring) else e for e in exprs) - self.exprs = list(exprs) - else: - try: - self.exprs = list(exprs) - except TypeError: - self.exprs = [exprs] - self.callPreparse = False - - def append(self, other): - self.exprs.append(other) - self.strRepr = None - return self - - def leaveWhitespace(self): - """Extends ``leaveWhitespace`` defined in base class, and also invokes ``leaveWhitespace`` on - all contained expressions.""" - self.skipWhitespace = False - self.exprs = [e.copy() for e in self.exprs] - for e in self.exprs: - e.leaveWhitespace() - return self - - def ignore(self, other): - if isinstance(other, Suppress): - if other not in self.ignoreExprs: - super(ParseExpression, self).ignore(other) - for e in self.exprs: - e.ignore(self.ignoreExprs[-1]) - else: - super(ParseExpression, self).ignore(other) - for e in self.exprs: - e.ignore(self.ignoreExprs[-1]) - return self - - def __str__(self): - try: - return super(ParseExpression, self).__str__() - except Exception: - pass - - if self.strRepr is None: - self.strRepr = "%s:(%s)" % (self.__class__.__name__, _ustr(self.exprs)) - return self.strRepr - - def streamline(self): - super(ParseExpression, self).streamline() - - for e in self.exprs: - e.streamline() - - # collapse nested And's of the form And(And(And(a, b), c), d) to And(a, b, c, d) - # but only if there are no parse actions or resultsNames on the nested And's - # (likewise for Or's and MatchFirst's) - if len(self.exprs) == 2: - other = self.exprs[0] - if (isinstance(other, self.__class__) - and not other.parseAction - and other.resultsName is None - and not other.debug): - self.exprs = other.exprs[:] + [self.exprs[1]] - self.strRepr = None - self.mayReturnEmpty |= other.mayReturnEmpty - self.mayIndexError |= other.mayIndexError - - other = self.exprs[-1] - if (isinstance(other, self.__class__) - and not other.parseAction - and other.resultsName is None - and not other.debug): - self.exprs = self.exprs[:-1] + other.exprs[:] - self.strRepr = None - self.mayReturnEmpty |= other.mayReturnEmpty - self.mayIndexError |= other.mayIndexError - - self.errmsg = "Expected " + _ustr(self) - - return self - - def validate(self, validateTrace=None): - tmp = (validateTrace if validateTrace is not None else [])[:] + [self] - for e in self.exprs: - e.validate(tmp) - self.checkRecursion([]) - - def copy(self): - ret = super(ParseExpression, self).copy() - ret.exprs = [e.copy() for e in self.exprs] - return ret - - def _setResultsName(self, name, listAllMatches=False): - if __diag__.warn_ungrouped_named_tokens_in_collection: - for e in self.exprs: - if isinstance(e, ParserElement) and e.resultsName: - warnings.warn("{0}: setting results name {1!r} on {2} expression " - "collides with {3!r} on contained expression".format("warn_ungrouped_named_tokens_in_collection", - name, - type(self).__name__, - e.resultsName), - stacklevel=3) - - return super(ParseExpression, self)._setResultsName(name, listAllMatches) - - -class And(ParseExpression): - """ - Requires all given :class:`ParseExpression` s to be found in the given order. - Expressions may be separated by whitespace. - May be constructed using the ``'+'`` operator. - May also be constructed using the ``'-'`` operator, which will - suppress backtracking. - - Example:: - - integer = Word(nums) - name_expr = OneOrMore(Word(alphas)) - - expr = And([integer("id"), name_expr("name"), integer("age")]) - # more easily written as: - expr = integer("id") + name_expr("name") + integer("age") - """ - - class _ErrorStop(Empty): - def __init__(self, *args, **kwargs): - super(And._ErrorStop, self).__init__(*args, **kwargs) - self.name = '-' - self.leaveWhitespace() - - def __init__(self, exprs, savelist=True): - exprs = list(exprs) - if exprs and Ellipsis in exprs: - tmp = [] - for i, expr in enumerate(exprs): - if expr is Ellipsis: - if i < len(exprs) - 1: - skipto_arg = (Empty() + exprs[i + 1]).exprs[-1] - tmp.append(SkipTo(skipto_arg)("_skipped*")) - else: - raise Exception("cannot construct And with sequence ending in ...") - else: - tmp.append(expr) - exprs[:] = tmp - super(And, self).__init__(exprs, savelist) - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - self.setWhitespaceChars(self.exprs[0].whiteChars) - self.skipWhitespace = self.exprs[0].skipWhitespace - self.callPreparse = True - - def streamline(self): - # collapse any _PendingSkip's - if self.exprs: - if any(isinstance(e, ParseExpression) and e.exprs and isinstance(e.exprs[-1], _PendingSkip) - for e in self.exprs[:-1]): - for i, e in enumerate(self.exprs[:-1]): - if e is None: - continue - if (isinstance(e, ParseExpression) - and e.exprs and isinstance(e.exprs[-1], _PendingSkip)): - e.exprs[-1] = e.exprs[-1] + self.exprs[i + 1] - self.exprs[i + 1] = None - self.exprs = [e for e in self.exprs if e is not None] - - super(And, self).streamline() - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - return self - - def parseImpl(self, instring, loc, doActions=True): - # pass False as last arg to _parse for first element, since we already - # pre-parsed the string as part of our And pre-parsing - loc, resultlist = self.exprs[0]._parse(instring, loc, doActions, callPreParse=False) - errorStop = False - for e in self.exprs[1:]: - if isinstance(e, And._ErrorStop): - errorStop = True - continue - if errorStop: - try: - loc, exprtokens = e._parse(instring, loc, doActions) - except ParseSyntaxException: - raise - except ParseBaseException as pe: - pe.__traceback__ = None - raise ParseSyntaxException._from_exception(pe) - except IndexError: - raise ParseSyntaxException(instring, len(instring), self.errmsg, self) - else: - loc, exprtokens = e._parse(instring, loc, doActions) - if exprtokens or exprtokens.haskeys(): - resultlist += exprtokens - return loc, resultlist - - def __iadd__(self, other): - if isinstance(other, basestring): - other = self._literalStringClass(other) - return self.append(other) # And([self, other]) - - def checkRecursion(self, parseElementList): - subRecCheckList = parseElementList[:] + [self] - for e in self.exprs: - e.checkRecursion(subRecCheckList) - if not e.mayReturnEmpty: - break - - def __str__(self): - if hasattr(self, "name"): - return self.name - - if self.strRepr is None: - self.strRepr = "{" + " ".join(_ustr(e) for e in self.exprs) + "}" - - return self.strRepr - - -class Or(ParseExpression): - """Requires that at least one :class:`ParseExpression` is found. If - two expressions match, the expression that matches the longest - string will be used. May be constructed using the ``'^'`` - operator. - - Example:: - - # construct Or using '^' operator - - number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) - print(number.searchString("123 3.1416 789")) - - prints:: - - [['123'], ['3.1416'], ['789']] - """ - def __init__(self, exprs, savelist=False): - super(Or, self).__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - else: - self.mayReturnEmpty = True - - def streamline(self): - super(Or, self).streamline() - if __compat__.collect_all_And_tokens: - self.saveAsList = any(e.saveAsList for e in self.exprs) - return self - - def parseImpl(self, instring, loc, doActions=True): - maxExcLoc = -1 - maxException = None - matches = [] - for e in self.exprs: - try: - loc2 = e.tryParse(instring, loc) - except ParseException as err: - err.__traceback__ = None - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - except IndexError: - if len(instring) > maxExcLoc: - maxException = ParseException(instring, len(instring), e.errmsg, self) - maxExcLoc = len(instring) - else: - # save match among all matches, to retry longest to shortest - matches.append((loc2, e)) - - if matches: - # re-evaluate all matches in descending order of length of match, in case attached actions - # might change whether or how much they match of the input. - matches.sort(key=itemgetter(0), reverse=True) - - if not doActions: - # no further conditions or parse actions to change the selection of - # alternative, so the first match will be the best match - best_expr = matches[0][1] - return best_expr._parse(instring, loc, doActions) - - longest = -1, None - for loc1, expr1 in matches: - if loc1 <= longest[0]: - # already have a longer match than this one will deliver, we are done - return longest - - try: - loc2, toks = expr1._parse(instring, loc, doActions) - except ParseException as err: - err.__traceback__ = None - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - else: - if loc2 >= loc1: - return loc2, toks - # didn't match as much as before - elif loc2 > longest[0]: - longest = loc2, toks - - if longest != (-1, None): - return longest - - if maxException is not None: - maxException.msg = self.errmsg - raise maxException - else: - raise ParseException(instring, loc, "no defined alternatives to match", self) - - - def __ixor__(self, other): - if isinstance(other, basestring): - other = self._literalStringClass(other) - return self.append(other) # Or([self, other]) - - def __str__(self): - if hasattr(self, "name"): - return self.name - - if self.strRepr is None: - self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}" - - return self.strRepr - - def checkRecursion(self, parseElementList): - subRecCheckList = parseElementList[:] + [self] - for e in self.exprs: - e.checkRecursion(subRecCheckList) - - def _setResultsName(self, name, listAllMatches=False): - if (not __compat__.collect_all_And_tokens - and __diag__.warn_multiple_tokens_in_named_alternation): - if any(isinstance(e, And) for e in self.exprs): - warnings.warn("{0}: setting results name {1!r} on {2} expression " - "may only return a single token for an And alternative, " - "in future will return the full list of tokens".format( - "warn_multiple_tokens_in_named_alternation", name, type(self).__name__), - stacklevel=3) - - return super(Or, self)._setResultsName(name, listAllMatches) - - -class MatchFirst(ParseExpression): - """Requires that at least one :class:`ParseExpression` is found. If - two expressions match, the first one listed is the one that will - match. May be constructed using the ``'|'`` operator. - - Example:: - - # construct MatchFirst using '|' operator - - # watch the order of expressions to match - number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) - print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] - - # put more selective expression first - number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) - print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] - """ - def __init__(self, exprs, savelist=False): - super(MatchFirst, self).__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - else: - self.mayReturnEmpty = True - - def streamline(self): - super(MatchFirst, self).streamline() - if __compat__.collect_all_And_tokens: - self.saveAsList = any(e.saveAsList for e in self.exprs) - return self - - def parseImpl(self, instring, loc, doActions=True): - maxExcLoc = -1 - maxException = None - for e in self.exprs: - try: - ret = e._parse(instring, loc, doActions) - return ret - except ParseException as err: - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - except IndexError: - if len(instring) > maxExcLoc: - maxException = ParseException(instring, len(instring), e.errmsg, self) - maxExcLoc = len(instring) - - # only got here if no expression matched, raise exception for match that made it the furthest - else: - if maxException is not None: - maxException.msg = self.errmsg - raise maxException - else: - raise ParseException(instring, loc, "no defined alternatives to match", self) - - def __ior__(self, other): - if isinstance(other, basestring): - other = self._literalStringClass(other) - return self.append(other) # MatchFirst([self, other]) - - def __str__(self): - if hasattr(self, "name"): - return self.name - - if self.strRepr is None: - self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}" - - return self.strRepr - - def checkRecursion(self, parseElementList): - subRecCheckList = parseElementList[:] + [self] - for e in self.exprs: - e.checkRecursion(subRecCheckList) - - def _setResultsName(self, name, listAllMatches=False): - if (not __compat__.collect_all_And_tokens - and __diag__.warn_multiple_tokens_in_named_alternation): - if any(isinstance(e, And) for e in self.exprs): - warnings.warn("{0}: setting results name {1!r} on {2} expression " - "may only return a single token for an And alternative, " - "in future will return the full list of tokens".format( - "warn_multiple_tokens_in_named_alternation", name, type(self).__name__), - stacklevel=3) - - return super(MatchFirst, self)._setResultsName(name, listAllMatches) - - -class Each(ParseExpression): - """Requires all given :class:`ParseExpression` s to be found, but in - any order. Expressions may be separated by whitespace. - - May be constructed using the ``'&'`` operator. - - Example:: - - color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") - shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") - integer = Word(nums) - shape_attr = "shape:" + shape_type("shape") - posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") - color_attr = "color:" + color("color") - size_attr = "size:" + integer("size") - - # use Each (using operator '&') to accept attributes in any order - # (shape and posn are required, color and size are optional) - shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) - - shape_spec.runTests(''' - shape: SQUARE color: BLACK posn: 100, 120 - shape: CIRCLE size: 50 color: BLUE posn: 50,80 - color:GREEN size:20 shape:TRIANGLE posn:20,40 - ''' - ) - - prints:: - - shape: SQUARE color: BLACK posn: 100, 120 - ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] - - color: BLACK - - posn: ['100', ',', '120'] - - x: 100 - - y: 120 - - shape: SQUARE - - - shape: CIRCLE size: 50 color: BLUE posn: 50,80 - ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] - - color: BLUE - - posn: ['50', ',', '80'] - - x: 50 - - y: 80 - - shape: CIRCLE - - size: 50 - - - color: GREEN size: 20 shape: TRIANGLE posn: 20,40 - ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] - - color: GREEN - - posn: ['20', ',', '40'] - - x: 20 - - y: 40 - - shape: TRIANGLE - - size: 20 - """ - def __init__(self, exprs, savelist=True): - super(Each, self).__init__(exprs, savelist) - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - self.skipWhitespace = True - self.initExprGroups = True - self.saveAsList = True - - def streamline(self): - super(Each, self).streamline() - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - return self - - def parseImpl(self, instring, loc, doActions=True): - if self.initExprGroups: - self.opt1map = dict((id(e.expr), e) for e in self.exprs if isinstance(e, Optional)) - opt1 = [e.expr for e in self.exprs if isinstance(e, Optional)] - opt2 = [e for e in self.exprs if e.mayReturnEmpty and not isinstance(e, (Optional, Regex))] - self.optionals = opt1 + opt2 - self.multioptionals = [e.expr for e in self.exprs if isinstance(e, ZeroOrMore)] - self.multirequired = [e.expr for e in self.exprs if isinstance(e, OneOrMore)] - self.required = [e for e in self.exprs if not isinstance(e, (Optional, ZeroOrMore, OneOrMore))] - self.required += self.multirequired - self.initExprGroups = False - tmpLoc = loc - tmpReqd = self.required[:] - tmpOpt = self.optionals[:] - matchOrder = [] - - keepMatching = True - while keepMatching: - tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired - failed = [] - for e in tmpExprs: - try: - tmpLoc = e.tryParse(instring, tmpLoc) - except ParseException: - failed.append(e) - else: - matchOrder.append(self.opt1map.get(id(e), e)) - if e in tmpReqd: - tmpReqd.remove(e) - elif e in tmpOpt: - tmpOpt.remove(e) - if len(failed) == len(tmpExprs): - keepMatching = False - - if tmpReqd: - missing = ", ".join(_ustr(e) for e in tmpReqd) - raise ParseException(instring, loc, "Missing one or more required elements (%s)" % missing) - - # add any unmatched Optionals, in case they have default values defined - matchOrder += [e for e in self.exprs if isinstance(e, Optional) and e.expr in tmpOpt] - - resultlist = [] - for e in matchOrder: - loc, results = e._parse(instring, loc, doActions) - resultlist.append(results) - - finalResults = sum(resultlist, ParseResults([])) - return loc, finalResults - - def __str__(self): - if hasattr(self, "name"): - return self.name - - if self.strRepr is None: - self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}" - - return self.strRepr - - def checkRecursion(self, parseElementList): - subRecCheckList = parseElementList[:] + [self] - for e in self.exprs: - e.checkRecursion(subRecCheckList) - - -class ParseElementEnhance(ParserElement): - """Abstract subclass of :class:`ParserElement`, for combining and - post-processing parsed tokens. - """ - def __init__(self, expr, savelist=False): - super(ParseElementEnhance, self).__init__(savelist) - if isinstance(expr, basestring): - if issubclass(self._literalStringClass, Token): - expr = self._literalStringClass(expr) - else: - expr = self._literalStringClass(Literal(expr)) - self.expr = expr - self.strRepr = None - if expr is not None: - self.mayIndexError = expr.mayIndexError - self.mayReturnEmpty = expr.mayReturnEmpty - self.setWhitespaceChars(expr.whiteChars) - self.skipWhitespace = expr.skipWhitespace - self.saveAsList = expr.saveAsList - self.callPreparse = expr.callPreparse - self.ignoreExprs.extend(expr.ignoreExprs) - - def parseImpl(self, instring, loc, doActions=True): - if self.expr is not None: - return self.expr._parse(instring, loc, doActions, callPreParse=False) - else: - raise ParseException("", loc, self.errmsg, self) - - def leaveWhitespace(self): - self.skipWhitespace = False - self.expr = self.expr.copy() - if self.expr is not None: - self.expr.leaveWhitespace() - return self - - def ignore(self, other): - if isinstance(other, Suppress): - if other not in self.ignoreExprs: - super(ParseElementEnhance, self).ignore(other) - if self.expr is not None: - self.expr.ignore(self.ignoreExprs[-1]) - else: - super(ParseElementEnhance, self).ignore(other) - if self.expr is not None: - self.expr.ignore(self.ignoreExprs[-1]) - return self - - def streamline(self): - super(ParseElementEnhance, self).streamline() - if self.expr is not None: - self.expr.streamline() - return self - - def checkRecursion(self, parseElementList): - if self in parseElementList: - raise RecursiveGrammarException(parseElementList + [self]) - subRecCheckList = parseElementList[:] + [self] - if self.expr is not None: - self.expr.checkRecursion(subRecCheckList) - - def validate(self, validateTrace=None): - if validateTrace is None: - validateTrace = [] - tmp = validateTrace[:] + [self] - if self.expr is not None: - self.expr.validate(tmp) - self.checkRecursion([]) - - def __str__(self): - try: - return super(ParseElementEnhance, self).__str__() - except Exception: - pass - - if self.strRepr is None and self.expr is not None: - self.strRepr = "%s:(%s)" % (self.__class__.__name__, _ustr(self.expr)) - return self.strRepr - - -class FollowedBy(ParseElementEnhance): - """Lookahead matching of the given parse expression. - ``FollowedBy`` does *not* advance the parsing position within - the input string, it only verifies that the specified parse - expression matches at the current position. ``FollowedBy`` - always returns a null token list. If any results names are defined - in the lookahead expression, those *will* be returned for access by - name. - - Example:: - - # use FollowedBy to match a label only if it is followed by a ':' - data_word = Word(alphas) - label = data_word + FollowedBy(':') - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) - - OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() - - prints:: - - [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] - """ - def __init__(self, expr): - super(FollowedBy, self).__init__(expr) - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - # by using self._expr.parse and deleting the contents of the returned ParseResults list - # we keep any named results that were defined in the FollowedBy expression - _, ret = self.expr._parse(instring, loc, doActions=doActions) - del ret[:] - - return loc, ret - - -class PrecededBy(ParseElementEnhance): - """Lookbehind matching of the given parse expression. - ``PrecededBy`` does not advance the parsing position within the - input string, it only verifies that the specified parse expression - matches prior to the current position. ``PrecededBy`` always - returns a null token list, but if a results name is defined on the - given expression, it is returned. - - Parameters: - - - expr - expression that must match prior to the current parse - location - - retreat - (default= ``None``) - (int) maximum number of characters - to lookbehind prior to the current parse location - - If the lookbehind expression is a string, Literal, Keyword, or - a Word or CharsNotIn with a specified exact or maximum length, then - the retreat parameter is not required. Otherwise, retreat must be - specified to give a maximum number of characters to look back from - the current parse position for a lookbehind match. - - Example:: - - # VB-style variable names with type prefixes - int_var = PrecededBy("#") + pyparsing_common.identifier - str_var = PrecededBy("$") + pyparsing_common.identifier - - """ - def __init__(self, expr, retreat=None): - super(PrecededBy, self).__init__(expr) - self.expr = self.expr().leaveWhitespace() - self.mayReturnEmpty = True - self.mayIndexError = False - self.exact = False - if isinstance(expr, str): - retreat = len(expr) - self.exact = True - elif isinstance(expr, (Literal, Keyword)): - retreat = expr.matchLen - self.exact = True - elif isinstance(expr, (Word, CharsNotIn)) and expr.maxLen != _MAX_INT: - retreat = expr.maxLen - self.exact = True - elif isinstance(expr, _PositionToken): - retreat = 0 - self.exact = True - self.retreat = retreat - self.errmsg = "not preceded by " + str(expr) - self.skipWhitespace = False - self.parseAction.append(lambda s, l, t: t.__delitem__(slice(None, None))) - - def parseImpl(self, instring, loc=0, doActions=True): - if self.exact: - if loc < self.retreat: - raise ParseException(instring, loc, self.errmsg) - start = loc - self.retreat - _, ret = self.expr._parse(instring, start) - else: - # retreat specified a maximum lookbehind window, iterate - test_expr = self.expr + StringEnd() - instring_slice = instring[max(0, loc - self.retreat):loc] - last_expr = ParseException(instring, loc, self.errmsg) - for offset in range(1, min(loc, self.retreat + 1)+1): - try: - # print('trying', offset, instring_slice, repr(instring_slice[loc - offset:])) - _, ret = test_expr._parse(instring_slice, len(instring_slice) - offset) - except ParseBaseException as pbe: - last_expr = pbe - else: - break - else: - raise last_expr - return loc, ret - - -class NotAny(ParseElementEnhance): - """Lookahead to disallow matching with the given parse expression. - ``NotAny`` does *not* advance the parsing position within the - input string, it only verifies that the specified parse expression - does *not* match at the current position. Also, ``NotAny`` does - *not* skip over leading whitespace. ``NotAny`` always returns - a null token list. May be constructed using the '~' operator. - - Example:: - - AND, OR, NOT = map(CaselessKeyword, "AND OR NOT".split()) - - # take care not to mistake keywords for identifiers - ident = ~(AND | OR | NOT) + Word(alphas) - boolean_term = Optional(NOT) + ident - - # very crude boolean expression - to support parenthesis groups and - # operation hierarchy, use infixNotation - boolean_expr = boolean_term + ZeroOrMore((AND | OR) + boolean_term) - - # integers that are followed by "." are actually floats - integer = Word(nums) + ~Char(".") - """ - def __init__(self, expr): - super(NotAny, self).__init__(expr) - # ~ self.leaveWhitespace() - self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs - self.mayReturnEmpty = True - self.errmsg = "Found unwanted token, " + _ustr(self.expr) - - def parseImpl(self, instring, loc, doActions=True): - if self.expr.canParseNext(instring, loc): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - def __str__(self): - if hasattr(self, "name"): - return self.name - - if self.strRepr is None: - self.strRepr = "~{" + _ustr(self.expr) + "}" - - return self.strRepr - -class _MultipleMatch(ParseElementEnhance): - def __init__(self, expr, stopOn=None): - super(_MultipleMatch, self).__init__(expr) - self.saveAsList = True - ender = stopOn - if isinstance(ender, basestring): - ender = self._literalStringClass(ender) - self.stopOn(ender) - - def stopOn(self, ender): - if isinstance(ender, basestring): - ender = self._literalStringClass(ender) - self.not_ender = ~ender if ender is not None else None - return self - - def parseImpl(self, instring, loc, doActions=True): - self_expr_parse = self.expr._parse - self_skip_ignorables = self._skipIgnorables - check_ender = self.not_ender is not None - if check_ender: - try_not_ender = self.not_ender.tryParse - - # must be at least one (but first see if we are the stopOn sentinel; - # if so, fail) - if check_ender: - try_not_ender(instring, loc) - loc, tokens = self_expr_parse(instring, loc, doActions, callPreParse=False) - try: - hasIgnoreExprs = (not not self.ignoreExprs) - while 1: - if check_ender: - try_not_ender(instring, loc) - if hasIgnoreExprs: - preloc = self_skip_ignorables(instring, loc) - else: - preloc = loc - loc, tmptokens = self_expr_parse(instring, preloc, doActions) - if tmptokens or tmptokens.haskeys(): - tokens += tmptokens - except (ParseException, IndexError): - pass - - return loc, tokens - - def _setResultsName(self, name, listAllMatches=False): - if __diag__.warn_ungrouped_named_tokens_in_collection: - for e in [self.expr] + getattr(self.expr, 'exprs', []): - if isinstance(e, ParserElement) and e.resultsName: - warnings.warn("{0}: setting results name {1!r} on {2} expression " - "collides with {3!r} on contained expression".format("warn_ungrouped_named_tokens_in_collection", - name, - type(self).__name__, - e.resultsName), - stacklevel=3) - - return super(_MultipleMatch, self)._setResultsName(name, listAllMatches) - - -class OneOrMore(_MultipleMatch): - """Repetition of one or more of the given expression. - - Parameters: - - expr - expression that must match one or more times - - stopOn - (default= ``None``) - expression for a terminating sentinel - (only required if the sentinel would ordinarily match the repetition - expression) - - Example:: - - data_word = Word(alphas) - label = data_word + FollowedBy(':') - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) - - text = "shape: SQUARE posn: upper left color: BLACK" - OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] - - # use stopOn attribute for OneOrMore to avoid reading label string as part of the data - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) - OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] - - # could also be written as - (attr_expr * (1,)).parseString(text).pprint() - """ - - def __str__(self): - if hasattr(self, "name"): - return self.name - - if self.strRepr is None: - self.strRepr = "{" + _ustr(self.expr) + "}..." - - return self.strRepr - -class ZeroOrMore(_MultipleMatch): - """Optional repetition of zero or more of the given expression. - - Parameters: - - expr - expression that must match zero or more times - - stopOn - (default= ``None``) - expression for a terminating sentinel - (only required if the sentinel would ordinarily match the repetition - expression) - - Example: similar to :class:`OneOrMore` - """ - def __init__(self, expr, stopOn=None): - super(ZeroOrMore, self).__init__(expr, stopOn=stopOn) - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - try: - return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) - except (ParseException, IndexError): - return loc, [] - - def __str__(self): - if hasattr(self, "name"): - return self.name - - if self.strRepr is None: - self.strRepr = "[" + _ustr(self.expr) + "]..." - - return self.strRepr - - -class _NullToken(object): - def __bool__(self): - return False - __nonzero__ = __bool__ - def __str__(self): - return "" - -class Optional(ParseElementEnhance): - """Optional matching of the given expression. - - Parameters: - - expr - expression that must match zero or more times - - default (optional) - value to be returned if the optional expression is not found. - - Example:: - - # US postal code can be a 5-digit zip, plus optional 4-digit qualifier - zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) - zip.runTests(''' - # traditional ZIP code - 12345 - - # ZIP+4 form - 12101-0001 - - # invalid ZIP - 98765- - ''') - - prints:: - - # traditional ZIP code - 12345 - ['12345'] - - # ZIP+4 form - 12101-0001 - ['12101-0001'] - - # invalid ZIP - 98765- - ^ - FAIL: Expected end of text (at char 5), (line:1, col:6) - """ - __optionalNotMatched = _NullToken() - - def __init__(self, expr, default=__optionalNotMatched): - super(Optional, self).__init__(expr, savelist=False) - self.saveAsList = self.expr.saveAsList - self.defaultValue = default - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - try: - loc, tokens = self.expr._parse(instring, loc, doActions, callPreParse=False) - except (ParseException, IndexError): - if self.defaultValue is not self.__optionalNotMatched: - if self.expr.resultsName: - tokens = ParseResults([self.defaultValue]) - tokens[self.expr.resultsName] = self.defaultValue - else: - tokens = [self.defaultValue] - else: - tokens = [] - return loc, tokens - - def __str__(self): - if hasattr(self, "name"): - return self.name - - if self.strRepr is None: - self.strRepr = "[" + _ustr(self.expr) + "]" - - return self.strRepr - -class SkipTo(ParseElementEnhance): - """Token for skipping over all undefined text until the matched - expression is found. - - Parameters: - - expr - target expression marking the end of the data to be skipped - - include - (default= ``False``) if True, the target expression is also parsed - (the skipped text and target expression are returned as a 2-element list). - - ignore - (default= ``None``) used to define grammars (typically quoted strings and - comments) that might contain false matches to the target expression - - failOn - (default= ``None``) define expressions that are not allowed to be - included in the skipped test; if found before the target expression is found, - the SkipTo is not a match - - Example:: - - report = ''' - Outstanding Issues Report - 1 Jan 2000 - - # | Severity | Description | Days Open - -----+----------+-------------------------------------------+----------- - 101 | Critical | Intermittent system crash | 6 - 94 | Cosmetic | Spelling error on Login ('log|n') | 14 - 79 | Minor | System slow when running too many reports | 47 - ''' - integer = Word(nums) - SEP = Suppress('|') - # use SkipTo to simply match everything up until the next SEP - # - ignore quoted strings, so that a '|' character inside a quoted string does not match - # - parse action will call token.strip() for each matched token, i.e., the description body - string_data = SkipTo(SEP, ignore=quotedString) - string_data.setParseAction(tokenMap(str.strip)) - ticket_expr = (integer("issue_num") + SEP - + string_data("sev") + SEP - + string_data("desc") + SEP - + integer("days_open")) - - for tkt in ticket_expr.searchString(report): - print tkt.dump() - - prints:: - - ['101', 'Critical', 'Intermittent system crash', '6'] - - days_open: 6 - - desc: Intermittent system crash - - issue_num: 101 - - sev: Critical - ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] - - days_open: 14 - - desc: Spelling error on Login ('log|n') - - issue_num: 94 - - sev: Cosmetic - ['79', 'Minor', 'System slow when running too many reports', '47'] - - days_open: 47 - - desc: System slow when running too many reports - - issue_num: 79 - - sev: Minor - """ - def __init__(self, other, include=False, ignore=None, failOn=None): - super(SkipTo, self).__init__(other) - self.ignoreExpr = ignore - self.mayReturnEmpty = True - self.mayIndexError = False - self.includeMatch = include - self.saveAsList = False - if isinstance(failOn, basestring): - self.failOn = self._literalStringClass(failOn) - else: - self.failOn = failOn - self.errmsg = "No match found for " + _ustr(self.expr) - - def parseImpl(self, instring, loc, doActions=True): - startloc = loc - instrlen = len(instring) - expr = self.expr - expr_parse = self.expr._parse - self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None - self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None - - tmploc = loc - while tmploc <= instrlen: - if self_failOn_canParseNext is not None: - # break if failOn expression matches - if self_failOn_canParseNext(instring, tmploc): - break - - if self_ignoreExpr_tryParse is not None: - # advance past ignore expressions - while 1: - try: - tmploc = self_ignoreExpr_tryParse(instring, tmploc) - except ParseBaseException: - break - - try: - expr_parse(instring, tmploc, doActions=False, callPreParse=False) - except (ParseException, IndexError): - # no match, advance loc in string - tmploc += 1 - else: - # matched skipto expr, done - break - - else: - # ran off the end of the input string without matching skipto expr, fail - raise ParseException(instring, loc, self.errmsg, self) - - # build up return values - loc = tmploc - skiptext = instring[startloc:loc] - skipresult = ParseResults(skiptext) - - if self.includeMatch: - loc, mat = expr_parse(instring, loc, doActions, callPreParse=False) - skipresult += mat - - return loc, skipresult - -class Forward(ParseElementEnhance): - """Forward declaration of an expression to be defined later - - used for recursive grammars, such as algebraic infix notation. - When the expression is known, it is assigned to the ``Forward`` - variable using the '<<' operator. - - Note: take care when assigning to ``Forward`` not to overlook - precedence of operators. - - Specifically, '|' has a lower precedence than '<<', so that:: - - fwdExpr << a | b | c - - will actually be evaluated as:: - - (fwdExpr << a) | b | c - - thereby leaving b and c out as parseable alternatives. It is recommended that you - explicitly group the values inserted into the ``Forward``:: - - fwdExpr << (a | b | c) - - Converting to use the '<<=' operator instead will avoid this problem. - - See :class:`ParseResults.pprint` for an example of a recursive - parser created using ``Forward``. - """ - def __init__(self, other=None): - super(Forward, self).__init__(other, savelist=False) - - def __lshift__(self, other): - if isinstance(other, basestring): - other = self._literalStringClass(other) - self.expr = other - self.strRepr = None - self.mayIndexError = self.expr.mayIndexError - self.mayReturnEmpty = self.expr.mayReturnEmpty - self.setWhitespaceChars(self.expr.whiteChars) - self.skipWhitespace = self.expr.skipWhitespace - self.saveAsList = self.expr.saveAsList - self.ignoreExprs.extend(self.expr.ignoreExprs) - return self - - def __ilshift__(self, other): - return self << other - - def leaveWhitespace(self): - self.skipWhitespace = False - return self - - def streamline(self): - if not self.streamlined: - self.streamlined = True - if self.expr is not None: - self.expr.streamline() - return self - - def validate(self, validateTrace=None): - if validateTrace is None: - validateTrace = [] - - if self not in validateTrace: - tmp = validateTrace[:] + [self] - if self.expr is not None: - self.expr.validate(tmp) - self.checkRecursion([]) - - def __str__(self): - if hasattr(self, "name"): - return self.name - if self.strRepr is not None: - return self.strRepr - - # Avoid infinite recursion by setting a temporary strRepr - self.strRepr = ": ..." - - # Use the string representation of main expression. - retString = '...' - try: - if self.expr is not None: - retString = _ustr(self.expr)[:1000] - else: - retString = "None" - finally: - self.strRepr = self.__class__.__name__ + ": " + retString - return self.strRepr - - def copy(self): - if self.expr is not None: - return super(Forward, self).copy() - else: - ret = Forward() - ret <<= self - return ret - - def _setResultsName(self, name, listAllMatches=False): - if __diag__.warn_name_set_on_empty_Forward: - if self.expr is None: - warnings.warn("{0}: setting results name {0!r} on {1} expression " - "that has no contained expression".format("warn_name_set_on_empty_Forward", - name, - type(self).__name__), - stacklevel=3) - - return super(Forward, self)._setResultsName(name, listAllMatches) - -class TokenConverter(ParseElementEnhance): - """ - Abstract subclass of :class:`ParseExpression`, for converting parsed results. - """ - def __init__(self, expr, savelist=False): - super(TokenConverter, self).__init__(expr) # , savelist) - self.saveAsList = False - -class Combine(TokenConverter): - """Converter to concatenate all matching tokens to a single string. - By default, the matching patterns must also be contiguous in the - input string; this can be disabled by specifying - ``'adjacent=False'`` in the constructor. - - Example:: - - real = Word(nums) + '.' + Word(nums) - print(real.parseString('3.1416')) # -> ['3', '.', '1416'] - # will also erroneously match the following - print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] - - real = Combine(Word(nums) + '.' + Word(nums)) - print(real.parseString('3.1416')) # -> ['3.1416'] - # no match when there are internal spaces - print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) - """ - def __init__(self, expr, joinString="", adjacent=True): - super(Combine, self).__init__(expr) - # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself - if adjacent: - self.leaveWhitespace() - self.adjacent = adjacent - self.skipWhitespace = True - self.joinString = joinString - self.callPreparse = True - - def ignore(self, other): - if self.adjacent: - ParserElement.ignore(self, other) - else: - super(Combine, self).ignore(other) - return self - - def postParse(self, instring, loc, tokenlist): - retToks = tokenlist.copy() - del retToks[:] - retToks += ParseResults(["".join(tokenlist._asStringList(self.joinString))], modal=self.modalResults) - - if self.resultsName and retToks.haskeys(): - return [retToks] - else: - return retToks - -class Group(TokenConverter): - """Converter to return the matched tokens as a list - useful for - returning tokens of :class:`ZeroOrMore` and :class:`OneOrMore` expressions. - - Example:: - - ident = Word(alphas) - num = Word(nums) - term = ident | num - func = ident + Optional(delimitedList(term)) - print(func.parseString("fn a, b, 100")) # -> ['fn', 'a', 'b', '100'] - - func = ident + Group(Optional(delimitedList(term))) - print(func.parseString("fn a, b, 100")) # -> ['fn', ['a', 'b', '100']] - """ - def __init__(self, expr): - super(Group, self).__init__(expr) - self.saveAsList = True - - def postParse(self, instring, loc, tokenlist): - return [tokenlist] - -class Dict(TokenConverter): - """Converter to return a repetitive expression as a list, but also - as a dictionary. Each element can also be referenced using the first - token in the expression as its key. Useful for tabular report - scraping when the first column can be used as a item key. - - Example:: - - data_word = Word(alphas) - label = data_word + FollowedBy(':') - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) - - text = "shape: SQUARE posn: upper left color: light blue texture: burlap" - attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) - - # print attributes as plain groups - print(OneOrMore(attr_expr).parseString(text).dump()) - - # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names - result = Dict(OneOrMore(Group(attr_expr))).parseString(text) - print(result.dump()) - - # access named fields as dict entries, or output as dict - print(result['shape']) - print(result.asDict()) - - prints:: - - ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] - [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - - color: light blue - - posn: upper left - - shape: SQUARE - - texture: burlap - SQUARE - {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} - - See more examples at :class:`ParseResults` of accessing fields by results name. - """ - def __init__(self, expr): - super(Dict, self).__init__(expr) - self.saveAsList = True - - def postParse(self, instring, loc, tokenlist): - for i, tok in enumerate(tokenlist): - if len(tok) == 0: - continue - ikey = tok[0] - if isinstance(ikey, int): - ikey = _ustr(tok[0]).strip() - if len(tok) == 1: - tokenlist[ikey] = _ParseResultsWithOffset("", i) - elif len(tok) == 2 and not isinstance(tok[1], ParseResults): - tokenlist[ikey] = _ParseResultsWithOffset(tok[1], i) - else: - dictvalue = tok.copy() # ParseResults(i) - del dictvalue[0] - if len(dictvalue) != 1 or (isinstance(dictvalue, ParseResults) and dictvalue.haskeys()): - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue, i) - else: - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0], i) - - if self.resultsName: - return [tokenlist] - else: - return tokenlist - - -class Suppress(TokenConverter): - """Converter for ignoring the results of a parsed expression. - - Example:: - - source = "a, b, c,d" - wd = Word(alphas) - wd_list1 = wd + ZeroOrMore(',' + wd) - print(wd_list1.parseString(source)) - - # often, delimiters that are useful during parsing are just in the - # way afterward - use Suppress to keep them out of the parsed output - wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) - print(wd_list2.parseString(source)) - - prints:: - - ['a', ',', 'b', ',', 'c', ',', 'd'] - ['a', 'b', 'c', 'd'] - - (See also :class:`delimitedList`.) - """ - def postParse(self, instring, loc, tokenlist): - return [] - - def suppress(self): - return self - - -class OnlyOnce(object): - """Wrapper for parse actions, to ensure they are only called once. - """ - def __init__(self, methodCall): - self.callable = _trim_arity(methodCall) - self.called = False - def __call__(self, s, l, t): - if not self.called: - results = self.callable(s, l, t) - self.called = True - return results - raise ParseException(s, l, "") - def reset(self): - self.called = False - -def traceParseAction(f): - """Decorator for debugging parse actions. - - When the parse action is called, this decorator will print - ``">> entering method-name(line:, , )"``. - When the parse action completes, the decorator will print - ``"<<"`` followed by the returned value, or any exception that the parse action raised. - - Example:: - - wd = Word(alphas) - - @traceParseAction - def remove_duplicate_chars(tokens): - return ''.join(sorted(set(''.join(tokens)))) - - wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) - print(wds.parseString("slkdjs sld sldd sdlf sdljf")) - - prints:: - - >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) - < 3: - thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc - sys.stderr.write(">>entering %s(line: '%s', %d, %r)\n" % (thisFunc, line(l, s), l, t)) - try: - ret = f(*paArgs) - except Exception as exc: - sys.stderr.write("< ['aa', 'bb', 'cc'] - delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] - """ - dlName = _ustr(expr) + " [" + _ustr(delim) + " " + _ustr(expr) + "]..." - if combine: - return Combine(expr + ZeroOrMore(delim + expr)).setName(dlName) - else: - return (expr + ZeroOrMore(Suppress(delim) + expr)).setName(dlName) - -def countedArray(expr, intExpr=None): - """Helper to define a counted list of expressions. - - This helper defines a pattern of the form:: - - integer expr expr expr... - - where the leading integer tells how many expr expressions follow. - The matched tokens returns the array of expr tokens as a list - the - leading count token is suppressed. - - If ``intExpr`` is specified, it should be a pyparsing expression - that produces an integer value. - - Example:: - - countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] - - # in this parser, the leading integer value is given in binary, - # '10' indicating that 2 values are in the array - binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) - countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] - """ - arrayExpr = Forward() - def countFieldParseAction(s, l, t): - n = t[0] - arrayExpr << (n and Group(And([expr] * n)) or Group(empty)) - return [] - if intExpr is None: - intExpr = Word(nums).setParseAction(lambda t: int(t[0])) - else: - intExpr = intExpr.copy() - intExpr.setName("arrayLen") - intExpr.addParseAction(countFieldParseAction, callDuringTry=True) - return (intExpr + arrayExpr).setName('(len) ' + _ustr(expr) + '...') - -def _flatten(L): - ret = [] - for i in L: - if isinstance(i, list): - ret.extend(_flatten(i)) - else: - ret.append(i) - return ret - -def matchPreviousLiteral(expr): - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = matchPreviousLiteral(first) - matchExpr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches a previous literal, will also match the leading - ``"1:1"`` in ``"1:10"``. If this is not desired, use - :class:`matchPreviousExpr`. Do *not* use with packrat parsing - enabled. - """ - rep = Forward() - def copyTokenToRepeater(s, l, t): - if t: - if len(t) == 1: - rep << t[0] - else: - # flatten t tokens - tflat = _flatten(t.asList()) - rep << And(Literal(tt) for tt in tflat) - else: - rep << Empty() - expr.addParseAction(copyTokenToRepeater, callDuringTry=True) - rep.setName('(prev) ' + _ustr(expr)) - return rep - -def matchPreviousExpr(expr): - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = matchPreviousExpr(first) - matchExpr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches by expressions, will *not* match the leading ``"1:1"`` - in ``"1:10"``; the expressions are evaluated first, and then - compared, so ``"1"`` is compared with ``"10"``. Do *not* use - with packrat parsing enabled. - """ - rep = Forward() - e2 = expr.copy() - rep <<= e2 - def copyTokenToRepeater(s, l, t): - matchTokens = _flatten(t.asList()) - def mustMatchTheseTokens(s, l, t): - theseTokens = _flatten(t.asList()) - if theseTokens != matchTokens: - raise ParseException('', 0, '') - rep.setParseAction(mustMatchTheseTokens, callDuringTry=True) - expr.addParseAction(copyTokenToRepeater, callDuringTry=True) - rep.setName('(prev) ' + _ustr(expr)) - return rep - -def _escapeRegexRangeChars(s): - # ~ escape these chars: ^-[] - for c in r"\^-[]": - s = s.replace(c, _bslash + c) - s = s.replace("\n", r"\n") - s = s.replace("\t", r"\t") - return _ustr(s) - -def oneOf(strs, caseless=False, useRegex=True, asKeyword=False): - """Helper to quickly define a set of alternative Literals, and makes - sure to do longest-first testing when there is a conflict, - regardless of the input order, but returns - a :class:`MatchFirst` for best performance. - - Parameters: - - - strs - a string of space-delimited literals, or a collection of - string literals - - caseless - (default= ``False``) - treat all literals as - caseless - - useRegex - (default= ``True``) - as an optimization, will - generate a Regex object; otherwise, will generate - a :class:`MatchFirst` object (if ``caseless=True`` or ``asKeyword=True``, or if - creating a :class:`Regex` raises an exception) - - asKeyword - (default=``False``) - enforce Keyword-style matching on the - generated expressions - - Example:: - - comp_oper = oneOf("< = > <= >= !=") - var = Word(alphas) - number = Word(nums) - term = var | number - comparison_expr = term + comp_oper + term - print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) - - prints:: - - [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] - """ - if isinstance(caseless, basestring): - warnings.warn("More than one string argument passed to oneOf, pass " - "choices as a list or space-delimited string", stacklevel=2) - - if caseless: - isequal = (lambda a, b: a.upper() == b.upper()) - masks = (lambda a, b: b.upper().startswith(a.upper())) - parseElementClass = CaselessKeyword if asKeyword else CaselessLiteral - else: - isequal = (lambda a, b: a == b) - masks = (lambda a, b: b.startswith(a)) - parseElementClass = Keyword if asKeyword else Literal - - symbols = [] - if isinstance(strs, basestring): - symbols = strs.split() - elif isinstance(strs, Iterable): - symbols = list(strs) - else: - warnings.warn("Invalid argument to oneOf, expected string or iterable", - SyntaxWarning, stacklevel=2) - if not symbols: - return NoMatch() - - if not asKeyword: - # if not producing keywords, need to reorder to take care to avoid masking - # longer choices with shorter ones - i = 0 - while i < len(symbols) - 1: - cur = symbols[i] - for j, other in enumerate(symbols[i + 1:]): - if isequal(other, cur): - del symbols[i + j + 1] - break - elif masks(cur, other): - del symbols[i + j + 1] - symbols.insert(i, other) - break - else: - i += 1 - - if not (caseless or asKeyword) and useRegex: - # ~ print (strs, "->", "|".join([_escapeRegexChars(sym) for sym in symbols])) - try: - if len(symbols) == len("".join(symbols)): - return Regex("[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols)).setName(' | '.join(symbols)) - else: - return Regex("|".join(re.escape(sym) for sym in symbols)).setName(' | '.join(symbols)) - except Exception: - warnings.warn("Exception creating Regex for oneOf, building MatchFirst", - SyntaxWarning, stacklevel=2) - - # last resort, just use MatchFirst - return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols)) - -def dictOf(key, value): - """Helper to easily and clearly define a dictionary by specifying - the respective patterns for the key and value. Takes care of - defining the :class:`Dict`, :class:`ZeroOrMore`, and - :class:`Group` tokens in the proper order. The key pattern - can include delimiting markers or punctuation, as long as they are - suppressed, thereby leaving the significant key text. The value - pattern can include named results, so that the :class:`Dict` results - can include named token fields. - - Example:: - - text = "shape: SQUARE posn: upper left color: light blue texture: burlap" - attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) - print(OneOrMore(attr_expr).parseString(text).dump()) - - attr_label = label - attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) - - # similar to Dict, but simpler call format - result = dictOf(attr_label, attr_value).parseString(text) - print(result.dump()) - print(result['shape']) - print(result.shape) # object attribute access works too - print(result.asDict()) - - prints:: - - [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - - color: light blue - - posn: upper left - - shape: SQUARE - - texture: burlap - SQUARE - SQUARE - {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} - """ - return Dict(OneOrMore(Group(key + value))) - -def originalTextFor(expr, asString=True): - """Helper to return the original, untokenized text for a given - expression. Useful to restore the parsed fields of an HTML start - tag into the raw tag text itself, or to revert separate tokens with - intervening whitespace back to the original matching input text. By - default, returns astring containing the original parsed text. - - If the optional ``asString`` argument is passed as - ``False``, then the return value is - a :class:`ParseResults` containing any results names that - were originally matched, and a single token containing the original - matched text from the input string. So if the expression passed to - :class:`originalTextFor` contains expressions with defined - results names, you must set ``asString`` to ``False`` if you - want to preserve those results name values. - - Example:: - - src = "this is test bold text normal text " - for tag in ("b", "i"): - opener, closer = makeHTMLTags(tag) - patt = originalTextFor(opener + SkipTo(closer) + closer) - print(patt.searchString(src)[0]) - - prints:: - - [' bold text '] - ['text'] - """ - locMarker = Empty().setParseAction(lambda s, loc, t: loc) - endlocMarker = locMarker.copy() - endlocMarker.callPreparse = False - matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") - if asString: - extractText = lambda s, l, t: s[t._original_start: t._original_end] - else: - def extractText(s, l, t): - t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] - matchExpr.setParseAction(extractText) - matchExpr.ignoreExprs = expr.ignoreExprs - return matchExpr - -def ungroup(expr): - """Helper to undo pyparsing's default grouping of And expressions, - even if all but one are non-empty. - """ - return TokenConverter(expr).addParseAction(lambda t: t[0]) - -def locatedExpr(expr): - """Helper to decorate a returned token with its starting and ending - locations in the input string. - - This helper adds the following results names: - - - locn_start = location where matched expression begins - - locn_end = location where matched expression ends - - value = the actual parsed results - - Be careful if the input text contains ```` characters, you - may want to call :class:`ParserElement.parseWithTabs` - - Example:: - - wd = Word(alphas) - for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): - print(match) - - prints:: - - [[0, 'ljsdf', 5]] - [[8, 'lksdjjf', 15]] - [[18, 'lkkjj', 23]] - """ - locator = Empty().setParseAction(lambda s, l, t: l) - return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end")) - - -# convenience constants for positional expressions -empty = Empty().setName("empty") -lineStart = LineStart().setName("lineStart") -lineEnd = LineEnd().setName("lineEnd") -stringStart = StringStart().setName("stringStart") -stringEnd = StringEnd().setName("stringEnd") - -_escapedPunc = Word(_bslash, r"\[]-*.$+^?()~ ", exact=2).setParseAction(lambda s, l, t: t[0][1]) -_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s, l, t: unichr(int(t[0].lstrip(r'\0x'), 16))) -_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s, l, t: unichr(int(t[0][1:], 8))) -_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1) -_charRange = Group(_singleChar + Suppress("-") + _singleChar) -_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group(OneOrMore(_charRange | _singleChar)).setResultsName("body") + "]" - -def srange(s): - r"""Helper to easily define string ranges for use in Word - construction. Borrows syntax from regexp '[]' string range - definitions:: - - srange("[0-9]") -> "0123456789" - srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" - srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" - - The input string must be enclosed in []'s, and the returned string - is the expanded character set joined into a single string. The - values enclosed in the []'s may be: - - - a single character - - an escaped character with a leading backslash (such as ``\-`` - or ``\]``) - - an escaped hex character with a leading ``'\x'`` - (``\x21``, which is a ``'!'`` character) (``\0x##`` - is also supported for backwards compatibility) - - an escaped octal character with a leading ``'\0'`` - (``\041``, which is a ``'!'`` character) - - a range of any of the above, separated by a dash (``'a-z'``, - etc.) - - any combination of the above (``'aeiouy'``, - ``'a-zA-Z0-9_$'``, etc.) - """ - _expanded = lambda p: p if not isinstance(p, ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]), ord(p[1]) + 1)) - try: - return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body) - except Exception: - return "" - -def matchOnlyAtCol(n): - """Helper method for defining parse actions that require matching at - a specific column in the input text. - """ - def verifyCol(strg, locn, toks): - if col(locn, strg) != n: - raise ParseException(strg, locn, "matched token not at column %d" % n) - return verifyCol - -def replaceWith(replStr): - """Helper method for common parse actions that simply return - a literal value. Especially useful when used with - :class:`transformString` (). - - Example:: - - num = Word(nums).setParseAction(lambda toks: int(toks[0])) - na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) - term = na | num - - OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] - """ - return lambda s, l, t: [replStr] - -def removeQuotes(s, l, t): - """Helper parse action for removing quotation marks from parsed - quoted strings. - - Example:: - - # by default, quotation marks are included in parsed results - quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] - - # use removeQuotes to strip quotation marks from parsed results - quotedString.setParseAction(removeQuotes) - quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] - """ - return t[0][1:-1] - -def tokenMap(func, *args): - """Helper to define a parse action by mapping a function to all - elements of a ParseResults list. If any additional args are passed, - they are forwarded to the given function as additional arguments - after the token, as in - ``hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))``, - which will convert the parsed data to an integer using base 16. - - Example (compare the last to example in :class:`ParserElement.transformString`:: - - hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) - hex_ints.runTests(''' - 00 11 22 aa FF 0a 0d 1a - ''') - - upperword = Word(alphas).setParseAction(tokenMap(str.upper)) - OneOrMore(upperword).runTests(''' - my kingdom for a horse - ''') - - wd = Word(alphas).setParseAction(tokenMap(str.title)) - OneOrMore(wd).setParseAction(' '.join).runTests(''' - now is the winter of our discontent made glorious summer by this sun of york - ''') - - prints:: - - 00 11 22 aa FF 0a 0d 1a - [0, 17, 34, 170, 255, 10, 13, 26] - - my kingdom for a horse - ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] - - now is the winter of our discontent made glorious summer by this sun of york - ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] - """ - def pa(s, l, t): - return [func(tokn, *args) for tokn in t] - - try: - func_name = getattr(func, '__name__', - getattr(func, '__class__').__name__) - except Exception: - func_name = str(func) - pa.__name__ = func_name - - return pa - -upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) -"""(Deprecated) Helper parse action to convert tokens to upper case. -Deprecated in favor of :class:`pyparsing_common.upcaseTokens`""" - -downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) -"""(Deprecated) Helper parse action to convert tokens to lower case. -Deprecated in favor of :class:`pyparsing_common.downcaseTokens`""" - -def _makeTags(tagStr, xml, - suppress_LT=Suppress("<"), - suppress_GT=Suppress(">")): - """Internal helper to construct opening and closing tag expressions, given a tag name""" - if isinstance(tagStr, basestring): - resname = tagStr - tagStr = Keyword(tagStr, caseless=not xml) - else: - resname = tagStr.name - - tagAttrName = Word(alphas, alphanums + "_-:") - if xml: - tagAttrValue = dblQuotedString.copy().setParseAction(removeQuotes) - openTag = (suppress_LT - + tagStr("tag") - + Dict(ZeroOrMore(Group(tagAttrName + Suppress("=") + tagAttrValue))) - + Optional("/", default=[False])("empty").setParseAction(lambda s, l, t: t[0] == '/') - + suppress_GT) - else: - tagAttrValue = quotedString.copy().setParseAction(removeQuotes) | Word(printables, excludeChars=">") - openTag = (suppress_LT - + tagStr("tag") - + Dict(ZeroOrMore(Group(tagAttrName.setParseAction(downcaseTokens) - + Optional(Suppress("=") + tagAttrValue)))) - + Optional("/", default=[False])("empty").setParseAction(lambda s, l, t: t[0] == '/') - + suppress_GT) - closeTag = Combine(_L("", adjacent=False) - - openTag.setName("<%s>" % resname) - # add start results name in parse action now that ungrouped names are not reported at two levels - openTag.addParseAction(lambda t: t.__setitem__("start" + "".join(resname.replace(":", " ").title().split()), t.copy())) - closeTag = closeTag("end" + "".join(resname.replace(":", " ").title().split())).setName("" % resname) - openTag.tag = resname - closeTag.tag = resname - openTag.tag_body = SkipTo(closeTag()) - return openTag, closeTag - -def makeHTMLTags(tagStr): - """Helper to construct opening and closing tag expressions for HTML, - given a tag name. Matches tags in either upper or lower case, - attributes with namespaces and with quoted or unquoted values. - - Example:: - - text = 'More info at the pyparsing wiki page' - # makeHTMLTags returns pyparsing expressions for the opening and - # closing tags as a 2-tuple - a, a_end = makeHTMLTags("A") - link_expr = a + SkipTo(a_end)("link_text") + a_end - - for link in link_expr.searchString(text): - # attributes in the tag (like "href" shown here) are - # also accessible as named results - print(link.link_text, '->', link.href) - - prints:: - - pyparsing -> https://github.com/pyparsing/pyparsing/wiki - """ - return _makeTags(tagStr, False) - -def makeXMLTags(tagStr): - """Helper to construct opening and closing tag expressions for XML, - given a tag name. Matches tags only in the given upper/lower case. - - Example: similar to :class:`makeHTMLTags` - """ - return _makeTags(tagStr, True) - -def withAttribute(*args, **attrDict): - """Helper to create a validating parse action to be used with start - tags created with :class:`makeXMLTags` or - :class:`makeHTMLTags`. Use ``withAttribute`` to qualify - a starting tag with a required attribute value, to avoid false - matches on common tags such as ```` or ``
``. - - Call ``withAttribute`` with a series of attribute names and - values. Specify the list of filter attributes names and values as: - - - keyword arguments, as in ``(align="right")``, or - - as an explicit dict with ``**`` operator, when an attribute - name is also a Python reserved word, as in ``**{"class":"Customer", "align":"right"}`` - - a list of name-value tuples, as in ``(("ns1:class", "Customer"), ("ns2:align", "right"))`` - - For attribute names with a namespace prefix, you must use the second - form. Attribute names are matched insensitive to upper/lower case. - - If just testing for ``class`` (with or without a namespace), use - :class:`withClass`. - - To verify that the attribute exists, but without specifying a value, - pass ``withAttribute.ANY_VALUE`` as the value. - - Example:: - - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this has no type
-
- - ''' - div,div_end = makeHTMLTags("div") - - # only match div tag having a type attribute with value "grid" - div_grid = div().setParseAction(withAttribute(type="grid")) - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - # construct a match with any div tag having a type attribute, regardless of the value - div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - - prints:: - - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - if args: - attrs = args[:] - else: - attrs = attrDict.items() - attrs = [(k, v) for k, v in attrs] - def pa(s, l, tokens): - for attrName, attrValue in attrs: - if attrName not in tokens: - raise ParseException(s, l, "no matching attribute " + attrName) - if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: - raise ParseException(s, l, "attribute '%s' has value '%s', must be '%s'" % - (attrName, tokens[attrName], attrValue)) - return pa -withAttribute.ANY_VALUE = object() - -def withClass(classname, namespace=''): - """Simplified version of :class:`withAttribute` when - matching on a div class - made difficult because ``class`` is - a reserved word in Python. - - Example:: - - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this <div> has no class
-
- - ''' - div,div_end = makeHTMLTags("div") - div_grid = div().setParseAction(withClass("grid")) - - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - - prints:: - - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - classattr = "%s:class" % namespace if namespace else "class" - return withAttribute(**{classattr: classname}) - -opAssoc = SimpleNamespace() -opAssoc.LEFT = object() -opAssoc.RIGHT = object() - -def infixNotation(baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')')): - """Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary - or binary, left- or right-associative. Parse actions can also be - attached to operator expressions. The generated parser will also - recognize the use of parentheses to override operator precedences - (see example below). - - Note: if you define a deep operator list, you may see performance - issues when using infixNotation. See - :class:`ParserElement.enablePackrat` for a mechanism to potentially - improve your parser performance. - - Parameters: - - baseExpr - expression representing the most basic element for the - nested - - opList - list of tuples, one for each operator precedence level - in the expression grammar; each tuple is of the form ``(opExpr, - numTerms, rightLeftAssoc, parseAction)``, where: - - - opExpr is the pyparsing expression for the operator; may also - be a string, which will be converted to a Literal; if numTerms - is 3, opExpr is a tuple of two expressions, for the two - operators separating the 3 terms - - numTerms is the number of terms for this operator (must be 1, - 2, or 3) - - rightLeftAssoc is the indicator whether the operator is right - or left associative, using the pyparsing-defined constants - ``opAssoc.RIGHT`` and ``opAssoc.LEFT``. - - parseAction is the parse action to be associated with - expressions matching this operator expression (the parse action - tuple member may be omitted); if the parse action is passed - a tuple or list of functions, this is equivalent to calling - ``setParseAction(*fn)`` - (:class:`ParserElement.setParseAction`) - - lpar - expression for matching left-parentheses - (default= ``Suppress('(')``) - - rpar - expression for matching right-parentheses - (default= ``Suppress(')')``) - - Example:: - - # simple example of four-function arithmetic with ints and - # variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infixNotation(integer | varname, - [ - ('-', 1, opAssoc.RIGHT), - (oneOf('* /'), 2, opAssoc.LEFT), - (oneOf('+ -'), 2, opAssoc.LEFT), - ]) - - arith_expr.runTests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', fullDump=False) - - prints:: - - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - # captive version of FollowedBy that does not do parse actions or capture results names - class _FB(FollowedBy): - def parseImpl(self, instring, loc, doActions=True): - self.expr.tryParse(instring, loc) - return loc, [] - - ret = Forward() - lastExpr = baseExpr | (lpar + ret + rpar) - for i, operDef in enumerate(opList): - opExpr, arity, rightLeftAssoc, pa = (operDef + (None, ))[:4] - termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr - if arity == 3: - if opExpr is None or len(opExpr) != 2: - raise ValueError( - "if numterms=3, opExpr must be a tuple or list of two expressions") - opExpr1, opExpr2 = opExpr - thisExpr = Forward().setName(termName) - if rightLeftAssoc == opAssoc.LEFT: - if arity == 1: - matchExpr = _FB(lastExpr + opExpr) + Group(lastExpr + OneOrMore(opExpr)) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + lastExpr) + Group(lastExpr + OneOrMore(opExpr + lastExpr)) - else: - matchExpr = _FB(lastExpr + lastExpr) + Group(lastExpr + OneOrMore(lastExpr)) - elif arity == 3: - matchExpr = (_FB(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) - + Group(lastExpr + OneOrMore(opExpr1 + lastExpr + opExpr2 + lastExpr))) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - elif rightLeftAssoc == opAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Optional): - opExpr = Optional(opExpr) - matchExpr = _FB(opExpr.expr + thisExpr) + Group(opExpr + thisExpr) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + thisExpr) + Group(lastExpr + OneOrMore(opExpr + thisExpr)) - else: - matchExpr = _FB(lastExpr + thisExpr) + Group(lastExpr + OneOrMore(thisExpr)) - elif arity == 3: - matchExpr = (_FB(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) - + Group(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr)) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - else: - raise ValueError("operator must indicate right or left associativity") - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.setParseAction(*pa) - else: - matchExpr.setParseAction(pa) - thisExpr <<= (matchExpr.setName(termName) | lastExpr) - lastExpr = thisExpr - ret <<= lastExpr - return ret - -operatorPrecedence = infixNotation -"""(Deprecated) Former name of :class:`infixNotation`, will be -dropped in a future release.""" - -dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*') + '"').setName("string enclosed in double quotes") -sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*") + "'").setName("string enclosed in single quotes") -quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*') + '"' - | Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*") + "'").setName("quotedString using single or double quotes") -unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") - -def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): - """Helper method for defining nested lists enclosed in opening and - closing delimiters ("(" and ")" are the default). - - Parameters: - - opener - opening character for a nested list - (default= ``"("``); can also be a pyparsing expression - - closer - closing character for a nested list - (default= ``")"``); can also be a pyparsing expression - - content - expression for items within the nested lists - (default= ``None``) - - ignoreExpr - expression for ignoring opening and closing - delimiters (default= :class:`quotedString`) - - If an expression is not provided for the content argument, the - nested expression will capture all whitespace-delimited content - between delimiters as a list of separate values. - - Use the ``ignoreExpr`` argument to define expressions that may - contain opening or closing characters that should not be treated as - opening or closing characters for nesting, such as quotedString or - a comment expression. Specify multiple expressions using an - :class:`Or` or :class:`MatchFirst`. The default is - :class:`quotedString`, but if no expressions are to be ignored, then - pass ``None`` for this argument. - - Example:: - - data_type = oneOf("void int short long char float double") - decl_data_type = Combine(data_type + Optional(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR, RPAR = map(Suppress, "()") - - code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Optional(delimitedList(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(cStyleComment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.searchString(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - - prints:: - - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener, basestring) and isinstance(closer, basestring): - if len(opener) == 1 and len(closer) == 1: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr - + CharsNotIn(opener - + closer - + ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).setParseAction(lambda t: t[0].strip())) - else: - content = (empty.copy() + CharsNotIn(opener - + closer - + ParserElement.DEFAULT_WHITE_CHARS - ).setParseAction(lambda t: t[0].strip())) - else: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr - + ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1)) - ).setParseAction(lambda t: t[0].strip())) - else: - content = (Combine(OneOrMore(~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1)) - ).setParseAction(lambda t: t[0].strip())) - else: - raise ValueError("opening and closing arguments must be strings if no content expression is given") - ret = Forward() - if ignoreExpr is not None: - ret <<= Group(Suppress(opener) + ZeroOrMore(ignoreExpr | ret | content) + Suppress(closer)) - else: - ret <<= Group(Suppress(opener) + ZeroOrMore(ret | content) + Suppress(closer)) - ret.setName('nested %s%s expression' % (opener, closer)) - return ret - -def indentedBlock(blockStatementExpr, indentStack, indent=True): - """Helper method for defining space-delimited indentation blocks, - such as those used to define block statements in Python source code. - - Parameters: - - - blockStatementExpr - expression defining syntax of statement that - is repeated within the indented block - - indentStack - list created by caller to manage indentation stack - (multiple statementWithIndentedBlock expressions within a single - grammar should share a common indentStack) - - indent - boolean indicating whether block must be indented beyond - the current level; set to False for block of left-most - statements (default= ``True``) - - A valid block must contain at least one ``blockStatement``. - - Example:: - - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group("(" + Optional(delimitedList(identifier)) + ")") + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group(funcDecl + func_body) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << (funcDef | assignment | identifier) - - module_body = OneOrMore(stmt) - - parseTree = module_body.parseString(data) - parseTree.pprint() - - prints:: - - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - backup_stack = indentStack[:] - - def reset_stack(): - indentStack[:] = backup_stack - - def checkPeerIndent(s, l, t): - if l >= len(s): return - curCol = col(l, s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseException(s, l, "illegal nesting") - raise ParseException(s, l, "not a peer entry") - - def checkSubIndent(s, l, t): - curCol = col(l, s) - if curCol > indentStack[-1]: - indentStack.append(curCol) - else: - raise ParseException(s, l, "not a subentry") - - def checkUnindent(s, l, t): - if l >= len(s): return - curCol = col(l, s) - if not(indentStack and curCol in indentStack): - raise ParseException(s, l, "not an unindent") - if curCol < indentStack[-1]: - indentStack.pop() - - NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress(), stopOn=StringEnd()) - INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') - PEER = Empty().setParseAction(checkPeerIndent).setName('') - UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') - if indent: - smExpr = Group(Optional(NL) - + INDENT - + OneOrMore(PEER + Group(blockStatementExpr) + Optional(NL), stopOn=StringEnd()) - + UNDENT) - else: - smExpr = Group(Optional(NL) - + OneOrMore(PEER + Group(blockStatementExpr) + Optional(NL), stopOn=StringEnd()) - + UNDENT) - smExpr.setFailAction(lambda a, b, c, d: reset_stack()) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.setName('indented block') - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - -anyOpenTag, anyCloseTag = makeHTMLTags(Word(alphas, alphanums + "_:").setName('any tag')) -_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(), '><& "\'')) -commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") -def replaceHTMLEntity(t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") -"Comment of the form ``/* ... */``" - -htmlComment = Regex(r"").setName("HTML comment") -"Comment of the form ````" - -restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") -dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") -"Comment of the form ``// ... (to end of line)``" - -cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/' | dblSlashComment).setName("C++ style comment") -"Comment of either form :class:`cStyleComment` or :class:`dblSlashComment`" - -javaStyleComment = cppStyleComment -"Same as :class:`cppStyleComment`" - -pythonStyleComment = Regex(r"#.*").setName("Python style comment") -"Comment of the form ``# ... (to end of line)``" - -_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') - + Optional(Word(" \t") - + ~Literal(",") + ~LineEnd()))).streamline().setName("commaItem") -commaSeparatedList = delimitedList(Optional(quotedString.copy() | _commasepitem, default="")).setName("commaSeparatedList") -"""(Deprecated) Predefined expression of 1 or more printable words or -quoted strings, separated by commas. - -This expression is deprecated in favor of :class:`pyparsing_common.comma_separated_list`. -""" - -# some other useful expressions - using lower-case class name since we are really using this as a namespace -class pyparsing_common: - """Here are some common low-level expressions that may be useful in - jump-starting parser development: - - - numeric forms (:class:`integers`, :class:`reals`, - :class:`scientific notation`) - - common :class:`programming identifiers` - - network addresses (:class:`MAC`, - :class:`IPv4`, :class:`IPv6`) - - ISO8601 :class:`dates` and - :class:`datetime` - - :class:`UUID` - - :class:`comma-separated list` - - Parse actions: - - - :class:`convertToInteger` - - :class:`convertToFloat` - - :class:`convertToDate` - - :class:`convertToDatetime` - - :class:`stripHTMLTags` - - :class:`upcaseTokens` - - :class:`downcaseTokens` - - Example:: - - pyparsing_common.number.runTests(''' - # any int or real number, returned as the appropriate type - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.fnumber.runTests(''' - # any int or real number, returned as float - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.hex_integer.runTests(''' - # hex numbers - 100 - FF - ''') - - pyparsing_common.fraction.runTests(''' - # fractions - 1/2 - -3/4 - ''') - - pyparsing_common.mixed_integer.runTests(''' - # mixed fractions - 1 - 1/2 - -3/4 - 1-3/4 - ''') - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(''' - # uuid - 12345678-1234-5678-1234-567812345678 - ''') - - prints:: - - # any int or real number, returned as the appropriate type - 100 - [100] - - -100 - [-100] - - +100 - [100] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # any int or real number, returned as float - 100 - [100.0] - - -100 - [-100.0] - - +100 - [100.0] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # hex numbers - 100 - [256] - - FF - [255] - - # fractions - 1/2 - [0.5] - - -3/4 - [-0.75] - - # mixed fractions - 1 - [1] - - 1/2 - [0.5] - - -3/4 - [-0.75] - - 1-3/4 - [1.75] - - # uuid - 12345678-1234-5678-1234-567812345678 - [UUID('12345678-1234-5678-1234-567812345678')] - """ - - convertToInteger = tokenMap(int) - """ - Parse action for converting parsed integers to Python int - """ - - convertToFloat = tokenMap(float) - """ - Parse action for converting parsed numbers to Python float - """ - - integer = Word(nums).setName("integer").setParseAction(convertToInteger) - """expression that parses an unsigned integer, returns an int""" - - hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int, 16)) - """expression that parses a hexadecimal integer, returns an int""" - - signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) - """expression that parses an integer with optional leading sign, returns an int""" - - fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") - """fractional expression of an integer divided by an integer, returns a float""" - fraction.addParseAction(lambda t: t[0]/t[-1]) - - mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") - """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" - mixed_integer.addParseAction(sum) - - real = Regex(r'[+-]?(?:\d+\.\d*|\.\d+)').setName("real number").setParseAction(convertToFloat) - """expression that parses a floating point number and returns a float""" - - sci_real = Regex(r'[+-]?(?:\d+(?:[eE][+-]?\d+)|(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) - """expression that parses a floating point number with optional - scientific notation and returns a float""" - - # streamlining this expression makes the docs nicer-looking - number = (sci_real | real | signed_integer).streamline() - """any numeric expression, returns the corresponding Python type""" - - fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) - """any int or real number, returned as float""" - - identifier = Word(alphas + '_', alphanums + '_').setName("identifier") - """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" - - ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") - "IPv4 address (``0.0.0.0 - 255.255.255.255``)" - - _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") - _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part) * 7).setName("full IPv6 address") - _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part) * (0, 6)) - + "::" - + Optional(_ipv6_part + (':' + _ipv6_part) * (0, 6)) - ).setName("short IPv6 address") - _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) - _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") - ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") - "IPv6 address (long, short, or mixed form)" - - mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") - "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" - - @staticmethod - def convertToDate(fmt="%Y-%m-%d"): - """ - Helper to create a parse action for converting parsed date string to Python datetime.date - - Params - - - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%d"``) - - Example:: - - date_expr = pyparsing_common.iso8601_date.copy() - date_expr.setParseAction(pyparsing_common.convertToDate()) - print(date_expr.parseString("1999-12-31")) - - prints:: - - [datetime.date(1999, 12, 31)] - """ - def cvt_fn(s, l, t): - try: - return datetime.strptime(t[0], fmt).date() - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - @staticmethod - def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): - """Helper to create a parse action for converting parsed - datetime string to Python datetime.datetime - - Params - - - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%dT%H:%M:%S.%f"``) - - Example:: - - dt_expr = pyparsing_common.iso8601_datetime.copy() - dt_expr.setParseAction(pyparsing_common.convertToDatetime()) - print(dt_expr.parseString("1999-12-31T23:59:59.999")) - - prints:: - - [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] - """ - def cvt_fn(s, l, t): - try: - return datetime.strptime(t[0], fmt) - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") - "ISO8601 date (``yyyy-mm-dd``)" - - iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") - "ISO8601 datetime (``yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)``) - trailing seconds, milliseconds, and timezone optional; accepts separating ``'T'`` or ``' '``" - - uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") - "UUID (``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``)" - - _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() - @staticmethod - def stripHTMLTags(s, l, tokens): - """Parse action to remove HTML tags from web page HTML source - - Example:: - - # strip HTML links from normal text - text = 'More info at the
pyparsing wiki page' - td, td_end = makeHTMLTags("TD") - table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end - print(table_text.parseString(text).body) - - Prints:: - - More info at the pyparsing wiki page - """ - return pyparsing_common._html_stripper.transformString(tokens[0]) - - _commasepitem = Combine(OneOrMore(~Literal(",") - + ~LineEnd() - + Word(printables, excludeChars=',') - + Optional(White(" \t")))).streamline().setName("commaItem") - comma_separated_list = delimitedList(Optional(quotedString.copy() - | _commasepitem, default='') - ).setName("comma separated list") - """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" - - upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) - """Parse action to convert tokens to upper case.""" - - downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) - """Parse action to convert tokens to lower case.""" - - -class _lazyclassproperty(object): - def __init__(self, fn): - self.fn = fn - self.__doc__ = fn.__doc__ - self.__name__ = fn.__name__ - - def __get__(self, obj, cls): - if cls is None: - cls = type(obj) - if not hasattr(cls, '_intern') or any(cls._intern is getattr(superclass, '_intern', []) - for superclass in cls.__mro__[1:]): - cls._intern = {} - attrname = self.fn.__name__ - if attrname not in cls._intern: - cls._intern[attrname] = self.fn(cls) - return cls._intern[attrname] - - -class unicode_set(object): - """ - A set of Unicode characters, for language-specific strings for - ``alphas``, ``nums``, ``alphanums``, and ``printables``. - A unicode_set is defined by a list of ranges in the Unicode character - set, in a class attribute ``_ranges``, such as:: - - _ranges = [(0x0020, 0x007e), (0x00a0, 0x00ff),] - - A unicode set can also be defined using multiple inheritance of other unicode sets:: - - class CJK(Chinese, Japanese, Korean): - pass - """ - _ranges = [] - - @classmethod - def _get_chars_for_ranges(cls): - ret = [] - for cc in cls.__mro__: - if cc is unicode_set: - break - for rr in cc._ranges: - ret.extend(range(rr[0], rr[-1] + 1)) - return [unichr(c) for c in sorted(set(ret))] - - @_lazyclassproperty - def printables(cls): - "all non-whitespace characters in this range" - return u''.join(filterfalse(unicode.isspace, cls._get_chars_for_ranges())) - - @_lazyclassproperty - def alphas(cls): - "all alphabetic characters in this range" - return u''.join(filter(unicode.isalpha, cls._get_chars_for_ranges())) - - @_lazyclassproperty - def nums(cls): - "all numeric digit characters in this range" - return u''.join(filter(unicode.isdigit, cls._get_chars_for_ranges())) - - @_lazyclassproperty - def alphanums(cls): - "all alphanumeric characters in this range" - return cls.alphas + cls.nums - - -class pyparsing_unicode(unicode_set): - """ - A namespace class for defining common language unicode_sets. - """ - _ranges = [(32, sys.maxunicode)] - - class Latin1(unicode_set): - "Unicode set for Latin-1 Unicode Character Range" - _ranges = [(0x0020, 0x007e), (0x00a0, 0x00ff),] - - class LatinA(unicode_set): - "Unicode set for Latin-A Unicode Character Range" - _ranges = [(0x0100, 0x017f),] - - class LatinB(unicode_set): - "Unicode set for Latin-B Unicode Character Range" - _ranges = [(0x0180, 0x024f),] - - class Greek(unicode_set): - "Unicode set for Greek Unicode Character Ranges" - _ranges = [ - (0x0370, 0x03ff), (0x1f00, 0x1f15), (0x1f18, 0x1f1d), (0x1f20, 0x1f45), (0x1f48, 0x1f4d), - (0x1f50, 0x1f57), (0x1f59,), (0x1f5b,), (0x1f5d,), (0x1f5f, 0x1f7d), (0x1f80, 0x1fb4), (0x1fb6, 0x1fc4), - (0x1fc6, 0x1fd3), (0x1fd6, 0x1fdb), (0x1fdd, 0x1fef), (0x1ff2, 0x1ff4), (0x1ff6, 0x1ffe), - ] - - class Cyrillic(unicode_set): - "Unicode set for Cyrillic Unicode Character Range" - _ranges = [(0x0400, 0x04ff)] - - class Chinese(unicode_set): - "Unicode set for Chinese Unicode Character Range" - _ranges = [(0x4e00, 0x9fff), (0x3000, 0x303f),] - - class Japanese(unicode_set): - "Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges" - _ranges = [] - - class Kanji(unicode_set): - "Unicode set for Kanji Unicode Character Range" - _ranges = [(0x4E00, 0x9Fbf), (0x3000, 0x303f),] - - class Hiragana(unicode_set): - "Unicode set for Hiragana Unicode Character Range" - _ranges = [(0x3040, 0x309f),] - - class Katakana(unicode_set): - "Unicode set for Katakana Unicode Character Range" - _ranges = [(0x30a0, 0x30ff),] - - class Korean(unicode_set): - "Unicode set for Korean Unicode Character Range" - _ranges = [(0xac00, 0xd7af), (0x1100, 0x11ff), (0x3130, 0x318f), (0xa960, 0xa97f), (0xd7b0, 0xd7ff), (0x3000, 0x303f),] - - class CJK(Chinese, Japanese, Korean): - "Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range" - pass - - class Thai(unicode_set): - "Unicode set for Thai Unicode Character Range" - _ranges = [(0x0e01, 0x0e3a), (0x0e3f, 0x0e5b),] - - class Arabic(unicode_set): - "Unicode set for Arabic Unicode Character Range" - _ranges = [(0x0600, 0x061b), (0x061e, 0x06ff), (0x0700, 0x077f),] - - class Hebrew(unicode_set): - "Unicode set for Hebrew Unicode Character Range" - _ranges = [(0x0590, 0x05ff),] - - class Devanagari(unicode_set): - "Unicode set for Devanagari Unicode Character Range" - _ranges = [(0x0900, 0x097f), (0xa8e0, 0xa8ff)] - -pyparsing_unicode.Japanese._ranges = (pyparsing_unicode.Japanese.Kanji._ranges - + pyparsing_unicode.Japanese.Hiragana._ranges - + pyparsing_unicode.Japanese.Katakana._ranges) - -# define ranges in language character sets -if PY_3: - setattr(pyparsing_unicode, u"العربية", pyparsing_unicode.Arabic) - setattr(pyparsing_unicode, u"中文", pyparsing_unicode.Chinese) - setattr(pyparsing_unicode, u"кириллица", pyparsing_unicode.Cyrillic) - setattr(pyparsing_unicode, u"Ελληνικά", pyparsing_unicode.Greek) - setattr(pyparsing_unicode, u"עִברִית", pyparsing_unicode.Hebrew) - setattr(pyparsing_unicode, u"日本語", pyparsing_unicode.Japanese) - setattr(pyparsing_unicode.Japanese, u"漢字", pyparsing_unicode.Japanese.Kanji) - setattr(pyparsing_unicode.Japanese, u"カタカナ", pyparsing_unicode.Japanese.Katakana) - setattr(pyparsing_unicode.Japanese, u"ひらがな", pyparsing_unicode.Japanese.Hiragana) - setattr(pyparsing_unicode, u"한국어", pyparsing_unicode.Korean) - setattr(pyparsing_unicode, u"ไทย", pyparsing_unicode.Thai) - setattr(pyparsing_unicode, u"देवनागरी", pyparsing_unicode.Devanagari) - - -class pyparsing_test: - """ - namespace class for classes useful in writing unit tests - """ - - class reset_pyparsing_context: - """ - Context manager to be used when writing unit tests that modify pyparsing config values: - - packrat parsing - - default whitespace characters. - - default keyword characters - - literal string auto-conversion class - - __diag__ settings - - Example: - with reset_pyparsing_context(): - # test that literals used to construct a grammar are automatically suppressed - ParserElement.inlineLiteralsUsing(Suppress) - - term = Word(alphas) | Word(nums) - group = Group('(' + term[...] + ')') - - # assert that the '()' characters are not included in the parsed tokens - self.assertParseAndCheckLisst(group, "(abc 123 def)", ['abc', '123', 'def']) - - # after exiting context manager, literals are converted to Literal expressions again - """ - - def __init__(self): - self._save_context = {} - - def save(self): - self._save_context["default_whitespace"] = ParserElement.DEFAULT_WHITE_CHARS - self._save_context["default_keyword_chars"] = Keyword.DEFAULT_KEYWORD_CHARS - self._save_context[ - "literal_string_class" - ] = ParserElement._literalStringClass - self._save_context["packrat_enabled"] = ParserElement._packratEnabled - self._save_context["packrat_parse"] = ParserElement._parse - self._save_context["__diag__"] = { - name: getattr(__diag__, name) for name in __diag__._all_names - } - self._save_context["__compat__"] = { - "collect_all_And_tokens": __compat__.collect_all_And_tokens - } - return self - - def restore(self): - # reset pyparsing global state - if ( - ParserElement.DEFAULT_WHITE_CHARS - != self._save_context["default_whitespace"] - ): - ParserElement.setDefaultWhitespaceChars( - self._save_context["default_whitespace"] - ) - Keyword.DEFAULT_KEYWORD_CHARS = self._save_context["default_keyword_chars"] - ParserElement.inlineLiteralsUsing( - self._save_context["literal_string_class"] - ) - for name, value in self._save_context["__diag__"].items(): - setattr(__diag__, name, value) - ParserElement._packratEnabled = self._save_context["packrat_enabled"] - ParserElement._parse = self._save_context["packrat_parse"] - __compat__.collect_all_And_tokens = self._save_context["__compat__"] - - def __enter__(self): - return self.save() - - def __exit__(self, *args): - return self.restore() - - class TestParseResultsAsserts: - """ - A mixin class to add parse results assertion methods to normal unittest.TestCase classes. - """ - def assertParseResultsEquals( - self, result, expected_list=None, expected_dict=None, msg=None - ): - """ - Unit test assertion to compare a ParseResults object with an optional expected_list, - and compare any defined results names with an optional expected_dict. - """ - if expected_list is not None: - self.assertEqual(expected_list, result.asList(), msg=msg) - if expected_dict is not None: - self.assertEqual(expected_dict, result.asDict(), msg=msg) - - def assertParseAndCheckList( - self, expr, test_string, expected_list, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ParseResults.asList() is equal to the expected_list. - """ - result = expr.parseString(test_string, parseAll=True) - if verbose: - print(result.dump()) - self.assertParseResultsEquals(result, expected_list=expected_list, msg=msg) - - def assertParseAndCheckDict( - self, expr, test_string, expected_dict, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ParseResults.asDict() is equal to the expected_dict. - """ - result = expr.parseString(test_string, parseAll=True) - if verbose: - print(result.dump()) - self.assertParseResultsEquals(result, expected_dict=expected_dict, msg=msg) - - def assertRunTestResults( - self, run_tests_report, expected_parse_results=None, msg=None - ): - """ - Unit test assertion to evaluate output of ParserElement.runTests(). If a list of - list-dict tuples is given as the expected_parse_results argument, then these are zipped - with the report tuples returned by runTests and evaluated using assertParseResultsEquals. - Finally, asserts that the overall runTests() success value is True. - - :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests - :param expected_parse_results (optional): [tuple(str, list, dict, Exception)] - """ - run_test_success, run_test_results = run_tests_report - - if expected_parse_results is not None: - merged = [ - (rpt[0], rpt[1], expected) - for rpt, expected in zip(run_test_results, expected_parse_results) - ] - for test_string, result, expected in merged: - # expected should be a tuple containing a list and/or a dict or an exception, - # and optional failure message string - # an empty tuple will skip any result validation - fail_msg = next( - (exp for exp in expected if isinstance(exp, str)), None - ) - expected_exception = next( - ( - exp - for exp in expected - if isinstance(exp, type) and issubclass(exp, Exception) - ), - None, - ) - if expected_exception is not None: - with self.assertRaises( - expected_exception=expected_exception, msg=fail_msg or msg - ): - if isinstance(result, Exception): - raise result - else: - expected_list = next( - (exp for exp in expected if isinstance(exp, list)), None - ) - expected_dict = next( - (exp for exp in expected if isinstance(exp, dict)), None - ) - if (expected_list, expected_dict) != (None, None): - self.assertParseResultsEquals( - result, - expected_list=expected_list, - expected_dict=expected_dict, - msg=fail_msg or msg, - ) - else: - # warning here maybe? - print("no validation for {!r}".format(test_string)) - - # do this last, in case some specific test results can be reported instead - self.assertTrue( - run_test_success, msg=msg if msg is not None else "failed runTests" - ) - - @contextmanager - def assertRaisesParseException(self, exc_type=ParseException, msg=None): - with self.assertRaises(exc_type, msg=msg): - yield - - -if __name__ == "__main__": - - selectToken = CaselessLiteral("select") - fromToken = CaselessLiteral("from") - - ident = Word(alphas, alphanums + "_$") - - columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - columnNameList = Group(delimitedList(columnName)).setName("columns") - columnSpec = ('*' | columnNameList) - - tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - tableNameList = Group(delimitedList(tableName)).setName("tables") - - simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") - - # demo runTests method, including embedded comments in test string - simpleSQL.runTests(""" - # '*' as column list and dotted table name - select * from SYS.XYZZY - - # caseless match on "SELECT", and casts back to "select" - SELECT * from XYZZY, ABC - - # list of column names, and mixed case SELECT keyword - Select AA,BB,CC from Sys.dual - - # multiple tables - Select A, B, C from Sys.dual, Table2 - - # invalid SELECT keyword - should fail - Xelect A, B, C from Sys.dual - - # incomplete command - should fail - Select - - # invalid column name - should fail - Select ^^^ frox Sys.dual - - """) - - pyparsing_common.number.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - # any int or real number, returned as float - pyparsing_common.fnumber.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - pyparsing_common.hex_integer.runTests(""" - 100 - FF - """) - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(""" - 12345678-1234-5678-1234-567812345678 - """) diff --git a/third_party/python/pyrsistent/pyrsistent.egg-info/PKG-INFO b/third_party/python/pyrsistent/pyrsistent.egg-info/PKG-INFO deleted file mode 100644 index 1d1c15903475..000000000000 --- a/third_party/python/pyrsistent/pyrsistent.egg-info/PKG-INFO +++ /dev/null @@ -1,742 +0,0 @@ -Metadata-Version: 1.1 -Name: pyrsistent -Version: 0.16.0 -Summary: Persistent/Functional/Immutable data structures -Home-page: http://github.com/tobgu/pyrsistent/ -Author: Tobias Gustafsson -Author-email: tobias.l.gustafsson@gmail.com -License: MIT -Description: Pyrsistent - ========== - .. image:: https://travis-ci.org/tobgu/pyrsistent.png?branch=master - :target: https://travis-ci.org/tobgu/pyrsistent - - .. image:: https://badge.fury.io/py/pyrsistent.svg - :target: https://badge.fury.io/py/pyrsistent - - .. image:: https://coveralls.io/repos/tobgu/pyrsistent/badge.svg?branch=master&service=github - :target: https://coveralls.io/github/tobgu/pyrsistent?branch=master - - - .. _Pyrthon: https://www.github.com/tobgu/pyrthon/ - - Pyrsistent is a number of persistent collections (by some referred to as functional data structures). Persistent in - the sense that they are immutable. - - All methods on a data structure that would normally mutate it instead return a new copy of the structure containing the - requested updates. The original structure is left untouched. - - This will simplify the reasoning about what a program does since no hidden side effects ever can take place to these - data structures. You can rest assured that the object you hold a reference to will remain the same throughout its - lifetime and need not worry that somewhere five stack levels below you in the darkest corner of your application - someone has decided to remove that element that you expected to be there. - - Pyrsistent is influenced by persistent data structures such as those found in the standard library of Clojure. The - data structures are designed to share common elements through path copying. - It aims at taking these concepts and make them as pythonic as possible so that they can be easily integrated into any python - program without hassle. - - If you want to go all in on persistent data structures and use literal syntax to define them in your code rather - than function calls check out Pyrthon_. - - Examples - -------- - .. _Sequence: collections_ - .. _Hashable: collections_ - .. _Mapping: collections_ - .. _Mappings: collections_ - .. _Set: collections_ - .. _collections: https://docs.python.org/3/library/collections.abc.html - .. _documentation: http://pyrsistent.readthedocs.org/ - - The collection types and key features currently implemented are: - - * PVector_, similar to a python list - * PMap_, similar to dict - * PSet_, similar to set - * PRecord_, a PMap on steroids with fixed fields, optional type and invariant checking and much more - * PClass_, a Python class fixed fields, optional type and invariant checking and much more - * `Checked collections`_, PVector, PMap and PSet with optional type and invariance checks and more - * PBag, similar to collections.Counter - * PList, a classic singly linked list - * PDeque, similar to collections.deque - * Immutable object type (immutable) built on the named tuple - * freeze_ and thaw_ functions to convert between pythons standard collections and pyrsistent collections. - * Flexible transformations_ of arbitrarily complex structures built from PMaps and PVectors. - - Below are examples of common usage patterns for some of the structures and features. More information and - full documentation for all data structures is available in the documentation_. - - .. _PVector: - - PVector - ~~~~~~~ - With full support for the Sequence_ protocol PVector is meant as a drop in replacement to the built in list from a readers - point of view. Write operations of course differ since no in place mutation is done but naming should be in line - with corresponding operations on the built in list. - - Support for the Hashable_ protocol also means that it can be used as key in Mappings_. - - Appends are amortized O(1). Random access and insert is log32(n) where n is the size of the vector. - - .. code:: python - - >>> from pyrsistent import v, pvector - - # No mutation of vectors once created, instead they - # are "evolved" leaving the original untouched - >>> v1 = v(1, 2, 3) - >>> v2 = v1.append(4) - >>> v3 = v2.set(1, 5) - >>> v1 - pvector([1, 2, 3]) - >>> v2 - pvector([1, 2, 3, 4]) - >>> v3 - pvector([1, 5, 3, 4]) - - # Random access and slicing - >>> v3[1] - 5 - >>> v3[1:3] - pvector([5, 3]) - - # Iteration - >>> list(x + 1 for x in v3) - [2, 6, 4, 5] - >>> pvector(2 * x for x in range(3)) - pvector([0, 2, 4]) - - .. _PMap: - - PMap - ~~~~ - With full support for the Mapping_ protocol PMap is meant as a drop in replacement to the built in dict from a readers point - of view. Support for the Hashable_ protocol also means that it can be used as key in other Mappings_. - - Random access and insert is log32(n) where n is the size of the map. - - .. code:: python - - >>> from pyrsistent import m, pmap, v - - # No mutation of maps once created, instead they are - # "evolved" leaving the original untouched - >>> m1 = m(a=1, b=2) - >>> m2 = m1.set('c', 3) - >>> m3 = m2.set('a', 5) - >>> m1 - pmap({'a': 1, 'b': 2}) - >>> m2 - pmap({'a': 1, 'c': 3, 'b': 2}) - >>> m3 - pmap({'a': 5, 'c': 3, 'b': 2}) - >>> m3['a'] - 5 - - # Evolution of nested persistent structures - >>> m4 = m(a=5, b=6, c=v(1, 2)) - >>> m4.transform(('c', 1), 17) - pmap({'a': 5, 'c': pvector([1, 17]), 'b': 6}) - >>> m5 = m(a=1, b=2) - - # Evolve by merging with other mappings - >>> m5.update(m(a=2, c=3), {'a': 17, 'd': 35}) - pmap({'a': 17, 'c': 3, 'b': 2, 'd': 35}) - >>> pmap({'x': 1, 'y': 2}) + pmap({'y': 3, 'z': 4}) - pmap({'y': 3, 'x': 1, 'z': 4}) - - # Dict-like methods to convert to list and iterate - >>> m3.items() - pvector([('a', 5), ('c', 3), ('b', 2)]) - >>> list(m3) - ['a', 'c', 'b'] - - .. _PSet: - - PSet - ~~~~ - With full support for the Set_ protocol PSet is meant as a drop in replacement to the built in set from a readers point - of view. Support for the Hashable_ protocol also means that it can be used as key in Mappings_. - - Random access and insert is log32(n) where n is the size of the set. - - .. code:: python - - >>> from pyrsistent import s - - # No mutation of sets once created, you know the story... - >>> s1 = s(1, 2, 3, 2) - >>> s2 = s1.add(4) - >>> s3 = s1.remove(1) - >>> s1 - pset([1, 2, 3]) - >>> s2 - pset([1, 2, 3, 4]) - >>> s3 - pset([2, 3]) - - # Full support for set operations - >>> s1 | s(3, 4, 5) - pset([1, 2, 3, 4, 5]) - >>> s1 & s(3, 4, 5) - pset([3]) - >>> s1 < s2 - True - >>> s1 < s(3, 4, 5) - False - - .. _PRecord: - - PRecord - ~~~~~~~ - A PRecord is a PMap with a fixed set of specified fields. Records are declared as python classes inheriting - from PRecord. Because it is a PMap it has full support for all Mapping methods such as iteration and element - access using subscript notation. - - .. code:: python - - >>> from pyrsistent import PRecord, field - >>> class ARecord(PRecord): - ... x = field() - ... - >>> r = ARecord(x=3) - >>> r - ARecord(x=3) - >>> r.x - 3 - >>> r.set(x=2) - ARecord(x=2) - >>> r.set(y=2) - Traceback (most recent call last): - AttributeError: 'y' is not among the specified fields for ARecord - - Type information - **************** - It is possible to add type information to the record to enforce type checks. Multiple allowed types can be specified - by providing an iterable of types. - - .. code:: python - - >>> class BRecord(PRecord): - ... x = field(type=int) - ... y = field(type=(int, type(None))) - ... - >>> BRecord(x=3, y=None) - BRecord(y=None, x=3) - >>> BRecord(x=3.0) - Traceback (most recent call last): - PTypeError: Invalid type for field BRecord.x, was float - - - Custom types (classes) that are iterable should be wrapped in a tuple to prevent their - members being added to the set of valid types. Although Enums in particular are now - supported without wrapping, see #83 for more information. - - Mandatory fields - **************** - Fields are not mandatory by default but can be specified as such. If fields are missing an - *InvariantException* will be thrown which contains information about the missing fields. - - .. code:: python - - >>> from pyrsistent import InvariantException - >>> class CRecord(PRecord): - ... x = field(mandatory=True) - ... - >>> r = CRecord(x=3) - >>> try: - ... r.discard('x') - ... except InvariantException as e: - ... print(e.missing_fields) - ... - ('CRecord.x',) - - Invariants - ********** - It is possible to add invariants that must hold when evolving the record. Invariants can be - specified on both field and record level. If invariants fail an *InvariantException* will be - thrown which contains information about the failing invariants. An invariant function should - return a tuple consisting of a boolean that tells if the invariant holds or not and an object - describing the invariant. This object can later be used to identify which invariant that failed. - - The global invariant function is only executed if all field invariants hold. - - Global invariants are inherited to subclasses. - - .. code:: python - - >>> class RestrictedVector(PRecord): - ... __invariant__ = lambda r: (r.y >= r.x, 'x larger than y') - ... x = field(invariant=lambda x: (x > 0, 'x negative')) - ... y = field(invariant=lambda y: (y > 0, 'y negative')) - ... - >>> r = RestrictedVector(y=3, x=2) - >>> try: - ... r.set(x=-1, y=-2) - ... except InvariantException as e: - ... print(e.invariant_errors) - ... - ('y negative', 'x negative') - >>> try: - ... r.set(x=2, y=1) - ... except InvariantException as e: - ... print(e.invariant_errors) - ... - ('x larger than y',) - - Invariants may also contain multiple assertions. For those cases the invariant function should - return a tuple of invariant tuples as described above. This structure is reflected in the - invariant_errors attribute of the exception which will contain tuples with data from all failed - invariants. Eg: - - .. code:: python - - >>> class EvenX(PRecord): - ... x = field(invariant=lambda x: ((x > 0, 'x negative'), (x % 2 == 0, 'x odd'))) - ... - >>> try: - ... EvenX(x=-1) - ... except InvariantException as e: - ... print(e.invariant_errors) - ... - (('x negative', 'x odd'),) - - - Factories - ********* - It's possible to specify factory functions for fields. The factory function receives whatever - is supplied as field value and the actual returned by the factory is assigned to the field - given that any type and invariant checks hold. - PRecords have a default factory specified as a static function on the class, create(). It takes - a *Mapping* as argument and returns an instance of the specific record. - If a record has fields of type PRecord the create() method of that record will - be called to create the "sub record" if no factory has explicitly been specified to override - this behaviour. - - .. code:: python - - >>> class DRecord(PRecord): - ... x = field(factory=int) - ... - >>> class ERecord(PRecord): - ... d = field(type=DRecord) - ... - >>> ERecord.create({'d': {'x': '1'}}) - ERecord(d=DRecord(x=1)) - - Collection fields - ***************** - It is also possible to have fields with ``pyrsistent`` collections. - - .. code:: python - - >>> from pyrsistent import pset_field, pmap_field, pvector_field - >>> class MultiRecord(PRecord): - ... set_of_ints = pset_field(int) - ... map_int_to_str = pmap_field(int, str) - ... vector_of_strs = pvector_field(str) - ... - - Serialization - ************* - PRecords support serialization back to dicts. Default serialization will take keys and values - "as is" and output them into a dict. It is possible to specify custom serialization functions - to take care of fields that require special treatment. - - .. code:: python - - >>> from datetime import date - >>> class Person(PRecord): - ... name = field(type=unicode) - ... birth_date = field(type=date, - ... serializer=lambda format, d: d.strftime(format['date'])) - ... - >>> john = Person(name=u'John', birth_date=date(1985, 10, 21)) - >>> john.serialize({'date': '%Y-%m-%d'}) - {'birth_date': '1985-10-21', 'name': u'John'} - - - .. _instar: https://github.com/boxed/instar/ - - .. _PClass: - - PClass - ~~~~~~ - A PClass is a python class with a fixed set of specified fields. PClasses are declared as python classes inheriting - from PClass. It is defined the same way that PRecords are and behaves like a PRecord in all aspects except that it - is not a PMap and hence not a collection but rather a plain Python object. - - .. code:: python - - >>> from pyrsistent import PClass, field - >>> class AClass(PClass): - ... x = field() - ... - >>> a = AClass(x=3) - >>> a - AClass(x=3) - >>> a.x - 3 - - - Checked collections - ~~~~~~~~~~~~~~~~~~~ - Checked collections currently come in three flavors: CheckedPVector, CheckedPMap and CheckedPSet. - - .. code:: python - - >>> from pyrsistent import CheckedPVector, CheckedPMap, CheckedPSet, thaw - >>> class Positives(CheckedPSet): - ... __type__ = (long, int) - ... __invariant__ = lambda n: (n >= 0, 'Negative') - ... - >>> class Lottery(PRecord): - ... name = field(type=str) - ... numbers = field(type=Positives, invariant=lambda p: (len(p) > 0, 'No numbers')) - ... - >>> class Lotteries(CheckedPVector): - ... __type__ = Lottery - ... - >>> class LotteriesByDate(CheckedPMap): - ... __key_type__ = date - ... __value_type__ = Lotteries - ... - >>> lotteries = LotteriesByDate.create({date(2015, 2, 15): [{'name': 'SuperLotto', 'numbers': {1, 2, 3}}, - ... {'name': 'MegaLotto', 'numbers': {4, 5, 6}}], - ... date(2015, 2, 16): [{'name': 'SuperLotto', 'numbers': {3, 2, 1}}, - ... {'name': 'MegaLotto', 'numbers': {6, 5, 4}}]}) - >>> lotteries - LotteriesByDate({datetime.date(2015, 2, 15): Lotteries([Lottery(numbers=Positives([1, 2, 3]), name='SuperLotto'), Lottery(numbers=Positives([4, 5, 6]), name='MegaLotto')]), datetime.date(2015, 2, 16): Lotteries([Lottery(numbers=Positives([1, 2, 3]), name='SuperLotto'), Lottery(numbers=Positives([4, 5, 6]), name='MegaLotto')])}) - - # The checked versions support all operations that the corresponding - # unchecked types do - >>> lottery_0215 = lotteries[date(2015, 2, 15)] - >>> lottery_0215.transform([0, 'name'], 'SuperDuperLotto') - Lotteries([Lottery(numbers=Positives([1, 2, 3]), name='SuperDuperLotto'), Lottery(numbers=Positives([4, 5, 6]), name='MegaLotto')]) - - # But also makes asserts that types and invariants hold - >>> lottery_0215.transform([0, 'name'], 999) - Traceback (most recent call last): - PTypeError: Invalid type for field Lottery.name, was int - - >>> lottery_0215.transform([0, 'numbers'], set()) - Traceback (most recent call last): - InvariantException: Field invariant failed - - # They can be converted back to python built ins with either thaw() - # or serialize() (which provides possibilities to customize serialization) - >>> thaw(lottery_0215) - [{'numbers': set([1, 2, 3]), 'name': 'SuperLotto'}, {'numbers': set([4, 5, 6]), 'name': 'MegaLotto'}] - >>> lottery_0215.serialize() - [{'numbers': set([1, 2, 3]), 'name': 'SuperLotto'}, {'numbers': set([4, 5, 6]), 'name': 'MegaLotto'}] - - .. _transformations: - - Transformations - ~~~~~~~~~~~~~~~ - Transformations are inspired by the cool library instar_ for Clojure. They let you evolve PMaps and PVectors - with arbitrarily deep/complex nesting using simple syntax and flexible matching syntax. - - The first argument to transformation is the path that points out the value to transform. The - second is the transformation to perform. If the transformation is callable it will be applied - to the value(s) matching the path. The path may also contain callables. In that case they are - treated as matchers. If the matcher returns True for a specific key it is considered for transformation. - - .. code:: python - - # Basic examples - >>> from pyrsistent import inc, freeze, thaw, rex, ny, discard - >>> v1 = freeze([1, 2, 3, 4, 5]) - >>> v1.transform([2], inc) - pvector([1, 2, 4, 4, 5]) - >>> v1.transform([lambda ix: 0 < ix < 4], 8) - pvector([1, 8, 8, 8, 5]) - >>> v1.transform([lambda ix, v: ix == 0 or v == 5], 0) - pvector([0, 2, 3, 4, 0]) - - # The (a)ny matcher can be used to match anything - >>> v1.transform([ny], 8) - pvector([8, 8, 8, 8, 8]) - - # Regular expressions can be used for matching - >>> scores = freeze({'John': 12, 'Joseph': 34, 'Sara': 23}) - >>> scores.transform([rex('^Jo')], 0) - pmap({'Joseph': 0, 'Sara': 23, 'John': 0}) - - # Transformations can be done on arbitrarily deep structures - >>> news_paper = freeze({'articles': [{'author': 'Sara', 'content': 'A short article'}, - ... {'author': 'Steve', 'content': 'A slightly longer article'}], - ... 'weather': {'temperature': '11C', 'wind': '5m/s'}}) - >>> short_news = news_paper.transform(['articles', ny, 'content'], lambda c: c[:25] + '...' if len(c) > 25 else c) - >>> very_short_news = news_paper.transform(['articles', ny, 'content'], lambda c: c[:15] + '...' if len(c) > 15 else c) - >>> very_short_news.articles[0].content - 'A short article' - >>> very_short_news.articles[1].content - 'A slightly long...' - - # When nothing has been transformed the original data structure is kept - >>> short_news is news_paper - True - >>> very_short_news is news_paper - False - >>> very_short_news.articles[0] is news_paper.articles[0] - True - - # There is a special transformation that can be used to discard elements. Also - # multiple transformations can be applied in one call - >>> thaw(news_paper.transform(['weather'], discard, ['articles', ny, 'content'], discard)) - {'articles': [{'author': 'Sara'}, {'author': 'Steve'}]} - - Evolvers - ~~~~~~~~ - PVector, PMap and PSet all have support for a concept dubbed *evolvers*. An evolver acts like a mutable - view of the underlying persistent data structure with "transaction like" semantics. No updates of the original - data structure is ever performed, it is still fully immutable. - - The evolvers have a very limited API by design to discourage excessive, and inappropriate, usage as that would - take us down the mutable road. In principle only basic mutation and element access functions are supported. - Check out the documentation_ of each data structure for specific examples. - - Examples of when you may want to use an evolver instead of working directly with the data structure include: - - * Multiple updates are done to the same data structure and the intermediate results are of no - interest. In this case using an evolver may be a more efficient and easier to work with. - * You need to pass a vector into a legacy function or a function that you have no control - over which performs in place mutations. In this case pass an evolver instance - instead and then create a new pvector from the evolver once the function returns. - - .. code:: python - - >>> from pyrsistent import v - - # In place mutation as when working with the built in counterpart - >>> v1 = v(1, 2, 3) - >>> e = v1.evolver() - >>> e[1] = 22 - >>> e = e.append(4) - >>> e = e.extend([5, 6]) - >>> e[5] += 1 - >>> len(e) - 6 - - # The evolver is considered *dirty* when it contains changes compared to the underlying vector - >>> e.is_dirty() - True - - # But the underlying pvector still remains untouched - >>> v1 - pvector([1, 2, 3]) - - # Once satisfied with the updates you can produce a new pvector containing the updates. - # The new pvector will share data with the original pvector in the same way that would have - # been done if only using operations on the pvector. - >>> v2 = e.persistent() - >>> v2 - pvector([1, 22, 3, 4, 5, 7]) - - # The evolver is now no longer considered *dirty* as it contains no differences compared to the - # pvector just produced. - >>> e.is_dirty() - False - - # You may continue to work with the same evolver without affecting the content of v2 - >>> e[0] = 11 - - # Or create a new evolver from v2. The two evolvers can be updated independently but will both - # share data with v2 where possible. - >>> e2 = v2.evolver() - >>> e2[0] = 1111 - >>> e.persistent() - pvector([11, 22, 3, 4, 5, 7]) - >>> e2.persistent() - pvector([1111, 22, 3, 4, 5, 7]) - - .. _freeze: - .. _thaw: - - freeze and thaw - ~~~~~~~~~~~~~~~ - These functions are great when your cozy immutable world has to interact with the evil mutable world outside. - - .. code:: python - - >>> from pyrsistent import freeze, thaw, v, m - >>> freeze([1, {'a': 3}]) - pvector([1, pmap({'a': 3})]) - >>> thaw(v(1, m(a=3))) - [1, {'a': 3}] - - Compatibility - ------------- - - Pyrsistent is developed and tested on Python 2.7, 3.5, 3.6, 3.7 and PyPy (Python 2 and 3 compatible). It will most - likely work on all other versions >= 3.4 but no guarantees are given. :) - - Compatibility issues - ~~~~~~~~~~~~~~~~~~~~ - - .. _27: https://github.com/tobgu/pyrsistent/issues/27 - - There is currently one known compatibility issue when comparing built in sets and frozensets to PSets as discussed in 27_. - It affects python 2 versions < 2.7.8 and python 3 versions < 3.4.0 and is due to a bug described in - http://bugs.python.org/issue8743. - - Comparisons will fail or be incorrect when using the set/frozenset as left hand side of the comparison. As a workaround - you need to either upgrade Python to a more recent version, avoid comparing sets/frozensets with PSets or always make - sure to convert both sides of the comparison to the same type before performing the comparison. - - Performance - ----------- - - Pyrsistent is developed with performance in mind. Still, while some operations are nearly on par with their built in, - mutable, counterparts in terms of speed, other operations are slower. In the cases where attempts at - optimizations have been done, speed has generally been valued over space. - - Pyrsistent comes with two API compatible flavors of PVector (on which PMap and PSet are based), one pure Python - implementation and one implemented as a C extension. The latter generally being 2 - 20 times faster than the former. - The C extension will be used automatically when possible. - - The pure python implementation is fully PyPy compatible. Running it under PyPy speeds operations up considerably if - the structures are used heavily (if JITed), for some cases the performance is almost on par with the built in counterparts. - - Type hints - ---------- - - PEP 561 style type hints for use with mypy and various editors are available for most types and functions in pyrsistent. - - Type classes for annotating your own code with pyrsistent types are also available under pyrsistent.typing. - - Installation - ------------ - - pip install pyrsistent - - Documentation - ------------- - - Available at http://pyrsistent.readthedocs.org/ - - Brief presentation available at http://slides.com/tobiasgustafsson/immutability-and-python/ - - Contributors - ------------ - - Tobias Gustafsson https://github.com/tobgu - - Christopher Armstrong https://github.com/radix - - Anders Hovmöller https://github.com/boxed - - Itamar Turner-Trauring https://github.com/itamarst - - Jonathan Lange https://github.com/jml - - Richard Futrell https://github.com/Futrell - - Jakob Hollenstein https://github.com/jkbjh - - David Honour https://github.com/foolswood - - David R. MacIver https://github.com/DRMacIver - - Marcus Ewert https://github.com/sarum90 - - Jean-Paul Calderone https://github.com/exarkun - - Douglas Treadwell https://github.com/douglas-treadwell - - Travis Parker https://github.com/teepark - - Julian Berman https://github.com/Julian - - Dennis Tomas https://github.com/dtomas - - Neil Vyas https://github.com/neilvyas - - doozr https://github.com/doozr - - Kamil Galuszka https://github.com/galuszkak - - Tsuyoshi Hombashi https://github.com/thombashi - - nattofriends https://github.com/nattofriends - - agberk https://github.com/agberk - - Waleed Khan https://github.com/arxanas - - Jean-Louis Fuchs https://github.com/ganwell - - Carlos Corbacho https://github.com/ccorbacho - - Felix Yan https://github.com/felixonmars - - benrg https://github.com/benrg - - Jere Lahelma https://github.com/je-l - - Max Taggart https://github.com/MaxTaggart - - Vincent Philippon https://github.com/vphilippon - - Semen Zhydenko https://github.com/ss18 - - Till Varoquaux https://github.com/till-varoquaux - - Michal Kowalik https://github.com/michalvi - - ossdev07 https://github.com/ossdev07 - - Kerry Olesen https://github.com/qhesz - - johnthagen https://github.com/johnthagen - - Contributing - ------------ - - Want to contribute? That's great! If you experience problems please log them on GitHub. If you want to contribute code, - please fork the repository and submit a pull request. - - Run tests - ~~~~~~~~~ - .. _tox: https://tox.readthedocs.io/en/latest/ - - Tests can be executed using tox_. - - Install tox: ``pip install tox`` - - Run test for Python 2.7: ``tox -epy27`` - - Release - ~~~~~~~ - * Update CHANGES.txt - * Update README with any new contributors and potential info needed. - * Update _pyrsistent_version.py - * python setup.py sdist upload - * Commit and tag with new version: git add -u . && git commit -m 'Prepare version vX.Y.Z' && git tag -a vX.Y.Z -m 'vX.Y.Z' - * Push commit and tags: git push && git push --tags - - Project status - -------------- - Pyrsistent can be considered stable and mature (who knows, there may even be a 1.0 some day :-)). The project is - maintained, bugs fixed, PRs reviewed and merged and new releases made. I currently do not have time for development - of new features or functionality which I don't have use for myself. I'm more than happy to take PRs for new - functionality though! - - There are a bunch of issues marked with ``enhancement`` and ``help wanted`` that contain requests for new functionality - that would be nice to include. The level of difficulty and extend of the issues varies, please reach out to me if you're - interested in working on any of them. - - If you feel that you have a grand master plan for where you would like Pyrsistent to go and have the time to put into - it please don't hesitate to discuss this with me and submit PRs for it. If all goes well I'd be more than happy to add - additional maintainers to the project! - -Platform: UNKNOWN -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: Implementation :: PyPy diff --git a/third_party/python/pyrsistent/pyrsistent.egg-info/SOURCES.txt b/third_party/python/pyrsistent/pyrsistent.egg-info/SOURCES.txt deleted file mode 100644 index 9d3cc0a8d6ae..000000000000 --- a/third_party/python/pyrsistent/pyrsistent.egg-info/SOURCES.txt +++ /dev/null @@ -1,53 +0,0 @@ -CHANGES.txt -LICENCE.mit -MANIFEST.in -README -README.rst -_pyrsistent_version.py -pvectorcmodule.c -setup.cfg -setup.py -pyrsistent/__init__.py -pyrsistent/__init__.pyi -pyrsistent/_checked_types.py -pyrsistent/_compat.py -pyrsistent/_field_common.py -pyrsistent/_helpers.py -pyrsistent/_immutable.py -pyrsistent/_pbag.py -pyrsistent/_pclass.py -pyrsistent/_pdeque.py -pyrsistent/_plist.py -pyrsistent/_pmap.py -pyrsistent/_precord.py -pyrsistent/_pset.py -pyrsistent/_pvector.py -pyrsistent/_toolz.py -pyrsistent/_transformations.py -pyrsistent/py.typed -pyrsistent/typing.py -pyrsistent/typing.pyi -pyrsistent.egg-info/PKG-INFO -pyrsistent.egg-info/SOURCES.txt -pyrsistent.egg-info/dependency_links.txt -pyrsistent.egg-info/requires.txt -pyrsistent.egg-info/top_level.txt -tests/bag_test.py -tests/checked_map_test.py -tests/checked_set_test.py -tests/checked_vector_test.py -tests/class_test.py -tests/deque_test.py -tests/field_test.py -tests/freeze_test.py -tests/hypothesis_vector_test.py -tests/immutable_object_test.py -tests/list_test.py -tests/map_test.py -tests/memory_profiling.py -tests/record_test.py -tests/regression_test.py -tests/set_test.py -tests/toolz_test.py -tests/transform_test.py -tests/vector_test.py \ No newline at end of file diff --git a/third_party/python/pyrsistent/pyrsistent.egg-info/dependency_links.txt b/third_party/python/pyrsistent/pyrsistent.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/pyrsistent/pyrsistent.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/pyrsistent/pyrsistent.egg-info/requires.txt b/third_party/python/pyrsistent/pyrsistent.egg-info/requires.txt deleted file mode 100644 index ffe2fce49895..000000000000 --- a/third_party/python/pyrsistent/pyrsistent.egg-info/requires.txt +++ /dev/null @@ -1 +0,0 @@ -six diff --git a/third_party/python/pyrsistent/pyrsistent.egg-info/top_level.txt b/third_party/python/pyrsistent/pyrsistent.egg-info/top_level.txt deleted file mode 100644 index f2460728a9d5..000000000000 --- a/third_party/python/pyrsistent/pyrsistent.egg-info/top_level.txt +++ /dev/null @@ -1,3 +0,0 @@ -_pyrsistent_version -pvectorc -pyrsistent diff --git a/third_party/python/pystache/pystache.egg-info/PKG-INFO b/third_party/python/pystache/pystache.egg-info/PKG-INFO deleted file mode 100644 index 92e0c7f8ed24..000000000000 --- a/third_party/python/pystache/pystache.egg-info/PKG-INFO +++ /dev/null @@ -1,536 +0,0 @@ -Metadata-Version: 1.1 -Name: pystache -Version: 0.5.4 -Summary: Mustache for Python -Home-page: http://github.com/defunkt/pystache -Author: Chris Jerdonek -Author-email: chris.jerdonek@gmail.com -License: MIT -Description: .. Do not edit this file. This file is auto-generated for PyPI by setup.py - .. using pandoc, so edits should go in the source files rather than here. - - Pystache - ======== - - .. figure:: http://defunkt.github.com/pystache/images/logo_phillips.png - :alt: mustachioed, monocled snake by David Phillips - - .. figure:: https://secure.travis-ci.org/defunkt/pystache.png - :alt: Travis CI current build status - - `Pystache `__ is a Python - implementation of `Mustache `__. Mustache - is a framework-agnostic, logic-free templating system inspired by - `ctemplate `__ and - `et `__. - Like ctemplate, Mustache "emphasizes separating logic from presentation: - it is impossible to embed application logic in this template language." - - The `mustache(5) `__ man - page provides a good introduction to Mustache's syntax. For a more - complete (and more current) description of Mustache's behavior, see the - official `Mustache spec `__. - - Pystache is `semantically versioned `__ and can be - found on `PyPI `__. This version - of Pystache passes all tests in `version - 1.1.2 `__ of the spec. - - Requirements - ------------ - - Pystache is tested with-- - - - Python 2.4 (requires simplejson `version - 2.0.9 `__ or earlier) - - Python 2.5 (requires - `simplejson `__) - - Python 2.6 - - Python 2.7 - - Python 3.1 - - Python 3.2 - - Python 3.3 - - `PyPy `__ - - `Distribute `__ (the setuptools - fork) is recommended over - `setuptools `__, and is required - in some cases (e.g. for Python 3 support). If you use - `pip `__, you probably already satisfy - this requirement. - - JSON support is needed only for the command-line interface and to run - the spec tests. We require simplejson for earlier versions of Python - since Python's `json `__ - module was added in Python 2.6. - - For Python 2.4 we require an earlier version of simplejson since - simplejson stopped officially supporting Python 2.4 in simplejson - version 2.1.0. Earlier versions of simplejson can be installed manually, - as follows: - - :: - - pip install 'simplejson<2.1.0' - - Official support for Python 2.4 will end with Pystache version 0.6.0. - - Install It - ---------- - - :: - - pip install pystache - - And test it-- - - :: - - pystache-test - - To install and test from source (e.g. from GitHub), see the Develop - section. - - Use It - ------ - - :: - - >>> import pystache - >>> print pystache.render('Hi {{person}}!', {'person': 'Mom'}) - Hi Mom! - - You can also create dedicated view classes to hold your view logic. - - Here's your view class (in .../examples/readme.py): - - :: - - class SayHello(object): - def to(self): - return "Pizza" - - Instantiating like so: - - :: - - >>> from pystache.tests.examples.readme import SayHello - >>> hello = SayHello() - - Then your template, say\_hello.mustache (by default in the same - directory as your class definition): - - :: - - Hello, {{to}}! - - Pull it together: - - :: - - >>> renderer = pystache.Renderer() - >>> print renderer.render(hello) - Hello, Pizza! - - For greater control over rendering (e.g. to specify a custom template - directory), use the ``Renderer`` class like above. One can pass - attributes to the Renderer class constructor or set them on a Renderer - instance. To customize template loading on a per-view basis, subclass - ``TemplateSpec``. See the docstrings of the - `Renderer `__ - class and - `TemplateSpec `__ - class for more information. - - You can also pre-parse a template: - - :: - - >>> parsed = pystache.parse(u"Hey {{#who}}{{.}}!{{/who}}") - >>> print parsed - [u'Hey ', _SectionNode(key=u'who', index_begin=12, index_end=18, parsed=[_EscapeNode(key=u'.'), u'!'])] - - And then: - - :: - - >>> print renderer.render(parsed, {'who': 'Pops'}) - Hey Pops! - >>> print renderer.render(parsed, {'who': 'you'}) - Hey you! - - Python 3 - -------- - - Pystache has supported Python 3 since version 0.5.1. Pystache behaves - slightly differently between Python 2 and 3, as follows: - - - In Python 2, the default html-escape function ``cgi.escape()`` does - not escape single quotes. In Python 3, the default escape function - ``html.escape()`` does escape single quotes. - - In both Python 2 and 3, the string and file encodings default to - ``sys.getdefaultencoding()``. However, this function can return - different values under Python 2 and 3, even when run from the same - system. Check your own system for the behavior on your system, or do - not rely on the defaults by passing in the encodings explicitly (e.g. - to the ``Renderer`` class). - - Unicode - ------- - - This section describes how Pystache handles unicode, strings, and - encodings. - - Internally, Pystache uses `only unicode - strings `__ - (``str`` in Python 3 and ``unicode`` in Python 2). For input, Pystache - accepts both unicode strings and byte strings (``bytes`` in Python 3 and - ``str`` in Python 2). For output, Pystache's template rendering methods - return only unicode. - - Pystache's ``Renderer`` class supports a number of attributes to control - how Pystache converts byte strings to unicode on input. These include - the ``file_encoding``, ``string_encoding``, and ``decode_errors`` - attributes. - - The ``file_encoding`` attribute is the encoding the renderer uses to - convert to unicode any files read from the file system. Similarly, - ``string_encoding`` is the encoding the renderer uses to convert any - other byte strings encountered during the rendering process into unicode - (e.g. context values that are encoded byte strings). - - The ``decode_errors`` attribute is what the renderer passes as the - ``errors`` argument to Python's built-in unicode-decoding function - (``str()`` in Python 3 and ``unicode()`` in Python 2). The valid values - for this argument are ``strict``, ``ignore``, and ``replace``. - - Each of these attributes can be set via the ``Renderer`` class's - constructor using a keyword argument of the same name. See the Renderer - class's docstrings for further details. In addition, the - ``file_encoding`` attribute can be controlled on a per-view basis by - subclassing the ``TemplateSpec`` class. When not specified explicitly, - these attributes default to values set in Pystache's ``defaults`` - module. - - Develop - ------- - - To test from a source distribution (without installing)-- - - :: - - python test_pystache.py - - To test Pystache with multiple versions of Python (with a single - command!), you can use `tox `__: - - :: - - pip install 'virtualenv<1.8' # Version 1.8 dropped support for Python 2.4. - pip install 'tox<1.4' # Version 1.4 dropped support for Python 2.4. - tox - - If you do not have all Python versions listed in ``tox.ini``-- - - :: - - tox -e py26,py32 # for example - - The source distribution tests also include doctests and tests from the - Mustache spec. To include tests from the Mustache spec in your test - runs: - - :: - - git submodule init - git submodule update - - The test harness parses the spec's (more human-readable) yaml files if - `PyYAML `__ is present. Otherwise, - it parses the json files. To install PyYAML-- - - :: - - pip install pyyaml - - To run a subset of the tests, you can use - `nose `__: - - :: - - pip install nose - nosetests --tests pystache/tests/test_context.py:GetValueTests.test_dictionary__key_present - - Using Python 3 with Pystache from source - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Pystache is written in Python 2 and must be converted to Python 3 prior - to using it with Python 3. The installation process (and tox) do this - automatically. - - To convert the code to Python 3 manually (while using Python 3)-- - - :: - - python setup.py build - - This writes the converted code to a subdirectory called ``build``. By - design, Python 3 builds - `cannot `__ - be created from Python 2. - - To convert the code without using setup.py, you can use - `2to3 `__ as follows (two - steps)-- - - :: - - 2to3 --write --nobackups --no-diffs --doctests_only pystache - 2to3 --write --nobackups --no-diffs pystache - - This converts the code (and doctests) in place. - - To ``import pystache`` from a source distribution while using Python 3, - be sure that you are importing from a directory containing a converted - version of the code (e.g. from the ``build`` directory after - converting), and not from the original (unconverted) source directory. - Otherwise, you will get a syntax error. You can help prevent this by not - running the Python IDE from the project directory when importing - Pystache while using Python 3. - - Mailing List - ------------ - - There is a `mailing list `__. - Note that there is a bit of a delay between posting a message and seeing - it appear in the mailing list archive. - - Credits - ------- - - :: - - >>> context = { 'author': 'Chris Wanstrath', 'maintainer': 'Chris Jerdonek' } - >>> print pystache.render("Author: {{author}}\nMaintainer: {{maintainer}}", context) - Author: Chris Wanstrath - Maintainer: Chris Jerdonek - - Pystache logo by `David Phillips `__ is - licensed under a `Creative Commons Attribution-ShareAlike 3.0 Unported - License `__. - |image0| - - History - ======= - - **Note:** Official support for Python 2.4 will end with Pystache version - 0.6.0. - - 0.5.4 (2014-07-11) - ------------------ - - - Bugfix: made test with filenames OS agnostic (issue #162). - - 0.5.3 (2012-11-03) - ------------------ - - - Added ability to customize string coercion (e.g. to have None render - as ``''``) (issue #130). - - Added Renderer.render\_name() to render a template by name (issue - #122). - - Added TemplateSpec.template\_path to specify an absolute path to a - template (issue #41). - - Added option of raising errors on missing tags/partials: - ``Renderer(missing_tags='strict')`` (issue #110). - - Added support for finding and loading templates by file name in - addition to by template name (issue #127). [xgecko] - - Added a ``parse()`` function that yields a printable, pre-compiled - parse tree. - - Added support for rendering pre-compiled templates. - - Added Python 3.3 to the list of supported versions. - - Added support for `PyPy `__ (issue #125). - - Added support for `Travis CI `__ (issue #124). - [msabramo] - - Bugfix: ``defaults.DELIMITERS`` can now be changed at runtime (issue - #135). [bennoleslie] - - Bugfix: exceptions raised from a property are no longer swallowed - when getting a key from a context stack (issue #110). - - Bugfix: lambda section values can now return non-ascii, non-unicode - strings (issue #118). - - Bugfix: allow ``test_pystache.py`` and ``tox`` to pass when run from - a downloaded sdist (i.e. without the spec test directory). - - Convert HISTORY and README files from reST to Markdown. - - More robust handling of byte strings in Python 3. - - Added Creative Commons license for David Phillips's logo. - - 0.5.2 (2012-05-03) - ------------------ - - - Added support for dot notation and version 1.1.2 of the spec (issue - #99). [rbp] - - Missing partials now render as empty string per latest version of - spec (issue #115). - - Bugfix: falsey values now coerced to strings using str(). - - Bugfix: lambda return values for sections no longer pushed onto - context stack (issue #113). - - Bugfix: lists of lambdas for sections were not rendered (issue #114). - - 0.5.1 (2012-04-24) - ------------------ - - - Added support for Python 3.1 and 3.2. - - Added tox support to test multiple Python versions. - - Added test script entry point: pystache-test. - - Added \_\_version\_\_ package attribute. - - Test harness now supports both YAML and JSON forms of Mustache spec. - - Test harness no longer requires nose. - - 0.5.0 (2012-04-03) - ------------------ - - This version represents a major rewrite and refactoring of the code base - that also adds features and fixes many bugs. All functionality and - nearly all unit tests have been preserved. However, some backwards - incompatible changes to the API have been made. - - Below is a selection of some of the changes (not exhaustive). - - Highlights: - - - Pystache now passes all tests in version 1.0.3 of the `Mustache - spec `__. [pvande] - - Removed View class: it is no longer necessary to subclass from View - or from any other class to create a view. - - Replaced Template with Renderer class: template rendering behavior - can be modified via the Renderer constructor or by setting attributes - on a Renderer instance. - - Added TemplateSpec class: template rendering can be specified on a - per-view basis by subclassing from TemplateSpec. - - Introduced separation of concerns and removed circular dependencies - (e.g. between Template and View classes, cf. `issue - #13 `__). - - Unicode now used consistently throughout the rendering process. - - Expanded test coverage: nosetests now runs doctests and ~105 test - cases from the Mustache spec (increasing the number of tests from 56 - to ~315). - - Added a rudimentary benchmarking script to gauge performance while - refactoring. - - Extensive documentation added (e.g. docstrings). - - Other changes: - - - Added a command-line interface. [vrde] - - The main rendering class now accepts a custom partial loader (e.g. a - dictionary) and a custom escape function. - - Non-ascii characters in str strings are now supported while - rendering. - - Added string encoding, file encoding, and errors options for decoding - to unicode. - - Removed the output encoding option. - - Removed the use of markupsafe. - - Bug fixes: - - - Context values no longer processed as template strings. - [jakearchibald] - - Whitespace surrounding sections is no longer altered, per the spec. - [heliodor] - - Zeroes now render correctly when using PyPy. [alex] - - Multline comments now permitted. [fczuardi] - - Extensionless template files are now supported. - - Passing ``**kwargs`` to ``Template()`` no longer modifies the - context. - - Passing ``**kwargs`` to ``Template()`` with no context no longer - raises an exception. - - 0.4.1 (2012-03-25) - ------------------ - - - Added support for Python 2.4. [wangtz, jvantuyl] - - 0.4.0 (2011-01-12) - ------------------ - - - Add support for nested contexts (within template and view) - - Add support for inverted lists - - Decoupled template loading - - 0.3.1 (2010-05-07) - ------------------ - - - Fix package - - 0.3.0 (2010-05-03) - ------------------ - - - View.template\_path can now hold a list of path - - Add {{& blah}} as an alias for {{{ blah }}} - - Higher Order Sections - - Inverted sections - - 0.2.0 (2010-02-15) - ------------------ - - - Bugfix: Methods returning False or None are not rendered - - Bugfix: Don't render an empty string when a tag's value is 0. - [enaeseth] - - Add support for using non-callables as View attributes. - [joshthecoder] - - Allow using View instances as attributes. [joshthecoder] - - Support for Unicode and non-ASCII-encoded bytestring output. - [enaeseth] - - Template file encoding awareness. [enaeseth] - - 0.1.1 (2009-11-13) - ------------------ - - - Ensure we're dealing with strings, always - - Tests can be run by executing the test file directly - - 0.1.0 (2009-11-12) - ------------------ - - - First release - - License - ======= - - Copyright (C) 2012 Chris Jerdonek. All rights reserved. - - Copyright (c) 2009 Chris Wanstrath - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - .. |image0| image:: http://i.creativecommons.org/l/by-sa/3.0/88x31.png - -Platform: UNKNOWN -Classifier: Development Status :: 4 - Beta -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.4 -Classifier: Programming Language :: Python :: 2.5 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.1 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: Implementation :: PyPy diff --git a/third_party/python/pystache/pystache.egg-info/SOURCES.txt b/third_party/python/pystache/pystache.egg-info/SOURCES.txt deleted file mode 100644 index 563fc1b28759..000000000000 --- a/third_party/python/pystache/pystache.egg-info/SOURCES.txt +++ /dev/null @@ -1,97 +0,0 @@ -HISTORY.md -LICENSE -MANIFEST.in -README.md -TODO.md -setup.py -setup_description.rst -test_pystache.py -tox.ini -pystache/__init__.py -pystache/common.py -pystache/context.py -pystache/defaults.py -pystache/init.py -pystache/loader.py -pystache/locator.py -pystache/parsed.py -pystache/parser.py -pystache/renderengine.py -pystache/renderer.py -pystache/specloader.py -pystache/template_spec.py -pystache.egg-info/PKG-INFO -pystache.egg-info/SOURCES.txt -pystache.egg-info/dependency_links.txt -pystache.egg-info/entry_points.txt -pystache.egg-info/top_level.txt -pystache/commands/__init__.py -pystache/commands/render.py -pystache/commands/test.py -pystache/tests/__init__.py -pystache/tests/benchmark.py -pystache/tests/common.py -pystache/tests/doctesting.py -pystache/tests/main.py -pystache/tests/spectesting.py -pystache/tests/test___init__.py -pystache/tests/test_commands.py -pystache/tests/test_context.py -pystache/tests/test_defaults.py -pystache/tests/test_examples.py -pystache/tests/test_loader.py -pystache/tests/test_locator.py -pystache/tests/test_parser.py -pystache/tests/test_pystache.py -pystache/tests/test_renderengine.py -pystache/tests/test_renderer.py -pystache/tests/test_simple.py -pystache/tests/test_specloader.py -pystache/tests/data/__init__.py -pystache/tests/data/ascii.mustache -pystache/tests/data/duplicate.mustache -pystache/tests/data/non_ascii.mustache -pystache/tests/data/sample_view.mustache -pystache/tests/data/say_hello.mustache -pystache/tests/data/views.py -pystache/tests/data/locator/__init__.py -pystache/tests/data/locator/duplicate.mustache -pystache/tests/data/locator/template.txt -pystache/tests/examples/__init__.py -pystache/tests/examples/comments.mustache -pystache/tests/examples/comments.py -pystache/tests/examples/complex.mustache -pystache/tests/examples/complex.py -pystache/tests/examples/delimiters.mustache -pystache/tests/examples/delimiters.py -pystache/tests/examples/double_section.mustache -pystache/tests/examples/double_section.py -pystache/tests/examples/escaped.mustache -pystache/tests/examples/escaped.py -pystache/tests/examples/inner_partial.mustache -pystache/tests/examples/inner_partial.txt -pystache/tests/examples/inverted.mustache -pystache/tests/examples/inverted.py -pystache/tests/examples/lambdas.mustache -pystache/tests/examples/lambdas.py -pystache/tests/examples/looping_partial.mustache -pystache/tests/examples/nested_context.mustache -pystache/tests/examples/nested_context.py -pystache/tests/examples/partial_in_partial.mustache -pystache/tests/examples/partial_with_lambda.mustache -pystache/tests/examples/partial_with_partial_and_lambda.mustache -pystache/tests/examples/partials_with_lambdas.py -pystache/tests/examples/readme.py -pystache/tests/examples/say_hello.mustache -pystache/tests/examples/simple.mustache -pystache/tests/examples/simple.py -pystache/tests/examples/tagless.mustache -pystache/tests/examples/template_partial.mustache -pystache/tests/examples/template_partial.py -pystache/tests/examples/template_partial.txt -pystache/tests/examples/unescaped.mustache -pystache/tests/examples/unescaped.py -pystache/tests/examples/unicode_input.mustache -pystache/tests/examples/unicode_input.py -pystache/tests/examples/unicode_output.mustache -pystache/tests/examples/unicode_output.py \ No newline at end of file diff --git a/third_party/python/pystache/pystache.egg-info/dependency_links.txt b/third_party/python/pystache/pystache.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/pystache/pystache.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/pystache/pystache.egg-info/entry_points.txt b/third_party/python/pystache/pystache.egg-info/entry_points.txt deleted file mode 100644 index 090db731cfe8..000000000000 --- a/third_party/python/pystache/pystache.egg-info/entry_points.txt +++ /dev/null @@ -1,4 +0,0 @@ -[console_scripts] -pystache = pystache.commands.render:main -pystache-test = pystache.commands.test:main - diff --git a/third_party/python/pystache/pystache.egg-info/top_level.txt b/third_party/python/pystache/pystache.egg-info/top_level.txt deleted file mode 100644 index 27d7fbb58260..000000000000 --- a/third_party/python/pystache/pystache.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pystache diff --git a/third_party/python/pytoml/pytoml.egg-info/PKG-INFO b/third_party/python/pytoml/pytoml.egg-info/PKG-INFO deleted file mode 100644 index 844436f95884..000000000000 --- a/third_party/python/pytoml/pytoml.egg-info/PKG-INFO +++ /dev/null @@ -1,10 +0,0 @@ -Metadata-Version: 1.0 -Name: pytoml -Version: 0.1.10 -Summary: A parser for TOML-0.4.0 -Home-page: https://github.com/avakar/pytoml -Author: Martin Vejnár -Author-email: avakar@ratatanek.cz -License: MIT -Description: UNKNOWN -Platform: UNKNOWN diff --git a/third_party/python/pytoml/pytoml.egg-info/SOURCES.txt b/third_party/python/pytoml/pytoml.egg-info/SOURCES.txt deleted file mode 100644 index c583f491f4e4..000000000000 --- a/third_party/python/pytoml/pytoml.egg-info/SOURCES.txt +++ /dev/null @@ -1,10 +0,0 @@ -setup.py -pytoml/__init__.py -pytoml/core.py -pytoml/parser.py -pytoml/writer.py -pytoml.egg-info/PKG-INFO -pytoml.egg-info/SOURCES.txt -pytoml.egg-info/dependency_links.txt -pytoml.egg-info/top_level.txt -test/test.py \ No newline at end of file diff --git a/third_party/python/pytoml/pytoml.egg-info/dependency_links.txt b/third_party/python/pytoml/pytoml.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/pytoml/pytoml.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/pytoml/pytoml.egg-info/top_level.txt b/third_party/python/pytoml/pytoml.egg-info/top_level.txt deleted file mode 100644 index 058140720adf..000000000000 --- a/third_party/python/pytoml/pytoml.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pytoml diff --git a/third_party/python/requirements.in b/third_party/python/requirements.in index 91642b846b0d..f95769cc3bd2 100644 --- a/third_party/python/requirements.in +++ b/third_party/python/requirements.in @@ -17,7 +17,6 @@ jsmin==2.1.0 json-e==2.7.0 mohawk==0.3.4 mozilla-version==0.3.4 -packaging==21.0 pathspec==0.8 pip-tools==5.5.0 ply==3.10 diff --git a/third_party/python/requirements.txt b/third_party/python/requirements.txt index a7b37e426c8e..269d293eb4b7 100644 --- a/third_party/python/requirements.txt +++ b/third_party/python/requirements.txt @@ -292,10 +292,6 @@ multidict==5.1.0 \ # via # aiohttp # yarl -packaging==21.0 \ - --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ - --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 - # via -r requirements-mach-vendor-python.in pathspec==0.8 \ --hash=sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0 \ --hash=sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061 @@ -332,10 +328,6 @@ pyasn1==0.4.8 \ pylru==1.0.9 \ --hash=sha256:71376192671f0ad1690b2a7427d39a29b1df994c8469a9b46b03ed7e28c0172c # via -r requirements-mach-vendor-python.in -pyparsing==2.4.7 \ - --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ - --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b - # via packaging pyrsistent==0.16.0 \ --hash=sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3 # via jsonschema diff --git a/third_party/python/slugid/slugid.egg-info/PKG-INFO b/third_party/python/slugid/slugid.egg-info/PKG-INFO deleted file mode 100644 index ba71c90babeb..000000000000 --- a/third_party/python/slugid/slugid.egg-info/PKG-INFO +++ /dev/null @@ -1,14 +0,0 @@ -Metadata-Version: 1.1 -Name: slugid -Version: 1.0.7 -Summary: Base64 encoded uuid v4 slugs -Home-page: http://taskcluster.github.io/slugid.py -Author: Pete Moore -Author-email: pmoore@mozilla.com -License: MPL 2.0 -Description: UNKNOWN -Platform: UNKNOWN -Classifier: Intended Audience :: Developers -Classifier: Natural Language :: English -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.5 diff --git a/third_party/python/slugid/slugid.egg-info/SOURCES.txt b/third_party/python/slugid/slugid.egg-info/SOURCES.txt deleted file mode 100644 index aab037721c4e..000000000000 --- a/third_party/python/slugid/slugid.egg-info/SOURCES.txt +++ /dev/null @@ -1,8 +0,0 @@ -README.rst -setup.py -slugid/__init__.py -slugid/slugid.py -slugid.egg-info/PKG-INFO -slugid.egg-info/SOURCES.txt -slugid.egg-info/dependency_links.txt -slugid.egg-info/top_level.txt \ No newline at end of file diff --git a/third_party/python/slugid/slugid.egg-info/dependency_links.txt b/third_party/python/slugid/slugid.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/slugid/slugid.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/slugid/slugid.egg-info/top_level.txt b/third_party/python/slugid/slugid.egg-info/top_level.txt deleted file mode 100644 index e1d5cb9085bf..000000000000 --- a/third_party/python/slugid/slugid.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -slugid diff --git a/third_party/python/yarl/yarl.egg-info/PKG-INFO b/third_party/python/yarl/yarl.egg-info/PKG-INFO deleted file mode 100644 index 3c242e513537..000000000000 --- a/third_party/python/yarl/yarl.egg-info/PKG-INFO +++ /dev/null @@ -1,797 +0,0 @@ -Metadata-Version: 2.1 -Name: yarl -Version: 1.6.3 -Summary: Yet another URL library -Home-page: https://github.com/aio-libs/yarl/ -Author: Andrew Svetlov -Author-email: andrew.svetlov@gmail.com -License: Apache 2 -Description: yarl - ==== - - .. image:: https://github.com/aio-libs/yarl/workflows/CI/badge.svg - :target: https://github.com/aio-libs/yarl/actions?query=workflow%3ACI - :align: right - - .. image:: https://codecov.io/gh/aio-libs/yarl/branch/master/graph/badge.svg - :target: https://codecov.io/gh/aio-libs/yarl - - .. image:: https://badge.fury.io/py/yarl.svg - :target: https://badge.fury.io/py/yarl - - - .. image:: https://readthedocs.org/projects/yarl/badge/?version=latest - :target: https://yarl.readthedocs.io - - - .. image:: https://img.shields.io/pypi/pyversions/yarl.svg - :target: https://pypi.python.org/pypi/yarl - - .. image:: https://badges.gitter.im/Join%20Chat.svg - :target: https://gitter.im/aio-libs/Lobby - :alt: Chat on Gitter - - Introduction - ------------ - - Url is constructed from ``str``: - - .. code-block:: pycon - - >>> from yarl import URL - >>> url = URL('https://www.python.org/~guido?arg=1#frag') - >>> url - URL('https://www.python.org/~guido?arg=1#frag') - - All url parts: *scheme*, *user*, *password*, *host*, *port*, *path*, - *query* and *fragment* are accessible by properties: - - .. code-block:: pycon - - >>> url.scheme - 'https' - >>> url.host - 'www.python.org' - >>> url.path - '/~guido' - >>> url.query_string - 'arg=1' - >>> url.query - - >>> url.fragment - 'frag' - - All url manipulations produce a new url object: - - .. code-block:: pycon - - >>> url = URL('https://www.python.org') - >>> url / 'foo' / 'bar' - URL('https://www.python.org/foo/bar') - >>> url / 'foo' % {'bar': 'baz'} - URL('https://www.python.org/foo?bar=baz') - - Strings passed to constructor and modification methods are - automatically encoded giving canonical representation as result: - - .. code-block:: pycon - - >>> url = URL('https://www.python.org/путь') - >>> url - URL('https://www.python.org/%D0%BF%D1%83%D1%82%D1%8C') - - Regular properties are *percent-decoded*, use ``raw_`` versions for - getting *encoded* strings: - - .. code-block:: pycon - - >>> url.path - '/путь' - - >>> url.raw_path - '/%D0%BF%D1%83%D1%82%D1%8C' - - Human readable representation of URL is available as ``.human_repr()``: - - .. code-block:: pycon - - >>> url.human_repr() - 'https://www.python.org/путь' - - For full documentation please read https://yarl.readthedocs.org. - - - Installation - ------------ - - :: - - $ pip install yarl - - The library is Python 3 only! - - PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install - ``yarl`` on another operating system (like *Alpine Linux*, which is not - manylinux-compliant because of the missing glibc and therefore, cannot be - used with our wheels) the the tarball will be used to compile the library from - the source code. It requires a C compiler and and Python headers installed. - - To skip the compilation you must explicitly opt-in by setting the `YARL_NO_EXTENSIONS` - environment variable to a non-empty value, e.g.: - - .. code-block:: bash - - $ YARL_NO_EXTENSIONS=1 pip install yarl - - Please note that the pure-Python (uncompiled) version is much slower. However, - PyPy always uses a pure-Python implementation, and, as such, it is unaffected - by this variable. - - Dependencies - ------------ - - YARL requires multidict_ library. - - - API documentation - ------------------ - - The documentation is located at https://yarl.readthedocs.org - - - Why isn't boolean supported by the URL query API? - ------------------------------------------------- - - There is no standard for boolean representation of boolean values. - - Some systems prefer ``true``/``false``, others like ``yes``/``no``, ``on``/``off``, - ``Y``/``N``, ``1``/``0``, etc. - - ``yarl`` cannot make an unambiguous decision on how to serialize ``bool`` values because - it is specific to how the end-user's application is built and would be different for - different apps. The library doesn't accept booleans in the API; a user should convert - bools into strings using own preferred translation protocol. - - - Comparison with other URL libraries - ------------------------------------ - - * furl (https://pypi.python.org/pypi/furl) - - The library has rich functionality but the ``furl`` object is mutable. - - I'm afraid to pass this object into foreign code: who knows if the - code will modify my url in a terrible way while I just want to send URL - with handy helpers for accessing URL properties. - - ``furl`` has other non-obvious tricky things but the main objection - is mutability. - - * URLObject (https://pypi.python.org/pypi/URLObject) - - URLObject is immutable, that's pretty good. - - Every URL change generates a new URL object. - - But the library doesn't do any decode/encode transformations leaving the - end user to cope with these gory details. - - - Source code - ----------- - - The project is hosted on GitHub_ - - Please file an issue on the `bug tracker - `_ if you have found a bug - or have some suggestion in order to improve the library. - - The library uses `Azure Pipelines `_ for - Continuous Integration. - - Discussion list - --------------- - - *aio-libs* google group: https://groups.google.com/forum/#!forum/aio-libs - - Feel free to post your questions and ideas here. - - - Authors and License - ------------------- - - The ``yarl`` package is written by Andrew Svetlov. - - It's *Apache 2* licensed and freely available. - - - .. _GitHub: https://github.com/aio-libs/yarl - - .. _multidict: https://github.com/aio-libs/multidict - - - ========= - Changelog - ========= - - .. - You should *NOT* be adding new change log entries to this file, this - file is managed by towncrier. You *may* edit previous change logs to - fix problems like typo corrections or such. - To add a new change log entry, please see - https://pip.pypa.io/en/latest/development/#adding-a-news-entry - we named the news folder "changes". - - WARNING: Don't drop the next directive! - - .. towncrier release notes start - - 1.6.3 (2020-11-14) - ================== - - Bugfixes - -------- - - - No longer loose characters when decoding incorrect percent-sequences (like ``%e2%82%f8``). All non-decodable percent-sequences are now preserved. - `#517 `_ - - Provide x86 Windows wheels. - `#535 `_ - - - ---- - - - 1.6.2 (2020-10-12) - ================== - - - Bugfixes - -------- - - - Provide generated ``.c`` files in TarBall distribution. - `#530 `_ - - 1.6.1 (2020-10-12) - ================== - - Features - -------- - - - Provide wheels for ``aarch64``, ``i686``, ``ppc64le``, ``s390x`` architectures on - Linux as well as ``x86_64``. - `#507 `_ - - Provide wheels for Python 3.9. - `#526 `_ - - Bugfixes - -------- - - - ``human_repr()`` now always produces valid representation equivalent to the original URL (if the original URL is valid). - `#511 `_ - - Fixed requoting a single percent followed by a percent-encoded character in the Cython implementation. - `#514 `_ - - Fix ValueError when decoding ``%`` which is not followed by two hexadecimal digits. - `#516 `_ - - Fix decoding ``%`` followed by a space and hexadecimal digit. - `#520 `_ - - Fix annotation of ``with_query()``/``update_query()`` methods for ``key=[val1, val2]`` case. - `#528 `_ - - Removal - ------- - - - Drop Python 3.5 support; Python 3.6 is the minimal supported Python version. - - - ---- - - - 1.6.0 (2020-09-23) - ================== - - Features - -------- - - - Allow for int and float subclasses in query, while still denying bool. - `#492 `_ - - - Bugfixes - -------- - - - Do not requote arguments in ``URL.build()``, ``with_xxx()`` and in ``/`` operator. - `#502 `_ - - Keep IPv6 brackets in ``origin()``. - `#504 `_ - - - ---- - - - 1.5.1 (2020-08-01) - ================== - - Bugfixes - -------- - - - Fix including relocated internal ``yarl._quoting_c`` C-extension into published PyPI dists. - `#485 `_ - - - Misc - ---- - - - `#484 `_ - - - ---- - - - 1.5.0 (2020-07-26) - ================== - - Features - -------- - - - Convert host to lowercase on URL building. - `#386 `_ - - Allow using ``mod`` operator (`%`) for updating query string (an alias for ``update_query()`` method). - `#435 `_ - - Allow use of sequences such as ``list`` and ``tuple`` in the values - of a mapping such as ``dict`` to represent that a key has many values:: - - url = URL("http://example.com") - assert url.with_query({"a": [1, 2]}) == URL("http://example.com/?a=1&a=2") - - `#443 `_ - - Support URL.build() with scheme and path (creates a relative URL). - `#464 `_ - - Cache slow IDNA encode/decode calls. - `#476 `_ - - Add ``@final`` / ``Final`` type hints - `#477 `_ - - Support URL authority/raw_authority properties and authority argument of ``URL.build()`` method. - `#478 `_ - - Hide the library implementation details, make the exposed public list very clean. - `#483 `_ - - - Bugfixes - -------- - - - Fix tests with newer Python (3.7.6, 3.8.1 and 3.9.0+). - `#409 `_ - - Fix a bug where query component, passed in a form of mapping or sequence, is unquoted in unexpected way. - `#426 `_ - - Hide `Query` and `QueryVariable` type aliases in `__init__.pyi`, now they are prefixed with underscore. - `#431 `_ - - Keep ipv6 brackets after updating port/user/password. - `#451 `_ - - - ---- - - - 1.4.2 (2019-12-05) - ================== - - Features - -------- - - - Workaround for missing `str.isascii()` in Python 3.6 - `#389 `_ - - - ---- - - - 1.4.1 (2019-11-29) - ================== - - * Fix regression, make the library work on Python 3.5 and 3.6 again. - - 1.4.0 (2019-11-29) - ================== - - * Distinguish an empty password in URL from a password not provided at all (#262) - - * Fixed annotations for optional parameters of ``URL.build`` (#309) - - * Use None as default value of ``user`` parameter of ``URL.build`` (#309) - - * Enforce building C Accelerated modules when installing from source tarball, use - ``YARL_NO_EXTENSIONS`` environment variable for falling back to (slower) Pure Python - implementation (#329) - - * Drop Python 3.5 support - - * Fix quoting of plus in path by pure python version (#339) - - * Don't create a new URL if fragment is unchanged (#292) - - * Included in error msg the path that produces starting slash forbidden error (#376) - - * Skip slow IDNA encoding for ASCII-only strings (#387) - - - 1.3.0 (2018-12-11) - ================== - - * Fix annotations for ``query`` parameter (#207) - - * An incoming query sequence can have int variables (the same as for - Mapping type) (#208) - - * Add ``URL.explicit_port`` property (#218) - - * Give a friendlier error when port cant be converted to int (#168) - - * ``bool(URL())`` now returns ``False`` (#272) - - 1.2.6 (2018-06-14) - ================== - - * Drop Python 3.4 trove classifier (#205) - - 1.2.5 (2018-05-23) - ================== - - * Fix annotations for ``build`` (#199) - - 1.2.4 (2018-05-08) - ================== - - * Fix annotations for ``cached_property`` (#195) - - 1.2.3 (2018-05-03) - ================== - - * Accept ``str`` subclasses in ``URL`` constructor (#190) - - 1.2.2 (2018-05-01) - ================== - - * Fix build - - 1.2.1 (2018-04-30) - ================== - - * Pin minimal required Python to 3.5.3 (#189) - - 1.2.0 (2018-04-30) - ================== - - * Forbid inheritance, replace ``__init__`` with ``__new__`` (#171) - - * Support PEP-561 (provide type hinting marker) (#182) - - 1.1.1 (2018-02-17) - ================== - - * Fix performance regression: don't encode enmpty netloc (#170) - - 1.1.0 (2018-01-21) - ================== - - * Make pure Python quoter consistent with Cython version (#162) - - 1.0.0 (2018-01-15) - ================== - - * Use fast path if quoted string does not need requoting (#154) - - * Speed up quoting/unquoting by ``_Quoter`` and ``_Unquoter`` classes (#155) - - * Drop ``yarl.quote`` and ``yarl.unquote`` public functions (#155) - - * Add custom string writer, reuse static buffer if available (#157) - Code is 50-80 times faster than Pure Python version (was 4-5 times faster) - - * Don't recode IP zone (#144) - - * Support ``encoded=True`` in ``yarl.URL.build()`` (#158) - - * Fix updating query with multiple keys (#160) - - 0.18.0 (2018-01-10) - =================== - - * Fallback to IDNA 2003 if domain name is not IDNA 2008 compatible (#152) - - 0.17.0 (2017-12-30) - =================== - - * Use IDNA 2008 for domain name processing (#149) - - 0.16.0 (2017-12-07) - =================== - - * Fix raising ``TypeError`` by ``url.query_string()`` after - ``url.with_query({})`` (empty mapping) (#141) - - 0.15.0 (2017-11-23) - =================== - - * Add ``raw_path_qs`` attribute (#137) - - 0.14.2 (2017-11-14) - =================== - - * Restore ``strict`` parameter as no-op in ``quote`` / ``unquote`` - - 0.14.1 (2017-11-13) - =================== - - * Restore ``strict`` parameter as no-op for sake of compatibility with - aiohttp 2.2 - - 0.14.0 (2017-11-11) - =================== - - * Drop strict mode (#123) - - * Fix ``"ValueError: Unallowed PCT %"`` when there's a ``"%"`` in the url (#124) - - 0.13.0 (2017-10-01) - =================== - - * Document ``encoded`` parameter (#102) - - * Support relative urls like ``'?key=value'`` (#100) - - * Unsafe encoding for QS fixed. Encode ``;`` char in value param (#104) - - * Process passwords without user names (#95) - - 0.12.0 (2017-06-26) - =================== - - * Properly support paths without leading slash in ``URL.with_path()`` (#90) - - * Enable type annotation checks - - 0.11.0 (2017-06-26) - =================== - - * Normalize path (#86) - - * Clear query and fragment parts in ``.with_path()`` (#85) - - 0.10.3 (2017-06-13) - =================== - - * Prevent double URL args unquoting (#83) - - 0.10.2 (2017-05-05) - =================== - - * Unexpected hash behaviour (#75) - - - 0.10.1 (2017-05-03) - =================== - - * Unexpected compare behaviour (#73) - - * Do not quote or unquote + if not a query string. (#74) - - - 0.10.0 (2017-03-14) - =================== - - * Added ``URL.build`` class method (#58) - - * Added ``path_qs`` attribute (#42) - - - 0.9.8 (2017-02-16) - ================== - - * Do not quote ``:`` in path - - - 0.9.7 (2017-02-16) - ================== - - * Load from pickle without _cache (#56) - - * Percent-encoded pluses in path variables become spaces (#59) - - - 0.9.6 (2017-02-15) - ================== - - * Revert backward incompatible change (BaseURL) - - - 0.9.5 (2017-02-14) - ================== - - * Fix BaseURL rich comparison support - - - 0.9.4 (2017-02-14) - ================== - - * Use BaseURL - - - 0.9.3 (2017-02-14) - ================== - - * Added BaseURL - - - 0.9.2 (2017-02-08) - ================== - - * Remove debug print - - - 0.9.1 (2017-02-07) - ================== - - * Do not lose tail chars (#45) - - - 0.9.0 (2017-02-07) - ================== - - * Allow to quote ``%`` in non strict mode (#21) - - * Incorrect parsing of query parameters with %3B (;) inside (#34) - - * Fix core dumps (#41) - - * tmpbuf - compiling error (#43) - - * Added ``URL.update_path()`` method - - * Added ``URL.update_query()`` method (#47) - - - 0.8.1 (2016-12-03) - ================== - - * Fix broken aiohttp: revert back ``quote`` / ``unquote``. - - - 0.8.0 (2016-12-03) - ================== - - * Support more verbose error messages in ``.with_query()`` (#24) - - * Don't percent-encode ``@`` and ``:`` in path (#32) - - * Don't expose ``yarl.quote`` and ``yarl.unquote``, these functions are - part of private API - - 0.7.1 (2016-11-18) - ================== - - * Accept not only ``str`` but all classes inherited from ``str`` also (#25) - - 0.7.0 (2016-11-07) - ================== - - * Accept ``int`` as value for ``.with_query()`` - - 0.6.0 (2016-11-07) - ================== - - * Explicitly use UTF8 encoding in setup.py (#20) - * Properly unquote non-UTF8 strings (#19) - - 0.5.3 (2016-11-02) - ================== - - * Don't use namedtuple fields but indexes on URL construction - - 0.5.2 (2016-11-02) - ================== - - * Inline ``_encode`` class method - - 0.5.1 (2016-11-02) - ================== - - * Make URL construction faster by removing extra classmethod calls - - 0.5.0 (2016-11-02) - ================== - - * Add cython optimization for quoting/unquoting - * Provide binary wheels - - 0.4.3 (2016-09-29) - ================== - - * Fix typing stubs - - 0.4.2 (2016-09-29) - ================== - - * Expose ``quote()`` and ``unquote()`` as public API - - 0.4.1 (2016-09-28) - ================== - - * Support empty values in query (``'/path?arg'``) - - 0.4.0 (2016-09-27) - ================== - - * Introduce ``relative()`` (#16) - - 0.3.2 (2016-09-27) - ================== - - * Typo fixes #15 - - 0.3.1 (2016-09-26) - ================== - - * Support sequence of pairs as ``with_query()`` parameter - - 0.3.0 (2016-09-26) - ================== - - * Introduce ``is_default_port()`` - - 0.2.1 (2016-09-26) - ================== - - * Raise ValueError for URLs like 'http://:8080/' - - 0.2.0 (2016-09-18) - ================== - - * Avoid doubling slashes when joining paths (#13) - - * Appending path starting from slash is forbidden (#12) - - 0.1.4 (2016-09-09) - ================== - - * Add kwargs support for ``with_query()`` (#10) - - 0.1.3 (2016-09-07) - ================== - - * Document ``with_query()``, ``with_fragment()`` and ``origin()`` - - * Allow ``None`` for ``with_query()`` and ``with_fragment()`` - - 0.1.2 (2016-09-07) - ================== - - * Fix links, tune docs theme. - - 0.1.1 (2016-09-06) - ================== - - * Update README, old version used obsolete API - - 0.1.0 (2016-09-06) - ================== - - * The library was deeply refactored, bytes are gone away but all - accepted strings are encoded if needed. - - 0.0.1 (2016-08-30) - ================== - - * The first release. - -Platform: UNKNOWN -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Intended Audience :: Developers -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Topic :: Internet :: WWW/HTTP -Requires-Python: >=3.6 -Description-Content-Type: text/x-rst diff --git a/third_party/python/yarl/yarl.egg-info/SOURCES.txt b/third_party/python/yarl/yarl.egg-info/SOURCES.txt deleted file mode 100644 index 383d95918be9..000000000000 --- a/third_party/python/yarl/yarl.egg-info/SOURCES.txt +++ /dev/null @@ -1,42 +0,0 @@ -CHANGES.rst -LICENSE -MANIFEST.in -README.rst -pyproject.toml -setup.cfg -setup.py -docs/Makefile -docs/api.rst -docs/conf.py -docs/index.rst -docs/make.bat -docs/spelling_wordlist.txt -docs/yarl-icon-128x128.xcf -docs/_static/yarl-icon-128x128.png -docs/_templates/about.html -tests/test_cache.py -tests/test_cached_property.py -tests/test_normalize_path.py -tests/test_pickle.py -tests/test_quoting.py -tests/test_update_query.py -tests/test_url.py -tests/test_url_build.py -tests/test_url_cmp_and_hash.py -tests/test_url_parsing.py -tests/test_url_query.py -tests/test_url_update_netloc.py -yarl/__init__.py -yarl/__init__.pyi -yarl/_quoting.py -yarl/_quoting_c.c -yarl/_quoting_c.pyi -yarl/_quoting_c.pyx -yarl/_quoting_py.py -yarl/_url.py -yarl/py.typed -yarl.egg-info/PKG-INFO -yarl.egg-info/SOURCES.txt -yarl.egg-info/dependency_links.txt -yarl.egg-info/requires.txt -yarl.egg-info/top_level.txt \ No newline at end of file diff --git a/third_party/python/yarl/yarl.egg-info/dependency_links.txt b/third_party/python/yarl/yarl.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/third_party/python/yarl/yarl.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/python/yarl/yarl.egg-info/requires.txt b/third_party/python/yarl/yarl.egg-info/requires.txt deleted file mode 100644 index bb40947df663..000000000000 --- a/third_party/python/yarl/yarl.egg-info/requires.txt +++ /dev/null @@ -1,5 +0,0 @@ -multidict>=4.0 -idna>=2.0 - -[:python_version < "3.8"] -typing_extensions>=3.7.4 diff --git a/third_party/python/yarl/yarl.egg-info/top_level.txt b/third_party/python/yarl/yarl.egg-info/top_level.txt deleted file mode 100644 index e93e8bddefb1..000000000000 --- a/third_party/python/yarl/yarl.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -yarl diff --git a/tools/tryselect/selectors/chooser/requirements.txt b/tools/tryselect/selectors/chooser/requirements.txt index de2e3e5b1703..642274f337e3 100644 --- a/tools/tryselect/selectors/chooser/requirements.txt +++ b/tools/tryselect/selectors/chooser/requirements.txt @@ -10,9 +10,9 @@ Werkzeug==0.14.1 \ itsdangerous==1.1.0 \ --hash=sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19 \ --hash=sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749 -Jinja2==2.10.1 \ - --hash=sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b \ - --hash=sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013 +Jinja2==2.10 \ + --hash=sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd \ + --hash=sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4 MarkupSafe==1.1.1 \ --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \ --hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \