[bumpversion]
-current_version = 67.3.3
+current_version = 67.4.0
commit = True
tag = True
+v67.4.0
+-------
+
+
+Changes
+^^^^^^^
+* #3832: Update vendored ``importlib-metadata`` (to 6.0.0) and
+ ``importlib-resources`` (to 5.10.2)
+
+
v67.3.3
-------
--- /dev/null
+
+ 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.
--- /dev/null
+Metadata-Version: 2.1
+Name: importlib-resources
+Version: 5.10.2
+Summary: Read resources from Python packages
+Home-page: https://github.com/python/importlib_resources
+Author: Barry Warsaw
+Author-email: barry@python.org
+Project-URL: Documentation, https://importlib-resources.readthedocs.io/
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.7
+License-File: LICENSE
+Requires-Dist: zipp (>=3.1.0) ; python_version < "3.10"
+Provides-Extra: docs
+Requires-Dist: sphinx (>=3.5) ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Requires-Dist: furo ; extra == 'docs'
+Requires-Dist: sphinx-lint ; extra == 'docs'
+Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: flake8 (<5) ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.3) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-flake8 ; (python_version < "3.12") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/importlib_resources.svg
+ :target: https://pypi.org/project/importlib_resources
+
+.. image:: https://img.shields.io/pypi/pyversions/importlib_resources.svg
+
+.. image:: https://github.com/python/importlib_resources/workflows/tests/badge.svg
+ :target: https://github.com/python/importlib_resources/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. 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-resources/badge/?version=latest
+ :target: https://importlib-resources.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2022-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/importlib-resources
+ :target: https://tidelift.com/subscription/pkg/pypi-importlib-resources?utm_source=pypi-importlib-resources&utm_medium=readme
+
+``importlib_resources`` is a backport of Python standard library
+`importlib.resources
+<https://docs.python.org/3/library/importlib.html#module-importlib.resources>`_
+module for older Pythons.
+
+The key goal of this module is to replace parts of `pkg_resources
+<https://setuptools.readthedocs.io/en/latest/pkg_resources.html>`_ with a
+solution in Python's stdlib that relies on well-defined APIs. This makes
+reading resources included in packages easier, with more stable and consistent
+semantics.
+
+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_resources
+ - stdlib
+ * - 5.9
+ - 3.12
+ * - 5.7
+ - 3.11
+ * - 5.0
+ - 3.10
+ * - 1.3
+ - 3.9
+ * - 0.5 (?)
+ - 3.7
+
+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-resources?utm_source=pypi-importlib-resources&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.
--- /dev/null
+importlib_resources-5.10.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+importlib_resources-5.10.2.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
+importlib_resources-5.10.2.dist-info/METADATA,sha256=Xo5ntATvDYUxdmW8tr8kxtfdiOC9889mOk-LE1LtZfI,4111
+importlib_resources-5.10.2.dist-info/RECORD,,
+importlib_resources-5.10.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources-5.10.2.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
+importlib_resources-5.10.2.dist-info/top_level.txt,sha256=fHIjHU1GZwAjvcydpmUnUrTnbvdiWjG4OEVZK8by0TQ,20
+importlib_resources/__init__.py,sha256=evPm12kLgYqTm-pbzm60bOuumumT8IpBNWFp0uMyrzE,506
+importlib_resources/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/__pycache__/_adapters.cpython-38.pyc,,
+importlib_resources/__pycache__/_common.cpython-38.pyc,,
+importlib_resources/__pycache__/_compat.cpython-38.pyc,,
+importlib_resources/__pycache__/_itertools.cpython-38.pyc,,
+importlib_resources/__pycache__/_legacy.cpython-38.pyc,,
+importlib_resources/__pycache__/abc.cpython-38.pyc,,
+importlib_resources/__pycache__/readers.cpython-38.pyc,,
+importlib_resources/__pycache__/simple.cpython-38.pyc,,
+importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504
+importlib_resources/_common.py,sha256=jSC4xfLdcMNbtbWHtpzbFkNa0W7kvf__nsYn14C_AEU,5457
+importlib_resources/_compat.py,sha256=dSadF6WPt8MwOqSm_NIOQPhw4x0iaMYTWxi-XS93p7M,2923
+importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884
+importlib_resources/_legacy.py,sha256=0TKdZixxLWA-xwtAZw4HcpqJmj4Xprx1Zkcty0gTRZY,3481
+importlib_resources/abc.py,sha256=Icr2IJ2QtH7vvAB9vC5WRJ9KBoaDyJa7KUs8McuROzo,5140
+importlib_resources/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/readers.py,sha256=PZsi5qacr2Qn3KHw4qw3Gm1MzrBblPHoTdjqjH7EKWw,3581
+importlib_resources/simple.py,sha256=0__2TQBTQoqkajYmNPt1HxERcReAT6boVKJA328pr04,2576
+importlib_resources/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/_compat.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/_path.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_contents.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_files.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_open.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_path.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_read.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_reader.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_resource.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/update-zips.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/util.cpython-38.pyc,,
+importlib_resources/tests/_compat.py,sha256=YTSB0U1R9oADnh6GrQcOCgojxcF_N6H1LklymEWf9SQ,708
+importlib_resources/tests/_path.py,sha256=yZyWsQzJZQ1Z8ARAxWkjAdaVVsjlzyqxO0qjBUofJ8M,1039
+importlib_resources/tests/data01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data01/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/data01/subdirectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data01/subdirectory/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/data01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44
+importlib_resources/tests/data01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20
+importlib_resources/tests/data02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data02/one/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/one/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data02/one/resource1.txt,sha256=10flKac7c-XXFzJ3t-AB5MJjlBy__dSZvPE_dOm2q6U,13
+importlib_resources/tests/data02/two/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/two/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data02/two/resource2.txt,sha256=lt2jbN3TMn9QiFKM832X39bU_62UptDdUkoYzkvEbl0,13
+importlib_resources/tests/namespacedata01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/namespacedata01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44
+importlib_resources/tests/namespacedata01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20
+importlib_resources/tests/test_compatibilty_files.py,sha256=NWkbIsylI8Wz3Dwsxo1quT4ZI6ToXFA2mojCG6Dzuxw,3260
+importlib_resources/tests/test_contents.py,sha256=V1Xfk3lqTDdvUsZuV18Kndf0CT_tkM2oEIwk9Vv0rhg,968
+importlib_resources/tests/test_files.py,sha256=1Y8da-g0xOQLzuREDYUiRc_qhWlvFNeydW_mUH7l15w,3251
+importlib_resources/tests/test_open.py,sha256=pmEgdrSFdM83L6FxtR8U_RT9BfI3JZ4snGmM_ZZIegY,2565
+importlib_resources/tests/test_path.py,sha256=xvPteNA-UKavDhKgLgrQuXSxKWYH7Q4nSNDVfBX95Gs,2103
+importlib_resources/tests/test_read.py,sha256=EyYvpHJ_7F4LuX2EU_c5EerIBQfRhOFmiIR7LOc5Y5E,2408
+importlib_resources/tests/test_reader.py,sha256=nPhldbYPq3fXoQs0ZAub4atjhp2lgNyLNv2G1pg6Agw,4480
+importlib_resources/tests/test_resource.py,sha256=EMoarxTEHcrq8R41LQDsndIG8Idtm4I_LpN8DYpHtT0,8478
+importlib_resources/tests/update-zips.py,sha256=x-SrO5v87iLLUMXyefxDwAd3imAs_slI94sLWvJ6N40,1417
+importlib_resources/tests/util.py,sha256=ARAlxZ47wC-lgR7PGlmgBoi4HnhzcykD5Is2-TAwY0I,4873
+importlib_resources/tests/zipdata01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/zipdata01/ziptestdata.zip,sha256=z5Of4dsv3T0t-46B0MsVhxlhsPGMz28aUhJDWpj3_oY,876
+importlib_resources/tests/zipdata02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/zipdata02/ziptestdata.zip,sha256=ydI-_j-xgQ7tDxqBp9cjOqXBGxUp6ZBbwVJu6Xj-nrY,698
--- /dev/null
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.38.4)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
--- /dev/null
+importlib_resources
+++ /dev/null
-Copyright 2017-2019 Brett Cannon, Barry Warsaw
-
-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.
+++ /dev/null
-Metadata-Version: 2.1
-Name: importlib-resources
-Version: 5.4.0
-Summary: Read resources from Python packages
-Home-page: https://github.com/python/importlib_resources
-Author: Barry Warsaw
-Author-email: barry@python.org
-License: UNKNOWN
-Project-URL: Documentation, https://importlib-resources.readthedocs.io/
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Requires-Python: >=3.6
-License-File: LICENSE
-Requires-Dist: zipp (>=3.1.0) ; python_version < "3.10"
-Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
-Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
-Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
-Provides-Extra: testing
-Requires-Dist: pytest (>=6) ; extra == 'testing'
-Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
-Requires-Dist: pytest-flake8 ; extra == 'testing'
-Requires-Dist: pytest-cov ; extra == 'testing'
-Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
-Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
-
-.. image:: https://img.shields.io/pypi/v/importlib_resources.svg
- :target: `PyPI link`_
-
-.. image:: https://img.shields.io/pypi/pyversions/importlib_resources.svg
- :target: `PyPI link`_
-
-.. _PyPI link: https://pypi.org/project/importlib_resources
-
-.. image:: https://github.com/python/importlib_resources/workflows/tests/badge.svg
- :target: https://github.com/python/importlib_resources/actions?query=workflow%3A%22tests%22
- :alt: tests
-
-.. 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-resources/badge/?version=latest
- :target: https://importlib-resources.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://img.shields.io/badge/skeleton-2021-informational
- :target: https://blog.jaraco.com/skeleton
-
-``importlib_resources`` is a backport of Python standard library
-`importlib.resources
-<https://docs.python.org/3/library/importlib.html#module-importlib.resources>`_
-module for older Pythons.
-
-The key goal of this module is to replace parts of `pkg_resources
-<https://setuptools.readthedocs.io/en/latest/pkg_resources.html>`_ with a
-solution in Python's stdlib that relies on well-defined APIs. This makes
-reading resources included in packages easier, with more stable and consistent
-semantics.
-
-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_resources
- - stdlib
- * - 5.2
- - 3.11
- * - 5.0
- - 3.10
- * - 1.3
- - 3.9
- * - 0.5 (?)
- - 3.7
-
-
+++ /dev/null
-importlib_resources-5.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-importlib_resources-5.4.0.dist-info/LICENSE,sha256=uWRjFdYGataJX2ziXk048ItUglQmjng3GWBALaWA36U,568\r
-importlib_resources-5.4.0.dist-info/METADATA,sha256=i5jH25IbM0Ls6u6UzSSCOa0c8hpDvePxqgnQwh2T5Io,3135\r
-importlib_resources-5.4.0.dist-info/RECORD,,\r
-importlib_resources-5.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources-5.4.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92\r
-importlib_resources-5.4.0.dist-info/top_level.txt,sha256=fHIjHU1GZwAjvcydpmUnUrTnbvdiWjG4OEVZK8by0TQ,20\r
-importlib_resources/__init__.py,sha256=zuA0lbRgtVVCcAztM0z5LuBiOCV9L_3qtI6mW2p5xAg,525\r
-importlib_resources/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_adapters.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_common.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_compat.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_itertools.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_legacy.cpython-311.pyc,,\r
-importlib_resources/__pycache__/abc.cpython-311.pyc,,\r
-importlib_resources/__pycache__/readers.cpython-311.pyc,,\r
-importlib_resources/__pycache__/simple.cpython-311.pyc,,\r
-importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504\r
-importlib_resources/_common.py,sha256=iIxAaQhotSh6TLLUEfL_ynU2fzEeyHMz9JcL46mUhLg,2741\r
-importlib_resources/_compat.py,sha256=3LpkIfeN9x4oXjRea5TxZP5VYhPlzuVRhGe-hEv-S0s,2704\r
-importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884\r
-importlib_resources/_legacy.py,sha256=TMLkx6aEM6U8xIREPXqGZrMbUhTiPUuPl6ESD7RdYj4,3494\r
-importlib_resources/abc.py,sha256=MvTJJXajbl74s36Gyeesf76egtbFnh-TMtzQMVhFWXo,3886\r
-importlib_resources/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/readers.py,sha256=_9QLGQ5AzrED3PY8S2Zf8V6yLR0-nqqYqtQmgleDJzY,3566\r
-importlib_resources/simple.py,sha256=xt0qhXbwt3bZ86zuaaKbTiE9A0mDbwu0saRjUq_pcY0,2836\r
-importlib_resources/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/_compat.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_contents.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_files.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_open.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_path.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_read.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_reader.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_resource.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/update-zips.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/util.cpython-311.pyc,,\r
-importlib_resources/tests/_compat.py,sha256=QGI_4p0DXybypoYvw0kr3jfQqvls3p8u4wy4Wvf0Z_o,435\r
-importlib_resources/tests/data01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data01/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
-importlib_resources/tests/data01/subdirectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data01/subdirectory/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
-importlib_resources/tests/data01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44\r
-importlib_resources/tests/data01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20\r
-importlib_resources/tests/data02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data02/one/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/one/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data02/one/resource1.txt,sha256=10flKac7c-XXFzJ3t-AB5MJjlBy__dSZvPE_dOm2q6U,13\r
-importlib_resources/tests/data02/two/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/two/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data02/two/resource2.txt,sha256=lt2jbN3TMn9QiFKM832X39bU_62UptDdUkoYzkvEbl0,13\r
-importlib_resources/tests/namespacedata01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
-importlib_resources/tests/namespacedata01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44\r
-importlib_resources/tests/namespacedata01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20\r
-importlib_resources/tests/test_compatibilty_files.py,sha256=NWkbIsylI8Wz3Dwsxo1quT4ZI6ToXFA2mojCG6Dzuxw,3260\r
-importlib_resources/tests/test_contents.py,sha256=V1Xfk3lqTDdvUsZuV18Kndf0CT_tkM2oEIwk9Vv0rhg,968\r
-importlib_resources/tests/test_files.py,sha256=1Nqv6VM_MjfwrmtXYL1a1CMT0QhCxi3hNMqwXlfMQTg,1184\r
-importlib_resources/tests/test_open.py,sha256=pmEgdrSFdM83L6FxtR8U_RT9BfI3JZ4snGmM_ZZIegY,2565\r
-importlib_resources/tests/test_path.py,sha256=xvPteNA-UKavDhKgLgrQuXSxKWYH7Q4nSNDVfBX95Gs,2103\r
-importlib_resources/tests/test_read.py,sha256=EyYvpHJ_7F4LuX2EU_c5EerIBQfRhOFmiIR7LOc5Y5E,2408\r
-importlib_resources/tests/test_reader.py,sha256=hgXHquqAEnioemv20ZZcDlVaiOrcZKADO37_FkiQ00Y,4286\r
-importlib_resources/tests/test_resource.py,sha256=DqfLNc9kaN5obqxU8kn0sRUWMf9MygagrpfMV5-QfWg,8145\r
-importlib_resources/tests/update-zips.py,sha256=x3iJVqWnMM5qp4Oob2Pl3o6Yi03sUjEv_5Wf-UCg3ps,1415\r
-importlib_resources/tests/util.py,sha256=X1j-0C96pu3_tmtJuLhzfBfcfMenOphDLkxtCt5j7t4,5309\r
-importlib_resources/tests/zipdata01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/zipdata01/ziptestdata.zip,sha256=z5Of4dsv3T0t-46B0MsVhxlhsPGMz28aUhJDWpj3_oY,876\r
-importlib_resources/tests/zipdata02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/zipdata02/ziptestdata.zip,sha256=ydI-_j-xgQ7tDxqBp9cjOqXBGxUp6ZBbwVJu6Xj-nrY,698\r
+++ /dev/null
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
+++ /dev/null
-importlib_resources
import contextlib
import types
import importlib
+import inspect
+import warnings
+import itertools
-from typing import Union, Optional
+from typing import Union, Optional, cast
from .abc import ResourceReader, Traversable
from ._compat import wrap_spec
Package = Union[types.ModuleType, str]
+Anchor = Package
-def files(package):
- # type: (Package) -> Traversable
+def package_to_anchor(func):
"""
- Get a Traversable resource from a package
+ Replace 'package' parameter as 'anchor' and warn about the change.
+
+ Other errors should fall through.
+
+ >>> files('a', 'b')
+ Traceback (most recent call last):
+ TypeError: files() takes from 0 to 1 positional arguments but 2 were given
+ """
+ undefined = object()
+
+ @functools.wraps(func)
+ def wrapper(anchor=undefined, package=undefined):
+ if package is not undefined:
+ if anchor is not undefined:
+ return func(anchor, package)
+ warnings.warn(
+ "First parameter to files is renamed to 'anchor'",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return func(package)
+ elif anchor is undefined:
+ return func()
+ return func(anchor)
+
+ return wrapper
+
+
+@package_to_anchor
+def files(anchor: Optional[Anchor] = None) -> Traversable:
+ """
+ Get a Traversable resource for an anchor.
"""
- return from_package(get_package(package))
+ return from_package(resolve(anchor))
-def get_resource_reader(package):
- # type: (types.ModuleType) -> Optional[ResourceReader]
+def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]:
"""
Return the package's loader if it's a ResourceReader.
"""
return reader(spec.name) # type: ignore
-def resolve(cand):
- # type: (Package) -> types.ModuleType
- return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand)
+@functools.singledispatch
+def resolve(cand: Optional[Anchor]) -> types.ModuleType:
+ return cast(types.ModuleType, cand)
+
+
+@resolve.register
+def _(cand: str) -> types.ModuleType:
+ return importlib.import_module(cand)
+
+@resolve.register
+def _(cand: None) -> types.ModuleType:
+ return resolve(_infer_caller().f_globals['__name__'])
-def get_package(package):
- # type: (Package) -> types.ModuleType
- """Take a package name or module object and return the module.
- Raise an exception if the resolved module is not a package.
+def _infer_caller():
"""
- resolved = resolve(package)
- if wrap_spec(resolved).submodule_search_locations is None:
- raise TypeError(f'{package!r} is not a package')
- return resolved
+ Walk the stack and find the frame of the first caller not in this module.
+ """
+
+ def is_this_file(frame_info):
+ return frame_info.filename == __file__
+
+ def is_wrapper(frame_info):
+ return frame_info.function == 'wrapper'
+
+ not_this_file = itertools.filterfalse(is_this_file, inspect.stack())
+ # also exclude 'wrapper' due to singledispatch in the call stack
+ callers = itertools.filterfalse(is_wrapper, not_this_file)
+ return next(callers).frame
-def from_package(package):
+def from_package(package: types.ModuleType):
"""
Return a Traversable object for the given package.
@contextlib.contextmanager
-def _tempfile(reader, suffix=''):
+def _tempfile(
+ reader,
+ suffix='',
+ # gh-93353: Keep a reference to call os.remove() in late Python
+ # finalization.
+ *,
+ _os_remove=os.remove,
+):
# Not using tempfile.NamedTemporaryFile as it leads to deeper 'try'
# blocks due to the need to close the temporary file to work on Windows
# properly.
yield pathlib.Path(raw_path)
finally:
try:
- os.remove(raw_path)
+ _os_remove(raw_path)
except FileNotFoundError:
pass
+def _temp_file(path):
+ return _tempfile(path.read_bytes, suffix=path.name)
+
+
+def _is_present_dir(path: Traversable) -> bool:
+ """
+ Some Traversables implement ``is_dir()`` to raise an
+ exception (i.e. ``FileNotFoundError``) when the
+ directory doesn't exist. This function wraps that call
+ to always return a boolean and only return True
+ if there's a dir and it exists.
+ """
+ with contextlib.suppress(FileNotFoundError):
+ return path.is_dir()
+ return False
+
+
@functools.singledispatch
def as_file(path):
"""
Given a Traversable object, return that object as a
path on the local file system in a context manager.
"""
- return _tempfile(path.read_bytes, suffix=path.name)
+ return _temp_dir(path) if _is_present_dir(path) else _temp_file(path)
@as_file.register(pathlib.Path)
Degenerate behavior for pathlib.Path objects.
"""
yield path
+
+
+@contextlib.contextmanager
+def _temp_path(dir: tempfile.TemporaryDirectory):
+ """
+ Wrap tempfile.TemporyDirectory to return a pathlib object.
+ """
+ with dir as result:
+ yield pathlib.Path(result)
+
+
+@contextlib.contextmanager
+def _temp_dir(path):
+ """
+ Given a traversable dir, recursively replicate the whole tree
+ to the file system in a context manager.
+ """
+ assert path.is_dir()
+ with _temp_path(tempfile.TemporaryDirectory()) as temp_dir:
+ yield _write_contents(temp_dir, path)
+
+
+def _write_contents(target, source):
+ child = target.joinpath(source.name)
+ if source.is_dir():
+ child.mkdir()
+ for item in source.iterdir():
+ _write_contents(child, item)
+ else:
+ child.write_bytes(source.read_bytes())
+ return child
# flake8: noqa
import abc
+import os
import sys
import pathlib
from contextlib import suppress
+from typing import Union
+
if sys.version_info >= (3, 10):
from zipfile import Path as ZipPath # type: ignore
from . import _adapters
return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)
+
+
+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]"]
return wrapper
-def normalize_path(path):
- # type: (Any) -> str
+def normalize_path(path: Any) -> str:
"""Normalize a path by ensuring it is a string.
If the resulting string contains path separators, an exception is raised.
import abc
-from typing import BinaryIO, Iterable, Text
+import io
+import itertools
+import pathlib
+from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional
-from ._compat import runtime_checkable, Protocol
+from ._compat import runtime_checkable, Protocol, StrPath
+
+
+__all__ = ["ResourceReader", "Traversable", "TraversableResources"]
class ResourceReader(metaclass=abc.ABCMeta):
raise FileNotFoundError
+class TraversalError(Exception):
+ pass
+
+
@runtime_checkable
class Traversable(Protocol):
"""
An object with a subset of pathlib.Path methods suitable for
traversing directories and opening files.
+
+ Any exceptions that occur when accessing the backing resource
+ may propagate unaltered.
"""
@abc.abstractmethod
- def iterdir(self):
+ def iterdir(self) -> Iterator["Traversable"]:
"""
Yield Traversable objects in self
"""
- def read_bytes(self):
+ def read_bytes(self) -> bytes:
"""
Read contents of self as bytes
"""
with self.open('rb') as strm:
return strm.read()
- def read_text(self, encoding=None):
+ def read_text(self, encoding: Optional[str] = None) -> str:
"""
Read contents of self as text
"""
Return True if self is a file
"""
- @abc.abstractmethod
- def joinpath(self, child):
+ def joinpath(self, *descendants: StrPath) -> "Traversable":
"""
- Return Traversable child in self
+ Return Traversable resolved with any descendants applied.
+
+ Each descendant should be a path segment relative to self
+ and each may contain multiple levels separated by
+ ``posixpath.sep`` (``/``).
"""
+ if not descendants:
+ return self
+ names = itertools.chain.from_iterable(
+ path.parts for path in map(pathlib.PurePosixPath, descendants)
+ )
+ target = next(names)
+ matches = (
+ traversable for traversable in self.iterdir() if traversable.name == target
+ )
+ try:
+ match = next(matches)
+ except StopIteration:
+ raise TraversalError(
+ "Target not found during traversal.", target, list(names)
+ )
+ return match.joinpath(*names)
- def __truediv__(self, child):
+ def __truediv__(self, child: StrPath) -> "Traversable":
"""
Return Traversable child in self
"""
accepted by io.TextIOWrapper.
"""
- @abc.abstractproperty
+ @property
+ @abc.abstractmethod
def name(self) -> str:
"""
The base name of this object without any parent references.
"""
@abc.abstractmethod
- def files(self):
+ def files(self) -> "Traversable":
"""Return a Traversable object for the loaded package."""
- def open_resource(self, resource):
+ def open_resource(self, resource: StrPath) -> io.BufferedReader:
return self.files().joinpath(resource).open('rb')
- def resource_path(self, resource):
+ def resource_path(self, resource: Any) -> NoReturn:
raise FileNotFoundError(resource)
- def is_resource(self, path):
+ def is_resource(self, path: StrPath) -> bool:
return self.files().joinpath(path).is_file()
- def contents(self):
+ def contents(self) -> Iterator[str]:
return (item.name for item in self.files().iterdir())
def is_file(self):
return False
- def joinpath(self, child):
- # first try to find child in current paths
- for file in self.iterdir():
- if file.name == child:
- return file
- # if it does not exist, construct it with the first path
- return self._paths[0] / child
-
- __truediv__ = joinpath
+ def joinpath(self, *descendants):
+ try:
+ return super().joinpath(*descendants)
+ except abc.TraversalError:
+ # One of the paths did not resolve (a directory does not exist).
+ # Just return something that will not exist.
+ return self._paths[0].joinpath(*descendants)
def open(self, *args, **kwargs):
raise FileNotFoundError(f'{self} is not a file')
provider.
"""
- @abc.abstractproperty
- def package(self):
- # type: () -> str
+ @property
+ @abc.abstractmethod
+ def package(self) -> str:
"""
The name of the package for which this reader loads resources.
"""
@abc.abstractmethod
- def children(self):
- # type: () -> List['SimpleReader']
+ def children(self) -> List['SimpleReader']:
"""
Obtain an iterable of SimpleReader for available
child containers (e.g. directories).
"""
@abc.abstractmethod
- def resources(self):
- # type: () -> List[str]
+ def resources(self) -> List[str]:
"""
Obtain available named resources for this virtual package.
"""
@abc.abstractmethod
- def open_binary(self, resource):
- # type: (str) -> BinaryIO
+ def open_binary(self, resource: str) -> BinaryIO:
"""
Obtain a File-like for a named resource.
"""
return self.package.split('.')[-1]
-class ResourceHandle(Traversable):
- """
- Handle to a named resource in a ResourceReader.
- """
-
- def __init__(self, parent, name):
- # type: (ResourceContainer, str) -> None
- self.parent = parent
- self.name = name # type: ignore
-
- def is_file(self):
- return True
-
- def is_dir(self):
- return False
-
- def open(self, mode='r', *args, **kwargs):
- stream = self.parent.reader.open_binary(self.name)
- if 'b' not in mode:
- stream = io.TextIOWrapper(*args, **kwargs)
- return stream
-
- def joinpath(self, name):
- raise RuntimeError("Cannot traverse into a resource")
-
-
class ResourceContainer(Traversable):
"""
Traversable container for a package's resources via its reader.
"""
- def __init__(self, reader):
- # type: (SimpleReader) -> None
+ def __init__(self, reader: SimpleReader):
self.reader = reader
def is_dir(self):
def open(self, *args, **kwargs):
raise IsADirectoryError()
+
+class ResourceHandle(Traversable):
+ """
+ Handle to a named resource in a ResourceReader.
+ """
+
+ def __init__(self, parent: ResourceContainer, name: str):
+ self.parent = parent
+ self.name = name # type: ignore
+
+ def is_file(self):
+ return True
+
+ def is_dir(self):
+ return False
+
+ def open(self, mode='r', *args, **kwargs):
+ stream = self.parent.reader.open_binary(self.name)
+ if 'b' not in mode:
+ stream = io.TextIOWrapper(*args, **kwargs)
+ return stream
+
def joinpath(self, name):
- return next(
- traversable for traversable in self.iterdir() if traversable.name == name
- )
+ raise RuntimeError("Cannot traverse into a resource")
class TraversableReader(TraversableResources, SimpleReader):
except ImportError:
# Python 3.9 and earlier
class import_helper: # type: ignore
- from test.support import modules_setup, modules_cleanup
+ from test.support import (
+ modules_setup,
+ modules_cleanup,
+ DirsOnSysPath,
+ CleanImport,
+ )
+
+
+try:
+ from test.support import os_helper # type: ignore
+except ImportError:
+ # Python 3.9 compat
+ class os_helper: # type:ignore
+ from test.support import temp_dir
try:
--- /dev/null
+import pathlib
+import functools
+
+
+####
+# from jaraco.path 3.4
+
+
+def build(spec, prefix=pathlib.Path()):
+ """
+ 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",
+ ... }
+ ... }
+ >>> tmpdir = getfixture('tmpdir')
+ >>> build(spec, tmpdir)
+ """
+ for name, contents in spec.items():
+ create(contents, pathlib.Path(prefix) / name)
+
+
+@functools.singledispatch
+def create(content, 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)
+
+
+# end from jaraco.path
+####
import typing
+import textwrap
import unittest
+import warnings
+import importlib
+import contextlib
import importlib_resources as resources
-from importlib_resources.abc import Traversable
+from ..abc import Traversable
from . import data01
from . import util
+from . import _path
+from ._compat import os_helper, import_helper
+
+
+@contextlib.contextmanager
+def suppress_known_deprecation():
+ with warnings.catch_warnings(record=True) as ctx:
+ warnings.simplefilter('default', category=DeprecationWarning)
+ yield ctx
class FilesTests:
def test_traversable(self):
assert isinstance(resources.files(self.data), Traversable)
+ def test_old_parameter(self):
+ """
+ Files used to take a 'package' parameter. Make sure anyone
+ passing by name is still supported.
+ """
+ with suppress_known_deprecation():
+ resources.files(package=self.data)
+
class OpenDiskTests(FilesTests, unittest.TestCase):
def setUp(self):
self.data = namespacedata01
+class SiteDir:
+ def setUp(self):
+ self.fixtures = contextlib.ExitStack()
+ self.addCleanup(self.fixtures.close)
+ self.site_dir = self.fixtures.enter_context(os_helper.temp_dir())
+ self.fixtures.enter_context(import_helper.DirsOnSysPath(self.site_dir))
+ self.fixtures.enter_context(import_helper.CleanImport())
+
+
+class ModulesFilesTests(SiteDir, unittest.TestCase):
+ def test_module_resources(self):
+ """
+ A module can have resources found adjacent to the module.
+ """
+ spec = {
+ 'mod.py': '',
+ 'res.txt': 'resources are the best',
+ }
+ _path.build(spec, self.site_dir)
+ import mod
+
+ actual = resources.files(mod).joinpath('res.txt').read_text()
+ assert actual == spec['res.txt']
+
+
+class ImplicitContextFilesTests(SiteDir, unittest.TestCase):
+ def test_implicit_files(self):
+ """
+ Without any parameter, files() will infer the location as the caller.
+ """
+ spec = {
+ 'somepkg': {
+ '__init__.py': textwrap.dedent(
+ """
+ import importlib_resources as res
+ val = res.files().joinpath('res.txt').read_text()
+ """
+ ),
+ 'res.txt': 'resources are the best',
+ },
+ }
+ _path.build(spec, self.site_dir)
+ assert importlib.import_module('somepkg').val == 'resources are the best'
+
+
if __name__ == '__main__':
unittest.main()
str(path.joinpath('imaginary'))[len(prefix) + 1 :],
os.path.join('namespacedata01', 'imaginary'),
)
+ self.assertEqual(path.joinpath(), path)
+
+ def test_join_path_compound(self):
+ path = MultiplexedPath(self.folder)
+ assert not path.joinpath('imaginary/foo.py').exists()
def test_repr(self):
self.assertEqual(
{'__init__.py', 'binary.file'},
)
+ def test_as_file_directory(self):
+ with resources.as_file(resources.files('ziptestdata')) as data:
+ assert data.name == 'ziptestdata'
+ assert data.is_dir()
+ assert data.joinpath('subdirectory').is_dir()
+ assert len(list(data.iterdir()))
+ assert not data.parent.exists()
+
class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase):
ZIP_MODULE = zipdata02 # type: ignore
def walk(datapath):
for dirpath, dirnames, filenames in os.walk(datapath):
- with contextlib.suppress(KeyError):
+ with contextlib.suppress(ValueError):
dirnames.remove('__pycache__')
for filename in filenames:
res = pathlib.Path(dirpath) / filename
import io
import sys
import types
-from pathlib import Path, PurePath
+import pathlib
from . import data01
from . import zipdata01
def test_pathlib_path(self):
# Passing in a pathlib.PurePath object for the path should succeed.
- path = PurePath('utf-8.file')
+ path = pathlib.PurePath('utf-8.file')
self.execute(data01, path)
def test_importing_module_as_side_effect(self):
del sys.modules[data01.__name__]
self.execute(data01.__name__, 'utf-8.file')
- def test_non_package_by_name(self):
- # The anchor package cannot be a module.
- with self.assertRaises(TypeError):
- self.execute(__name__, 'utf-8.file')
-
- def test_non_package_by_package(self):
- # The anchor package cannot be a module.
- with self.assertRaises(TypeError):
- module = sys.modules['importlib_resources.tests.util']
- self.execute(module, 'utf-8.file')
-
def test_missing_path(self):
# Attempting to open or read or request the path for a
# non-existent path should succeed if open_resource
@classmethod
def setUpClass(cls):
- data_path = Path(cls.ZIP_MODULE.__file__)
+ data_path = pathlib.Path(cls.ZIP_MODULE.__file__)
data_dir = data_path.parent
cls._zip_path = str(data_dir / 'ziptestdata.zip')
sys.path.append(cls._zip_path)
jaraco.text==3.7.0
# required for jaraco.text on older Pythons
-importlib_resources==5.4.0
+importlib_resources==5.10.2
# required for importlib_resources on older Pythons
zipp==3.7.0
[metadata]
name = setuptools
-version = 67.3.3
+version = 67.4.0
author = Python Packaging Authority
author_email = distutils-sig@python.org
description = Easily download, build, install, upgrade, and uninstall Python packages
+++ /dev/null
-Copyright 2017-2019 Jason R. Coombs, Barry Warsaw
-
-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.
+++ /dev/null
-Metadata-Version: 2.1
-Name: importlib-metadata
-Version: 4.11.1
-Summary: Read metadata from Python packages
-Home-page: https://github.com/python/importlib_metadata
-Author: Jason R. Coombs
-Author-email: jaraco@jaraco.com
-License: UNKNOWN
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Requires-Python: >=3.7
-License-File: LICENSE
-Requires-Dist: zipp (>=0.5)
-Requires-Dist: typing-extensions (>=3.6.4) ; python_version < "3.8"
-Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
-Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
-Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
-Provides-Extra: perf
-Requires-Dist: ipython ; extra == 'perf'
-Provides-Extra: testing
-Requires-Dist: pytest (>=6) ; extra == 'testing'
-Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
-Requires-Dist: pytest-flake8 ; extra == 'testing'
-Requires-Dist: pytest-cov ; extra == 'testing'
-Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
-Requires-Dist: packaging ; extra == 'testing'
-Requires-Dist: pyfakefs ; extra == 'testing'
-Requires-Dist: flufl.flake8 ; extra == 'testing'
-Requires-Dist: pytest-perf (>=0.9.2) ; extra == 'testing'
-Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: importlib-resources (>=1.3) ; (python_version < "3.9") and extra == 'testing'
-
-.. image:: https://img.shields.io/pypi/v/importlib_metadata.svg
- :target: `PyPI link`_
-
-.. image:: https://img.shields.io/pypi/pyversions/importlib_metadata.svg
- :target: `PyPI link`_
-
-.. _PyPI link: https://pypi.org/project/importlib_metadata
-
-.. 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/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-2022-informational
- :target: https://blog.jaraco.com/skeleton
-
-
-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
- * - 4.8
- - 3.11
- * - 4.4
- - 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/
-
-
+++ /dev/null
-importlib_metadata-4.11.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-importlib_metadata-4.11.1.dist-info/LICENSE,sha256=wNe6dAchmJ1VvVB8D9oTc-gHHadCuaSBAev36sYEM6U,571\r
-importlib_metadata-4.11.1.dist-info/METADATA,sha256=XNgM09x6V8tbt6ugvKjiUxH9yB7pBdILWuWE5YNWHRw,3999\r
-importlib_metadata-4.11.1.dist-info/RECORD,,\r
-importlib_metadata-4.11.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_metadata-4.11.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92\r
-importlib_metadata-4.11.1.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19\r
-importlib_metadata/__init__.py,sha256=Wkh_tb0u0Ds_615ByV9VLLjqgoOWirwMY8EW40oO3nM,30122\r
-importlib_metadata/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_metadata/__pycache__/_adapters.cpython-311.pyc,,\r
-importlib_metadata/__pycache__/_collections.cpython-311.pyc,,\r
-importlib_metadata/__pycache__/_compat.cpython-311.pyc,,\r
-importlib_metadata/__pycache__/_functools.cpython-311.pyc,,\r
-importlib_metadata/__pycache__/_itertools.cpython-311.pyc,,\r
-importlib_metadata/__pycache__/_meta.cpython-311.pyc,,\r
-importlib_metadata/__pycache__/_text.cpython-311.pyc,,\r
-importlib_metadata/_adapters.py,sha256=B6fCi5-8mLVDFUZj3krI5nAo-mKp1dH_qIavyIyFrJs,1862\r
-importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743\r
-importlib_metadata/_compat.py,sha256=EU2XCFBPFByuI0Of6XkAuBYbzqSyjwwwwqmsK4ccna0,1826\r
-importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895\r
-importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068\r
-importlib_metadata/_meta.py,sha256=_F48Hu_jFxkfKWz5wcYS8vO23qEygbVdF9r-6qh-hjE,1154\r
-importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166\r
-importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
+++ /dev/null
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.1)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
+++ /dev/null
-importlib_metadata
--- /dev/null
+
+ 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.
--- /dev/null
+Metadata-Version: 2.1
+Name: importlib-metadata
+Version: 6.0.0
+Summary: Read metadata from Python packages
+Home-page: https://github.com/python/importlib_metadata
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.7
+License-File: LICENSE
+Requires-Dist: zipp (>=0.5)
+Requires-Dist: typing-extensions (>=3.6.4) ; python_version < "3.8"
+Provides-Extra: docs
+Requires-Dist: sphinx (>=3.5) ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Requires-Dist: furo ; extra == 'docs'
+Requires-Dist: sphinx-lint ; extra == 'docs'
+Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'
+Provides-Extra: perf
+Requires-Dist: ipython ; extra == 'perf'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: flake8 (<5) ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.3) ; extra == 'testing'
+Requires-Dist: packaging ; extra == 'testing'
+Requires-Dist: pyfakefs ; extra == 'testing'
+Requires-Dist: flufl.flake8 ; extra == 'testing'
+Requires-Dist: pytest-perf (>=0.9.2) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-flake8 ; (python_version < "3.12") and extra == 'testing'
+Requires-Dist: importlib-resources (>=1.3) ; (python_version < "3.9") and extra == 'testing'
+
+.. 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/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-2022-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
+ * - 5.0
+ - 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.
--- /dev/null
+importlib_metadata-6.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+importlib_metadata-6.0.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
+importlib_metadata-6.0.0.dist-info/METADATA,sha256=tZIEx9HdEXD34SWuitkNXaYBqSnyNukx2l4FKQAz9hY,4958
+importlib_metadata-6.0.0.dist-info/RECORD,,
+importlib_metadata-6.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_metadata-6.0.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
+importlib_metadata-6.0.0.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19
+importlib_metadata/__init__.py,sha256=wiMJxNXXhPtRRHSX2N9gGLnTh0YszmE1rn3uKYRrNcs,26490
+importlib_metadata/__pycache__/__init__.cpython-38.pyc,,
+importlib_metadata/__pycache__/_adapters.cpython-38.pyc,,
+importlib_metadata/__pycache__/_collections.cpython-38.pyc,,
+importlib_metadata/__pycache__/_compat.cpython-38.pyc,,
+importlib_metadata/__pycache__/_functools.cpython-38.pyc,,
+importlib_metadata/__pycache__/_itertools.cpython-38.pyc,,
+importlib_metadata/__pycache__/_meta.cpython-38.pyc,,
+importlib_metadata/__pycache__/_py39compat.cpython-38.pyc,,
+importlib_metadata/__pycache__/_text.cpython-38.pyc,,
+importlib_metadata/_adapters.py,sha256=i8S6Ib1OQjcILA-l4gkzktMZe18TaeUNI49PLRp6OBU,2454
+importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743
+importlib_metadata/_compat.py,sha256=9zOKf0eDgkCMnnaEhU5kQVxHd1P8BIYV7Stso7av5h8,1857
+importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895
+importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068
+importlib_metadata/_meta.py,sha256=v5e1ZDG7yZTH3h7TjbS5bM5p8AGzMPVOu8skDMv4h6k,1165
+importlib_metadata/_py39compat.py,sha256=2Tk5twb_VgLCY-1NEAQjdZp_S9OFMC-pUzP2isuaPsQ,1098
+importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
+importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
--- /dev/null
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.38.4)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
--- /dev/null
+importlib_metadata
import posixpath
import collections
-from . import _adapters, _meta
+from . import _adapters, _meta, _py39compat
from ._collections import FreezableDefaultDict, Pair
from ._compat import (
NullFinder,
from importlib import import_module
from importlib.abc import MetaPathFinder
from itertools import starmap
-from typing import List, Mapping, Optional, Union
+from typing import List, Mapping, Optional
__all__ = [
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.",
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(
following the attr, and following any extras.
"""
+ name: str
+ value: str
+ group: str
+
dist: Optional['Distribution'] = None
def __init__(self, name, value, group):
@property
def extras(self):
match = self.pattern.match(self.value)
- return list(re.finditer(r'\w+', match.group('extras') or ''))
+ return re.findall(r'\w+', match.group('extras') or '')
def _for(self, dist):
vars(self).update(dist=dist)
return self
- def __iter__(self):
+ def matches(self, **params):
"""
- Supply iter so one may construct dicts of EntryPoints by name.
+ 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
"""
- msg = (
- "Construction of dict of EntryPoints is deprecated in "
- "favor of EntryPoints."
- )
- warnings.warn(msg, DeprecationWarning)
- return iter((self.name, self))
-
- def matches(self, **params):
attrs = (getattr(self, param) for param in params)
return all(map(operator.eq, params.values(), attrs))
return hash(self._key())
-class DeprecatedList(list):
- """
- Allow an otherwise immutable object to implement mutability
- for compatibility.
-
- >>> recwarn = getfixture('recwarn')
- >>> dl = DeprecatedList(range(3))
- >>> dl[0] = 1
- >>> dl.append(3)
- >>> del dl[3]
- >>> dl.reverse()
- >>> dl.sort()
- >>> dl.extend([4])
- >>> dl.pop(-1)
- 4
- >>> dl.remove(1)
- >>> dl += [5]
- >>> dl + [6]
- [1, 2, 5, 6]
- >>> dl + (6,)
- [1, 2, 5, 6]
- >>> dl.insert(0, 0)
- >>> dl
- [0, 1, 2, 5]
- >>> dl == [0, 1, 2, 5]
- True
- >>> dl == (0, 1, 2, 5)
- True
- >>> len(recwarn)
- 1
- """
-
- __slots__ = ()
-
- _warn = functools.partial(
- warnings.warn,
- "EntryPoints list interface is deprecated. Cast to list if needed.",
- DeprecationWarning,
- stacklevel=pypy_partial(2),
- )
-
- def _wrap_deprecated_method(method_name: str): # type: ignore
- def wrapped(self, *args, **kwargs):
- self._warn()
- return getattr(super(), method_name)(*args, **kwargs)
-
- return method_name, wrapped
-
- locals().update(
- map(
- _wrap_deprecated_method,
- '__setitem__ __delitem__ append reverse extend pop remove '
- '__iadd__ insert sort'.split(),
- )
- )
-
- def __add__(self, other):
- if not isinstance(other, tuple):
- self._warn()
- other = tuple(other)
- return self.__class__(tuple(self) + other)
-
- def __eq__(self, other):
- if not isinstance(other, tuple):
- self._warn()
- other = tuple(other)
-
- return tuple(self).__eq__(other)
-
-
-class EntryPoints(DeprecatedList):
+class EntryPoints(tuple):
"""
An immutable collection of selectable EntryPoint objects.
"""
"""
Get the EntryPoint in self matching name.
"""
- if isinstance(name, int):
- warnings.warn(
- "Accessing entry points by index is deprecated. "
- "Cast to tuple if needed.",
- DeprecationWarning,
- stacklevel=2,
- )
- return super().__getitem__(name)
try:
return next(iter(self.select(name=name)))
except StopIteration:
Select entry points from self that match the
given parameters (typically group and/or name).
"""
- return EntryPoints(ep for ep in self if ep.matches(**params))
+ return EntryPoints(ep for ep in self if _py39compat.ep_matches(ep, **params))
@property
def names(self):
def groups(self):
"""
Return the set of all groups of all entry points.
-
- For coverage while SelectableGroups is present.
- >>> EntryPoints().groups
- set()
"""
return {ep.group for ep in self}
)
-class Deprecated:
- """
- Compatibility add-in for mapping to indicate that
- mapping behavior is deprecated.
-
- >>> recwarn = getfixture('recwarn')
- >>> class DeprecatedDict(Deprecated, dict): pass
- >>> dd = DeprecatedDict(foo='bar')
- >>> dd.get('baz', None)
- >>> dd['foo']
- 'bar'
- >>> list(dd)
- ['foo']
- >>> list(dd.keys())
- ['foo']
- >>> 'foo' in dd
- True
- >>> list(dd.values())
- ['bar']
- >>> len(recwarn)
- 1
- """
-
- _warn = functools.partial(
- warnings.warn,
- "SelectableGroups dict interface is deprecated. Use select.",
- DeprecationWarning,
- stacklevel=pypy_partial(2),
- )
-
- def __getitem__(self, name):
- self._warn()
- return super().__getitem__(name)
-
- def get(self, name, default=None):
- self._warn()
- return super().get(name, default)
-
- def __iter__(self):
- self._warn()
- return super().__iter__()
-
- def __contains__(self, *args):
- self._warn()
- return super().__contains__(*args)
-
- def keys(self):
- self._warn()
- return super().keys()
-
- def values(self):
- self._warn()
- return super().values()
-
-
-class SelectableGroups(Deprecated, dict):
- """
- A backward- and forward-compatible result from
- entry_points that fully implements the dict interface.
- """
-
- @classmethod
- def load(cls, eps):
- by_group = operator.attrgetter('group')
- ordered = sorted(eps, key=by_group)
- grouped = itertools.groupby(ordered, by_group)
- return cls((group, EntryPoints(eps)) for group, eps in grouped)
-
- @property
- def _all(self):
- """
- Reconstruct a list of all entrypoints from the groups.
- """
- groups = super(Deprecated, self).values()
- return EntryPoints(itertools.chain.from_iterable(groups))
-
- @property
- def groups(self):
- return self._all.groups
-
- @property
- def names(self):
- """
- for coverage:
- >>> SelectableGroups().names
- set()
- """
- return self._all.names
-
- def select(self, **params):
- if not params:
- return self
- return self._all.select(**params)
-
-
class PackagePath(pathlib.PurePosixPath):
"""A reference to a path in a package"""
return f'<FileHash mode: {self.mode} value: {self.value}>'
-class Distribution:
+class Distribution(metaclass=abc.ABCMeta):
"""A Python distribution package."""
@abc.abstractmethod
"""
@classmethod
- def from_name(cls, name):
+ def from_name(cls, name: str):
"""Return the Distribution for the given package name.
:param name: The name of the distribution package to search for.
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.
"""
- for resolver in cls._discover_resolvers():
- dists = resolver(DistributionFinder.Context(name=name))
- dist = next(iter(dists), None)
- if dist is not None:
- return dist
- else:
+ if not name:
+ raise ValueError("A distribution name is required.")
+ try:
+ return next(cls.discover(name=name))
+ except StopIteration:
raise PackageNotFoundError(name)
@classmethod
return super().__new__(cls)
def __init__(self, root):
- self.root = str(root)
+ self.root = root
def joinpath(self, child):
return pathlib.Path(self.root, child)
normalized name from the file system path.
"""
stem = os.path.basename(str(self._path))
- return self._name_from_stem(stem) or super()._normalized_name
+ return (
+ pass_none(Prepared.normalize)(self._name_from_stem(stem))
+ or super()._normalized_name
+ )
- def _name_from_stem(self, stem):
- name, ext = os.path.splitext(stem)
+ @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 = stem.partition('-')
+ name, sep, rest = filename.partition('-')
return name
return distribution(distribution_name).version
-def entry_points(**params) -> Union[EntryPoints, SelectableGroups]:
+_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()).
- For compatibility, returns ``SelectableGroups`` object unless
- selection parameters are supplied. In the future, this function
- will return ``EntryPoints`` instead of ``SelectableGroups``
- even when no selection parameters are supplied.
-
- For maximum future compatibility, pass selection parameters
- or invoke ``.select`` with parameters on the result.
-
- :return: EntryPoints or SelectableGroups for all installed packages.
+ :return: EntryPoints for all installed packages.
"""
- norm_name = operator.attrgetter('_normalized_name')
- unique = functools.partial(unique_everseen, key=norm_name)
eps = itertools.chain.from_iterable(
- dist.entry_points for dist in unique(distributions())
+ dist.entry_points for dist in _unique(distributions())
)
- return SelectableGroups.load(eps).select(**params)
+ return EntryPoints(eps).select(**params)
def files(distribution_name):
+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):
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"
try:
from typing import Protocol
except ImportError: # pragma: no cover
+ # Python 3.7 compatibility
from ..typing_extensions import Protocol # type: ignore
"""
-class SimplePath(Protocol):
+class SimplePath(Protocol[_T]):
"""
A minimal subset of pathlib.Path required by PathDistribution.
"""
- def joinpath(self) -> 'SimplePath':
+ def joinpath(self) -> _T:
... # pragma: no cover
- def __truediv__(self) -> 'SimplePath':
+ def __truediv__(self, other: Union[str, _T]) -> _T:
... # pragma: no cover
- def parent(self) -> 'SimplePath':
+ @property
+ def parent(self) -> _T:
... # pragma: no cover
def read_text(self) -> str:
--- /dev/null
+"""
+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)
--- /dev/null
+
+ 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.
--- /dev/null
+Metadata-Version: 2.1
+Name: importlib-resources
+Version: 5.10.2
+Summary: Read resources from Python packages
+Home-page: https://github.com/python/importlib_resources
+Author: Barry Warsaw
+Author-email: barry@python.org
+Project-URL: Documentation, https://importlib-resources.readthedocs.io/
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.7
+License-File: LICENSE
+Requires-Dist: zipp (>=3.1.0) ; python_version < "3.10"
+Provides-Extra: docs
+Requires-Dist: sphinx (>=3.5) ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Requires-Dist: furo ; extra == 'docs'
+Requires-Dist: sphinx-lint ; extra == 'docs'
+Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: flake8 (<5) ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.3) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-flake8 ; (python_version < "3.12") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/importlib_resources.svg
+ :target: https://pypi.org/project/importlib_resources
+
+.. image:: https://img.shields.io/pypi/pyversions/importlib_resources.svg
+
+.. image:: https://github.com/python/importlib_resources/workflows/tests/badge.svg
+ :target: https://github.com/python/importlib_resources/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. 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-resources/badge/?version=latest
+ :target: https://importlib-resources.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2022-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/importlib-resources
+ :target: https://tidelift.com/subscription/pkg/pypi-importlib-resources?utm_source=pypi-importlib-resources&utm_medium=readme
+
+``importlib_resources`` is a backport of Python standard library
+`importlib.resources
+<https://docs.python.org/3/library/importlib.html#module-importlib.resources>`_
+module for older Pythons.
+
+The key goal of this module is to replace parts of `pkg_resources
+<https://setuptools.readthedocs.io/en/latest/pkg_resources.html>`_ with a
+solution in Python's stdlib that relies on well-defined APIs. This makes
+reading resources included in packages easier, with more stable and consistent
+semantics.
+
+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_resources
+ - stdlib
+ * - 5.9
+ - 3.12
+ * - 5.7
+ - 3.11
+ * - 5.0
+ - 3.10
+ * - 1.3
+ - 3.9
+ * - 0.5 (?)
+ - 3.7
+
+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-resources?utm_source=pypi-importlib-resources&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.
--- /dev/null
+importlib_resources-5.10.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+importlib_resources-5.10.2.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
+importlib_resources-5.10.2.dist-info/METADATA,sha256=Xo5ntATvDYUxdmW8tr8kxtfdiOC9889mOk-LE1LtZfI,4111
+importlib_resources-5.10.2.dist-info/RECORD,,
+importlib_resources-5.10.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources-5.10.2.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
+importlib_resources-5.10.2.dist-info/top_level.txt,sha256=fHIjHU1GZwAjvcydpmUnUrTnbvdiWjG4OEVZK8by0TQ,20
+importlib_resources/__init__.py,sha256=evPm12kLgYqTm-pbzm60bOuumumT8IpBNWFp0uMyrzE,506
+importlib_resources/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/__pycache__/_adapters.cpython-38.pyc,,
+importlib_resources/__pycache__/_common.cpython-38.pyc,,
+importlib_resources/__pycache__/_compat.cpython-38.pyc,,
+importlib_resources/__pycache__/_itertools.cpython-38.pyc,,
+importlib_resources/__pycache__/_legacy.cpython-38.pyc,,
+importlib_resources/__pycache__/abc.cpython-38.pyc,,
+importlib_resources/__pycache__/readers.cpython-38.pyc,,
+importlib_resources/__pycache__/simple.cpython-38.pyc,,
+importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504
+importlib_resources/_common.py,sha256=jSC4xfLdcMNbtbWHtpzbFkNa0W7kvf__nsYn14C_AEU,5457
+importlib_resources/_compat.py,sha256=dSadF6WPt8MwOqSm_NIOQPhw4x0iaMYTWxi-XS93p7M,2923
+importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884
+importlib_resources/_legacy.py,sha256=0TKdZixxLWA-xwtAZw4HcpqJmj4Xprx1Zkcty0gTRZY,3481
+importlib_resources/abc.py,sha256=Icr2IJ2QtH7vvAB9vC5WRJ9KBoaDyJa7KUs8McuROzo,5140
+importlib_resources/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/readers.py,sha256=PZsi5qacr2Qn3KHw4qw3Gm1MzrBblPHoTdjqjH7EKWw,3581
+importlib_resources/simple.py,sha256=0__2TQBTQoqkajYmNPt1HxERcReAT6boVKJA328pr04,2576
+importlib_resources/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/_compat.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/_path.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_contents.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_files.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_open.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_path.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_read.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_reader.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/test_resource.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/update-zips.cpython-38.pyc,,
+importlib_resources/tests/__pycache__/util.cpython-38.pyc,,
+importlib_resources/tests/_compat.py,sha256=YTSB0U1R9oADnh6GrQcOCgojxcF_N6H1LklymEWf9SQ,708
+importlib_resources/tests/_path.py,sha256=yZyWsQzJZQ1Z8ARAxWkjAdaVVsjlzyqxO0qjBUofJ8M,1039
+importlib_resources/tests/data01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data01/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/data01/subdirectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data01/subdirectory/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/data01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44
+importlib_resources/tests/data01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20
+importlib_resources/tests/data02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data02/one/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/one/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data02/one/resource1.txt,sha256=10flKac7c-XXFzJ3t-AB5MJjlBy__dSZvPE_dOm2q6U,13
+importlib_resources/tests/data02/two/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/two/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/data02/two/resource2.txt,sha256=lt2jbN3TMn9QiFKM832X39bU_62UptDdUkoYzkvEbl0,13
+importlib_resources/tests/namespacedata01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/namespacedata01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44
+importlib_resources/tests/namespacedata01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20
+importlib_resources/tests/test_compatibilty_files.py,sha256=NWkbIsylI8Wz3Dwsxo1quT4ZI6ToXFA2mojCG6Dzuxw,3260
+importlib_resources/tests/test_contents.py,sha256=V1Xfk3lqTDdvUsZuV18Kndf0CT_tkM2oEIwk9Vv0rhg,968
+importlib_resources/tests/test_files.py,sha256=1Y8da-g0xOQLzuREDYUiRc_qhWlvFNeydW_mUH7l15w,3251
+importlib_resources/tests/test_open.py,sha256=pmEgdrSFdM83L6FxtR8U_RT9BfI3JZ4snGmM_ZZIegY,2565
+importlib_resources/tests/test_path.py,sha256=xvPteNA-UKavDhKgLgrQuXSxKWYH7Q4nSNDVfBX95Gs,2103
+importlib_resources/tests/test_read.py,sha256=EyYvpHJ_7F4LuX2EU_c5EerIBQfRhOFmiIR7LOc5Y5E,2408
+importlib_resources/tests/test_reader.py,sha256=nPhldbYPq3fXoQs0ZAub4atjhp2lgNyLNv2G1pg6Agw,4480
+importlib_resources/tests/test_resource.py,sha256=EMoarxTEHcrq8R41LQDsndIG8Idtm4I_LpN8DYpHtT0,8478
+importlib_resources/tests/update-zips.py,sha256=x-SrO5v87iLLUMXyefxDwAd3imAs_slI94sLWvJ6N40,1417
+importlib_resources/tests/util.py,sha256=ARAlxZ47wC-lgR7PGlmgBoi4HnhzcykD5Is2-TAwY0I,4873
+importlib_resources/tests/zipdata01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/zipdata01/ziptestdata.zip,sha256=z5Of4dsv3T0t-46B0MsVhxlhsPGMz28aUhJDWpj3_oY,876
+importlib_resources/tests/zipdata02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-38.pyc,,
+importlib_resources/tests/zipdata02/ziptestdata.zip,sha256=ydI-_j-xgQ7tDxqBp9cjOqXBGxUp6ZBbwVJu6Xj-nrY,698
--- /dev/null
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.38.4)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
--- /dev/null
+importlib_resources
+++ /dev/null
-Copyright 2017-2019 Brett Cannon, Barry Warsaw
-
-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.
+++ /dev/null
-Metadata-Version: 2.1
-Name: importlib-resources
-Version: 5.4.0
-Summary: Read resources from Python packages
-Home-page: https://github.com/python/importlib_resources
-Author: Barry Warsaw
-Author-email: barry@python.org
-License: UNKNOWN
-Project-URL: Documentation, https://importlib-resources.readthedocs.io/
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Requires-Python: >=3.6
-License-File: LICENSE
-Requires-Dist: zipp (>=3.1.0) ; python_version < "3.10"
-Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
-Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
-Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
-Provides-Extra: testing
-Requires-Dist: pytest (>=6) ; extra == 'testing'
-Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
-Requires-Dist: pytest-flake8 ; extra == 'testing'
-Requires-Dist: pytest-cov ; extra == 'testing'
-Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
-Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
-Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
-
-.. image:: https://img.shields.io/pypi/v/importlib_resources.svg
- :target: `PyPI link`_
-
-.. image:: https://img.shields.io/pypi/pyversions/importlib_resources.svg
- :target: `PyPI link`_
-
-.. _PyPI link: https://pypi.org/project/importlib_resources
-
-.. image:: https://github.com/python/importlib_resources/workflows/tests/badge.svg
- :target: https://github.com/python/importlib_resources/actions?query=workflow%3A%22tests%22
- :alt: tests
-
-.. 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-resources/badge/?version=latest
- :target: https://importlib-resources.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://img.shields.io/badge/skeleton-2021-informational
- :target: https://blog.jaraco.com/skeleton
-
-``importlib_resources`` is a backport of Python standard library
-`importlib.resources
-<https://docs.python.org/3/library/importlib.html#module-importlib.resources>`_
-module for older Pythons.
-
-The key goal of this module is to replace parts of `pkg_resources
-<https://setuptools.readthedocs.io/en/latest/pkg_resources.html>`_ with a
-solution in Python's stdlib that relies on well-defined APIs. This makes
-reading resources included in packages easier, with more stable and consistent
-semantics.
-
-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_resources
- - stdlib
- * - 5.2
- - 3.11
- * - 5.0
- - 3.10
- * - 1.3
- - 3.9
- * - 0.5 (?)
- - 3.7
-
-
+++ /dev/null
-importlib_resources-5.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4\r
-importlib_resources-5.4.0.dist-info/LICENSE,sha256=uWRjFdYGataJX2ziXk048ItUglQmjng3GWBALaWA36U,568\r
-importlib_resources-5.4.0.dist-info/METADATA,sha256=i5jH25IbM0Ls6u6UzSSCOa0c8hpDvePxqgnQwh2T5Io,3135\r
-importlib_resources-5.4.0.dist-info/RECORD,,\r
-importlib_resources-5.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources-5.4.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92\r
-importlib_resources-5.4.0.dist-info/top_level.txt,sha256=fHIjHU1GZwAjvcydpmUnUrTnbvdiWjG4OEVZK8by0TQ,20\r
-importlib_resources/__init__.py,sha256=zuA0lbRgtVVCcAztM0z5LuBiOCV9L_3qtI6mW2p5xAg,525\r
-importlib_resources/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_adapters.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_common.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_compat.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_itertools.cpython-311.pyc,,\r
-importlib_resources/__pycache__/_legacy.cpython-311.pyc,,\r
-importlib_resources/__pycache__/abc.cpython-311.pyc,,\r
-importlib_resources/__pycache__/readers.cpython-311.pyc,,\r
-importlib_resources/__pycache__/simple.cpython-311.pyc,,\r
-importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504\r
-importlib_resources/_common.py,sha256=iIxAaQhotSh6TLLUEfL_ynU2fzEeyHMz9JcL46mUhLg,2741\r
-importlib_resources/_compat.py,sha256=3LpkIfeN9x4oXjRea5TxZP5VYhPlzuVRhGe-hEv-S0s,2704\r
-importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884\r
-importlib_resources/_legacy.py,sha256=TMLkx6aEM6U8xIREPXqGZrMbUhTiPUuPl6ESD7RdYj4,3494\r
-importlib_resources/abc.py,sha256=MvTJJXajbl74s36Gyeesf76egtbFnh-TMtzQMVhFWXo,3886\r
-importlib_resources/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/readers.py,sha256=_9QLGQ5AzrED3PY8S2Zf8V6yLR0-nqqYqtQmgleDJzY,3566\r
-importlib_resources/simple.py,sha256=xt0qhXbwt3bZ86zuaaKbTiE9A0mDbwu0saRjUq_pcY0,2836\r
-importlib_resources/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/_compat.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_contents.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_files.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_open.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_path.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_read.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_reader.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/test_resource.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/update-zips.cpython-311.pyc,,\r
-importlib_resources/tests/__pycache__/util.cpython-311.pyc,,\r
-importlib_resources/tests/_compat.py,sha256=QGI_4p0DXybypoYvw0kr3jfQqvls3p8u4wy4Wvf0Z_o,435\r
-importlib_resources/tests/data01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data01/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
-importlib_resources/tests/data01/subdirectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data01/subdirectory/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
-importlib_resources/tests/data01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44\r
-importlib_resources/tests/data01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20\r
-importlib_resources/tests/data02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data02/one/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/one/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data02/one/resource1.txt,sha256=10flKac7c-XXFzJ3t-AB5MJjlBy__dSZvPE_dOm2q6U,13\r
-importlib_resources/tests/data02/two/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/data02/two/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/data02/two/resource2.txt,sha256=lt2jbN3TMn9QiFKM832X39bU_62UptDdUkoYzkvEbl0,13\r
-importlib_resources/tests/namespacedata01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4\r
-importlib_resources/tests/namespacedata01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44\r
-importlib_resources/tests/namespacedata01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20\r
-importlib_resources/tests/test_compatibilty_files.py,sha256=NWkbIsylI8Wz3Dwsxo1quT4ZI6ToXFA2mojCG6Dzuxw,3260\r
-importlib_resources/tests/test_contents.py,sha256=V1Xfk3lqTDdvUsZuV18Kndf0CT_tkM2oEIwk9Vv0rhg,968\r
-importlib_resources/tests/test_files.py,sha256=1Nqv6VM_MjfwrmtXYL1a1CMT0QhCxi3hNMqwXlfMQTg,1184\r
-importlib_resources/tests/test_open.py,sha256=pmEgdrSFdM83L6FxtR8U_RT9BfI3JZ4snGmM_ZZIegY,2565\r
-importlib_resources/tests/test_path.py,sha256=xvPteNA-UKavDhKgLgrQuXSxKWYH7Q4nSNDVfBX95Gs,2103\r
-importlib_resources/tests/test_read.py,sha256=EyYvpHJ_7F4LuX2EU_c5EerIBQfRhOFmiIR7LOc5Y5E,2408\r
-importlib_resources/tests/test_reader.py,sha256=hgXHquqAEnioemv20ZZcDlVaiOrcZKADO37_FkiQ00Y,4286\r
-importlib_resources/tests/test_resource.py,sha256=DqfLNc9kaN5obqxU8kn0sRUWMf9MygagrpfMV5-QfWg,8145\r
-importlib_resources/tests/update-zips.py,sha256=x3iJVqWnMM5qp4Oob2Pl3o6Yi03sUjEv_5Wf-UCg3ps,1415\r
-importlib_resources/tests/util.py,sha256=X1j-0C96pu3_tmtJuLhzfBfcfMenOphDLkxtCt5j7t4,5309\r
-importlib_resources/tests/zipdata01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/zipdata01/ziptestdata.zip,sha256=z5Of4dsv3T0t-46B0MsVhxlhsPGMz28aUhJDWpj3_oY,876\r
-importlib_resources/tests/zipdata02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0\r
-importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-311.pyc,,\r
-importlib_resources/tests/zipdata02/ziptestdata.zip,sha256=ydI-_j-xgQ7tDxqBp9cjOqXBGxUp6ZBbwVJu6Xj-nrY,698\r
+++ /dev/null
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
+++ /dev/null
-importlib_resources
import contextlib
import types
import importlib
+import inspect
+import warnings
+import itertools
-from typing import Union, Optional
+from typing import Union, Optional, cast
from .abc import ResourceReader, Traversable
from ._compat import wrap_spec
Package = Union[types.ModuleType, str]
+Anchor = Package
-def files(package):
- # type: (Package) -> Traversable
+def package_to_anchor(func):
"""
- Get a Traversable resource from a package
+ Replace 'package' parameter as 'anchor' and warn about the change.
+
+ Other errors should fall through.
+
+ >>> files('a', 'b')
+ Traceback (most recent call last):
+ TypeError: files() takes from 0 to 1 positional arguments but 2 were given
+ """
+ undefined = object()
+
+ @functools.wraps(func)
+ def wrapper(anchor=undefined, package=undefined):
+ if package is not undefined:
+ if anchor is not undefined:
+ return func(anchor, package)
+ warnings.warn(
+ "First parameter to files is renamed to 'anchor'",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return func(package)
+ elif anchor is undefined:
+ return func()
+ return func(anchor)
+
+ return wrapper
+
+
+@package_to_anchor
+def files(anchor: Optional[Anchor] = None) -> Traversable:
+ """
+ Get a Traversable resource for an anchor.
"""
- return from_package(get_package(package))
+ return from_package(resolve(anchor))
-def get_resource_reader(package):
- # type: (types.ModuleType) -> Optional[ResourceReader]
+def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]:
"""
Return the package's loader if it's a ResourceReader.
"""
return reader(spec.name) # type: ignore
-def resolve(cand):
- # type: (Package) -> types.ModuleType
- return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand)
+@functools.singledispatch
+def resolve(cand: Optional[Anchor]) -> types.ModuleType:
+ return cast(types.ModuleType, cand)
+
+
+@resolve.register
+def _(cand: str) -> types.ModuleType:
+ return importlib.import_module(cand)
+
+@resolve.register
+def _(cand: None) -> types.ModuleType:
+ return resolve(_infer_caller().f_globals['__name__'])
-def get_package(package):
- # type: (Package) -> types.ModuleType
- """Take a package name or module object and return the module.
- Raise an exception if the resolved module is not a package.
+def _infer_caller():
"""
- resolved = resolve(package)
- if wrap_spec(resolved).submodule_search_locations is None:
- raise TypeError(f'{package!r} is not a package')
- return resolved
+ Walk the stack and find the frame of the first caller not in this module.
+ """
+
+ def is_this_file(frame_info):
+ return frame_info.filename == __file__
+
+ def is_wrapper(frame_info):
+ return frame_info.function == 'wrapper'
+
+ not_this_file = itertools.filterfalse(is_this_file, inspect.stack())
+ # also exclude 'wrapper' due to singledispatch in the call stack
+ callers = itertools.filterfalse(is_wrapper, not_this_file)
+ return next(callers).frame
-def from_package(package):
+def from_package(package: types.ModuleType):
"""
Return a Traversable object for the given package.
@contextlib.contextmanager
-def _tempfile(reader, suffix=''):
+def _tempfile(
+ reader,
+ suffix='',
+ # gh-93353: Keep a reference to call os.remove() in late Python
+ # finalization.
+ *,
+ _os_remove=os.remove,
+):
# Not using tempfile.NamedTemporaryFile as it leads to deeper 'try'
# blocks due to the need to close the temporary file to work on Windows
# properly.
yield pathlib.Path(raw_path)
finally:
try:
- os.remove(raw_path)
+ _os_remove(raw_path)
except FileNotFoundError:
pass
+def _temp_file(path):
+ return _tempfile(path.read_bytes, suffix=path.name)
+
+
+def _is_present_dir(path: Traversable) -> bool:
+ """
+ Some Traversables implement ``is_dir()`` to raise an
+ exception (i.e. ``FileNotFoundError``) when the
+ directory doesn't exist. This function wraps that call
+ to always return a boolean and only return True
+ if there's a dir and it exists.
+ """
+ with contextlib.suppress(FileNotFoundError):
+ return path.is_dir()
+ return False
+
+
@functools.singledispatch
def as_file(path):
"""
Given a Traversable object, return that object as a
path on the local file system in a context manager.
"""
- return _tempfile(path.read_bytes, suffix=path.name)
+ return _temp_dir(path) if _is_present_dir(path) else _temp_file(path)
@as_file.register(pathlib.Path)
Degenerate behavior for pathlib.Path objects.
"""
yield path
+
+
+@contextlib.contextmanager
+def _temp_path(dir: tempfile.TemporaryDirectory):
+ """
+ Wrap tempfile.TemporyDirectory to return a pathlib object.
+ """
+ with dir as result:
+ yield pathlib.Path(result)
+
+
+@contextlib.contextmanager
+def _temp_dir(path):
+ """
+ Given a traversable dir, recursively replicate the whole tree
+ to the file system in a context manager.
+ """
+ assert path.is_dir()
+ with _temp_path(tempfile.TemporaryDirectory()) as temp_dir:
+ yield _write_contents(temp_dir, path)
+
+
+def _write_contents(target, source):
+ child = target.joinpath(source.name)
+ if source.is_dir():
+ child.mkdir()
+ for item in source.iterdir():
+ _write_contents(child, item)
+ else:
+ child.write_bytes(source.read_bytes())
+ return child
# flake8: noqa
import abc
+import os
import sys
import pathlib
from contextlib import suppress
+from typing import Union
+
if sys.version_info >= (3, 10):
from zipfile import Path as ZipPath # type: ignore
from . import _adapters
return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)
+
+
+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]"]
return wrapper
-def normalize_path(path):
- # type: (Any) -> str
+def normalize_path(path: Any) -> str:
"""Normalize a path by ensuring it is a string.
If the resulting string contains path separators, an exception is raised.
import abc
-from typing import BinaryIO, Iterable, Text
+import io
+import itertools
+import pathlib
+from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional
-from ._compat import runtime_checkable, Protocol
+from ._compat import runtime_checkable, Protocol, StrPath
+
+
+__all__ = ["ResourceReader", "Traversable", "TraversableResources"]
class ResourceReader(metaclass=abc.ABCMeta):
raise FileNotFoundError
+class TraversalError(Exception):
+ pass
+
+
@runtime_checkable
class Traversable(Protocol):
"""
An object with a subset of pathlib.Path methods suitable for
traversing directories and opening files.
+
+ Any exceptions that occur when accessing the backing resource
+ may propagate unaltered.
"""
@abc.abstractmethod
- def iterdir(self):
+ def iterdir(self) -> Iterator["Traversable"]:
"""
Yield Traversable objects in self
"""
- def read_bytes(self):
+ def read_bytes(self) -> bytes:
"""
Read contents of self as bytes
"""
with self.open('rb') as strm:
return strm.read()
- def read_text(self, encoding=None):
+ def read_text(self, encoding: Optional[str] = None) -> str:
"""
Read contents of self as text
"""
Return True if self is a file
"""
- @abc.abstractmethod
- def joinpath(self, child):
+ def joinpath(self, *descendants: StrPath) -> "Traversable":
"""
- Return Traversable child in self
+ Return Traversable resolved with any descendants applied.
+
+ Each descendant should be a path segment relative to self
+ and each may contain multiple levels separated by
+ ``posixpath.sep`` (``/``).
"""
+ if not descendants:
+ return self
+ names = itertools.chain.from_iterable(
+ path.parts for path in map(pathlib.PurePosixPath, descendants)
+ )
+ target = next(names)
+ matches = (
+ traversable for traversable in self.iterdir() if traversable.name == target
+ )
+ try:
+ match = next(matches)
+ except StopIteration:
+ raise TraversalError(
+ "Target not found during traversal.", target, list(names)
+ )
+ return match.joinpath(*names)
- def __truediv__(self, child):
+ def __truediv__(self, child: StrPath) -> "Traversable":
"""
Return Traversable child in self
"""
accepted by io.TextIOWrapper.
"""
- @abc.abstractproperty
+ @property
+ @abc.abstractmethod
def name(self) -> str:
"""
The base name of this object without any parent references.
"""
@abc.abstractmethod
- def files(self):
+ def files(self) -> "Traversable":
"""Return a Traversable object for the loaded package."""
- def open_resource(self, resource):
+ def open_resource(self, resource: StrPath) -> io.BufferedReader:
return self.files().joinpath(resource).open('rb')
- def resource_path(self, resource):
+ def resource_path(self, resource: Any) -> NoReturn:
raise FileNotFoundError(resource)
- def is_resource(self, path):
+ def is_resource(self, path: StrPath) -> bool:
return self.files().joinpath(path).is_file()
- def contents(self):
+ def contents(self) -> Iterator[str]:
return (item.name for item in self.files().iterdir())
def is_file(self):
return False
- def joinpath(self, child):
- # first try to find child in current paths
- for file in self.iterdir():
- if file.name == child:
- return file
- # if it does not exist, construct it with the first path
- return self._paths[0] / child
-
- __truediv__ = joinpath
+ def joinpath(self, *descendants):
+ try:
+ return super().joinpath(*descendants)
+ except abc.TraversalError:
+ # One of the paths did not resolve (a directory does not exist).
+ # Just return something that will not exist.
+ return self._paths[0].joinpath(*descendants)
def open(self, *args, **kwargs):
raise FileNotFoundError(f'{self} is not a file')
provider.
"""
- @abc.abstractproperty
- def package(self):
- # type: () -> str
+ @property
+ @abc.abstractmethod
+ def package(self) -> str:
"""
The name of the package for which this reader loads resources.
"""
@abc.abstractmethod
- def children(self):
- # type: () -> List['SimpleReader']
+ def children(self) -> List['SimpleReader']:
"""
Obtain an iterable of SimpleReader for available
child containers (e.g. directories).
"""
@abc.abstractmethod
- def resources(self):
- # type: () -> List[str]
+ def resources(self) -> List[str]:
"""
Obtain available named resources for this virtual package.
"""
@abc.abstractmethod
- def open_binary(self, resource):
- # type: (str) -> BinaryIO
+ def open_binary(self, resource: str) -> BinaryIO:
"""
Obtain a File-like for a named resource.
"""
return self.package.split('.')[-1]
-class ResourceHandle(Traversable):
- """
- Handle to a named resource in a ResourceReader.
- """
-
- def __init__(self, parent, name):
- # type: (ResourceContainer, str) -> None
- self.parent = parent
- self.name = name # type: ignore
-
- def is_file(self):
- return True
-
- def is_dir(self):
- return False
-
- def open(self, mode='r', *args, **kwargs):
- stream = self.parent.reader.open_binary(self.name)
- if 'b' not in mode:
- stream = io.TextIOWrapper(*args, **kwargs)
- return stream
-
- def joinpath(self, name):
- raise RuntimeError("Cannot traverse into a resource")
-
-
class ResourceContainer(Traversable):
"""
Traversable container for a package's resources via its reader.
"""
- def __init__(self, reader):
- # type: (SimpleReader) -> None
+ def __init__(self, reader: SimpleReader):
self.reader = reader
def is_dir(self):
def open(self, *args, **kwargs):
raise IsADirectoryError()
+
+class ResourceHandle(Traversable):
+ """
+ Handle to a named resource in a ResourceReader.
+ """
+
+ def __init__(self, parent: ResourceContainer, name: str):
+ self.parent = parent
+ self.name = name # type: ignore
+
+ def is_file(self):
+ return True
+
+ def is_dir(self):
+ return False
+
+ def open(self, mode='r', *args, **kwargs):
+ stream = self.parent.reader.open_binary(self.name)
+ if 'b' not in mode:
+ stream = io.TextIOWrapper(*args, **kwargs)
+ return stream
+
def joinpath(self, name):
- return next(
- traversable for traversable in self.iterdir() if traversable.name == name
- )
+ raise RuntimeError("Cannot traverse into a resource")
class TraversableReader(TraversableResources, SimpleReader):
except ImportError:
# Python 3.9 and earlier
class import_helper: # type: ignore
- from test.support import modules_setup, modules_cleanup
+ from test.support import (
+ modules_setup,
+ modules_cleanup,
+ DirsOnSysPath,
+ CleanImport,
+ )
+
+
+try:
+ from test.support import os_helper # type: ignore
+except ImportError:
+ # Python 3.9 compat
+ class os_helper: # type:ignore
+ from test.support import temp_dir
try:
--- /dev/null
+import pathlib
+import functools
+
+
+####
+# from jaraco.path 3.4
+
+
+def build(spec, prefix=pathlib.Path()):
+ """
+ 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",
+ ... }
+ ... }
+ >>> tmpdir = getfixture('tmpdir')
+ >>> build(spec, tmpdir)
+ """
+ for name, contents in spec.items():
+ create(contents, pathlib.Path(prefix) / name)
+
+
+@functools.singledispatch
+def create(content, 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)
+
+
+# end from jaraco.path
+####
import typing
+import textwrap
import unittest
+import warnings
+import importlib
+import contextlib
import importlib_resources as resources
-from importlib_resources.abc import Traversable
+from ..abc import Traversable
from . import data01
from . import util
+from . import _path
+from ._compat import os_helper, import_helper
+
+
+@contextlib.contextmanager
+def suppress_known_deprecation():
+ with warnings.catch_warnings(record=True) as ctx:
+ warnings.simplefilter('default', category=DeprecationWarning)
+ yield ctx
class FilesTests:
def test_traversable(self):
assert isinstance(resources.files(self.data), Traversable)
+ def test_old_parameter(self):
+ """
+ Files used to take a 'package' parameter. Make sure anyone
+ passing by name is still supported.
+ """
+ with suppress_known_deprecation():
+ resources.files(package=self.data)
+
class OpenDiskTests(FilesTests, unittest.TestCase):
def setUp(self):
self.data = namespacedata01
+class SiteDir:
+ def setUp(self):
+ self.fixtures = contextlib.ExitStack()
+ self.addCleanup(self.fixtures.close)
+ self.site_dir = self.fixtures.enter_context(os_helper.temp_dir())
+ self.fixtures.enter_context(import_helper.DirsOnSysPath(self.site_dir))
+ self.fixtures.enter_context(import_helper.CleanImport())
+
+
+class ModulesFilesTests(SiteDir, unittest.TestCase):
+ def test_module_resources(self):
+ """
+ A module can have resources found adjacent to the module.
+ """
+ spec = {
+ 'mod.py': '',
+ 'res.txt': 'resources are the best',
+ }
+ _path.build(spec, self.site_dir)
+ import mod
+
+ actual = resources.files(mod).joinpath('res.txt').read_text()
+ assert actual == spec['res.txt']
+
+
+class ImplicitContextFilesTests(SiteDir, unittest.TestCase):
+ def test_implicit_files(self):
+ """
+ Without any parameter, files() will infer the location as the caller.
+ """
+ spec = {
+ 'somepkg': {
+ '__init__.py': textwrap.dedent(
+ """
+ import importlib_resources as res
+ val = res.files().joinpath('res.txt').read_text()
+ """
+ ),
+ 'res.txt': 'resources are the best',
+ },
+ }
+ _path.build(spec, self.site_dir)
+ assert importlib.import_module('somepkg').val == 'resources are the best'
+
+
if __name__ == '__main__':
unittest.main()
str(path.joinpath('imaginary'))[len(prefix) + 1 :],
os.path.join('namespacedata01', 'imaginary'),
)
+ self.assertEqual(path.joinpath(), path)
+
+ def test_join_path_compound(self):
+ path = MultiplexedPath(self.folder)
+ assert not path.joinpath('imaginary/foo.py').exists()
def test_repr(self):
self.assertEqual(
{'__init__.py', 'binary.file'},
)
+ def test_as_file_directory(self):
+ with resources.as_file(resources.files('ziptestdata')) as data:
+ assert data.name == 'ziptestdata'
+ assert data.is_dir()
+ assert data.joinpath('subdirectory').is_dir()
+ assert len(list(data.iterdir()))
+ assert not data.parent.exists()
+
class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase):
ZIP_MODULE = zipdata02 # type: ignore
def walk(datapath):
for dirpath, dirnames, filenames in os.walk(datapath):
- with contextlib.suppress(KeyError):
+ with contextlib.suppress(ValueError):
dirnames.remove('__pycache__')
for filename in filenames:
res = pathlib.Path(dirpath) / filename
import io
import sys
import types
-from pathlib import Path, PurePath
+import pathlib
from . import data01
from . import zipdata01
def test_pathlib_path(self):
# Passing in a pathlib.PurePath object for the path should succeed.
- path = PurePath('utf-8.file')
+ path = pathlib.PurePath('utf-8.file')
self.execute(data01, path)
def test_importing_module_as_side_effect(self):
del sys.modules[data01.__name__]
self.execute(data01.__name__, 'utf-8.file')
- def test_non_package_by_name(self):
- # The anchor package cannot be a module.
- with self.assertRaises(TypeError):
- self.execute(__name__, 'utf-8.file')
-
- def test_non_package_by_package(self):
- # The anchor package cannot be a module.
- with self.assertRaises(TypeError):
- module = sys.modules['importlib_resources.tests.util']
- self.execute(module, 'utf-8.file')
-
def test_missing_path(self):
# Attempting to open or read or request the path for a
# non-existent path should succeed if open_resource
@classmethod
def setUpClass(cls):
- data_path = Path(cls.ZIP_MODULE.__file__)
+ data_path = pathlib.Path(cls.ZIP_MODULE.__file__)
data_dir = data_path.parent
cls._zip_path = str(data_dir / 'ziptestdata.zip')
sys.path.append(cls._zip_path)
ordered-set==3.1.1
more_itertools==8.8.0
jaraco.text==3.7.0
-importlib_resources==5.4.0
-importlib_metadata==4.11.1
+importlib_resources==5.10.2
+importlib_metadata==6.0.0
# required for importlib_metadata on older Pythons
typing_extensions==4.0.1
# required for importlib_resources and _metadata on older Pythons