[M120 Migration] Fix build with python 3.7 environment 25/318725/2
authorSatyaveer Singh <satyaveer.s@samsung.com>
Mon, 7 Oct 2024 10:50:23 +0000 (16:20 +0530)
committerBot Blink <blinkbot@samsung.com>
Thu, 10 Oct 2024 03:35:33 +0000 (03:35 +0000)
Currently, chromium is considered to be built only in the python 3.9
version environment, so the necessary packages have been addded to
build with the python 3.7 version.

Added packages: importlib_metadata, typing_extensions, and zipp

References:
1) https://review.tizen.org/gerrit/294968
2) https://review.tizen.org/gerrit/296060

Change-Id: I24a27bfed6c833ccd96316ea29e5a77e138263b6
Signed-off-by: Satyaveer Singh <satyaveer.s@samsung.com>
78 files changed:
third_party/blink/renderer/bindings/scripts/bind_gen/__init__.py
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.editorconfig [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.github/FUNDING.yml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.github/dependabot.yml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.github/workflows/main.yml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.gitignore [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.pre-commit-config.yaml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.readthedocs.yaml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/LICENSE [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/NEWS.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/README.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/conftest.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/__init__.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/api.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/conf.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/history.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/index.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/migration.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/using.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/exercises.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/__init__.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_adapters.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_collections.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_compat.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_functools.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_itertools.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_meta.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_py39compat.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_text.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/py.typed [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/mypy.ini [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/newsfragments/+drop-py37.feature.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example/example/__init__.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example/setup.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example2/example2/__init__.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example2/pyproject.toml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/pyproject.toml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/pytest.ini [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/setup.cfg [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/__init__.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/_context.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/_path.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/__init__.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example-21.12-py3-none-any.whl [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example-21.12-py3.6.egg [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example2-1.0.0-py3-none-any.whl [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/fixtures.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/py39compat.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_api.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_integration.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_main.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_py39compat.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_zip.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/towncrier.toml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tox.ini [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/typing_extensions/LICENSE [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/typing_extensions/PKG-INFO [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/typing_extensions/pyproject.toml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/typing_extensions/src/typing_extensions.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/.flake8 [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/.pre-commit-config.yaml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/.readthedocs.yml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/.travis.yml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/CHANGES.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/LICENSE [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/README.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/appveyor.yml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/conftest.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/docs/conf.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/docs/history.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/docs/index.rst [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/pyproject.toml [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/setup.cfg [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/setup.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/skeleton.md [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/test_zipp.py [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/tox.ini [new file with mode: 0644]
third_party/wpt_tools/wpt/tools/third_party/zipp/zipp.py [new file with mode: 0644]

index 5b91787e546bd63cee1e2409c9eaaa9cfebd4333..1a23199b070a05ad65ad6f6ba118e58654bea82e 100644 (file)
@@ -25,6 +25,12 @@ def _setup_sys_path():
         # //third_party/mako/mako/mako
         os.path.join(root_dir, 'third_party', 'mako', 'mako'),
     )
+    if sys.version_info < (3, 8):
+        extra_module_dirs = (
+            # //third_party/wpt_tools/wpt/tools/third_party
+            os.path.join(root_dir, 'third_party', 'wpt_tools', 'wpt', 'tools', 'third_party'),
+        )
+        module_dirs += extra_module_dirs
     for module_dir in reversed(module_dirs):
         # Preserve sys.path[0] as is.
         # https://docs.python.org/3/library/sys.html?highlight=path[0]#sys.path
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.editorconfig b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.editorconfig
new file mode 100644 (file)
index 0000000..304196f
--- /dev/null
@@ -0,0 +1,19 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = tab
+indent_size = 4
+insert_final_newline = true
+end_of_line = lf
+
+[*.py]
+indent_style = space
+max_line_length = 88
+
+[*.{yml,yaml}]
+indent_style = space
+indent_size = 2
+
+[*.rst]
+indent_style = space
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.github/FUNDING.yml b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.github/FUNDING.yml
new file mode 100644 (file)
index 0000000..5cbfe04
--- /dev/null
@@ -0,0 +1 @@
+tidelift: pypi/importlib-metadata
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.github/dependabot.yml b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.github/dependabot.yml
new file mode 100644 (file)
index 0000000..89ff339
--- /dev/null
@@ -0,0 +1,8 @@
+version: 2
+updates:
+  - package-ecosystem: "pip"
+    directory: "/"
+    schedule:
+      interval: "daily"
+    allow:
+      - dependency-type: "all"
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.github/workflows/main.yml b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.github/workflows/main.yml
new file mode 100644 (file)
index 0000000..e9ad36b
--- /dev/null
@@ -0,0 +1,150 @@
+name: tests
+
+on: [push, pull_request]
+
+permissions:
+  contents: read
+
+env:
+  # Environment variables to support color support (jaraco/skeleton#66):
+  # Request colored output from CLI tools supporting it. Different tools
+  # interpret the value differently. For some, just being set is sufficient.
+  # For others, it must be a non-zero integer. For yet others, being set
+  # to a non-empty value is sufficient. For tox, it must be one of
+  # <blank>, 0, 1, false, no, off, on, true, yes. The only enabling value
+  # in common is "1".
+  FORCE_COLOR: 1
+  # MyPy's color enforcement (must be a non-zero number)
+  MYPY_FORCE_COLOR: -42
+  # Recognized by the `py` package, dependency of `pytest` (must be "1")
+  PY_COLORS: 1
+  # Make tox-wrapped tools see color requests
+  TOX_TESTENV_PASSENV: >-
+    FORCE_COLOR
+    MYPY_FORCE_COLOR
+    NO_COLOR
+    PY_COLORS
+    PYTEST_THEME
+    PYTEST_THEME_MODE
+
+  # Suppress noisy pip warnings
+  PIP_DISABLE_PIP_VERSION_CHECK: 'true'
+  PIP_NO_PYTHON_VERSION_WARNING: 'true'
+  PIP_NO_WARN_SCRIPT_LOCATION: 'true'
+
+  # Disable the spinner, noise in GHA; TODO(webknjaz): Fix this upstream
+  # Must be "1".
+  TOX_PARALLEL_NO_SPINNER: 1
+
+
+jobs:
+  test:
+    strategy:
+      matrix:
+        python:
+        - "3.8"
+        - "3.11"
+        - "3.12"
+        platform:
+        - ubuntu-latest
+        - macos-latest
+        - windows-latest
+        include:
+        - python: "3.9"
+          platform: ubuntu-latest
+        - python: "3.10"
+          platform: ubuntu-latest
+        - python: pypy3.9
+          platform: ubuntu-latest
+        - platform: ubuntu-latest
+          python: "3.8"
+        - platform: ubuntu-latest
+          python: "3.9"
+    runs-on: ${{ matrix.platform }}
+    continue-on-error: ${{ matrix.python == '3.12' }}
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          # fetch all branches and tags (to get tags for versioning)
+          # ref actions/checkout#448
+          fetch-depth: 0
+      - name: Setup Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: ${{ matrix.python }}
+          allow-prereleases: true
+      - name: Install tox
+        run: |
+          python -m pip install tox
+      - name: Run tests
+        run: tox
+
+  docs:
+    runs-on: ubuntu-latest
+    env:
+      TOXENV: docs
+    steps:
+      - uses: actions/checkout@v3
+      - name: Setup Python
+        uses: actions/setup-python@v4
+      - name: Install tox
+        run: |
+          python -m pip install tox
+      - name: Run tests
+        run: tox
+
+  check:  # This job does nothing and is only used for the branch protection
+    if: always()
+
+    needs:
+    - test
+    - docs
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Decide whether the needed jobs succeeded or failed
+      uses: re-actors/alls-green@release/v1
+      with:
+        jobs: ${{ toJSON(needs) }}
+  
+  diffcov:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+      - name: Setup Python
+        uses: actions/setup-python@v2
+        with:
+          python-version: 3.9
+      - name: Install tox
+        run: |
+          python -m pip install tox
+      - name: Evaluate coverage
+        run: tox
+        env:
+          TOXENV: diffcov
+
+  release:
+    permissions:
+      contents: write
+    needs:
+    - check
+    if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v3
+      - name: Setup Python
+        uses: actions/setup-python@v4
+        with:
+          python-version: 3.11-dev
+      - name: Install tox
+        run: |
+          python -m pip install tox
+      - name: Release
+        run: tox -e release
+        env:
+          TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.gitignore b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.gitignore
new file mode 100644 (file)
index 0000000..ae864d6
--- /dev/null
@@ -0,0 +1,13 @@
+build
+/coverage.xml
+/diffcov.html
+htmlcov
+importlib_metadata.egg-info
+.mypy_cache
+/.coverage
+/.DS_Store
+artifacts
+.eggs
+.doctrees
+dist
+pip-wheel-metadata
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.pre-commit-config.yaml b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.pre-commit-config.yaml
new file mode 100644 (file)
index 0000000..af50201
--- /dev/null
@@ -0,0 +1,5 @@
+repos:
+- repo: https://github.com/psf/black
+  rev: 22.6.0
+  hooks:
+  - id: black
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.readthedocs.yaml b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/.readthedocs.yaml
new file mode 100644 (file)
index 0000000..053c728
--- /dev/null
@@ -0,0 +1,12 @@
+version: 2
+python:
+  install:
+  - path: .
+    extra_requirements:
+      - docs
+
+# required boilerplate readthedocs/readthedocs.org#10401
+build:
+  os: ubuntu-22.04
+  tools:
+    python: "3"
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/LICENSE b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/LICENSE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -0,0 +1,202 @@
+
+                                 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
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/NEWS.rst b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/NEWS.rst
new file mode 100644 (file)
index 0000000..255bbd3
--- /dev/null
@@ -0,0 +1,843 @@
+v6.7.0
+======
+
+* #453: When inferring top-level names that are importable for
+  distributions in ``package_distributions``, now symlinks to
+  other directories are honored.
+
+v6.6.0
+======
+
+* #449: Expanded type annotations.
+
+v6.5.1
+======
+
+* python/cpython#103661: Removed excess error suppression in
+  ``_read_files_egginfo_installed`` and fixed path handling
+  on Windows.
+
+v6.5.0
+======
+
+* #422: Removed ABC metaclass from ``Distribution`` and instead
+  deprecated construction of ``Distribution`` objects without
+  concrete methods.
+
+v6.4.1
+======
+
+* Updated docs with tweaks from upstream CPython.
+
+v6.4.0
+======
+
+* Consolidated some behaviors in tests around ``_path``.
+* Added type annotation for ``Distribution.read_text``.
+
+v6.3.0
+======
+
+* #115: Support ``installed-files.txt`` for ``Distribution.files``
+  when present.
+
+v6.2.1
+======
+
+* #442: Fixed issue introduced in v6.1.0 where non-importable
+  names (metadata dirs) began appearing in
+  ``packages_distributions``.
+
+v6.2.0
+======
+
+* #384: ``PackageMetadata`` now stipulates an additional ``get``
+  method allowing for easy querying of metadata keys that may not
+  be present.
+
+v6.1.0
+======
+
+* #428: ``packages_distributions`` now honors packages and modules
+  with Python modules that not ``.py`` sources (e.g. ``.pyc``,
+  ``.so``).
+
+v6.0.1
+======
+
+* #434: Expand protocol for ``PackageMetadata.get_all`` to match
+  the upstream implementation of ``email.message.Message.get_all``
+  in python/typeshed#9620.
+
+v6.0.0
+======
+
+* #419: Declared ``Distribution`` as an abstract class, enforcing
+  definition of abstract methods in instantiated subclasses. It's no
+  longer possible to instantiate a ``Distribution`` or any subclasses
+  unless they define the abstract methods.
+
+  Please comment in the issue if this change breaks any projects.
+  This change will likely be rolled back if it causes significant
+  disruption.
+
+v5.2.0
+======
+
+* #371: Deprecated expectation that ``PackageMetadata.__getitem__``
+  will return ``None`` for missing keys. In the future, it will raise a
+  ``KeyError``.
+
+v5.1.0
+======
+
+* #415: Instrument ``SimplePath`` with generic support.
+
+v5.0.0
+======
+
+* #97, #284, #300: Removed compatibility shims for deprecated entry
+  point interfaces.
+
+v4.13.0
+=======
+
+* #396: Added compatibility for ``PathDistributions`` originating
+  from Python 3.8 and 3.9.
+
+v4.12.0
+=======
+
+* py-93259: Now raise ``ValueError`` when ``None`` or an empty
+  string are passed to ``Distribution.from_name`` (and other
+  callers).
+
+v4.11.4
+=======
+
+* #379: In ``PathDistribution._name_from_stem``, avoid including
+  parts of the extension in the result.
+* #381: In ``PathDistribution._normalized_name``, ensure names
+  loaded from the stem of the filename are also normalized, ensuring
+  duplicate entry points by packages varying only by non-normalized
+  name are hidden.
+
+v4.11.3
+=======
+
+* #372: Removed cast of path items in FastPath, not needed.
+
+v4.11.2
+=======
+
+* #369: Fixed bug where ``EntryPoint.extras`` was returning
+  match objects and not the extras strings.
+
+v4.11.1
+=======
+
+* #367: In ``Distribution.requires`` for egg-info, if ``requires.txt``
+  is empty, return an empty list.
+
+v4.11.0
+=======
+
+* bpo-46246: Added ``__slots__`` to ``EntryPoints``.
+
+v4.10.2
+=======
+
+* #365 and bpo-46546: Avoid leaking ``method_name`` in
+  ``DeprecatedList``.
+
+v4.10.1
+=======
+
+v2.1.3
+=======
+
+* #361: Avoid potential REDoS in ``EntryPoint.pattern``.
+
+v4.10.0
+=======
+
+* #354: Removed ``Distribution._local`` factory. This
+  functionality was created as a demonstration of the
+  possible implementation. Now, the
+  `pep517 <https://pypi.org/project/pep517>`_ package
+  provides this functionality directly through
+  `pep517.meta.load <https://github.com/pypa/pep517/blob/a942316305395f8f757f210e2b16f738af73f8b8/pep517/meta.py#L63-L73>`_.
+
+v4.9.0
+======
+
+* Require Python 3.7 or later.
+
+v4.8.3
+======
+
+* #357: Fixed requirement generation from egg-info when a
+  URL requirement is given.
+
+v4.8.2
+======
+
+v2.1.2
+======
+
+* #353: Fixed discovery of distributions when path is empty.
+
+v4.8.1
+======
+
+* #348: Restored support for ``EntryPoint`` access by item,
+  deprecating support in the process. Users are advised
+  to use direct member access instead of item-based access::
+
+  - ep[0] -> ep.name
+  - ep[1] -> ep.value
+  - ep[2] -> ep.group
+  - ep[:] -> ep.name, ep.value, ep.group
+
+v4.8.0
+======
+
+* #337: Rewrote ``EntryPoint`` as a simple class, still
+  immutable and still with the attributes, but without any
+  expectation for ``namedtuple`` functionality such as
+  ``_asdict``.
+
+v4.7.1
+======
+
+* #344: Fixed regression in ``packages_distributions`` when
+  neither top-level.txt nor a files manifest is present.
+
+v4.7.0
+======
+
+* #330: In ``packages_distributions``, now infer top-level
+  names from ``.files()`` when a ``top-level.txt``
+  (Setuptools-specific metadata) is not present.
+
+v4.6.4
+======
+
+* #334: Correct ``SimplePath`` protocol to match ``pathlib``
+  protocol for ``__truediv__``.
+
+v4.6.3
+======
+
+* Moved workaround for #327 to ``_compat`` module.
+
+v4.6.2
+======
+
+* bpo-44784: Avoid errors in test suite when
+  DeprecationWarnings are treated as errors.
+
+v4.6.1
+======
+
+* #327: Deprecation warnings now honor call stack variance
+  on PyPy.
+
+v4.6.0
+======
+
+* #326: Performance tests now rely on
+  `pytest-perf <https://pypi.org/project/pytest-perf>`_.
+  To disable these tests, which require network access
+  and a git checkout, pass ``-p no:perf`` to pytest.
+
+v4.5.0
+======
+
+* #319: Remove ``SelectableGroups`` deprecation exception
+  for flake8.
+
+v4.4.0
+======
+
+* #300: Restore compatibility in the result from
+  ``Distribution.entry_points`` (``EntryPoints``) to honor
+  expectations in older implementations and issuing
+  deprecation warnings for these cases:
+
+  - ``EntryPoints`` objects are once again mutable, allowing
+    for ``sort()`` and other list-based mutation operations.
+    Avoid deprecation warnings by casting to a
+    mutable sequence (e.g.
+    ``list(dist.entry_points).sort()``).
+
+  - ``EntryPoints`` results once again allow
+    for access by index. To avoid deprecation warnings,
+    cast the result to a Sequence first
+    (e.g. ``tuple(dist.entry_points)[0]``).
+
+v4.3.1
+======
+
+* #320: Fix issue where normalized name for eggs was
+  incorrectly solicited, leading to metadata being
+  unavailable for eggs.
+
+v4.3.0
+======
+
+* #317: De-duplication of distributions no longer requires
+  loading the full metadata for ``PathDistribution`` objects,
+  entry point loading performance by ~10x.
+
+v4.2.0
+======
+
+* Prefer f-strings to ``.format`` calls.
+
+v4.1.0
+======
+
+* #312: Add support for metadata 2.2 (``Dynamic`` field).
+
+* #315: Add ``SimplePath`` protocol for interface clarity
+  in ``PathDistribution``.
+
+v4.0.1
+======
+
+* #306: Clearer guidance about compatibility in readme.
+
+v4.0.0
+======
+
+* #304: ``PackageMetadata`` as returned by ``metadata()``
+  and ``Distribution.metadata()`` now provides normalized
+  metadata honoring PEP 566:
+
+  - If a long description is provided in the payload of the
+    RFC 822 value, it can be retrieved as the ``Description``
+    field.
+  - Any multi-line values in the metadata will be returned as
+    such.
+  - For any multi-line values, line continuation characters
+    are removed. This backward-incompatible change means
+    that any projects relying on the RFC 822 line continuation
+    characters being present must be tolerant to them having
+    been removed.
+  - Add a ``json`` property that provides the metadata
+    converted to a JSON-compatible form per PEP 566.
+
+
+v3.10.1
+=======
+
+* Minor tweaks from CPython.
+
+v3.10.0
+=======
+
+* #295: Internal refactoring to unify section parsing logic.
+
+v3.9.1
+======
+
+* #296: Exclude 'prepare' package.
+* #297: Fix ValueError when entry points contains comments.
+
+v3.9.0
+======
+
+* Use of Mapping (dict) interfaces on ``SelectableGroups``
+  is now flagged as deprecated. Instead, users are advised
+  to use the select interface for future compatibility.
+
+  Suppress the warning with this filter:
+  ``ignore:SelectableGroups dict interface``.
+
+  Or with this invocation in the Python environment:
+  ``warnings.filterwarnings('ignore', 'SelectableGroups dict interface')``.
+
+  Preferably, switch to the ``select`` interface introduced
+  in 3.7.0. See the
+  `entry points documentation <https://importlib-metadata.readthedocs.io/en/latest/using.html#entry-points>`_ and changelog for the 3.6
+  release below for more detail.
+
+  For some use-cases, especially those that rely on
+  ``importlib.metadata`` in Python 3.8 and 3.9 or
+  those relying on older ``importlib_metadata`` (especially
+  on Python 3.5 and earlier),
+  `backports.entry_points_selectable <https://pypi.org/project/backports.entry_points_selectable>`_
+  was created to ease the transition. Please have a look
+  at that project if simply relying on importlib_metadata 3.6+
+  is not straightforward. Background in #298.
+
+* #283: Entry point parsing no longer relies on ConfigParser
+  and instead uses a custom, one-pass parser to load the
+  config, resulting in a ~20% performance improvement when
+  loading entry points.
+
+v3.8.2
+======
+
+* #293: Re-enabled lazy evaluation of path lookup through
+  a FreezableDefaultDict.
+
+v3.8.1
+======
+
+* #293: Workaround for error in distribution search.
+
+v3.8.0
+======
+
+* #290: Add mtime-based caching for ``FastPath`` and its
+  lookups, dramatically increasing performance for repeated
+  distribution lookups.
+
+v3.7.3
+======
+
+* Docs enhancements and cleanup following review in
+  `GH-24782 <https://github.com/python/cpython/pull/24782>`_.
+
+v3.7.2
+======
+
+* Cleaned up cruft in entry_points docstring.
+
+v3.7.1
+======
+
+* Internal refactoring to facilitate ``entry_points() -> dict``
+  deprecation.
+
+v3.7.0
+======
+
+* #131: Added ``packages_distributions`` to conveniently
+  resolve a top-level package or module to its distribution(s).
+
+v3.6.0
+======
+
+* #284: Introduces new ``EntryPoints`` object, a tuple of
+  ``EntryPoint`` objects but with convenience properties for
+  selecting and inspecting the results:
+
+  - ``.select()`` accepts ``group`` or ``name`` keyword
+    parameters and returns a new ``EntryPoints`` tuple
+    with only those that match the selection.
+  - ``.groups`` property presents all of the group names.
+  - ``.names`` property presents the names of the entry points.
+  - Item access (e.g. ``eps[name]``) retrieves a single
+    entry point by name.
+
+  ``entry_points`` now accepts "selection parameters",
+  same as ``EntryPoint.select()``.
+
+  ``entry_points()`` now provides a future-compatible
+  ``SelectableGroups`` object that supplies the above interface
+  (except item access) but remains a dict for compatibility.
+
+  In the future, ``entry_points()`` will return an
+  ``EntryPoints`` object for all entry points.
+
+  If passing selection parameters to ``entry_points``, the
+  future behavior is invoked and an ``EntryPoints`` is the
+  result.
+
+* #284: Construction of entry points using
+  ``dict([EntryPoint, ...])`` is now deprecated and raises
+  an appropriate DeprecationWarning and will be removed in
+  a future version.
+
+* #300: ``Distribution.entry_points`` now presents as an
+  ``EntryPoints`` object and access by index is no longer
+  allowed. If access by index is required, cast to a sequence
+  first.
+
+v3.5.0
+======
+
+* #280: ``entry_points`` now only returns entry points for
+  unique distributions (by name).
+
+v3.4.0
+======
+
+* #10: Project now declares itself as being typed.
+* #272: Additional performance enhancements to distribution
+  discovery.
+* #111: For PyPA projects, add test ensuring that
+  ``MetadataPathFinder._search_paths`` honors the needed
+  interface. Method is still private.
+
+v3.3.0
+======
+
+* #265: ``EntryPoint`` objects now expose a ``.dist`` object
+  referencing the ``Distribution`` when constructed from a
+  Distribution.
+
+v3.2.0
+======
+
+* The object returned by ``metadata()`` now has a
+  formally-defined protocol called ``PackageMetadata``
+  with declared support for the ``.get_all()`` method.
+  Fixes #126.
+
+v3.1.1
+======
+
+v2.1.1
+======
+
+* #261: Restored compatibility for package discovery for
+  metadata without version in the name and for legacy
+  eggs.
+
+v3.1.0
+======
+
+* Merge with 2.1.0.
+
+v2.1.0
+======
+
+* #253: When querying for package metadata, the lookup
+  now honors
+  `package normalization rules <https://packaging.python.org/specifications/recording-installed-packages/>`_.
+
+v3.0.0
+======
+
+* Require Python 3.6 or later.
+
+v2.0.0
+======
+
+* ``importlib_metadata`` no longer presents a
+  ``__version__`` attribute. Consumers wishing to
+  resolve the version of the package should query it
+  directly with
+  ``importlib_metadata.version('importlib-metadata')``.
+  Closes #71.
+
+v1.7.0
+======
+
+* ``PathNotFoundError`` now has a custom ``__str__``
+  mentioning "package metadata" being missing to help
+  guide users to the cause when the package is installed
+  but no metadata is present. Closes #124.
+
+v1.6.1
+======
+
+* Added ``Distribution._local()`` as a provisional
+  demonstration of how to load metadata for a local
+  package. Implicitly requires that
+  `pep517 <https://pypi.org/project/pep517>`_ is
+  installed. Ref #42.
+* Ensure inputs to FastPath are Unicode. Closes #121.
+* Tests now rely on ``importlib.resources.files`` (and
+  backport) instead of the older ``path`` function.
+* Support any iterable from ``find_distributions``.
+  Closes #122.
+
+v1.6.0
+======
+
+* Added ``module`` and ``attr`` attributes to ``EntryPoint``
+
+v1.5.2
+======
+
+* Fix redundant entries from ``FastPath.zip_children``.
+  Closes #117.
+
+v1.5.1
+======
+
+* Improve reliability and consistency of compatibility
+  imports for contextlib and pathlib when running tests.
+  Closes #116.
+
+v1.5.0
+======
+
+* Additional performance optimizations in FastPath now
+  saves an additional 20% on a typical call.
+* Correct for issue where PyOxidizer finder has no
+  ``__module__`` attribute. Closes #110.
+
+v1.4.0
+======
+
+* Through careful optimization, ``distribution()`` is
+  3-4x faster. Thanks to Antony Lee for the
+  contribution. Closes #95.
+
+* When searching through ``sys.path``, if any error
+  occurs attempting to list a path entry, that entry
+  is skipped, making the system much more lenient
+  to errors. Closes #94.
+
+v1.3.0
+======
+
+* Improve custom finders documentation. Closes #105.
+
+v1.2.0
+======
+
+* Once again, drop support for Python 3.4. Ref #104.
+
+v1.1.3
+======
+
+* Restored support for Python 3.4 due to improper version
+  compatibility declarations in the v1.1.0 and v1.1.1
+  releases. Closes #104.
+
+v1.1.2
+======
+
+* Repaired project metadata to correctly declare the
+  ``python_requires`` directive. Closes #103.
+
+v1.1.1
+======
+
+* Fixed ``repr(EntryPoint)`` on PyPy 3 also. Closes #102.
+
+v1.1.0
+======
+
+* Dropped support for Python 3.4.
+* EntryPoints are now pickleable. Closes #96.
+* Fixed ``repr(EntryPoint)`` on PyPy 2. Closes #97.
+
+v1.0.0
+======
+
+* Project adopts semver for versioning.
+
+* Removed compatibility shim introduced in 0.23.
+
+* For better compatibility with the stdlib implementation and to
+  avoid the same distributions being discovered by the stdlib and
+  backport implementations, the backport now disables the
+  stdlib DistributionFinder during initialization (import time).
+  Closes #91 and closes #100.
+
+0.23
+====
+
+* Added a compatibility shim to prevent failures on beta releases
+  of Python before the signature changed to accept the
+  "context" parameter on find_distributions. This workaround
+  will have a limited lifespan, not to extend beyond release of
+  Python 3.8 final.
+
+0.22
+====
+
+* Renamed ``package`` parameter to ``distribution_name``
+  as `recommended <https://bugs.python.org/issue34632#msg349423>`_
+  in the following functions: ``distribution``, ``metadata``,
+  ``version``, ``files``, and ``requires``. This
+  backward-incompatible change is expected to have little impact
+  as these functions are assumed to be primarily used with
+  positional parameters.
+
+0.21
+====
+
+* ``importlib.metadata`` now exposes the ``DistributionFinder``
+  metaclass and references it in the docs for extending the
+  search algorithm.
+* Add ``Distribution.at`` for constructing a Distribution object
+  from a known metadata directory on the file system. Closes #80.
+* Distribution finders now receive a context object that
+  supplies ``.path`` and ``.name`` properties. This change
+  introduces a fundamental backward incompatibility for
+  any projects implementing a ``find_distributions`` method
+  on a ``MetaPathFinder``. This new layer of abstraction
+  allows this context to be supplied directly or constructed
+  on demand and opens the opportunity for a
+  ``find_distributions`` method to solicit additional
+  context from the caller. Closes #85.
+
+0.20
+====
+
+* Clarify in the docs that calls to ``.files`` could return
+  ``None`` when the metadata is not present. Closes #69.
+* Return all requirements and not just the first for dist-info
+  packages. Closes #67.
+
+0.19
+====
+
+* Restrain over-eager egg metadata resolution.
+* Add support for entry points with colons in the name. Closes #75.
+
+0.18
+====
+
+* Parse entry points case sensitively.  Closes #68
+* Add a version constraint on the backport configparser package.  Closes #66
+
+0.17
+====
+
+* Fix a permission problem in the tests on Windows.
+
+0.16
+====
+
+* Don't crash if there exists an EGG-INFO directory on sys.path.
+
+0.15
+====
+
+* Fix documentation.
+
+0.14
+====
+
+* Removed ``local_distribution`` function from the API.
+  **This backward-incompatible change removes this
+  behavior summarily**. Projects should remove their
+  reliance on this behavior. A replacement behavior is
+  under review in the `pep517 project
+  <https://github.com/pypa/pep517>`_. Closes #42.
+
+0.13
+====
+
+* Update docstrings to match PEP 8. Closes #63.
+* Merged modules into one module. Closes #62.
+
+0.12
+====
+
+* Add support for eggs.  !65; Closes #19.
+
+0.11
+====
+
+* Support generic zip files (not just wheels).  Closes #59
+* Support zip files with multiple distributions in them.  Closes #60
+* Fully expose the public API in ``importlib_metadata.__all__``.
+
+0.10
+====
+
+* The ``Distribution`` ABC is now officially part of the public API.
+  Closes #37.
+* Fixed support for older single file egg-info formats.  Closes #43.
+* Fixed a testing bug when ``$CWD`` has spaces in the path.  Closes #50.
+* Add Python 3.8 to the ``tox`` testing matrix.
+
+0.9
+===
+
+* Fixed issue where entry points without an attribute would raise an
+  Exception.  Closes #40.
+* Removed unused ``name`` parameter from ``entry_points()``. Closes #44.
+* ``DistributionFinder`` classes must now be instantiated before
+  being placed on ``sys.meta_path``.
+
+0.8
+===
+
+* This library can now discover/enumerate all installed packages. **This
+  backward-incompatible change alters the protocol finders must
+  implement to support distribution package discovery.** Closes #24.
+* The signature of ``find_distributions()`` on custom installer finders
+  should now accept two parameters, ``name`` and ``path`` and
+  these parameters must supply defaults.
+* The ``entry_points()`` method no longer accepts a package name
+  but instead returns all entry points in a dictionary keyed by the
+  ``EntryPoint.group``. The ``resolve`` method has been removed. Instead,
+  call ``EntryPoint.load()``, which has the same semantics as
+  ``pkg_resources`` and ``entrypoints``.  **This is a backward incompatible
+  change.**
+* Metadata is now always returned as Unicode text regardless of
+  Python version. Closes #29.
+* This library can now discover metadata for a 'local' package (found
+  in the current-working directory). Closes #27.
+* Added ``files()`` function for resolving files from a distribution.
+* Added a new ``requires()`` function, which returns the requirements
+  for a package suitable for parsing by
+  ``packaging.requirements.Requirement``. Closes #18.
+* The top-level ``read_text()`` function has been removed.  Use
+  ``PackagePath.read_text()`` on instances returned by the ``files()``
+  function.  **This is a backward incompatible change.**
+* Release dates are now automatically injected into the changelog
+  based on SCM tags.
+
+0.7
+===
+
+* Fixed issue where packages with dashes in their names would
+  not be discovered. Closes #21.
+* Distribution lookup is now case-insensitive. Closes #20.
+* Wheel distributions can no longer be discovered by their module
+  name. Like Path distributions, they must be indicated by their
+  distribution package name.
+
+0.6
+===
+
+* Removed ``importlib_metadata.distribution`` function. Now
+  the public interface is primarily the utility functions exposed
+  in ``importlib_metadata.__all__``. Closes #14.
+* Added two new utility functions ``read_text`` and
+  ``metadata``.
+
+0.5
+===
+
+* Updated README and removed details about Distribution
+  class, now considered private. Closes #15.
+* Added test suite support for Python 3.4+.
+* Fixed SyntaxErrors on Python 3.4 and 3.5. !12
+* Fixed errors on Windows joining Path elements. !15
+
+0.4
+===
+
+* Housekeeping.
+
+0.3
+===
+
+* Added usage documentation.  Closes #8
+* Add support for getting metadata from wheels on ``sys.path``.  Closes #9
+
+0.2
+===
+
+* Added ``importlib_metadata.entry_points()``.  Closes #1
+* Added ``importlib_metadata.resolve()``.  Closes #12
+* Add support for Python 2.7.  Closes #4
+
+0.1
+===
+
+* Initial release.
+
+
+..
+   Local Variables:
+   mode: change-log-mode
+   indent-tabs-mode: nil
+   sentence-end-double-space: t
+   fill-column: 78
+   coding: utf-8
+   End:
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/README.rst b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/README.rst
new file mode 100644 (file)
index 0000000..f42e88d
--- /dev/null
@@ -0,0 +1,99 @@
+.. image:: https://img.shields.io/pypi/v/importlib_metadata.svg
+   :target: https://pypi.org/project/importlib_metadata
+
+.. image:: https://img.shields.io/pypi/pyversions/importlib_metadata.svg
+
+.. image:: https://github.com/python/importlib_metadata/workflows/tests/badge.svg
+   :target: https://github.com/python/importlib_metadata/actions?query=workflow%3A%22tests%22
+   :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+    :target: https://github.com/astral-sh/ruff
+    :alt: Ruff
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+   :target: https://github.com/psf/black
+   :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest
+   :target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2023-informational
+   :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/importlib-metadata
+   :target: https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=readme
+
+Library to access the metadata for a Python package.
+
+This package supplies third-party access to the functionality of
+`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_
+including improvements added to subsequent Python versions.
+
+
+Compatibility
+=============
+
+New features are introduced in this third-party library and later merged
+into CPython. The following table indicates which versions of this library
+were contributed to different versions in the standard library:
+
+.. list-table::
+   :header-rows: 1
+
+   * - importlib_metadata
+     - stdlib
+   * - 6.5
+     - 3.12
+   * - 4.13
+     - 3.11
+   * - 4.6
+     - 3.10
+   * - 1.4
+     - 3.8
+
+
+Usage
+=====
+
+See the `online documentation <https://importlib-metadata.readthedocs.io/>`_
+for usage details.
+
+`Finder authors
+<https://docs.python.org/3/reference/import.html#finders-and-loaders>`_ can
+also add support for custom package installers.  See the above documentation
+for details.
+
+
+Caveats
+=======
+
+This project primarily supports third-party packages installed by PyPA
+tools (or other conforming packages). It does not support:
+
+- Packages in the stdlib.
+- Packages installed without metadata.
+
+Project details
+===============
+
+ * Project home: https://github.com/python/importlib_metadata
+ * Report bugs at: https://github.com/python/importlib_metadata/issues
+ * Code hosting: https://github.com/python/importlib_metadata
+ * Documentation: https://importlib-metadata.readthedocs.io/
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=referral&utm_campaign=github>`_.
+
+Security Contact
+================
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/conftest.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/conftest.py
new file mode 100644 (file)
index 0000000..ab6c8ca
--- /dev/null
@@ -0,0 +1,25 @@
+import sys
+
+
+collect_ignore = [
+    # this module fails mypy tests because 'setup.py' matches './setup.py'
+    'prepare/example/setup.py',
+]
+
+
+def pytest_configure():
+    remove_importlib_metadata()
+
+
+def remove_importlib_metadata():
+    """
+    Because pytest imports importlib_metadata, the coverage
+    reports are broken (#322). So work around the issue by
+    undoing the changes made by pytest's import of
+    importlib_metadata (if any).
+    """
+    if sys.meta_path[-1].__class__.__name__ == 'MetadataPathFinder':
+        del sys.meta_path[-1]
+    for mod in list(sys.modules):
+        if mod.startswith('importlib_metadata'):
+            del sys.modules[mod]
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/__init__.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/api.rst b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/api.rst
new file mode 100644 (file)
index 0000000..02b389b
--- /dev/null
@@ -0,0 +1,11 @@
+=============
+API Reference
+=============
+
+``importlib_metadata`` module
+-----------------------------
+
+.. automodule:: importlib_metadata
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/conf.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/conf.py
new file mode 100644 (file)
index 0000000..134d753
--- /dev/null
@@ -0,0 +1,74 @@
+extensions = [
+    'sphinx.ext.autodoc',
+    'jaraco.packaging.sphinx',
+]
+
+master_doc = "index"
+html_theme = "furo"
+
+# Link dates and other references in the changelog
+extensions += ['rst.linker']
+link_files = {
+    '../NEWS.rst': dict(
+        using=dict(GH='https://github.com'),
+        replace=[
+            dict(
+                pattern=r'(Issue #|\B#)(?P<issue>\d+)',
+                url='{package_url}/issues/{issue}',
+            ),
+            dict(
+                pattern=r'(?m:^((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n)',
+                with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n',
+            ),
+            dict(
+                pattern=r'PEP[- ](?P<pep_number>\d+)',
+                url='https://peps.python.org/pep-{pep_number:0>4}/',
+            ),
+            dict(
+                pattern=r'(Python #|py-)(?P<python>\d+)',
+                url='https://github.com/python/cpython/issues/{python}',
+            ),
+        ],
+    )
+}
+
+# Be strict about any broken references
+nitpicky = True
+
+# Include Python intersphinx mapping to prevent failures
+# jaraco/skeleton#51
+extensions += ['sphinx.ext.intersphinx']
+intersphinx_mapping = {
+    'python': ('https://docs.python.org/3', None),
+}
+
+# Preserve authored syntax for defaults
+autodoc_preserve_defaults = True
+
+extensions += ['jaraco.tidelift']
+
+intersphinx_mapping.update(
+    importlib_resources=(
+        'https://importlib-resources.readthedocs.io/en/latest/',
+        None,
+    ),
+)
+
+intersphinx_mapping.update(
+    packaging=(
+        'https://packaging.python.org/en/latest/',
+        None,
+    ),
+)
+
+nitpick_ignore = [
+    # Workaround for #316
+    ('py:class', 'importlib_metadata.EntryPoints'),
+    ('py:class', 'importlib_metadata.PackagePath'),
+    ('py:class', 'importlib_metadata.SelectableGroups'),
+    ('py:class', 'importlib_metadata._meta._T'),
+    # Workaround for #435
+    ('py:class', '_T'),
+    # Other workarounds
+    ('py:class', 'importlib_metadata.DeprecatedNonAbstract'),
+]
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/history.rst b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/history.rst
new file mode 100644 (file)
index 0000000..5bdc232
--- /dev/null
@@ -0,0 +1,8 @@
+:tocdepth: 2
+
+.. _changes:
+
+History
+*******
+
+.. include:: ../NEWS (links).rst
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/index.rst b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/index.rst
new file mode 100644 (file)
index 0000000..a5bacd4
--- /dev/null
@@ -0,0 +1,39 @@
+Welcome to |project| documentation!
+===================================
+
+``importlib_metadata`` supplies a backport of :mod:`importlib.metadata`,
+enabling early access to features of future Python versions and making
+functionality available for older Python versions. Users are encouraged to
+use the Python standard library where suitable and fall back to
+this library for future compatibility. Developers looking for detailed API
+descriptions should refer to the standard library documentation.
+
+The documentation here includes a general :ref:`usage <using>` guide.
+
+
+.. toctree::
+   :maxdepth: 1
+
+   using
+   api
+   migration
+   history
+
+.. tidelift-referral-banner::
+
+
+Project details
+===============
+
+ * Project home: https://github.com/python/importlib_metadata
+ * Report bugs at: https://github.com/python/importlib_metadata/issues
+ * Code hosting: https://github.com/python/importlib_metadata
+ * Documentation: https://importlib-metadata.readthedocs.io/
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/migration.rst b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/migration.rst
new file mode 100644 (file)
index 0000000..3c70077
--- /dev/null
@@ -0,0 +1,84 @@
+.. _migration:
+
+=================
+ Migration guide
+=================
+
+The following guide will help you migrate common ``pkg_resources``
+APIs to ``importlib_metadata``. ``importlib_metadata`` aims to
+replace the following ``pkg_resources`` APIs:
+
+* ``pkg_resources.iter_entry_points()``
+* ``pkg_resources.require()``
+* convenience functions
+* ``pkg_resources.find_distributions()``
+* ``pkg_resources.get_distribution()``
+
+Other functionality from ``pkg_resources`` is replaced by other
+packages such as
+`importlib_resources <https://pypi.org/project/importlib_resources>`_
+and `packaging <https://pypi.org/project/packaging>`_.
+
+
+pkg_resources.iter_entry_points()
+=================================
+
+``importlib_metadata`` provides :ref:`entry-points`.
+
+Compatibility note: entry points provided by importlib_metadata
+do not have the following implicit behaviors found in those
+from ``pkg_resources``:
+
+- Each EntryPoint is not automatically validated to match. To
+  ensure each one is validated, invoke any property on the
+  object (e.g. ``ep.name``).
+
+- When invoking ``EntryPoint.load()``, no checks are performed
+  to ensure the declared extras are installed. If this behavior
+  is desired/required, it is left to the user to perform the
+  check and install any dependencies. See
+  `importlib_metadata#368 <https://github.com/python/importlib_metadata/issues/368>`_
+  for more details.
+
+pkg_resources.require()
+=======================
+
+``importlib_metadata`` does not provide support for dynamically
+discovering or requiring distributions nor does it provide any
+support for managing the "working set". Furthermore,
+``importlib_metadata`` assumes that only one version of a given
+distribution is discoverable at any time (no support for multi-version
+installs). Any projects that require the above behavior needs to
+provide that behavior independently.
+
+``importlib_metadata`` does aim to resolve metadata concerns late
+such that any dynamic changes to package availability should be
+reflected immediately.
+
+Convenience functions
+=====================
+
+In addition to the support for direct access to ``Distribution``
+objects (below), ``importlib_metadata`` presents some top-level
+functions for easy access to the most common metadata:
+
+- :ref:`metadata` queries the metadata fields from the distribution.
+- :ref:`version` provides quick access to the distribution version.
+- :ref:`requirements` presents the requirements of the distribution.
+- :ref:`files` provides file-like access to the data blobs backing
+  the metadata.
+
+pkg_resources.find_distributions()
+==================================
+
+``importlib_metadata`` provides functionality
+similar to ``find_distributions()``. Both ``distributions(...)`` and
+``Distribution.discover(...)`` return an iterable of :ref:`distributions`
+matching the indicated parameters.
+
+pkg_resources.get_distribution()
+=================================
+
+Similar to ``distributions``, the ``distribution()`` function provides
+access to a single distribution by name.
+
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/using.rst b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/docs/using.rst
new file mode 100644 (file)
index 0000000..17af34d
--- /dev/null
@@ -0,0 +1,381 @@
+.. _using:
+
+=================================
+ Using :mod:`!importlib_metadata`
+=================================
+
+``importlib_metadata`` is a library that provides access to
+the metadata of an installed :term:`packaging:Distribution Package`,
+such as its entry points
+or its top-level names (:term:`packaging:Import Package`\s, modules, if any).
+Built in part on Python's import system, this library
+intends to replace similar functionality in the `entry point
+API`_ and `metadata API`_ of ``pkg_resources``.  Along with
+:mod:`importlib.resources`,
+this package can eliminate the need to use the older and less efficient
+``pkg_resources`` package.
+
+``importlib_metadata`` operates on third-party *distribution packages*
+installed into Python's ``site-packages`` directory via tools such as
+`pip <https://pypi.org/project/pip/>`_.
+Specifically, it works with distributions with discoverable
+``dist-info`` or ``egg-info`` directories,
+and metadata defined by the :ref:`packaging:core-metadata`.
+
+.. important::
+
+   These are *not* necessarily equivalent to or correspond 1:1 with
+   the top-level *import package* names
+   that can be imported inside Python code.
+   One *distribution package* can contain multiple *import packages*
+   (and single modules),
+   and one top-level *import package*
+   may map to multiple *distribution packages*
+   if it is a namespace package.
+   You can use :ref:`package_distributions() <package-distributions>`
+   to get a mapping between them.
+
+By default, distribution metadata can live on the file system
+or in zip archives on
+:data:`sys.path`.  Through an extension mechanism, the metadata can live almost
+anywhere.
+
+
+.. seealso::
+
+   https://importlib-metadata.readthedocs.io/
+      The documentation for ``importlib_metadata``, which supplies a
+      backport of ``importlib.metadata``.
+      This includes an `API reference
+      <https://importlib-metadata.readthedocs.io/en/latest/api.html>`__
+      for this module's classes and functions,
+      as well as a `migration guide
+      <https://importlib-metadata.readthedocs.io/en/latest/migration.html>`__
+      for existing users of ``pkg_resources``.
+
+
+Overview
+========
+
+Let's say you wanted to get the version string for a
+:term:`packaging:Distribution Package` you've installed
+using ``pip``.  We start by creating a virtual environment and installing
+something into it::
+
+    $ python -m venv example
+    $ source example/bin/activate
+    (example) $ python -m pip install importlib_metadata
+    (example) $ python -m pip install wheel
+
+You can get the version string for ``wheel`` by running the following::
+
+    (example) $ python
+    >>> from importlib_metadata import version
+    >>> version('wheel')
+    '0.32.3'
+
+You can also get a collection of entry points selectable by properties of the EntryPoint (typically 'group' or 'name'), such as
+``console_scripts``, ``distutils.commands`` and others.  Each group contains a
+collection of :ref:`EntryPoint <entry-points>` objects.
+
+You can get the :ref:`metadata for a distribution <metadata>`::
+
+    >>> list(metadata('wheel'))
+    ['Metadata-Version', 'Name', 'Version', 'Summary', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', 'Project-URL', 'Project-URL', 'Project-URL', 'Keywords', 'Platform', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Requires-Python', 'Provides-Extra', 'Requires-Dist', 'Requires-Dist']
+
+You can also get a :ref:`distribution's version number <version>`, list its
+:ref:`constituent files <files>`, and get a list of the distribution's
+:ref:`requirements`.
+
+
+Functional API
+==============
+
+This package provides the following functionality via its public API.
+
+
+.. _entry-points:
+
+Entry points
+------------
+
+The ``entry_points()`` function returns a collection of entry points.
+Entry points are represented by ``EntryPoint`` instances;
+each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and
+a ``.load()`` method to resolve the value.  There are also ``.module``,
+``.attr``, and ``.extras`` attributes for getting the components of the
+``.value`` attribute.
+
+Query all entry points::
+
+    >>> eps = entry_points()
+
+The ``entry_points()`` function returns an ``EntryPoints`` object,
+a collection of all ``EntryPoint`` objects with ``names`` and ``groups``
+attributes for convenience::
+
+    >>> sorted(eps.groups)
+    ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']
+
+``EntryPoints`` has a ``select`` method to select entry points
+matching specific properties. Select entry points in the
+``console_scripts`` group::
+
+    >>> scripts = eps.select(group='console_scripts')
+
+Equivalently, since ``entry_points`` passes keyword arguments
+through to select::
+
+    >>> scripts = entry_points(group='console_scripts')
+
+Pick out a specific script named "wheel" (found in the wheel project)::
+
+    >>> 'wheel' in scripts.names
+    True
+    >>> wheel = scripts['wheel']
+
+Equivalently, query for that entry point during selection::
+
+    >>> (wheel,) = entry_points(group='console_scripts', name='wheel')
+    >>> (wheel,) = entry_points().select(group='console_scripts', name='wheel')
+
+Inspect the resolved entry point::
+
+    >>> wheel
+    EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')
+    >>> wheel.module
+    'wheel.cli'
+    >>> wheel.attr
+    'main'
+    >>> wheel.extras
+    []
+    >>> main = wheel.load()
+    >>> main
+    <function main at 0x103528488>
+
+The ``group`` and ``name`` are arbitrary values defined by the package author
+and usually a client will wish to resolve all entry points for a particular
+group.  Read `the setuptools docs
+<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_
+for more information on entry points, their definition, and usage.
+
+*Compatibility Note*
+
+The "selectable" entry points were introduced in ``importlib_metadata``
+3.6 and Python 3.10. Prior to those changes, ``entry_points`` accepted
+no parameters and always returned a dictionary of entry points, keyed
+by group. With ``importlib_metadata`` 5.0 and Python 3.12,
+``entry_points`` always returns an ``EntryPoints`` object. See
+`backports.entry_points_selectable <https://pypi.org/project/backports.entry_points_selectable>`_
+for compatibility options.
+
+
+.. _metadata:
+
+Distribution metadata
+---------------------
+
+Every :term:`packaging:Distribution Package` includes some metadata,
+which you can extract using the
+``metadata()`` function::
+
+    >>> wheel_metadata = metadata('wheel')
+
+The keys of the returned data structure, a ``PackageMetadata``,
+name the metadata keywords, and
+the values are returned unparsed from the distribution metadata::
+
+    >>> wheel_metadata['Requires-Python']
+    '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
+
+``PackageMetadata`` also presents a ``json`` attribute that returns
+all the metadata in a JSON-compatible form per PEP 566::
+
+    >>> wheel_metadata.json['requires_python']
+    '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
+
+.. note::
+
+    The actual type of the object returned by ``metadata()`` is an
+    implementation detail and should be accessed only through the interface
+    described by the
+    `PackageMetadata protocol <https://importlib-metadata.readthedocs.io/en/latest/api.html#importlib_metadata.PackageMetadata>`_.
+
+
+.. _version:
+
+Distribution versions
+---------------------
+
+The ``version()`` function is the quickest way to get a
+:term:`packaging:Distribution Package`'s version
+number, as a string::
+
+    >>> version('wheel')
+    '0.32.3'
+
+
+.. _files:
+
+Distribution files
+------------------
+
+You can also get the full set of files contained within a distribution.  The
+``files()`` function takes a :term:`packaging:Distribution Package` name
+and returns all of the
+files installed by this distribution.  Each file object returned is a
+``PackagePath``, a :class:`pathlib.PurePath` derived object with additional ``dist``,
+``size``, and ``hash`` properties as indicated by the metadata.  For example::
+
+    >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0]
+    >>> util
+    PackagePath('wheel/util.py')
+    >>> util.size
+    859
+    >>> util.dist
+    <importlib_metadata._hooks.PathDistribution object at 0x101e0cef0>
+    >>> util.hash
+    <FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>
+
+Once you have the file, you can also read its contents::
+
+    >>> print(util.read_text())
+    import base64
+    import sys
+    ...
+    def as_bytes(s):
+        if isinstance(s, text_type):
+            return s.encode('utf-8')
+        return s
+
+You can also use the ``locate`` method to get a the absolute path to the
+file::
+
+    >>> util.locate()  # doctest: +SKIP
+    PosixPath('/home/gustav/example/lib/site-packages/wheel/util.py')
+
+In the case where the metadata file listing files
+(RECORD or SOURCES.txt) is missing, ``files()`` will
+return ``None``. The caller may wish to wrap calls to
+``files()`` in `always_iterable
+<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable>`_
+or otherwise guard against this condition if the target
+distribution is not known to have the metadata present.
+
+.. _requirements:
+
+Distribution requirements
+-------------------------
+
+To get the full set of requirements for a :term:`packaging:Distribution Package`,
+use the ``requires()``
+function::
+
+    >>> requires('wheel')
+    ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
+
+
+.. _package-distributions:
+.. _import-distribution-package-mapping:
+
+Mapping import to distribution packages
+---------------------------------------
+
+A convenience method to resolve the :term:`packaging:Distribution Package`
+name (or names, in the case of a namespace package)
+that provide each importable top-level
+Python module or :term:`packaging:Import Package`::
+
+    >>> packages_distributions()
+    {'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}
+
+Some editable installs, `do not supply top-level names
+<https://github.com/pypa/packaging-problems/issues/609>`_, and thus this
+function is not reliable with such installs.
+
+.. _distributions:
+
+Distributions
+=============
+
+While the above API is the most common and convenient usage, you can get all
+of that information from the ``Distribution`` class.  A ``Distribution`` is an
+abstract object that represents the metadata for
+a Python :term:`packaging:Distribution Package`.  You can
+get the ``Distribution`` instance::
+
+    >>> from importlib_metadata import distribution
+    >>> dist = distribution('wheel')
+
+Thus, an alternative way to get the version number is through the
+``Distribution`` instance::
+
+    >>> dist.version
+    '0.32.3'
+
+There are all kinds of additional metadata available on the ``Distribution``
+instance::
+
+    >>> dist.metadata['Requires-Python']
+    '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
+    >>> dist.metadata['License']
+    'MIT'
+
+The full set of available metadata is not described here.
+See the :ref:`packaging:core-metadata` for additional details.
+
+
+Distribution Discovery
+======================
+
+By default, this package provides built-in support for discovery of metadata
+for file system and zip file :term:`packaging:Distribution Package`\s.
+This metadata finder search defaults to ``sys.path``, but varies slightly in how it interprets those values from how other import machinery does. In particular:
+
+- ``importlib_metadata`` does not honor :class:`bytes` objects on ``sys.path``.
+- ``importlib_metadata`` will incidentally honor :py:class:`pathlib.Path` objects on ``sys.path`` even though such values will be ignored for imports.
+
+
+Extending the search algorithm
+==============================
+
+Because :term:`packaging:Distribution Package` metadata
+is not available through :data:`sys.path` searches, or
+package loaders directly,
+the metadata for a distribution is found through import
+system :ref:`finders <finders-and-loaders>`.  To find a distribution package's metadata,
+``importlib.metadata`` queries the list of :term:`meta path finders <meta path finder>` on
+:data:`sys.meta_path`.
+
+By default ``importlib_metadata`` installs a finder for distribution packages
+found on the file system.
+This finder doesn't actually find any *distributions*,
+but it can find their metadata.
+
+The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the
+interface expected of finders by Python's import system.
+``importlib_metadata`` extends this protocol by looking for an optional
+``find_distributions`` callable on the finders from
+:data:`sys.meta_path` and presents this extended interface as the
+``DistributionFinder`` abstract base class, which defines this abstract
+method::
+
+    @abc.abstractmethod
+    def find_distributions(context=DistributionFinder.Context()):
+        """Return an iterable of all Distribution instances capable of
+        loading the metadata for packages for the indicated ``context``.
+        """
+
+The ``DistributionFinder.Context`` object provides ``.path`` and ``.name``
+properties indicating the path to search and name to match and may
+supply other relevant context.
+
+What this means in practice is that to support finding distribution package
+metadata in locations other than the file system, subclass
+``Distribution`` and implement the abstract methods. Then from
+a custom finder, return instances of this derived ``Distribution`` in the
+``find_distributions()`` method.
+
+
+.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
+.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/exercises.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/exercises.py
new file mode 100644 (file)
index 0000000..c88fa98
--- /dev/null
@@ -0,0 +1,45 @@
+from pytest_perf.deco import extras
+
+
+@extras('perf')
+def discovery_perf():
+    "discovery"
+    import importlib_metadata  # end warmup
+
+    importlib_metadata.distribution('ipython')
+
+
+def entry_points_perf():
+    "entry_points()"
+    import importlib_metadata  # end warmup
+
+    importlib_metadata.entry_points()
+
+
+@extras('perf')
+def cached_distribution_perf():
+    "cached distribution"
+    import importlib_metadata
+
+    importlib_metadata.distribution('ipython')  # end warmup
+    importlib_metadata.distribution('ipython')
+
+
+@extras('perf')
+def uncached_distribution_perf():
+    "uncached distribution"
+    import importlib
+    import importlib_metadata
+
+    # end warmup
+    importlib.invalidate_caches()
+    importlib_metadata.distribution('ipython')
+
+
+def entrypoint_regexp_perf():
+    import importlib_metadata
+    import re
+
+    input = '0' + ' ' * 2**10 + '0'  # end warmup
+
+    re.match(importlib_metadata.EntryPoint.pattern, input)
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/__init__.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/__init__.py
new file mode 100644 (file)
index 0000000..8147d2f
--- /dev/null
@@ -0,0 +1,1015 @@
+import os
+import re
+import abc
+import csv
+import sys
+import zipp
+import email
+import inspect
+import pathlib
+import operator
+import textwrap
+import warnings
+import functools
+import itertools
+import posixpath
+import collections
+
+from . import _adapters, _meta, _py39compat
+from ._collections import FreezableDefaultDict, Pair
+from ._compat import (
+    NullFinder,
+    StrPath,
+    install,
+    pypy_partial,
+)
+from ._functools import method_cache, pass_none
+from ._itertools import always_iterable, unique_everseen
+from ._meta import PackageMetadata, SimplePath
+
+from contextlib import suppress
+from importlib import import_module
+from importlib.abc import MetaPathFinder
+from itertools import starmap
+from typing import Iterable, List, Mapping, Optional, Set, cast
+
+__all__ = [
+    'Distribution',
+    'DistributionFinder',
+    'PackageMetadata',
+    'PackageNotFoundError',
+    'distribution',
+    'distributions',
+    'entry_points',
+    'files',
+    'metadata',
+    'packages_distributions',
+    'requires',
+    'version',
+]
+
+
+class PackageNotFoundError(ModuleNotFoundError):
+    """The package was not found."""
+
+    def __str__(self) -> str:
+        return f"No package metadata was found for {self.name}"
+
+    @property
+    def name(self) -> str:  # type: ignore[override]
+        (name,) = self.args
+        return name
+
+
+class Sectioned:
+    """
+    A simple entry point config parser for performance
+
+    >>> for item in Sectioned.read(Sectioned._sample):
+    ...     print(item)
+    Pair(name='sec1', value='# comments ignored')
+    Pair(name='sec1', value='a = 1')
+    Pair(name='sec1', value='b = 2')
+    Pair(name='sec2', value='a = 2')
+
+    >>> res = Sectioned.section_pairs(Sectioned._sample)
+    >>> item = next(res)
+    >>> item.name
+    'sec1'
+    >>> item.value
+    Pair(name='a', value='1')
+    >>> item = next(res)
+    >>> item.value
+    Pair(name='b', value='2')
+    >>> item = next(res)
+    >>> item.name
+    'sec2'
+    >>> item.value
+    Pair(name='a', value='2')
+    >>> list(res)
+    []
+    """
+
+    _sample = textwrap.dedent(
+        """
+        [sec1]
+        # comments ignored
+        a = 1
+        b = 2
+
+        [sec2]
+        a = 2
+        """
+    ).lstrip()
+
+    @classmethod
+    def section_pairs(cls, text):
+        return (
+            section._replace(value=Pair.parse(section.value))
+            for section in cls.read(text, filter_=cls.valid)
+            if section.name is not None
+        )
+
+    @staticmethod
+    def read(text, filter_=None):
+        lines = filter(filter_, map(str.strip, text.splitlines()))
+        name = None
+        for value in lines:
+            section_match = value.startswith('[') and value.endswith(']')
+            if section_match:
+                name = value.strip('[]')
+                continue
+            yield Pair(name, value)
+
+    @staticmethod
+    def valid(line: str):
+        return line and not line.startswith('#')
+
+
+class DeprecatedTuple:
+    """
+    Provide subscript item access for backward compatibility.
+
+    >>> recwarn = getfixture('recwarn')
+    >>> ep = EntryPoint(name='name', value='value', group='group')
+    >>> ep[:]
+    ('name', 'value', 'group')
+    >>> ep[0]
+    'name'
+    >>> len(recwarn)
+    1
+    """
+
+    # Do not remove prior to 2023-05-01 or Python 3.13
+    _warn = functools.partial(
+        warnings.warn,
+        "EntryPoint tuple interface is deprecated. Access members by name.",
+        DeprecationWarning,
+        stacklevel=pypy_partial(2),
+    )
+
+    def __getitem__(self, item):
+        self._warn()
+        return self._key()[item]
+
+
+class EntryPoint(DeprecatedTuple):
+    """An entry point as defined by Python packaging conventions.
+
+    See `the packaging docs on entry points
+    <https://packaging.python.org/specifications/entry-points/>`_
+    for more information.
+
+    >>> ep = EntryPoint(
+    ...     name=None, group=None, value='package.module:attr [extra1, extra2]')
+    >>> ep.module
+    'package.module'
+    >>> ep.attr
+    'attr'
+    >>> ep.extras
+    ['extra1', 'extra2']
+    """
+
+    pattern = re.compile(
+        r'(?P<module>[\w.]+)\s*'
+        r'(:\s*(?P<attr>[\w.]+)\s*)?'
+        r'((?P<extras>\[.*\])\s*)?$'
+    )
+    """
+    A regular expression describing the syntax for an entry point,
+    which might look like:
+
+        - module
+        - package.module
+        - package.module:attribute
+        - package.module:object.attribute
+        - package.module:attr [extra1, extra2]
+
+    Other combinations are possible as well.
+
+    The expression is lenient about whitespace around the ':',
+    following the attr, and following any extras.
+    """
+
+    name: str
+    value: str
+    group: str
+
+    dist: Optional['Distribution'] = None
+
+    def __init__(self, name: str, value: str, group: str) -> None:
+        vars(self).update(name=name, value=value, group=group)
+
+    def load(self):
+        """Load the entry point from its definition. If only a module
+        is indicated by the value, return that module. Otherwise,
+        return the named object.
+        """
+        match = self.pattern.match(self.value)
+        module = import_module(match.group('module'))
+        attrs = filter(None, (match.group('attr') or '').split('.'))
+        return functools.reduce(getattr, attrs, module)
+
+    @property
+    def module(self) -> str:
+        match = self.pattern.match(self.value)
+        assert match is not None
+        return match.group('module')
+
+    @property
+    def attr(self) -> str:
+        match = self.pattern.match(self.value)
+        assert match is not None
+        return match.group('attr')
+
+    @property
+    def extras(self) -> List[str]:
+        match = self.pattern.match(self.value)
+        assert match is not None
+        return re.findall(r'\w+', match.group('extras') or '')
+
+    def _for(self, dist):
+        vars(self).update(dist=dist)
+        return self
+
+    def matches(self, **params):
+        """
+        EntryPoint matches the given parameters.
+
+        >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]')
+        >>> ep.matches(group='foo')
+        True
+        >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]')
+        True
+        >>> ep.matches(group='foo', name='other')
+        False
+        >>> ep.matches()
+        True
+        >>> ep.matches(extras=['extra1', 'extra2'])
+        True
+        >>> ep.matches(module='bing')
+        True
+        >>> ep.matches(attr='bong')
+        True
+        """
+        attrs = (getattr(self, param) for param in params)
+        return all(map(operator.eq, params.values(), attrs))
+
+    def _key(self):
+        return self.name, self.value, self.group
+
+    def __lt__(self, other):
+        return self._key() < other._key()
+
+    def __eq__(self, other):
+        return self._key() == other._key()
+
+    def __setattr__(self, name, value):
+        raise AttributeError("EntryPoint objects are immutable.")
+
+    def __repr__(self):
+        return (
+            f'EntryPoint(name={self.name!r}, value={self.value!r}, '
+            f'group={self.group!r})'
+        )
+
+    def __hash__(self) -> int:
+        return hash(self._key())
+
+
+class EntryPoints(tuple):
+    """
+    An immutable collection of selectable EntryPoint objects.
+    """
+
+    __slots__ = ()
+
+    def __getitem__(self, name: str) -> EntryPoint:  # type: ignore[override]
+        """
+        Get the EntryPoint in self matching name.
+        """
+        try:
+            return next(iter(self.select(name=name)))
+        except StopIteration:
+            raise KeyError(name)
+
+    def select(self, **params):
+        """
+        Select entry points from self that match the
+        given parameters (typically group and/or name).
+        """
+        return EntryPoints(ep for ep in self if _py39compat.ep_matches(ep, **params))
+
+    @property
+    def names(self) -> Set[str]:
+        """
+        Return the set of all names of all entry points.
+        """
+        return {ep.name for ep in self}
+
+    @property
+    def groups(self) -> Set[str]:
+        """
+        Return the set of all groups of all entry points.
+        """
+        return {ep.group for ep in self}
+
+    @classmethod
+    def _from_text_for(cls, text, dist):
+        return cls(ep._for(dist) for ep in cls._from_text(text))
+
+    @staticmethod
+    def _from_text(text):
+        return (
+            EntryPoint(name=item.value.name, value=item.value.value, group=item.name)
+            for item in Sectioned.section_pairs(text or '')
+        )
+
+
+class PackagePath(pathlib.PurePosixPath):
+    """A reference to a path in a package"""
+
+    hash: Optional["FileHash"]
+    size: int
+    dist: "Distribution"
+
+    def read_text(self, encoding: str = 'utf-8') -> str:  # type: ignore[override]
+        with self.locate().open(encoding=encoding) as stream:
+            return stream.read()
+
+    def read_binary(self) -> bytes:
+        with self.locate().open('rb') as stream:
+            return stream.read()
+
+    def locate(self) -> pathlib.Path:
+        """Return a path-like object for this path"""
+        return self.dist.locate_file(self)
+
+
+class FileHash:
+    def __init__(self, spec: str) -> None:
+        self.mode, _, self.value = spec.partition('=')
+
+    def __repr__(self) -> str:
+        return f'<FileHash mode: {self.mode} value: {self.value}>'
+
+
+class DeprecatedNonAbstract:
+    def __new__(cls, *args, **kwargs):
+        all_names = {
+            name for subclass in inspect.getmro(cls) for name in vars(subclass)
+        }
+        abstract = {
+            name
+            for name in all_names
+            if getattr(getattr(cls, name), '__isabstractmethod__', False)
+        }
+        if abstract:
+            warnings.warn(
+                f"Unimplemented abstract methods {abstract}",
+                DeprecationWarning,
+                stacklevel=2,
+            )
+        return super().__new__(cls)
+
+
+class Distribution(DeprecatedNonAbstract):
+    """A Python distribution package."""
+
+    @abc.abstractmethod
+    def read_text(self, filename) -> Optional[str]:
+        """Attempt to load metadata file given by the name.
+
+        :param filename: The name of the file in the distribution info.
+        :return: The text if found, otherwise None.
+        """
+
+    @abc.abstractmethod
+    def locate_file(self, path: StrPath) -> pathlib.Path:
+        """
+        Given a path to a file in this distribution, return a path
+        to it.
+        """
+
+    @classmethod
+    def from_name(cls, name: str) -> "Distribution":
+        """Return the Distribution for the given package name.
+
+        :param name: The name of the distribution package to search for.
+        :return: The Distribution instance (or subclass thereof) for the named
+            package, if found.
+        :raises PackageNotFoundError: When the named package's distribution
+            metadata cannot be found.
+        :raises ValueError: When an invalid value is supplied for name.
+        """
+        if not name:
+            raise ValueError("A distribution name is required.")
+        try:
+            return next(iter(cls.discover(name=name)))
+        except StopIteration:
+            raise PackageNotFoundError(name)
+
+    @classmethod
+    def discover(cls, **kwargs) -> Iterable["Distribution"]:
+        """Return an iterable of Distribution objects for all packages.
+
+        Pass a ``context`` or pass keyword arguments for constructing
+        a context.
+
+        :context: A ``DistributionFinder.Context`` object.
+        :return: Iterable of Distribution objects for all packages.
+        """
+        context = kwargs.pop('context', None)
+        if context and kwargs:
+            raise ValueError("cannot accept context and kwargs")
+        context = context or DistributionFinder.Context(**kwargs)
+        return itertools.chain.from_iterable(
+            resolver(context) for resolver in cls._discover_resolvers()
+        )
+
+    @staticmethod
+    def at(path: StrPath) -> "Distribution":
+        """Return a Distribution for the indicated metadata path
+
+        :param path: a string or path-like object
+        :return: a concrete Distribution instance for the path
+        """
+        return PathDistribution(pathlib.Path(path))
+
+    @staticmethod
+    def _discover_resolvers():
+        """Search the meta_path for resolvers."""
+        declared = (
+            getattr(finder, 'find_distributions', None) for finder in sys.meta_path
+        )
+        return filter(None, declared)
+
+    @property
+    def metadata(self) -> _meta.PackageMetadata:
+        """Return the parsed metadata for this Distribution.
+
+        The returned object will have keys that name the various bits of
+        metadata.  See PEP 566 for details.
+        """
+        opt_text = (
+            self.read_text('METADATA')
+            or self.read_text('PKG-INFO')
+            # This last clause is here to support old egg-info files.  Its
+            # effect is to just end up using the PathDistribution's self._path
+            # (which points to the egg-info file) attribute unchanged.
+            or self.read_text('')
+        )
+        text = cast(str, opt_text)
+        return _adapters.Message(email.message_from_string(text))
+
+    @property
+    def name(self) -> str:
+        """Return the 'Name' metadata for the distribution package."""
+        return self.metadata['Name']
+
+    @property
+    def _normalized_name(self):
+        """Return a normalized version of the name."""
+        return Prepared.normalize(self.name)
+
+    @property
+    def version(self) -> str:
+        """Return the 'Version' metadata for the distribution package."""
+        return self.metadata['Version']
+
+    @property
+    def entry_points(self) -> EntryPoints:
+        return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
+
+    @property
+    def files(self) -> Optional[List[PackagePath]]:
+        """Files in this distribution.
+
+        :return: List of PackagePath for this distribution or None
+
+        Result is `None` if the metadata file that enumerates files
+        (i.e. RECORD for dist-info, or installed-files.txt or
+        SOURCES.txt for egg-info) is missing.
+        Result may be empty if the metadata exists but is empty.
+        """
+
+        def make_file(name, hash=None, size_str=None):
+            result = PackagePath(name)
+            result.hash = FileHash(hash) if hash else None
+            result.size = int(size_str) if size_str else None
+            result.dist = self
+            return result
+
+        @pass_none
+        def make_files(lines):
+            return starmap(make_file, csv.reader(lines))
+
+        @pass_none
+        def skip_missing_files(package_paths):
+            return list(filter(lambda path: path.locate().exists(), package_paths))
+
+        return skip_missing_files(
+            make_files(
+                self._read_files_distinfo()
+                or self._read_files_egginfo_installed()
+                or self._read_files_egginfo_sources()
+            )
+        )
+
+    def _read_files_distinfo(self):
+        """
+        Read the lines of RECORD
+        """
+        text = self.read_text('RECORD')
+        return text and text.splitlines()
+
+    def _read_files_egginfo_installed(self):
+        """
+        Read installed-files.txt and return lines in a similar
+        CSV-parsable format as RECORD: each file must be placed
+        relative to the site-packages directory and must also be
+        quoted (since file names can contain literal commas).
+
+        This file is written when the package is installed by pip,
+        but it might not be written for other installation methods.
+        Assume the file is accurate if it exists.
+        """
+        text = self.read_text('installed-files.txt')
+        # Prepend the .egg-info/ subdir to the lines in this file.
+        # But this subdir is only available from PathDistribution's
+        # self._path.
+        subdir = getattr(self, '_path', None)
+        if not text or not subdir:
+            return
+
+        paths = (
+            (subdir / name)
+            .resolve()
+            .relative_to(self.locate_file('').resolve())
+            .as_posix()
+            for name in text.splitlines()
+        )
+        return map('"{}"'.format, paths)
+
+    def _read_files_egginfo_sources(self):
+        """
+        Read SOURCES.txt and return lines in a similar CSV-parsable
+        format as RECORD: each file name must be quoted (since it
+        might contain literal commas).
+
+        Note that SOURCES.txt is not a reliable source for what
+        files are installed by a package. This file is generated
+        for a source archive, and the files that are present
+        there (e.g. setup.py) may not correctly reflect the files
+        that are present after the package has been installed.
+        """
+        text = self.read_text('SOURCES.txt')
+        return text and map('"{}"'.format, text.splitlines())
+
+    @property
+    def requires(self) -> Optional[List[str]]:
+        """Generated requirements specified for this Distribution"""
+        reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
+        return reqs and list(reqs)
+
+    def _read_dist_info_reqs(self):
+        return self.metadata.get_all('Requires-Dist')
+
+    def _read_egg_info_reqs(self):
+        source = self.read_text('requires.txt')
+        return pass_none(self._deps_from_requires_text)(source)
+
+    @classmethod
+    def _deps_from_requires_text(cls, source):
+        return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source))
+
+    @staticmethod
+    def _convert_egg_info_reqs_to_simple_reqs(sections):
+        """
+        Historically, setuptools would solicit and store 'extra'
+        requirements, including those with environment markers,
+        in separate sections. More modern tools expect each
+        dependency to be defined separately, with any relevant
+        extras and environment markers attached directly to that
+        requirement. This method converts the former to the
+        latter. See _test_deps_from_requires_text for an example.
+        """
+
+        def make_condition(name):
+            return name and f'extra == "{name}"'
+
+        def quoted_marker(section):
+            section = section or ''
+            extra, sep, markers = section.partition(':')
+            if extra and markers:
+                markers = f'({markers})'
+            conditions = list(filter(None, [markers, make_condition(extra)]))
+            return '; ' + ' and '.join(conditions) if conditions else ''
+
+        def url_req_space(req):
+            """
+            PEP 508 requires a space between the url_spec and the quoted_marker.
+            Ref python/importlib_metadata#357.
+            """
+            # '@' is uniquely indicative of a url_req.
+            return ' ' * ('@' in req)
+
+        for section in sections:
+            space = url_req_space(section.value)
+            yield section.value + space + quoted_marker(section.name)
+
+
+class DistributionFinder(MetaPathFinder):
+    """
+    A MetaPathFinder capable of discovering installed distributions.
+    """
+
+    class Context:
+        """
+        Keyword arguments presented by the caller to
+        ``distributions()`` or ``Distribution.discover()``
+        to narrow the scope of a search for distributions
+        in all DistributionFinders.
+
+        Each DistributionFinder may expect any parameters
+        and should attempt to honor the canonical
+        parameters defined below when appropriate.
+        """
+
+        name = None
+        """
+        Specific name for which a distribution finder should match.
+        A name of ``None`` matches all distributions.
+        """
+
+        def __init__(self, **kwargs):
+            vars(self).update(kwargs)
+
+        @property
+        def path(self) -> List[str]:
+            """
+            The sequence of directory path that a distribution finder
+            should search.
+
+            Typically refers to Python installed package paths such as
+            "site-packages" directories and defaults to ``sys.path``.
+            """
+            return vars(self).get('path', sys.path)
+
+    @abc.abstractmethod
+    def find_distributions(self, context=Context()) -> Iterable[Distribution]:
+        """
+        Find distributions.
+
+        Return an iterable of all Distribution instances capable of
+        loading the metadata for packages matching the ``context``,
+        a DistributionFinder.Context instance.
+        """
+
+
+class FastPath:
+    """
+    Micro-optimized class for searching a path for
+    children.
+
+    >>> FastPath('').children()
+    ['...']
+    """
+
+    @functools.lru_cache()  # type: ignore
+    def __new__(cls, root):
+        return super().__new__(cls)
+
+    def __init__(self, root):
+        self.root = root
+
+    def joinpath(self, child):
+        return pathlib.Path(self.root, child)
+
+    def children(self):
+        with suppress(Exception):
+            return os.listdir(self.root or '.')
+        with suppress(Exception):
+            return self.zip_children()
+        return []
+
+    def zip_children(self):
+        zip_path = zipp.Path(self.root)
+        names = zip_path.root.namelist()
+        self.joinpath = zip_path.joinpath
+
+        return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names)
+
+    def search(self, name):
+        return self.lookup(self.mtime).search(name)
+
+    @property
+    def mtime(self):
+        with suppress(OSError):
+            return os.stat(self.root).st_mtime
+        self.lookup.cache_clear()
+
+    @method_cache
+    def lookup(self, mtime):
+        return Lookup(self)
+
+
+class Lookup:
+    def __init__(self, path: FastPath):
+        base = os.path.basename(path.root).lower()
+        base_is_egg = base.endswith(".egg")
+        self.infos = FreezableDefaultDict(list)
+        self.eggs = FreezableDefaultDict(list)
+
+        for child in path.children():
+            low = child.lower()
+            if low.endswith((".dist-info", ".egg-info")):
+                # rpartition is faster than splitext and suitable for this purpose.
+                name = low.rpartition(".")[0].partition("-")[0]
+                normalized = Prepared.normalize(name)
+                self.infos[normalized].append(path.joinpath(child))
+            elif base_is_egg and low == "egg-info":
+                name = base.rpartition(".")[0].partition("-")[0]
+                legacy_normalized = Prepared.legacy_normalize(name)
+                self.eggs[legacy_normalized].append(path.joinpath(child))
+
+        self.infos.freeze()
+        self.eggs.freeze()
+
+    def search(self, prepared):
+        infos = (
+            self.infos[prepared.normalized]
+            if prepared
+            else itertools.chain.from_iterable(self.infos.values())
+        )
+        eggs = (
+            self.eggs[prepared.legacy_normalized]
+            if prepared
+            else itertools.chain.from_iterable(self.eggs.values())
+        )
+        return itertools.chain(infos, eggs)
+
+
+class Prepared:
+    """
+    A prepared search for metadata on a possibly-named package.
+    """
+
+    normalized = None
+    legacy_normalized = None
+
+    def __init__(self, name):
+        self.name = name
+        if name is None:
+            return
+        self.normalized = self.normalize(name)
+        self.legacy_normalized = self.legacy_normalize(name)
+
+    @staticmethod
+    def normalize(name):
+        """
+        PEP 503 normalization plus dashes as underscores.
+        """
+        return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')
+
+    @staticmethod
+    def legacy_normalize(name):
+        """
+        Normalize the package name as found in the convention in
+        older packaging tools versions and specs.
+        """
+        return name.lower().replace('-', '_')
+
+    def __bool__(self):
+        return bool(self.name)
+
+
+@install
+class MetadataPathFinder(NullFinder, DistributionFinder):
+    """A degenerate finder for distribution packages on the file system.
+
+    This finder supplies only a find_distributions() method for versions
+    of Python that do not have a PathFinder find_distributions().
+    """
+
+    def find_distributions(
+        self, context=DistributionFinder.Context()
+    ) -> Iterable["PathDistribution"]:
+        """
+        Find distributions.
+
+        Return an iterable of all Distribution instances capable of
+        loading the metadata for packages matching ``context.name``
+        (or all names if ``None`` indicated) along the paths in the list
+        of directories ``context.path``.
+        """
+        found = self._search_paths(context.name, context.path)
+        return map(PathDistribution, found)
+
+    @classmethod
+    def _search_paths(cls, name, paths):
+        """Find metadata directories in paths heuristically."""
+        prepared = Prepared(name)
+        return itertools.chain.from_iterable(
+            path.search(prepared) for path in map(FastPath, paths)
+        )
+
+    def invalidate_caches(cls) -> None:
+        FastPath.__new__.cache_clear()
+
+
+class PathDistribution(Distribution):
+    def __init__(self, path: SimplePath) -> None:
+        """Construct a distribution.
+
+        :param path: SimplePath indicating the metadata directory.
+        """
+        self._path = path
+
+    def read_text(self, filename: StrPath) -> Optional[str]:
+        with suppress(
+            FileNotFoundError,
+            IsADirectoryError,
+            KeyError,
+            NotADirectoryError,
+            PermissionError,
+        ):
+            return self._path.joinpath(filename).read_text(encoding='utf-8')
+
+        return None
+
+    read_text.__doc__ = Distribution.read_text.__doc__
+
+    def locate_file(self, path: StrPath) -> pathlib.Path:
+        return self._path.parent / path
+
+    @property
+    def _normalized_name(self):
+        """
+        Performance optimization: where possible, resolve the
+        normalized name from the file system path.
+        """
+        stem = os.path.basename(str(self._path))
+        return (
+            pass_none(Prepared.normalize)(self._name_from_stem(stem))
+            or super()._normalized_name
+        )
+
+    @staticmethod
+    def _name_from_stem(stem):
+        """
+        >>> PathDistribution._name_from_stem('foo-3.0.egg-info')
+        'foo'
+        >>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info')
+        'CherryPy'
+        >>> PathDistribution._name_from_stem('face.egg-info')
+        'face'
+        >>> PathDistribution._name_from_stem('foo.bar')
+        """
+        filename, ext = os.path.splitext(stem)
+        if ext not in ('.dist-info', '.egg-info'):
+            return
+        name, sep, rest = filename.partition('-')
+        return name
+
+
+def distribution(distribution_name) -> Distribution:
+    """Get the ``Distribution`` instance for the named package.
+
+    :param distribution_name: The name of the distribution package as a string.
+    :return: A ``Distribution`` instance (or subclass thereof).
+    """
+    return Distribution.from_name(distribution_name)
+
+
+def distributions(**kwargs) -> Iterable[Distribution]:
+    """Get all ``Distribution`` instances in the current environment.
+
+    :return: An iterable of ``Distribution`` instances.
+    """
+    return Distribution.discover(**kwargs)
+
+
+def metadata(distribution_name) -> _meta.PackageMetadata:
+    """Get the metadata for the named package.
+
+    :param distribution_name: The name of the distribution package to query.
+    :return: A PackageMetadata containing the parsed metadata.
+    """
+    return Distribution.from_name(distribution_name).metadata
+
+
+def version(distribution_name) -> str:
+    """Get the version string for the named package.
+
+    :param distribution_name: The name of the distribution package to query.
+    :return: The version string for the package as defined in the package's
+        "Version" metadata key.
+    """
+    return distribution(distribution_name).version
+
+
+_unique = functools.partial(
+    unique_everseen,
+    key=_py39compat.normalized_name,
+)
+"""
+Wrapper for ``distributions`` to return unique distributions by name.
+"""
+
+
+def entry_points(**params) -> EntryPoints:
+    """Return EntryPoint objects for all installed packages.
+
+    Pass selection parameters (group or name) to filter the
+    result to entry points matching those properties (see
+    EntryPoints.select()).
+
+    :return: EntryPoints for all installed packages.
+    """
+    eps = itertools.chain.from_iterable(
+        dist.entry_points for dist in _unique(distributions())
+    )
+    return EntryPoints(eps).select(**params)
+
+
+def files(distribution_name) -> Optional[List[PackagePath]]:
+    """Return a list of files for the named package.
+
+    :param distribution_name: The name of the distribution package to query.
+    :return: List of files composing the distribution.
+    """
+    return distribution(distribution_name).files
+
+
+def requires(distribution_name) -> Optional[List[str]]:
+    """
+    Return a list of requirements for the named package.
+
+    :return: An iterable of requirements, suitable for
+        packaging.requirement.Requirement.
+    """
+    return distribution(distribution_name).requires
+
+
+def packages_distributions() -> Mapping[str, List[str]]:
+    """
+    Return a mapping of top-level packages to their
+    distributions.
+
+    >>> import collections.abc
+    >>> pkgs = packages_distributions()
+    >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values())
+    True
+    """
+    pkg_to_dist = collections.defaultdict(list)
+    for dist in distributions():
+        for pkg in _top_level_declared(dist) or _top_level_inferred(dist):
+            pkg_to_dist[pkg].append(dist.metadata['Name'])
+    return dict(pkg_to_dist)
+
+
+def _top_level_declared(dist):
+    return (dist.read_text('top_level.txt') or '').split()
+
+
+def _topmost(name: PackagePath) -> Optional[str]:
+    """
+    Return the top-most parent as long as there is a parent.
+    """
+    top, *rest = name.parts
+    return top if rest else None
+
+
+def _get_toplevel_name(name: PackagePath) -> str:
+    """
+    Infer a possibly importable module name from a name presumed on
+    sys.path.
+
+    >>> _get_toplevel_name(PackagePath('foo.py'))
+    'foo'
+    >>> _get_toplevel_name(PackagePath('foo'))
+    'foo'
+    >>> _get_toplevel_name(PackagePath('foo.pyc'))
+    'foo'
+    >>> _get_toplevel_name(PackagePath('foo/__init__.py'))
+    'foo'
+    >>> _get_toplevel_name(PackagePath('foo.pth'))
+    'foo.pth'
+    >>> _get_toplevel_name(PackagePath('foo.dist-info'))
+    'foo.dist-info'
+    """
+    return _topmost(name) or (
+        # python/typeshed#10328
+        inspect.getmodulename(name)  # type: ignore
+        or str(name)
+    )
+
+
+def _top_level_inferred(dist):
+    opt_names = set(map(_get_toplevel_name, always_iterable(dist.files)))
+
+    def importable_name(name):
+        return '.' not in name
+
+    return filter(importable_name, opt_names)
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_adapters.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_adapters.py
new file mode 100644 (file)
index 0000000..e33cba5
--- /dev/null
@@ -0,0 +1,90 @@
+import functools
+import warnings
+import re
+import textwrap
+import email.message
+
+from ._text import FoldedCase
+from ._compat import pypy_partial
+
+
+# Do not remove prior to 2024-01-01 or Python 3.14
+_warn = functools.partial(
+    warnings.warn,
+    "Implicit None on return values is deprecated and will raise KeyErrors.",
+    DeprecationWarning,
+    stacklevel=pypy_partial(2),
+)
+
+
+class Message(email.message.Message):
+    multiple_use_keys = set(
+        map(
+            FoldedCase,
+            [
+                'Classifier',
+                'Obsoletes-Dist',
+                'Platform',
+                'Project-URL',
+                'Provides-Dist',
+                'Provides-Extra',
+                'Requires-Dist',
+                'Requires-External',
+                'Supported-Platform',
+                'Dynamic',
+            ],
+        )
+    )
+    """
+    Keys that may be indicated multiple times per PEP 566.
+    """
+
+    def __new__(cls, orig: email.message.Message):
+        res = super().__new__(cls)
+        vars(res).update(vars(orig))
+        return res
+
+    def __init__(self, *args, **kwargs):
+        self._headers = self._repair_headers()
+
+    # suppress spurious error from mypy
+    def __iter__(self):
+        return super().__iter__()
+
+    def __getitem__(self, item):
+        """
+        Warn users that a ``KeyError`` can be expected when a
+        mising key is supplied. Ref python/importlib_metadata#371.
+        """
+        res = super().__getitem__(item)
+        if res is None:
+            _warn()
+        return res
+
+    def _repair_headers(self):
+        def redent(value):
+            "Correct for RFC822 indentation"
+            if not value or '\n' not in value:
+                return value
+            return textwrap.dedent(' ' * 8 + value)
+
+        headers = [(key, redent(value)) for key, value in vars(self)['_headers']]
+        if self._payload:
+            headers.append(('Description', self.get_payload()))
+        return headers
+
+    @property
+    def json(self):
+        """
+        Convert PackageMetadata to a JSON-compatible format
+        per PEP 0566.
+        """
+
+        def transform(key):
+            value = self.get_all(key) if key in self.multiple_use_keys else self[key]
+            if key == 'Keywords':
+                value = re.split(r'\s+', value)
+            tk = key.lower().replace('-', '_')
+            return tk, value
+
+        return dict(map(transform, map(FoldedCase, self)))
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_collections.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_collections.py
new file mode 100644 (file)
index 0000000..cf0954e
--- /dev/null
@@ -0,0 +1,30 @@
+import collections
+
+
+# from jaraco.collections 3.3
+class FreezableDefaultDict(collections.defaultdict):
+    """
+    Often it is desirable to prevent the mutation of
+    a default dict after its initial construction, such
+    as to prevent mutation during iteration.
+
+    >>> dd = FreezableDefaultDict(list)
+    >>> dd[0].append('1')
+    >>> dd.freeze()
+    >>> dd[1]
+    []
+    >>> len(dd)
+    1
+    """
+
+    def __missing__(self, key):
+        return getattr(self, '_frozen', super().__missing__)(key)
+
+    def freeze(self):
+        self._frozen = lambda key: self.default_factory()
+
+
+class Pair(collections.namedtuple('Pair', 'name value')):
+    @classmethod
+    def parse(cls, text):
+        return cls(*map(str.strip, text.split("=", 1)))
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_compat.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_compat.py
new file mode 100644 (file)
index 0000000..b7abd09
--- /dev/null
@@ -0,0 +1,74 @@
+import os
+import sys
+import platform
+
+from typing import Union
+
+
+__all__ = ['install', 'NullFinder', 'Protocol']
+
+
+try:
+    from typing import Protocol
+except ImportError:  # pragma: no cover
+    # Python 3.7 compatibility
+    from typing_extensions import Protocol  # type: ignore
+
+
+def install(cls):
+    """
+    Class decorator for installation on sys.meta_path.
+
+    Adds the backport DistributionFinder to sys.meta_path and
+    attempts to disable the finder functionality of the stdlib
+    DistributionFinder.
+    """
+    sys.meta_path.append(cls())
+    disable_stdlib_finder()
+    return cls
+
+
+def disable_stdlib_finder():
+    """
+    Give the backport primacy for discovering path-based distributions
+    by monkey-patching the stdlib O_O.
+
+    See #91 for more background for rationale on this sketchy
+    behavior.
+    """
+
+    def matches(finder):
+        return getattr(
+            finder, '__module__', None
+        ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions')
+
+    for finder in filter(matches, sys.meta_path):  # pragma: nocover
+        del finder.find_distributions
+
+
+class NullFinder:
+    """
+    A "Finder" (aka "MetaClassFinder") that never finds any modules,
+    but may find distributions.
+    """
+
+    @staticmethod
+    def find_spec(*args, **kwargs):
+        return None
+
+
+def pypy_partial(val):
+    """
+    Adjust for variable stacklevel on partial under PyPy.
+
+    Workaround for #327.
+    """
+    is_pypy = platform.python_implementation() == 'PyPy'
+    return val + is_pypy
+
+
+if sys.version_info >= (3, 9):
+    StrPath = Union[str, os.PathLike[str]]
+else:
+    # PathLike is only subscriptable at runtime in 3.9+
+    StrPath = Union[str, "os.PathLike[str]"]  # pragma: no cover
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_functools.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_functools.py
new file mode 100644 (file)
index 0000000..71f66bd
--- /dev/null
@@ -0,0 +1,104 @@
+import types
+import functools
+
+
+# from jaraco.functools 3.3
+def method_cache(method, cache_wrapper=None):
+    """
+    Wrap lru_cache to support storing the cache data in the object instances.
+
+    Abstracts the common paradigm where the method explicitly saves an
+    underscore-prefixed protected property on first call and returns that
+    subsequently.
+
+    >>> class MyClass:
+    ...     calls = 0
+    ...
+    ...     @method_cache
+    ...     def method(self, value):
+    ...         self.calls += 1
+    ...         return value
+
+    >>> a = MyClass()
+    >>> a.method(3)
+    3
+    >>> for x in range(75):
+    ...     res = a.method(x)
+    >>> a.calls
+    75
+
+    Note that the apparent behavior will be exactly like that of lru_cache
+    except that the cache is stored on each instance, so values in one
+    instance will not flush values from another, and when an instance is
+    deleted, so are the cached values for that instance.
+
+    >>> b = MyClass()
+    >>> for x in range(35):
+    ...     res = b.method(x)
+    >>> b.calls
+    35
+    >>> a.method(0)
+    0
+    >>> a.calls
+    75
+
+    Note that if method had been decorated with ``functools.lru_cache()``,
+    a.calls would have been 76 (due to the cached value of 0 having been
+    flushed by the 'b' instance).
+
+    Clear the cache with ``.cache_clear()``
+
+    >>> a.method.cache_clear()
+
+    Same for a method that hasn't yet been called.
+
+    >>> c = MyClass()
+    >>> c.method.cache_clear()
+
+    Another cache wrapper may be supplied:
+
+    >>> cache = functools.lru_cache(maxsize=2)
+    >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache)
+    >>> a = MyClass()
+    >>> a.method2()
+    3
+
+    Caution - do not subsequently wrap the method with another decorator, such
+    as ``@property``, which changes the semantics of the function.
+
+    See also
+    http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
+    for another implementation and additional justification.
+    """
+    cache_wrapper = cache_wrapper or functools.lru_cache()
+
+    def wrapper(self, *args, **kwargs):
+        # it's the first call, replace the method with a cached, bound method
+        bound_method = types.MethodType(method, self)
+        cached_method = cache_wrapper(bound_method)
+        setattr(self, method.__name__, cached_method)
+        return cached_method(*args, **kwargs)
+
+    # Support cache clear even before cache has been created.
+    wrapper.cache_clear = lambda: None
+
+    return wrapper
+
+
+# From jaraco.functools 3.3
+def pass_none(func):
+    """
+    Wrap func so it's not called if its first param is None
+
+    >>> print_text = pass_none(print)
+    >>> print_text('text')
+    text
+    >>> print_text(None)
+    """
+
+    @functools.wraps(func)
+    def wrapper(param, *args, **kwargs):
+        if param is not None:
+            return func(param, *args, **kwargs)
+
+    return wrapper
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_itertools.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_itertools.py
new file mode 100644 (file)
index 0000000..d4ca9b9
--- /dev/null
@@ -0,0 +1,73 @@
+from itertools import filterfalse
+
+
+def unique_everseen(iterable, key=None):
+    "List unique elements, preserving order. Remember all elements ever seen."
+    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
+    # unique_everseen('ABBCcAD', str.lower) --> A B C D
+    seen = set()
+    seen_add = seen.add
+    if key is None:
+        for element in filterfalse(seen.__contains__, iterable):
+            seen_add(element)
+            yield element
+    else:
+        for element in iterable:
+            k = key(element)
+            if k not in seen:
+                seen_add(k)
+                yield element
+
+
+# copied from more_itertools 8.8
+def always_iterable(obj, base_type=(str, bytes)):
+    """If *obj* is iterable, return an iterator over its items::
+
+        >>> obj = (1, 2, 3)
+        >>> list(always_iterable(obj))
+        [1, 2, 3]
+
+    If *obj* is not iterable, return a one-item iterable containing *obj*::
+
+        >>> obj = 1
+        >>> list(always_iterable(obj))
+        [1]
+
+    If *obj* is ``None``, return an empty iterable:
+
+        >>> obj = None
+        >>> list(always_iterable(None))
+        []
+
+    By default, binary and text strings are not considered iterable::
+
+        >>> obj = 'foo'
+        >>> list(always_iterable(obj))
+        ['foo']
+
+    If *base_type* is set, objects for which ``isinstance(obj, base_type)``
+    returns ``True`` won't be considered iterable.
+
+        >>> obj = {'a': 1}
+        >>> list(always_iterable(obj))  # Iterate over the dict's keys
+        ['a']
+        >>> list(always_iterable(obj, base_type=dict))  # Treat dicts as a unit
+        [{'a': 1}]
+
+    Set *base_type* to ``None`` to avoid any special handling and treat objects
+    Python considers iterable as iterable:
+
+        >>> obj = 'foo'
+        >>> list(always_iterable(obj, base_type=None))
+        ['f', 'o', 'o']
+    """
+    if obj is None:
+        return iter(())
+
+    if (base_type is not None) and isinstance(obj, base_type):
+        return iter((obj,))
+
+    try:
+        return iter(obj)
+    except TypeError:
+        return iter((obj,))
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_meta.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_meta.py
new file mode 100644 (file)
index 0000000..0c7e879
--- /dev/null
@@ -0,0 +1,63 @@
+from ._compat import Protocol
+from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload
+
+
+_T = TypeVar("_T")
+
+
+class PackageMetadata(Protocol):
+    def __len__(self) -> int:
+        ...  # pragma: no cover
+
+    def __contains__(self, item: str) -> bool:
+        ...  # pragma: no cover
+
+    def __getitem__(self, key: str) -> str:
+        ...  # pragma: no cover
+
+    def __iter__(self) -> Iterator[str]:
+        ...  # pragma: no cover
+
+    @overload
+    def get(self, name: str, failobj: None = None) -> Optional[str]:
+        ...  # pragma: no cover
+
+    @overload
+    def get(self, name: str, failobj: _T) -> Union[str, _T]:
+        ...  # pragma: no cover
+
+    # overload per python/importlib_metadata#435
+    @overload
+    def get_all(self, name: str, failobj: None = None) -> Optional[List[Any]]:
+        ...  # pragma: no cover
+
+    @overload
+    def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]:
+        """
+        Return all values associated with a possibly multi-valued key.
+        """
+
+    @property
+    def json(self) -> Dict[str, Union[str, List[str]]]:
+        """
+        A JSON-compatible form of the metadata.
+        """
+
+
+class SimplePath(Protocol[_T]):
+    """
+    A minimal subset of pathlib.Path required by PathDistribution.
+    """
+
+    def joinpath(self, other: Union[str, _T]) -> _T:
+        ...  # pragma: no cover
+
+    def __truediv__(self, other: Union[str, _T]) -> _T:
+        ...  # pragma: no cover
+
+    @property
+    def parent(self) -> _T:
+        ...  # pragma: no cover
+
+    def read_text(self) -> str:
+        ...  # pragma: no cover
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_py39compat.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_py39compat.py
new file mode 100644 (file)
index 0000000..cde4558
--- /dev/null
@@ -0,0 +1,35 @@
+"""
+Compatibility layer with Python 3.8/3.9
+"""
+from typing import TYPE_CHECKING, Any, Optional
+
+if TYPE_CHECKING:  # pragma: no cover
+    # Prevent circular imports on runtime.
+    from . import Distribution, EntryPoint
+else:
+    Distribution = EntryPoint = Any
+
+
+def normalized_name(dist: Distribution) -> Optional[str]:
+    """
+    Honor name normalization for distributions that don't provide ``_normalized_name``.
+    """
+    try:
+        return dist._normalized_name
+    except AttributeError:
+        from . import Prepared  # -> delay to prevent circular imports.
+
+        return Prepared.normalize(getattr(dist, "name", None) or dist.metadata['Name'])
+
+
+def ep_matches(ep: EntryPoint, **params) -> bool:
+    """
+    Workaround for ``EntryPoint`` objects without the ``matches`` method.
+    """
+    try:
+        return ep.matches(**params)
+    except AttributeError:
+        from . import EntryPoint  # -> delay to prevent circular imports.
+
+        # Reconstruct the EntryPoint object to make sure it is compatible.
+        return EntryPoint(ep.name, ep.value, ep.group).matches(**params)
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_text.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/_text.py
new file mode 100644 (file)
index 0000000..c88cfbb
--- /dev/null
@@ -0,0 +1,99 @@
+import re
+
+from ._functools import method_cache
+
+
+# from jaraco.text 3.5
+class FoldedCase(str):
+    """
+    A case insensitive string class; behaves just like str
+    except compares equal when the only variation is case.
+
+    >>> s = FoldedCase('hello world')
+
+    >>> s == 'Hello World'
+    True
+
+    >>> 'Hello World' == s
+    True
+
+    >>> s != 'Hello World'
+    False
+
+    >>> s.index('O')
+    4
+
+    >>> s.split('O')
+    ['hell', ' w', 'rld']
+
+    >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
+    ['alpha', 'Beta', 'GAMMA']
+
+    Sequence membership is straightforward.
+
+    >>> "Hello World" in [s]
+    True
+    >>> s in ["Hello World"]
+    True
+
+    You may test for set inclusion, but candidate and elements
+    must both be folded.
+
+    >>> FoldedCase("Hello World") in {s}
+    True
+    >>> s in {FoldedCase("Hello World")}
+    True
+
+    String inclusion works as long as the FoldedCase object
+    is on the right.
+
+    >>> "hello" in FoldedCase("Hello World")
+    True
+
+    But not if the FoldedCase object is on the left:
+
+    >>> FoldedCase('hello') in 'Hello World'
+    False
+
+    In that case, use in_:
+
+    >>> FoldedCase('hello').in_('Hello World')
+    True
+
+    >>> FoldedCase('hello') > FoldedCase('Hello')
+    False
+    """
+
+    def __lt__(self, other):
+        return self.lower() < other.lower()
+
+    def __gt__(self, other):
+        return self.lower() > other.lower()
+
+    def __eq__(self, other):
+        return self.lower() == other.lower()
+
+    def __ne__(self, other):
+        return self.lower() != other.lower()
+
+    def __hash__(self):
+        return hash(self.lower())
+
+    def __contains__(self, other):
+        return super().lower().__contains__(other.lower())
+
+    def in_(self, other):
+        "Does self appear in other?"
+        return self in FoldedCase(other)
+
+    # cache lower since it's likely to be called frequently.
+    @method_cache
+    def lower(self):
+        return super().lower()
+
+    def index(self, sub):
+        return self.lower().index(sub.lower())
+
+    def split(self, splitter=' ', maxsplit=0):
+        pattern = re.compile(re.escape(splitter), re.I)
+        return pattern.split(self, maxsplit)
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/py.typed b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/importlib_metadata/py.typed
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/mypy.ini b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/mypy.ini
new file mode 100644 (file)
index 0000000..b6f9727
--- /dev/null
@@ -0,0 +1,5 @@
+[mypy]
+ignore_missing_imports = True
+# required to support namespace packages
+# https://github.com/python/mypy/issues/14057
+explicit_package_bases = True
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/newsfragments/+drop-py37.feature.rst b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/newsfragments/+drop-py37.feature.rst
new file mode 100644 (file)
index 0000000..ccabdaa
--- /dev/null
@@ -0,0 +1 @@
+Require Python 3.8 or later.
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example/example/__init__.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example/example/__init__.py
new file mode 100644 (file)
index 0000000..ba73b74
--- /dev/null
@@ -0,0 +1,2 @@
+def main():
+    return 'example'
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example/setup.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example/setup.py
new file mode 100644 (file)
index 0000000..479488a
--- /dev/null
@@ -0,0 +1,11 @@
+from setuptools import setup
+
+setup(
+    name='example',
+    version='21.12',
+    license='Apache Software License',
+    packages=['example'],
+    entry_points={
+        'console_scripts': ['example = example:main', 'Example=example:main'],
+    },
+)
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example2/example2/__init__.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example2/example2/__init__.py
new file mode 100644 (file)
index 0000000..de645c2
--- /dev/null
@@ -0,0 +1,2 @@
+def main():
+    return "example"
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example2/pyproject.toml b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/prepare/example2/pyproject.toml
new file mode 100644 (file)
index 0000000..011f475
--- /dev/null
@@ -0,0 +1,10 @@
+[build-system]
+build-backend = 'trampolim'
+requires = ['trampolim']
+
+[project]
+name = 'example2'
+version = '1.0.0'
+
+[project.scripts]
+example = 'example2:main'
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/pyproject.toml b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/pyproject.toml
new file mode 100644 (file)
index 0000000..d5f3487
--- /dev/null
@@ -0,0 +1,20 @@
+[build-system]
+requires = ["setuptools>=56", "setuptools_scm[toml]>=3.4.1"]
+build-backend = "setuptools.build_meta"
+
+[tool.black]
+skip-string-normalization = true
+
+[tool.setuptools_scm]
+
+[tool.pytest-enabler.black]
+addopts = "--black"
+
+[tool.pytest-enabler.mypy]
+addopts = "--mypy"
+
+[tool.pytest-enabler.cov]
+addopts = "--cov"
+
+[tool.pytest-enabler.ruff]
+addopts = "--ruff"
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/pytest.ini b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/pytest.ini
new file mode 100644 (file)
index 0000000..d9a15ed
--- /dev/null
@@ -0,0 +1,27 @@
+[pytest]
+norecursedirs=dist build .tox .eggs
+addopts=--doctest-modules
+filterwarnings=
+       ## upstream
+
+       # Ensure ResourceWarnings are emitted
+       default::ResourceWarning
+
+       # shopkeep/pytest-black#55
+       ignore:<class 'pytest_black.BlackItem'> is not using a cooperative constructor:pytest.PytestDeprecationWarning
+       ignore:The \(fspath. py.path.local\) argument to BlackItem is deprecated.:pytest.PytestDeprecationWarning
+       ignore:BlackItem is an Item subclass and should not be a collector:pytest.PytestWarning
+
+       # shopkeep/pytest-black#67
+       ignore:'encoding' argument not specified::pytest_black
+
+       # realpython/pytest-mypy#152
+       ignore:'encoding' argument not specified::pytest_mypy
+
+       # python/cpython#100750
+       ignore:'encoding' argument not specified::platform
+
+       # pypa/build#615
+       ignore:'encoding' argument not specified::build.env
+
+       ## end upstream
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/setup.cfg b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/setup.cfg
new file mode 100644 (file)
index 0000000..68aef9f
--- /dev/null
@@ -0,0 +1,69 @@
+[metadata]
+name = importlib_metadata
+author = Jason R. Coombs
+author_email = jaraco@jaraco.com
+description = Read metadata from Python packages
+long_description = file:README.rst
+url = https://github.com/python/importlib_metadata
+classifiers =
+       Development Status :: 5 - Production/Stable
+       Intended Audience :: Developers
+       License :: OSI Approved :: Apache Software License
+       Programming Language :: Python :: 3
+       Programming Language :: Python :: 3 :: Only
+
+[options]
+packages = find_namespace:
+include_package_data = true
+python_requires = >=3.8
+install_requires =
+       zipp>=0.5
+       typing-extensions>=3.6.4; python_version < "3.8"
+
+[options.packages.find]
+exclude =
+       build*
+       dist*
+       docs*
+       tests*
+       prepare*
+
+[options.extras_require]
+testing =
+       # upstream
+       pytest >= 6
+       pytest-checkdocs >= 2.4
+       pytest-black >= 0.3.7; \
+               # workaround for jaraco/skeleton#22
+               python_implementation != "PyPy"
+       pytest-cov
+       pytest-mypy >= 0.9.1; \
+               # workaround for jaraco/skeleton#22
+               python_implementation != "PyPy"
+       pytest-enabler >= 1.3
+       pytest-ruff
+
+       # local
+       importlib_resources>=1.3; python_version < "3.9"
+       packaging
+       pyfakefs
+       flufl.flake8
+       pytest-perf >= 0.9.2
+
+docs =
+       # upstream
+       sphinx >= 3.5
+       jaraco.packaging >= 9
+       rst.linker >= 1.9
+       furo
+       sphinx-lint
+
+       # tidelift
+       jaraco.tidelift >= 1.4
+
+       # local
+
+perf =
+       ipython
+
+[options.entry_points]
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/__init__.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/_context.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/_context.py
new file mode 100644 (file)
index 0000000..8a53eb5
--- /dev/null
@@ -0,0 +1,13 @@
+import contextlib
+
+
+# from jaraco.context 4.3
+class suppress(contextlib.suppress, contextlib.ContextDecorator):
+    """
+    A version of contextlib.suppress with decorator support.
+
+    >>> @suppress(KeyError)
+    ... def key_error():
+    ...     {}['']
+    >>> key_error()
+    """
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/_path.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/_path.py
new file mode 100644 (file)
index 0000000..9762c5e
--- /dev/null
@@ -0,0 +1,125 @@
+# from jaraco.path 3.6
+
+import functools
+import pathlib
+from typing import Dict, Union
+
+try:
+    from typing import Protocol, runtime_checkable
+except ImportError:  # pragma: no cover
+    # Python 3.7
+    from typing_extensions import Protocol, runtime_checkable  # type: ignore
+
+
+class Symlink(str):
+    """
+    A string indicating the target of a symlink.
+    """
+
+
+FilesSpec = Dict[str, Union[str, bytes, Symlink, 'FilesSpec']]  # type: ignore
+
+
+@runtime_checkable
+class TreeMaker(Protocol):
+    def __truediv__(self, *args, **kwargs):
+        ...  # pragma: no cover
+
+    def mkdir(self, **kwargs):
+        ...  # pragma: no cover
+
+    def write_text(self, content, **kwargs):
+        ...  # pragma: no cover
+
+    def write_bytes(self, content):
+        ...  # pragma: no cover
+
+    def symlink_to(self, target):
+        ...  # pragma: no cover
+
+
+def _ensure_tree_maker(obj: Union[str, TreeMaker]) -> TreeMaker:
+    return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj)  # type: ignore
+
+
+def build(
+    spec: FilesSpec,
+    prefix: Union[str, TreeMaker] = pathlib.Path(),  # type: ignore
+):
+    """
+    Build a set of files/directories, as described by the spec.
+
+    Each key represents a pathname, and the value represents
+    the content. Content may be a nested directory.
+
+    >>> spec = {
+    ...     'README.txt': "A README file",
+    ...     "foo": {
+    ...         "__init__.py": "",
+    ...         "bar": {
+    ...             "__init__.py": "",
+    ...         },
+    ...         "baz.py": "# Some code",
+    ...         "bar.py": Symlink("baz.py"),
+    ...     },
+    ...     "bing": Symlink("foo"),
+    ... }
+    >>> target = getfixture('tmp_path')
+    >>> build(spec, target)
+    >>> target.joinpath('foo/baz.py').read_text(encoding='utf-8')
+    '# Some code'
+    >>> target.joinpath('bing/bar.py').read_text(encoding='utf-8')
+    '# Some code'
+    """
+    for name, contents in spec.items():
+        create(contents, _ensure_tree_maker(prefix) / name)
+
+
+@functools.singledispatch
+def create(content: Union[str, bytes, FilesSpec], path):
+    path.mkdir(exist_ok=True)
+    build(content, prefix=path)  # type: ignore
+
+
+@create.register
+def _(content: bytes, path):
+    path.write_bytes(content)
+
+
+@create.register
+def _(content: str, path):
+    path.write_text(content, encoding='utf-8')
+
+
+@create.register
+def _(content: Symlink, path):
+    path.symlink_to(content)
+
+
+class Recording:
+    """
+    A TreeMaker object that records everything that would be written.
+
+    >>> r = Recording()
+    >>> build({'foo': {'foo1.txt': 'yes'}, 'bar.txt': 'abc'}, r)
+    >>> r.record
+    ['foo/foo1.txt', 'bar.txt']
+    """
+
+    def __init__(self, loc=pathlib.PurePosixPath(), record=None):
+        self.loc = loc
+        self.record = record if record is not None else []
+
+    def __truediv__(self, other):
+        return Recording(self.loc / other, self.record)
+
+    def write_text(self, content, **kwargs):
+        self.record.append(str(self.loc))
+
+    write_bytes = write_text
+
+    def mkdir(self, **kwargs):
+        return
+
+    def symlink_to(self, target):
+        pass
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/__init__.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example-21.12-py3-none-any.whl b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example-21.12-py3-none-any.whl
new file mode 100644 (file)
index 0000000..641ab07
Binary files /dev/null and b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example-21.12-py3-none-any.whl differ
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example-21.12-py3.6.egg b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example-21.12-py3.6.egg
new file mode 100644 (file)
index 0000000..cdb298a
Binary files /dev/null and b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example-21.12-py3.6.egg differ
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example2-1.0.0-py3-none-any.whl b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example2-1.0.0-py3-none-any.whl
new file mode 100644 (file)
index 0000000..5ca9365
Binary files /dev/null and b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/data/example2-1.0.0-py3-none-any.whl differ
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/fixtures.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/fixtures.py
new file mode 100644 (file)
index 0000000..c0b0fa3
--- /dev/null
@@ -0,0 +1,380 @@
+import os
+import sys
+import copy
+import shutil
+import pathlib
+import tempfile
+import textwrap
+import functools
+import contextlib
+
+from .py39compat import FS_NONASCII
+
+from . import _path
+from ._path import FilesSpec
+
+
+try:
+    from importlib import resources  # type: ignore
+
+    getattr(resources, 'files')
+    getattr(resources, 'as_file')
+except (ImportError, AttributeError):
+    import importlib_resources as resources  # type: ignore
+
+
+@contextlib.contextmanager
+def tempdir():
+    tmpdir = tempfile.mkdtemp()
+    try:
+        yield pathlib.Path(tmpdir)
+    finally:
+        shutil.rmtree(tmpdir)
+
+
+@contextlib.contextmanager
+def save_cwd():
+    orig = os.getcwd()
+    try:
+        yield
+    finally:
+        os.chdir(orig)
+
+
+@contextlib.contextmanager
+def tempdir_as_cwd():
+    with tempdir() as tmp:
+        with save_cwd():
+            os.chdir(str(tmp))
+            yield tmp
+
+
+@contextlib.contextmanager
+def install_finder(finder):
+    sys.meta_path.append(finder)
+    try:
+        yield
+    finally:
+        sys.meta_path.remove(finder)
+
+
+class Fixtures:
+    def setUp(self):
+        self.fixtures = contextlib.ExitStack()
+        self.addCleanup(self.fixtures.close)
+
+
+class SiteDir(Fixtures):
+    def setUp(self):
+        super().setUp()
+        self.site_dir = self.fixtures.enter_context(tempdir())
+
+
+class OnSysPath(Fixtures):
+    @staticmethod
+    @contextlib.contextmanager
+    def add_sys_path(dir):
+        sys.path[:0] = [str(dir)]
+        try:
+            yield
+        finally:
+            sys.path.remove(str(dir))
+
+    def setUp(self):
+        super().setUp()
+        self.fixtures.enter_context(self.add_sys_path(self.site_dir))
+
+
+class DistInfoPkg(OnSysPath, SiteDir):
+    files: FilesSpec = {
+        "distinfo_pkg-1.0.0.dist-info": {
+            "METADATA": """
+                Name: distinfo-pkg
+                Author: Steven Ma
+                Version: 1.0.0
+                Requires-Dist: wheel >= 1.0
+                Requires-Dist: pytest; extra == 'test'
+                Keywords: sample package
+
+                Once upon a time
+                There was a distinfo pkg
+                """,
+            "RECORD": "mod.py,sha256=abc,20\n",
+            "entry_points.txt": """
+                [entries]
+                main = mod:main
+                ns:sub = mod:main
+            """,
+        },
+        "mod.py": """
+            def main():
+                print("hello world")
+            """,
+    }
+
+    def setUp(self):
+        super().setUp()
+        build_files(DistInfoPkg.files, self.site_dir)
+
+    def make_uppercase(self):
+        """
+        Rewrite metadata with everything uppercase.
+        """
+        shutil.rmtree(self.site_dir / "distinfo_pkg-1.0.0.dist-info")
+        files = copy.deepcopy(DistInfoPkg.files)
+        info = files["distinfo_pkg-1.0.0.dist-info"]
+        info["METADATA"] = info["METADATA"].upper()
+        build_files(files, self.site_dir)
+
+
+class DistInfoPkgWithDot(OnSysPath, SiteDir):
+    files: FilesSpec = {
+        "pkg_dot-1.0.0.dist-info": {
+            "METADATA": """
+                Name: pkg.dot
+                Version: 1.0.0
+                """,
+        },
+    }
+
+    def setUp(self):
+        super().setUp()
+        build_files(DistInfoPkgWithDot.files, self.site_dir)
+
+
+class DistInfoPkgWithDotLegacy(OnSysPath, SiteDir):
+    files: FilesSpec = {
+        "pkg.dot-1.0.0.dist-info": {
+            "METADATA": """
+                Name: pkg.dot
+                Version: 1.0.0
+                """,
+        },
+        "pkg.lot.egg-info": {
+            "METADATA": """
+                Name: pkg.lot
+                Version: 1.0.0
+                """,
+        },
+    }
+
+    def setUp(self):
+        super().setUp()
+        build_files(DistInfoPkgWithDotLegacy.files, self.site_dir)
+
+
+class DistInfoPkgOffPath(SiteDir):
+    def setUp(self):
+        super().setUp()
+        build_files(DistInfoPkg.files, self.site_dir)
+
+
+class EggInfoPkg(OnSysPath, SiteDir):
+    files: FilesSpec = {
+        "egginfo_pkg.egg-info": {
+            "PKG-INFO": """
+                Name: egginfo-pkg
+                Author: Steven Ma
+                License: Unknown
+                Version: 1.0.0
+                Classifier: Intended Audience :: Developers
+                Classifier: Topic :: Software Development :: Libraries
+                Keywords: sample package
+                Description: Once upon a time
+                        There was an egginfo package
+                """,
+            "SOURCES.txt": """
+                mod.py
+                egginfo_pkg.egg-info/top_level.txt
+            """,
+            "entry_points.txt": """
+                [entries]
+                main = mod:main
+            """,
+            "requires.txt": """
+                wheel >= 1.0; python_version >= "2.7"
+                [test]
+                pytest
+            """,
+            "top_level.txt": "mod\n",
+        },
+        "mod.py": """
+            def main():
+                print("hello world")
+            """,
+    }
+
+    def setUp(self):
+        super().setUp()
+        build_files(EggInfoPkg.files, prefix=self.site_dir)
+
+
+class EggInfoPkgPipInstalledNoToplevel(OnSysPath, SiteDir):
+    files: FilesSpec = {
+        "egg_with_module_pkg.egg-info": {
+            "PKG-INFO": "Name: egg_with_module-pkg",
+            # SOURCES.txt is made from the source archive, and contains files
+            # (setup.py) that are not present after installation.
+            "SOURCES.txt": """
+                egg_with_module.py
+                setup.py
+                egg_with_module_pkg.egg-info/PKG-INFO
+                egg_with_module_pkg.egg-info/SOURCES.txt
+                egg_with_module_pkg.egg-info/top_level.txt
+            """,
+            # installed-files.txt is written by pip, and is a strictly more
+            # accurate source than SOURCES.txt as to the installed contents of
+            # the package.
+            "installed-files.txt": """
+                ../egg_with_module.py
+                PKG-INFO
+                SOURCES.txt
+                top_level.txt
+            """,
+            # missing top_level.txt (to trigger fallback to installed-files.txt)
+        },
+        "egg_with_module.py": """
+            def main():
+                print("hello world")
+            """,
+    }
+
+    def setUp(self):
+        super().setUp()
+        build_files(EggInfoPkgPipInstalledNoToplevel.files, prefix=self.site_dir)
+
+
+class EggInfoPkgPipInstalledNoModules(OnSysPath, SiteDir):
+    files: FilesSpec = {
+        "egg_with_no_modules_pkg.egg-info": {
+            "PKG-INFO": "Name: egg_with_no_modules-pkg",
+            # SOURCES.txt is made from the source archive, and contains files
+            # (setup.py) that are not present after installation.
+            "SOURCES.txt": """
+                setup.py
+                egg_with_no_modules_pkg.egg-info/PKG-INFO
+                egg_with_no_modules_pkg.egg-info/SOURCES.txt
+                egg_with_no_modules_pkg.egg-info/top_level.txt
+            """,
+            # installed-files.txt is written by pip, and is a strictly more
+            # accurate source than SOURCES.txt as to the installed contents of
+            # the package.
+            "installed-files.txt": """
+                PKG-INFO
+                SOURCES.txt
+                top_level.txt
+            """,
+            # top_level.txt correctly reflects that no modules are installed
+            "top_level.txt": b"\n",
+        },
+    }
+
+    def setUp(self):
+        super().setUp()
+        build_files(EggInfoPkgPipInstalledNoModules.files, prefix=self.site_dir)
+
+
+class EggInfoPkgSourcesFallback(OnSysPath, SiteDir):
+    files: FilesSpec = {
+        "sources_fallback_pkg.egg-info": {
+            "PKG-INFO": "Name: sources_fallback-pkg",
+            # SOURCES.txt is made from the source archive, and contains files
+            # (setup.py) that are not present after installation.
+            "SOURCES.txt": """
+                sources_fallback.py
+                setup.py
+                sources_fallback_pkg.egg-info/PKG-INFO
+                sources_fallback_pkg.egg-info/SOURCES.txt
+            """,
+            # missing installed-files.txt (i.e. not installed by pip) and
+            # missing top_level.txt (to trigger fallback to SOURCES.txt)
+        },
+        "sources_fallback.py": """
+            def main():
+                print("hello world")
+            """,
+    }
+
+    def setUp(self):
+        super().setUp()
+        build_files(EggInfoPkgSourcesFallback.files, prefix=self.site_dir)
+
+
+class EggInfoFile(OnSysPath, SiteDir):
+    files: FilesSpec = {
+        "egginfo_file.egg-info": """
+            Metadata-Version: 1.0
+            Name: egginfo_file
+            Version: 0.1
+            Summary: An example package
+            Home-page: www.example.com
+            Author: Eric Haffa-Vee
+            Author-email: eric@example.coms
+            License: UNKNOWN
+            Description: UNKNOWN
+            Platform: UNKNOWN
+            """,
+    }
+
+    def setUp(self):
+        super().setUp()
+        build_files(EggInfoFile.files, prefix=self.site_dir)
+
+
+# dedent all text strings before writing
+orig = _path.create.registry[str]
+_path.create.register(str, lambda content, path: orig(DALS(content), path))
+
+
+build_files = _path.build
+
+
+def build_record(file_defs):
+    return ''.join(f'{name},,\n' for name in record_names(file_defs))
+
+
+def record_names(file_defs):
+    recording = _path.Recording()
+    _path.build(file_defs, recording)
+    return recording.record
+
+
+class FileBuilder:
+    def unicode_filename(self):
+        return FS_NONASCII or self.skip("File system does not support non-ascii.")
+
+
+def DALS(str):
+    "Dedent and left-strip"
+    return textwrap.dedent(str).lstrip()
+
+
+class ZipFixtures:
+    root = 'tests.data'
+
+    def _fixture_on_path(self, filename):
+        pkg_file = resources.files(self.root).joinpath(filename)
+        file = self.resources.enter_context(resources.as_file(pkg_file))
+        assert file.name.startswith('example'), file.name
+        sys.path.insert(0, str(file))
+        self.resources.callback(sys.path.pop, 0)
+
+    def setUp(self):
+        # Add self.zip_name to the front of sys.path.
+        self.resources = contextlib.ExitStack()
+        self.addCleanup(self.resources.close)
+
+
+def parameterize(*args_set):
+    """Run test method with a series of parameters."""
+
+    def wrapper(func):
+        @functools.wraps(func)
+        def _inner(self):
+            for args in args_set:
+                with self.subTest(**args):
+                    func(self, **args)
+
+        return _inner
+
+    return wrapper
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/py39compat.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/py39compat.py
new file mode 100644 (file)
index 0000000..926dcad
--- /dev/null
@@ -0,0 +1,4 @@
+try:
+    from test.support.os_helper import FS_NONASCII
+except ImportError:
+    from test.support import FS_NONASCII  # noqa
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_api.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_api.py
new file mode 100644 (file)
index 0000000..a85c62a
--- /dev/null
@@ -0,0 +1,326 @@
+import re
+import textwrap
+import unittest
+import warnings
+import importlib
+import contextlib
+
+from . import fixtures
+from importlib_metadata import (
+    Distribution,
+    PackageNotFoundError,
+    distribution,
+    entry_points,
+    files,
+    metadata,
+    requires,
+    version,
+)
+
+
+@contextlib.contextmanager
+def suppress_known_deprecation():
+    with warnings.catch_warnings(record=True) as ctx:
+        warnings.simplefilter('default', category=DeprecationWarning)
+        yield ctx
+
+
+class APITests(
+    fixtures.EggInfoPkg,
+    fixtures.EggInfoPkgPipInstalledNoToplevel,
+    fixtures.EggInfoPkgPipInstalledNoModules,
+    fixtures.EggInfoPkgSourcesFallback,
+    fixtures.DistInfoPkg,
+    fixtures.DistInfoPkgWithDot,
+    fixtures.EggInfoFile,
+    unittest.TestCase,
+):
+    version_pattern = r'\d+\.\d+(\.\d)?'
+
+    def test_retrieves_version_of_self(self):
+        pkg_version = version('egginfo-pkg')
+        assert isinstance(pkg_version, str)
+        assert re.match(self.version_pattern, pkg_version)
+
+    def test_retrieves_version_of_distinfo_pkg(self):
+        pkg_version = version('distinfo-pkg')
+        assert isinstance(pkg_version, str)
+        assert re.match(self.version_pattern, pkg_version)
+
+    def test_for_name_does_not_exist(self):
+        with self.assertRaises(PackageNotFoundError):
+            distribution('does-not-exist')
+
+    def test_name_normalization(self):
+        names = 'pkg.dot', 'pkg_dot', 'pkg-dot', 'pkg..dot', 'Pkg.Dot'
+        for name in names:
+            with self.subTest(name):
+                assert distribution(name).metadata['Name'] == 'pkg.dot'
+
+    def test_prefix_not_matched(self):
+        prefixes = 'p', 'pkg', 'pkg.'
+        for prefix in prefixes:
+            with self.subTest(prefix):
+                with self.assertRaises(PackageNotFoundError):
+                    distribution(prefix)
+
+    def test_for_top_level(self):
+        tests = [
+            ('egginfo-pkg', 'mod'),
+            ('egg_with_no_modules-pkg', ''),
+        ]
+        for pkg_name, expect_content in tests:
+            with self.subTest(pkg_name):
+                self.assertEqual(
+                    distribution(pkg_name).read_text('top_level.txt').strip(),
+                    expect_content,
+                )
+
+    def test_read_text(self):
+        tests = [
+            ('egginfo-pkg', 'mod\n'),
+            ('egg_with_no_modules-pkg', '\n'),
+        ]
+        for pkg_name, expect_content in tests:
+            with self.subTest(pkg_name):
+                top_level = [
+                    path for path in files(pkg_name) if path.name == 'top_level.txt'
+                ][0]
+                self.assertEqual(top_level.read_text(), expect_content)
+
+    def test_entry_points(self):
+        eps = entry_points()
+        assert 'entries' in eps.groups
+        entries = eps.select(group='entries')
+        assert 'main' in entries.names
+        ep = entries['main']
+        self.assertEqual(ep.value, 'mod:main')
+        self.assertEqual(ep.extras, [])
+
+    def test_entry_points_distribution(self):
+        entries = entry_points(group='entries')
+        for entry in ("main", "ns:sub"):
+            ep = entries[entry]
+            self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg'))
+            self.assertEqual(ep.dist.version, "1.0.0")
+
+    def test_entry_points_unique_packages_normalized(self):
+        """
+        Entry points should only be exposed for the first package
+        on sys.path with a given name (even when normalized).
+        """
+        alt_site_dir = self.fixtures.enter_context(fixtures.tempdir())
+        self.fixtures.enter_context(self.add_sys_path(alt_site_dir))
+        alt_pkg = {
+            "DistInfo_pkg-1.1.0.dist-info": {
+                "METADATA": """
+                Name: distinfo-pkg
+                Version: 1.1.0
+                """,
+                "entry_points.txt": """
+                [entries]
+                main = mod:altmain
+            """,
+            },
+        }
+        fixtures.build_files(alt_pkg, alt_site_dir)
+        entries = entry_points(group='entries')
+        assert not any(
+            ep.dist.name == 'distinfo-pkg' and ep.dist.version == '1.0.0'
+            for ep in entries
+        )
+        # ns:sub doesn't exist in alt_pkg
+        assert 'ns:sub' not in entries.names
+
+    def test_entry_points_missing_name(self):
+        with self.assertRaises(KeyError):
+            entry_points(group='entries')['missing']
+
+    def test_entry_points_missing_group(self):
+        assert entry_points(group='missing') == ()
+
+    def test_entry_points_allows_no_attributes(self):
+        ep = entry_points().select(group='entries', name='main')
+        with self.assertRaises(AttributeError):
+            ep.foo = 4
+
+    def test_metadata_for_this_package(self):
+        md = metadata('egginfo-pkg')
+        assert md['author'] == 'Steven Ma'
+        assert md['LICENSE'] == 'Unknown'
+        assert md['Name'] == 'egginfo-pkg'
+        classifiers = md.get_all('Classifier')
+        assert 'Topic :: Software Development :: Libraries' in classifiers
+
+    def test_importlib_metadata_version(self):
+        resolved = version('importlib-metadata')
+        assert re.match(self.version_pattern, resolved)
+
+    def test_missing_key_legacy(self):
+        """
+        Requesting a missing key will still return None, but warn.
+        """
+        md = metadata('distinfo-pkg')
+        with suppress_known_deprecation():
+            assert md['does-not-exist'] is None
+
+    def test_get_key(self):
+        """
+        Getting a key gets the key.
+        """
+        md = metadata('egginfo-pkg')
+        assert md.get('Name') == 'egginfo-pkg'
+
+    def test_get_missing_key(self):
+        """
+        Requesting a missing key will return None.
+        """
+        md = metadata('distinfo-pkg')
+        assert md.get('does-not-exist') is None
+
+    @staticmethod
+    def _test_files(files):
+        root = files[0].root
+        for file in files:
+            assert file.root == root
+            assert not file.hash or file.hash.value
+            assert not file.hash or file.hash.mode == 'sha256'
+            assert not file.size or file.size >= 0
+            assert file.locate().exists()
+            assert isinstance(file.read_binary(), bytes)
+            if file.name.endswith('.py'):
+                file.read_text()
+
+    def test_file_hash_repr(self):
+        util = [p for p in files('distinfo-pkg') if p.name == 'mod.py'][0]
+        self.assertRegex(repr(util.hash), '<FileHash mode: sha256 value: .*>')
+
+    def test_files_dist_info(self):
+        self._test_files(files('distinfo-pkg'))
+
+    def test_files_egg_info(self):
+        self._test_files(files('egginfo-pkg'))
+        self._test_files(files('egg_with_module-pkg'))
+        self._test_files(files('egg_with_no_modules-pkg'))
+        self._test_files(files('sources_fallback-pkg'))
+
+    def test_version_egg_info_file(self):
+        self.assertEqual(version('egginfo-file'), '0.1')
+
+    def test_requires_egg_info_file(self):
+        requirements = requires('egginfo-file')
+        self.assertIsNone(requirements)
+
+    def test_requires_egg_info(self):
+        deps = requires('egginfo-pkg')
+        assert len(deps) == 2
+        assert any(dep == 'wheel >= 1.0; python_version >= "2.7"' for dep in deps)
+
+    def test_requires_egg_info_empty(self):
+        fixtures.build_files(
+            {
+                'requires.txt': '',
+            },
+            self.site_dir.joinpath('egginfo_pkg.egg-info'),
+        )
+        deps = requires('egginfo-pkg')
+        assert deps == []
+
+    def test_requires_dist_info(self):
+        deps = requires('distinfo-pkg')
+        assert len(deps) == 2
+        assert all(deps)
+        assert 'wheel >= 1.0' in deps
+        assert "pytest; extra == 'test'" in deps
+
+    def test_more_complex_deps_requires_text(self):
+        requires = textwrap.dedent(
+            """
+            dep1
+            dep2
+
+            [:python_version < "3"]
+            dep3
+
+            [extra1]
+            dep4
+            dep6@ git+https://example.com/python/dep.git@v1.0.0
+
+            [extra2:python_version < "3"]
+            dep5
+            """
+        )
+        deps = sorted(Distribution._deps_from_requires_text(requires))
+        expected = [
+            'dep1',
+            'dep2',
+            'dep3; python_version < "3"',
+            'dep4; extra == "extra1"',
+            'dep5; (python_version < "3") and extra == "extra2"',
+            'dep6@ git+https://example.com/python/dep.git@v1.0.0 ; extra == "extra1"',
+        ]
+        # It's important that the environment marker expression be
+        # wrapped in parentheses to avoid the following 'and' binding more
+        # tightly than some other part of the environment expression.
+
+        assert deps == expected
+
+    def test_as_json(self):
+        md = metadata('distinfo-pkg').json
+        assert 'name' in md
+        assert md['keywords'] == ['sample', 'package']
+        desc = md['description']
+        assert desc.startswith('Once upon a time\nThere was')
+        assert len(md['requires_dist']) == 2
+
+    def test_as_json_egg_info(self):
+        md = metadata('egginfo-pkg').json
+        assert 'name' in md
+        assert md['keywords'] == ['sample', 'package']
+        desc = md['description']
+        assert desc.startswith('Once upon a time\nThere was')
+        assert len(md['classifier']) == 2
+
+    def test_as_json_odd_case(self):
+        self.make_uppercase()
+        md = metadata('distinfo-pkg').json
+        assert 'name' in md
+        assert len(md['requires_dist']) == 2
+        assert md['keywords'] == ['SAMPLE', 'PACKAGE']
+
+
+class LegacyDots(fixtures.DistInfoPkgWithDotLegacy, unittest.TestCase):
+    def test_name_normalization(self):
+        names = 'pkg.dot', 'pkg_dot', 'pkg-dot', 'pkg..dot', 'Pkg.Dot'
+        for name in names:
+            with self.subTest(name):
+                assert distribution(name).metadata['Name'] == 'pkg.dot'
+
+    def test_name_normalization_versionless_egg_info(self):
+        names = 'pkg.lot', 'pkg_lot', 'pkg-lot', 'pkg..lot', 'Pkg.Lot'
+        for name in names:
+            with self.subTest(name):
+                assert distribution(name).metadata['Name'] == 'pkg.lot'
+
+
+class OffSysPathTests(fixtures.DistInfoPkgOffPath, unittest.TestCase):
+    def test_find_distributions_specified_path(self):
+        dists = Distribution.discover(path=[str(self.site_dir)])
+        assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists)
+
+    def test_distribution_at_pathlib(self):
+        """Demonstrate how to load metadata direct from a directory."""
+        dist_info_path = self.site_dir / 'distinfo_pkg-1.0.0.dist-info'
+        dist = Distribution.at(dist_info_path)
+        assert dist.version == '1.0.0'
+
+    def test_distribution_at_str(self):
+        dist_info_path = self.site_dir / 'distinfo_pkg-1.0.0.dist-info'
+        dist = Distribution.at(str(dist_info_path))
+        assert dist.version == '1.0.0'
+
+
+class InvalidateCache(unittest.TestCase):
+    def test_invalidate_cache(self):
+        # No externally observable behavior, but ensures test coverage...
+        importlib.invalidate_caches()
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_integration.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_integration.py
new file mode 100644 (file)
index 0000000..f7af67f
--- /dev/null
@@ -0,0 +1,53 @@
+"""
+Test behaviors specific to importlib_metadata.
+
+These tests are excluded downstream in CPython as they
+test functionality only in importlib_metadata or require
+behaviors ('packaging') that aren't available in the
+stdlib.
+"""
+
+import unittest
+import packaging.requirements
+import packaging.version
+
+from . import fixtures
+from importlib_metadata import (
+    _compat,
+    version,
+)
+
+
+class IntegrationTests(fixtures.DistInfoPkg, unittest.TestCase):
+    def test_package_spec_installed(self):
+        """
+        Illustrate the recommended procedure to determine if
+        a specified version of a package is installed.
+        """
+
+        def is_installed(package_spec):
+            req = packaging.requirements.Requirement(package_spec)
+            return version(req.name) in req.specifier
+
+        assert is_installed('distinfo-pkg==1.0')
+        assert is_installed('distinfo-pkg>=1.0,<2.0')
+        assert not is_installed('distinfo-pkg<1.0')
+
+
+class FinderTests(fixtures.Fixtures, unittest.TestCase):
+    def test_finder_without_module(self):
+        class ModuleFreeFinder:
+            """
+            A finder without an __module__ attribute
+            """
+
+            def find_module(self, name):
+                pass
+
+            def __getattribute__(self, name):
+                if name == '__module__':
+                    raise AttributeError(name)
+                return super().__getattribute__(name)
+
+        self.fixtures.enter_context(fixtures.install_finder(ModuleFreeFinder()))
+        _compat.disable_stdlib_finder()
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_main.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_main.py
new file mode 100644 (file)
index 0000000..79181bf
--- /dev/null
@@ -0,0 +1,459 @@
+import re
+import pickle
+import unittest
+import warnings
+import importlib
+import importlib_metadata
+import contextlib
+import pyfakefs.fake_filesystem_unittest as ffs
+
+from . import fixtures
+from ._context import suppress
+from ._path import Symlink
+from importlib_metadata import (
+    Distribution,
+    EntryPoint,
+    PackageNotFoundError,
+    _unique,
+    distributions,
+    entry_points,
+    metadata,
+    packages_distributions,
+    version,
+)
+
+
+@contextlib.contextmanager
+def suppress_known_deprecation():
+    with warnings.catch_warnings(record=True) as ctx:
+        warnings.simplefilter('default', category=DeprecationWarning)
+        yield ctx
+
+
+class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
+    version_pattern = r'\d+\.\d+(\.\d)?'
+
+    def test_retrieves_version_of_self(self):
+        dist = Distribution.from_name('distinfo-pkg')
+        assert isinstance(dist.version, str)
+        assert re.match(self.version_pattern, dist.version)
+
+    def test_for_name_does_not_exist(self):
+        with self.assertRaises(PackageNotFoundError):
+            Distribution.from_name('does-not-exist')
+
+    def test_package_not_found_mentions_metadata(self):
+        """
+        When a package is not found, that could indicate that the
+        package is not installed or that it is installed without
+        metadata. Ensure the exception mentions metadata to help
+        guide users toward the cause. See #124.
+        """
+        with self.assertRaises(PackageNotFoundError) as ctx:
+            Distribution.from_name('does-not-exist')
+
+        assert "metadata" in str(ctx.exception)
+
+    # expected to fail until ABC is enforced
+    @suppress(AssertionError)
+    @suppress_known_deprecation()
+    def test_abc_enforced(self):
+        with self.assertRaises(TypeError):
+            type('DistributionSubclass', (Distribution,), {})()
+
+    @fixtures.parameterize(
+        dict(name=None),
+        dict(name=''),
+    )
+    def test_invalid_inputs_to_from_name(self, name):
+        with self.assertRaises(Exception):
+            Distribution.from_name(name)
+
+
+class ImportTests(fixtures.DistInfoPkg, unittest.TestCase):
+    def test_import_nonexistent_module(self):
+        # Ensure that the MetadataPathFinder does not crash an import of a
+        # non-existent module.
+        with self.assertRaises(ImportError):
+            importlib.import_module('does_not_exist')
+
+    def test_resolve(self):
+        ep = entry_points(group='entries')['main']
+        self.assertEqual(ep.load().__name__, "main")
+
+    def test_entrypoint_with_colon_in_name(self):
+        ep = entry_points(group='entries')['ns:sub']
+        self.assertEqual(ep.value, 'mod:main')
+
+    def test_resolve_without_attr(self):
+        ep = EntryPoint(
+            name='ep',
+            value='importlib_metadata',
+            group='grp',
+        )
+        assert ep.load() is importlib_metadata
+
+
+class NameNormalizationTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
+    @staticmethod
+    def make_pkg(name):
+        """
+        Create minimal metadata for a dist-info package with
+        the indicated name on the file system.
+        """
+        return {
+            f'{name}.dist-info': {
+                'METADATA': 'VERSION: 1.0\n',
+            },
+        }
+
+    def test_dashes_in_dist_name_found_as_underscores(self):
+        """
+        For a package with a dash in the name, the dist-info metadata
+        uses underscores in the name. Ensure the metadata loads.
+        """
+        fixtures.build_files(self.make_pkg('my_pkg'), self.site_dir)
+        assert version('my-pkg') == '1.0'
+
+    def test_dist_name_found_as_any_case(self):
+        """
+        Ensure the metadata loads when queried with any case.
+        """
+        pkg_name = 'CherryPy'
+        fixtures.build_files(self.make_pkg(pkg_name), self.site_dir)
+        assert version(pkg_name) == '1.0'
+        assert version(pkg_name.lower()) == '1.0'
+        assert version(pkg_name.upper()) == '1.0'
+
+    def test_unique_distributions(self):
+        """
+        Two distributions varying only by non-normalized name on
+        the file system should resolve as the same.
+        """
+        fixtures.build_files(self.make_pkg('abc'), self.site_dir)
+        before = list(_unique(distributions()))
+
+        alt_site_dir = self.fixtures.enter_context(fixtures.tempdir())
+        self.fixtures.enter_context(self.add_sys_path(alt_site_dir))
+        fixtures.build_files(self.make_pkg('ABC'), alt_site_dir)
+        after = list(_unique(distributions()))
+
+        assert len(after) == len(before)
+
+
+class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
+    @staticmethod
+    def pkg_with_non_ascii_description(site_dir):
+        """
+        Create minimal metadata for a package with non-ASCII in
+        the description.
+        """
+        contents = {
+            'portend.dist-info': {
+                'METADATA': 'Description: pôrˈtend',
+            },
+        }
+        fixtures.build_files(contents, site_dir)
+        return 'portend'
+
+    @staticmethod
+    def pkg_with_non_ascii_description_egg_info(site_dir):
+        """
+        Create minimal metadata for an egg-info package with
+        non-ASCII in the description.
+        """
+        contents = {
+            'portend.dist-info': {
+                'METADATA': """
+                Name: portend
+
+                pôrˈtend""",
+            },
+        }
+        fixtures.build_files(contents, site_dir)
+        return 'portend'
+
+    def test_metadata_loads(self):
+        pkg_name = self.pkg_with_non_ascii_description(self.site_dir)
+        meta = metadata(pkg_name)
+        assert meta['Description'] == 'pôrˈtend'
+
+    def test_metadata_loads_egg_info(self):
+        pkg_name = self.pkg_with_non_ascii_description_egg_info(self.site_dir)
+        meta = metadata(pkg_name)
+        assert meta['Description'] == 'pôrˈtend'
+
+
+class DiscoveryTests(
+    fixtures.EggInfoPkg,
+    fixtures.EggInfoPkgPipInstalledNoToplevel,
+    fixtures.EggInfoPkgPipInstalledNoModules,
+    fixtures.EggInfoPkgSourcesFallback,
+    fixtures.DistInfoPkg,
+    unittest.TestCase,
+):
+    def test_package_discovery(self):
+        dists = list(distributions())
+        assert all(isinstance(dist, Distribution) for dist in dists)
+        assert any(dist.metadata['Name'] == 'egginfo-pkg' for dist in dists)
+        assert any(dist.metadata['Name'] == 'egg_with_module-pkg' for dist in dists)
+        assert any(dist.metadata['Name'] == 'egg_with_no_modules-pkg' for dist in dists)
+        assert any(dist.metadata['Name'] == 'sources_fallback-pkg' for dist in dists)
+        assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists)
+
+    def test_invalid_usage(self):
+        with self.assertRaises(ValueError):
+            list(distributions(context='something', name='else'))
+
+    def test_interleaved_discovery(self):
+        """
+        Ensure interleaved searches are safe.
+
+        When the search is cached, it is possible for searches to be
+        interleaved, so make sure those use-cases are safe.
+
+        Ref #293
+        """
+        dists = distributions()
+        next(dists)
+        version('egginfo-pkg')
+        next(dists)
+
+
+class DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
+    def test_egg_info(self):
+        # make an `EGG-INFO` directory that's unrelated
+        self.site_dir.joinpath('EGG-INFO').mkdir()
+        # used to crash with `IsADirectoryError`
+        with self.assertRaises(PackageNotFoundError):
+            version('unknown-package')
+
+    def test_egg(self):
+        egg = self.site_dir.joinpath('foo-3.6.egg')
+        egg.mkdir()
+        with self.add_sys_path(egg):
+            with self.assertRaises(PackageNotFoundError):
+                version('foo')
+
+
+class MissingSysPath(fixtures.OnSysPath, unittest.TestCase):
+    site_dir = '/does-not-exist'
+
+    def test_discovery(self):
+        """
+        Discovering distributions should succeed even if
+        there is an invalid path on sys.path.
+        """
+        importlib_metadata.distributions()
+
+
+class InaccessibleSysPath(fixtures.OnSysPath, ffs.TestCase):
+    site_dir = '/access-denied'
+
+    def setUp(self):
+        super().setUp()
+        self.setUpPyfakefs()
+        self.fs.create_dir(self.site_dir, perm_bits=000)
+
+    def test_discovery(self):
+        """
+        Discovering distributions should succeed even if
+        there is an invalid path on sys.path.
+        """
+        list(importlib_metadata.distributions())
+
+
+class TestEntryPoints(unittest.TestCase):
+    def __init__(self, *args):
+        super().__init__(*args)
+        self.ep = importlib_metadata.EntryPoint(
+            name='name', value='value', group='group'
+        )
+
+    def test_entry_point_pickleable(self):
+        revived = pickle.loads(pickle.dumps(self.ep))
+        assert revived == self.ep
+
+    def test_positional_args(self):
+        """
+        Capture legacy (namedtuple) construction, discouraged.
+        """
+        EntryPoint('name', 'value', 'group')
+
+    def test_immutable(self):
+        """EntryPoints should be immutable"""
+        with self.assertRaises(AttributeError):
+            self.ep.name = 'badactor'
+
+    def test_repr(self):
+        assert 'EntryPoint' in repr(self.ep)
+        assert 'name=' in repr(self.ep)
+        assert "'name'" in repr(self.ep)
+
+    def test_hashable(self):
+        """EntryPoints should be hashable"""
+        hash(self.ep)
+
+    def test_module(self):
+        assert self.ep.module == 'value'
+
+    def test_attr(self):
+        assert self.ep.attr is None
+
+    def test_sortable(self):
+        """
+        EntryPoint objects are sortable, but result is undefined.
+        """
+        sorted(
+            [
+                EntryPoint(name='b', value='val', group='group'),
+                EntryPoint(name='a', value='val', group='group'),
+            ]
+        )
+
+
+class FileSystem(
+    fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder, unittest.TestCase
+):
+    def test_unicode_dir_on_sys_path(self):
+        """
+        Ensure a Unicode subdirectory of a directory on sys.path
+        does not crash.
+        """
+        fixtures.build_files(
+            {self.unicode_filename(): {}},
+            prefix=self.site_dir,
+        )
+        list(distributions())
+
+
+class PackagesDistributionsPrebuiltTest(fixtures.ZipFixtures, unittest.TestCase):
+    def test_packages_distributions_example(self):
+        self._fixture_on_path('example-21.12-py3-none-any.whl')
+        assert packages_distributions()['example'] == ['example']
+
+    def test_packages_distributions_example2(self):
+        """
+        Test packages_distributions on a wheel built
+        by trampolim.
+        """
+        self._fixture_on_path('example2-1.0.0-py3-none-any.whl')
+        assert packages_distributions()['example2'] == ['example2']
+
+
+class PackagesDistributionsTest(
+    fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase
+):
+    def test_packages_distributions_neither_toplevel_nor_files(self):
+        """
+        Test a package built without 'top-level.txt' or a file list.
+        """
+        fixtures.build_files(
+            {
+                'trim_example-1.0.0.dist-info': {
+                    'METADATA': """
+                Name: trim_example
+                Version: 1.0.0
+                """,
+                }
+            },
+            prefix=self.site_dir,
+        )
+        packages_distributions()
+
+    def test_packages_distributions_all_module_types(self):
+        """
+        Test top-level modules detected on a package without 'top-level.txt'.
+        """
+        suffixes = importlib.machinery.all_suffixes()
+        metadata = dict(
+            METADATA="""
+                Name: all_distributions
+                Version: 1.0.0
+                """,
+        )
+        files = {
+            'all_distributions-1.0.0.dist-info': metadata,
+        }
+        for i, suffix in enumerate(suffixes):
+            files.update(
+                {
+                    f'importable-name {i}{suffix}': '',
+                    f'in_namespace_{i}': {
+                        f'mod{suffix}': '',
+                    },
+                    f'in_package_{i}': {
+                        '__init__.py': '',
+                        f'mod{suffix}': '',
+                    },
+                }
+            )
+        metadata.update(RECORD=fixtures.build_record(files))
+        fixtures.build_files(files, prefix=self.site_dir)
+
+        distributions = packages_distributions()
+
+        for i in range(len(suffixes)):
+            assert distributions[f'importable-name {i}'] == ['all_distributions']
+            assert distributions[f'in_namespace_{i}'] == ['all_distributions']
+            assert distributions[f'in_package_{i}'] == ['all_distributions']
+
+        assert not any(name.endswith('.dist-info') for name in distributions)
+
+    def test_packages_distributions_symlinked_top_level(self):
+        """
+        Distribution is resolvable from a simple top-level symlink in RECORD.
+        See #452.
+        """
+
+        files: fixtures.FilesSpec = {
+            "symlinked_pkg-1.0.0.dist-info": {
+                "METADATA": """
+                    Name: symlinked-pkg
+                    Version: 1.0.0
+                    """,
+                "RECORD": "symlinked,,\n",
+            },
+            ".symlink.target": {},
+            "symlinked": Symlink(".symlink.target"),
+        }
+
+        fixtures.build_files(files, self.site_dir)
+        assert packages_distributions()['symlinked'] == ['symlinked-pkg']
+
+
+class PackagesDistributionsEggTest(
+    fixtures.EggInfoPkg,
+    fixtures.EggInfoPkgPipInstalledNoToplevel,
+    fixtures.EggInfoPkgPipInstalledNoModules,
+    fixtures.EggInfoPkgSourcesFallback,
+    unittest.TestCase,
+):
+    def test_packages_distributions_on_eggs(self):
+        """
+        Test old-style egg packages with a variation of 'top_level.txt',
+        'SOURCES.txt', and 'installed-files.txt', available.
+        """
+        distributions = packages_distributions()
+
+        def import_names_from_package(package_name):
+            return {
+                import_name
+                for import_name, package_names in distributions.items()
+                if package_name in package_names
+            }
+
+        # egginfo-pkg declares one import ('mod') via top_level.txt
+        assert import_names_from_package('egginfo-pkg') == {'mod'}
+
+        # egg_with_module-pkg has one import ('egg_with_module') inferred from
+        # installed-files.txt (top_level.txt is missing)
+        assert import_names_from_package('egg_with_module-pkg') == {'egg_with_module'}
+
+        # egg_with_no_modules-pkg should not be associated with any import names
+        # (top_level.txt is empty, and installed-files.txt has no .py files)
+        assert import_names_from_package('egg_with_no_modules-pkg') == set()
+
+        # sources_fallback-pkg has one import ('sources_fallback') inferred from
+        # SOURCES.txt (top_level.txt and installed-files.txt is missing)
+        assert import_names_from_package('sources_fallback-pkg') == {'sources_fallback'}
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_py39compat.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_py39compat.py
new file mode 100644 (file)
index 0000000..7e6235e
--- /dev/null
@@ -0,0 +1,74 @@
+import sys
+import pathlib
+import unittest
+
+from . import fixtures
+from importlib_metadata import (
+    distribution,
+    distributions,
+    entry_points,
+    metadata,
+    version,
+)
+
+
+class OldStdlibFinderTests(fixtures.DistInfoPkgOffPath, unittest.TestCase):
+    def setUp(self):
+        python_version = sys.version_info[:2]
+        if python_version < (3, 8) or python_version > (3, 9):
+            self.skipTest("Tests specific for Python 3.8/3.9")
+        super().setUp()
+
+    def _meta_path_finder(self):
+        from importlib.metadata import (
+            Distribution,
+            DistributionFinder,
+            PathDistribution,
+        )
+        from importlib.util import spec_from_file_location
+
+        path = pathlib.Path(self.site_dir)
+
+        class CustomDistribution(Distribution):
+            def __init__(self, name, path):
+                self.name = name
+                self._path_distribution = PathDistribution(path)
+
+            def read_text(self, filename):
+                return self._path_distribution.read_text(filename)
+
+            def locate_file(self, path):
+                return self._path_distribution.locate_file(path)
+
+        class CustomFinder:
+            @classmethod
+            def find_spec(cls, fullname, _path=None, _target=None):
+                candidate = pathlib.Path(path, *fullname.split(".")).with_suffix(".py")
+                if candidate.exists():
+                    return spec_from_file_location(fullname, candidate)
+
+            @classmethod
+            def find_distributions(self, context=DistributionFinder.Context()):
+                for dist_info in path.glob("*.dist-info"):
+                    yield PathDistribution(dist_info)
+                    name, _, _ = str(dist_info).partition("-")
+                    yield CustomDistribution(name + "_custom", dist_info)
+
+        return CustomFinder
+
+    def test_compatibility_with_old_stdlib_path_distribution(self):
+        """
+        Given a custom finder that uses Python 3.8/3.9 importlib.metadata is installed,
+        when importlib_metadata functions are called, there should be no exceptions.
+        Ref python/importlib_metadata#396.
+        """
+        self.fixtures.enter_context(fixtures.install_finder(self._meta_path_finder()))
+
+        assert list(distributions())
+        assert distribution("distinfo_pkg")
+        assert distribution("distinfo_pkg_custom")
+        assert version("distinfo_pkg") > "0"
+        assert version("distinfo_pkg_custom") > "0"
+        assert list(metadata("distinfo_pkg"))
+        assert list(metadata("distinfo_pkg_custom"))
+        assert list(entry_points(group="entries"))
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_zip.py b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tests/test_zip.py
new file mode 100644 (file)
index 0000000..01aba6d
--- /dev/null
@@ -0,0 +1,62 @@
+import sys
+import unittest
+
+from . import fixtures
+from importlib_metadata import (
+    PackageNotFoundError,
+    distribution,
+    distributions,
+    entry_points,
+    files,
+    version,
+)
+
+
+class TestZip(fixtures.ZipFixtures, unittest.TestCase):
+    def setUp(self):
+        super().setUp()
+        self._fixture_on_path('example-21.12-py3-none-any.whl')
+
+    def test_zip_version(self):
+        self.assertEqual(version('example'), '21.12')
+
+    def test_zip_version_does_not_match(self):
+        with self.assertRaises(PackageNotFoundError):
+            version('definitely-not-installed')
+
+    def test_zip_entry_points(self):
+        scripts = entry_points(group='console_scripts')
+        entry_point = scripts['example']
+        self.assertEqual(entry_point.value, 'example:main')
+        entry_point = scripts['Example']
+        self.assertEqual(entry_point.value, 'example:main')
+
+    def test_missing_metadata(self):
+        self.assertIsNone(distribution('example').read_text('does not exist'))
+
+    def test_case_insensitive(self):
+        self.assertEqual(version('Example'), '21.12')
+
+    def test_files(self):
+        for file in files('example'):
+            path = str(file.dist.locate_file(file))
+            assert '.whl/' in path, path
+
+    def test_one_distribution(self):
+        dists = list(distributions(path=sys.path[:1]))
+        assert len(dists) == 1
+
+
+class TestEgg(TestZip):
+    def setUp(self):
+        super().setUp()
+        self._fixture_on_path('example-21.12-py3.6.egg')
+
+    def test_files(self):
+        for file in files('example'):
+            path = str(file.dist.locate_file(file))
+            assert '.egg/' in path, path
+
+    def test_normalized_name(self):
+        dist = distribution('example')
+        assert dist._normalized_name == 'example'
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/towncrier.toml b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/towncrier.toml
new file mode 100644 (file)
index 0000000..6fa480e
--- /dev/null
@@ -0,0 +1,2 @@
+[tool.towncrier]
+title_format = "{version}"
diff --git a/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tox.ini b/third_party/wpt_tools/wpt/tools/third_party/importlib_metadata/tox.ini
new file mode 100644 (file)
index 0000000..03e9ae8
--- /dev/null
@@ -0,0 +1,59 @@
+[tox]
+toxworkdir={env:TOX_WORK_DIR:.tox}
+
+
+[testenv]
+deps =
+setenv =
+       PYTHONWARNDEFAULTENCODING = 1
+commands =
+       pytest {posargs}
+passenv =
+       HOME
+usedevelop = True
+extras =
+       testing
+
+
+[testenv:docs]
+extras =
+       docs
+       testing
+changedir = docs
+commands =
+       python -m sphinx -W --keep-going . {toxinidir}/build/html
+       python -m sphinxlint
+
+[testenv:diffcov]
+deps =
+       diff-cover
+commands =
+       pytest {posargs} --cov-report xml
+       diff-cover coverage.xml --compare-branch=origin/main --html-report diffcov.html
+       diff-cover coverage.xml --compare-branch=origin/main --fail-under=100
+
+[testenv:finalize]
+skip_install = True
+deps =
+       towncrier
+       jaraco.develop
+passenv = *
+commands =
+       python -m jaraco.develop.towncrier build --yes
+
+[testenv:release]
+skip_install = True
+deps =
+       build
+       twine>=3
+       jaraco.develop>=7.1
+passenv =
+       TWINE_PASSWORD
+       GITHUB_TOKEN
+setenv =
+       TWINE_USERNAME = {env:TWINE_USERNAME:__token__}
+commands =
+       python -c "import shutil; shutil.rmtree('dist', ignore_errors=True)"
+       python -m build
+       python -m twine upload dist/*
+       python -m jaraco.develop.create-github-release
diff --git a/third_party/wpt_tools/wpt/tools/third_party/typing_extensions/LICENSE b/third_party/wpt_tools/wpt/tools/third_party/typing_extensions/LICENSE
new file mode 100644 (file)
index 0000000..583f9f6
--- /dev/null
@@ -0,0 +1,254 @@
+A. HISTORY OF THE SOFTWARE
+==========================
+
+Python was created in the early 1990s by Guido van Rossum at Stichting
+Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
+as a successor of a language called ABC.  Guido remains Python's
+principal author, although it includes many contributions from others.
+
+In 1995, Guido continued his work on Python at the Corporation for
+National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
+in Reston, Virginia where he released several versions of the
+software.
+
+In May 2000, Guido and the Python core development team moved to
+BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
+year, the PythonLabs team moved to Digital Creations (now Zope
+Corporation, see http://www.zope.com).  In 2001, the Python Software
+Foundation (PSF, see http://www.python.org/psf/) was formed, a
+non-profit organization created specifically to own Python-related
+Intellectual Property.  Zope Corporation is a sponsoring member of
+the PSF.
+
+All Python releases are Open Source (see http://www.opensource.org for
+the Open Source Definition).  Historically, most, but not all, Python
+releases have also been GPL-compatible; the table below summarizes
+the various releases.
+
+    Release         Derived     Year        Owner       GPL-
+                    from                                compatible? (1)
+
+    0.9.0 thru 1.2              1991-1995   CWI         yes
+    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
+    1.6             1.5.2       2000        CNRI        no
+    2.0             1.6         2000        BeOpen.com  no
+    1.6.1           1.6         2001        CNRI        yes (2)
+    2.1             2.0+1.6.1   2001        PSF         no
+    2.0.1           2.0+1.6.1   2001        PSF         yes
+    2.1.1           2.1+2.0.1   2001        PSF         yes
+    2.1.2           2.1.1       2002        PSF         yes
+    2.1.3           2.1.2       2002        PSF         yes
+    2.2 and above   2.1.1       2001-now    PSF         yes
+
+Footnotes:
+
+(1) GPL-compatible doesn't mean that we're distributing Python under
+    the GPL.  All Python licenses, unlike the GPL, let you distribute
+    a modified version without making your changes open source.  The
+    GPL-compatible licenses make it possible to combine Python with
+    other software that is released under the GPL; the others don't.
+
+(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
+    because its license has a choice of law clause.  According to
+    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
+    is "not incompatible" with the GPL.
+
+Thanks to the many outside volunteers who have worked under Guido's
+direction to make these releases possible.
+
+
+B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
+===============================================================
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are
+retained in Python alone or in any derivative version prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee.  This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions.  Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee.  This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party.  As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee.  Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement.  This Agreement together with
+Python 1.6.1 may be located on the Internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013.  This
+Agreement may also be obtained from a proxy server on the Internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee.  This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+        ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands.  All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/third_party/wpt_tools/wpt/tools/third_party/typing_extensions/PKG-INFO b/third_party/wpt_tools/wpt/tools/third_party/typing_extensions/PKG-INFO
new file mode 100644 (file)
index 0000000..ae2892d
--- /dev/null
@@ -0,0 +1,35 @@
+Metadata-Version: 2.1
+Name: typing_extensions
+Version: 4.1.1
+Summary: Backported and Experimental Type Hints for Python 3.6+
+Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing
+Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" <levkivskyi@gmail.com>
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst
+Classifier: Development Status :: 3 - Alpha
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: Operating System :: OS Independent
+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 :: 3.10
+Classifier: Topic :: Software Development
+Project-URL: Home, https://github.com/python/typing/blob/master/typing_extensions/README.rst
+
+Typing Extensions -- Backported and Experimental Type Hints for Python
+
+The ``typing`` module was added to the standard library in Python 3.5, but
+many new features have been added to the module since then.
+This means users of older Python versions who are unable to upgrade will not be
+able to take advantage of new types added to the ``typing`` module, such as
+``typing.Protocol`` or ``typing.TypedDict``.
+
+The ``typing_extensions`` module contains backports of these changes.
+Experimental types that may eventually be added to the ``typing``
+module are also included in ``typing_extensions``.
+
diff --git a/third_party/wpt_tools/wpt/tools/third_party/typing_extensions/pyproject.toml b/third_party/wpt_tools/wpt/tools/third_party/typing_extensions/pyproject.toml
new file mode 100644 (file)
index 0000000..354c206
--- /dev/null
@@ -0,0 +1,63 @@
+# Build system requirements.
+[build-system]
+requires = ["flit_core >=3.4,<4"]
+build-backend = "flit_core.buildapi"
+
+# Project metadata
+[project]
+name = "typing_extensions"
+version = "4.1.1"
+description = "Backported and Experimental Type Hints for Python 3.6+"
+readme.text = """\
+Typing Extensions -- Backported and Experimental Type Hints for Python
+
+The ``typing`` module was added to the standard library in Python 3.5, but
+many new features have been added to the module since then.
+This means users of older Python versions who are unable to upgrade will not be
+able to take advantage of new types added to the ``typing`` module, such as
+``typing.Protocol`` or ``typing.TypedDict``.
+
+The ``typing_extensions`` module contains backports of these changes.
+Experimental types that may eventually be added to the ``typing``
+module are also included in ``typing_extensions``.
+"""
+readme.content-type = "text/x-rst"
+requires-python = ">=3.6"
+urls.Home = "https://github.com/python/typing/blob/master/typing_extensions/README.rst"
+license.file = "LICENSE"
+keywords = [
+    "annotations",
+    "backport",
+    "checker",
+    "checking",
+    "function",
+    "hinting",
+    "hints",
+    "type",
+    "typechecking",
+    "typehinting",
+    "typehints",
+    "typing"
+]
+# Classifiers list: https://pypi.org/classifiers/
+classifiers = [
+    "Development Status :: 3 - Alpha",
+    "Environment :: Console",
+    "Intended Audience :: Developers",
+    "License :: OSI Approved :: Python Software Foundation License",
+    "Operating System :: OS Independent",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3 :: Only",
+    "Programming Language :: Python :: 3.6",
+    "Programming Language :: Python :: 3.7",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Topic :: Software Development"
+]
+
+# Project metadata -- authors. Flit stores this as a list of dicts, so it can't
+# be inline above.
+[[project.authors]]
+name = "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee"
+email = "levkivskyi@gmail.com"
diff --git a/third_party/wpt_tools/wpt/tools/third_party/typing_extensions/src/typing_extensions.py b/third_party/wpt_tools/wpt/tools/third_party/typing_extensions/src/typing_extensions.py
new file mode 100644 (file)
index 0000000..194731c
--- /dev/null
@@ -0,0 +1,2908 @@
+import abc
+import collections
+import collections.abc
+import operator
+import sys
+import types as _types
+import typing
+
+# After PEP 560, internal typing API was substantially reworked.
+# This is especially important for Protocol class which uses internal APIs
+# quite extensively.
+PEP_560 = sys.version_info[:3] >= (3, 7, 0)
+
+if PEP_560:
+    GenericMeta = type
+else:
+    # 3.6
+    from typing import GenericMeta, _type_vars  # noqa
+
+
+# Please keep __all__ alphabetized within each category.
+__all__ = [
+    # Super-special typing primitives.
+    'ClassVar',
+    'Concatenate',
+    'Final',
+    'LiteralString',
+    'ParamSpec',
+    'Self',
+    'Type',
+    'TypeVarTuple',
+    'Unpack',
+
+    # ABCs (from collections.abc).
+    'Awaitable',
+    'AsyncIterator',
+    'AsyncIterable',
+    'Coroutine',
+    'AsyncGenerator',
+    'AsyncContextManager',
+    'ChainMap',
+
+    # Concrete collection types.
+    'ContextManager',
+    'Counter',
+    'Deque',
+    'DefaultDict',
+    'OrderedDict',
+    'TypedDict',
+
+    # Structural checks, a.k.a. protocols.
+    'SupportsIndex',
+
+    # One-off things.
+    'Annotated',
+    'assert_never',
+    'dataclass_transform',
+    'final',
+    'IntVar',
+    'is_typeddict',
+    'Literal',
+    'NewType',
+    'overload',
+    'Protocol',
+    'reveal_type',
+    'runtime',
+    'runtime_checkable',
+    'Text',
+    'TypeAlias',
+    'TypeGuard',
+    'TYPE_CHECKING',
+    'Never',
+    'NoReturn',
+    'Required',
+    'NotRequired',
+]
+
+if PEP_560:
+    __all__.extend(["get_args", "get_origin", "get_type_hints"])
+
+# The functions below are modified copies of typing internal helpers.
+# They are needed by _ProtocolMeta and they provide support for PEP 646.
+
+
+def _no_slots_copy(dct):
+    dict_copy = dict(dct)
+    if '__slots__' in dict_copy:
+        for slot in dict_copy['__slots__']:
+            dict_copy.pop(slot, None)
+    return dict_copy
+
+
+_marker = object()
+
+
+def _check_generic(cls, parameters, elen=_marker):
+    """Check correct count for parameters of a generic cls (internal helper).
+    This gives a nice error message in case of count mismatch.
+    """
+    if not elen:
+        raise TypeError(f"{cls} is not a generic class")
+    if elen is _marker:
+        if not hasattr(cls, "__parameters__") or not cls.__parameters__:
+            raise TypeError(f"{cls} is not a generic class")
+        elen = len(cls.__parameters__)
+    alen = len(parameters)
+    if alen != elen:
+        if hasattr(cls, "__parameters__"):
+            parameters = [p for p in cls.__parameters__ if not _is_unpack(p)]
+            num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters)
+            if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples):
+                return
+        raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};"
+                        f" actual {alen}, expected {elen}")
+
+
+if sys.version_info >= (3, 10):
+    def _should_collect_from_parameters(t):
+        return isinstance(
+            t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType)
+        )
+elif sys.version_info >= (3, 9):
+    def _should_collect_from_parameters(t):
+        return isinstance(t, (typing._GenericAlias, _types.GenericAlias))
+else:
+    def _should_collect_from_parameters(t):
+        return isinstance(t, typing._GenericAlias) and not t._special
+
+
+def _collect_type_vars(types, typevar_types=None):
+    """Collect all type variable contained in types in order of
+    first appearance (lexicographic order). For example::
+
+        _collect_type_vars((T, List[S, T])) == (T, S)
+    """
+    if typevar_types is None:
+        typevar_types = typing.TypeVar
+    tvars = []
+    for t in types:
+        if (
+            isinstance(t, typevar_types) and
+            t not in tvars and
+            not _is_unpack(t)
+        ):
+            tvars.append(t)
+        if _should_collect_from_parameters(t):
+            tvars.extend([t for t in t.__parameters__ if t not in tvars])
+    return tuple(tvars)
+
+
+# 3.6.2+
+if hasattr(typing, 'NoReturn'):
+    NoReturn = typing.NoReturn
+# 3.6.0-3.6.1
+else:
+    class _NoReturn(typing._FinalTypingBase, _root=True):
+        """Special type indicating functions that never return.
+        Example::
+
+          from typing import NoReturn
+
+          def stop() -> NoReturn:
+              raise Exception('no way')
+
+        This type is invalid in other positions, e.g., ``List[NoReturn]``
+        will fail in static type checkers.
+        """
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError("NoReturn cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError("NoReturn cannot be used with issubclass().")
+
+    NoReturn = _NoReturn(_root=True)
+
+# Some unconstrained type variables.  These are used by the container types.
+# (These are not for export.)
+T = typing.TypeVar('T')  # Any type.
+KT = typing.TypeVar('KT')  # Key type.
+VT = typing.TypeVar('VT')  # Value type.
+T_co = typing.TypeVar('T_co', covariant=True)  # Any type covariant containers.
+T_contra = typing.TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
+
+ClassVar = typing.ClassVar
+
+# On older versions of typing there is an internal class named "Final".
+# 3.8+
+if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7):
+    Final = typing.Final
+# 3.7
+elif sys.version_info[:2] >= (3, 7):
+    class _FinalForm(typing._SpecialForm, _root=True):
+
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only single type')
+            return typing._GenericAlias(self, (item,))
+
+    Final = _FinalForm('Final',
+                       doc="""A special typing construct to indicate that a name
+                       cannot be re-assigned or overridden in a subclass.
+                       For example:
+
+                           MAX_SIZE: Final = 9000
+                           MAX_SIZE += 1  # Error reported by type checker
+
+                           class Connection:
+                               TIMEOUT: Final[int] = 10
+                           class FastConnector(Connection):
+                               TIMEOUT = 1  # Error reported by type checker
+
+                       There is no runtime checking of these properties.""")
+# 3.6
+else:
+    class _Final(typing._FinalTypingBase, _root=True):
+        """A special typing construct to indicate that a name
+        cannot be re-assigned or overridden in a subclass.
+        For example:
+
+            MAX_SIZE: Final = 9000
+            MAX_SIZE += 1  # Error reported by type checker
+
+            class Connection:
+                TIMEOUT: Final[int] = 10
+            class FastConnector(Connection):
+                TIMEOUT = 1  # Error reported by type checker
+
+        There is no runtime checking of these properties.
+        """
+
+        __slots__ = ('__type__',)
+
+        def __init__(self, tp=None, **kwds):
+            self.__type__ = tp
+
+        def __getitem__(self, item):
+            cls = type(self)
+            if self.__type__ is None:
+                return cls(typing._type_check(item,
+                           f'{cls.__name__[1:]} accepts only single type.'),
+                           _root=True)
+            raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted')
+
+        def _eval_type(self, globalns, localns):
+            new_tp = typing._eval_type(self.__type__, globalns, localns)
+            if new_tp == self.__type__:
+                return self
+            return type(self)(new_tp, _root=True)
+
+        def __repr__(self):
+            r = super().__repr__()
+            if self.__type__ is not None:
+                r += f'[{typing._type_repr(self.__type__)}]'
+            return r
+
+        def __hash__(self):
+            return hash((type(self).__name__, self.__type__))
+
+        def __eq__(self, other):
+            if not isinstance(other, _Final):
+                return NotImplemented
+            if self.__type__ is not None:
+                return self.__type__ == other.__type__
+            return self is other
+
+    Final = _Final(_root=True)
+
+
+if sys.version_info >= (3, 11):
+    final = typing.final
+else:
+    # @final exists in 3.8+, but we backport it for all versions
+    # before 3.11 to keep support for the __final__ attribute.
+    # See https://bugs.python.org/issue46342
+    def final(f):
+        """This decorator can be used to indicate to type checkers that
+        the decorated method cannot be overridden, and decorated class
+        cannot be subclassed. For example:
+
+            class Base:
+                @final
+                def done(self) -> None:
+                    ...
+            class Sub(Base):
+                def done(self) -> None:  # Error reported by type checker
+                    ...
+            @final
+            class Leaf:
+                ...
+            class Other(Leaf):  # Error reported by type checker
+                ...
+
+        There is no runtime checking of these properties. The decorator
+        sets the ``__final__`` attribute to ``True`` on the decorated object
+        to allow runtime introspection.
+        """
+        try:
+            f.__final__ = True
+        except (AttributeError, TypeError):
+            # Skip the attribute silently if it is not writable.
+            # AttributeError happens if the object has __slots__ or a
+            # read-only property, TypeError if it's a builtin class.
+            pass
+        return f
+
+
+def IntVar(name):
+    return typing.TypeVar(name)
+
+
+# 3.8+:
+if hasattr(typing, 'Literal'):
+    Literal = typing.Literal
+# 3.7:
+elif sys.version_info[:2] >= (3, 7):
+    class _LiteralForm(typing._SpecialForm, _root=True):
+
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            return typing._GenericAlias(self, parameters)
+
+    Literal = _LiteralForm('Literal',
+                           doc="""A type that can be used to indicate to type checkers
+                           that the corresponding value has a value literally equivalent
+                           to the provided parameter. For example:
+
+                               var: Literal[4] = 4
+
+                           The type checker understands that 'var' is literally equal to
+                           the value 4 and no other value.
+
+                           Literal[...] cannot be subclassed. There is no runtime
+                           checking verifying that the parameter is actually a value
+                           instead of a type.""")
+# 3.6:
+else:
+    class _Literal(typing._FinalTypingBase, _root=True):
+        """A type that can be used to indicate to type checkers that the
+        corresponding value has a value literally equivalent to the
+        provided parameter. For example:
+
+            var: Literal[4] = 4
+
+        The type checker understands that 'var' is literally equal to the
+        value 4 and no other value.
+
+        Literal[...] cannot be subclassed. There is no runtime checking
+        verifying that the parameter is actually a value instead of a type.
+        """
+
+        __slots__ = ('__values__',)
+
+        def __init__(self, values=None, **kwds):
+            self.__values__ = values
+
+        def __getitem__(self, values):
+            cls = type(self)
+            if self.__values__ is None:
+                if not isinstance(values, tuple):
+                    values = (values,)
+                return cls(values, _root=True)
+            raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted')
+
+        def _eval_type(self, globalns, localns):
+            return self
+
+        def __repr__(self):
+            r = super().__repr__()
+            if self.__values__ is not None:
+                r += f'[{", ".join(map(typing._type_repr, self.__values__))}]'
+            return r
+
+        def __hash__(self):
+            return hash((type(self).__name__, self.__values__))
+
+        def __eq__(self, other):
+            if not isinstance(other, _Literal):
+                return NotImplemented
+            if self.__values__ is not None:
+                return self.__values__ == other.__values__
+            return self is other
+
+    Literal = _Literal(_root=True)
+
+
+_overload_dummy = typing._overload_dummy  # noqa
+overload = typing.overload
+
+
+# This is not a real generic class.  Don't use outside annotations.
+Type = typing.Type
+
+# Various ABCs mimicking those in collections.abc.
+# A few are simply re-exported for completeness.
+
+
+class _ExtensionsGenericMeta(GenericMeta):
+    def __subclasscheck__(self, subclass):
+        """This mimics a more modern GenericMeta.__subclasscheck__() logic
+        (that does not have problems with recursion) to work around interactions
+        between collections, typing, and typing_extensions on older
+        versions of Python, see https://github.com/python/typing/issues/501.
+        """
+        if self.__origin__ is not None:
+            if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']:
+                raise TypeError("Parameterized generics cannot be used with class "
+                                "or instance checks")
+            return False
+        if not self.__extra__:
+            return super().__subclasscheck__(subclass)
+        res = self.__extra__.__subclasshook__(subclass)
+        if res is not NotImplemented:
+            return res
+        if self.__extra__ in subclass.__mro__:
+            return True
+        for scls in self.__extra__.__subclasses__():
+            if isinstance(scls, GenericMeta):
+                continue
+            if issubclass(subclass, scls):
+                return True
+        return False
+
+
+Awaitable = typing.Awaitable
+Coroutine = typing.Coroutine
+AsyncIterable = typing.AsyncIterable
+AsyncIterator = typing.AsyncIterator
+
+# 3.6.1+
+if hasattr(typing, 'Deque'):
+    Deque = typing.Deque
+# 3.6.0
+else:
+    class Deque(collections.deque, typing.MutableSequence[T],
+                metaclass=_ExtensionsGenericMeta,
+                extra=collections.deque):
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwds):
+            if cls._gorg is Deque:
+                return collections.deque(*args, **kwds)
+            return typing._generic_new(collections.deque, cls, *args, **kwds)
+
+ContextManager = typing.ContextManager
+# 3.6.2+
+if hasattr(typing, 'AsyncContextManager'):
+    AsyncContextManager = typing.AsyncContextManager
+# 3.6.0-3.6.1
+else:
+    from _collections_abc import _check_methods as _check_methods_in_mro  # noqa
+
+    class AsyncContextManager(typing.Generic[T_co]):
+        __slots__ = ()
+
+        async def __aenter__(self):
+            return self
+
+        @abc.abstractmethod
+        async def __aexit__(self, exc_type, exc_value, traceback):
+            return None
+
+        @classmethod
+        def __subclasshook__(cls, C):
+            if cls is AsyncContextManager:
+                return _check_methods_in_mro(C, "__aenter__", "__aexit__")
+            return NotImplemented
+
+DefaultDict = typing.DefaultDict
+
+# 3.7.2+
+if hasattr(typing, 'OrderedDict'):
+    OrderedDict = typing.OrderedDict
+# 3.7.0-3.7.2
+elif (3, 7, 0) <= sys.version_info[:3] < (3, 7, 2):
+    OrderedDict = typing._alias(collections.OrderedDict, (KT, VT))
+# 3.6
+else:
+    class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT],
+                      metaclass=_ExtensionsGenericMeta,
+                      extra=collections.OrderedDict):
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwds):
+            if cls._gorg is OrderedDict:
+                return collections.OrderedDict(*args, **kwds)
+            return typing._generic_new(collections.OrderedDict, cls, *args, **kwds)
+
+# 3.6.2+
+if hasattr(typing, 'Counter'):
+    Counter = typing.Counter
+# 3.6.0-3.6.1
+else:
+    class Counter(collections.Counter,
+                  typing.Dict[T, int],
+                  metaclass=_ExtensionsGenericMeta, extra=collections.Counter):
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwds):
+            if cls._gorg is Counter:
+                return collections.Counter(*args, **kwds)
+            return typing._generic_new(collections.Counter, cls, *args, **kwds)
+
+# 3.6.1+
+if hasattr(typing, 'ChainMap'):
+    ChainMap = typing.ChainMap
+elif hasattr(collections, 'ChainMap'):
+    class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT],
+                   metaclass=_ExtensionsGenericMeta,
+                   extra=collections.ChainMap):
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwds):
+            if cls._gorg is ChainMap:
+                return collections.ChainMap(*args, **kwds)
+            return typing._generic_new(collections.ChainMap, cls, *args, **kwds)
+
+# 3.6.1+
+if hasattr(typing, 'AsyncGenerator'):
+    AsyncGenerator = typing.AsyncGenerator
+# 3.6.0
+else:
+    class AsyncGenerator(AsyncIterator[T_co], typing.Generic[T_co, T_contra],
+                         metaclass=_ExtensionsGenericMeta,
+                         extra=collections.abc.AsyncGenerator):
+        __slots__ = ()
+
+NewType = typing.NewType
+Text = typing.Text
+TYPE_CHECKING = typing.TYPE_CHECKING
+
+
+def _gorg(cls):
+    """This function exists for compatibility with old typing versions."""
+    assert isinstance(cls, GenericMeta)
+    if hasattr(cls, '_gorg'):
+        return cls._gorg
+    while cls.__origin__ is not None:
+        cls = cls.__origin__
+    return cls
+
+
+_PROTO_WHITELIST = ['Callable', 'Awaitable',
+                    'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator',
+                    'Hashable', 'Sized', 'Container', 'Collection', 'Reversible',
+                    'ContextManager', 'AsyncContextManager']
+
+
+def _get_protocol_attrs(cls):
+    attrs = set()
+    for base in cls.__mro__[:-1]:  # without object
+        if base.__name__ in ('Protocol', 'Generic'):
+            continue
+        annotations = getattr(base, '__annotations__', {})
+        for attr in list(base.__dict__.keys()) + list(annotations.keys()):
+            if (not attr.startswith('_abc_') and attr not in (
+                    '__abstractmethods__', '__annotations__', '__weakref__',
+                    '_is_protocol', '_is_runtime_protocol', '__dict__',
+                    '__args__', '__slots__',
+                    '__next_in_mro__', '__parameters__', '__origin__',
+                    '__orig_bases__', '__extra__', '__tree_hash__',
+                    '__doc__', '__subclasshook__', '__init__', '__new__',
+                    '__module__', '_MutableMapping__marker', '_gorg')):
+                attrs.add(attr)
+    return attrs
+
+
+def _is_callable_members_only(cls):
+    return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls))
+
+
+# 3.8+
+if hasattr(typing, 'Protocol'):
+    Protocol = typing.Protocol
+# 3.7
+elif PEP_560:
+
+    def _no_init(self, *args, **kwargs):
+        if type(self)._is_protocol:
+            raise TypeError('Protocols cannot be instantiated')
+
+    class _ProtocolMeta(abc.ABCMeta):
+        # This metaclass is a bit unfortunate and exists only because of the lack
+        # of __instancehook__.
+        def __instancecheck__(cls, instance):
+            # We need this method for situations where attributes are
+            # assigned in __init__.
+            if ((not getattr(cls, '_is_protocol', False) or
+                 _is_callable_members_only(cls)) and
+                    issubclass(instance.__class__, cls)):
+                return True
+            if cls._is_protocol:
+                if all(hasattr(instance, attr) and
+                       (not callable(getattr(cls, attr, None)) or
+                        getattr(instance, attr) is not None)
+                       for attr in _get_protocol_attrs(cls)):
+                    return True
+            return super().__instancecheck__(instance)
+
+    class Protocol(metaclass=_ProtocolMeta):
+        # There is quite a lot of overlapping code with typing.Generic.
+        # Unfortunately it is hard to avoid this while these live in two different
+        # modules. The duplicated code will be removed when Protocol is moved to typing.
+        """Base class for protocol classes. Protocol classes are defined as::
+
+            class Proto(Protocol):
+                def meth(self) -> int:
+                    ...
+
+        Such classes are primarily used with static type checkers that recognize
+        structural subtyping (static duck-typing), for example::
+
+            class C:
+                def meth(self) -> int:
+                    return 0
+
+            def func(x: Proto) -> int:
+                return x.meth()
+
+            func(C())  # Passes static type check
+
+        See PEP 544 for details. Protocol classes decorated with
+        @typing_extensions.runtime act as simple-minded runtime protocol that checks
+        only the presence of given attributes, ignoring their type signatures.
+
+        Protocol classes can be generic, they are defined as::
+
+            class GenProto(Protocol[T]):
+                def meth(self) -> T:
+                    ...
+        """
+        __slots__ = ()
+        _is_protocol = True
+
+        def __new__(cls, *args, **kwds):
+            if cls is Protocol:
+                raise TypeError("Type Protocol cannot be instantiated; "
+                                "it can only be used as a base class")
+            return super().__new__(cls)
+
+        @typing._tp_cache
+        def __class_getitem__(cls, params):
+            if not isinstance(params, tuple):
+                params = (params,)
+            if not params and cls is not typing.Tuple:
+                raise TypeError(
+                    f"Parameter list to {cls.__qualname__}[...] cannot be empty")
+            msg = "Parameters to generic types must be types."
+            params = tuple(typing._type_check(p, msg) for p in params)  # noqa
+            if cls is Protocol:
+                # Generic can only be subscripted with unique type variables.
+                if not all(isinstance(p, typing.TypeVar) for p in params):
+                    i = 0
+                    while isinstance(params[i], typing.TypeVar):
+                        i += 1
+                    raise TypeError(
+                        "Parameters to Protocol[...] must all be type variables."
+                        f" Parameter {i + 1} is {params[i]}")
+                if len(set(params)) != len(params):
+                    raise TypeError(
+                        "Parameters to Protocol[...] must all be unique")
+            else:
+                # Subscripting a regular Generic subclass.
+                _check_generic(cls, params, len(cls.__parameters__))
+            return typing._GenericAlias(cls, params)
+
+        def __init_subclass__(cls, *args, **kwargs):
+            tvars = []
+            if '__orig_bases__' in cls.__dict__:
+                error = typing.Generic in cls.__orig_bases__
+            else:
+                error = typing.Generic in cls.__bases__
+            if error:
+                raise TypeError("Cannot inherit from plain Generic")
+            if '__orig_bases__' in cls.__dict__:
+                tvars = typing._collect_type_vars(cls.__orig_bases__)
+                # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
+                # If found, tvars must be a subset of it.
+                # If not found, tvars is it.
+                # Also check for and reject plain Generic,
+                # and reject multiple Generic[...] and/or Protocol[...].
+                gvars = None
+                for base in cls.__orig_bases__:
+                    if (isinstance(base, typing._GenericAlias) and
+                            base.__origin__ in (typing.Generic, Protocol)):
+                        # for error messages
+                        the_base = base.__origin__.__name__
+                        if gvars is not None:
+                            raise TypeError(
+                                "Cannot inherit from Generic[...]"
+                                " and/or Protocol[...] multiple types.")
+                        gvars = base.__parameters__
+                if gvars is None:
+                    gvars = tvars
+                else:
+                    tvarset = set(tvars)
+                    gvarset = set(gvars)
+                    if not tvarset <= gvarset:
+                        s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
+                        s_args = ', '.join(str(g) for g in gvars)
+                        raise TypeError(f"Some type variables ({s_vars}) are"
+                                        f" not listed in {the_base}[{s_args}]")
+                    tvars = gvars
+            cls.__parameters__ = tuple(tvars)
+
+            # Determine if this is a protocol or a concrete subclass.
+            if not cls.__dict__.get('_is_protocol', None):
+                cls._is_protocol = any(b is Protocol for b in cls.__bases__)
+
+            # Set (or override) the protocol subclass hook.
+            def _proto_hook(other):
+                if not cls.__dict__.get('_is_protocol', None):
+                    return NotImplemented
+                if not getattr(cls, '_is_runtime_protocol', False):
+                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
+                        return NotImplemented
+                    raise TypeError("Instance and class checks can only be used with"
+                                    " @runtime protocols")
+                if not _is_callable_members_only(cls):
+                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
+                        return NotImplemented
+                    raise TypeError("Protocols with non-method members"
+                                    " don't support issubclass()")
+                if not isinstance(other, type):
+                    # Same error as for issubclass(1, int)
+                    raise TypeError('issubclass() arg 1 must be a class')
+                for attr in _get_protocol_attrs(cls):
+                    for base in other.__mro__:
+                        if attr in base.__dict__:
+                            if base.__dict__[attr] is None:
+                                return NotImplemented
+                            break
+                        annotations = getattr(base, '__annotations__', {})
+                        if (isinstance(annotations, typing.Mapping) and
+                                attr in annotations and
+                                isinstance(other, _ProtocolMeta) and
+                                other._is_protocol):
+                            break
+                    else:
+                        return NotImplemented
+                return True
+            if '__subclasshook__' not in cls.__dict__:
+                cls.__subclasshook__ = _proto_hook
+
+            # We have nothing more to do for non-protocols.
+            if not cls._is_protocol:
+                return
+
+            # Check consistency of bases.
+            for base in cls.__bases__:
+                if not (base in (object, typing.Generic) or
+                        base.__module__ == 'collections.abc' and
+                        base.__name__ in _PROTO_WHITELIST or
+                        isinstance(base, _ProtocolMeta) and base._is_protocol):
+                    raise TypeError('Protocols can only inherit from other'
+                                    f' protocols, got {repr(base)}')
+            cls.__init__ = _no_init
+# 3.6
+else:
+    from typing import _next_in_mro, _type_check  # noqa
+
+    def _no_init(self, *args, **kwargs):
+        if type(self)._is_protocol:
+            raise TypeError('Protocols cannot be instantiated')
+
+    class _ProtocolMeta(GenericMeta):
+        """Internal metaclass for Protocol.
+
+        This exists so Protocol classes can be generic without deriving
+        from Generic.
+        """
+        def __new__(cls, name, bases, namespace,
+                    tvars=None, args=None, origin=None, extra=None, orig_bases=None):
+            # This is just a version copied from GenericMeta.__new__ that
+            # includes "Protocol" special treatment. (Comments removed for brevity.)
+            assert extra is None  # Protocols should not have extra
+            if tvars is not None:
+                assert origin is not None
+                assert all(isinstance(t, typing.TypeVar) for t in tvars), tvars
+            else:
+                tvars = _type_vars(bases)
+                gvars = None
+                for base in bases:
+                    if base is typing.Generic:
+                        raise TypeError("Cannot inherit from plain Generic")
+                    if (isinstance(base, GenericMeta) and
+                            base.__origin__ in (typing.Generic, Protocol)):
+                        if gvars is not None:
+                            raise TypeError(
+                                "Cannot inherit from Generic[...] or"
+                                " Protocol[...] multiple times.")
+                        gvars = base.__parameters__
+                if gvars is None:
+                    gvars = tvars
+                else:
+                    tvarset = set(tvars)
+                    gvarset = set(gvars)
+                    if not tvarset <= gvarset:
+                        s_vars = ", ".join(str(t) for t in tvars if t not in gvarset)
+                        s_args = ", ".join(str(g) for g in gvars)
+                        cls_name = "Generic" if any(b.__origin__ is typing.Generic
+                                                    for b in bases) else "Protocol"
+                        raise TypeError(f"Some type variables ({s_vars}) are"
+                                        f" not listed in {cls_name}[{s_args}]")
+                    tvars = gvars
+
+            initial_bases = bases
+            if (extra is not None and type(extra) is abc.ABCMeta and
+                    extra not in bases):
+                bases = (extra,) + bases
+            bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b
+                          for b in bases)
+            if any(isinstance(b, GenericMeta) and b is not typing.Generic for b in bases):
+                bases = tuple(b for b in bases if b is not typing.Generic)
+            namespace.update({'__origin__': origin, '__extra__': extra})
+            self = super(GenericMeta, cls).__new__(cls, name, bases, namespace,
+                                                   _root=True)
+            super(GenericMeta, self).__setattr__('_gorg',
+                                                 self if not origin else
+                                                 _gorg(origin))
+            self.__parameters__ = tvars
+            self.__args__ = tuple(... if a is typing._TypingEllipsis else
+                                  () if a is typing._TypingEmpty else
+                                  a for a in args) if args else None
+            self.__next_in_mro__ = _next_in_mro(self)
+            if orig_bases is None:
+                self.__orig_bases__ = initial_bases
+            elif origin is not None:
+                self._abc_registry = origin._abc_registry
+                self._abc_cache = origin._abc_cache
+            if hasattr(self, '_subs_tree'):
+                self.__tree_hash__ = (hash(self._subs_tree()) if origin else
+                                      super(GenericMeta, self).__hash__())
+            return self
+
+        def __init__(cls, *args, **kwargs):
+            super().__init__(*args, **kwargs)
+            if not cls.__dict__.get('_is_protocol', None):
+                cls._is_protocol = any(b is Protocol or
+                                       isinstance(b, _ProtocolMeta) and
+                                       b.__origin__ is Protocol
+                                       for b in cls.__bases__)
+            if cls._is_protocol:
+                for base in cls.__mro__[1:]:
+                    if not (base in (object, typing.Generic) or
+                            base.__module__ == 'collections.abc' and
+                            base.__name__ in _PROTO_WHITELIST or
+                            isinstance(base, typing.TypingMeta) and base._is_protocol or
+                            isinstance(base, GenericMeta) and
+                            base.__origin__ is typing.Generic):
+                        raise TypeError(f'Protocols can only inherit from other'
+                                        f' protocols, got {repr(base)}')
+
+                cls.__init__ = _no_init
+
+            def _proto_hook(other):
+                if not cls.__dict__.get('_is_protocol', None):
+                    return NotImplemented
+                if not isinstance(other, type):
+                    # Same error as for issubclass(1, int)
+                    raise TypeError('issubclass() arg 1 must be a class')
+                for attr in _get_protocol_attrs(cls):
+                    for base in other.__mro__:
+                        if attr in base.__dict__:
+                            if base.__dict__[attr] is None:
+                                return NotImplemented
+                            break
+                        annotations = getattr(base, '__annotations__', {})
+                        if (isinstance(annotations, typing.Mapping) and
+                                attr in annotations and
+                                isinstance(other, _ProtocolMeta) and
+                                other._is_protocol):
+                            break
+                    else:
+                        return NotImplemented
+                return True
+            if '__subclasshook__' not in cls.__dict__:
+                cls.__subclasshook__ = _proto_hook
+
+        def __instancecheck__(self, instance):
+            # We need this method for situations where attributes are
+            # assigned in __init__.
+            if ((not getattr(self, '_is_protocol', False) or
+                    _is_callable_members_only(self)) and
+                    issubclass(instance.__class__, self)):
+                return True
+            if self._is_protocol:
+                if all(hasattr(instance, attr) and
+                        (not callable(getattr(self, attr, None)) or
+                         getattr(instance, attr) is not None)
+                        for attr in _get_protocol_attrs(self)):
+                    return True
+            return super(GenericMeta, self).__instancecheck__(instance)
+
+        def __subclasscheck__(self, cls):
+            if self.__origin__ is not None:
+                if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']:
+                    raise TypeError("Parameterized generics cannot be used with class "
+                                    "or instance checks")
+                return False
+            if (self.__dict__.get('_is_protocol', None) and
+                    not self.__dict__.get('_is_runtime_protocol', None)):
+                if sys._getframe(1).f_globals['__name__'] in ['abc',
+                                                              'functools',
+                                                              'typing']:
+                    return False
+                raise TypeError("Instance and class checks can only be used with"
+                                " @runtime protocols")
+            if (self.__dict__.get('_is_runtime_protocol', None) and
+                    not _is_callable_members_only(self)):
+                if sys._getframe(1).f_globals['__name__'] in ['abc',
+                                                              'functools',
+                                                              'typing']:
+                    return super(GenericMeta, self).__subclasscheck__(cls)
+                raise TypeError("Protocols with non-method members"
+                                " don't support issubclass()")
+            return super(GenericMeta, self).__subclasscheck__(cls)
+
+        @typing._tp_cache
+        def __getitem__(self, params):
+            # We also need to copy this from GenericMeta.__getitem__ to get
+            # special treatment of "Protocol". (Comments removed for brevity.)
+            if not isinstance(params, tuple):
+                params = (params,)
+            if not params and _gorg(self) is not typing.Tuple:
+                raise TypeError(
+                    f"Parameter list to {self.__qualname__}[...] cannot be empty")
+            msg = "Parameters to generic types must be types."
+            params = tuple(_type_check(p, msg) for p in params)
+            if self in (typing.Generic, Protocol):
+                if not all(isinstance(p, typing.TypeVar) for p in params):
+                    raise TypeError(
+                        f"Parameters to {repr(self)}[...] must all be type variables")
+                if len(set(params)) != len(params):
+                    raise TypeError(
+                        f"Parameters to {repr(self)}[...] must all be unique")
+                tvars = params
+                args = params
+            elif self in (typing.Tuple, typing.Callable):
+                tvars = _type_vars(params)
+                args = params
+            elif self.__origin__ in (typing.Generic, Protocol):
+                raise TypeError(f"Cannot subscript already-subscripted {repr(self)}")
+            else:
+                _check_generic(self, params, len(self.__parameters__))
+                tvars = _type_vars(params)
+                args = params
+
+            prepend = (self,) if self.__origin__ is None else ()
+            return self.__class__(self.__name__,
+                                  prepend + self.__bases__,
+                                  _no_slots_copy(self.__dict__),
+                                  tvars=tvars,
+                                  args=args,
+                                  origin=self,
+                                  extra=self.__extra__,
+                                  orig_bases=self.__orig_bases__)
+
+    class Protocol(metaclass=_ProtocolMeta):
+        """Base class for protocol classes. Protocol classes are defined as::
+
+          class Proto(Protocol):
+              def meth(self) -> int:
+                  ...
+
+        Such classes are primarily used with static type checkers that recognize
+        structural subtyping (static duck-typing), for example::
+
+          class C:
+              def meth(self) -> int:
+                  return 0
+
+          def func(x: Proto) -> int:
+              return x.meth()
+
+          func(C())  # Passes static type check
+
+        See PEP 544 for details. Protocol classes decorated with
+        @typing_extensions.runtime act as simple-minded runtime protocol that checks
+        only the presence of given attributes, ignoring their type signatures.
+
+        Protocol classes can be generic, they are defined as::
+
+          class GenProto(Protocol[T]):
+              def meth(self) -> T:
+                  ...
+        """
+        __slots__ = ()
+        _is_protocol = True
+
+        def __new__(cls, *args, **kwds):
+            if _gorg(cls) is Protocol:
+                raise TypeError("Type Protocol cannot be instantiated; "
+                                "it can be used only as a base class")
+            return typing._generic_new(cls.__next_in_mro__, cls, *args, **kwds)
+
+
+# 3.8+
+if hasattr(typing, 'runtime_checkable'):
+    runtime_checkable = typing.runtime_checkable
+# 3.6-3.7
+else:
+    def runtime_checkable(cls):
+        """Mark a protocol class as a runtime protocol, so that it
+        can be used with isinstance() and issubclass(). Raise TypeError
+        if applied to a non-protocol class.
+
+        This allows a simple-minded structural check very similar to the
+        one-offs in collections.abc such as Hashable.
+        """
+        if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol:
+            raise TypeError('@runtime_checkable can be only applied to protocol classes,'
+                            f' got {cls!r}')
+        cls._is_runtime_protocol = True
+        return cls
+
+
+# Exists for backwards compatibility.
+runtime = runtime_checkable
+
+
+# 3.8+
+if hasattr(typing, 'SupportsIndex'):
+    SupportsIndex = typing.SupportsIndex
+# 3.6-3.7
+else:
+    @runtime_checkable
+    class SupportsIndex(Protocol):
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __index__(self) -> int:
+            pass
+
+
+if hasattr(typing, "Required"):
+    # The standard library TypedDict in Python 3.8 does not store runtime information
+    # about which (if any) keys are optional.  See https://bugs.python.org/issue38834
+    # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
+    # keyword with old-style TypedDict().  See https://bugs.python.org/issue42059
+    # The standard library TypedDict below Python 3.11 does not store runtime
+    # information about optional and required keys when using Required or NotRequired.
+    TypedDict = typing.TypedDict
+    _TypedDictMeta = typing._TypedDictMeta
+    is_typeddict = typing.is_typeddict
+else:
+    def _check_fails(cls, other):
+        try:
+            if sys._getframe(1).f_globals['__name__'] not in ['abc',
+                                                              'functools',
+                                                              'typing']:
+                # Typed dicts are only for static structural subtyping.
+                raise TypeError('TypedDict does not support instance and class checks')
+        except (AttributeError, ValueError):
+            pass
+        return False
+
+    def _dict_new(*args, **kwargs):
+        if not args:
+            raise TypeError('TypedDict.__new__(): not enough arguments')
+        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
+        return dict(*args, **kwargs)
+
+    _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)'
+
+    def _typeddict_new(*args, total=True, **kwargs):
+        if not args:
+            raise TypeError('TypedDict.__new__(): not enough arguments')
+        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
+        if args:
+            typename, args = args[0], args[1:]  # allow the "_typename" keyword be passed
+        elif '_typename' in kwargs:
+            typename = kwargs.pop('_typename')
+            import warnings
+            warnings.warn("Passing '_typename' as keyword argument is deprecated",
+                          DeprecationWarning, stacklevel=2)
+        else:
+            raise TypeError("TypedDict.__new__() missing 1 required positional "
+                            "argument: '_typename'")
+        if args:
+            try:
+                fields, = args  # allow the "_fields" keyword be passed
+            except ValueError:
+                raise TypeError('TypedDict.__new__() takes from 2 to 3 '
+                                f'positional arguments but {len(args) + 2} '
+                                'were given')
+        elif '_fields' in kwargs and len(kwargs) == 1:
+            fields = kwargs.pop('_fields')
+            import warnings
+            warnings.warn("Passing '_fields' as keyword argument is deprecated",
+                          DeprecationWarning, stacklevel=2)
+        else:
+            fields = None
+
+        if fields is None:
+            fields = kwargs
+        elif kwargs:
+            raise TypeError("TypedDict takes either a dict or keyword arguments,"
+                            " but not both")
+
+        ns = {'__annotations__': dict(fields)}
+        try:
+            # Setting correct module is necessary to make typed dict classes pickleable.
+            ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
+        except (AttributeError, ValueError):
+            pass
+
+        return _TypedDictMeta(typename, (), ns, total=total)
+
+    _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,'
+                                         ' /, *, total=True, **kwargs)')
+
+    class _TypedDictMeta(type):
+        def __init__(cls, name, bases, ns, total=True):
+            super().__init__(name, bases, ns)
+
+        def __new__(cls, name, bases, ns, total=True):
+            # Create new typed dict class object.
+            # This method is called directly when TypedDict is subclassed,
+            # or via _typeddict_new when TypedDict is instantiated. This way
+            # TypedDict supports all three syntaxes described in its docstring.
+            # Subclasses and instances of TypedDict return actual dictionaries
+            # via _dict_new.
+            ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new
+            tp_dict = super().__new__(cls, name, (dict,), ns)
+
+            annotations = {}
+            own_annotations = ns.get('__annotations__', {})
+            msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
+            own_annotations = {
+                n: typing._type_check(tp, msg) for n, tp in own_annotations.items()
+            }
+            required_keys = set()
+            optional_keys = set()
+
+            for base in bases:
+                annotations.update(base.__dict__.get('__annotations__', {}))
+                required_keys.update(base.__dict__.get('__required_keys__', ()))
+                optional_keys.update(base.__dict__.get('__optional_keys__', ()))
+
+            annotations.update(own_annotations)
+            if PEP_560:
+                for annotation_key, annotation_type in own_annotations.items():
+                    annotation_origin = get_origin(annotation_type)
+                    if annotation_origin is Annotated:
+                        annotation_args = get_args(annotation_type)
+                        if annotation_args:
+                            annotation_type = annotation_args[0]
+                            annotation_origin = get_origin(annotation_type)
+
+                    if annotation_origin is Required:
+                        required_keys.add(annotation_key)
+                    elif annotation_origin is NotRequired:
+                        optional_keys.add(annotation_key)
+                    elif total:
+                        required_keys.add(annotation_key)
+                    else:
+                        optional_keys.add(annotation_key)
+            else:
+                own_annotation_keys = set(own_annotations.keys())
+                if total:
+                    required_keys.update(own_annotation_keys)
+                else:
+                    optional_keys.update(own_annotation_keys)
+
+            tp_dict.__annotations__ = annotations
+            tp_dict.__required_keys__ = frozenset(required_keys)
+            tp_dict.__optional_keys__ = frozenset(optional_keys)
+            if not hasattr(tp_dict, '__total__'):
+                tp_dict.__total__ = total
+            return tp_dict
+
+        __instancecheck__ = __subclasscheck__ = _check_fails
+
+    TypedDict = _TypedDictMeta('TypedDict', (dict,), {})
+    TypedDict.__module__ = __name__
+    TypedDict.__doc__ = \
+        """A simple typed name space. At runtime it is equivalent to a plain dict.
+
+        TypedDict creates a dictionary type that expects all of its
+        instances to have a certain set of keys, with each key
+        associated with a value of a consistent type. This expectation
+        is not checked at runtime but is only enforced by type checkers.
+        Usage::
+
+            class Point2D(TypedDict):
+                x: int
+                y: int
+                label: str
+
+            a: Point2D = {'x': 1, 'y': 2, 'label': 'good'}  # OK
+            b: Point2D = {'z': 3, 'label': 'bad'}           # Fails type check
+
+            assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
+
+        The type info can be accessed via the Point2D.__annotations__ dict, and
+        the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
+        TypedDict supports two additional equivalent forms::
+
+            Point2D = TypedDict('Point2D', x=int, y=int, label=str)
+            Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
+
+        The class syntax is only supported in Python 3.6+, while two other
+        syntax forms work for Python 2.7 and 3.2+
+        """
+
+    if hasattr(typing, "_TypedDictMeta"):
+        _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta)
+    else:
+        _TYPEDDICT_TYPES = (_TypedDictMeta,)
+
+    def is_typeddict(tp):
+        """Check if an annotation is a TypedDict class
+
+        For example::
+            class Film(TypedDict):
+                title: str
+                year: int
+
+            is_typeddict(Film)  # => True
+            is_typeddict(Union[list, str])  # => False
+        """
+        return isinstance(tp, tuple(_TYPEDDICT_TYPES))
+
+if hasattr(typing, "Required"):
+    get_type_hints = typing.get_type_hints
+elif PEP_560:
+    import functools
+    import types
+
+    # replaces _strip_annotations()
+    def _strip_extras(t):
+        """Strips Annotated, Required and NotRequired from a given type."""
+        if isinstance(t, _AnnotatedAlias):
+            return _strip_extras(t.__origin__)
+        if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired):
+            return _strip_extras(t.__args__[0])
+        if isinstance(t, typing._GenericAlias):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return t.copy_with(stripped_args)
+        if hasattr(types, "GenericAlias") and isinstance(t, types.GenericAlias):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return types.GenericAlias(t.__origin__, stripped_args)
+        if hasattr(types, "UnionType") and isinstance(t, types.UnionType):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return functools.reduce(operator.or_, stripped_args)
+
+        return t
+
+    def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
+        """Return type hints for an object.
+
+        This is often the same as obj.__annotations__, but it handles
+        forward references encoded as string literals, adds Optional[t] if a
+        default value equal to None is set and recursively replaces all
+        'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T'
+        (unless 'include_extras=True').
+
+        The argument may be a module, class, method, or function. The annotations
+        are returned as a dictionary. For classes, annotations include also
+        inherited members.
+
+        TypeError is raised if the argument is not of a type that can contain
+        annotations, and an empty dictionary is returned if no annotations are
+        present.
+
+        BEWARE -- the behavior of globalns and localns is counterintuitive
+        (unless you are familiar with how eval() and exec() work).  The
+        search order is locals first, then globals.
+
+        - If no dict arguments are passed, an attempt is made to use the
+          globals from obj (or the respective module's globals for classes),
+          and these are also used as the locals.  If the object does not appear
+          to have globals, an empty dictionary is used.
+
+        - If one dict argument is passed, it is used for both globals and
+          locals.
+
+        - If two dict arguments are passed, they specify globals and
+          locals, respectively.
+        """
+        if hasattr(typing, "Annotated"):
+            hint = typing.get_type_hints(
+                obj, globalns=globalns, localns=localns, include_extras=True
+            )
+        else:
+            hint = typing.get_type_hints(obj, globalns=globalns, localns=localns)
+        if include_extras:
+            return hint
+        return {k: _strip_extras(t) for k, t in hint.items()}
+
+
+# Python 3.9+ has PEP 593 (Annotated)
+if hasattr(typing, 'Annotated'):
+    Annotated = typing.Annotated
+    # Not exported and not a public API, but needed for get_origin() and get_args()
+    # to work.
+    _AnnotatedAlias = typing._AnnotatedAlias
+# 3.7-3.8
+elif PEP_560:
+    class _AnnotatedAlias(typing._GenericAlias, _root=True):
+        """Runtime representation of an annotated type.
+
+        At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
+        with extra annotations. The alias behaves like a normal typing alias,
+        instantiating is the same as instantiating the underlying type, binding
+        it to types is also the same.
+        """
+        def __init__(self, origin, metadata):
+            if isinstance(origin, _AnnotatedAlias):
+                metadata = origin.__metadata__ + metadata
+                origin = origin.__origin__
+            super().__init__(origin, origin)
+            self.__metadata__ = metadata
+
+        def copy_with(self, params):
+            assert len(params) == 1
+            new_type = params[0]
+            return _AnnotatedAlias(new_type, self.__metadata__)
+
+        def __repr__(self):
+            return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, "
+                    f"{', '.join(repr(a) for a in self.__metadata__)}]")
+
+        def __reduce__(self):
+            return operator.getitem, (
+                Annotated, (self.__origin__,) + self.__metadata__
+            )
+
+        def __eq__(self, other):
+            if not isinstance(other, _AnnotatedAlias):
+                return NotImplemented
+            if self.__origin__ != other.__origin__:
+                return False
+            return self.__metadata__ == other.__metadata__
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__metadata__))
+
+    class Annotated:
+        """Add context specific metadata to a type.
+
+        Example: Annotated[int, runtime_check.Unsigned] indicates to the
+        hypothetical runtime_check module that this type is an unsigned int.
+        Every other consumer of this type can ignore this metadata and treat
+        this type as int.
+
+        The first argument to Annotated must be a valid type (and will be in
+        the __origin__ field), the remaining arguments are kept as a tuple in
+        the __extra__ field.
+
+        Details:
+
+        - It's an error to call `Annotated` with less than two arguments.
+        - Nested Annotated are flattened::
+
+            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
+
+        - Instantiating an annotated type is equivalent to instantiating the
+        underlying type::
+
+            Annotated[C, Ann1](5) == C(5)
+
+        - Annotated can be used as a generic type alias::
+
+            Optimized = Annotated[T, runtime.Optimize()]
+            Optimized[int] == Annotated[int, runtime.Optimize()]
+
+            OptimizedList = Annotated[List[T], runtime.Optimize()]
+            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+        """
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwargs):
+            raise TypeError("Type Annotated cannot be instantiated.")
+
+        @typing._tp_cache
+        def __class_getitem__(cls, params):
+            if not isinstance(params, tuple) or len(params) < 2:
+                raise TypeError("Annotated[...] should be used "
+                                "with at least two arguments (a type and an "
+                                "annotation).")
+            allowed_special_forms = (ClassVar, Final)
+            if get_origin(params[0]) in allowed_special_forms:
+                origin = params[0]
+            else:
+                msg = "Annotated[t, ...]: t must be a type."
+                origin = typing._type_check(params[0], msg)
+            metadata = tuple(params[1:])
+            return _AnnotatedAlias(origin, metadata)
+
+        def __init_subclass__(cls, *args, **kwargs):
+            raise TypeError(
+                f"Cannot subclass {cls.__module__}.Annotated"
+            )
+# 3.6
+else:
+
+    def _is_dunder(name):
+        """Returns True if name is a __dunder_variable_name__."""
+        return len(name) > 4 and name.startswith('__') and name.endswith('__')
+
+    # Prior to Python 3.7 types did not have `copy_with`. A lot of the equality
+    # checks, argument expansion etc. are done on the _subs_tre. As a result we
+    # can't provide a get_type_hints function that strips out annotations.
+
+    class AnnotatedMeta(typing.GenericMeta):
+        """Metaclass for Annotated"""
+
+        def __new__(cls, name, bases, namespace, **kwargs):
+            if any(b is not object for b in bases):
+                raise TypeError("Cannot subclass " + str(Annotated))
+            return super().__new__(cls, name, bases, namespace, **kwargs)
+
+        @property
+        def __metadata__(self):
+            return self._subs_tree()[2]
+
+        def _tree_repr(self, tree):
+            cls, origin, metadata = tree
+            if not isinstance(origin, tuple):
+                tp_repr = typing._type_repr(origin)
+            else:
+                tp_repr = origin[0]._tree_repr(origin)
+            metadata_reprs = ", ".join(repr(arg) for arg in metadata)
+            return f'{cls}[{tp_repr}, {metadata_reprs}]'
+
+        def _subs_tree(self, tvars=None, args=None):  # noqa
+            if self is Annotated:
+                return Annotated
+            res = super()._subs_tree(tvars=tvars, args=args)
+            # Flatten nested Annotated
+            if isinstance(res[1], tuple) and res[1][0] is Annotated:
+                sub_tp = res[1][1]
+                sub_annot = res[1][2]
+                return (Annotated, sub_tp, sub_annot + res[2])
+            return res
+
+        def _get_cons(self):
+            """Return the class used to create instance of this type."""
+            if self.__origin__ is None:
+                raise TypeError("Cannot get the underlying type of a "
+                                "non-specialized Annotated type.")
+            tree = self._subs_tree()
+            while isinstance(tree, tuple) and tree[0] is Annotated:
+                tree = tree[1]
+            if isinstance(tree, tuple):
+                return tree[0]
+            else:
+                return tree
+
+        @typing._tp_cache
+        def __getitem__(self, params):
+            if not isinstance(params, tuple):
+                params = (params,)
+            if self.__origin__ is not None:  # specializing an instantiated type
+                return super().__getitem__(params)
+            elif not isinstance(params, tuple) or len(params) < 2:
+                raise TypeError("Annotated[...] should be instantiated "
+                                "with at least two arguments (a type and an "
+                                "annotation).")
+            else:
+                if (
+                    isinstance(params[0], typing._TypingBase) and
+                    type(params[0]).__name__ == "_ClassVar"
+                ):
+                    tp = params[0]
+                else:
+                    msg = "Annotated[t, ...]: t must be a type."
+                    tp = typing._type_check(params[0], msg)
+                metadata = tuple(params[1:])
+            return self.__class__(
+                self.__name__,
+                self.__bases__,
+                _no_slots_copy(self.__dict__),
+                tvars=_type_vars((tp,)),
+                # Metadata is a tuple so it won't be touched by _replace_args et al.
+                args=(tp, metadata),
+                origin=self,
+            )
+
+        def __call__(self, *args, **kwargs):
+            cons = self._get_cons()
+            result = cons(*args, **kwargs)
+            try:
+                result.__orig_class__ = self
+            except AttributeError:
+                pass
+            return result
+
+        def __getattr__(self, attr):
+            # For simplicity we just don't relay all dunder names
+            if self.__origin__ is not None and not _is_dunder(attr):
+                return getattr(self._get_cons(), attr)
+            raise AttributeError(attr)
+
+        def __setattr__(self, attr, value):
+            if _is_dunder(attr) or attr.startswith('_abc_'):
+                super().__setattr__(attr, value)
+            elif self.__origin__ is None:
+                raise AttributeError(attr)
+            else:
+                setattr(self._get_cons(), attr, value)
+
+        def __instancecheck__(self, obj):
+            raise TypeError("Annotated cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError("Annotated cannot be used with issubclass().")
+
+    class Annotated(metaclass=AnnotatedMeta):
+        """Add context specific metadata to a type.
+
+        Example: Annotated[int, runtime_check.Unsigned] indicates to the
+        hypothetical runtime_check module that this type is an unsigned int.
+        Every other consumer of this type can ignore this metadata and treat
+        this type as int.
+
+        The first argument to Annotated must be a valid type, the remaining
+        arguments are kept as a tuple in the __metadata__ field.
+
+        Details:
+
+        - It's an error to call `Annotated` with less than two arguments.
+        - Nested Annotated are flattened::
+
+            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
+
+        - Instantiating an annotated type is equivalent to instantiating the
+        underlying type::
+
+            Annotated[C, Ann1](5) == C(5)
+
+        - Annotated can be used as a generic type alias::
+
+            Optimized = Annotated[T, runtime.Optimize()]
+            Optimized[int] == Annotated[int, runtime.Optimize()]
+
+            OptimizedList = Annotated[List[T], runtime.Optimize()]
+            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+        """
+
+# Python 3.8 has get_origin() and get_args() but those implementations aren't
+# Annotated-aware, so we can't use those. Python 3.9's versions don't support
+# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do.
+if sys.version_info[:2] >= (3, 10):
+    get_origin = typing.get_origin
+    get_args = typing.get_args
+# 3.7-3.9
+elif PEP_560:
+    try:
+        # 3.9+
+        from typing import _BaseGenericAlias
+    except ImportError:
+        _BaseGenericAlias = typing._GenericAlias
+    try:
+        # 3.9+
+        from typing import GenericAlias
+    except ImportError:
+        GenericAlias = typing._GenericAlias
+
+    def get_origin(tp):
+        """Get the unsubscripted version of a type.
+
+        This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
+        and Annotated. Return None for unsupported types. Examples::
+
+            get_origin(Literal[42]) is Literal
+            get_origin(int) is None
+            get_origin(ClassVar[int]) is ClassVar
+            get_origin(Generic) is Generic
+            get_origin(Generic[T]) is Generic
+            get_origin(Union[T, int]) is Union
+            get_origin(List[Tuple[T, T]][int]) == list
+            get_origin(P.args) is P
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return Annotated
+        if isinstance(tp, (typing._GenericAlias, GenericAlias, _BaseGenericAlias,
+                           ParamSpecArgs, ParamSpecKwargs)):
+            return tp.__origin__
+        if tp is typing.Generic:
+            return typing.Generic
+        return None
+
+    def get_args(tp):
+        """Get type arguments with all substitutions performed.
+
+        For unions, basic simplifications used by Union constructor are performed.
+        Examples::
+            get_args(Dict[str, int]) == (str, int)
+            get_args(int) == ()
+            get_args(Union[int, Union[T, int], str][int]) == (int, str)
+            get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
+            get_args(Callable[[], T][int]) == ([], int)
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return (tp.__origin__,) + tp.__metadata__
+        if isinstance(tp, (typing._GenericAlias, GenericAlias)):
+            if getattr(tp, "_special", False):
+                return ()
+            res = tp.__args__
+            if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
+                res = (list(res[:-1]), res[-1])
+            return res
+        return ()
+
+
+# 3.10+
+if hasattr(typing, 'TypeAlias'):
+    TypeAlias = typing.TypeAlias
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    class _TypeAliasForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    @_TypeAliasForm
+    def TypeAlias(self, parameters):
+        """Special marker indicating that an assignment should
+        be recognized as a proper type alias definition by type
+        checkers.
+
+        For example::
+
+            Predicate: TypeAlias = Callable[..., bool]
+
+        It's invalid when used anywhere except as in the example above.
+        """
+        raise TypeError(f"{self} is not subscriptable")
+# 3.7-3.8
+elif sys.version_info[:2] >= (3, 7):
+    class _TypeAliasForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    TypeAlias = _TypeAliasForm('TypeAlias',
+                               doc="""Special marker indicating that an assignment should
+                               be recognized as a proper type alias definition by type
+                               checkers.
+
+                               For example::
+
+                                   Predicate: TypeAlias = Callable[..., bool]
+
+                               It's invalid when used anywhere except as in the example
+                               above.""")
+# 3.6
+else:
+    class _TypeAliasMeta(typing.TypingMeta):
+        """Metaclass for TypeAlias"""
+
+        def __repr__(self):
+            return 'typing_extensions.TypeAlias'
+
+    class _TypeAliasBase(typing._FinalTypingBase, metaclass=_TypeAliasMeta, _root=True):
+        """Special marker indicating that an assignment should
+        be recognized as a proper type alias definition by type
+        checkers.
+
+        For example::
+
+            Predicate: TypeAlias = Callable[..., bool]
+
+        It's invalid when used anywhere except as in the example above.
+        """
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError("TypeAlias cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError("TypeAlias cannot be used with issubclass().")
+
+        def __repr__(self):
+            return 'typing_extensions.TypeAlias'
+
+    TypeAlias = _TypeAliasBase(_root=True)
+
+
+# Python 3.10+ has PEP 612
+if hasattr(typing, 'ParamSpecArgs'):
+    ParamSpecArgs = typing.ParamSpecArgs
+    ParamSpecKwargs = typing.ParamSpecKwargs
+# 3.6-3.9
+else:
+    class _Immutable:
+        """Mixin to indicate that object should not be copied."""
+        __slots__ = ()
+
+        def __copy__(self):
+            return self
+
+        def __deepcopy__(self, memo):
+            return self
+
+    class ParamSpecArgs(_Immutable):
+        """The args for a ParamSpec object.
+
+        Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
+
+        ParamSpecArgs objects have a reference back to their ParamSpec:
+
+        P.args.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.args"
+
+        def __eq__(self, other):
+            if not isinstance(other, ParamSpecArgs):
+                return NotImplemented
+            return self.__origin__ == other.__origin__
+
+    class ParamSpecKwargs(_Immutable):
+        """The kwargs for a ParamSpec object.
+
+        Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
+
+        ParamSpecKwargs objects have a reference back to their ParamSpec:
+
+        P.kwargs.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.kwargs"
+
+        def __eq__(self, other):
+            if not isinstance(other, ParamSpecKwargs):
+                return NotImplemented
+            return self.__origin__ == other.__origin__
+
+# 3.10+
+if hasattr(typing, 'ParamSpec'):
+    ParamSpec = typing.ParamSpec
+# 3.6-3.9
+else:
+
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class ParamSpec(list):
+        """Parameter specification variable.
+
+        Usage::
+
+           P = ParamSpec('P')
+
+        Parameter specification variables exist primarily for the benefit of static
+        type checkers.  They are used to forward the parameter types of one
+        callable to another callable, a pattern commonly found in higher order
+        functions and decorators.  They are only valid when used in ``Concatenate``,
+        or s the first argument to ``Callable``. In Python 3.10 and higher,
+        they are also supported in user-defined Generics at runtime.
+        See class Generic for more information on generic types.  An
+        example for annotating a decorator::
+
+           T = TypeVar('T')
+           P = ParamSpec('P')
+
+           def add_logging(f: Callable[P, T]) -> Callable[P, T]:
+               '''A type-safe decorator to add logging to a function.'''
+               def inner(*args: P.args, **kwargs: P.kwargs) -> T:
+                   logging.info(f'{f.__name__} was called')
+                   return f(*args, **kwargs)
+               return inner
+
+           @add_logging
+           def add_two(x: float, y: float) -> float:
+               '''Add two numbers together.'''
+               return x + y
+
+        Parameter specification variables defined with covariant=True or
+        contravariant=True can be used to declare covariant or contravariant
+        generic types.  These keyword arguments are valid, but their actual semantics
+        are yet to be decided.  See PEP 612 for details.
+
+        Parameter specification variables can be introspected. e.g.:
+
+           P.__name__ == 'T'
+           P.__bound__ == None
+           P.__covariant__ == False
+           P.__contravariant__ == False
+
+        Note that only parameter specification variables defined in global scope can
+        be pickled.
+        """
+
+        # Trick Generic __parameters__.
+        __class__ = typing.TypeVar
+
+        @property
+        def args(self):
+            return ParamSpecArgs(self)
+
+        @property
+        def kwargs(self):
+            return ParamSpecKwargs(self)
+
+        def __init__(self, name, *, bound=None, covariant=False, contravariant=False):
+            super().__init__([self])
+            self.__name__ = name
+            self.__covariant__ = bool(covariant)
+            self.__contravariant__ = bool(contravariant)
+            if bound:
+                self.__bound__ = typing._type_check(bound, 'Bound must be a type.')
+            else:
+                self.__bound__ = None
+
+            # for pickling:
+            try:
+                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+            except (AttributeError, ValueError):
+                def_mod = None
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+        def __repr__(self):
+            if self.__covariant__:
+                prefix = '+'
+            elif self.__contravariant__:
+                prefix = '-'
+            else:
+                prefix = '~'
+            return prefix + self.__name__
+
+        def __hash__(self):
+            return object.__hash__(self)
+
+        def __eq__(self, other):
+            return self is other
+
+        def __reduce__(self):
+            return self.__name__
+
+        # Hack to get typing._type_check to pass.
+        def __call__(self, *args, **kwargs):
+            pass
+
+        if not PEP_560:
+            # Only needed in 3.6.
+            def _get_type_vars(self, tvars):
+                if self not in tvars:
+                    tvars.append(self)
+
+
+# 3.6-3.9
+if not hasattr(typing, 'Concatenate'):
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class _ConcatenateGenericAlias(list):
+
+        # Trick Generic into looking into this for __parameters__.
+        if PEP_560:
+            __class__ = typing._GenericAlias
+        else:
+            __class__ = typing._TypingBase
+
+        # Flag in 3.8.
+        _special = False
+        # Attribute in 3.6 and earlier.
+        _gorg = typing.Generic
+
+        def __init__(self, origin, args):
+            super().__init__(args)
+            self.__origin__ = origin
+            self.__args__ = args
+
+        def __repr__(self):
+            _type_repr = typing._type_repr
+            return (f'{_type_repr(self.__origin__)}'
+                    f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]')
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__args__))
+
+        # Hack to get typing._type_check to pass in Generic.
+        def __call__(self, *args, **kwargs):
+            pass
+
+        @property
+        def __parameters__(self):
+            return tuple(
+                tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
+            )
+
+        if not PEP_560:
+            # Only required in 3.6.
+            def _get_type_vars(self, tvars):
+                if self.__origin__ and self.__parameters__:
+                    typing._get_type_vars(self.__parameters__, tvars)
+
+
+# 3.6-3.9
+@typing._tp_cache
+def _concatenate_getitem(self, parameters):
+    if parameters == ():
+        raise TypeError("Cannot take a Concatenate of no types.")
+    if not isinstance(parameters, tuple):
+        parameters = (parameters,)
+    if not isinstance(parameters[-1], ParamSpec):
+        raise TypeError("The last parameter to Concatenate should be a "
+                        "ParamSpec variable.")
+    msg = "Concatenate[arg, ...]: each arg must be a type."
+    parameters = tuple(typing._type_check(p, msg) for p in parameters)
+    return _ConcatenateGenericAlias(self, parameters)
+
+
+# 3.10+
+if hasattr(typing, 'Concatenate'):
+    Concatenate = typing.Concatenate
+    _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_TypeAliasForm
+    def Concatenate(self, parameters):
+        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """
+        return _concatenate_getitem(self, parameters)
+# 3.7-8
+elif sys.version_info[:2] >= (3, 7):
+    class _ConcatenateForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            return _concatenate_getitem(self, parameters)
+
+    Concatenate = _ConcatenateForm(
+        'Concatenate',
+        doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """)
+# 3.6
+else:
+    class _ConcatenateAliasMeta(typing.TypingMeta):
+        """Metaclass for Concatenate."""
+
+        def __repr__(self):
+            return 'typing_extensions.Concatenate'
+
+    class _ConcatenateAliasBase(typing._FinalTypingBase,
+                                metaclass=_ConcatenateAliasMeta,
+                                _root=True):
+        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError("Concatenate cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError("Concatenate cannot be used with issubclass().")
+
+        def __repr__(self):
+            return 'typing_extensions.Concatenate'
+
+        def __getitem__(self, parameters):
+            return _concatenate_getitem(self, parameters)
+
+    Concatenate = _ConcatenateAliasBase(_root=True)
+
+# 3.10+
+if hasattr(typing, 'TypeGuard'):
+    TypeGuard = typing.TypeGuard
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    class _TypeGuardForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    @_TypeGuardForm
+    def TypeGuard(self, parameters):
+        """Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """
+        item = typing._type_check(parameters, f'{self} accepts only single type.')
+        return typing._GenericAlias(self, (item,))
+# 3.7-3.8
+elif sys.version_info[:2] >= (3, 7):
+    class _TypeGuardForm(typing._SpecialForm, _root=True):
+
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type')
+            return typing._GenericAlias(self, (item,))
+
+    TypeGuard = _TypeGuardForm(
+        'TypeGuard',
+        doc="""Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """)
+# 3.6
+else:
+    class _TypeGuard(typing._FinalTypingBase, _root=True):
+        """Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """
+
+        __slots__ = ('__type__',)
+
+        def __init__(self, tp=None, **kwds):
+            self.__type__ = tp
+
+        def __getitem__(self, item):
+            cls = type(self)
+            if self.__type__ is None:
+                return cls(typing._type_check(item,
+                           f'{cls.__name__[1:]} accepts only a single type.'),
+                           _root=True)
+            raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted')
+
+        def _eval_type(self, globalns, localns):
+            new_tp = typing._eval_type(self.__type__, globalns, localns)
+            if new_tp == self.__type__:
+                return self
+            return type(self)(new_tp, _root=True)
+
+        def __repr__(self):
+            r = super().__repr__()
+            if self.__type__ is not None:
+                r += f'[{typing._type_repr(self.__type__)}]'
+            return r
+
+        def __hash__(self):
+            return hash((type(self).__name__, self.__type__))
+
+        def __eq__(self, other):
+            if not isinstance(other, _TypeGuard):
+                return NotImplemented
+            if self.__type__ is not None:
+                return self.__type__ == other.__type__
+            return self is other
+
+    TypeGuard = _TypeGuard(_root=True)
+
+
+if sys.version_info[:2] >= (3, 7):
+    # Vendored from cpython typing._SpecialFrom
+    class _SpecialForm(typing._Final, _root=True):
+        __slots__ = ('_name', '__doc__', '_getitem')
+
+        def __init__(self, getitem):
+            self._getitem = getitem
+            self._name = getitem.__name__
+            self.__doc__ = getitem.__doc__
+
+        def __getattr__(self, item):
+            if item in {'__name__', '__qualname__'}:
+                return self._name
+
+            raise AttributeError(item)
+
+        def __mro_entries__(self, bases):
+            raise TypeError(f"Cannot subclass {self!r}")
+
+        def __repr__(self):
+            return f'typing_extensions.{self._name}'
+
+        def __reduce__(self):
+            return self._name
+
+        def __call__(self, *args, **kwds):
+            raise TypeError(f"Cannot instantiate {self!r}")
+
+        def __or__(self, other):
+            return typing.Union[self, other]
+
+        def __ror__(self, other):
+            return typing.Union[other, self]
+
+        def __instancecheck__(self, obj):
+            raise TypeError(f"{self} cannot be used with isinstance()")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError(f"{self} cannot be used with issubclass()")
+
+        @typing._tp_cache
+        def __getitem__(self, parameters):
+            return self._getitem(self, parameters)
+
+
+if hasattr(typing, "LiteralString"):
+    LiteralString = typing.LiteralString
+elif sys.version_info[:2] >= (3, 7):
+    @_SpecialForm
+    def LiteralString(self, params):
+        """Represents an arbitrary literal string.
+
+        Example::
+
+          from typing_extensions import LiteralString
+
+          def query(sql: LiteralString) -> ...:
+              ...
+
+          query("SELECT * FROM table")  # ok
+          query(f"SELECT * FROM {input()}")  # not ok
+
+        See PEP 675 for details.
+
+        """
+        raise TypeError(f"{self} is not subscriptable")
+else:
+    class _LiteralString(typing._FinalTypingBase, _root=True):
+        """Represents an arbitrary literal string.
+
+        Example::
+
+          from typing_extensions import LiteralString
+
+          def query(sql: LiteralString) -> ...:
+              ...
+
+          query("SELECT * FROM table")  # ok
+          query(f"SELECT * FROM {input()}")  # not ok
+
+        See PEP 675 for details.
+
+        """
+
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError(f"{self} cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError(f"{self} cannot be used with issubclass().")
+
+    LiteralString = _LiteralString(_root=True)
+
+
+if hasattr(typing, "Self"):
+    Self = typing.Self
+elif sys.version_info[:2] >= (3, 7):
+    @_SpecialForm
+    def Self(self, params):
+        """Used to spell the type of "self" in classes.
+
+        Example::
+
+          from typing import Self
+
+          class ReturnsSelf:
+              def parse(self, data: bytes) -> Self:
+                  ...
+                  return self
+
+        """
+
+        raise TypeError(f"{self} is not subscriptable")
+else:
+    class _Self(typing._FinalTypingBase, _root=True):
+        """Used to spell the type of "self" in classes.
+
+        Example::
+
+          from typing import Self
+
+          class ReturnsSelf:
+              def parse(self, data: bytes) -> Self:
+                  ...
+                  return self
+
+        """
+
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError(f"{self} cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError(f"{self} cannot be used with issubclass().")
+
+    Self = _Self(_root=True)
+
+
+if hasattr(typing, "Never"):
+    Never = typing.Never
+elif sys.version_info[:2] >= (3, 7):
+    @_SpecialForm
+    def Never(self, params):
+        """The bottom type, a type that has no members.
+
+        This can be used to define a function that should never be
+        called, or a function that never returns::
+
+            from typing_extensions import Never
+
+            def never_call_me(arg: Never) -> None:
+                pass
+
+            def int_or_str(arg: int | str) -> None:
+                never_call_me(arg)  # type checker error
+                match arg:
+                    case int():
+                        print("It's an int")
+                    case str():
+                        print("It's a str")
+                    case _:
+                        never_call_me(arg)  # ok, arg is of type Never
+
+        """
+
+        raise TypeError(f"{self} is not subscriptable")
+else:
+    class _Never(typing._FinalTypingBase, _root=True):
+        """The bottom type, a type that has no members.
+
+        This can be used to define a function that should never be
+        called, or a function that never returns::
+
+            from typing_extensions import Never
+
+            def never_call_me(arg: Never) -> None:
+                pass
+
+            def int_or_str(arg: int | str) -> None:
+                never_call_me(arg)  # type checker error
+                match arg:
+                    case int():
+                        print("It's an int")
+                    case str():
+                        print("It's a str")
+                    case _:
+                        never_call_me(arg)  # ok, arg is of type Never
+
+        """
+
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError(f"{self} cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError(f"{self} cannot be used with issubclass().")
+
+    Never = _Never(_root=True)
+
+
+if hasattr(typing, 'Required'):
+    Required = typing.Required
+    NotRequired = typing.NotRequired
+elif sys.version_info[:2] >= (3, 9):
+    class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    @_ExtensionsSpecialForm
+    def Required(self, parameters):
+        """A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only single type')
+        return typing._GenericAlias(self, (item,))
+
+    @_ExtensionsSpecialForm
+    def NotRequired(self, parameters):
+        """A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only single type')
+        return typing._GenericAlias(self, (item,))
+
+elif sys.version_info[:2] >= (3, 7):
+    class _RequiredForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      '{} accepts only single type'.format(self._name))
+            return typing._GenericAlias(self, (item,))
+
+    Required = _RequiredForm(
+        'Required',
+        doc="""A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """)
+    NotRequired = _RequiredForm(
+        'NotRequired',
+        doc="""A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """)
+else:
+    # NOTE: Modeled after _Final's implementation when _FinalTypingBase available
+    class _MaybeRequired(typing._FinalTypingBase, _root=True):
+        __slots__ = ('__type__',)
+
+        def __init__(self, tp=None, **kwds):
+            self.__type__ = tp
+
+        def __getitem__(self, item):
+            cls = type(self)
+            if self.__type__ is None:
+                return cls(typing._type_check(item,
+                           '{} accepts only single type.'.format(cls.__name__[1:])),
+                           _root=True)
+            raise TypeError('{} cannot be further subscripted'
+                            .format(cls.__name__[1:]))
+
+        def _eval_type(self, globalns, localns):
+            new_tp = typing._eval_type(self.__type__, globalns, localns)
+            if new_tp == self.__type__:
+                return self
+            return type(self)(new_tp, _root=True)
+
+        def __repr__(self):
+            r = super().__repr__()
+            if self.__type__ is not None:
+                r += '[{}]'.format(typing._type_repr(self.__type__))
+            return r
+
+        def __hash__(self):
+            return hash((type(self).__name__, self.__type__))
+
+        def __eq__(self, other):
+            if not isinstance(other, type(self)):
+                return NotImplemented
+            if self.__type__ is not None:
+                return self.__type__ == other.__type__
+            return self is other
+
+    class _Required(_MaybeRequired, _root=True):
+        """A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """
+
+    class _NotRequired(_MaybeRequired, _root=True):
+        """A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """
+
+    Required = _Required(_root=True)
+    NotRequired = _NotRequired(_root=True)
+
+
+if sys.version_info[:2] >= (3, 9):
+    class _UnpackSpecialForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    class _UnpackAlias(typing._GenericAlias, _root=True):
+        __class__ = typing.TypeVar
+
+    @_UnpackSpecialForm
+    def Unpack(self, parameters):
+        """A special typing construct to unpack a variadic type. For example:
+
+            Shape = TypeVarTuple('Shape')
+            Batch = NewType('Batch', int)
+
+            def add_batch_axis(
+                x: Array[Unpack[Shape]]
+            ) -> Array[Batch, Unpack[Shape]]: ...
+
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only single type')
+        return _UnpackAlias(self, (item,))
+
+    def _is_unpack(obj):
+        return isinstance(obj, _UnpackAlias)
+
+elif sys.version_info[:2] >= (3, 7):
+    class _UnpackAlias(typing._GenericAlias, _root=True):
+        __class__ = typing.TypeVar
+
+    class _UnpackForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only single type')
+            return _UnpackAlias(self, (item,))
+
+    Unpack = _UnpackForm(
+        'Unpack',
+        doc="""A special typing construct to unpack a variadic type. For example:
+
+            Shape = TypeVarTuple('Shape')
+            Batch = NewType('Batch', int)
+
+            def add_batch_axis(
+                x: Array[Unpack[Shape]]
+            ) -> Array[Batch, Unpack[Shape]]: ...
+
+        """)
+
+    def _is_unpack(obj):
+        return isinstance(obj, _UnpackAlias)
+
+else:
+    # NOTE: Modeled after _Final's implementation when _FinalTypingBase available
+    class _Unpack(typing._FinalTypingBase, _root=True):
+        """A special typing construct to unpack a variadic type. For example:
+
+            Shape = TypeVarTuple('Shape')
+            Batch = NewType('Batch', int)
+
+            def add_batch_axis(
+                x: Array[Unpack[Shape]]
+            ) -> Array[Batch, Unpack[Shape]]: ...
+
+        """
+        __slots__ = ('__type__',)
+        __class__ = typing.TypeVar
+
+        def __init__(self, tp=None, **kwds):
+            self.__type__ = tp
+
+        def __getitem__(self, item):
+            cls = type(self)
+            if self.__type__ is None:
+                return cls(typing._type_check(item,
+                           'Unpack accepts only single type.'),
+                           _root=True)
+            raise TypeError('Unpack cannot be further subscripted')
+
+        def _eval_type(self, globalns, localns):
+            new_tp = typing._eval_type(self.__type__, globalns, localns)
+            if new_tp == self.__type__:
+                return self
+            return type(self)(new_tp, _root=True)
+
+        def __repr__(self):
+            r = super().__repr__()
+            if self.__type__ is not None:
+                r += '[{}]'.format(typing._type_repr(self.__type__))
+            return r
+
+        def __hash__(self):
+            return hash((type(self).__name__, self.__type__))
+
+        def __eq__(self, other):
+            if not isinstance(other, _Unpack):
+                return NotImplemented
+            if self.__type__ is not None:
+                return self.__type__ == other.__type__
+            return self is other
+
+        # For 3.6 only
+        def _get_type_vars(self, tvars):
+            self.__type__._get_type_vars(tvars)
+
+    Unpack = _Unpack(_root=True)
+
+    def _is_unpack(obj):
+        return isinstance(obj, _Unpack)
+
+
+class TypeVarTuple:
+    """Type variable tuple.
+
+    Usage::
+
+        Ts = TypeVarTuple('Ts')
+
+    In the same way that a normal type variable is a stand-in for a single
+    type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* type such as
+    ``Tuple[int, str]``.
+
+    Type variable tuples can be used in ``Generic`` declarations.
+    Consider the following example::
+
+        class Array(Generic[*Ts]): ...
+
+    The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``,
+    where ``T1`` and ``T2`` are type variables. To use these type variables
+    as type parameters of ``Array``, we must *unpack* the type variable tuple using
+    the star operator: ``*Ts``. The signature of ``Array`` then behaves
+    as if we had simply written ``class Array(Generic[T1, T2]): ...``.
+    In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows
+    us to parameterise the class with an *arbitrary* number of type parameters.
+
+    Type variable tuples can be used anywhere a normal ``TypeVar`` can.
+    This includes class definitions, as shown above, as well as function
+    signatures and variable annotations::
+
+        class Array(Generic[*Ts]):
+
+            def __init__(self, shape: Tuple[*Ts]):
+                self._shape: Tuple[*Ts] = shape
+
+            def get_shape(self) -> Tuple[*Ts]:
+                return self._shape
+
+        shape = (Height(480), Width(640))
+        x: Array[Height, Width] = Array(shape)
+        y = abs(x)  # Inferred type is Array[Height, Width]
+        z = x + x   #        ...    is Array[Height, Width]
+        x.get_shape()  #     ...    is tuple[Height, Width]
+
+    """
+
+    # Trick Generic __parameters__.
+    __class__ = typing.TypeVar
+
+    def __iter__(self):
+        yield self.__unpacked__
+
+    def __init__(self, name):
+        self.__name__ = name
+
+        # for pickling:
+        try:
+            def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+        except (AttributeError, ValueError):
+            def_mod = None
+        if def_mod != 'typing_extensions':
+            self.__module__ = def_mod
+
+        self.__unpacked__ = Unpack[self]
+
+    def __repr__(self):
+        return self.__name__
+
+    def __hash__(self):
+        return object.__hash__(self)
+
+    def __eq__(self, other):
+        return self is other
+
+    def __reduce__(self):
+        return self.__name__
+
+    def __init_subclass__(self, *args, **kwds):
+        if '_root' not in kwds:
+            raise TypeError("Cannot subclass special typing classes")
+
+    if not PEP_560:
+        # Only needed in 3.6.
+        def _get_type_vars(self, tvars):
+            if self not in tvars:
+                tvars.append(self)
+
+
+if hasattr(typing, "reveal_type"):
+    reveal_type = typing.reveal_type
+else:
+    def reveal_type(__obj: T) -> T:
+        """Reveal the inferred type of a variable.
+
+        When a static type checker encounters a call to ``reveal_type()``,
+        it will emit the inferred type of the argument::
+
+            x: int = 1
+            reveal_type(x)
+
+        Running a static type checker (e.g., ``mypy``) on this example
+        will produce output similar to 'Revealed type is "builtins.int"'.
+
+        At runtime, the function prints the runtime type of the
+        argument and returns it unchanged.
+
+        """
+        print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr)
+        return __obj
+
+
+if hasattr(typing, "assert_never"):
+    assert_never = typing.assert_never
+else:
+    def assert_never(__arg: Never) -> Never:
+        """Assert to the type checker that a line of code is unreachable.
+
+        Example::
+
+            def int_or_str(arg: int | str) -> None:
+                match arg:
+                    case int():
+                        print("It's an int")
+                    case str():
+                        print("It's a str")
+                    case _:
+                        assert_never(arg)
+
+        If a type checker finds that a call to assert_never() is
+        reachable, it will emit an error.
+
+        At runtime, this throws an exception when called.
+
+        """
+        raise AssertionError("Expected code to be unreachable")
+
+
+if hasattr(typing, 'dataclass_transform'):
+    dataclass_transform = typing.dataclass_transform
+else:
+    def dataclass_transform(
+        *,
+        eq_default: bool = True,
+        order_default: bool = False,
+        kw_only_default: bool = False,
+        field_descriptors: typing.Tuple[
+            typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]],
+            ...
+        ] = (),
+    ) -> typing.Callable[[T], T]:
+        """Decorator that marks a function, class, or metaclass as providing
+        dataclass-like behavior.
+
+        Example:
+
+            from typing_extensions import dataclass_transform
+
+            _T = TypeVar("_T")
+
+            # Used on a decorator function
+            @dataclass_transform()
+            def create_model(cls: type[_T]) -> type[_T]:
+                ...
+                return cls
+
+            @create_model
+            class CustomerModel:
+                id: int
+                name: str
+
+            # Used on a base class
+            @dataclass_transform()
+            class ModelBase: ...
+
+            class CustomerModel(ModelBase):
+                id: int
+                name: str
+
+            # Used on a metaclass
+            @dataclass_transform()
+            class ModelMeta(type): ...
+
+            class ModelBase(metaclass=ModelMeta): ...
+
+            class CustomerModel(ModelBase):
+                id: int
+                name: str
+
+        Each of the ``CustomerModel`` classes defined in this example will now
+        behave similarly to a dataclass created with the ``@dataclasses.dataclass``
+        decorator. For example, the type checker will synthesize an ``__init__``
+        method.
+
+        The arguments to this decorator can be used to customize this behavior:
+        - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be
+          True or False if it is omitted by the caller.
+        - ``order_default`` indicates whether the ``order`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``kw_only_default`` indicates whether the ``kw_only`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``field_descriptors`` specifies a static list of supported classes
+          or functions, that describe fields, similar to ``dataclasses.field()``.
+
+        At runtime, this decorator records its arguments in the
+        ``__dataclass_transform__`` attribute on the decorated object.
+
+        See PEP 681 for details.
+
+        """
+        def decorator(cls_or_fn):
+            cls_or_fn.__dataclass_transform__ = {
+                "eq_default": eq_default,
+                "order_default": order_default,
+                "kw_only_default": kw_only_default,
+                "field_descriptors": field_descriptors,
+            }
+            return cls_or_fn
+        return decorator
+
+
+# We have to do some monkey patching to deal with the dual nature of
+# Unpack/TypeVarTuple:
+# - We want Unpack to be a kind of TypeVar so it gets accepted in
+#   Generic[Unpack[Ts]]
+# - We want it to *not* be treated as a TypeVar for the purposes of
+#   counting generic parameters, so that when we subscript a generic,
+#   the runtime doesn't try to substitute the Unpack with the subscripted type.
+if not hasattr(typing, "TypeVarTuple"):
+    typing._collect_type_vars = _collect_type_vars
+    typing._check_generic = _check_generic
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/.flake8 b/third_party/wpt_tools/wpt/tools/third_party/zipp/.flake8
new file mode 100644 (file)
index 0000000..790c109
--- /dev/null
@@ -0,0 +1,9 @@
+[flake8]
+max-line-length = 88
+ignore =
+       # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513
+       W503
+       # W504 has issues https://github.com/OCA/maintainer-quality-tools/issues/545
+       W504
+       # Black creates whitespace before colon
+       E203
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/.pre-commit-config.yaml b/third_party/wpt_tools/wpt/tools/third_party/zipp/.pre-commit-config.yaml
new file mode 100644 (file)
index 0000000..922d942
--- /dev/null
@@ -0,0 +1,5 @@
+repos:
+- repo: https://github.com/ambv/black
+  rev: 18.9b0
+  hooks:
+  - id: black
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/.readthedocs.yml b/third_party/wpt_tools/wpt/tools/third_party/zipp/.readthedocs.yml
new file mode 100644 (file)
index 0000000..8ae4468
--- /dev/null
@@ -0,0 +1,5 @@
+python:
+  version: 3
+  extra_requirements:
+    - docs
+  pip_install: true
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/.travis.yml b/third_party/wpt_tools/wpt/tools/third_party/zipp/.travis.yml
new file mode 100644 (file)
index 0000000..b7d8f3a
--- /dev/null
@@ -0,0 +1,28 @@
+dist: xenial
+language: python
+
+python:
+- 2.7
+- 3.6
+- &latest_py3 3.8
+
+jobs:
+  fast_finish: true
+  include:
+  - stage: deploy
+    if: tag IS present
+    python: *latest_py3
+    before_script: skip
+    script: tox -e release
+
+cache: pip
+
+install:
+- pip install tox tox-venv
+
+before_script:
+  # Disable IPv6. Ref travis-ci/travis-ci#8361
+  - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
+      sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6';
+    fi
+script: tox
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/CHANGES.rst b/third_party/wpt_tools/wpt/tools/third_party/zipp/CHANGES.rst
new file mode 100644 (file)
index 0000000..a464a63
--- /dev/null
@@ -0,0 +1,100 @@
+v1.2.0
+======
+
+#44: ``zipp.Path.open()`` now supports a compatible signature
+as ``pathlib.Path.open()``, accepting text (default) or binary
+modes and soliciting keyword parameters passed through to
+``io.TextIOWrapper`` (encoding, newline, etc). The stream is
+opened in text-mode by default now. ``open`` no
+longer accepts ``pwd`` as a positional argument and does not
+accept the ``force_zip64`` parameter at all. This change is
+a backward-incompatible change for that single function.
+
+v1.1.1
+======
+
+#43: Restored performance of implicit dir computation.
+
+v1.1.0
+======
+
+#32: For read-only zip files, complexity of ``.exists`` and
+``joinpath`` is now constant time instead of ``O(n)``, preventing
+quadratic time in common use-cases and rendering large
+zip files unusable for Path. Big thanks to Benjy Weinberger
+for the bug report and contributed fix (#33).
+
+v1.0.0
+======
+
+Re-release of 0.6 to correspond with release as found in
+Python 3.8.
+
+v0.6.0
+======
+
+#12: When adding implicit dirs, ensure that ancestral directories
+are added and that duplicates are excluded.
+
+The library now relies on
+`more_itertools <https://pypi.org/project/more_itertools>`_.
+
+v0.5.2
+======
+
+#7: Parent of a directory now actually returns the parent.
+
+v0.5.1
+======
+
+Declared package as backport.
+
+v0.5.0
+======
+
+Add ``.joinpath()`` method and ``.parent`` property.
+
+Now a backport release of the ``zipfile.Path`` class.
+
+v0.4.0
+======
+
+#4: Add support for zip files with implied directories.
+
+v0.3.3
+======
+
+#3: Fix issue where ``.name`` on a directory was empty.
+
+v0.3.2
+======
+
+#2: Fix TypeError on Python 2.7 when classic division is used.
+
+v0.3.1
+======
+
+#1: Fix TypeError on Python 3.5 when joining to a path-like object.
+
+v0.3.0
+======
+
+Add support for constructing a ``zipp.Path`` from any path-like
+object.
+
+``zipp.Path`` is now a new-style class on Python 2.7.
+
+v0.2.1
+======
+
+Fix issue with ``__str__``.
+
+v0.2.0
+======
+
+Drop reliance on future-fstrings.
+
+v0.1.0
+======
+
+Initial release with basic functionality.
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/LICENSE b/third_party/wpt_tools/wpt/tools/third_party/zipp/LICENSE
new file mode 100644 (file)
index 0000000..5e795a6
--- /dev/null
@@ -0,0 +1,7 @@
+Copyright Jason R. Coombs
+
+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/wpt_tools/wpt/tools/third_party/zipp/README.rst b/third_party/wpt_tools/wpt/tools/third_party/zipp/README.rst
new file mode 100644 (file)
index 0000000..ce128a3
--- /dev/null
@@ -0,0 +1,21 @@
+.. image:: https://img.shields.io/pypi/v/zipp.svg
+   :target: https://pypi.org/project/zipp
+
+.. image:: https://img.shields.io/pypi/pyversions/zipp.svg
+
+.. image:: https://img.shields.io/travis/jaraco/zipp/master.svg
+   :target: https://travis-ci.org/jaraco/zipp
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+   :target: https://github.com/ambv/black
+   :alt: Code style: Black
+
+.. image:: https://img.shields.io/appveyor/ci/jaraco/zipp/master.svg
+   :target: https://ci.appveyor.com/project/jaraco/zipp/branch/master
+
+.. .. image:: https://readthedocs.org/projects/zipp/badge/?version=latest
+..    :target: https://zipp.readthedocs.io/en/latest/?badge=latest
+
+
+A pathlib-compatible Zipfile object wrapper. A backport of the
+`Path object <https://docs.python.org/3.8/library/zipfile.html#path-objects>`_.
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/appveyor.yml b/third_party/wpt_tools/wpt/tools/third_party/zipp/appveyor.yml
new file mode 100644 (file)
index 0000000..f35aa27
--- /dev/null
@@ -0,0 +1,24 @@
+environment:
+
+  APPVEYOR: true
+
+  matrix:
+    - PYTHON: "C:\\Python36-x64"
+    - PYTHON: "C:\\Python27-x64"
+
+install:
+  # symlink python from a directory with a space
+  - "mklink /d \"C:\\Program Files\\Python\" %PYTHON%"
+  - "SET PYTHON=\"C:\\Program Files\\Python\""
+  - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
+
+build: off
+
+cache:
+  - '%LOCALAPPDATA%\pip\Cache'
+
+test_script:
+  - "python -m pip install -U tox tox-venv virtualenv"
+  - "tox"
+
+version: '{build}'
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/conftest.py b/third_party/wpt_tools/wpt/tools/third_party/zipp/conftest.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/docs/conf.py b/third_party/wpt_tools/wpt/tools/third_party/zipp/docs/conf.py
new file mode 100644 (file)
index 0000000..41b5355
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker']
+
+master_doc = "index"
+
+link_files = {
+    '../CHANGES.rst': dict(
+        using=dict(GH='https://github.com'),
+        replace=[
+            dict(
+                pattern=r'(Issue #|\B#)(?P<issue>\d+)',
+                url='{package_url}/issues/{issue}',
+            ),
+            dict(
+                pattern=r'^(?m)((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n',
+                with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n',
+            ),
+            dict(
+                pattern=r'PEP[- ](?P<pep_number>\d+)',
+                url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/',
+            ),
+        ],
+    )
+}
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/docs/history.rst b/third_party/wpt_tools/wpt/tools/third_party/zipp/docs/history.rst
new file mode 100644 (file)
index 0000000..8e21750
--- /dev/null
@@ -0,0 +1,8 @@
+:tocdepth: 2
+
+.. _changes:
+
+History
+*******
+
+.. include:: ../CHANGES (links).rst
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/docs/index.rst b/third_party/wpt_tools/wpt/tools/third_party/zipp/docs/index.rst
new file mode 100644 (file)
index 0000000..ff49bf9
--- /dev/null
@@ -0,0 +1,22 @@
+Welcome to zipp documentation!
+========================================
+
+.. toctree::
+   :maxdepth: 1
+
+   history
+
+
+.. automodule:: zipp
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/pyproject.toml b/third_party/wpt_tools/wpt/tools/third_party/zipp/pyproject.toml
new file mode 100644 (file)
index 0000000..3afc8c3
--- /dev/null
@@ -0,0 +1,6 @@
+[build-system]
+requires = ["setuptools>=34.4", "wheel", "setuptools_scm>=1.15"]
+build-backend = "setuptools.build_meta"
+
+[tool.black]
+skip-string-normalization = true
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/setup.cfg b/third_party/wpt_tools/wpt/tools/third_party/zipp/setup.cfg
new file mode 100644 (file)
index 0000000..3d193c3
--- /dev/null
@@ -0,0 +1,46 @@
+[bdist_wheel]
+universal = 1
+
+[metadata]
+license_file = LICENSE
+name = zipp
+author = Jason R. Coombs
+author_email = jaraco@jaraco.com
+description = Backport of pathlib-compatible object wrapper for zip files
+long_description = file:README.rst
+url = https://github.com/jaraco/zipp
+classifiers =
+       Development Status :: 5 - Production/Stable
+       Intended Audience :: Developers
+       License :: OSI Approved :: MIT License
+       Programming Language :: Python :: 2.7
+       Programming Language :: Python :: 3
+
+[options]
+py_modules = zipp
+packages = find:
+include_package_data = true
+python_requires = >=2.7
+install_requires =
+       contextlib2; python_version < "3.4"
+setup_requires = setuptools_scm >= 1.15.0
+
+[options.extras_require]
+testing =
+       # upstream
+
+       # local
+       pathlib2
+       unittest2
+       jaraco.itertools
+       func-timeout
+
+docs =
+       # upstream
+       sphinx
+       jaraco.packaging >= 3.2
+       rst.linker >= 1.9
+
+       # local
+
+[options.entry_points]
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/setup.py b/third_party/wpt_tools/wpt/tools/third_party/zipp/setup.py
new file mode 100644 (file)
index 0000000..827e955
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+import setuptools
+
+if __name__ == "__main__":
+    setuptools.setup(use_scm_version=True)
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/skeleton.md b/third_party/wpt_tools/wpt/tools/third_party/zipp/skeleton.md
new file mode 100644 (file)
index 0000000..52b97f0
--- /dev/null
@@ -0,0 +1,137 @@
+# Overview
+
+This project is merged with [skeleton](https://github.com/jaraco/skeleton). What is skeleton? It's the scaffolding of a Python project jaraco [introduced in his blog](https://blog.jaraco.com/a-project-skeleton-for-python-projects/). It seeks to provide a means to re-use techniques and inherit advances when managing projects for distribution.
+
+## An SCM Managed Approach
+
+While maintaining dozens of projects in PyPI, jaraco derives best practices for project distribution and publishes them in the [skeleton repo](https://github.com/jaraco/skeleton), a git repo capturing the evolution and culmination of these best practices.
+
+It's intended to be used by a new or existing project to adopt these practices and honed and proven techniques. Adopters are encouraged to use the project directly and maintain a small deviation from the technique, make their own fork for more substantial changes unique to their environment or preferences, or simply adopt the skeleton once and abandon it thereafter.
+
+The primary advantage to using an SCM for maintaining these techniques is that those tools help facilitate the merge between the template and its adopting projects.
+
+Another advantage to using an SCM-managed approach is that tools like GitHub recognize that a change in the skeleton is the _same change_ across all projects that merge with that skeleton. Without the ancestry, with a traditional copy/paste approach, a [commit like this](https://github.com/jaraco/skeleton/commit/12eed1326e1bc26ce256e7b3f8cd8d3a5beab2d5) would produce notifications in the upstream project issue for each and every application, but because it's centralized, GitHub provides just the one notification when the change is added to the skeleton.
+
+# Usage
+
+## new projects
+
+To use skeleton for a new project, simply pull the skeleton into a new project:
+
+```
+$ git init my-new-project
+$ cd my-new-project
+$ git pull gh://jaraco/skeleton
+```
+
+Now customize the project to suit your individual project needs.
+
+## existing projects
+
+If you have an existing project, you can still incorporate the skeleton by merging it into the codebase.
+
+```
+$ git merge skeleton --allow-unrelated-histories
+```
+
+The `--allow-unrelated-histories` is necessary because the history from the skeleton was previously unrelated to the existing codebase. Resolve any merge conflicts and commit to the master, and now the project is based on the shared skeleton.
+
+## Updating
+
+Whenever a change is needed or desired for the general technique for packaging, it can be made in the skeleton project and then merged into each of the derived projects as needed, recommended before each release. As a result, features and best practices for packaging are centrally maintained and readily trickle into a whole suite of packages. This technique lowers the amount of tedious work necessary to create or maintain a project, and coupled with other techniques like continuous integration and deployment, lowers the cost of creating and maintaining refined Python projects to just a few, familiar git operations.
+
+Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints.
+
+# Features
+
+The features/techniques employed by the skeleton include:
+
+- PEP 517/518 based build relying on setuptools as the build tool
+- setuptools declarative configuration using setup.cfg
+- tox for running tests
+- A README.rst as reStructuredText with some popular badges, but with readthedocs and appveyor badges commented out
+- A CHANGES.rst file intended for publishing release notes about the project
+- Use of [black](https://black.readthedocs.io/en/stable/) for code formatting (disabled on unsupported Python 3.5 and earlier)
+
+## Packaging Conventions
+
+A pyproject.toml is included to enable PEP 517 and PEP 518 compatibility and declares the requirements necessary to build the project on setuptools (a minimum version compatible with setup.cfg declarative config).
+
+The setup.cfg file implements the following features:
+
+- Assumes universal wheel for release
+- Advertises the project's LICENSE file (MIT by default)
+- Reads the README.rst file into the long description
+- Some common Trove classifiers
+- Includes all packages discovered in the repo
+- Data files in the package are also included (not just Python files)
+- Declares the required Python versions
+- Declares install requirements (empty by default)
+- Declares setup requirements for legacy environments
+- Supplies two 'extras':
+  - testing: requirements for running tests
+  - docs: requirements for building docs
+  - these extras split the declaration into "upstream" (requirements as declared by the skeleton) and "local" (those specific to the local project); these markers help avoid merge conflicts
+- Placeholder for defining entry points
+
+Additionally, the setup.py file declares `use_scm_version` which relies on [setuptools_scm](https://pypi.org/project/setuptools_scm) to do two things:
+
+- derive the project version from SCM tags
+- ensure that all files committed to the repo are automatically included in releases
+
+## Running Tests
+
+The skeleton assumes the developer has [tox](https://pypi.org/project/tox) installed. The developer is expected to run `tox` to run tests on the current Python version using [pytest](https://pypi.org/project/pytest).
+
+Other environments (invoked with `tox -e {name}`) supplied include:
+
+  - a `build-docs` environment to build the documentation
+  - a `release` environment to publish the package to PyPI
+
+A pytest.ini is included to define common options around running tests. In particular:
+
+- rely on default test discovery in the current directory
+- avoid recursing into common directories not containing tests
+- run doctests on modules and invoke flake8 tests
+- in doctests, allow unicode literals and regular literals to match, allowing for doctests to run on Python 2 and 3. Also enable ELLIPSES, a default that would be undone by supplying the prior option.
+- filters out known warnings caused by libraries/functionality included by the skeleton
+
+Relies a .flake8 file to correct some default behaviors:
+
+- disable mutually incompatible rules W503 and W504
+- support for black format
+
+## Continuous Integration
+
+The project is pre-configured to run tests in [Travis-CI](https://travis-ci.org) (.travis.yml). Any new project must be enabled either through their web site or with the `travis enable` command.
+
+Features include:
+- test against Python 2 and 3
+- run on Ubuntu Xenial
+- correct for broken IPv6
+
+Also provided is a minimal template for running under Appveyor (Windows).
+
+### Continuous Deployments
+
+In addition to running tests, an additional deploy stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with Travis as the TWINE_PASSWORD environment variable. After the Travis project is created, configure the token through the web UI or with a command like the following (bash syntax):
+
+```
+TWINE_PASSWORD={token} travis env copy TWINE_PASSWORD
+```
+
+## Building Documentation
+
+Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e build-docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`.
+
+In addition to building the sphinx docs scaffolded in `docs/`, the docs build a `history.html` file that first injects release dates and hyperlinks into the CHANGES.rst before incorporating it as history in the docs.
+
+## Cutting releases
+
+By default, tagged commits are released through the continuous integration deploy stage.
+
+Releases may also be cut manually by invoking the tox environment `release` with the PyPI token set as the TWINE_PASSWORD:
+
+```
+TWINE_PASSWORD={token} tox -e release
+```
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/test_zipp.py b/third_party/wpt_tools/wpt/tools/third_party/zipp/test_zipp.py
new file mode 100644 (file)
index 0000000..810d10b
--- /dev/null
@@ -0,0 +1,245 @@
+# coding: utf-8
+
+from __future__ import division, unicode_literals
+
+import io
+import zipfile
+import contextlib
+import tempfile
+import shutil
+import string
+
+try:
+    import pathlib
+except ImportError:
+    import pathlib2 as pathlib
+
+if not hasattr(contextlib, 'ExitStack'):
+    import contextlib2
+    contextlib.ExitStack = contextlib2.ExitStack
+
+try:
+    import unittest
+
+    unittest.TestCase.subTest
+except AttributeError:
+    import unittest2 as unittest
+
+import jaraco.itertools
+import func_timeout
+
+import zipp
+
+__metaclass__ = type
+consume = tuple
+
+
+def add_dirs(zf):
+    """
+    Given a writable zip file zf, inject directory entries for
+    any directories implied by the presence of children.
+    """
+    for name in zipp.CompleteDirs._implied_dirs(zf.namelist()):
+        zf.writestr(name, b"")
+    return zf
+
+
+def build_alpharep_fixture():
+    """
+    Create a zip file with this structure:
+
+    .
+    ├── a.txt
+    ├── b
+    │   ├── c.txt
+    │   ├── d
+    │   │   └── e.txt
+    │   └── f.txt
+    └── g
+        └── h
+            └── i.txt
+
+    This fixture has the following key characteristics:
+
+    - a file at the root (a)
+    - a file two levels deep (b/d/e)
+    - multiple files in a directory (b/c, b/f)
+    - a directory containing only a directory (g/h)
+
+    "alpha" because it uses alphabet
+    "rep" because it's a representative example
+    """
+    data = io.BytesIO()
+    zf = zipfile.ZipFile(data, "w")
+    zf.writestr("a.txt", b"content of a")
+    zf.writestr("b/c.txt", b"content of c")
+    zf.writestr("b/d/e.txt", b"content of e")
+    zf.writestr("b/f.txt", b"content of f")
+    zf.writestr("g/h/i.txt", b"content of i")
+    zf.filename = "alpharep.zip"
+    return zf
+
+
+@contextlib.contextmanager
+def temp_dir():
+    tmpdir = tempfile.mkdtemp()
+    try:
+        yield pathlib.Path(tmpdir)
+    finally:
+        shutil.rmtree(tmpdir)
+
+
+class TestPath(unittest.TestCase):
+    def setUp(self):
+        self.fixtures = contextlib.ExitStack()
+        self.addCleanup(self.fixtures.close)
+
+    def zipfile_alpharep(self):
+        with self.subTest():
+            yield build_alpharep_fixture()
+        with self.subTest():
+            yield add_dirs(build_alpharep_fixture())
+
+    def zipfile_ondisk(self):
+        tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir()))
+        for alpharep in self.zipfile_alpharep():
+            buffer = alpharep.fp
+            alpharep.close()
+            path = tmpdir / alpharep.filename
+            with path.open("wb") as strm:
+                strm.write(buffer.getvalue())
+            yield path
+
+    def test_iterdir_and_types(self):
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            assert root.is_dir()
+            a, b, g = root.iterdir()
+            assert a.is_file()
+            assert b.is_dir()
+            assert g.is_dir()
+            c, f, d = b.iterdir()
+            assert c.is_file() and f.is_file()
+            e, = d.iterdir()
+            assert e.is_file()
+            h, = g.iterdir()
+            i, = h.iterdir()
+            assert i.is_file()
+
+    def test_open(self):
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            a, b, g = root.iterdir()
+            with a.open() as strm:
+                data = strm.read()
+            assert data == "content of a"
+
+    def test_read(self):
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            a, b, g = root.iterdir()
+            assert a.read_text() == "content of a"
+            assert a.read_bytes() == b"content of a"
+
+    def test_joinpath(self):
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            a = root.joinpath("a")
+            assert a.is_file()
+            e = root.joinpath("b").joinpath("d").joinpath("e.txt")
+            assert e.read_text() == "content of e"
+
+    def test_traverse_truediv(self):
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            a = root / "a"
+            assert a.is_file()
+            e = root / "b" / "d" / "e.txt"
+            assert e.read_text() == "content of e"
+
+    def test_traverse_simplediv(self):
+        """
+        Disable the __future__.division when testing traversal.
+        """
+        for alpharep in self.zipfile_alpharep():
+            code = compile(
+                source="zipp.Path(alpharep) / 'a'",
+                filename="(test)",
+                mode="eval",
+                dont_inherit=True,
+            )
+            eval(code)
+
+    def test_pathlike_construction(self):
+        """
+        zipp.Path should be constructable from a path-like object
+        """
+        for zipfile_ondisk in self.zipfile_ondisk():
+            pathlike = pathlib.Path(str(zipfile_ondisk))
+            zipp.Path(pathlike)
+
+    def test_traverse_pathlike(self):
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            root / pathlib.Path("a")
+
+    def test_parent(self):
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            assert (root / 'a').parent.at == ''
+            assert (root / 'a' / 'b').parent.at == 'a/'
+
+    def test_dir_parent(self):
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            assert (root / 'b').parent.at == ''
+            assert (root / 'b/').parent.at == ''
+
+    def test_missing_dir_parent(self):
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            assert (root / 'missing dir/').parent.at == ''
+
+    def test_mutability(self):
+        """
+        If the underlying zipfile is changed, the Path object should
+        reflect that change.
+        """
+        for alpharep in self.zipfile_alpharep():
+            root = zipp.Path(alpharep)
+            a, b, g = root.iterdir()
+            alpharep.writestr('foo.txt', b'foo')
+            alpharep.writestr('bar/baz.txt', b'baz')
+            assert any(
+                child.name == 'foo.txt'
+                for child in root.iterdir())
+            assert (root / 'foo.txt').read_text() == 'foo'
+            baz, = (root / 'bar').iterdir()
+            assert baz.read_text() == 'baz'
+
+    HUGE_ZIPFILE_NUM_ENTRIES = 2 ** 13
+
+    def huge_zipfile(self):
+        """Create a read-only zipfile with a huge number of entries entries."""
+        strm = io.BytesIO()
+        zf = zipfile.ZipFile(strm, "w")
+        for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)):
+            zf.writestr(entry, entry)
+        zf.mode = 'r'
+        return zf
+
+    def test_joinpath_constant_time(self):
+        """
+        Ensure joinpath on items in zipfile is linear time.
+        """
+        root = zipp.Path(self.huge_zipfile())
+        entries = jaraco.itertools.Counter(root.iterdir())
+        for entry in entries:
+            entry.joinpath('suffix')
+        # Check the file iterated all items
+        assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES
+
+    @func_timeout.func_set_timeout(3)
+    def test_implied_dirs_performance(self):
+        data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)]
+        zipp.CompleteDirs._implied_dirs(data)
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/tox.ini b/third_party/wpt_tools/wpt/tools/third_party/zipp/tox.ini
new file mode 100644 (file)
index 0000000..cb542c1
--- /dev/null
@@ -0,0 +1,36 @@
+[tox]
+envlist = python
+minversion = 3.2
+# https://github.com/jaraco/skeleton/issues/6
+tox_pip_extensions_ext_venv_update = true
+
+[testenv]
+deps =
+       setuptools>=31.0.1
+commands =
+       python -m unittest discover
+usedevelop = True
+extras = testing
+
+[testenv:build-docs]
+extras =
+    docs
+    testing
+changedir = docs
+commands =
+    python -m sphinx . {toxinidir}/build/html
+
+[testenv:release]
+skip_install = True
+deps =
+       pep517>=0.5
+       twine>=1.13
+       path.py
+passenv =
+       TWINE_PASSWORD
+setenv =
+       TWINE_USERNAME = {env:TWINE_USERNAME:__token__}
+commands =
+       python -c "import path; path.Path('dist').rmtree_p()"
+       python -m pep517.build .
+       python -m twine upload dist/*
diff --git a/third_party/wpt_tools/wpt/tools/third_party/zipp/zipp.py b/third_party/wpt_tools/wpt/tools/third_party/zipp/zipp.py
new file mode 100644 (file)
index 0000000..8922058
--- /dev/null
@@ -0,0 +1,286 @@
+# coding: utf-8
+
+from __future__ import division
+
+import io
+import sys
+import posixpath
+import zipfile
+import functools
+import itertools
+from collections import OrderedDict
+
+try:
+    from contextlib import suppress
+except ImportError:
+    from contextlib2 import suppress
+
+__metaclass__ = type
+
+
+def _parents(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all parents of that path.
+
+    >>> list(_parents('b/d'))
+    ['b']
+    >>> list(_parents('/b/d/'))
+    ['/b']
+    >>> list(_parents('b/d/f/'))
+    ['b/d', 'b']
+    >>> list(_parents('b'))
+    []
+    >>> list(_parents(''))
+    []
+    """
+    return itertools.islice(_ancestry(path), 1, None)
+
+
+def _ancestry(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all elements of that path
+
+    >>> list(_ancestry('b/d'))
+    ['b/d', 'b']
+    >>> list(_ancestry('/b/d/'))
+    ['/b/d', '/b']
+    >>> list(_ancestry('b/d/f/'))
+    ['b/d/f', 'b/d', 'b']
+    >>> list(_ancestry('b'))
+    ['b']
+    >>> list(_ancestry(''))
+    []
+    """
+    path = path.rstrip(posixpath.sep)
+    while path and path != posixpath.sep:
+        yield path
+        path, tail = posixpath.split(path)
+
+
+class CompleteDirs(zipfile.ZipFile):
+    """
+    A ZipFile subclass that ensures that implied directories
+    are always included in the namelist.
+    """
+
+    @staticmethod
+    def _implied_dirs(names):
+        parents = itertools.chain.from_iterable(map(_parents, names))
+        # Cast names to a set for O(1) lookups
+        existing = set(names)
+        # Deduplicate entries in original order
+        implied_dirs = OrderedDict.fromkeys(
+            p + posixpath.sep for p in parents
+            if p + posixpath.sep not in existing
+        )
+        return implied_dirs
+
+    def namelist(self):
+        names = super(CompleteDirs, self).namelist()
+        return names + list(self._implied_dirs(names))
+
+    def _name_set(self):
+        return set(self.namelist())
+
+    def resolve_dir(self, name):
+        """
+        If the name represents a directory, return that name
+        as a directory (with the trailing slash).
+        """
+        names = self._name_set()
+        dirname = name + '/'
+        dir_match = name not in names and dirname in names
+        return dirname if dir_match else name
+
+    @classmethod
+    def make(cls, source):
+        """
+        Given a source (filename or zipfile), return an
+        appropriate CompleteDirs subclass.
+        """
+        if isinstance(source, CompleteDirs):
+            return source
+
+        if not isinstance(source, zipfile.ZipFile):
+            return cls(_pathlib_compat(source))
+
+        # Only allow for FastPath when supplied zipfile is read-only
+        if 'r' not in source.mode:
+            cls = CompleteDirs
+
+        res = cls.__new__(cls)
+        vars(res).update(vars(source))
+        return res
+
+
+class FastLookup(CompleteDirs):
+    """
+    ZipFile subclass to ensure implicit
+    dirs exist and are resolved rapidly.
+    """
+    def namelist(self):
+        with suppress(AttributeError):
+            return self.__names
+        self.__names = super(FastLookup, self).namelist()
+        return self.__names
+
+    def _name_set(self):
+        with suppress(AttributeError):
+            return self.__lookup
+        self.__lookup = super(FastLookup, self)._name_set()
+        return self.__lookup
+
+
+def _pathlib_compat(path):
+    """
+    For path-like objects, convert to a filename for compatibility
+    on Python 3.6.1 and earlier.
+    """
+    try:
+        return path.__fspath__()
+    except AttributeError:
+        return str(path)
+
+
+class Path:
+    """
+    A pathlib-compatible interface for zip files.
+
+    Consider a zip file with this structure::
+
+        .
+        ├── a.txt
+        └── b
+            ├── c.txt
+            └── d
+                └── e.txt
+
+    >>> data = io.BytesIO()
+    >>> zf = zipfile.ZipFile(data, 'w')
+    >>> zf.writestr('a.txt', 'content of a')
+    >>> zf.writestr('b/c.txt', 'content of c')
+    >>> zf.writestr('b/d/e.txt', 'content of e')
+    >>> zf.filename = 'abcde.zip'
+
+    Path accepts the zipfile object itself or a filename
+
+    >>> root = Path(zf)
+
+    From there, several path operations are available.
+
+    Directory iteration (including the zip file itself):
+
+    >>> a, b = root.iterdir()
+    >>> a
+    Path('abcde.zip', 'a.txt')
+    >>> b
+    Path('abcde.zip', 'b/')
+
+    name property:
+
+    >>> b.name
+    'b'
+
+    join with divide operator:
+
+    >>> c = b / 'c.txt'
+    >>> c
+    Path('abcde.zip', 'b/c.txt')
+    >>> c.name
+    'c.txt'
+
+    Read text:
+
+    >>> c.read_text()
+    'content of c'
+
+    existence:
+
+    >>> c.exists()
+    True
+    >>> (b / 'missing.txt').exists()
+    False
+
+    Coercion to string:
+
+    >>> str(c)
+    'abcde.zip/b/c.txt'
+    """
+
+    __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})"
+
+    def __init__(self, root, at=""):
+        self.root = FastLookup.make(root)
+        self.at = at
+
+    def open(self, mode='r', *args, **kwargs):
+        """
+        Open this entry as text or binary following the semantics
+        of ``pathlib.Path.open()`` by passing arguments through
+        to io.TextIOWrapper().
+        """
+        pwd = kwargs.pop('pwd', None)
+        zip_mode = mode[0]
+        stream = self.root.open(self.at, zip_mode, pwd=pwd)
+        if 'b' in mode:
+            if args or kwargs:
+                raise ValueError("encoding args invalid for binary operation")
+            return stream
+        return io.TextIOWrapper(stream, *args, **kwargs)
+
+    @property
+    def name(self):
+        return posixpath.basename(self.at.rstrip("/"))
+
+    def read_text(self, *args, **kwargs):
+        with self.open('r', *args, **kwargs) as strm:
+            return strm.read()
+
+    def read_bytes(self):
+        with self.open('rb') as strm:
+            return strm.read()
+
+    def _is_child(self, path):
+        return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/")
+
+    def _next(self, at):
+        return Path(self.root, at)
+
+    def is_dir(self):
+        return not self.at or self.at.endswith("/")
+
+    def is_file(self):
+        return not self.is_dir()
+
+    def exists(self):
+        return self.at in self.root._name_set()
+
+    def iterdir(self):
+        if not self.is_dir():
+            raise ValueError("Can't listdir a file")
+        subs = map(self._next, self.root.namelist())
+        return filter(self._is_child, subs)
+
+    def __str__(self):
+        return posixpath.join(self.root.filename, self.at)
+
+    def __repr__(self):
+        return self.__repr.format(self=self)
+
+    def joinpath(self, add):
+        next = posixpath.join(self.at, _pathlib_compat(add))
+        return self._next(self.root.resolve_dir(next))
+
+    __truediv__ = joinpath
+
+    @property
+    def parent(self):
+        parent_at = posixpath.dirname(self.at.rstrip('/'))
+        if parent_at:
+            parent_at += '/'
+        return self._next(parent_at)
+
+    if sys.version_info < (3,):
+        __div__ = __truediv__