././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6542168 zipp-3.21.0/0000755000175100001770000000000014714145655012200 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/.coveragerc0000644000175100001770000000054514714145626014323 0ustar00runnerdocker[run] omit = # leading `*/` for pytest-dev/pytest-cov#456 */.tox/* disable_warnings = couldnt-parse [report] show_missing = True exclude_also = # Exclude common false positives per # https://coverage.readthedocs.io/en/latest/excluding.html#advanced-exclusion # Ref jaraco/skeleton#97 and jaraco/skeleton#135 class .*\bProtocol\): if TYPE_CHECKING: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/.editorconfig0000644000175100001770000000036614714145626014660 0ustar00runnerdockerroot = 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 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6462166 zipp-3.21.0/.github/0000755000175100001770000000000014714145655013540 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/.github/FUNDING.yml0000644000175100001770000000002414714145626015347 0ustar00runnerdockertidelift: pypi/zipp ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/.github/dependabot.yml0000644000175100001770000000022414714145626016364 0ustar00runnerdockerversion: 2 updates: - package-ecosystem: "pip" directory: "/" schedule: interval: "daily" allow: - dependency-type: "all" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6462166 zipp-3.21.0/.github/workflows/0000755000175100001770000000000014714145655015575 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/.github/workflows/main.yml0000644000175100001770000000566714714145626017260 0ustar00runnerdockername: tests on: merge_group: push: branches-ignore: # temporary GH branches relating to merge queues (jaraco/skeleton#93) - gh-readonly-queue/** tags: # required if branches-ignore is supplied (jaraco/skeleton#103) - '**' pull_request: workflow_dispatch: permissions: contents: read env: # Environment variable to support color support (jaraco/skeleton#66) FORCE_COLOR: 1 # Suppress noisy pip warnings PIP_DISABLE_PIP_VERSION_CHECK: 'true' PIP_NO_PYTHON_VERSION_WARNING: 'true' PIP_NO_WARN_SCRIPT_LOCATION: 'true' # Ensure tests can sense settings about the environment TOX_OVERRIDE: >- testenv.pass_env+=GITHUB_*,FORCE_COLOR jobs: test: strategy: # https://blog.jaraco.com/efficient-use-of-ci-resources/ matrix: python: - "3.9" - "3.13" platform: - ubuntu-latest - macos-latest - windows-latest include: - python: "3.10" platform: ubuntu-latest - python: "3.11" platform: ubuntu-latest - python: "3.12" platform: ubuntu-latest - python: "3.14" platform: ubuntu-latest - python: pypy3.10 platform: ubuntu-latest runs-on: ${{ matrix.platform }} continue-on-error: ${{ matrix.python == '3.14' }} steps: - uses: actions/checkout@v4 - 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 run: tox collateral: strategy: fail-fast: false matrix: job: - diffcov - docs runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Python uses: actions/setup-python@v4 with: python-version: 3.x - name: Install tox run: python -m pip install tox - name: Eval ${{ matrix.job }} run: tox -e ${{ matrix.job }} check: # This job does nothing and is only used for the branch protection if: always() needs: - test - collateral 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) }} release: permissions: contents: write needs: - check if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v4 with: python-version: 3.x - name: Install tox run: python -m pip install tox - name: Run run: tox -e release env: TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/.pre-commit-config.yaml0000644000175100001770000000022614714145626016457 0ustar00runnerdockerrepos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.7.1 hooks: - id: ruff args: [--fix, --unsafe-fixes] - id: ruff-format ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/.readthedocs.yaml0000644000175100001770000000051614714145626015427 0ustar00runnerdockerversion: 2 python: install: - path: . extra_requirements: - doc # required boilerplate readthedocs/readthedocs.org#10401 build: os: ubuntu-lts-latest tools: python: latest # post-checkout job to ensure the clone isn't shallow jaraco/skeleton#114 jobs: post_checkout: - git fetch --unshallow || true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/LICENSE0000644000175100001770000000177714714145626013217 0ustar00runnerdockerPermission 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/NEWS.rst0000644000175100001770000001533614714145626013514 0ustar00runnerdockerv3.21.0 ======= Features -------- - Improve performances of :meth:`zipfile.Path.open` for non-reading modes. (1a1928d) - Rely on cached_property to cache values on the instance. - Rely on save_method_args to save method args. v3.20.2 ======= Bugfixes -------- - Make zipp.compat.overlay.zipfile hashable. (#126) v3.20.1 ======= Bugfixes -------- - Replaced SanitizedNames with a more surgical fix for infinite loops, restoring support for names with special characters in the archive. (python/cpython#123270) v3.20.0 ======= Features -------- - Made the zipfile compatibility overlay available as zipp.compat.overlay. v3.19.3 ======= Bugfixes -------- - Also match directories in Path.glob. (#121) v3.19.2 ======= No significant changes. v3.19.1 ======= Bugfixes -------- - Improved handling of malformed zip files. (#119) v3.19.0 ======= Features -------- - Implement is_symlink. (#117) v3.18.2 ======= No significant changes. v3.18.1 ======= No significant changes. v3.18.0 ======= Features -------- - Bypass ZipFile.namelist in glob for better performance. (#106) - Refactored glob functionality to support a more generalized solution with support for platform-specific path separators. (#108) Bugfixes -------- - Add special accounting for pypy when computing the stack level for text encoding warnings. (#114) v3.17.0 ======= Features -------- - Added ``CompleteDirs.inject`` classmethod to make available for use elsewhere. Bugfixes -------- - Avoid matching path separators for '?' in glob. v3.16.2 ======= Bugfixes -------- - In ``Path.match``, Windows path separators are no longer honored. The fact that they were was incidental and never supported. (#92) - Fixed name/suffix/suffixes/stem operations when no filename is present and the Path is not at the root of the zipfile. (#96) - Reworked glob utilizing the namelist directly. (#101) v3.16.1 ======= Bugfixes -------- - Replaced the ``fnmatch.translate`` with a fresh glob-to-regex translator for more correct matching behavior. (#98) v3.16.0 ======= Features -------- - Require Python 3.8 or later. v3.15.0 ======= * gh-102209: ``test_implied_dirs_performance`` now tests measures the time complexity experimentally. v3.14.0 ======= * Minor cleanup in tests, including #93. v3.13.0 ======= * In tests, add a fallback when ``func_timeout`` isn't available. v3.12.1 ======= * gh-101566: In ``CompleteDirs``, override ``ZipFile.getinfo`` to supply a ``ZipInfo`` for implied dirs. v3.12.0 ======= * gh-101144: Honor ``encoding`` as positional parameter to ``Path.open()`` and ``Path.read_text()``. v3.11.0 ======= * #85: Added support for new methods on ``Path``: - ``match`` - ``glob`` and ``rglob`` - ``relative_to`` - ``is_symlink`` v3.10.0 ======= * ``zipp`` is now a package. v3.9.1 ====== * Removed 'print' expression in test_pickle. * bpo-43651: Apply ``io.text_encoding`` on Python 3.10 and later. v3.9.0 ====== * #81: ``Path`` objects are now pickleable if they've been constructed from pickleable objects. Any restored objects will re-construct the zip file with the original arguments. v3.8.1 ====== Refreshed packaging. Enrolled with Tidelift. v3.8.0 ====== Removed compatibility code. v3.7.0 ====== Require Python 3.7 or later. v3.6.0 ====== #78: Only ``Path`` is exposed in the public API. v3.5.1 ====== #77: Remove news file intended only for CPython. v3.5.0 ====== #74 and bpo-44095: Added ``.suffix``, ``.suffixes``, and ``.stem`` properties. v3.4.2 ====== Refresh package metadata. v3.4.1 ====== Refresh packaging. v3.4.0 ====== #68 and bpo-42090: ``Path.joinpath`` now takes arbitrary positional arguments and no longer accepts ``add`` as a keyword argument. v3.3.2 ====== Updated project metadata including badges. v3.3.1 ====== bpo-42043: Add tests capturing subclassing requirements. v3.3.0 ====== #9: ``Path`` objects now expose a ``.filename`` attribute and rely on that to resolve ``.name`` and ``.parent`` when the ``Path`` object is at the root of the zipfile. v3.2.0 ====== #57 and bpo-40564: Mutate the passed ZipFile object type instead of making a copy. Prevents issues when both the local copy and the caller's copy attempt to close the same file handle. #56 and bpo-41035: ``Path._next`` now honors subclasses. #55: ``Path.is_file()`` now returns False for non-existent names. v3.1.0 ====== #47: ``.open`` now raises ``FileNotFoundError`` and ``IsADirectoryError`` when appropriate. v3.0.0 ====== #44: Merge with v1.2.0. 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. v2.2.1 ====== #43: Merge with v1.1.1. v1.1.1 ====== #43: Restored performance of implicit dir computation. v2.2.0 ====== #36: Rebuild package with minimum Python version declared both in package metadata and in the python tag. v2.1.0 ====== #32: Merge with v1.1.0. 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). v2.0.1 ====== #30: Corrected version inference (from jaraco/skeleton#12). v2.0.0 ====== Require Python 3.6 or later. 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 `_. 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. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6542168 zipp-3.21.0/PKG-INFO0000644000175100001770000000710314714145655013276 0ustar00runnerdockerMetadata-Version: 2.1 Name: zipp Version: 3.21.0 Summary: Backport of pathlib-compatible object wrapper for zip files Author-email: "Jason R. Coombs" Project-URL: Source, https://github.com/jaraco/zipp Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Requires-Python: >=3.9 Description-Content-Type: text/x-rst License-File: LICENSE Provides-Extra: test Requires-Dist: pytest!=8.1.*,>=6; extra == "test" Requires-Dist: jaraco.itertools; extra == "test" Requires-Dist: jaraco.functools; extra == "test" Requires-Dist: more_itertools; extra == "test" Requires-Dist: big-O; extra == "test" Requires-Dist: pytest-ignore-flaky; extra == "test" Requires-Dist: jaraco.test; extra == "test" Requires-Dist: importlib_resources; python_version < "3.9" and extra == "test" Provides-Extra: doc Requires-Dist: sphinx>=3.5; extra == "doc" Requires-Dist: jaraco.packaging>=9.3; extra == "doc" Requires-Dist: rst.linker>=1.9; extra == "doc" Requires-Dist: furo; extra == "doc" Requires-Dist: sphinx-lint; extra == "doc" Requires-Dist: jaraco.tidelift>=1.4; extra == "doc" Provides-Extra: check Requires-Dist: pytest-checkdocs>=2.4; extra == "check" Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check" Provides-Extra: cover Requires-Dist: pytest-cov; extra == "cover" Provides-Extra: enabler Requires-Dist: pytest-enabler>=2.2; extra == "enabler" Provides-Extra: type Requires-Dist: pytest-mypy; extra == "type" .. 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://github.com/jaraco/zipp/actions/workflows/main.yml/badge.svg :target: https://github.com/jaraco/zipp/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://readthedocs.org/projects/zipp/badge/?version=latest .. :target: https://zipp.readthedocs.io/en/latest/?badge=latest .. image:: https://img.shields.io/badge/skeleton-2024-informational :target: https://blog.jaraco.com/skeleton .. image:: https://tidelift.com/badges/package/pypi/zipp :target: https://tidelift.com/subscription/pkg/pypi-zipp?utm_source=pypi-zipp&utm_medium=readme A pathlib-compatible Zipfile object wrapper. Official backport of the standard library `Path object `_. 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 * - zipp - stdlib * - 3.18 - 3.13 * - 3.16 - 3.12 * - 3.5 - 3.11 * - 3.2 - 3.10 * - 3.3 ?? - 3.9 * - 1.0 - 3.8 Usage ===== Use ``zipp.Path`` in place of ``zipfile.Path`` on any Python. 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 `_. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/README.rst0000644000175100001770000000372014714145626013667 0ustar00runnerdocker.. 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://github.com/jaraco/zipp/actions/workflows/main.yml/badge.svg :target: https://github.com/jaraco/zipp/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://readthedocs.org/projects/zipp/badge/?version=latest .. :target: https://zipp.readthedocs.io/en/latest/?badge=latest .. image:: https://img.shields.io/badge/skeleton-2024-informational :target: https://blog.jaraco.com/skeleton .. image:: https://tidelift.com/badges/package/pypi/zipp :target: https://tidelift.com/subscription/pkg/pypi-zipp?utm_source=pypi-zipp&utm_medium=readme A pathlib-compatible Zipfile object wrapper. Official backport of the standard library `Path object `_. 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 * - zipp - stdlib * - 3.18 - 3.13 * - 3.16 - 3.12 * - 3.5 - 3.11 * - 3.2 - 3.10 * - 3.3 ?? - 3.9 * - 1.0 - 3.8 Usage ===== Use ``zipp.Path`` in place of ``zipfile.Path`` on any Python. 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 `_. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/SECURITY.md0000644000175100001770000000026414714145626013771 0ustar00runnerdocker# Security Contact To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/conftest.py0000644000175100001770000000036414714145626014400 0ustar00runnerdockerimport builtins import sys def pytest_configure(): add_future_flags() def add_future_flags(): # pragma: no cover if sys.version_info > (3, 10): return builtins.EncodingWarning = type('EncodingWarning', (Warning,), {}) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6462166 zipp-3.21.0/docs/0000755000175100001770000000000014714145655013130 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/docs/conf.py0000644000175100001770000000344714714145626014435 0ustar00runnerdockerfrom __future__ import annotations 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\d+)', url='{package_url}/issues/{issue}', ), dict( pattern=r'(?m:^((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n)', with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', ), dict( pattern=r'PEP[- ](?P\d+)', url='https://peps.python.org/pep-{pep_number:0>4}/', ), dict( pattern=r'(bpo-)(?P\d+)', url='http://bugs.python.org/issue{bpo}', ), dict( pattern=r'(gh-)(?P\d+)', url='http://bugs.python.org/issue{python_gh}', ), ], ) } # Be strict about any broken references nitpicky = True nitpick_ignore: list[tuple[str, str]] = [] # 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 # Add support for linking usernames, PyPI projects, Wikipedia pages github_url = 'https://github.com/' extlinks = { 'user': (f'{github_url}%s', '@%s'), 'pypi': ('https://pypi.org/project/%s', '%s'), 'wiki': ('https://wikipedia.org/wiki/%s', '%s'), } extensions += ['sphinx.ext.extlinks'] # local extensions += ['jaraco.tidelift'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/docs/history.rst0000644000175100001770000000011614714145626015357 0ustar00runnerdocker:tocdepth: 2 .. _changes: History ******* .. include:: ../NEWS (links).rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/docs/index.rst0000644000175100001770000000067214714145626014774 0ustar00runnerdockerWelcome to |project| documentation! =================================== .. sidebar-links:: :home: :pypi: .. toctree:: :maxdepth: 1 history .. tidelift-referral-banner:: .. automodule:: zipp :members: :undoc-members: :show-inheritance: .. automodule:: zipp.glob :members: :undoc-members: :show-inheritance: Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/mypy.ini0000644000175100001770000000102614714145626013674 0ustar00runnerdocker[mypy] # Is the project well-typed? strict = False # Early opt-in even when strict = False warn_unused_ignores = True warn_redundant_casts = True enable_error_code = ignore-without-code # Support namespace packages per https://github.com/python/mypy/issues/14057 explicit_package_bases = True disable_error_code = # Disable due to many false positives overload-overlap, # jaraco/jaraco.test#7 [mypy-jaraco.test.*] ignore_missing_imports = True # jaraco/jaraco.itertools#20 [mypy-jaraco.itertools] ignore_missing_imports = True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/pyproject.toml0000644000175100001770000000256014714145626015115 0ustar00runnerdocker[build-system] requires = ["setuptools>=61.2", "setuptools_scm[toml]>=3.4.1"] build-backend = "setuptools.build_meta" [project] name = "zipp" authors = [ { name = "Jason R. Coombs", email = "jaraco@jaraco.com" }, ] description = "Backport of pathlib-compatible object wrapper for zip files" readme = "README.rst" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", ] requires-python = ">=3.9" dependencies = [ ] dynamic = ["version"] [project.urls] Source = "https://github.com/jaraco/zipp" [project.optional-dependencies] test = [ # upstream "pytest >= 6, != 8.1.*", # local "jaraco.itertools", "jaraco.functools", "more_itertools", "big-O", "pytest-ignore-flaky", "jaraco.test", "importlib_resources; python_version < '3.9'", ] doc = [ # upstream "sphinx >= 3.5", "jaraco.packaging >= 9.3", "rst.linker >= 1.9", "furo", "sphinx-lint", # tidelift "jaraco.tidelift >= 1.4", # local ] check = [ "pytest-checkdocs >= 2.4", "pytest-ruff >= 0.2.1; sys_platform != 'cygwin'", ] cover = [ "pytest-cov", ] enabler = [ "pytest-enabler >= 2.2", ] type = [ # upstream "pytest-mypy", # local ] [tool.setuptools_scm] [tool.pytest-enabler.mypy] # Disabled due to jaraco/skeleton#143 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/pytest.ini0000644000175100001770000000111014714145626014220 0ustar00runnerdocker[pytest] norecursedirs=dist build .tox .eggs addopts= --doctest-modules --import-mode importlib consider_namespace_packages=true filterwarnings= ## upstream # Ensure ResourceWarnings are emitted default::ResourceWarning # 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 # dateutil/dateutil#1284 ignore:datetime.datetime.utcfromtimestamp:DeprecationWarning:dateutil.tz.tz ## end upstream ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/ruff.toml0000644000175100001770000000117114714145626014035 0ustar00runnerdocker# extend pyproject.toml for requires-python (workaround astral-sh/ruff#10299) extend = "pyproject.toml" [lint] extend-select = [ "C901", "PERF401", "W", ] ignore = [ # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules "W191", "E111", "E114", "E117", "D206", "D300", "Q000", "Q001", "Q002", "Q003", "COM812", "COM819", "ISC001", "ISC002", ] [format] # Enable preview to get hugged parenthesis unwrapping and other nice surprises # See https://github.com/jaraco/skeleton/pull/133#issuecomment-2239538373 preview = true # https://docs.astral.sh/ruff/settings/#format_quote-style quote-style = "preserve" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6542168 zipp-3.21.0/setup.cfg0000644000175100001770000000004614714145655014021 0ustar00runnerdocker[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6502168 zipp-3.21.0/tests/0000755000175100001770000000000014714145655013342 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/__init__.py0000644000175100001770000000000014714145626015437 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/_support.py0000644000175100001770000000033314714145626015564 0ustar00runnerdockerimport importlib import unittest def import_or_skip(name): try: return importlib.import_module(name) except ImportError: # pragma: no cover raise unittest.SkipTest(f'Unable to import {name}') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/_test_params.py0000644000175100001770000000161414714145626016375 0ustar00runnerdockerimport functools import types from more_itertools import always_iterable def parameterize(names, value_groups): """ Decorate a test method to run it as a set of subtests. Modeled after pytest.parametrize. """ def decorator(func): @functools.wraps(func) def wrapped(self): for values in value_groups: resolved = map(Invoked.eval, always_iterable(values)) params = dict(zip(always_iterable(names), resolved)) with self.subTest(**params): func(self, **params) return wrapped return decorator class Invoked(types.SimpleNamespace): """ Wrap a function to be invoked for each usage. """ @classmethod def wrap(cls, func): return cls(func=func) @classmethod def eval(cls, cand): return cand.func() if isinstance(cand, cls) else cand ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6502168 zipp-3.21.0/tests/compat/0000755000175100001770000000000014714145655014625 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/compat/__init__.py0000644000175100001770000000000014714145626016722 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/compat/py310.py0000644000175100001770000000027014714145626016050 0ustar00runnerdockerimport sys if sys.version_info >= (3, 11): from importlib.resources.abc import Traversable else: # pragma: no cover from .py38 import Traversable __all__ = ['Traversable'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/compat/py38.py0000644000175100001770000000044414714145626016002 0ustar00runnerdockerimport sys if (3, 9) <= sys.version_info < (3, 11): # pragma: no cover from importlib.abc import Traversable # type: ignore[attr-defined, unused-ignore] elif sys.version_info < (3, 9): # pragma: no cover from importlib_resources.abc import Traversable __all__ = ['Traversable'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/compat/py39.py0000644000175100001770000000033114714145626015776 0ustar00runnerdockerimport sys from jaraco.test.cpython import from_test_support, try_import os_helper = try_import('os_helper') or from_test_support( 'FakePath', 'temp_dir', ) sys.modules[__name__ + '.os_helper'] = os_helper ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/test_complexity.py0000644000175100001770000000632114714145626017150 0ustar00runnerdockerimport io import itertools import math import re import string import unittest from jaraco.functools import compose from more_itertools import consume from zipp.compat.overlay import zipfile from ._support import import_or_skip big_o = import_or_skip('big_o') pytest = import_or_skip('pytest') class TestComplexity(unittest.TestCase): @pytest.mark.flaky def test_implied_dirs_performance(self): best, others = big_o.big_o( compose(consume, zipfile._path.CompleteDirs._implied_dirs), lambda size: [ '/'.join(string.ascii_lowercase + str(n)) for n in range(size) ], max_n=1000, min_n=1, ) assert best <= big_o.complexities.Linear def make_zip_path(self, depth=1, width=1): """ Construct a Path with width files at every level of depth. """ zf = zipfile.ZipFile(io.BytesIO(), mode='w') pairs = itertools.product(self.make_deep_paths(depth), self.make_names(width)) for path, name in pairs: zf.writestr(f"{path}{name}.txt", b'') zf.filename = "big un.zip" return zipfile.Path(zf) @classmethod def make_names(cls, width, letters=string.ascii_lowercase): """ >>> list(TestComplexity.make_names(1)) ['a'] >>> list(TestComplexity.make_names(2)) ['a', 'b'] >>> list(TestComplexity.make_names(30)) ['aa', 'ab', ..., 'bd'] >>> list(TestComplexity.make_names(17124)) ['aaa', 'aab', ..., 'zip'] """ # determine how many products are needed to produce width n_products = max(1, math.ceil(math.log(width, len(letters)))) inputs = (letters,) * n_products combinations = itertools.product(*inputs) names = map(''.join, combinations) return itertools.islice(names, width) @classmethod def make_deep_paths(cls, depth): return map(cls.make_deep_path, range(depth)) @classmethod def make_deep_path(cls, depth): return ''.join(('d/',) * depth) def test_baseline_regex_complexity(self): best, others = big_o.big_o( lambda path: re.fullmatch(r'[^/]*\\.txt', path), self.make_deep_path, max_n=100, min_n=1, ) assert best <= big_o.complexities.Constant @pytest.mark.flaky def test_glob_depth(self): best, others = big_o.big_o( lambda path: consume(path.glob('*.txt')), self.make_zip_path, max_n=100, min_n=1, ) assert best <= big_o.complexities.Linear @pytest.mark.flaky def test_glob_width(self): best, others = big_o.big_o( lambda path: consume(path.glob('*.txt')), lambda size: self.make_zip_path(width=size), max_n=100, min_n=1, ) assert best <= big_o.complexities.Linear @pytest.mark.flaky def test_glob_width_and_depth(self): best, others = big_o.big_o( lambda path: consume(path.glob('*.txt')), lambda size: self.make_zip_path(depth=size, width=size), max_n=10, min_n=1, ) assert best <= big_o.complexities.Linear ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/test_path.py0000644000175100001770000005325314714145626015715 0ustar00runnerdockerimport contextlib import io import itertools import pathlib import pickle import stat import sys import time import unittest import jaraco.itertools from jaraco.functools import compose from zipp.compat.overlay import zipfile from ._test_params import Invoked, parameterize from .compat.py39.os_helper import FakePath, temp_dir # type: ignore[import-not-found] def _make_link(info: zipfile.ZipInfo): # type: ignore[name-defined] info.external_attr |= stat.S_IFLNK << 16 def build_alpharep_fixture(): """ Create a zip file with this structure: . ├── a.txt ├── n.txt (-> a.txt) ├── b │ ├── c.txt │ ├── d │ │ └── e.txt │ └── f.txt ├── g │ └── h │ └── i.txt └── j ├── k.bin ├── l.baz └── m.bar 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) - a directory with files of different extensions (j/klm) - a symlink (n) pointing to (a) "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.writestr("j/k.bin", b"content of k") zf.writestr("j/l.baz", b"content of l") zf.writestr("j/m.bar", b"content of m") zf.writestr("n.txt", b"a.txt") _make_link(zf.infolist()[-1]) zf.filename = "alpharep.zip" return zf alpharep_generators = [ Invoked.wrap(build_alpharep_fixture), Invoked.wrap(compose(zipfile._path.CompleteDirs.inject, build_alpharep_fixture)), ] pass_alpharep = parameterize(['alpharep'], alpharep_generators) class TestPath(unittest.TestCase): def setUp(self): self.fixtures = contextlib.ExitStack() self.addCleanup(self.fixtures.close) def zipfile_ondisk(self, alpharep): tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir())) buffer = alpharep.fp alpharep.close() path = tmpdir / alpharep.filename with path.open("wb") as strm: strm.write(buffer.getvalue()) return path @pass_alpharep def test_iterdir_and_types(self, alpharep): root = zipfile.Path(alpharep) assert root.is_dir() a, n, b, g, j = 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() @pass_alpharep def test_is_file_missing(self, alpharep): root = zipfile.Path(alpharep) assert not root.joinpath('missing.txt').is_file() @pass_alpharep def test_iterdir_on_file(self, alpharep): root = zipfile.Path(alpharep) a, n, b, g, j = root.iterdir() with self.assertRaises(ValueError): a.iterdir() @pass_alpharep def test_subdir_is_dir(self, alpharep): root = zipfile.Path(alpharep) assert (root / 'b').is_dir() assert (root / 'b/').is_dir() assert (root / 'g').is_dir() assert (root / 'g/').is_dir() @pass_alpharep def test_open(self, alpharep): root = zipfile.Path(alpharep) a, n, b, g, j = root.iterdir() with a.open(encoding="utf-8") as strm: data = strm.read() self.assertEqual(data, "content of a") with a.open('r', "utf-8") as strm: # not a kw, no gh-101144 TypeError data = strm.read() self.assertEqual(data, "content of a") def test_open_encoding_utf16(self): in_memory_file = io.BytesIO() zf = zipfile.ZipFile(in_memory_file, "w") zf.writestr("path/16.txt", "This was utf-16".encode("utf-16")) zf.filename = "test_open_utf16.zip" root = zipfile.Path(zf) (path,) = root.iterdir() u16 = path.joinpath("16.txt") with u16.open('r', "utf-16") as strm: data = strm.read() assert data == "This was utf-16" with u16.open(encoding="utf-16") as strm: data = strm.read() assert data == "This was utf-16" def test_open_encoding_errors(self): in_memory_file = io.BytesIO() zf = zipfile.ZipFile(in_memory_file, "w") zf.writestr("path/bad-utf8.bin", b"invalid utf-8: \xff\xff.") zf.filename = "test_read_text_encoding_errors.zip" root = zipfile.Path(zf) (path,) = root.iterdir() u16 = path.joinpath("bad-utf8.bin") # encoding= as a positional argument for gh-101144. data = u16.read_text("utf-8", errors="ignore") assert data == "invalid utf-8: ." with u16.open("r", "utf-8", errors="surrogateescape") as f: assert f.read() == "invalid utf-8: \udcff\udcff." # encoding= both positional and keyword is an error; gh-101144. with self.assertRaisesRegex(TypeError, "encoding"): data = u16.read_text("utf-8", encoding="utf-8") # both keyword arguments work. with u16.open("r", encoding="utf-8", errors="strict") as f: # error during decoding with wrong codec. with self.assertRaises(UnicodeDecodeError): f.read() @unittest.skipIf( not getattr(sys.flags, 'warn_default_encoding', 0), "Requires warn_default_encoding", ) @pass_alpharep def test_encoding_warnings(self, alpharep): """EncodingWarning must blame the read_text and open calls.""" assert sys.flags.warn_default_encoding root = zipfile.Path(alpharep) with self.assertWarns(EncodingWarning) as wc: # noqa: F821 (astral-sh/ruff#13296) root.joinpath("a.txt").read_text() assert __file__ == wc.filename with self.assertWarns(EncodingWarning) as wc: # noqa: F821 (astral-sh/ruff#13296) root.joinpath("a.txt").open("r").close() assert __file__ == wc.filename def test_open_write(self): """ If the zipfile is open for write, it should be possible to write bytes or text to it. """ zf = zipfile.Path(zipfile.ZipFile(io.BytesIO(), mode='w')) with zf.joinpath('file.bin').open('wb') as strm: strm.write(b'binary contents') with zf.joinpath('file.txt').open('w', encoding="utf-8") as strm: strm.write('text file') @pass_alpharep def test_open_extant_directory(self, alpharep): """ Attempting to open a directory raises IsADirectoryError. """ zf = zipfile.Path(alpharep) with self.assertRaises(IsADirectoryError): zf.joinpath('b').open() @pass_alpharep def test_open_binary_invalid_args(self, alpharep): root = zipfile.Path(alpharep) with self.assertRaises(ValueError): root.joinpath('a.txt').open('rb', encoding='utf-8') with self.assertRaises(ValueError): root.joinpath('a.txt').open('rb', 'utf-8') @pass_alpharep def test_open_missing_directory(self, alpharep): """ Attempting to open a missing directory raises FileNotFoundError. """ zf = zipfile.Path(alpharep) with self.assertRaises(FileNotFoundError): zf.joinpath('z').open() @pass_alpharep def test_read(self, alpharep): root = zipfile.Path(alpharep) a, n, b, g, j = root.iterdir() assert a.read_text(encoding="utf-8") == "content of a" # Also check positional encoding arg (gh-101144). assert a.read_text("utf-8") == "content of a" assert a.read_bytes() == b"content of a" @pass_alpharep def test_joinpath(self, alpharep): root = zipfile.Path(alpharep) a = root.joinpath("a.txt") assert a.is_file() e = root.joinpath("b").joinpath("d").joinpath("e.txt") assert e.read_text(encoding="utf-8") == "content of e" @pass_alpharep def test_joinpath_multiple(self, alpharep): root = zipfile.Path(alpharep) e = root.joinpath("b", "d", "e.txt") assert e.read_text(encoding="utf-8") == "content of e" @pass_alpharep def test_traverse_truediv(self, alpharep): root = zipfile.Path(alpharep) a = root / "a.txt" assert a.is_file() e = root / "b" / "d" / "e.txt" assert e.read_text(encoding="utf-8") == "content of e" @pass_alpharep def test_pathlike_construction(self, alpharep): """ zipfile.Path should be constructable from a path-like object """ zipfile_ondisk = self.zipfile_ondisk(alpharep) pathlike = FakePath(str(zipfile_ondisk)) zipfile.Path(pathlike) @pass_alpharep def test_traverse_pathlike(self, alpharep): root = zipfile.Path(alpharep) root / FakePath("a") @pass_alpharep def test_parent(self, alpharep): root = zipfile.Path(alpharep) assert (root / 'a').parent.at == '' assert (root / 'a' / 'b').parent.at == 'a/' @pass_alpharep def test_dir_parent(self, alpharep): root = zipfile.Path(alpharep) assert (root / 'b').parent.at == '' assert (root / 'b/').parent.at == '' @pass_alpharep def test_missing_dir_parent(self, alpharep): root = zipfile.Path(alpharep) assert (root / 'missing dir/').parent.at == '' @pass_alpharep def test_mutability(self, alpharep): """ If the underlying zipfile is changed, the Path object should reflect that change. """ root = zipfile.Path(alpharep) a, n, b, g, j = root.iterdir() alpharep.writestr('foo.txt', 'foo') alpharep.writestr('bar/baz.txt', 'baz') assert any(child.name == 'foo.txt' for child in root.iterdir()) assert (root / 'foo.txt').read_text(encoding="utf-8") == 'foo' (baz,) = (root / 'bar').iterdir() assert baz.read_text(encoding="utf-8") == '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 = zipfile.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 @pass_alpharep def test_read_does_not_close(self, alpharep): alpharep = self.zipfile_ondisk(alpharep) with zipfile.ZipFile(alpharep) as file: for rep in range(2): zipfile.Path(file, 'a.txt').read_text(encoding="utf-8") @pass_alpharep def test_subclass(self, alpharep): class Subclass(zipfile.Path): pass root = Subclass(alpharep) assert isinstance(root / 'b', Subclass) @pass_alpharep def test_filename(self, alpharep): root = zipfile.Path(alpharep) assert root.filename == pathlib.Path('alpharep.zip') @pass_alpharep def test_root_name(self, alpharep): """ The name of the root should be the name of the zipfile """ root = zipfile.Path(alpharep) assert root.name == 'alpharep.zip' == root.filename.name @pass_alpharep def test_suffix(self, alpharep): """ The suffix of the root should be the suffix of the zipfile. The suffix of each nested file is the final component's last suffix, if any. Includes the leading period, just like pathlib.Path. """ root = zipfile.Path(alpharep) assert root.suffix == '.zip' == root.filename.suffix b = root / "b.txt" assert b.suffix == ".txt" c = root / "c" / "filename.tar.gz" assert c.suffix == ".gz" d = root / "d" assert d.suffix == "" @pass_alpharep def test_suffixes(self, alpharep): """ The suffix of the root should be the suffix of the zipfile. The suffix of each nested file is the final component's last suffix, if any. Includes the leading period, just like pathlib.Path. """ root = zipfile.Path(alpharep) assert root.suffixes == ['.zip'] == root.filename.suffixes b = root / 'b.txt' assert b.suffixes == ['.txt'] c = root / 'c' / 'filename.tar.gz' assert c.suffixes == ['.tar', '.gz'] d = root / 'd' assert d.suffixes == [] e = root / '.hgrc' assert e.suffixes == [] @pass_alpharep def test_suffix_no_filename(self, alpharep): alpharep.filename = None root = zipfile.Path(alpharep) assert root.joinpath('example').suffix == "" assert root.joinpath('example').suffixes == [] @pass_alpharep def test_stem(self, alpharep): """ The final path component, without its suffix """ root = zipfile.Path(alpharep) assert root.stem == 'alpharep' == root.filename.stem b = root / "b.txt" assert b.stem == "b" c = root / "c" / "filename.tar.gz" assert c.stem == "filename.tar" d = root / "d" assert d.stem == "d" assert (root / ".gitignore").stem == ".gitignore" @pass_alpharep def test_root_parent(self, alpharep): root = zipfile.Path(alpharep) assert root.parent == pathlib.Path('.') root.root.filename = 'foo/bar.zip' assert root.parent == pathlib.Path('foo') @pass_alpharep def test_root_unnamed(self, alpharep): """ It is an error to attempt to get the name or parent of an unnamed zipfile. """ alpharep.filename = None root = zipfile.Path(alpharep) with self.assertRaises(TypeError): root.name with self.assertRaises(TypeError): root.parent # .name and .parent should still work on subs sub = root / "b" assert sub.name == "b" assert sub.parent @pass_alpharep def test_match_and_glob(self, alpharep): root = zipfile.Path(alpharep) assert not root.match("*.txt") assert list(root.glob("b/c.*")) == [zipfile.Path(alpharep, "b/c.txt")] assert list(root.glob("b/*.txt")) == [ zipfile.Path(alpharep, "b/c.txt"), zipfile.Path(alpharep, "b/f.txt"), ] @pass_alpharep def test_glob_recursive(self, alpharep): root = zipfile.Path(alpharep) files = root.glob("**/*.txt") assert all(each.match("*.txt") for each in files) assert list(root.glob("**/*.txt")) == list(root.rglob("*.txt")) @pass_alpharep def test_glob_dirs(self, alpharep): root = zipfile.Path(alpharep) assert list(root.glob('b')) == [zipfile.Path(alpharep, "b/")] assert list(root.glob('b*')) == [zipfile.Path(alpharep, "b/")] @pass_alpharep def test_glob_subdir(self, alpharep): root = zipfile.Path(alpharep) assert list(root.glob('g/h')) == [zipfile.Path(alpharep, "g/h/")] assert list(root.glob('g*/h*')) == [zipfile.Path(alpharep, "g/h/")] @pass_alpharep def test_glob_subdirs(self, alpharep): root = zipfile.Path(alpharep) assert list(root.glob("*/i.txt")) == [] assert list(root.rglob("*/i.txt")) == [zipfile.Path(alpharep, "g/h/i.txt")] @pass_alpharep def test_glob_does_not_overmatch_dot(self, alpharep): root = zipfile.Path(alpharep) assert list(root.glob("*.xt")) == [] @pass_alpharep def test_glob_single_char(self, alpharep): root = zipfile.Path(alpharep) assert list(root.glob("a?txt")) == [zipfile.Path(alpharep, "a.txt")] assert list(root.glob("a[.]txt")) == [zipfile.Path(alpharep, "a.txt")] assert list(root.glob("a[?]txt")) == [] @pass_alpharep def test_glob_chars(self, alpharep): root = zipfile.Path(alpharep) assert list(root.glob("j/?.b[ai][nz]")) == [ zipfile.Path(alpharep, "j/k.bin"), zipfile.Path(alpharep, "j/l.baz"), ] def test_glob_empty(self): root = zipfile.Path(zipfile.ZipFile(io.BytesIO(), 'w')) with self.assertRaises(ValueError): root.glob('') @pass_alpharep def test_eq_hash(self, alpharep): root = zipfile.Path(alpharep) assert root == zipfile.Path(alpharep) assert root != (root / "a.txt") assert (root / "a.txt") == (root / "a.txt") root = zipfile.Path(alpharep) assert root in {root} @pass_alpharep def test_is_symlink(self, alpharep): root = zipfile.Path(alpharep) assert not root.joinpath('a.txt').is_symlink() assert root.joinpath('n.txt').is_symlink() @pass_alpharep def test_relative_to(self, alpharep): root = zipfile.Path(alpharep) relative = root.joinpath("b", "c.txt").relative_to(root / "b") assert str(relative) == "c.txt" relative = root.joinpath("b", "d", "e.txt").relative_to(root / "b") assert str(relative) == "d/e.txt" @pass_alpharep def test_inheritance(self, alpharep): cls = type('PathChild', (zipfile.Path,), {}) file = cls(alpharep).joinpath('some dir').parent assert isinstance(file, cls) @parameterize( ['alpharep', 'path_type', 'subpath'], itertools.product( alpharep_generators, [str, FakePath], ['', 'b/'], ), ) def test_pickle(self, alpharep, path_type, subpath): zipfile_ondisk = path_type(str(self.zipfile_ondisk(alpharep))) saved_1 = pickle.dumps(zipfile.Path(zipfile_ondisk, at=subpath)) restored_1 = pickle.loads(saved_1) first, *rest = restored_1.iterdir() assert first.read_text(encoding='utf-8').startswith('content of ') @pass_alpharep def test_extract_orig_with_implied_dirs(self, alpharep): """ A zip file wrapped in a Path should extract even with implied dirs. """ source_path = self.zipfile_ondisk(alpharep) zf = zipfile.ZipFile(source_path) # wrap the zipfile for its side effect zipfile.Path(zf) zf.extractall(source_path.parent) @pass_alpharep def test_getinfo_missing(self, alpharep): """ Validate behavior of getinfo on original zipfile after wrapping. """ zipfile.Path(alpharep) with self.assertRaises(KeyError): alpharep.getinfo('does-not-exist') def test_malformed_paths(self): """ Path should handle malformed paths gracefully. Paths with leading slashes are not visible. Paths with dots are treated like regular files. """ data = io.BytesIO() zf = zipfile.ZipFile(data, "w") zf.writestr("/one-slash.txt", b"content") zf.writestr("//two-slash.txt", b"content") zf.writestr("../parent.txt", b"content") zf.filename = '' root = zipfile.Path(zf) assert list(map(str, root.iterdir())) == ['../'] assert root.joinpath('..').joinpath('parent.txt').read_bytes() == b'content' def test_unsupported_names(self): """ Path segments with special characters are readable. On some platforms or file systems, characters like ``:`` and ``?`` are not allowed, but they are valid in the zip file. """ data = io.BytesIO() zf = zipfile.ZipFile(data, "w") zf.writestr("path?", b"content") zf.writestr("V: NMS.flac", b"fLaC...") zf.filename = '' root = zipfile.Path(zf) contents = root.iterdir() assert next(contents).name == 'path?' assert next(contents).name == 'V: NMS.flac' assert root.joinpath('V: NMS.flac').read_bytes() == b"fLaC..." def test_backslash_not_separator(self): """ In a zip file, backslashes are not separators. """ data = io.BytesIO() zf = zipfile.ZipFile(data, "w") zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content") zf.filename = '' root = zipfile.Path(zf) (first,) = root.iterdir() assert not first.is_dir() assert first.name == 'foo\\bar' @pass_alpharep def test_interface(self, alpharep): from .compat.py310 import Traversable zf = zipfile.Path(alpharep) assert isinstance(zf, Traversable) class DirtyZipInfo(zipfile.ZipInfo): """ Bypass name sanitization. """ def __init__(self, filename, *args, **kwargs): super().__init__(filename, *args, **kwargs) self.filename = filename @classmethod def for_name(cls, name, archive): """ Construct the same way that ZipFile.writestr does. TODO: extract this functionality and re-use """ self = cls(filename=name, date_time=time.localtime(time.time())[:6]) self.compress_type = archive.compression self.compress_level = archive.compresslevel if self.filename.endswith('/'): # pragma: no cover self.external_attr = 0o40775 << 16 # drwxrwxr-x self.external_attr |= 0x10 # MS-DOS directory flag else: self.external_attr = 0o600 << 16 # ?rw------- return self ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tests/write-alpharep.py0000644000175100001770000000015614714145626016640 0ustar00runnerdockerfrom . import test_path __name__ == '__main__' and test_path.build_alpharep_fixture().extractall('alpharep') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/towncrier.toml0000644000175100001770000000005414714145626015106 0ustar00runnerdocker[tool.towncrier] title_format = "{version}" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/tox.ini0000644000175100001770000000245414714145626013516 0ustar00runnerdocker[testenv] description = perform primary checks (tests, style, types, coverage) deps = setenv = PYTHONWARNDEFAULTENCODING = 1 commands = pytest {posargs} usedevelop = True extras = test check cover enabler type [testenv:diffcov] description = run tests and check that diff from main is covered deps = {[testenv]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:docs] description = build the documentation extras = doc test changedir = docs commands = python -m sphinx -W --keep-going . {toxinidir}/build/html python -m sphinxlint [testenv:finalize] description = assemble changelog and tag a release skip_install = True deps = towncrier jaraco.develop >= 7.23 pass_env = * commands = python -m jaraco.develop.finalize [testenv:release] description = publish the package to PyPI and GitHub skip_install = True deps = build twine>=3 jaraco.develop>=7.1 pass_env = 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 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6502168 zipp-3.21.0/zipp/0000755000175100001770000000000014714145655013162 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/zipp/__init__.py0000644000175100001770000002704114714145626015275 0ustar00runnerdocker""" A Path-like interface for zipfiles. This codebase is shared between zipfile.Path in the stdlib and zipp in PyPI. See https://github.com/python/importlib_metadata/wiki/Development-Methodology for more detail. """ import functools import io import itertools import pathlib import posixpath import re import stat import sys import zipfile from .compat.py310 import text_encoding from .glob import Translator from ._functools import save_method_args __all__ = ['Path'] 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('')) [] Multiple separators are treated like a single. >>> list(_ancestry('//b//d///f//')) ['//b//d///f', '//b//d', '//b'] """ path = path.rstrip(posixpath.sep) while path.rstrip(posixpath.sep): yield path path, tail = posixpath.split(path) _dedupe = dict.fromkeys """Deduplicate an iterable in original order""" def _difference(minuend, subtrahend): """ Return items in minuend not in subtrahend, retaining order with O(1) lookup. """ return itertools.filterfalse(set(subtrahend).__contains__, minuend) class InitializedState: """ Mix-in to save the initialization state for pickling. """ @save_method_args def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def __getstate__(self): return self._saved___init__.args, self._saved___init__.kwargs def __setstate__(self, state): args, kwargs = state super().__init__(*args, **kwargs) class CompleteDirs(InitializedState, zipfile.ZipFile): """ A ZipFile subclass that ensures that implied directories are always included in the namelist. >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt'])) ['foo/', 'foo/bar/'] >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt', 'foo/bar/'])) ['foo/'] """ @staticmethod def _implied_dirs(names): parents = itertools.chain.from_iterable(map(_parents, names)) as_dirs = (p + posixpath.sep for p in parents) return _dedupe(_difference(as_dirs, names)) def namelist(self): names = super().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 def getinfo(self, name): """ Supplement getinfo for implied dirs. """ try: return super().getinfo(name) except KeyError: if not name.endswith('/') or name not in self._name_set(): raise return zipfile.ZipInfo(filename=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(source) # Only allow for FastLookup when supplied zipfile is read-only if 'r' not in source.mode: cls = CompleteDirs source.__class__ = cls return source @classmethod def inject(cls, zf: zipfile.ZipFile) -> zipfile.ZipFile: """ Given a writable zip file zf, inject directory entries for any directories implied by the presence of children. """ for name in cls._implied_dirs(zf.namelist()): zf.writestr(name, b"") return zf class FastLookup(CompleteDirs): """ ZipFile subclass to ensure implicit dirs exist and are resolved rapidly. """ def namelist(self): return self._namelist @functools.cached_property def _namelist(self): return super().namelist() def _name_set(self): return self._name_set_prop @functools.cached_property def _name_set_prop(self): return super()._name_set() def _extract_text_encoding(encoding=None, *args, **kwargs): # compute stack level so that the caller of the caller sees any warning. is_pypy = sys.implementation.name == 'pypy' stack_level = 3 + is_pypy return text_encoding(encoding, stack_level), args, kwargs class Path: """ A :class:`importlib.resources.abc.Traversable` interface for zip files. Implements many of the features users enjoy from :class:`pathlib.Path`. 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 = 'mem/abcde.zip' Path accepts the zipfile object itself or a filename >>> path = Path(zf) From there, several path operations are available. Directory iteration (including the zip file itself): >>> a, b = path.iterdir() >>> a Path('mem/abcde.zip', 'a.txt') >>> b Path('mem/abcde.zip', 'b/') name property: >>> b.name 'b' join with divide operator: >>> c = b / 'c.txt' >>> c Path('mem/abcde.zip', 'b/c.txt') >>> c.name 'c.txt' Read text: >>> c.read_text(encoding='utf-8') 'content of c' existence: >>> c.exists() True >>> (b / 'missing.txt').exists() False Coercion to string: >>> import os >>> str(c).replace(os.sep, posixpath.sep) 'mem/abcde.zip/b/c.txt' At the root, ``name``, ``filename``, and ``parent`` resolve to the zipfile. >>> str(path) 'mem/abcde.zip/' >>> path.name 'abcde.zip' >>> path.filename == pathlib.Path('mem/abcde.zip') True >>> str(path.parent) 'mem' If the zipfile has no filename, such attributes are not valid and accessing them will raise an Exception. >>> zf.filename = None >>> path.name Traceback (most recent call last): ... TypeError: ... >>> path.filename Traceback (most recent call last): ... TypeError: ... >>> path.parent Traceback (most recent call last): ... TypeError: ... # workaround python/cpython#106763 >>> pass """ __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})" def __init__(self, root, at=""): """ Construct a Path from a ZipFile or filename. Note: When the source is an existing ZipFile object, its type (__class__) will be mutated to a specialized type. If the caller wishes to retain the original type, the caller should either create a separate ZipFile object or pass a filename. """ self.root = FastLookup.make(root) self.at = at def __eq__(self, other): """ >>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo' False """ if self.__class__ is not other.__class__: return NotImplemented return (self.root, self.at) == (other.root, other.at) def __hash__(self): return hash((self.root, self.at)) def open(self, mode='r', *args, pwd=None, **kwargs): """ Open this entry as text or binary following the semantics of ``pathlib.Path.open()`` by passing arguments through to io.TextIOWrapper(). """ if self.is_dir(): raise IsADirectoryError(self) zip_mode = mode[0] if zip_mode == 'r' and not self.exists(): raise FileNotFoundError(self) 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 # Text mode: encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) return io.TextIOWrapper(stream, encoding, *args, **kwargs) def _base(self): return pathlib.PurePosixPath(self.at or self.root.filename) @property def name(self): return self._base().name @property def suffix(self): return self._base().suffix @property def suffixes(self): return self._base().suffixes @property def stem(self): return self._base().stem @property def filename(self): return pathlib.Path(self.root.filename).joinpath(self.at) def read_text(self, *args, **kwargs): encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) with self.open('r', encoding, *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 self.__class__(self.root, at) def is_dir(self): return not self.at or self.at.endswith("/") def is_file(self): return self.exists() and 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 match(self, path_pattern): return pathlib.PurePosixPath(self.at).match(path_pattern) def is_symlink(self): """ Return whether this path is a symlink. """ info = self.root.getinfo(self.at) mode = info.external_attr >> 16 return stat.S_ISLNK(mode) def glob(self, pattern): if not pattern: raise ValueError(f"Unacceptable pattern: {pattern!r}") prefix = re.escape(self.at) tr = Translator(seps='/') matches = re.compile(prefix + tr.translate(pattern)).fullmatch return map(self._next, filter(matches, self.root.namelist())) def rglob(self, pattern): return self.glob(f'**/{pattern}') def relative_to(self, other, *extra): return posixpath.relpath(str(self), str(other.joinpath(*extra))) def __str__(self): return posixpath.join(self.root.filename, self.at) def __repr__(self): return self.__repr.format(self=self) def joinpath(self, *other): next = posixpath.join(self.at, *other) return self._next(self.root.resolve_dir(next)) __truediv__ = joinpath @property def parent(self): if not self.at: return self.filename.parent parent_at = posixpath.dirname(self.at.rstrip('/')) if parent_at: parent_at += '/' return self._next(parent_at) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/zipp/_functools.py0000644000175100001770000000105714714145626015710 0ustar00runnerdockerimport collections import functools # from jaraco.functools 4.0.2 def save_method_args(method): """ Wrap a method such that when it is called, the args and kwargs are saved on the method. """ args_and_kwargs = collections.namedtuple('args_and_kwargs', 'args kwargs') @functools.wraps(method) def wrapper(self, /, *args, **kwargs): attr_name = '_saved_' + method.__name__ attr = args_and_kwargs(args, kwargs) setattr(self, attr_name, attr) return method(self, *args, **kwargs) return wrapper ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6502168 zipp-3.21.0/zipp/compat/0000755000175100001770000000000014714145655014445 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/zipp/compat/__init__.py0000644000175100001770000000000014714145626016542 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/zipp/compat/overlay.py0000644000175100001770000000141714714145626016501 0ustar00runnerdocker""" Expose zipp.Path as .zipfile.Path. Includes everything else in ``zipfile`` to match future usage. Just use: >>> from zipp.compat.overlay import zipfile in place of ``import zipfile``. Relative imports are supported too. >>> from zipp.compat.overlay.zipfile import ZipInfo The ``zipfile`` object added to ``sys.modules`` needs to be hashable (#126). >>> _ = hash(sys.modules['zipp.compat.overlay.zipfile']) """ import importlib import sys import types import zipp class HashableNamespace(types.SimpleNamespace): def __hash__(self): return hash(tuple(vars(self))) zipfile = HashableNamespace(**vars(importlib.import_module('zipfile'))) zipfile.Path = zipp.Path zipfile._path = zipp sys.modules[__name__ + '.zipfile'] = zipfile # type: ignore[assignment] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/zipp/compat/py310.py0000644000175100001770000000040014714145626015663 0ustar00runnerdockerimport io import sys def _text_encoding(encoding, stacklevel=2, /): # pragma: no cover return encoding text_encoding = ( io.text_encoding # type: ignore[unused-ignore, attr-defined] if sys.version_info > (3, 10) else _text_encoding ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251094.0 zipp-3.21.0/zipp/glob.py0000644000175100001770000000636214714145626014464 0ustar00runnerdockerimport os import re _default_seps = os.sep + str(os.altsep) * bool(os.altsep) class Translator: """ >>> Translator('xyz') Traceback (most recent call last): ... AssertionError: Invalid separators >>> Translator('') Traceback (most recent call last): ... AssertionError: Invalid separators """ seps: str def __init__(self, seps: str = _default_seps): assert seps and set(seps) <= set(_default_seps), "Invalid separators" self.seps = seps def translate(self, pattern): """ Given a glob pattern, produce a regex that matches it. """ return self.extend(self.match_dirs(self.translate_core(pattern))) def extend(self, pattern): r""" Extend regex for pattern-wide concerns. Apply '(?s:)' to create a non-matching group that matches newlines (valid on Unix). Append '\Z' to imply fullmatch even when match is used. """ return rf'(?s:{pattern})\Z' def match_dirs(self, pattern): """ Ensure that zipfile.Path directory names are matched. zipfile.Path directory names always end in a slash. """ return rf'{pattern}[/]?' def translate_core(self, pattern): r""" Given a glob pattern, produce a regex that matches it. >>> t = Translator() >>> t.translate_core('*.txt').replace('\\\\', '') '[^/]*\\.txt' >>> t.translate_core('a?txt') 'a[^/]txt' >>> t.translate_core('**/*').replace('\\\\', '') '.*/[^/][^/]*' """ self.restrict_rglob(pattern) return ''.join(map(self.replace, separate(self.star_not_empty(pattern)))) def replace(self, match): """ Perform the replacements for a match from :func:`separate`. """ return match.group('set') or ( re.escape(match.group(0)) .replace('\\*\\*', r'.*') .replace('\\*', rf'[^{re.escape(self.seps)}]*') .replace('\\?', r'[^/]') ) def restrict_rglob(self, pattern): """ Raise ValueError if ** appears in anything but a full path segment. >>> Translator().translate('**foo') Traceback (most recent call last): ... ValueError: ** must appear alone in a path segment """ seps_pattern = rf'[{re.escape(self.seps)}]+' segments = re.split(seps_pattern, pattern) if any('**' in segment and segment != '**' for segment in segments): raise ValueError("** must appear alone in a path segment") def star_not_empty(self, pattern): """ Ensure that * will not match an empty segment. """ def handle_segment(match): segment = match.group(0) return '?*' if segment == '*' else segment not_seps_pattern = rf'[^{re.escape(self.seps)}]+' return re.sub(not_seps_pattern, handle_segment, pattern) def separate(pattern): """ Separate out character sets to avoid translating their contents. >>> [m.group(0) for m in separate('*.txt')] ['*.txt'] >>> [m.group(0) for m in separate('a[?]txt')] ['a', '[?]', 'txt'] """ return re.finditer(r'([^\[]+)|(?P[\[].*?[\]])|([\[][^\]]*$)', pattern) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1731251116.6502168 zipp-3.21.0/zipp.egg-info/0000755000175100001770000000000014714145655014654 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251116.0 zipp-3.21.0/zipp.egg-info/PKG-INFO0000644000175100001770000000710314714145654015751 0ustar00runnerdockerMetadata-Version: 2.1 Name: zipp Version: 3.21.0 Summary: Backport of pathlib-compatible object wrapper for zip files Author-email: "Jason R. Coombs" Project-URL: Source, https://github.com/jaraco/zipp Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Requires-Python: >=3.9 Description-Content-Type: text/x-rst License-File: LICENSE Provides-Extra: test Requires-Dist: pytest!=8.1.*,>=6; extra == "test" Requires-Dist: jaraco.itertools; extra == "test" Requires-Dist: jaraco.functools; extra == "test" Requires-Dist: more_itertools; extra == "test" Requires-Dist: big-O; extra == "test" Requires-Dist: pytest-ignore-flaky; extra == "test" Requires-Dist: jaraco.test; extra == "test" Requires-Dist: importlib_resources; python_version < "3.9" and extra == "test" Provides-Extra: doc Requires-Dist: sphinx>=3.5; extra == "doc" Requires-Dist: jaraco.packaging>=9.3; extra == "doc" Requires-Dist: rst.linker>=1.9; extra == "doc" Requires-Dist: furo; extra == "doc" Requires-Dist: sphinx-lint; extra == "doc" Requires-Dist: jaraco.tidelift>=1.4; extra == "doc" Provides-Extra: check Requires-Dist: pytest-checkdocs>=2.4; extra == "check" Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check" Provides-Extra: cover Requires-Dist: pytest-cov; extra == "cover" Provides-Extra: enabler Requires-Dist: pytest-enabler>=2.2; extra == "enabler" Provides-Extra: type Requires-Dist: pytest-mypy; extra == "type" .. 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://github.com/jaraco/zipp/actions/workflows/main.yml/badge.svg :target: https://github.com/jaraco/zipp/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://readthedocs.org/projects/zipp/badge/?version=latest .. :target: https://zipp.readthedocs.io/en/latest/?badge=latest .. image:: https://img.shields.io/badge/skeleton-2024-informational :target: https://blog.jaraco.com/skeleton .. image:: https://tidelift.com/badges/package/pypi/zipp :target: https://tidelift.com/subscription/pkg/pypi-zipp?utm_source=pypi-zipp&utm_medium=readme A pathlib-compatible Zipfile object wrapper. Official backport of the standard library `Path object `_. 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 * - zipp - stdlib * - 3.18 - 3.13 * - 3.16 - 3.12 * - 3.5 - 3.11 * - 3.2 - 3.10 * - 3.3 ?? - 3.9 * - 1.0 - 3.8 Usage ===== Use ``zipp.Path`` in place of ``zipfile.Path`` on any Python. 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 `_. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251116.0 zipp-3.21.0/zipp.egg-info/SOURCES.txt0000644000175100001770000000140514714145654016537 0ustar00runnerdocker.coveragerc .editorconfig .pre-commit-config.yaml .readthedocs.yaml LICENSE NEWS.rst README.rst SECURITY.md conftest.py mypy.ini pyproject.toml pytest.ini ruff.toml towncrier.toml tox.ini .github/FUNDING.yml .github/dependabot.yml .github/workflows/main.yml docs/conf.py docs/history.rst docs/index.rst tests/__init__.py tests/_support.py tests/_test_params.py tests/test_complexity.py tests/test_path.py tests/write-alpharep.py tests/compat/__init__.py tests/compat/py310.py tests/compat/py38.py tests/compat/py39.py zipp/__init__.py zipp/_functools.py zipp/glob.py zipp.egg-info/PKG-INFO zipp.egg-info/SOURCES.txt zipp.egg-info/dependency_links.txt zipp.egg-info/requires.txt zipp.egg-info/top_level.txt zipp/compat/__init__.py zipp/compat/overlay.py zipp/compat/py310.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251116.0 zipp-3.21.0/zipp.egg-info/dependency_links.txt0000644000175100001770000000000114714145654020721 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251116.0 zipp-3.21.0/zipp.egg-info/requires.txt0000644000175100001770000000063614714145654017260 0ustar00runnerdocker [check] pytest-checkdocs>=2.4 [check:sys_platform != "cygwin"] pytest-ruff>=0.2.1 [cover] pytest-cov [doc] sphinx>=3.5 jaraco.packaging>=9.3 rst.linker>=1.9 furo sphinx-lint jaraco.tidelift>=1.4 [enabler] pytest-enabler>=2.2 [test] pytest!=8.1.*,>=6 jaraco.itertools jaraco.functools more_itertools big-O pytest-ignore-flaky jaraco.test [test:python_version < "3.9"] importlib_resources [type] pytest-mypy ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1731251116.0 zipp-3.21.0/zipp.egg-info/top_level.txt0000644000175100001770000000000514714145654017400 0ustar00runnerdockerzipp