Initial public upload from Projekte/CodingAgent (part 2)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/INSTALLER +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/METADATA +67 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/RECORD +34 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/REQUESTED +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/WHEEL +6 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/entry_points.txt +5 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/licenses/LICENSE +23 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/top_level.txt +2 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/setuptools_ext.py +231 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/vengine_cpy.py +1058 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/vengine_gen.py +679 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/verifier.py +306 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/INSTALLER +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/METADATA +84 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/RECORD +24 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/REQUESTED +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/WHEEL +4 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/licenses/LICENSE.txt +28 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/__init__.py +126 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_compat.py +626 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_termui_impl.py +945 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_textwrap.py +188 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_utils.py +36 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_winconsole.py +297 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/core.py +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/decorators.py +575 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/exceptions.py +378 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/formatting.py +320 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/globals.py +67 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/parser.py +533 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/py.typed +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/shell_completion.py +705 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/termui.py +945 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/testing.py +772 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/types.py +1374 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/utils.py +653 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/INSTALLER +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/METADATA +110 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/RECORD +119 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/REQUESTED +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/WHEEL +5 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE +3 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.APACHE +202 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.BSD +27 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json +1365 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/sboms/sbom.json +43 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/__about__.py +17 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/__init__.py +13 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/exceptions.py +52 -0
.gitattributes
CHANGED
|
@@ -37,3 +37,4 @@ hai-mcp-pr7/.venv/bin/python filter=lfs diff=lfs merge=lfs -text
|
|
| 37 |
hai-mcp-pr7/.venv/bin/python3 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
hai-mcp-pr7/.venv/bin/python3.13 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
hai-mcp-pr7/.venv/lib/python3.13/site-packages/_cffi_backend.cpython-313-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 37 |
hai-mcp-pr7/.venv/bin/python3 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
hai-mcp-pr7/.venv/bin/python3.13 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
hai-mcp-pr7/.venv/lib/python3.13/site-packages/_cffi_backend.cpython-313-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust.abi3.so filter=lfs diff=lfs merge=lfs -text
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
uv
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: cffi
|
| 3 |
+
Version: 2.1.0
|
| 4 |
+
Summary: Foreign Function Interface for Python calling C code.
|
| 5 |
+
Author: Armin Rigo, Maciej Fijalkowski
|
| 6 |
+
Maintainer: Matt Davis, Matt Clay
|
| 7 |
+
License-Expression: MIT-0
|
| 8 |
+
Project-URL: Documentation, https://cffi.readthedocs.io/
|
| 9 |
+
Project-URL: Changelog, https://cffi.readthedocs.io/en/latest/whatsnew.html
|
| 10 |
+
Project-URL: Download, https://github.com/python-cffi/cffi/releases
|
| 11 |
+
Project-URL: Contact, https://groups.google.com/forum/#!forum/python-cffi
|
| 12 |
+
Project-URL: Source Code, https://github.com/python-cffi/cffi
|
| 13 |
+
Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues
|
| 14 |
+
Classifier: Programming Language :: Python
|
| 15 |
+
Classifier: Programming Language :: Python :: 3
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.14
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.15
|
| 22 |
+
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
|
| 23 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 24 |
+
Requires-Python: >=3.10
|
| 25 |
+
Description-Content-Type: text/markdown
|
| 26 |
+
License-File: LICENSE
|
| 27 |
+
Requires-Dist: pycparser; implementation_name != "PyPy"
|
| 28 |
+
Dynamic: license-file
|
| 29 |
+
|
| 30 |
+
[](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml?query=branch%3Amain++)
|
| 31 |
+
[](https://pypi.org/project/cffi)
|
| 32 |
+
[][Documentation]
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
CFFI
|
| 36 |
+
====
|
| 37 |
+
|
| 38 |
+
Foreign Function Interface for Python calling C code.
|
| 39 |
+
|
| 40 |
+
Please see the [Documentation] or uncompiled in the `doc/` subdirectory.
|
| 41 |
+
|
| 42 |
+
Download
|
| 43 |
+
--------
|
| 44 |
+
|
| 45 |
+
[Download page](https://github.com/python-cffi/cffi/releases)
|
| 46 |
+
|
| 47 |
+
Source Code
|
| 48 |
+
-----------
|
| 49 |
+
|
| 50 |
+
Source code is publicly available on
|
| 51 |
+
[GitHub](https://github.com/python-cffi/cffi).
|
| 52 |
+
|
| 53 |
+
Contact
|
| 54 |
+
-------
|
| 55 |
+
|
| 56 |
+
[Mailing list](https://groups.google.com/forum/#!forum/python-cffi)
|
| 57 |
+
|
| 58 |
+
Testing/development tips
|
| 59 |
+
------------------------
|
| 60 |
+
|
| 61 |
+
After `git clone` or `wget && tar`, we will get a directory called `cffi` or `cffi-x.x.x`. We call it `repo-directory`. To run tests under CPython, run the following in the `repo-directory`:
|
| 62 |
+
|
| 63 |
+
pip install pytest
|
| 64 |
+
pip install -e . # editable install of CFFI for local development
|
| 65 |
+
pytest src/c/ testing/
|
| 66 |
+
|
| 67 |
+
[Documentation]: http://cffi.readthedocs.org/
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/RECORD
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/cffi-gen-src,sha256=gQBF1M_k--4W_dAA3BnQHxYJqobd0Pg1pv1Eo0h1e3Y,345
|
| 2 |
+
_cffi_backend.cpython-313-x86_64-linux-gnu.so,sha256=U1rIRJs4e7ftHp1aHLZzHvrgyAGCOA7x-6tuaWtLQE0,348752
|
| 3 |
+
cffi-2.1.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
| 4 |
+
cffi-2.1.0.dist-info/METADATA,sha256=o1B5ubtFvAVY5uayCy7qSbnrzi-HpyZLlVdKG3QbVhE,2527
|
| 5 |
+
cffi-2.1.0.dist-info/RECORD,,
|
| 6 |
+
cffi-2.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 7 |
+
cffi-2.1.0.dist-info/WHEEL,sha256=6ms3XnzBnat_sZzZrDidy-tujCypEPg5zKUj6j3hTbo,151
|
| 8 |
+
cffi-2.1.0.dist-info/entry_points.txt,sha256=prYQgBH44nBQ_lJpu6iMgFG0FGl4gNOwtvvjoDoLABw,132
|
| 9 |
+
cffi-2.1.0.dist-info/licenses/LICENSE,sha256=W6JN3FcGf5JJrdZEw6_EGl1tw34jQz73Wdld83Cwr2M,1123
|
| 10 |
+
cffi-2.1.0.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19
|
| 11 |
+
cffi/__init__.py,sha256=qJYerSqD7BKZYtDi_74wS0zFrr4PIQrunzJ82tLArc8,511
|
| 12 |
+
cffi/_cffi_errors.h,sha256=Wzg-0Bu1_K2GEK0pSU5b6Q2GAAudcaF87HNG8LoFmWw,3583
|
| 13 |
+
cffi/_cffi_gen_src.py,sha256=yUR53pFSbsa3ssF7_VOqXTaKeL5AJTHM_dyZ9IGY9sw,7044
|
| 14 |
+
cffi/_cffi_include.h,sha256=zY6KFoo_bpa02PagQs1hzeW9O56snioloyxqvRKAot8,15234
|
| 15 |
+
cffi/_embedding.h,sha256=pTcVPFV7Ng65hD68swN2ym70Gw7i842JDaa_FY9Tczg,17554
|
| 16 |
+
cffi/_imp_emulation.py,sha256=RxREG8zAbI2RPGBww90u_5fi8sWdahpdipOoPzkp7C0,2960
|
| 17 |
+
cffi/_shimmed_dist_utils.py,sha256=Bjj2wm8yZbvFvWEx5AEfmqaqZyZFhYfoyLLQHkXZuao,2230
|
| 18 |
+
cffi/api.py,sha256=cWX1ZlscmOyTkvthiBpUWLcSRBtqlpn-moBu4esBmzI,42013
|
| 19 |
+
cffi/backend_ctypes.py,sha256=T7CSc14P5vyloi5hyox68qhBWX079owHXaGDbY_U7bo,42283
|
| 20 |
+
cffi/cffi_opcode.py,sha256=5-1cU6ppHGzsstF_eoglD_8tCDk76_Zhkzj3fW4Sic0,5723
|
| 21 |
+
cffi/commontypes.py,sha256=7N6zPtCFlvxXMWhHV08psUjdYIK2XgsN3yo5dgua_v4,2805
|
| 22 |
+
cffi/cparser.py,sha256=WY-ox4UGSuVSYTtgm02FmGgbmcuaaBK3moFlkdFSmsU,44754
|
| 23 |
+
cffi/error.py,sha256=v6xTiS4U0kvDcy4h_BDRo5v39ZQuj-IMRYLv5ETddZs,877
|
| 24 |
+
cffi/ffiplatform.py,sha256=avxFjdikYGJoEtmJO7ewVmwG_VEVl6EZ_WaNhZYCqv4,3584
|
| 25 |
+
cffi/gen_src.py,sha256=TU_ZVDwoLnHPlal8_mPmpxqyWJ7U752jTc3UsTmE1QI,132
|
| 26 |
+
cffi/lock.py,sha256=l9TTdwMIMpi6jDkJGnQgE9cvTIR7CAntIJr8EGHt3pY,747
|
| 27 |
+
cffi/model.py,sha256=cA-bEAibcHUbtbqfgMq0CLQjFPAjV0JSRzMyKViNgF4,21752
|
| 28 |
+
cffi/parse_c_type.h,sha256=OdwQfwM9ktq6vlCB43exFQmxDBtj2MBNdK8LYl15tjw,5976
|
| 29 |
+
cffi/pkgconfig.py,sha256=uyt117TQeea3djagM1W5go8RpnySGTgoCzN8QHyZdlc,4365
|
| 30 |
+
cffi/recompiler.py,sha256=TJVF07mFzYGTV6szp2zPrJd2dJKHN-eB_SuiJiGl9Kw,65140
|
| 31 |
+
cffi/setuptools_ext.py,sha256=pZrnTqVCkGMqu8NxIxkdbItl65gehMP1LoraUdT7-4U,9566
|
| 32 |
+
cffi/vengine_cpy.py,sha256=rRouYQ6BllAIXNjZGTPUW54rB952qi3gcm16SWXWKLc,43030
|
| 33 |
+
cffi/vengine_gen.py,sha256=Z_KqaF6r54n5i4hjQThZdb-1DBTEimnU4y4E_KxSCDg,26931
|
| 34 |
+
cffi/verifier.py,sha256=xIzV8iDMhZ1-qhX3RRX4DWo2k07WpcJxdhhAFXAjrmY,11154
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/REQUESTED
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (83.0.0)
|
| 3 |
+
Root-Is-Purelib: false
|
| 4 |
+
Tag: cp313-cp313-manylinux_2_17_x86_64
|
| 5 |
+
Tag: cp313-cp313-manylinux2014_x86_64
|
| 6 |
+
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
cffi-gen-src = cffi._cffi_gen_src:run
|
| 3 |
+
|
| 4 |
+
[distutils.setup_keywords]
|
| 5 |
+
cffi_modules = cffi.setuptools_ext:cffi_modules
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Except when otherwise stated (look for LICENSE files in directories or
|
| 3 |
+
information at the beginning of each file) all software and
|
| 4 |
+
documentation is licensed as follows:
|
| 5 |
+
|
| 6 |
+
MIT No Attribution
|
| 7 |
+
|
| 8 |
+
Permission is hereby granted, free of charge, to any person
|
| 9 |
+
obtaining a copy of this software and associated documentation
|
| 10 |
+
files (the "Software"), to deal in the Software without
|
| 11 |
+
restriction, including without limitation the rights to use,
|
| 12 |
+
copy, modify, merge, publish, distribute, sublicense, and/or
|
| 13 |
+
sell copies of the Software, and to permit persons to whom the
|
| 14 |
+
Software is furnished to do so.
|
| 15 |
+
|
| 16 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
| 17 |
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 18 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
| 19 |
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 20 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
| 21 |
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
| 22 |
+
DEALINGS IN THE SOFTWARE.
|
| 23 |
+
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_cffi_backend
|
| 2 |
+
cffi
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/setuptools_ext.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import sysconfig
|
| 4 |
+
|
| 5 |
+
try:
|
| 6 |
+
basestring
|
| 7 |
+
except NameError:
|
| 8 |
+
# Python 3.x
|
| 9 |
+
basestring = str
|
| 10 |
+
|
| 11 |
+
def error(msg):
|
| 12 |
+
from cffi._shimmed_dist_utils import DistutilsSetupError
|
| 13 |
+
raise DistutilsSetupError(msg)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def execfile(filename, glob):
|
| 17 |
+
# We use execfile() (here rewritten for Python 3) instead of
|
| 18 |
+
# __import__() to load the build script. The problem with
|
| 19 |
+
# a normal import is that in some packages, the intermediate
|
| 20 |
+
# __init__.py files may already try to import the file that
|
| 21 |
+
# we are generating.
|
| 22 |
+
with open(filename) as f:
|
| 23 |
+
src = f.read()
|
| 24 |
+
src += '\n' # Python 2.6 compatibility
|
| 25 |
+
code = compile(src, filename, 'exec')
|
| 26 |
+
exec(code, glob, glob)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def add_cffi_module(dist, mod_spec):
|
| 30 |
+
from cffi.api import FFI
|
| 31 |
+
|
| 32 |
+
if not isinstance(mod_spec, basestring):
|
| 33 |
+
error("argument to 'cffi_modules=...' must be a str or a list of str,"
|
| 34 |
+
" not %r" % (type(mod_spec).__name__,))
|
| 35 |
+
mod_spec = str(mod_spec)
|
| 36 |
+
try:
|
| 37 |
+
build_file_name, ffi_var_name = mod_spec.split(':')
|
| 38 |
+
except ValueError:
|
| 39 |
+
error("%r must be of the form 'path/build.py:ffi_variable'" %
|
| 40 |
+
(mod_spec,))
|
| 41 |
+
if not os.path.exists(build_file_name):
|
| 42 |
+
ext = ''
|
| 43 |
+
rewritten = build_file_name.replace('.', '/') + '.py'
|
| 44 |
+
if os.path.exists(rewritten):
|
| 45 |
+
ext = ' (rewrite cffi_modules to [%r])' % (
|
| 46 |
+
rewritten + ':' + ffi_var_name,)
|
| 47 |
+
error("%r does not name an existing file%s" % (build_file_name, ext))
|
| 48 |
+
|
| 49 |
+
mod_vars = {'__name__': '__cffi__', '__file__': build_file_name}
|
| 50 |
+
execfile(build_file_name, mod_vars)
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
ffi = mod_vars[ffi_var_name]
|
| 54 |
+
except KeyError:
|
| 55 |
+
error("%r: object %r not found in module" % (mod_spec,
|
| 56 |
+
ffi_var_name))
|
| 57 |
+
if not isinstance(ffi, FFI):
|
| 58 |
+
ffi = ffi() # maybe it's a function instead of directly an ffi
|
| 59 |
+
if not isinstance(ffi, FFI):
|
| 60 |
+
error("%r is not an FFI instance (got %r)" % (mod_spec,
|
| 61 |
+
type(ffi).__name__))
|
| 62 |
+
if not hasattr(ffi, '_assigned_source'):
|
| 63 |
+
error("%r: the set_source() method was not called" % (mod_spec,))
|
| 64 |
+
module_name, source, source_extension, kwds = ffi._assigned_source
|
| 65 |
+
if ffi._windows_unicode:
|
| 66 |
+
kwds = kwds.copy()
|
| 67 |
+
ffi._apply_windows_unicode(kwds)
|
| 68 |
+
|
| 69 |
+
if source is None:
|
| 70 |
+
_add_py_module(dist, ffi, module_name)
|
| 71 |
+
else:
|
| 72 |
+
_add_c_module(dist, ffi, module_name, source, source_extension, kwds)
|
| 73 |
+
|
| 74 |
+
def _set_py_limited_api(Extension, kwds):
|
| 75 |
+
"""
|
| 76 |
+
Add py_limited_api to kwds if setuptools >= 26 is in use.
|
| 77 |
+
Do not alter the setting if it already exists.
|
| 78 |
+
Setuptools takes care of ignoring the flag on Python 2 and PyPy.
|
| 79 |
+
|
| 80 |
+
CPython itself should ignore the flag in a debugging version
|
| 81 |
+
(by not listing .abi3.so in the extensions it supports), but
|
| 82 |
+
it doesn't so far, creating troubles. That's why we check
|
| 83 |
+
for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent
|
| 84 |
+
of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401)
|
| 85 |
+
|
| 86 |
+
On Windows, with CPython <= 3.4, it's better not to use py_limited_api
|
| 87 |
+
because virtualenv *still* doesn't copy PYTHON3.DLL on these versions.
|
| 88 |
+
Recently (2020) we started shipping only >= 3.5 wheels, though. So
|
| 89 |
+
we'll give it another try and set py_limited_api on Windows >= 3.5.
|
| 90 |
+
"""
|
| 91 |
+
from cffi._shimmed_dist_utils import log
|
| 92 |
+
from cffi import recompiler
|
| 93 |
+
|
| 94 |
+
if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount')
|
| 95 |
+
and recompiler.USE_LIMITED_API):
|
| 96 |
+
import setuptools
|
| 97 |
+
try:
|
| 98 |
+
setuptools_major_version = int(setuptools.__version__.partition('.')[0])
|
| 99 |
+
if setuptools_major_version >= 26:
|
| 100 |
+
kwds['py_limited_api'] = True
|
| 101 |
+
except ValueError: # certain development versions of setuptools
|
| 102 |
+
# If we don't know the version number of setuptools, we
|
| 103 |
+
# try to set 'py_limited_api' anyway. At worst, we get a
|
| 104 |
+
# warning.
|
| 105 |
+
kwds['py_limited_api'] = True
|
| 106 |
+
|
| 107 |
+
if sysconfig.get_config_var("Py_GIL_DISABLED"):
|
| 108 |
+
if sys.version_info < (3, 15):
|
| 109 |
+
if kwds.get('py_limited_api'):
|
| 110 |
+
log.info("Ignoring py_limited_api=True for free-threaded build.")
|
| 111 |
+
kwds['py_limited_api'] = False
|
| 112 |
+
else:
|
| 113 |
+
kwds.setdefault("define_macros", []).append(("Py_TARGET_ABI3T", "0x030f0000"))
|
| 114 |
+
|
| 115 |
+
if kwds.get('py_limited_api') is False:
|
| 116 |
+
# avoid setting Py_LIMITED_API if py_limited_api=False
|
| 117 |
+
# which _cffi_include.h does unless _CFFI_NO_LIMITED_API is defined
|
| 118 |
+
kwds.setdefault("define_macros", []).append(("_CFFI_NO_LIMITED_API", None))
|
| 119 |
+
return kwds
|
| 120 |
+
|
| 121 |
+
def _add_c_module(dist, ffi, module_name, source, source_extension, kwds):
|
| 122 |
+
# We are a setuptools extension. Need this build_ext for py_limited_api.
|
| 123 |
+
from setuptools.command.build_ext import build_ext
|
| 124 |
+
from cffi._shimmed_dist_utils import Extension, log, mkpath
|
| 125 |
+
from cffi import recompiler
|
| 126 |
+
|
| 127 |
+
allsources = ['$PLACEHOLDER']
|
| 128 |
+
allsources.extend(kwds.pop('sources', []))
|
| 129 |
+
kwds = _set_py_limited_api(Extension, kwds)
|
| 130 |
+
ext = Extension(name=module_name, sources=allsources, **kwds)
|
| 131 |
+
|
| 132 |
+
def make_mod(tmpdir, pre_run=None):
|
| 133 |
+
c_file = os.path.join(tmpdir, module_name + source_extension)
|
| 134 |
+
log.info("generating cffi module %r" % c_file)
|
| 135 |
+
mkpath(tmpdir)
|
| 136 |
+
# a setuptools-only, API-only hook: called with the "ext" and "ffi"
|
| 137 |
+
# arguments just before we turn the ffi into C code. To use it,
|
| 138 |
+
# subclass the 'distutils.command.build_ext.build_ext' class and
|
| 139 |
+
# add a method 'def pre_run(self, ext, ffi)'.
|
| 140 |
+
if pre_run is not None:
|
| 141 |
+
pre_run(ext, ffi)
|
| 142 |
+
updated = recompiler.make_c_source(ffi, module_name, source, c_file)
|
| 143 |
+
if not updated:
|
| 144 |
+
log.info("already up-to-date")
|
| 145 |
+
return c_file
|
| 146 |
+
|
| 147 |
+
if dist.ext_modules is None:
|
| 148 |
+
dist.ext_modules = []
|
| 149 |
+
dist.ext_modules.append(ext)
|
| 150 |
+
|
| 151 |
+
base_class = dist.cmdclass.get('build_ext', build_ext)
|
| 152 |
+
class build_ext_make_mod(base_class):
|
| 153 |
+
def run(self):
|
| 154 |
+
if ext.sources[0] == '$PLACEHOLDER':
|
| 155 |
+
pre_run = getattr(self, 'pre_run', None)
|
| 156 |
+
ext.sources[0] = make_mod(self.build_temp, pre_run)
|
| 157 |
+
base_class.run(self)
|
| 158 |
+
dist.cmdclass['build_ext'] = build_ext_make_mod
|
| 159 |
+
# NB. multiple runs here will create multiple 'build_ext_make_mod'
|
| 160 |
+
# classes. Even in this case the 'build_ext' command should be
|
| 161 |
+
# run once; but just in case, the logic above does nothing if
|
| 162 |
+
# called again.
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def _add_py_module(dist, ffi, module_name):
|
| 166 |
+
from setuptools.command.build_py import build_py
|
| 167 |
+
from setuptools.command.build_ext import build_ext
|
| 168 |
+
from cffi._shimmed_dist_utils import log, mkpath
|
| 169 |
+
from cffi import recompiler
|
| 170 |
+
|
| 171 |
+
def generate_mod(py_file):
|
| 172 |
+
log.info("generating cffi module %r" % py_file)
|
| 173 |
+
mkpath(os.path.dirname(py_file))
|
| 174 |
+
updated = recompiler.make_py_source(ffi, module_name, py_file)
|
| 175 |
+
if not updated:
|
| 176 |
+
log.info("already up-to-date")
|
| 177 |
+
|
| 178 |
+
base_class = dist.cmdclass.get('build_py', build_py)
|
| 179 |
+
class build_py_make_mod(base_class):
|
| 180 |
+
def run(self):
|
| 181 |
+
base_class.run(self)
|
| 182 |
+
module_path = module_name.split('.')
|
| 183 |
+
module_path[-1] += '.py'
|
| 184 |
+
generate_mod(os.path.join(self.build_lib, *module_path))
|
| 185 |
+
def get_source_files(self):
|
| 186 |
+
# This is called from 'setup.py sdist' only. Exclude
|
| 187 |
+
# the generate .py module in this case.
|
| 188 |
+
saved_py_modules = self.py_modules
|
| 189 |
+
try:
|
| 190 |
+
if saved_py_modules:
|
| 191 |
+
self.py_modules = [m for m in saved_py_modules
|
| 192 |
+
if m != module_name]
|
| 193 |
+
return base_class.get_source_files(self)
|
| 194 |
+
finally:
|
| 195 |
+
self.py_modules = saved_py_modules
|
| 196 |
+
dist.cmdclass['build_py'] = build_py_make_mod
|
| 197 |
+
|
| 198 |
+
# distutils and setuptools have no notion I could find of a
|
| 199 |
+
# generated python module. If we don't add module_name to
|
| 200 |
+
# dist.py_modules, then things mostly work but there are some
|
| 201 |
+
# combination of options (--root and --record) that will miss
|
| 202 |
+
# the module. So we add it here, which gives a few apparently
|
| 203 |
+
# harmless warnings about not finding the file outside the
|
| 204 |
+
# build directory.
|
| 205 |
+
# Then we need to hack more in get_source_files(); see above.
|
| 206 |
+
if dist.py_modules is None:
|
| 207 |
+
dist.py_modules = []
|
| 208 |
+
dist.py_modules.append(module_name)
|
| 209 |
+
|
| 210 |
+
# the following is only for "build_ext -i"
|
| 211 |
+
base_class_2 = dist.cmdclass.get('build_ext', build_ext)
|
| 212 |
+
class build_ext_make_mod(base_class_2):
|
| 213 |
+
def run(self):
|
| 214 |
+
base_class_2.run(self)
|
| 215 |
+
if self.inplace:
|
| 216 |
+
# from get_ext_fullpath() in distutils/command/build_ext.py
|
| 217 |
+
module_path = module_name.split('.')
|
| 218 |
+
package = '.'.join(module_path[:-1])
|
| 219 |
+
build_py = self.get_finalized_command('build_py')
|
| 220 |
+
package_dir = build_py.get_package_dir(package)
|
| 221 |
+
file_name = module_path[-1] + '.py'
|
| 222 |
+
generate_mod(os.path.join(package_dir, file_name))
|
| 223 |
+
dist.cmdclass['build_ext'] = build_ext_make_mod
|
| 224 |
+
|
| 225 |
+
def cffi_modules(dist, attr, value):
|
| 226 |
+
assert attr == 'cffi_modules'
|
| 227 |
+
if isinstance(value, basestring):
|
| 228 |
+
value = [value]
|
| 229 |
+
|
| 230 |
+
for cffi_module in value:
|
| 231 |
+
add_cffi_module(dist, cffi_module)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/vengine_cpy.py
ADDED
|
@@ -0,0 +1,1058 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# DEPRECATED: implementation for ffi.verify()
|
| 3 |
+
#
|
| 4 |
+
import sys
|
| 5 |
+
from . import model
|
| 6 |
+
from .error import VerificationError
|
| 7 |
+
from . import _imp_emulation as imp
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class VCPythonEngine:
|
| 11 |
+
_class_key = 'x'
|
| 12 |
+
_gen_python_module = True
|
| 13 |
+
|
| 14 |
+
def __init__(self, verifier):
|
| 15 |
+
self.verifier = verifier
|
| 16 |
+
self.ffi = verifier.ffi
|
| 17 |
+
self._struct_pending_verification = {}
|
| 18 |
+
self._types_of_builtin_functions = {}
|
| 19 |
+
|
| 20 |
+
def patch_extension_kwds(self, kwds):
|
| 21 |
+
pass
|
| 22 |
+
|
| 23 |
+
def find_module(self, module_name, path, so_suffixes):
|
| 24 |
+
try:
|
| 25 |
+
f, filename, descr = imp.find_module(module_name, path)
|
| 26 |
+
except ImportError:
|
| 27 |
+
return None
|
| 28 |
+
if f is not None:
|
| 29 |
+
f.close()
|
| 30 |
+
# Note that after a setuptools installation, there are both .py
|
| 31 |
+
# and .so files with the same basename. The code here relies on
|
| 32 |
+
# imp.find_module() locating the .so in priority.
|
| 33 |
+
if descr[0] not in so_suffixes:
|
| 34 |
+
return None
|
| 35 |
+
return filename
|
| 36 |
+
|
| 37 |
+
def collect_types(self):
|
| 38 |
+
self._typesdict = {}
|
| 39 |
+
self._generate("collecttype")
|
| 40 |
+
|
| 41 |
+
def _prnt(self, what=''):
|
| 42 |
+
self._f.write(what + '\n')
|
| 43 |
+
|
| 44 |
+
def _gettypenum(self, type):
|
| 45 |
+
# a KeyError here is a bug. please report it! :-)
|
| 46 |
+
return self._typesdict[type]
|
| 47 |
+
|
| 48 |
+
def _do_collect_type(self, tp):
|
| 49 |
+
if ((not isinstance(tp, model.PrimitiveType)
|
| 50 |
+
or tp.name == 'long double')
|
| 51 |
+
and tp not in self._typesdict):
|
| 52 |
+
num = len(self._typesdict)
|
| 53 |
+
self._typesdict[tp] = num
|
| 54 |
+
|
| 55 |
+
def write_source_to_f(self):
|
| 56 |
+
self.collect_types()
|
| 57 |
+
#
|
| 58 |
+
# The new module will have a _cffi_setup() function that receives
|
| 59 |
+
# objects from the ffi world, and that calls some setup code in
|
| 60 |
+
# the module. This setup code is split in several independent
|
| 61 |
+
# functions, e.g. one per constant. The functions are "chained"
|
| 62 |
+
# by ending in a tail call to each other.
|
| 63 |
+
#
|
| 64 |
+
# This is further split in two chained lists, depending on if we
|
| 65 |
+
# can do it at import-time or if we must wait for _cffi_setup() to
|
| 66 |
+
# provide us with the <ctype> objects. This is needed because we
|
| 67 |
+
# need the values of the enum constants in order to build the
|
| 68 |
+
# <ctype 'enum'> that we may have to pass to _cffi_setup().
|
| 69 |
+
#
|
| 70 |
+
# The following two 'chained_list_constants' items contains
|
| 71 |
+
# the head of these two chained lists, as a string that gives the
|
| 72 |
+
# call to do, if any.
|
| 73 |
+
self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)']
|
| 74 |
+
#
|
| 75 |
+
prnt = self._prnt
|
| 76 |
+
# first paste some standard set of lines that are mostly '#define'
|
| 77 |
+
prnt(cffimod_header)
|
| 78 |
+
prnt()
|
| 79 |
+
# then paste the C source given by the user, verbatim.
|
| 80 |
+
prnt(self.verifier.preamble)
|
| 81 |
+
prnt()
|
| 82 |
+
#
|
| 83 |
+
# call generate_cpy_xxx_decl(), for every xxx found from
|
| 84 |
+
# ffi._parser._declarations. This generates all the functions.
|
| 85 |
+
self._generate("decl")
|
| 86 |
+
#
|
| 87 |
+
# implement the function _cffi_setup_custom() as calling the
|
| 88 |
+
# head of the chained list.
|
| 89 |
+
self._generate_setup_custom()
|
| 90 |
+
prnt()
|
| 91 |
+
#
|
| 92 |
+
# produce the method table, including the entries for the
|
| 93 |
+
# generated Python->C function wrappers, which are done
|
| 94 |
+
# by generate_cpy_function_method().
|
| 95 |
+
prnt('static PyMethodDef _cffi_methods[] = {')
|
| 96 |
+
self._generate("method")
|
| 97 |
+
prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},')
|
| 98 |
+
prnt(' {NULL, NULL, 0, NULL} /* Sentinel */')
|
| 99 |
+
prnt('};')
|
| 100 |
+
prnt()
|
| 101 |
+
#
|
| 102 |
+
# standard init.
|
| 103 |
+
modname = self.verifier.get_module_name()
|
| 104 |
+
constants = self._chained_list_constants[False]
|
| 105 |
+
prnt('static struct PyModuleDef _cffi_module_def = {')
|
| 106 |
+
prnt(' PyModuleDef_HEAD_INIT,')
|
| 107 |
+
prnt(' "%s",' % modname)
|
| 108 |
+
prnt(' NULL,')
|
| 109 |
+
prnt(' -1,')
|
| 110 |
+
prnt(' _cffi_methods,')
|
| 111 |
+
prnt(' NULL, NULL, NULL, NULL')
|
| 112 |
+
prnt('};')
|
| 113 |
+
prnt()
|
| 114 |
+
prnt('PyMODINIT_FUNC')
|
| 115 |
+
prnt('PyInit_%s(void)' % modname)
|
| 116 |
+
prnt('{')
|
| 117 |
+
prnt(' PyObject *lib;')
|
| 118 |
+
prnt(' lib = PyModule_Create(&_cffi_module_def);')
|
| 119 |
+
prnt(' if (lib == NULL)')
|
| 120 |
+
prnt(' return NULL;')
|
| 121 |
+
prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,))
|
| 122 |
+
prnt(' Py_DECREF(lib);')
|
| 123 |
+
prnt(' return NULL;')
|
| 124 |
+
prnt(' }')
|
| 125 |
+
prnt('#if Py_GIL_DISABLED')
|
| 126 |
+
prnt(' PyUnstable_Module_SetGIL(lib, Py_MOD_GIL_NOT_USED);')
|
| 127 |
+
prnt('#endif')
|
| 128 |
+
prnt(' return lib;')
|
| 129 |
+
prnt('}')
|
| 130 |
+
prnt()
|
| 131 |
+
|
| 132 |
+
def load_library(self, flags=None):
|
| 133 |
+
# XXX review all usages of 'self' here!
|
| 134 |
+
# import it as a new extension module
|
| 135 |
+
imp.acquire_lock()
|
| 136 |
+
try:
|
| 137 |
+
if hasattr(sys, "getdlopenflags"):
|
| 138 |
+
previous_flags = sys.getdlopenflags()
|
| 139 |
+
try:
|
| 140 |
+
if hasattr(sys, "setdlopenflags") and flags is not None:
|
| 141 |
+
sys.setdlopenflags(flags)
|
| 142 |
+
module = imp.load_dynamic(self.verifier.get_module_name(),
|
| 143 |
+
self.verifier.modulefilename)
|
| 144 |
+
except ImportError as e:
|
| 145 |
+
error = "importing %r: %s" % (self.verifier.modulefilename, e)
|
| 146 |
+
raise VerificationError(error)
|
| 147 |
+
finally:
|
| 148 |
+
if hasattr(sys, "setdlopenflags"):
|
| 149 |
+
sys.setdlopenflags(previous_flags)
|
| 150 |
+
finally:
|
| 151 |
+
imp.release_lock()
|
| 152 |
+
#
|
| 153 |
+
# call loading_cpy_struct() to get the struct layout inferred by
|
| 154 |
+
# the C compiler
|
| 155 |
+
self._load(module, 'loading')
|
| 156 |
+
#
|
| 157 |
+
# the C code will need the <ctype> objects. Collect them in
|
| 158 |
+
# order in a list.
|
| 159 |
+
revmapping = dict([(value, key)
|
| 160 |
+
for (key, value) in self._typesdict.items()])
|
| 161 |
+
lst = [revmapping[i] for i in range(len(revmapping))]
|
| 162 |
+
lst = list(map(self.ffi._get_cached_btype, lst))
|
| 163 |
+
#
|
| 164 |
+
# build the FFILibrary class and instance and call _cffi_setup().
|
| 165 |
+
# this will set up some fields like '_cffi_types', and only then
|
| 166 |
+
# it will invoke the chained list of functions that will really
|
| 167 |
+
# build (notably) the constant objects, as <cdata> if they are
|
| 168 |
+
# pointers, and store them as attributes on the 'library' object.
|
| 169 |
+
class FFILibrary:
|
| 170 |
+
_cffi_python_module = module
|
| 171 |
+
_cffi_ffi = self.ffi
|
| 172 |
+
_cffi_dir = []
|
| 173 |
+
def __dir__(self):
|
| 174 |
+
return FFILibrary._cffi_dir + list(self.__dict__)
|
| 175 |
+
library = FFILibrary()
|
| 176 |
+
if module._cffi_setup(lst, VerificationError, library):
|
| 177 |
+
import warnings
|
| 178 |
+
warnings.warn("reimporting %r might overwrite older definitions"
|
| 179 |
+
% (self.verifier.get_module_name()))
|
| 180 |
+
#
|
| 181 |
+
# finally, call the loaded_cpy_xxx() functions. This will perform
|
| 182 |
+
# the final adjustments, like copying the Python->C wrapper
|
| 183 |
+
# functions from the module to the 'library' object, and setting
|
| 184 |
+
# up the FFILibrary class with properties for the global C variables.
|
| 185 |
+
self._load(module, 'loaded', library=library)
|
| 186 |
+
module._cffi_original_ffi = self.ffi
|
| 187 |
+
module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions
|
| 188 |
+
return library
|
| 189 |
+
|
| 190 |
+
def _get_declarations(self):
|
| 191 |
+
lst = [(key, tp) for (key, (tp, qual)) in
|
| 192 |
+
self.ffi._parser._declarations.items()]
|
| 193 |
+
lst.sort()
|
| 194 |
+
return lst
|
| 195 |
+
|
| 196 |
+
def _generate(self, step_name):
|
| 197 |
+
for name, tp in self._get_declarations():
|
| 198 |
+
kind, realname = name.split(' ', 1)
|
| 199 |
+
try:
|
| 200 |
+
method = getattr(self, '_generate_cpy_%s_%s' % (kind,
|
| 201 |
+
step_name))
|
| 202 |
+
except AttributeError:
|
| 203 |
+
raise VerificationError(
|
| 204 |
+
"not implemented in verify(): %r" % name)
|
| 205 |
+
try:
|
| 206 |
+
method(tp, realname)
|
| 207 |
+
except Exception as e:
|
| 208 |
+
model.attach_exception_info(e, name)
|
| 209 |
+
raise
|
| 210 |
+
|
| 211 |
+
def _load(self, module, step_name, **kwds):
|
| 212 |
+
for name, tp in self._get_declarations():
|
| 213 |
+
kind, realname = name.split(' ', 1)
|
| 214 |
+
method = getattr(self, '_%s_cpy_%s' % (step_name, kind))
|
| 215 |
+
try:
|
| 216 |
+
method(tp, realname, module, **kwds)
|
| 217 |
+
except Exception as e:
|
| 218 |
+
model.attach_exception_info(e, name)
|
| 219 |
+
raise
|
| 220 |
+
|
| 221 |
+
def _generate_nothing(self, tp, name):
|
| 222 |
+
pass
|
| 223 |
+
|
| 224 |
+
def _loaded_noop(self, tp, name, module, **kwds):
|
| 225 |
+
pass
|
| 226 |
+
|
| 227 |
+
# ----------
|
| 228 |
+
|
| 229 |
+
def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
|
| 230 |
+
extraarg = ''
|
| 231 |
+
if isinstance(tp, model.PrimitiveType):
|
| 232 |
+
if tp.is_integer_type() and tp.name != '_Bool':
|
| 233 |
+
converter = '_cffi_to_c_int'
|
| 234 |
+
extraarg = ', %s' % tp.name
|
| 235 |
+
elif tp.is_complex_type():
|
| 236 |
+
raise VerificationError(
|
| 237 |
+
"not implemented in verify(): complex types")
|
| 238 |
+
else:
|
| 239 |
+
converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''),
|
| 240 |
+
tp.name.replace(' ', '_'))
|
| 241 |
+
errvalue = '-1'
|
| 242 |
+
#
|
| 243 |
+
elif isinstance(tp, model.PointerType):
|
| 244 |
+
self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
|
| 245 |
+
tovar, errcode)
|
| 246 |
+
return
|
| 247 |
+
#
|
| 248 |
+
elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
|
| 249 |
+
# a struct (not a struct pointer) as a function argument
|
| 250 |
+
self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
|
| 251 |
+
% (tovar, self._gettypenum(tp), fromvar))
|
| 252 |
+
self._prnt(' %s;' % errcode)
|
| 253 |
+
return
|
| 254 |
+
#
|
| 255 |
+
elif isinstance(tp, model.FunctionPtrType):
|
| 256 |
+
converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
|
| 257 |
+
extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
|
| 258 |
+
errvalue = 'NULL'
|
| 259 |
+
#
|
| 260 |
+
else:
|
| 261 |
+
raise NotImplementedError(tp)
|
| 262 |
+
#
|
| 263 |
+
self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
|
| 264 |
+
self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % (
|
| 265 |
+
tovar, tp.get_c_name(''), errvalue))
|
| 266 |
+
self._prnt(' %s;' % errcode)
|
| 267 |
+
|
| 268 |
+
def _extra_local_variables(self, tp, localvars, freelines):
|
| 269 |
+
if isinstance(tp, model.PointerType):
|
| 270 |
+
localvars.add('Py_ssize_t datasize')
|
| 271 |
+
localvars.add('struct _cffi_freeme_s *large_args_free = NULL')
|
| 272 |
+
freelines.add('if (large_args_free != NULL)'
|
| 273 |
+
' _cffi_free_array_arguments(large_args_free);')
|
| 274 |
+
|
| 275 |
+
def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
|
| 276 |
+
self._prnt(' datasize = _cffi_prepare_pointer_call_argument(')
|
| 277 |
+
self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % (
|
| 278 |
+
self._gettypenum(tp), fromvar, tovar))
|
| 279 |
+
self._prnt(' if (datasize != 0) {')
|
| 280 |
+
self._prnt(' %s = ((size_t)datasize) <= 640 ? '
|
| 281 |
+
'alloca((size_t)datasize) : NULL;' % (tovar,))
|
| 282 |
+
self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, '
|
| 283 |
+
'(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))
|
| 284 |
+
self._prnt(' datasize, &large_args_free) < 0)')
|
| 285 |
+
self._prnt(' %s;' % errcode)
|
| 286 |
+
self._prnt(' }')
|
| 287 |
+
|
| 288 |
+
def _convert_expr_from_c(self, tp, var, context):
|
| 289 |
+
if isinstance(tp, model.PrimitiveType):
|
| 290 |
+
if tp.is_integer_type() and tp.name != '_Bool':
|
| 291 |
+
return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
|
| 292 |
+
elif tp.name != 'long double':
|
| 293 |
+
return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
|
| 294 |
+
else:
|
| 295 |
+
return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
|
| 296 |
+
var, self._gettypenum(tp))
|
| 297 |
+
elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
|
| 298 |
+
return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
|
| 299 |
+
var, self._gettypenum(tp))
|
| 300 |
+
elif isinstance(tp, model.ArrayType):
|
| 301 |
+
return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
|
| 302 |
+
var, self._gettypenum(model.PointerType(tp.item)))
|
| 303 |
+
elif isinstance(tp, model.StructOrUnion):
|
| 304 |
+
if tp.fldnames is None:
|
| 305 |
+
raise TypeError("'%s' is used as %s, but is opaque" % (
|
| 306 |
+
tp._get_c_name(), context))
|
| 307 |
+
return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
|
| 308 |
+
var, self._gettypenum(tp))
|
| 309 |
+
elif isinstance(tp, model.EnumType):
|
| 310 |
+
return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
|
| 311 |
+
var, self._gettypenum(tp))
|
| 312 |
+
else:
|
| 313 |
+
raise NotImplementedError(tp)
|
| 314 |
+
|
| 315 |
+
# ----------
|
| 316 |
+
# typedefs: generates no code so far
|
| 317 |
+
|
| 318 |
+
_generate_cpy_typedef_collecttype = _generate_nothing
|
| 319 |
+
_generate_cpy_typedef_decl = _generate_nothing
|
| 320 |
+
_generate_cpy_typedef_method = _generate_nothing
|
| 321 |
+
_loading_cpy_typedef = _loaded_noop
|
| 322 |
+
_loaded_cpy_typedef = _loaded_noop
|
| 323 |
+
|
| 324 |
+
# ----------
|
| 325 |
+
# function declarations
|
| 326 |
+
|
| 327 |
+
def _generate_cpy_function_collecttype(self, tp, name):
|
| 328 |
+
assert isinstance(tp, model.FunctionPtrType)
|
| 329 |
+
if tp.ellipsis:
|
| 330 |
+
self._do_collect_type(tp)
|
| 331 |
+
else:
|
| 332 |
+
# don't call _do_collect_type(tp) in this common case,
|
| 333 |
+
# otherwise test_autofilled_struct_as_argument fails
|
| 334 |
+
for type in tp.args:
|
| 335 |
+
self._do_collect_type(type)
|
| 336 |
+
self._do_collect_type(tp.result)
|
| 337 |
+
|
| 338 |
+
def _generate_cpy_function_decl(self, tp, name):
|
| 339 |
+
assert isinstance(tp, model.FunctionPtrType)
|
| 340 |
+
if tp.ellipsis:
|
| 341 |
+
# cannot support vararg functions better than this: check for its
|
| 342 |
+
# exact type (including the fixed arguments), and build it as a
|
| 343 |
+
# constant function pointer (no CPython wrapper)
|
| 344 |
+
self._generate_cpy_const(False, name, tp)
|
| 345 |
+
return
|
| 346 |
+
prnt = self._prnt
|
| 347 |
+
numargs = len(tp.args)
|
| 348 |
+
if numargs == 0:
|
| 349 |
+
argname = 'noarg'
|
| 350 |
+
elif numargs == 1:
|
| 351 |
+
argname = 'arg0'
|
| 352 |
+
else:
|
| 353 |
+
argname = 'args'
|
| 354 |
+
prnt('static PyObject *')
|
| 355 |
+
prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
|
| 356 |
+
prnt('{')
|
| 357 |
+
#
|
| 358 |
+
context = 'argument of %s' % name
|
| 359 |
+
for i, type in enumerate(tp.args):
|
| 360 |
+
prnt(' %s;' % type.get_c_name(' x%d' % i, context))
|
| 361 |
+
#
|
| 362 |
+
localvars = set()
|
| 363 |
+
freelines = set()
|
| 364 |
+
for type in tp.args:
|
| 365 |
+
self._extra_local_variables(type, localvars, freelines)
|
| 366 |
+
for decl in sorted(localvars):
|
| 367 |
+
prnt(' %s;' % (decl,))
|
| 368 |
+
#
|
| 369 |
+
if not isinstance(tp.result, model.VoidType):
|
| 370 |
+
result_code = 'result = '
|
| 371 |
+
context = 'result of %s' % name
|
| 372 |
+
prnt(' %s;' % tp.result.get_c_name(' result', context))
|
| 373 |
+
prnt(' PyObject *pyresult;')
|
| 374 |
+
else:
|
| 375 |
+
result_code = ''
|
| 376 |
+
#
|
| 377 |
+
if len(tp.args) > 1:
|
| 378 |
+
rng = range(len(tp.args))
|
| 379 |
+
for i in rng:
|
| 380 |
+
prnt(' PyObject *arg%d;' % i)
|
| 381 |
+
prnt()
|
| 382 |
+
prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % (
|
| 383 |
+
'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
|
| 384 |
+
prnt(' return NULL;')
|
| 385 |
+
prnt()
|
| 386 |
+
#
|
| 387 |
+
for i, type in enumerate(tp.args):
|
| 388 |
+
self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
|
| 389 |
+
'return NULL')
|
| 390 |
+
prnt()
|
| 391 |
+
#
|
| 392 |
+
prnt(' Py_BEGIN_ALLOW_THREADS')
|
| 393 |
+
prnt(' _cffi_restore_errno();')
|
| 394 |
+
prnt(' { %s%s(%s); }' % (
|
| 395 |
+
result_code, name,
|
| 396 |
+
', '.join(['x%d' % i for i in range(len(tp.args))])))
|
| 397 |
+
prnt(' _cffi_save_errno();')
|
| 398 |
+
prnt(' Py_END_ALLOW_THREADS')
|
| 399 |
+
prnt()
|
| 400 |
+
#
|
| 401 |
+
prnt(' (void)self; /* unused */')
|
| 402 |
+
if numargs == 0:
|
| 403 |
+
prnt(' (void)noarg; /* unused */')
|
| 404 |
+
if result_code:
|
| 405 |
+
prnt(' pyresult = %s;' %
|
| 406 |
+
self._convert_expr_from_c(tp.result, 'result', 'result type'))
|
| 407 |
+
for freeline in freelines:
|
| 408 |
+
prnt(' ' + freeline)
|
| 409 |
+
prnt(' return pyresult;')
|
| 410 |
+
else:
|
| 411 |
+
for freeline in freelines:
|
| 412 |
+
prnt(' ' + freeline)
|
| 413 |
+
prnt(' Py_INCREF(Py_None);')
|
| 414 |
+
prnt(' return Py_None;')
|
| 415 |
+
prnt('}')
|
| 416 |
+
prnt()
|
| 417 |
+
|
| 418 |
+
def _generate_cpy_function_method(self, tp, name):
|
| 419 |
+
if tp.ellipsis:
|
| 420 |
+
return
|
| 421 |
+
numargs = len(tp.args)
|
| 422 |
+
if numargs == 0:
|
| 423 |
+
meth = 'METH_NOARGS'
|
| 424 |
+
elif numargs == 1:
|
| 425 |
+
meth = 'METH_O'
|
| 426 |
+
else:
|
| 427 |
+
meth = 'METH_VARARGS'
|
| 428 |
+
self._prnt(' {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth))
|
| 429 |
+
|
| 430 |
+
_loading_cpy_function = _loaded_noop
|
| 431 |
+
|
| 432 |
+
def _loaded_cpy_function(self, tp, name, module, library):
|
| 433 |
+
if tp.ellipsis:
|
| 434 |
+
return
|
| 435 |
+
func = getattr(module, name)
|
| 436 |
+
setattr(library, name, func)
|
| 437 |
+
self._types_of_builtin_functions[func] = tp
|
| 438 |
+
|
| 439 |
+
# ----------
|
| 440 |
+
# named structs
|
| 441 |
+
|
| 442 |
+
_generate_cpy_struct_collecttype = _generate_nothing
|
| 443 |
+
def _generate_cpy_struct_decl(self, tp, name):
|
| 444 |
+
assert name == tp.name
|
| 445 |
+
self._generate_struct_or_union_decl(tp, 'struct', name)
|
| 446 |
+
def _generate_cpy_struct_method(self, tp, name):
|
| 447 |
+
self._generate_struct_or_union_method(tp, 'struct', name)
|
| 448 |
+
def _loading_cpy_struct(self, tp, name, module):
|
| 449 |
+
self._loading_struct_or_union(tp, 'struct', name, module)
|
| 450 |
+
def _loaded_cpy_struct(self, tp, name, module, **kwds):
|
| 451 |
+
self._loaded_struct_or_union(tp)
|
| 452 |
+
|
| 453 |
+
_generate_cpy_union_collecttype = _generate_nothing
|
| 454 |
+
def _generate_cpy_union_decl(self, tp, name):
|
| 455 |
+
assert name == tp.name
|
| 456 |
+
self._generate_struct_or_union_decl(tp, 'union', name)
|
| 457 |
+
def _generate_cpy_union_method(self, tp, name):
|
| 458 |
+
self._generate_struct_or_union_method(tp, 'union', name)
|
| 459 |
+
def _loading_cpy_union(self, tp, name, module):
|
| 460 |
+
self._loading_struct_or_union(tp, 'union', name, module)
|
| 461 |
+
def _loaded_cpy_union(self, tp, name, module, **kwds):
|
| 462 |
+
self._loaded_struct_or_union(tp)
|
| 463 |
+
|
| 464 |
+
def _generate_struct_or_union_decl(self, tp, prefix, name):
|
| 465 |
+
if tp.fldnames is None:
|
| 466 |
+
return # nothing to do with opaque structs
|
| 467 |
+
checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
|
| 468 |
+
layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
|
| 469 |
+
cname = ('%s %s' % (prefix, name)).strip()
|
| 470 |
+
#
|
| 471 |
+
prnt = self._prnt
|
| 472 |
+
prnt('static void %s(%s *p)' % (checkfuncname, cname))
|
| 473 |
+
prnt('{')
|
| 474 |
+
prnt(' /* only to generate compile-time warnings or errors */')
|
| 475 |
+
prnt(' (void)p;')
|
| 476 |
+
for fname, ftype, fbitsize, fqual in tp.enumfields():
|
| 477 |
+
if (isinstance(ftype, model.PrimitiveType)
|
| 478 |
+
and ftype.is_integer_type()) or fbitsize >= 0:
|
| 479 |
+
# accept all integers, but complain on float or double
|
| 480 |
+
prnt(' (void)((p->%s) << 1);' % fname)
|
| 481 |
+
else:
|
| 482 |
+
# only accept exactly the type declared.
|
| 483 |
+
try:
|
| 484 |
+
prnt(' { %s = &p->%s; (void)tmp; }' % (
|
| 485 |
+
ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
|
| 486 |
+
fname))
|
| 487 |
+
except VerificationError as e:
|
| 488 |
+
prnt(' /* %s */' % str(e)) # cannot verify it, ignore
|
| 489 |
+
prnt('}')
|
| 490 |
+
prnt('static PyObject *')
|
| 491 |
+
prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,))
|
| 492 |
+
prnt('{')
|
| 493 |
+
prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname)
|
| 494 |
+
prnt(' static Py_ssize_t nums[] = {')
|
| 495 |
+
prnt(' sizeof(%s),' % cname)
|
| 496 |
+
prnt(' offsetof(struct _cffi_aligncheck, y),')
|
| 497 |
+
for fname, ftype, fbitsize, fqual in tp.enumfields():
|
| 498 |
+
if fbitsize >= 0:
|
| 499 |
+
continue # xxx ignore fbitsize for now
|
| 500 |
+
prnt(' offsetof(%s, %s),' % (cname, fname))
|
| 501 |
+
if isinstance(ftype, model.ArrayType) and ftype.length is None:
|
| 502 |
+
prnt(' 0, /* %s */' % ftype._get_c_name())
|
| 503 |
+
else:
|
| 504 |
+
prnt(' sizeof(((%s *)0)->%s),' % (cname, fname))
|
| 505 |
+
prnt(' -1')
|
| 506 |
+
prnt(' };')
|
| 507 |
+
prnt(' (void)self; /* unused */')
|
| 508 |
+
prnt(' (void)noarg; /* unused */')
|
| 509 |
+
prnt(' return _cffi_get_struct_layout(nums);')
|
| 510 |
+
prnt(' /* the next line is not executed, but compiled */')
|
| 511 |
+
prnt(' %s(0);' % (checkfuncname,))
|
| 512 |
+
prnt('}')
|
| 513 |
+
prnt()
|
| 514 |
+
|
| 515 |
+
def _generate_struct_or_union_method(self, tp, prefix, name):
|
| 516 |
+
if tp.fldnames is None:
|
| 517 |
+
return # nothing to do with opaque structs
|
| 518 |
+
layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
|
| 519 |
+
self._prnt(' {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname,
|
| 520 |
+
layoutfuncname))
|
| 521 |
+
|
| 522 |
+
def _loading_struct_or_union(self, tp, prefix, name, module):
|
| 523 |
+
if tp.fldnames is None:
|
| 524 |
+
return # nothing to do with opaque structs
|
| 525 |
+
layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
|
| 526 |
+
#
|
| 527 |
+
function = getattr(module, layoutfuncname)
|
| 528 |
+
layout = function()
|
| 529 |
+
if isinstance(tp, model.StructOrUnion) and tp.partial:
|
| 530 |
+
# use the function()'s sizes and offsets to guide the
|
| 531 |
+
# layout of the struct
|
| 532 |
+
totalsize = layout[0]
|
| 533 |
+
totalalignment = layout[1]
|
| 534 |
+
fieldofs = layout[2::2]
|
| 535 |
+
fieldsize = layout[3::2]
|
| 536 |
+
tp.force_flatten()
|
| 537 |
+
assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
|
| 538 |
+
tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
|
| 539 |
+
else:
|
| 540 |
+
cname = ('%s %s' % (prefix, name)).strip()
|
| 541 |
+
self._struct_pending_verification[tp] = layout, cname
|
| 542 |
+
|
| 543 |
+
def _loaded_struct_or_union(self, tp):
|
| 544 |
+
if tp.fldnames is None:
|
| 545 |
+
return # nothing to do with opaque structs
|
| 546 |
+
self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered
|
| 547 |
+
|
| 548 |
+
if tp in self._struct_pending_verification:
|
| 549 |
+
# check that the layout sizes and offsets match the real ones
|
| 550 |
+
def check(realvalue, expectedvalue, msg):
|
| 551 |
+
if realvalue != expectedvalue:
|
| 552 |
+
raise VerificationError(
|
| 553 |
+
"%s (we have %d, but C compiler says %d)"
|
| 554 |
+
% (msg, expectedvalue, realvalue))
|
| 555 |
+
ffi = self.ffi
|
| 556 |
+
BStruct = ffi._get_cached_btype(tp)
|
| 557 |
+
layout, cname = self._struct_pending_verification.pop(tp)
|
| 558 |
+
check(layout[0], ffi.sizeof(BStruct), "wrong total size")
|
| 559 |
+
check(layout[1], ffi.alignof(BStruct), "wrong total alignment")
|
| 560 |
+
i = 2
|
| 561 |
+
for fname, ftype, fbitsize, fqual in tp.enumfields():
|
| 562 |
+
if fbitsize >= 0:
|
| 563 |
+
continue # xxx ignore fbitsize for now
|
| 564 |
+
check(layout[i], ffi.offsetof(BStruct, fname),
|
| 565 |
+
"wrong offset for field %r" % (fname,))
|
| 566 |
+
if layout[i+1] != 0:
|
| 567 |
+
BField = ffi._get_cached_btype(ftype)
|
| 568 |
+
check(layout[i+1], ffi.sizeof(BField),
|
| 569 |
+
"wrong size for field %r" % (fname,))
|
| 570 |
+
i += 2
|
| 571 |
+
assert i == len(layout)
|
| 572 |
+
|
| 573 |
+
# ----------
|
| 574 |
+
# 'anonymous' declarations. These are produced for anonymous structs
|
| 575 |
+
# or unions; the 'name' is obtained by a typedef.
|
| 576 |
+
|
| 577 |
+
_generate_cpy_anonymous_collecttype = _generate_nothing
|
| 578 |
+
|
| 579 |
+
def _generate_cpy_anonymous_decl(self, tp, name):
|
| 580 |
+
if isinstance(tp, model.EnumType):
|
| 581 |
+
self._generate_cpy_enum_decl(tp, name, '')
|
| 582 |
+
else:
|
| 583 |
+
self._generate_struct_or_union_decl(tp, '', name)
|
| 584 |
+
|
| 585 |
+
def _generate_cpy_anonymous_method(self, tp, name):
|
| 586 |
+
if not isinstance(tp, model.EnumType):
|
| 587 |
+
self._generate_struct_or_union_method(tp, '', name)
|
| 588 |
+
|
| 589 |
+
def _loading_cpy_anonymous(self, tp, name, module):
|
| 590 |
+
if isinstance(tp, model.EnumType):
|
| 591 |
+
self._loading_cpy_enum(tp, name, module)
|
| 592 |
+
else:
|
| 593 |
+
self._loading_struct_or_union(tp, '', name, module)
|
| 594 |
+
|
| 595 |
+
def _loaded_cpy_anonymous(self, tp, name, module, **kwds):
|
| 596 |
+
if isinstance(tp, model.EnumType):
|
| 597 |
+
self._loaded_cpy_enum(tp, name, module, **kwds)
|
| 598 |
+
else:
|
| 599 |
+
self._loaded_struct_or_union(tp)
|
| 600 |
+
|
| 601 |
+
# ----------
|
| 602 |
+
# constants, likely declared with '#define'
|
| 603 |
+
|
| 604 |
+
def _generate_cpy_const(self, is_int, name, tp=None, category='const',
|
| 605 |
+
vartp=None, delayed=True, size_too=False,
|
| 606 |
+
check_value=None):
|
| 607 |
+
prnt = self._prnt
|
| 608 |
+
funcname = '_cffi_%s_%s' % (category, name)
|
| 609 |
+
prnt('static int %s(PyObject *lib)' % funcname)
|
| 610 |
+
prnt('{')
|
| 611 |
+
prnt(' PyObject *o;')
|
| 612 |
+
prnt(' int res;')
|
| 613 |
+
if not is_int:
|
| 614 |
+
prnt(' %s;' % (vartp or tp).get_c_name(' i', name))
|
| 615 |
+
else:
|
| 616 |
+
assert category == 'const'
|
| 617 |
+
#
|
| 618 |
+
if check_value is not None:
|
| 619 |
+
self._check_int_constant_value(name, check_value)
|
| 620 |
+
#
|
| 621 |
+
if not is_int:
|
| 622 |
+
if category == 'var':
|
| 623 |
+
realexpr = '&' + name
|
| 624 |
+
else:
|
| 625 |
+
realexpr = name
|
| 626 |
+
prnt(' i = (%s);' % (realexpr,))
|
| 627 |
+
prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i',
|
| 628 |
+
'variable type'),))
|
| 629 |
+
assert delayed
|
| 630 |
+
else:
|
| 631 |
+
prnt(' o = _cffi_from_c_int_const(%s);' % name)
|
| 632 |
+
prnt(' if (o == NULL)')
|
| 633 |
+
prnt(' return -1;')
|
| 634 |
+
if size_too:
|
| 635 |
+
prnt(' {')
|
| 636 |
+
prnt(' PyObject *o1 = o;')
|
| 637 |
+
prnt(' o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));'
|
| 638 |
+
% (name,))
|
| 639 |
+
prnt(' Py_DECREF(o1);')
|
| 640 |
+
prnt(' if (o == NULL)')
|
| 641 |
+
prnt(' return -1;')
|
| 642 |
+
prnt(' }')
|
| 643 |
+
prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name)
|
| 644 |
+
prnt(' Py_DECREF(o);')
|
| 645 |
+
prnt(' if (res < 0)')
|
| 646 |
+
prnt(' return -1;')
|
| 647 |
+
prnt(' return %s;' % self._chained_list_constants[delayed])
|
| 648 |
+
self._chained_list_constants[delayed] = funcname + '(lib)'
|
| 649 |
+
prnt('}')
|
| 650 |
+
prnt()
|
| 651 |
+
|
| 652 |
+
def _generate_cpy_constant_collecttype(self, tp, name):
|
| 653 |
+
is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
|
| 654 |
+
if not is_int:
|
| 655 |
+
self._do_collect_type(tp)
|
| 656 |
+
|
| 657 |
+
def _generate_cpy_constant_decl(self, tp, name):
|
| 658 |
+
is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
|
| 659 |
+
self._generate_cpy_const(is_int, name, tp)
|
| 660 |
+
|
| 661 |
+
_generate_cpy_constant_method = _generate_nothing
|
| 662 |
+
_loading_cpy_constant = _loaded_noop
|
| 663 |
+
_loaded_cpy_constant = _loaded_noop
|
| 664 |
+
|
| 665 |
+
# ----------
|
| 666 |
+
# enums
|
| 667 |
+
|
| 668 |
+
def _check_int_constant_value(self, name, value, err_prefix=''):
|
| 669 |
+
prnt = self._prnt
|
| 670 |
+
if value <= 0:
|
| 671 |
+
prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % (
|
| 672 |
+
name, name, value))
|
| 673 |
+
else:
|
| 674 |
+
prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
|
| 675 |
+
name, name, value))
|
| 676 |
+
prnt(' char buf[64];')
|
| 677 |
+
prnt(' if ((%s) <= 0)' % name)
|
| 678 |
+
prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name)
|
| 679 |
+
prnt(' else')
|
| 680 |
+
prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' %
|
| 681 |
+
name)
|
| 682 |
+
prnt(' PyErr_Format(_cffi_VerificationError,')
|
| 683 |
+
prnt(' "%s%s has the real value %s, not %s",')
|
| 684 |
+
prnt(' "%s", "%s", buf, "%d");' % (
|
| 685 |
+
err_prefix, name, value))
|
| 686 |
+
prnt(' return -1;')
|
| 687 |
+
prnt(' }')
|
| 688 |
+
|
| 689 |
+
def _enum_funcname(self, prefix, name):
|
| 690 |
+
# "$enum_$1" => "___D_enum____D_1"
|
| 691 |
+
name = name.replace('$', '___D_')
|
| 692 |
+
return '_cffi_e_%s_%s' % (prefix, name)
|
| 693 |
+
|
| 694 |
+
def _generate_cpy_enum_decl(self, tp, name, prefix='enum'):
|
| 695 |
+
if tp.partial:
|
| 696 |
+
for enumerator in tp.enumerators:
|
| 697 |
+
self._generate_cpy_const(True, enumerator, delayed=False)
|
| 698 |
+
return
|
| 699 |
+
#
|
| 700 |
+
funcname = self._enum_funcname(prefix, name)
|
| 701 |
+
prnt = self._prnt
|
| 702 |
+
prnt('static int %s(PyObject *lib)' % funcname)
|
| 703 |
+
prnt('{')
|
| 704 |
+
for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
|
| 705 |
+
self._check_int_constant_value(enumerator, enumvalue,
|
| 706 |
+
"enum %s: " % name)
|
| 707 |
+
prnt(' return %s;' % self._chained_list_constants[True])
|
| 708 |
+
self._chained_list_constants[True] = funcname + '(lib)'
|
| 709 |
+
prnt('}')
|
| 710 |
+
prnt()
|
| 711 |
+
|
| 712 |
+
_generate_cpy_enum_collecttype = _generate_nothing
|
| 713 |
+
_generate_cpy_enum_method = _generate_nothing
|
| 714 |
+
|
| 715 |
+
def _loading_cpy_enum(self, tp, name, module):
|
| 716 |
+
if tp.partial:
|
| 717 |
+
enumvalues = [getattr(module, enumerator)
|
| 718 |
+
for enumerator in tp.enumerators]
|
| 719 |
+
tp.enumvalues = tuple(enumvalues)
|
| 720 |
+
tp.partial_resolved = True
|
| 721 |
+
|
| 722 |
+
def _loaded_cpy_enum(self, tp, name, module, library):
|
| 723 |
+
for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
|
| 724 |
+
setattr(library, enumerator, enumvalue)
|
| 725 |
+
|
| 726 |
+
# ----------
|
| 727 |
+
# macros: for now only for integers
|
| 728 |
+
|
| 729 |
+
def _generate_cpy_macro_decl(self, tp, name):
|
| 730 |
+
if tp == '...':
|
| 731 |
+
check_value = None
|
| 732 |
+
else:
|
| 733 |
+
check_value = tp # an integer
|
| 734 |
+
self._generate_cpy_const(True, name, check_value=check_value)
|
| 735 |
+
|
| 736 |
+
_generate_cpy_macro_collecttype = _generate_nothing
|
| 737 |
+
_generate_cpy_macro_method = _generate_nothing
|
| 738 |
+
_loading_cpy_macro = _loaded_noop
|
| 739 |
+
_loaded_cpy_macro = _loaded_noop
|
| 740 |
+
|
| 741 |
+
# ----------
|
| 742 |
+
# global variables
|
| 743 |
+
|
| 744 |
+
def _generate_cpy_variable_collecttype(self, tp, name):
|
| 745 |
+
if isinstance(tp, model.ArrayType):
|
| 746 |
+
tp_ptr = model.PointerType(tp.item)
|
| 747 |
+
else:
|
| 748 |
+
tp_ptr = model.PointerType(tp)
|
| 749 |
+
self._do_collect_type(tp_ptr)
|
| 750 |
+
|
| 751 |
+
def _generate_cpy_variable_decl(self, tp, name):
|
| 752 |
+
if isinstance(tp, model.ArrayType):
|
| 753 |
+
tp_ptr = model.PointerType(tp.item)
|
| 754 |
+
self._generate_cpy_const(False, name, tp, vartp=tp_ptr,
|
| 755 |
+
size_too = tp.length_is_unknown())
|
| 756 |
+
else:
|
| 757 |
+
tp_ptr = model.PointerType(tp)
|
| 758 |
+
self._generate_cpy_const(False, name, tp_ptr, category='var')
|
| 759 |
+
|
| 760 |
+
_generate_cpy_variable_method = _generate_nothing
|
| 761 |
+
_loading_cpy_variable = _loaded_noop
|
| 762 |
+
|
| 763 |
+
def _loaded_cpy_variable(self, tp, name, module, library):
|
| 764 |
+
value = getattr(library, name)
|
| 765 |
+
if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the
|
| 766 |
+
# sense that "a=..." is forbidden
|
| 767 |
+
if tp.length_is_unknown():
|
| 768 |
+
assert isinstance(value, tuple)
|
| 769 |
+
(value, size) = value
|
| 770 |
+
BItemType = self.ffi._get_cached_btype(tp.item)
|
| 771 |
+
length, rest = divmod(size, self.ffi.sizeof(BItemType))
|
| 772 |
+
if rest != 0:
|
| 773 |
+
raise VerificationError(
|
| 774 |
+
"bad size: %r does not seem to be an array of %s" %
|
| 775 |
+
(name, tp.item))
|
| 776 |
+
tp = tp.resolve_length(length)
|
| 777 |
+
# 'value' is a <cdata 'type *'> which we have to replace with
|
| 778 |
+
# a <cdata 'type[N]'> if the N is actually known
|
| 779 |
+
if tp.length is not None:
|
| 780 |
+
BArray = self.ffi._get_cached_btype(tp)
|
| 781 |
+
value = self.ffi.cast(BArray, value)
|
| 782 |
+
setattr(library, name, value)
|
| 783 |
+
return
|
| 784 |
+
# remove ptr=<cdata 'int *'> from the library instance, and replace
|
| 785 |
+
# it by a property on the class, which reads/writes into ptr[0].
|
| 786 |
+
ptr = value
|
| 787 |
+
delattr(library, name)
|
| 788 |
+
def getter(library):
|
| 789 |
+
return ptr[0]
|
| 790 |
+
def setter(library, value):
|
| 791 |
+
ptr[0] = value
|
| 792 |
+
setattr(type(library), name, property(getter, setter))
|
| 793 |
+
type(library)._cffi_dir.append(name)
|
| 794 |
+
|
| 795 |
+
# ----------
|
| 796 |
+
|
| 797 |
+
def _generate_setup_custom(self):
|
| 798 |
+
prnt = self._prnt
|
| 799 |
+
prnt('static int _cffi_setup_custom(PyObject *lib)')
|
| 800 |
+
prnt('{')
|
| 801 |
+
prnt(' return %s;' % self._chained_list_constants[True])
|
| 802 |
+
prnt('}')
|
| 803 |
+
|
| 804 |
+
cffimod_header = r'''
|
| 805 |
+
#include <Python.h>
|
| 806 |
+
#include <stddef.h>
|
| 807 |
+
|
| 808 |
+
/* this block of #ifs should be kept exactly identical between
|
| 809 |
+
c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
|
| 810 |
+
and cffi/_cffi_include.h */
|
| 811 |
+
#if defined(_MSC_VER)
|
| 812 |
+
# include <malloc.h> /* for alloca() */
|
| 813 |
+
# if _MSC_VER < 1600 /* MSVC < 2010 */
|
| 814 |
+
typedef __int8 int8_t;
|
| 815 |
+
typedef __int16 int16_t;
|
| 816 |
+
typedef __int32 int32_t;
|
| 817 |
+
typedef __int64 int64_t;
|
| 818 |
+
typedef unsigned __int8 uint8_t;
|
| 819 |
+
typedef unsigned __int16 uint16_t;
|
| 820 |
+
typedef unsigned __int32 uint32_t;
|
| 821 |
+
typedef unsigned __int64 uint64_t;
|
| 822 |
+
typedef __int8 int_least8_t;
|
| 823 |
+
typedef __int16 int_least16_t;
|
| 824 |
+
typedef __int32 int_least32_t;
|
| 825 |
+
typedef __int64 int_least64_t;
|
| 826 |
+
typedef unsigned __int8 uint_least8_t;
|
| 827 |
+
typedef unsigned __int16 uint_least16_t;
|
| 828 |
+
typedef unsigned __int32 uint_least32_t;
|
| 829 |
+
typedef unsigned __int64 uint_least64_t;
|
| 830 |
+
typedef __int8 int_fast8_t;
|
| 831 |
+
typedef __int16 int_fast16_t;
|
| 832 |
+
typedef __int32 int_fast32_t;
|
| 833 |
+
typedef __int64 int_fast64_t;
|
| 834 |
+
typedef unsigned __int8 uint_fast8_t;
|
| 835 |
+
typedef unsigned __int16 uint_fast16_t;
|
| 836 |
+
typedef unsigned __int32 uint_fast32_t;
|
| 837 |
+
typedef unsigned __int64 uint_fast64_t;
|
| 838 |
+
typedef __int64 intmax_t;
|
| 839 |
+
typedef unsigned __int64 uintmax_t;
|
| 840 |
+
# else
|
| 841 |
+
# include <stdint.h>
|
| 842 |
+
# endif
|
| 843 |
+
# if _MSC_VER < 1800 /* MSVC < 2013 */
|
| 844 |
+
# ifndef __cplusplus
|
| 845 |
+
typedef unsigned char _Bool;
|
| 846 |
+
# endif
|
| 847 |
+
# endif
|
| 848 |
+
# define _cffi_float_complex_t _Fcomplex /* include <complex.h> for it */
|
| 849 |
+
# define _cffi_double_complex_t _Dcomplex /* include <complex.h> for it */
|
| 850 |
+
#else
|
| 851 |
+
# include <stdint.h>
|
| 852 |
+
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
|
| 853 |
+
# include <alloca.h>
|
| 854 |
+
# endif
|
| 855 |
+
# define _cffi_float_complex_t float _Complex
|
| 856 |
+
# define _cffi_double_complex_t double _Complex
|
| 857 |
+
#endif
|
| 858 |
+
|
| 859 |
+
#define _cffi_from_c_double PyFloat_FromDouble
|
| 860 |
+
#define _cffi_from_c_float PyFloat_FromDouble
|
| 861 |
+
#define _cffi_from_c_long PyLong_FromLong
|
| 862 |
+
#define _cffi_from_c_ulong PyLong_FromUnsignedLong
|
| 863 |
+
#define _cffi_from_c_longlong PyLong_FromLongLong
|
| 864 |
+
#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong
|
| 865 |
+
#define _cffi_from_c__Bool PyBool_FromLong
|
| 866 |
+
|
| 867 |
+
#define _cffi_to_c_double PyFloat_AsDouble
|
| 868 |
+
#define _cffi_to_c_float PyFloat_AsDouble
|
| 869 |
+
|
| 870 |
+
#define _cffi_from_c_int_const(x) \
|
| 871 |
+
(((x) > 0) ? \
|
| 872 |
+
((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \
|
| 873 |
+
PyLong_FromLong((long)(x)) : \
|
| 874 |
+
PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \
|
| 875 |
+
((long long)(x) >= (long long)LONG_MIN) ? \
|
| 876 |
+
PyLong_FromLong((long)(x)) : \
|
| 877 |
+
PyLong_FromLongLong((long long)(x)))
|
| 878 |
+
|
| 879 |
+
#define _cffi_from_c_int(x, type) \
|
| 880 |
+
(((type)-1) > 0 ? /* unsigned */ \
|
| 881 |
+
(sizeof(type) < sizeof(long) ? \
|
| 882 |
+
PyLong_FromLong((long)x) : \
|
| 883 |
+
sizeof(type) == sizeof(long) ? \
|
| 884 |
+
PyLong_FromUnsignedLong((unsigned long)x) : \
|
| 885 |
+
PyLong_FromUnsignedLongLong((unsigned long long)x)) : \
|
| 886 |
+
(sizeof(type) <= sizeof(long) ? \
|
| 887 |
+
PyLong_FromLong((long)x) : \
|
| 888 |
+
PyLong_FromLongLong((long long)x)))
|
| 889 |
+
|
| 890 |
+
#define _cffi_to_c_int(o, type) \
|
| 891 |
+
((type)( \
|
| 892 |
+
sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \
|
| 893 |
+
: (type)_cffi_to_c_i8(o)) : \
|
| 894 |
+
sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \
|
| 895 |
+
: (type)_cffi_to_c_i16(o)) : \
|
| 896 |
+
sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \
|
| 897 |
+
: (type)_cffi_to_c_i32(o)) : \
|
| 898 |
+
sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \
|
| 899 |
+
: (type)_cffi_to_c_i64(o)) : \
|
| 900 |
+
(Py_FatalError("unsupported size for type " #type), (type)0)))
|
| 901 |
+
|
| 902 |
+
#define _cffi_to_c_i8 \
|
| 903 |
+
((int(*)(PyObject *))_cffi_exports[1])
|
| 904 |
+
#define _cffi_to_c_u8 \
|
| 905 |
+
((int(*)(PyObject *))_cffi_exports[2])
|
| 906 |
+
#define _cffi_to_c_i16 \
|
| 907 |
+
((int(*)(PyObject *))_cffi_exports[3])
|
| 908 |
+
#define _cffi_to_c_u16 \
|
| 909 |
+
((int(*)(PyObject *))_cffi_exports[4])
|
| 910 |
+
#define _cffi_to_c_i32 \
|
| 911 |
+
((int(*)(PyObject *))_cffi_exports[5])
|
| 912 |
+
#define _cffi_to_c_u32 \
|
| 913 |
+
((unsigned int(*)(PyObject *))_cffi_exports[6])
|
| 914 |
+
#define _cffi_to_c_i64 \
|
| 915 |
+
((long long(*)(PyObject *))_cffi_exports[7])
|
| 916 |
+
#define _cffi_to_c_u64 \
|
| 917 |
+
((unsigned long long(*)(PyObject *))_cffi_exports[8])
|
| 918 |
+
#define _cffi_to_c_char \
|
| 919 |
+
((int(*)(PyObject *))_cffi_exports[9])
|
| 920 |
+
#define _cffi_from_c_pointer \
|
| 921 |
+
((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10])
|
| 922 |
+
#define _cffi_to_c_pointer \
|
| 923 |
+
((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11])
|
| 924 |
+
#define _cffi_get_struct_layout \
|
| 925 |
+
((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12])
|
| 926 |
+
#define _cffi_restore_errno \
|
| 927 |
+
((void(*)(void))_cffi_exports[13])
|
| 928 |
+
#define _cffi_save_errno \
|
| 929 |
+
((void(*)(void))_cffi_exports[14])
|
| 930 |
+
#define _cffi_from_c_char \
|
| 931 |
+
((PyObject *(*)(char))_cffi_exports[15])
|
| 932 |
+
#define _cffi_from_c_deref \
|
| 933 |
+
((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
|
| 934 |
+
#define _cffi_to_c \
|
| 935 |
+
((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
|
| 936 |
+
#define _cffi_from_c_struct \
|
| 937 |
+
((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18])
|
| 938 |
+
#define _cffi_to_c_wchar_t \
|
| 939 |
+
((wchar_t(*)(PyObject *))_cffi_exports[19])
|
| 940 |
+
#define _cffi_from_c_wchar_t \
|
| 941 |
+
((PyObject *(*)(wchar_t))_cffi_exports[20])
|
| 942 |
+
#define _cffi_to_c_long_double \
|
| 943 |
+
((long double(*)(PyObject *))_cffi_exports[21])
|
| 944 |
+
#define _cffi_to_c__Bool \
|
| 945 |
+
((_Bool(*)(PyObject *))_cffi_exports[22])
|
| 946 |
+
#define _cffi_prepare_pointer_call_argument \
|
| 947 |
+
((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23])
|
| 948 |
+
#define _cffi_convert_array_from_object \
|
| 949 |
+
((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24])
|
| 950 |
+
#define _CFFI_NUM_EXPORTS 25
|
| 951 |
+
|
| 952 |
+
typedef struct _ctypedescr CTypeDescrObject;
|
| 953 |
+
|
| 954 |
+
static void *_cffi_exports[_CFFI_NUM_EXPORTS];
|
| 955 |
+
static PyObject *_cffi_types, *_cffi_VerificationError;
|
| 956 |
+
|
| 957 |
+
static int _cffi_setup_custom(PyObject *lib); /* forward */
|
| 958 |
+
|
| 959 |
+
static PyObject *_cffi_setup(PyObject *self, PyObject *args)
|
| 960 |
+
{
|
| 961 |
+
PyObject *library;
|
| 962 |
+
int was_alive = (_cffi_types != NULL);
|
| 963 |
+
(void)self; /* unused */
|
| 964 |
+
if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError,
|
| 965 |
+
&library))
|
| 966 |
+
return NULL;
|
| 967 |
+
Py_INCREF(_cffi_types);
|
| 968 |
+
Py_INCREF(_cffi_VerificationError);
|
| 969 |
+
if (_cffi_setup_custom(library) < 0)
|
| 970 |
+
return NULL;
|
| 971 |
+
return PyBool_FromLong(was_alive);
|
| 972 |
+
}
|
| 973 |
+
|
| 974 |
+
union _cffi_union_alignment_u {
|
| 975 |
+
unsigned char m_char;
|
| 976 |
+
unsigned short m_short;
|
| 977 |
+
unsigned int m_int;
|
| 978 |
+
unsigned long m_long;
|
| 979 |
+
unsigned long long m_longlong;
|
| 980 |
+
float m_float;
|
| 981 |
+
double m_double;
|
| 982 |
+
long double m_longdouble;
|
| 983 |
+
};
|
| 984 |
+
|
| 985 |
+
struct _cffi_freeme_s {
|
| 986 |
+
struct _cffi_freeme_s *next;
|
| 987 |
+
union _cffi_union_alignment_u alignment;
|
| 988 |
+
};
|
| 989 |
+
|
| 990 |
+
#ifdef __GNUC__
|
| 991 |
+
__attribute__((unused))
|
| 992 |
+
#endif
|
| 993 |
+
static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg,
|
| 994 |
+
char **output_data, Py_ssize_t datasize,
|
| 995 |
+
struct _cffi_freeme_s **freeme)
|
| 996 |
+
{
|
| 997 |
+
char *p;
|
| 998 |
+
if (datasize < 0)
|
| 999 |
+
return -1;
|
| 1000 |
+
|
| 1001 |
+
p = *output_data;
|
| 1002 |
+
if (p == NULL) {
|
| 1003 |
+
struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc(
|
| 1004 |
+
offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize);
|
| 1005 |
+
if (fp == NULL)
|
| 1006 |
+
return -1;
|
| 1007 |
+
fp->next = *freeme;
|
| 1008 |
+
*freeme = fp;
|
| 1009 |
+
p = *output_data = (char *)&fp->alignment;
|
| 1010 |
+
}
|
| 1011 |
+
memset((void *)p, 0, (size_t)datasize);
|
| 1012 |
+
return _cffi_convert_array_from_object(p, ctptr, arg);
|
| 1013 |
+
}
|
| 1014 |
+
|
| 1015 |
+
#ifdef __GNUC__
|
| 1016 |
+
__attribute__((unused))
|
| 1017 |
+
#endif
|
| 1018 |
+
static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme)
|
| 1019 |
+
{
|
| 1020 |
+
do {
|
| 1021 |
+
void *p = (void *)freeme;
|
| 1022 |
+
freeme = freeme->next;
|
| 1023 |
+
PyObject_Free(p);
|
| 1024 |
+
} while (freeme != NULL);
|
| 1025 |
+
}
|
| 1026 |
+
|
| 1027 |
+
static int _cffi_init(void)
|
| 1028 |
+
{
|
| 1029 |
+
PyObject *module, *c_api_object = NULL;
|
| 1030 |
+
|
| 1031 |
+
module = PyImport_ImportModule("_cffi_backend");
|
| 1032 |
+
if (module == NULL)
|
| 1033 |
+
goto failure;
|
| 1034 |
+
|
| 1035 |
+
c_api_object = PyObject_GetAttrString(module, "_C_API");
|
| 1036 |
+
if (c_api_object == NULL)
|
| 1037 |
+
goto failure;
|
| 1038 |
+
if (!PyCapsule_CheckExact(c_api_object)) {
|
| 1039 |
+
PyErr_SetNone(PyExc_ImportError);
|
| 1040 |
+
goto failure;
|
| 1041 |
+
}
|
| 1042 |
+
memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"),
|
| 1043 |
+
_CFFI_NUM_EXPORTS * sizeof(void *));
|
| 1044 |
+
|
| 1045 |
+
Py_DECREF(module);
|
| 1046 |
+
Py_DECREF(c_api_object);
|
| 1047 |
+
return 0;
|
| 1048 |
+
|
| 1049 |
+
failure:
|
| 1050 |
+
Py_XDECREF(module);
|
| 1051 |
+
Py_XDECREF(c_api_object);
|
| 1052 |
+
return -1;
|
| 1053 |
+
}
|
| 1054 |
+
|
| 1055 |
+
#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num))
|
| 1056 |
+
|
| 1057 |
+
/**********/
|
| 1058 |
+
'''
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/vengine_gen.py
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# DEPRECATED: implementation for ffi.verify()
|
| 3 |
+
#
|
| 4 |
+
import sys, os
|
| 5 |
+
import types
|
| 6 |
+
|
| 7 |
+
from . import model
|
| 8 |
+
from .error import VerificationError
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class VGenericEngine:
|
| 12 |
+
_class_key = 'g'
|
| 13 |
+
_gen_python_module = False
|
| 14 |
+
|
| 15 |
+
def __init__(self, verifier):
|
| 16 |
+
self.verifier = verifier
|
| 17 |
+
self.ffi = verifier.ffi
|
| 18 |
+
self.export_symbols = []
|
| 19 |
+
self._struct_pending_verification = {}
|
| 20 |
+
|
| 21 |
+
def patch_extension_kwds(self, kwds):
|
| 22 |
+
# add 'export_symbols' to the dictionary. Note that we add the
|
| 23 |
+
# list before filling it. When we fill it, it will thus also show
|
| 24 |
+
# up in kwds['export_symbols'].
|
| 25 |
+
kwds.setdefault('export_symbols', self.export_symbols)
|
| 26 |
+
|
| 27 |
+
def find_module(self, module_name, path, so_suffixes):
|
| 28 |
+
for so_suffix in so_suffixes:
|
| 29 |
+
basename = module_name + so_suffix
|
| 30 |
+
if path is None:
|
| 31 |
+
path = sys.path
|
| 32 |
+
for dirname in path:
|
| 33 |
+
filename = os.path.join(dirname, basename)
|
| 34 |
+
if os.path.isfile(filename):
|
| 35 |
+
return filename
|
| 36 |
+
|
| 37 |
+
def collect_types(self):
|
| 38 |
+
pass # not needed in the generic engine
|
| 39 |
+
|
| 40 |
+
def _prnt(self, what=''):
|
| 41 |
+
self._f.write(what + '\n')
|
| 42 |
+
|
| 43 |
+
def write_source_to_f(self):
|
| 44 |
+
prnt = self._prnt
|
| 45 |
+
# first paste some standard set of lines that are mostly '#include'
|
| 46 |
+
prnt(cffimod_header)
|
| 47 |
+
# then paste the C source given by the user, verbatim.
|
| 48 |
+
prnt(self.verifier.preamble)
|
| 49 |
+
#
|
| 50 |
+
# call generate_gen_xxx_decl(), for every xxx found from
|
| 51 |
+
# ffi._parser._declarations. This generates all the functions.
|
| 52 |
+
self._generate('decl')
|
| 53 |
+
#
|
| 54 |
+
# on Windows, distutils insists on putting init_cffi_xyz in
|
| 55 |
+
# 'export_symbols', so instead of fighting it, just give up and
|
| 56 |
+
# give it one
|
| 57 |
+
if sys.platform == 'win32':
|
| 58 |
+
if sys.version_info >= (3,):
|
| 59 |
+
prefix = 'PyInit_'
|
| 60 |
+
else:
|
| 61 |
+
prefix = 'init'
|
| 62 |
+
modname = self.verifier.get_module_name()
|
| 63 |
+
prnt("void %s%s(void) { }\n" % (prefix, modname))
|
| 64 |
+
|
| 65 |
+
def load_library(self, flags=0):
|
| 66 |
+
# import it with the CFFI backend
|
| 67 |
+
backend = self.ffi._backend
|
| 68 |
+
# needs to make a path that contains '/', on Posix
|
| 69 |
+
filename = os.path.join(os.curdir, self.verifier.modulefilename)
|
| 70 |
+
module = backend.load_library(filename, flags)
|
| 71 |
+
#
|
| 72 |
+
# call loading_gen_struct() to get the struct layout inferred by
|
| 73 |
+
# the C compiler
|
| 74 |
+
self._load(module, 'loading')
|
| 75 |
+
|
| 76 |
+
# build the FFILibrary class and instance, this is a module subclass
|
| 77 |
+
# because modules are expected to have usually-constant-attributes and
|
| 78 |
+
# in PyPy this means the JIT is able to treat attributes as constant,
|
| 79 |
+
# which we want.
|
| 80 |
+
class FFILibrary(types.ModuleType):
|
| 81 |
+
_cffi_generic_module = module
|
| 82 |
+
_cffi_ffi = self.ffi
|
| 83 |
+
_cffi_dir = []
|
| 84 |
+
def __dir__(self):
|
| 85 |
+
return FFILibrary._cffi_dir
|
| 86 |
+
library = FFILibrary("")
|
| 87 |
+
#
|
| 88 |
+
# finally, call the loaded_gen_xxx() functions. This will set
|
| 89 |
+
# up the 'library' object.
|
| 90 |
+
self._load(module, 'loaded', library=library)
|
| 91 |
+
return library
|
| 92 |
+
|
| 93 |
+
def _get_declarations(self):
|
| 94 |
+
lst = [(key, tp) for (key, (tp, qual)) in
|
| 95 |
+
self.ffi._parser._declarations.items()]
|
| 96 |
+
lst.sort()
|
| 97 |
+
return lst
|
| 98 |
+
|
| 99 |
+
def _generate(self, step_name):
|
| 100 |
+
for name, tp in self._get_declarations():
|
| 101 |
+
kind, realname = name.split(' ', 1)
|
| 102 |
+
try:
|
| 103 |
+
method = getattr(self, '_generate_gen_%s_%s' % (kind,
|
| 104 |
+
step_name))
|
| 105 |
+
except AttributeError:
|
| 106 |
+
raise VerificationError(
|
| 107 |
+
"not implemented in verify(): %r" % name)
|
| 108 |
+
try:
|
| 109 |
+
method(tp, realname)
|
| 110 |
+
except Exception as e:
|
| 111 |
+
model.attach_exception_info(e, name)
|
| 112 |
+
raise
|
| 113 |
+
|
| 114 |
+
def _load(self, module, step_name, **kwds):
|
| 115 |
+
for name, tp in self._get_declarations():
|
| 116 |
+
kind, realname = name.split(' ', 1)
|
| 117 |
+
method = getattr(self, '_%s_gen_%s' % (step_name, kind))
|
| 118 |
+
try:
|
| 119 |
+
method(tp, realname, module, **kwds)
|
| 120 |
+
except Exception as e:
|
| 121 |
+
model.attach_exception_info(e, name)
|
| 122 |
+
raise
|
| 123 |
+
|
| 124 |
+
def _generate_nothing(self, tp, name):
|
| 125 |
+
pass
|
| 126 |
+
|
| 127 |
+
def _loaded_noop(self, tp, name, module, **kwds):
|
| 128 |
+
pass
|
| 129 |
+
|
| 130 |
+
# ----------
|
| 131 |
+
# typedefs: generates no code so far
|
| 132 |
+
|
| 133 |
+
_generate_gen_typedef_decl = _generate_nothing
|
| 134 |
+
_loading_gen_typedef = _loaded_noop
|
| 135 |
+
_loaded_gen_typedef = _loaded_noop
|
| 136 |
+
|
| 137 |
+
# ----------
|
| 138 |
+
# function declarations
|
| 139 |
+
|
| 140 |
+
def _generate_gen_function_decl(self, tp, name):
|
| 141 |
+
assert isinstance(tp, model.FunctionPtrType)
|
| 142 |
+
if tp.ellipsis:
|
| 143 |
+
# cannot support vararg functions better than this: check for its
|
| 144 |
+
# exact type (including the fixed arguments), and build it as a
|
| 145 |
+
# constant function pointer (no _cffi_f_%s wrapper)
|
| 146 |
+
self._generate_gen_const(False, name, tp)
|
| 147 |
+
return
|
| 148 |
+
prnt = self._prnt
|
| 149 |
+
numargs = len(tp.args)
|
| 150 |
+
argnames = []
|
| 151 |
+
for i, type in enumerate(tp.args):
|
| 152 |
+
indirection = ''
|
| 153 |
+
if isinstance(type, model.StructOrUnion):
|
| 154 |
+
indirection = '*'
|
| 155 |
+
argnames.append('%sx%d' % (indirection, i))
|
| 156 |
+
context = 'argument of %s' % name
|
| 157 |
+
arglist = [type.get_c_name(' %s' % arg, context)
|
| 158 |
+
for type, arg in zip(tp.args, argnames)]
|
| 159 |
+
tpresult = tp.result
|
| 160 |
+
if isinstance(tpresult, model.StructOrUnion):
|
| 161 |
+
arglist.insert(0, tpresult.get_c_name(' *r', context))
|
| 162 |
+
tpresult = model.void_type
|
| 163 |
+
arglist = ', '.join(arglist) or 'void'
|
| 164 |
+
wrappername = '_cffi_f_%s' % name
|
| 165 |
+
self.export_symbols.append(wrappername)
|
| 166 |
+
if tp.abi:
|
| 167 |
+
abi = tp.abi + ' '
|
| 168 |
+
else:
|
| 169 |
+
abi = ''
|
| 170 |
+
funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist)
|
| 171 |
+
context = 'result of %s' % name
|
| 172 |
+
prnt(tpresult.get_c_name(funcdecl, context))
|
| 173 |
+
prnt('{')
|
| 174 |
+
#
|
| 175 |
+
if isinstance(tp.result, model.StructOrUnion):
|
| 176 |
+
result_code = '*r = '
|
| 177 |
+
elif not isinstance(tp.result, model.VoidType):
|
| 178 |
+
result_code = 'return '
|
| 179 |
+
else:
|
| 180 |
+
result_code = ''
|
| 181 |
+
prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames)))
|
| 182 |
+
prnt('}')
|
| 183 |
+
prnt()
|
| 184 |
+
|
| 185 |
+
_loading_gen_function = _loaded_noop
|
| 186 |
+
|
| 187 |
+
def _loaded_gen_function(self, tp, name, module, library):
|
| 188 |
+
assert isinstance(tp, model.FunctionPtrType)
|
| 189 |
+
if tp.ellipsis:
|
| 190 |
+
newfunction = self._load_constant(False, tp, name, module)
|
| 191 |
+
else:
|
| 192 |
+
indirections = []
|
| 193 |
+
base_tp = tp
|
| 194 |
+
if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args)
|
| 195 |
+
or isinstance(tp.result, model.StructOrUnion)):
|
| 196 |
+
indirect_args = []
|
| 197 |
+
for i, typ in enumerate(tp.args):
|
| 198 |
+
if isinstance(typ, model.StructOrUnion):
|
| 199 |
+
typ = model.PointerType(typ)
|
| 200 |
+
indirections.append((i, typ))
|
| 201 |
+
indirect_args.append(typ)
|
| 202 |
+
indirect_result = tp.result
|
| 203 |
+
if isinstance(indirect_result, model.StructOrUnion):
|
| 204 |
+
if indirect_result.fldtypes is None:
|
| 205 |
+
raise TypeError("'%s' is used as result type, "
|
| 206 |
+
"but is opaque" % (
|
| 207 |
+
indirect_result._get_c_name(),))
|
| 208 |
+
indirect_result = model.PointerType(indirect_result)
|
| 209 |
+
indirect_args.insert(0, indirect_result)
|
| 210 |
+
indirections.insert(0, ("result", indirect_result))
|
| 211 |
+
indirect_result = model.void_type
|
| 212 |
+
tp = model.FunctionPtrType(tuple(indirect_args),
|
| 213 |
+
indirect_result, tp.ellipsis)
|
| 214 |
+
BFunc = self.ffi._get_cached_btype(tp)
|
| 215 |
+
wrappername = '_cffi_f_%s' % name
|
| 216 |
+
newfunction = module.load_function(BFunc, wrappername)
|
| 217 |
+
for i, typ in indirections:
|
| 218 |
+
newfunction = self._make_struct_wrapper(newfunction, i, typ,
|
| 219 |
+
base_tp)
|
| 220 |
+
setattr(library, name, newfunction)
|
| 221 |
+
type(library)._cffi_dir.append(name)
|
| 222 |
+
|
| 223 |
+
def _make_struct_wrapper(self, oldfunc, i, tp, base_tp):
|
| 224 |
+
backend = self.ffi._backend
|
| 225 |
+
BType = self.ffi._get_cached_btype(tp)
|
| 226 |
+
if i == "result":
|
| 227 |
+
ffi = self.ffi
|
| 228 |
+
def newfunc(*args):
|
| 229 |
+
res = ffi.new(BType)
|
| 230 |
+
oldfunc(res, *args)
|
| 231 |
+
return res[0]
|
| 232 |
+
else:
|
| 233 |
+
def newfunc(*args):
|
| 234 |
+
args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
|
| 235 |
+
return oldfunc(*args)
|
| 236 |
+
newfunc._cffi_base_type = base_tp
|
| 237 |
+
return newfunc
|
| 238 |
+
|
| 239 |
+
# ----------
|
| 240 |
+
# named structs
|
| 241 |
+
|
| 242 |
+
def _generate_gen_struct_decl(self, tp, name):
|
| 243 |
+
assert name == tp.name
|
| 244 |
+
self._generate_struct_or_union_decl(tp, 'struct', name)
|
| 245 |
+
|
| 246 |
+
def _loading_gen_struct(self, tp, name, module):
|
| 247 |
+
self._loading_struct_or_union(tp, 'struct', name, module)
|
| 248 |
+
|
| 249 |
+
def _loaded_gen_struct(self, tp, name, module, **kwds):
|
| 250 |
+
self._loaded_struct_or_union(tp)
|
| 251 |
+
|
| 252 |
+
def _generate_gen_union_decl(self, tp, name):
|
| 253 |
+
assert name == tp.name
|
| 254 |
+
self._generate_struct_or_union_decl(tp, 'union', name)
|
| 255 |
+
|
| 256 |
+
def _loading_gen_union(self, tp, name, module):
|
| 257 |
+
self._loading_struct_or_union(tp, 'union', name, module)
|
| 258 |
+
|
| 259 |
+
def _loaded_gen_union(self, tp, name, module, **kwds):
|
| 260 |
+
self._loaded_struct_or_union(tp)
|
| 261 |
+
|
| 262 |
+
def _generate_struct_or_union_decl(self, tp, prefix, name):
|
| 263 |
+
if tp.fldnames is None:
|
| 264 |
+
return # nothing to do with opaque structs
|
| 265 |
+
checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
|
| 266 |
+
layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
|
| 267 |
+
cname = ('%s %s' % (prefix, name)).strip()
|
| 268 |
+
#
|
| 269 |
+
prnt = self._prnt
|
| 270 |
+
prnt('static void %s(%s *p)' % (checkfuncname, cname))
|
| 271 |
+
prnt('{')
|
| 272 |
+
prnt(' /* only to generate compile-time warnings or errors */')
|
| 273 |
+
prnt(' (void)p;')
|
| 274 |
+
for fname, ftype, fbitsize, fqual in tp.enumfields():
|
| 275 |
+
if (isinstance(ftype, model.PrimitiveType)
|
| 276 |
+
and ftype.is_integer_type()) or fbitsize >= 0:
|
| 277 |
+
# accept all integers, but complain on float or double
|
| 278 |
+
prnt(' (void)((p->%s) << 1);' % fname)
|
| 279 |
+
else:
|
| 280 |
+
# only accept exactly the type declared.
|
| 281 |
+
try:
|
| 282 |
+
prnt(' { %s = &p->%s; (void)tmp; }' % (
|
| 283 |
+
ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
|
| 284 |
+
fname))
|
| 285 |
+
except VerificationError as e:
|
| 286 |
+
prnt(' /* %s */' % str(e)) # cannot verify it, ignore
|
| 287 |
+
prnt('}')
|
| 288 |
+
self.export_symbols.append(layoutfuncname)
|
| 289 |
+
prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,))
|
| 290 |
+
prnt('{')
|
| 291 |
+
prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname)
|
| 292 |
+
prnt(' static intptr_t nums[] = {')
|
| 293 |
+
prnt(' sizeof(%s),' % cname)
|
| 294 |
+
prnt(' offsetof(struct _cffi_aligncheck, y),')
|
| 295 |
+
for fname, ftype, fbitsize, fqual in tp.enumfields():
|
| 296 |
+
if fbitsize >= 0:
|
| 297 |
+
continue # xxx ignore fbitsize for now
|
| 298 |
+
prnt(' offsetof(%s, %s),' % (cname, fname))
|
| 299 |
+
if isinstance(ftype, model.ArrayType) and ftype.length is None:
|
| 300 |
+
prnt(' 0, /* %s */' % ftype._get_c_name())
|
| 301 |
+
else:
|
| 302 |
+
prnt(' sizeof(((%s *)0)->%s),' % (cname, fname))
|
| 303 |
+
prnt(' -1')
|
| 304 |
+
prnt(' };')
|
| 305 |
+
prnt(' return nums[i];')
|
| 306 |
+
prnt(' /* the next line is not executed, but compiled */')
|
| 307 |
+
prnt(' %s(0);' % (checkfuncname,))
|
| 308 |
+
prnt('}')
|
| 309 |
+
prnt()
|
| 310 |
+
|
| 311 |
+
def _loading_struct_or_union(self, tp, prefix, name, module):
|
| 312 |
+
if tp.fldnames is None:
|
| 313 |
+
return # nothing to do with opaque structs
|
| 314 |
+
layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
|
| 315 |
+
#
|
| 316 |
+
BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0]
|
| 317 |
+
function = module.load_function(BFunc, layoutfuncname)
|
| 318 |
+
layout = []
|
| 319 |
+
num = 0
|
| 320 |
+
while True:
|
| 321 |
+
x = function(num)
|
| 322 |
+
if x < 0: break
|
| 323 |
+
layout.append(x)
|
| 324 |
+
num += 1
|
| 325 |
+
if isinstance(tp, model.StructOrUnion) and tp.partial:
|
| 326 |
+
# use the function()'s sizes and offsets to guide the
|
| 327 |
+
# layout of the struct
|
| 328 |
+
totalsize = layout[0]
|
| 329 |
+
totalalignment = layout[1]
|
| 330 |
+
fieldofs = layout[2::2]
|
| 331 |
+
fieldsize = layout[3::2]
|
| 332 |
+
tp.force_flatten()
|
| 333 |
+
assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
|
| 334 |
+
tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
|
| 335 |
+
else:
|
| 336 |
+
cname = ('%s %s' % (prefix, name)).strip()
|
| 337 |
+
self._struct_pending_verification[tp] = layout, cname
|
| 338 |
+
|
| 339 |
+
def _loaded_struct_or_union(self, tp):
|
| 340 |
+
if tp.fldnames is None:
|
| 341 |
+
return # nothing to do with opaque structs
|
| 342 |
+
self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered
|
| 343 |
+
|
| 344 |
+
if tp in self._struct_pending_verification:
|
| 345 |
+
# check that the layout sizes and offsets match the real ones
|
| 346 |
+
def check(realvalue, expectedvalue, msg):
|
| 347 |
+
if realvalue != expectedvalue:
|
| 348 |
+
raise VerificationError(
|
| 349 |
+
"%s (we have %d, but C compiler says %d)"
|
| 350 |
+
% (msg, expectedvalue, realvalue))
|
| 351 |
+
ffi = self.ffi
|
| 352 |
+
BStruct = ffi._get_cached_btype(tp)
|
| 353 |
+
layout, cname = self._struct_pending_verification.pop(tp)
|
| 354 |
+
check(layout[0], ffi.sizeof(BStruct), "wrong total size")
|
| 355 |
+
check(layout[1], ffi.alignof(BStruct), "wrong total alignment")
|
| 356 |
+
i = 2
|
| 357 |
+
for fname, ftype, fbitsize, fqual in tp.enumfields():
|
| 358 |
+
if fbitsize >= 0:
|
| 359 |
+
continue # xxx ignore fbitsize for now
|
| 360 |
+
check(layout[i], ffi.offsetof(BStruct, fname),
|
| 361 |
+
"wrong offset for field %r" % (fname,))
|
| 362 |
+
if layout[i+1] != 0:
|
| 363 |
+
BField = ffi._get_cached_btype(ftype)
|
| 364 |
+
check(layout[i+1], ffi.sizeof(BField),
|
| 365 |
+
"wrong size for field %r" % (fname,))
|
| 366 |
+
i += 2
|
| 367 |
+
assert i == len(layout)
|
| 368 |
+
|
| 369 |
+
# ----------
|
| 370 |
+
# 'anonymous' declarations. These are produced for anonymous structs
|
| 371 |
+
# or unions; the 'name' is obtained by a typedef.
|
| 372 |
+
|
| 373 |
+
def _generate_gen_anonymous_decl(self, tp, name):
|
| 374 |
+
if isinstance(tp, model.EnumType):
|
| 375 |
+
self._generate_gen_enum_decl(tp, name, '')
|
| 376 |
+
else:
|
| 377 |
+
self._generate_struct_or_union_decl(tp, '', name)
|
| 378 |
+
|
| 379 |
+
def _loading_gen_anonymous(self, tp, name, module):
|
| 380 |
+
if isinstance(tp, model.EnumType):
|
| 381 |
+
self._loading_gen_enum(tp, name, module, '')
|
| 382 |
+
else:
|
| 383 |
+
self._loading_struct_or_union(tp, '', name, module)
|
| 384 |
+
|
| 385 |
+
def _loaded_gen_anonymous(self, tp, name, module, **kwds):
|
| 386 |
+
if isinstance(tp, model.EnumType):
|
| 387 |
+
self._loaded_gen_enum(tp, name, module, **kwds)
|
| 388 |
+
else:
|
| 389 |
+
self._loaded_struct_or_union(tp)
|
| 390 |
+
|
| 391 |
+
# ----------
|
| 392 |
+
# constants, likely declared with '#define'
|
| 393 |
+
|
| 394 |
+
def _generate_gen_const(self, is_int, name, tp=None, category='const',
|
| 395 |
+
check_value=None):
|
| 396 |
+
prnt = self._prnt
|
| 397 |
+
funcname = '_cffi_%s_%s' % (category, name)
|
| 398 |
+
self.export_symbols.append(funcname)
|
| 399 |
+
if check_value is not None:
|
| 400 |
+
assert is_int
|
| 401 |
+
assert category == 'const'
|
| 402 |
+
prnt('int %s(char *out_error)' % funcname)
|
| 403 |
+
prnt('{')
|
| 404 |
+
self._check_int_constant_value(name, check_value)
|
| 405 |
+
prnt(' return 0;')
|
| 406 |
+
prnt('}')
|
| 407 |
+
elif is_int:
|
| 408 |
+
assert category == 'const'
|
| 409 |
+
prnt('int %s(long long *out_value)' % funcname)
|
| 410 |
+
prnt('{')
|
| 411 |
+
prnt(' *out_value = (long long)(%s);' % (name,))
|
| 412 |
+
prnt(' return (%s) <= 0;' % (name,))
|
| 413 |
+
prnt('}')
|
| 414 |
+
else:
|
| 415 |
+
assert tp is not None
|
| 416 |
+
assert check_value is None
|
| 417 |
+
if category == 'var':
|
| 418 |
+
ampersand = '&'
|
| 419 |
+
else:
|
| 420 |
+
ampersand = ''
|
| 421 |
+
extra = ''
|
| 422 |
+
if category == 'const' and isinstance(tp, model.StructOrUnion):
|
| 423 |
+
extra = 'const *'
|
| 424 |
+
ampersand = '&'
|
| 425 |
+
prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name))
|
| 426 |
+
prnt('{')
|
| 427 |
+
prnt(' return (%s%s);' % (ampersand, name))
|
| 428 |
+
prnt('}')
|
| 429 |
+
prnt()
|
| 430 |
+
|
| 431 |
+
def _generate_gen_constant_decl(self, tp, name):
|
| 432 |
+
is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
|
| 433 |
+
self._generate_gen_const(is_int, name, tp)
|
| 434 |
+
|
| 435 |
+
_loading_gen_constant = _loaded_noop
|
| 436 |
+
|
| 437 |
+
def _load_constant(self, is_int, tp, name, module, check_value=None):
|
| 438 |
+
funcname = '_cffi_const_%s' % name
|
| 439 |
+
if check_value is not None:
|
| 440 |
+
assert is_int
|
| 441 |
+
self._load_known_int_constant(module, funcname)
|
| 442 |
+
value = check_value
|
| 443 |
+
elif is_int:
|
| 444 |
+
BType = self.ffi._typeof_locked("long long*")[0]
|
| 445 |
+
BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0]
|
| 446 |
+
function = module.load_function(BFunc, funcname)
|
| 447 |
+
p = self.ffi.new(BType)
|
| 448 |
+
negative = function(p)
|
| 449 |
+
value = int(p[0])
|
| 450 |
+
if value < 0 and not negative:
|
| 451 |
+
BLongLong = self.ffi._typeof_locked("long long")[0]
|
| 452 |
+
value += (1 << (8*self.ffi.sizeof(BLongLong)))
|
| 453 |
+
else:
|
| 454 |
+
assert check_value is None
|
| 455 |
+
fntypeextra = '(*)(void)'
|
| 456 |
+
if isinstance(tp, model.StructOrUnion):
|
| 457 |
+
fntypeextra = '*' + fntypeextra
|
| 458 |
+
BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0]
|
| 459 |
+
function = module.load_function(BFunc, funcname)
|
| 460 |
+
value = function()
|
| 461 |
+
if isinstance(tp, model.StructOrUnion):
|
| 462 |
+
value = value[0]
|
| 463 |
+
return value
|
| 464 |
+
|
| 465 |
+
def _loaded_gen_constant(self, tp, name, module, library):
|
| 466 |
+
is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
|
| 467 |
+
value = self._load_constant(is_int, tp, name, module)
|
| 468 |
+
setattr(library, name, value)
|
| 469 |
+
type(library)._cffi_dir.append(name)
|
| 470 |
+
|
| 471 |
+
# ----------
|
| 472 |
+
# enums
|
| 473 |
+
|
| 474 |
+
def _check_int_constant_value(self, name, value):
|
| 475 |
+
prnt = self._prnt
|
| 476 |
+
if value <= 0:
|
| 477 |
+
prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % (
|
| 478 |
+
name, name, value))
|
| 479 |
+
else:
|
| 480 |
+
prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
|
| 481 |
+
name, name, value))
|
| 482 |
+
prnt(' char buf[64];')
|
| 483 |
+
prnt(' if ((%s) <= 0)' % name)
|
| 484 |
+
prnt(' sprintf(buf, "%%ld", (long)(%s));' % name)
|
| 485 |
+
prnt(' else')
|
| 486 |
+
prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' %
|
| 487 |
+
name)
|
| 488 |
+
prnt(' sprintf(out_error, "%s has the real value %s, not %s",')
|
| 489 |
+
prnt(' "%s", buf, "%d");' % (name[:100], value))
|
| 490 |
+
prnt(' return -1;')
|
| 491 |
+
prnt(' }')
|
| 492 |
+
|
| 493 |
+
def _load_known_int_constant(self, module, funcname):
|
| 494 |
+
BType = self.ffi._typeof_locked("char[]")[0]
|
| 495 |
+
BFunc = self.ffi._typeof_locked("int(*)(char*)")[0]
|
| 496 |
+
function = module.load_function(BFunc, funcname)
|
| 497 |
+
p = self.ffi.new(BType, 256)
|
| 498 |
+
if function(p) < 0:
|
| 499 |
+
error = self.ffi.string(p)
|
| 500 |
+
if sys.version_info >= (3,):
|
| 501 |
+
error = str(error, 'utf-8')
|
| 502 |
+
raise VerificationError(error)
|
| 503 |
+
|
| 504 |
+
def _enum_funcname(self, prefix, name):
|
| 505 |
+
# "$enum_$1" => "___D_enum____D_1"
|
| 506 |
+
name = name.replace('$', '___D_')
|
| 507 |
+
return '_cffi_e_%s_%s' % (prefix, name)
|
| 508 |
+
|
| 509 |
+
def _generate_gen_enum_decl(self, tp, name, prefix='enum'):
|
| 510 |
+
if tp.partial:
|
| 511 |
+
for enumerator in tp.enumerators:
|
| 512 |
+
self._generate_gen_const(True, enumerator)
|
| 513 |
+
return
|
| 514 |
+
#
|
| 515 |
+
funcname = self._enum_funcname(prefix, name)
|
| 516 |
+
self.export_symbols.append(funcname)
|
| 517 |
+
prnt = self._prnt
|
| 518 |
+
prnt('int %s(char *out_error)' % funcname)
|
| 519 |
+
prnt('{')
|
| 520 |
+
for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
|
| 521 |
+
self._check_int_constant_value(enumerator, enumvalue)
|
| 522 |
+
prnt(' return 0;')
|
| 523 |
+
prnt('}')
|
| 524 |
+
prnt()
|
| 525 |
+
|
| 526 |
+
def _loading_gen_enum(self, tp, name, module, prefix='enum'):
|
| 527 |
+
if tp.partial:
|
| 528 |
+
enumvalues = [self._load_constant(True, tp, enumerator, module)
|
| 529 |
+
for enumerator in tp.enumerators]
|
| 530 |
+
tp.enumvalues = tuple(enumvalues)
|
| 531 |
+
tp.partial_resolved = True
|
| 532 |
+
else:
|
| 533 |
+
funcname = self._enum_funcname(prefix, name)
|
| 534 |
+
self._load_known_int_constant(module, funcname)
|
| 535 |
+
|
| 536 |
+
def _loaded_gen_enum(self, tp, name, module, library):
|
| 537 |
+
for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
|
| 538 |
+
setattr(library, enumerator, enumvalue)
|
| 539 |
+
type(library)._cffi_dir.append(enumerator)
|
| 540 |
+
|
| 541 |
+
# ----------
|
| 542 |
+
# macros: for now only for integers
|
| 543 |
+
|
| 544 |
+
def _generate_gen_macro_decl(self, tp, name):
|
| 545 |
+
if tp == '...':
|
| 546 |
+
check_value = None
|
| 547 |
+
else:
|
| 548 |
+
check_value = tp # an integer
|
| 549 |
+
self._generate_gen_const(True, name, check_value=check_value)
|
| 550 |
+
|
| 551 |
+
_loading_gen_macro = _loaded_noop
|
| 552 |
+
|
| 553 |
+
def _loaded_gen_macro(self, tp, name, module, library):
|
| 554 |
+
if tp == '...':
|
| 555 |
+
check_value = None
|
| 556 |
+
else:
|
| 557 |
+
check_value = tp # an integer
|
| 558 |
+
value = self._load_constant(True, tp, name, module,
|
| 559 |
+
check_value=check_value)
|
| 560 |
+
setattr(library, name, value)
|
| 561 |
+
type(library)._cffi_dir.append(name)
|
| 562 |
+
|
| 563 |
+
# ----------
|
| 564 |
+
# global variables
|
| 565 |
+
|
| 566 |
+
def _generate_gen_variable_decl(self, tp, name):
|
| 567 |
+
if isinstance(tp, model.ArrayType):
|
| 568 |
+
if tp.length_is_unknown():
|
| 569 |
+
prnt = self._prnt
|
| 570 |
+
funcname = '_cffi_sizeof_%s' % (name,)
|
| 571 |
+
self.export_symbols.append(funcname)
|
| 572 |
+
prnt("size_t %s(void)" % funcname)
|
| 573 |
+
prnt("{")
|
| 574 |
+
prnt(" return sizeof(%s);" % (name,))
|
| 575 |
+
prnt("}")
|
| 576 |
+
tp_ptr = model.PointerType(tp.item)
|
| 577 |
+
self._generate_gen_const(False, name, tp_ptr)
|
| 578 |
+
else:
|
| 579 |
+
tp_ptr = model.PointerType(tp)
|
| 580 |
+
self._generate_gen_const(False, name, tp_ptr, category='var')
|
| 581 |
+
|
| 582 |
+
_loading_gen_variable = _loaded_noop
|
| 583 |
+
|
| 584 |
+
def _loaded_gen_variable(self, tp, name, module, library):
|
| 585 |
+
if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the
|
| 586 |
+
# sense that "a=..." is forbidden
|
| 587 |
+
if tp.length_is_unknown():
|
| 588 |
+
funcname = '_cffi_sizeof_%s' % (name,)
|
| 589 |
+
BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0]
|
| 590 |
+
function = module.load_function(BFunc, funcname)
|
| 591 |
+
size = function()
|
| 592 |
+
BItemType = self.ffi._get_cached_btype(tp.item)
|
| 593 |
+
length, rest = divmod(size, self.ffi.sizeof(BItemType))
|
| 594 |
+
if rest != 0:
|
| 595 |
+
raise VerificationError(
|
| 596 |
+
"bad size: %r does not seem to be an array of %s" %
|
| 597 |
+
(name, tp.item))
|
| 598 |
+
tp = tp.resolve_length(length)
|
| 599 |
+
tp_ptr = model.PointerType(tp.item)
|
| 600 |
+
value = self._load_constant(False, tp_ptr, name, module)
|
| 601 |
+
# 'value' is a <cdata 'type *'> which we have to replace with
|
| 602 |
+
# a <cdata 'type[N]'> if the N is actually known
|
| 603 |
+
if tp.length is not None:
|
| 604 |
+
BArray = self.ffi._get_cached_btype(tp)
|
| 605 |
+
value = self.ffi.cast(BArray, value)
|
| 606 |
+
setattr(library, name, value)
|
| 607 |
+
type(library)._cffi_dir.append(name)
|
| 608 |
+
return
|
| 609 |
+
# remove ptr=<cdata 'int *'> from the library instance, and replace
|
| 610 |
+
# it by a property on the class, which reads/writes into ptr[0].
|
| 611 |
+
funcname = '_cffi_var_%s' % name
|
| 612 |
+
BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0]
|
| 613 |
+
function = module.load_function(BFunc, funcname)
|
| 614 |
+
ptr = function()
|
| 615 |
+
def getter(library):
|
| 616 |
+
return ptr[0]
|
| 617 |
+
def setter(library, value):
|
| 618 |
+
ptr[0] = value
|
| 619 |
+
setattr(type(library), name, property(getter, setter))
|
| 620 |
+
type(library)._cffi_dir.append(name)
|
| 621 |
+
|
| 622 |
+
cffimod_header = r'''
|
| 623 |
+
#include <stdio.h>
|
| 624 |
+
#include <stddef.h>
|
| 625 |
+
#include <stdarg.h>
|
| 626 |
+
#include <errno.h>
|
| 627 |
+
#include <sys/types.h> /* XXX for ssize_t on some platforms */
|
| 628 |
+
|
| 629 |
+
/* this block of #ifs should be kept exactly identical between
|
| 630 |
+
c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
|
| 631 |
+
and cffi/_cffi_include.h */
|
| 632 |
+
#if defined(_MSC_VER)
|
| 633 |
+
# include <malloc.h> /* for alloca() */
|
| 634 |
+
# if _MSC_VER < 1600 /* MSVC < 2010 */
|
| 635 |
+
typedef __int8 int8_t;
|
| 636 |
+
typedef __int16 int16_t;
|
| 637 |
+
typedef __int32 int32_t;
|
| 638 |
+
typedef __int64 int64_t;
|
| 639 |
+
typedef unsigned __int8 uint8_t;
|
| 640 |
+
typedef unsigned __int16 uint16_t;
|
| 641 |
+
typedef unsigned __int32 uint32_t;
|
| 642 |
+
typedef unsigned __int64 uint64_t;
|
| 643 |
+
typedef __int8 int_least8_t;
|
| 644 |
+
typedef __int16 int_least16_t;
|
| 645 |
+
typedef __int32 int_least32_t;
|
| 646 |
+
typedef __int64 int_least64_t;
|
| 647 |
+
typedef unsigned __int8 uint_least8_t;
|
| 648 |
+
typedef unsigned __int16 uint_least16_t;
|
| 649 |
+
typedef unsigned __int32 uint_least32_t;
|
| 650 |
+
typedef unsigned __int64 uint_least64_t;
|
| 651 |
+
typedef __int8 int_fast8_t;
|
| 652 |
+
typedef __int16 int_fast16_t;
|
| 653 |
+
typedef __int32 int_fast32_t;
|
| 654 |
+
typedef __int64 int_fast64_t;
|
| 655 |
+
typedef unsigned __int8 uint_fast8_t;
|
| 656 |
+
typedef unsigned __int16 uint_fast16_t;
|
| 657 |
+
typedef unsigned __int32 uint_fast32_t;
|
| 658 |
+
typedef unsigned __int64 uint_fast64_t;
|
| 659 |
+
typedef __int64 intmax_t;
|
| 660 |
+
typedef unsigned __int64 uintmax_t;
|
| 661 |
+
# else
|
| 662 |
+
# include <stdint.h>
|
| 663 |
+
# endif
|
| 664 |
+
# if _MSC_VER < 1800 /* MSVC < 2013 */
|
| 665 |
+
# ifndef __cplusplus
|
| 666 |
+
typedef unsigned char _Bool;
|
| 667 |
+
# endif
|
| 668 |
+
# endif
|
| 669 |
+
# define _cffi_float_complex_t _Fcomplex /* include <complex.h> for it */
|
| 670 |
+
# define _cffi_double_complex_t _Dcomplex /* include <complex.h> for it */
|
| 671 |
+
#else
|
| 672 |
+
# include <stdint.h>
|
| 673 |
+
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
|
| 674 |
+
# include <alloca.h>
|
| 675 |
+
# endif
|
| 676 |
+
# define _cffi_float_complex_t float _Complex
|
| 677 |
+
# define _cffi_double_complex_t double _Complex
|
| 678 |
+
#endif
|
| 679 |
+
'''
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/verifier.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# DEPRECATED: implementation for ffi.verify()
|
| 3 |
+
#
|
| 4 |
+
import sys, os, binascii, shutil, io
|
| 5 |
+
from . import __version_verifier_modules__
|
| 6 |
+
from . import ffiplatform
|
| 7 |
+
from .error import VerificationError
|
| 8 |
+
|
| 9 |
+
if sys.version_info >= (3, 3):
|
| 10 |
+
import importlib.machinery
|
| 11 |
+
def _extension_suffixes():
|
| 12 |
+
return importlib.machinery.EXTENSION_SUFFIXES[:]
|
| 13 |
+
else:
|
| 14 |
+
import imp
|
| 15 |
+
def _extension_suffixes():
|
| 16 |
+
return [suffix for suffix, _, type in imp.get_suffixes()
|
| 17 |
+
if type == imp.C_EXTENSION]
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
if sys.version_info >= (3,):
|
| 21 |
+
NativeIO = io.StringIO
|
| 22 |
+
else:
|
| 23 |
+
class NativeIO(io.BytesIO):
|
| 24 |
+
def write(self, s):
|
| 25 |
+
if isinstance(s, unicode):
|
| 26 |
+
s = s.encode('ascii')
|
| 27 |
+
super().write(s)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class Verifier:
|
| 31 |
+
|
| 32 |
+
def __init__(self, ffi, preamble, tmpdir=None, modulename=None,
|
| 33 |
+
ext_package=None, tag='', force_generic_engine=False,
|
| 34 |
+
source_extension='.c', flags=None, relative_to=None, **kwds):
|
| 35 |
+
if ffi._parser._uses_new_feature:
|
| 36 |
+
raise VerificationError(
|
| 37 |
+
"feature not supported with ffi.verify(), but only "
|
| 38 |
+
"with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,))
|
| 39 |
+
self.ffi = ffi
|
| 40 |
+
self.preamble = preamble
|
| 41 |
+
if not modulename:
|
| 42 |
+
flattened_kwds = ffiplatform.flatten(kwds)
|
| 43 |
+
vengine_class = _locate_engine_class(ffi, force_generic_engine)
|
| 44 |
+
self._vengine = vengine_class(self)
|
| 45 |
+
self._vengine.patch_extension_kwds(kwds)
|
| 46 |
+
self.flags = flags
|
| 47 |
+
self.kwds = self.make_relative_to(kwds, relative_to)
|
| 48 |
+
#
|
| 49 |
+
if modulename:
|
| 50 |
+
if tag:
|
| 51 |
+
raise TypeError("can't specify both 'modulename' and 'tag'")
|
| 52 |
+
else:
|
| 53 |
+
key = '\x00'.join(['%d.%d' % sys.version_info[:2],
|
| 54 |
+
__version_verifier_modules__,
|
| 55 |
+
preamble, flattened_kwds] +
|
| 56 |
+
ffi._cdefsources)
|
| 57 |
+
if sys.version_info >= (3,):
|
| 58 |
+
key = key.encode('utf-8')
|
| 59 |
+
k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
|
| 60 |
+
k1 = k1.lstrip('0x').rstrip('L')
|
| 61 |
+
k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
|
| 62 |
+
k2 = k2.lstrip('0').rstrip('L')
|
| 63 |
+
modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key,
|
| 64 |
+
k1, k2)
|
| 65 |
+
suffix = _get_so_suffixes()[0]
|
| 66 |
+
self.tmpdir = tmpdir or _caller_dir_pycache()
|
| 67 |
+
self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension)
|
| 68 |
+
self.modulefilename = os.path.join(self.tmpdir, modulename + suffix)
|
| 69 |
+
self.ext_package = ext_package
|
| 70 |
+
self._has_source = False
|
| 71 |
+
self._has_module = False
|
| 72 |
+
|
| 73 |
+
def write_source(self, file=None):
|
| 74 |
+
"""Write the C source code. It is produced in 'self.sourcefilename',
|
| 75 |
+
which can be tweaked beforehand."""
|
| 76 |
+
with self.ffi._lock:
|
| 77 |
+
if self._has_source and file is None:
|
| 78 |
+
raise VerificationError(
|
| 79 |
+
"source code already written")
|
| 80 |
+
self._write_source(file)
|
| 81 |
+
|
| 82 |
+
def compile_module(self):
|
| 83 |
+
"""Write the C source code (if not done already) and compile it.
|
| 84 |
+
This produces a dynamic link library in 'self.modulefilename'."""
|
| 85 |
+
with self.ffi._lock:
|
| 86 |
+
if self._has_module:
|
| 87 |
+
raise VerificationError("module already compiled")
|
| 88 |
+
if not self._has_source:
|
| 89 |
+
self._write_source()
|
| 90 |
+
self._compile_module()
|
| 91 |
+
|
| 92 |
+
def load_library(self):
|
| 93 |
+
"""Get a C module from this Verifier instance.
|
| 94 |
+
Returns an instance of a FFILibrary class that behaves like the
|
| 95 |
+
objects returned by ffi.dlopen(), but that delegates all
|
| 96 |
+
operations to the C module. If necessary, the C code is written
|
| 97 |
+
and compiled first.
|
| 98 |
+
"""
|
| 99 |
+
with self.ffi._lock:
|
| 100 |
+
if not self._has_module:
|
| 101 |
+
self._locate_module()
|
| 102 |
+
if not self._has_module:
|
| 103 |
+
if not self._has_source:
|
| 104 |
+
self._write_source()
|
| 105 |
+
self._compile_module()
|
| 106 |
+
return self._load_library()
|
| 107 |
+
|
| 108 |
+
def get_module_name(self):
|
| 109 |
+
basename = os.path.basename(self.modulefilename)
|
| 110 |
+
# kill both the .so extension and the other .'s, as introduced
|
| 111 |
+
# by Python 3: 'basename.cpython-33m.so'
|
| 112 |
+
basename = basename.split('.', 1)[0]
|
| 113 |
+
# and the _d added in Python 2 debug builds --- but try to be
|
| 114 |
+
# conservative and not kill a legitimate _d
|
| 115 |
+
if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'):
|
| 116 |
+
basename = basename[:-2]
|
| 117 |
+
return basename
|
| 118 |
+
|
| 119 |
+
def get_extension(self):
|
| 120 |
+
if not self._has_source:
|
| 121 |
+
with self.ffi._lock:
|
| 122 |
+
if not self._has_source:
|
| 123 |
+
self._write_source()
|
| 124 |
+
sourcename = ffiplatform.maybe_relative_path(self.sourcefilename)
|
| 125 |
+
modname = self.get_module_name()
|
| 126 |
+
return ffiplatform.get_extension(sourcename, modname, **self.kwds)
|
| 127 |
+
|
| 128 |
+
def generates_python_module(self):
|
| 129 |
+
return self._vengine._gen_python_module
|
| 130 |
+
|
| 131 |
+
def make_relative_to(self, kwds, relative_to):
|
| 132 |
+
if relative_to and os.path.dirname(relative_to):
|
| 133 |
+
dirname = os.path.dirname(relative_to)
|
| 134 |
+
kwds = kwds.copy()
|
| 135 |
+
for key in ffiplatform.LIST_OF_FILE_NAMES:
|
| 136 |
+
if key in kwds:
|
| 137 |
+
lst = kwds[key]
|
| 138 |
+
if not isinstance(lst, (list, tuple)):
|
| 139 |
+
raise TypeError("keyword '%s' should be a list or tuple"
|
| 140 |
+
% (key,))
|
| 141 |
+
lst = [os.path.join(dirname, fn) for fn in lst]
|
| 142 |
+
kwds[key] = lst
|
| 143 |
+
return kwds
|
| 144 |
+
|
| 145 |
+
# ----------
|
| 146 |
+
|
| 147 |
+
def _locate_module(self):
|
| 148 |
+
if not os.path.isfile(self.modulefilename):
|
| 149 |
+
if self.ext_package:
|
| 150 |
+
try:
|
| 151 |
+
pkg = __import__(self.ext_package, None, None, ['__doc__'])
|
| 152 |
+
except ImportError:
|
| 153 |
+
return # cannot import the package itself, give up
|
| 154 |
+
# (e.g. it might be called differently before installation)
|
| 155 |
+
path = pkg.__path__
|
| 156 |
+
else:
|
| 157 |
+
path = None
|
| 158 |
+
filename = self._vengine.find_module(self.get_module_name(), path,
|
| 159 |
+
_get_so_suffixes())
|
| 160 |
+
if filename is None:
|
| 161 |
+
return
|
| 162 |
+
self.modulefilename = filename
|
| 163 |
+
self._vengine.collect_types()
|
| 164 |
+
self._has_module = True
|
| 165 |
+
|
| 166 |
+
def _write_source_to(self, file):
|
| 167 |
+
self._vengine._f = file
|
| 168 |
+
try:
|
| 169 |
+
self._vengine.write_source_to_f()
|
| 170 |
+
finally:
|
| 171 |
+
del self._vengine._f
|
| 172 |
+
|
| 173 |
+
def _write_source(self, file=None):
|
| 174 |
+
if file is not None:
|
| 175 |
+
self._write_source_to(file)
|
| 176 |
+
else:
|
| 177 |
+
# Write our source file to an in memory file.
|
| 178 |
+
f = NativeIO()
|
| 179 |
+
self._write_source_to(f)
|
| 180 |
+
source_data = f.getvalue()
|
| 181 |
+
|
| 182 |
+
# Determine if this matches the current file
|
| 183 |
+
if os.path.exists(self.sourcefilename):
|
| 184 |
+
with open(self.sourcefilename, "r") as fp:
|
| 185 |
+
needs_written = fp.read() != source_data
|
| 186 |
+
else:
|
| 187 |
+
needs_written = True
|
| 188 |
+
|
| 189 |
+
# Actually write the file out if it doesn't match
|
| 190 |
+
if needs_written:
|
| 191 |
+
_ensure_dir(self.sourcefilename)
|
| 192 |
+
with open(self.sourcefilename, "w") as fp:
|
| 193 |
+
fp.write(source_data)
|
| 194 |
+
|
| 195 |
+
# Set this flag
|
| 196 |
+
self._has_source = True
|
| 197 |
+
|
| 198 |
+
def _compile_module(self):
|
| 199 |
+
# compile this C source
|
| 200 |
+
tmpdir = os.path.dirname(self.sourcefilename)
|
| 201 |
+
outputfilename = ffiplatform.compile(tmpdir, self.get_extension())
|
| 202 |
+
try:
|
| 203 |
+
same = ffiplatform.samefile(outputfilename, self.modulefilename)
|
| 204 |
+
except OSError:
|
| 205 |
+
same = False
|
| 206 |
+
if not same:
|
| 207 |
+
_ensure_dir(self.modulefilename)
|
| 208 |
+
shutil.move(outputfilename, self.modulefilename)
|
| 209 |
+
self._has_module = True
|
| 210 |
+
|
| 211 |
+
def _load_library(self):
|
| 212 |
+
assert self._has_module
|
| 213 |
+
if self.flags is not None:
|
| 214 |
+
return self._vengine.load_library(self.flags)
|
| 215 |
+
else:
|
| 216 |
+
return self._vengine.load_library()
|
| 217 |
+
|
| 218 |
+
# ____________________________________________________________
|
| 219 |
+
|
| 220 |
+
_FORCE_GENERIC_ENGINE = False # for tests
|
| 221 |
+
|
| 222 |
+
def _locate_engine_class(ffi, force_generic_engine):
|
| 223 |
+
if _FORCE_GENERIC_ENGINE:
|
| 224 |
+
force_generic_engine = True
|
| 225 |
+
if not force_generic_engine:
|
| 226 |
+
if '__pypy__' in sys.builtin_module_names:
|
| 227 |
+
force_generic_engine = True
|
| 228 |
+
else:
|
| 229 |
+
try:
|
| 230 |
+
import _cffi_backend
|
| 231 |
+
except ImportError:
|
| 232 |
+
_cffi_backend = '?'
|
| 233 |
+
if ffi._backend is not _cffi_backend:
|
| 234 |
+
force_generic_engine = True
|
| 235 |
+
if force_generic_engine:
|
| 236 |
+
from . import vengine_gen
|
| 237 |
+
return vengine_gen.VGenericEngine
|
| 238 |
+
else:
|
| 239 |
+
from . import vengine_cpy
|
| 240 |
+
return vengine_cpy.VCPythonEngine
|
| 241 |
+
|
| 242 |
+
# ____________________________________________________________
|
| 243 |
+
|
| 244 |
+
_TMPDIR = None
|
| 245 |
+
|
| 246 |
+
def _caller_dir_pycache():
|
| 247 |
+
if _TMPDIR:
|
| 248 |
+
return _TMPDIR
|
| 249 |
+
result = os.environ.get('CFFI_TMPDIR')
|
| 250 |
+
if result:
|
| 251 |
+
return result
|
| 252 |
+
filename = sys._getframe(2).f_code.co_filename
|
| 253 |
+
return os.path.abspath(os.path.join(os.path.dirname(filename),
|
| 254 |
+
'__pycache__'))
|
| 255 |
+
|
| 256 |
+
def set_tmpdir(dirname):
|
| 257 |
+
"""Set the temporary directory to use instead of __pycache__."""
|
| 258 |
+
global _TMPDIR
|
| 259 |
+
_TMPDIR = dirname
|
| 260 |
+
|
| 261 |
+
def cleanup_tmpdir(tmpdir=None, keep_so=False):
|
| 262 |
+
"""Clean up the temporary directory by removing all files in it
|
| 263 |
+
called `_cffi_*.{c,so}` as well as the `build` subdirectory."""
|
| 264 |
+
tmpdir = tmpdir or _caller_dir_pycache()
|
| 265 |
+
try:
|
| 266 |
+
filelist = os.listdir(tmpdir)
|
| 267 |
+
except OSError:
|
| 268 |
+
return
|
| 269 |
+
if keep_so:
|
| 270 |
+
suffix = '.c' # only remove .c files
|
| 271 |
+
else:
|
| 272 |
+
suffix = _get_so_suffixes()[0].lower()
|
| 273 |
+
for fn in filelist:
|
| 274 |
+
if fn.lower().startswith('_cffi_') and (
|
| 275 |
+
fn.lower().endswith(suffix) or fn.lower().endswith('.c')):
|
| 276 |
+
try:
|
| 277 |
+
os.unlink(os.path.join(tmpdir, fn))
|
| 278 |
+
except OSError:
|
| 279 |
+
pass
|
| 280 |
+
clean_dir = [os.path.join(tmpdir, 'build')]
|
| 281 |
+
for dir in clean_dir:
|
| 282 |
+
try:
|
| 283 |
+
for fn in os.listdir(dir):
|
| 284 |
+
fn = os.path.join(dir, fn)
|
| 285 |
+
if os.path.isdir(fn):
|
| 286 |
+
clean_dir.append(fn)
|
| 287 |
+
else:
|
| 288 |
+
os.unlink(fn)
|
| 289 |
+
except OSError:
|
| 290 |
+
pass
|
| 291 |
+
|
| 292 |
+
def _get_so_suffixes():
|
| 293 |
+
suffixes = _extension_suffixes()
|
| 294 |
+
if not suffixes:
|
| 295 |
+
# bah, no C_EXTENSION available. Occurs on pypy without cpyext
|
| 296 |
+
if sys.platform == 'win32':
|
| 297 |
+
suffixes = [".pyd"]
|
| 298 |
+
else:
|
| 299 |
+
suffixes = [".so"]
|
| 300 |
+
|
| 301 |
+
return suffixes
|
| 302 |
+
|
| 303 |
+
def _ensure_dir(filename):
|
| 304 |
+
dirname = os.path.dirname(filename)
|
| 305 |
+
if dirname and not os.path.isdir(dirname):
|
| 306 |
+
os.makedirs(dirname)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
uv
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/METADATA
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: click
|
| 3 |
+
Version: 8.4.2
|
| 4 |
+
Summary: Composable command line interface toolkit
|
| 5 |
+
Maintainer-email: Pallets <contact@palletsprojects.com>
|
| 6 |
+
Requires-Python: >=3.10
|
| 7 |
+
Description-Content-Type: text/markdown
|
| 8 |
+
License-Expression: BSD-3-Clause
|
| 9 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 10 |
+
Classifier: Intended Audience :: Developers
|
| 11 |
+
Classifier: Operating System :: OS Independent
|
| 12 |
+
Classifier: Programming Language :: Python
|
| 13 |
+
Classifier: Typing :: Typed
|
| 14 |
+
License-File: LICENSE.txt
|
| 15 |
+
Requires-Dist: colorama; platform_system == 'Windows'
|
| 16 |
+
Project-URL: Changes, https://click.palletsprojects.com/page/changes/
|
| 17 |
+
Project-URL: Chat, https://discord.gg/pallets
|
| 18 |
+
Project-URL: Documentation, https://click.palletsprojects.com/
|
| 19 |
+
Project-URL: Donate, https://palletsprojects.com/donate
|
| 20 |
+
Project-URL: Source, https://github.com/pallets/click/
|
| 21 |
+
|
| 22 |
+
<div align="center"><img src="https://raw.githubusercontent.com/pallets/click/refs/heads/stable/docs/_static/click-name.svg" alt="" height="150"></div>
|
| 23 |
+
|
| 24 |
+
# Click
|
| 25 |
+
|
| 26 |
+
Click is a Python package for creating beautiful command line interfaces
|
| 27 |
+
in a composable way with as little code as necessary. It's the "Command
|
| 28 |
+
Line Interface Creation Kit". It's highly configurable but comes with
|
| 29 |
+
sensible defaults out of the box.
|
| 30 |
+
|
| 31 |
+
It aims to make the process of writing command line tools quick and fun
|
| 32 |
+
while also preventing any frustration caused by the inability to
|
| 33 |
+
implement an intended CLI API.
|
| 34 |
+
|
| 35 |
+
Click in three points:
|
| 36 |
+
|
| 37 |
+
- Arbitrary nesting of commands
|
| 38 |
+
- Automatic help page generation
|
| 39 |
+
- Supports lazy loading of subcommands at runtime
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
## A Simple Example
|
| 43 |
+
|
| 44 |
+
```python
|
| 45 |
+
import click
|
| 46 |
+
|
| 47 |
+
@click.command()
|
| 48 |
+
@click.option("--count", default=1, help="Number of greetings.")
|
| 49 |
+
@click.option("--name", prompt="Your name", help="The person to greet.")
|
| 50 |
+
def hello(count, name):
|
| 51 |
+
"""Simple program that greets NAME for a total of COUNT times."""
|
| 52 |
+
for _ in range(count):
|
| 53 |
+
click.echo(f"Hello, {name}!")
|
| 54 |
+
|
| 55 |
+
if __name__ == '__main__':
|
| 56 |
+
hello()
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
```
|
| 60 |
+
$ python hello.py --count=3
|
| 61 |
+
Your name: Click
|
| 62 |
+
Hello, Click!
|
| 63 |
+
Hello, Click!
|
| 64 |
+
Hello, Click!
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
## Donate
|
| 69 |
+
|
| 70 |
+
The Pallets organization develops and supports Click and other popular
|
| 71 |
+
packages. In order to grow the community of contributors and users, and
|
| 72 |
+
allow the maintainers to devote more time to the projects, [please
|
| 73 |
+
donate today][].
|
| 74 |
+
|
| 75 |
+
[please donate today]: https://palletsprojects.com/donate
|
| 76 |
+
|
| 77 |
+
## Contributing
|
| 78 |
+
|
| 79 |
+
See our [detailed contributing documentation][contrib] for many ways to
|
| 80 |
+
contribute, including reporting issues, requesting features, asking or answering
|
| 81 |
+
questions, and making PRs.
|
| 82 |
+
|
| 83 |
+
[contrib]: https://palletsprojects.com/contributing/
|
| 84 |
+
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/RECORD
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
click-8.4.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
| 2 |
+
click-8.4.2.dist-info/METADATA,sha256=GUyd2B1Wf5CB8CbH5AEGD7r6e8FHyOClizZotApkwDE,2621
|
| 3 |
+
click-8.4.2.dist-info/RECORD,,
|
| 4 |
+
click-8.4.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 5 |
+
click-8.4.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
| 6 |
+
click-8.4.2.dist-info/licenses/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475
|
| 7 |
+
click/__init__.py,sha256=FId2fXCSJB3yeWD-e2uON-mBhFa2Yc9MvXGmHu8OXG0,4634
|
| 8 |
+
click/_compat.py,sha256=gPNtXQ9q-G6Qil2b-MC5CsHsGGcQ4u6YSWy9_tlmuhc,18879
|
| 9 |
+
click/_termui_impl.py,sha256=CGdg24AeXijeGSzbu0Z7x3c4aaahVFjVBpEbbjhQ5K4,31730
|
| 10 |
+
click/_textwrap.py,sha256=7Z0N7Vmn-66TNSTUwp6OXJbcUXRmYET9h9c2ucD8oQQ,6270
|
| 11 |
+
click/_utils.py,sha256=eCZCtwJtsYD5QYkkNWJ8MY_8ABIjy8MczgMMyVY32rQ,996
|
| 12 |
+
click/_winconsole.py,sha256=KSxfNbMlYRa6GOJuCLgsg2Pb3dVkgJNPqLJPae-Pa10,8543
|
| 13 |
+
click/core.py,sha256=rZz76ihNTFV4Y2sxp3H-m93GxL2acD5Pqs0IobEvmuk,140616
|
| 14 |
+
click/decorators.py,sha256=9e1Ndu4jhGAcP6RGdNPAwAWtuP9hEs4ETp1u3lKmH1o,19709
|
| 15 |
+
click/exceptions.py,sha256=HvSY34G4auj_bYRR8-T8CU8Jwq_1-OcsRU4ezfozeEk,11862
|
| 16 |
+
click/formatting.py,sha256=8SW2KGkvjfz9Q1NbeojMHuZBN0cfnQJDs4mqDP6oXms,10444
|
| 17 |
+
click/globals.py,sha256=gM-Nh6A4M0HB_SgkaF5M4ncGGMDHc_flHXu9_oh4GEU,1923
|
| 18 |
+
click/parser.py,sha256=oJ-fU_3mvxugIuNtHaCATZ56lgEmHRggjJiSqEgYrjA,19052
|
| 19 |
+
click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 20 |
+
click/shell_completion.py,sha256=5tGGY5pV3mAZ17xT23OnuKrWqzEyyLVtrJ30npUxjkU,22618
|
| 21 |
+
click/termui.py,sha256=Vn9ehmrQl92z2_6R4bVZOsHUI6j8LrT8u0RzNZUpCvY,33213
|
| 22 |
+
click/testing.py,sha256=S9I-pspAlJH3RvZJWDQoJXb-M0nrAEJzXcUzrVXsT34,26458
|
| 23 |
+
click/types.py,sha256=9G4DB-nBj-omA_XWsYwbQ3H9BkpH82wJj-kxIPScKmA,44788
|
| 24 |
+
click/utils.py,sha256=XwrDxOzU__rnHn-rvJmJcD7ecbypUKMeDJQRjN2F-OA,20942
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/REQUESTED
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: flit 3.12.0
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/licenses/LICENSE.txt
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright 2014 Pallets
|
| 2 |
+
|
| 3 |
+
Redistribution and use in source and binary forms, with or without
|
| 4 |
+
modification, are permitted provided that the following conditions are
|
| 5 |
+
met:
|
| 6 |
+
|
| 7 |
+
1. Redistributions of source code must retain the above copyright
|
| 8 |
+
notice, this list of conditions and the following disclaimer.
|
| 9 |
+
|
| 10 |
+
2. Redistributions in binary form must reproduce the above copyright
|
| 11 |
+
notice, this list of conditions and the following disclaimer in the
|
| 12 |
+
documentation and/or other materials provided with the distribution.
|
| 13 |
+
|
| 14 |
+
3. Neither the name of the copyright holder nor the names of its
|
| 15 |
+
contributors may be used to endorse or promote products derived from
|
| 16 |
+
this software without specific prior written permission.
|
| 17 |
+
|
| 18 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 19 |
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 20 |
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
| 21 |
+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 22 |
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 23 |
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
| 24 |
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
| 25 |
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
| 26 |
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
| 27 |
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 28 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/__init__.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Click is a simple Python module inspired by the stdlib optparse to make
|
| 3 |
+
writing command line scripts fun. Unlike other modules, it's based
|
| 4 |
+
around a simple API that does not come with too much magic and is
|
| 5 |
+
composable.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
from .core import Argument as Argument
|
| 11 |
+
from .core import Command as Command
|
| 12 |
+
from .core import CommandCollection as CommandCollection
|
| 13 |
+
from .core import Context as Context
|
| 14 |
+
from .core import Group as Group
|
| 15 |
+
from .core import Option as Option
|
| 16 |
+
from .core import Parameter as Parameter
|
| 17 |
+
from .core import ParameterSource as ParameterSource
|
| 18 |
+
from .decorators import argument as argument
|
| 19 |
+
from .decorators import command as command
|
| 20 |
+
from .decorators import confirmation_option as confirmation_option
|
| 21 |
+
from .decorators import group as group
|
| 22 |
+
from .decorators import help_option as help_option
|
| 23 |
+
from .decorators import make_pass_decorator as make_pass_decorator
|
| 24 |
+
from .decorators import option as option
|
| 25 |
+
from .decorators import pass_context as pass_context
|
| 26 |
+
from .decorators import pass_obj as pass_obj
|
| 27 |
+
from .decorators import password_option as password_option
|
| 28 |
+
from .decorators import version_option as version_option
|
| 29 |
+
from .exceptions import Abort as Abort
|
| 30 |
+
from .exceptions import BadArgumentUsage as BadArgumentUsage
|
| 31 |
+
from .exceptions import BadOptionUsage as BadOptionUsage
|
| 32 |
+
from .exceptions import BadParameter as BadParameter
|
| 33 |
+
from .exceptions import ClickException as ClickException
|
| 34 |
+
from .exceptions import FileError as FileError
|
| 35 |
+
from .exceptions import MissingParameter as MissingParameter
|
| 36 |
+
from .exceptions import NoSuchCommand as NoSuchCommand
|
| 37 |
+
from .exceptions import NoSuchOption as NoSuchOption
|
| 38 |
+
from .exceptions import UsageError as UsageError
|
| 39 |
+
from .formatting import HelpFormatter as HelpFormatter
|
| 40 |
+
from .formatting import wrap_text as wrap_text
|
| 41 |
+
from .globals import get_current_context as get_current_context
|
| 42 |
+
from .termui import clear as clear
|
| 43 |
+
from .termui import confirm as confirm
|
| 44 |
+
from .termui import echo_via_pager as echo_via_pager
|
| 45 |
+
from .termui import edit as edit
|
| 46 |
+
from .termui import get_pager_file as get_pager_file
|
| 47 |
+
from .termui import getchar as getchar
|
| 48 |
+
from .termui import launch as launch
|
| 49 |
+
from .termui import pause as pause
|
| 50 |
+
from .termui import progressbar as progressbar
|
| 51 |
+
from .termui import prompt as prompt
|
| 52 |
+
from .termui import secho as secho
|
| 53 |
+
from .termui import style as style
|
| 54 |
+
from .termui import unstyle as unstyle
|
| 55 |
+
from .types import BOOL as BOOL
|
| 56 |
+
from .types import Choice as Choice
|
| 57 |
+
from .types import DateTime as DateTime
|
| 58 |
+
from .types import File as File
|
| 59 |
+
from .types import FLOAT as FLOAT
|
| 60 |
+
from .types import FloatRange as FloatRange
|
| 61 |
+
from .types import INT as INT
|
| 62 |
+
from .types import IntRange as IntRange
|
| 63 |
+
from .types import ParamType as ParamType
|
| 64 |
+
from .types import Path as Path
|
| 65 |
+
from .types import STRING as STRING
|
| 66 |
+
from .types import Tuple as Tuple
|
| 67 |
+
from .types import UNPROCESSED as UNPROCESSED
|
| 68 |
+
from .types import UUID as UUID
|
| 69 |
+
from .utils import echo as echo
|
| 70 |
+
from .utils import format_filename as format_filename
|
| 71 |
+
from .utils import get_app_dir as get_app_dir
|
| 72 |
+
from .utils import get_binary_stream as get_binary_stream
|
| 73 |
+
from .utils import get_text_stream as get_text_stream
|
| 74 |
+
from .utils import open_file as open_file
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def __getattr__(name: str) -> object:
|
| 78 |
+
import warnings
|
| 79 |
+
|
| 80 |
+
if name == "BaseCommand":
|
| 81 |
+
from .core import _BaseCommand
|
| 82 |
+
|
| 83 |
+
warnings.warn(
|
| 84 |
+
"'BaseCommand' is deprecated and will be removed in Click 9.0. Use"
|
| 85 |
+
" 'Command' instead.",
|
| 86 |
+
DeprecationWarning,
|
| 87 |
+
stacklevel=2,
|
| 88 |
+
)
|
| 89 |
+
return _BaseCommand
|
| 90 |
+
|
| 91 |
+
if name == "MultiCommand":
|
| 92 |
+
from .core import _MultiCommand
|
| 93 |
+
|
| 94 |
+
warnings.warn(
|
| 95 |
+
"'MultiCommand' is deprecated and will be removed in Click 9.0. Use"
|
| 96 |
+
" 'Group' instead.",
|
| 97 |
+
DeprecationWarning,
|
| 98 |
+
stacklevel=2,
|
| 99 |
+
)
|
| 100 |
+
return _MultiCommand
|
| 101 |
+
|
| 102 |
+
if name == "OptionParser":
|
| 103 |
+
from .parser import _OptionParser
|
| 104 |
+
|
| 105 |
+
warnings.warn(
|
| 106 |
+
"'OptionParser' is deprecated and will be removed in Click 9.0. The"
|
| 107 |
+
" old parser is available in 'optparse'.",
|
| 108 |
+
DeprecationWarning,
|
| 109 |
+
stacklevel=2,
|
| 110 |
+
)
|
| 111 |
+
return _OptionParser
|
| 112 |
+
|
| 113 |
+
if name == "__version__":
|
| 114 |
+
import importlib.metadata
|
| 115 |
+
import warnings
|
| 116 |
+
|
| 117 |
+
warnings.warn(
|
| 118 |
+
"The '__version__' attribute is deprecated and will be removed in"
|
| 119 |
+
" Click 9.1. Use feature detection or"
|
| 120 |
+
" 'importlib.metadata.version(\"click\")' instead.",
|
| 121 |
+
DeprecationWarning,
|
| 122 |
+
stacklevel=2,
|
| 123 |
+
)
|
| 124 |
+
return importlib.metadata.version("click")
|
| 125 |
+
|
| 126 |
+
raise AttributeError(name)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_compat.py
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import codecs
|
| 4 |
+
import collections.abc as cabc
|
| 5 |
+
import io
|
| 6 |
+
import os
|
| 7 |
+
import re
|
| 8 |
+
import sys
|
| 9 |
+
import typing as t
|
| 10 |
+
from types import TracebackType
|
| 11 |
+
from weakref import WeakKeyDictionary
|
| 12 |
+
|
| 13 |
+
CYGWIN = sys.platform.startswith("cygwin")
|
| 14 |
+
WIN = sys.platform.startswith("win")
|
| 15 |
+
auto_wrap_for_ansi: t.Callable[[t.TextIO], t.TextIO] | None = None
|
| 16 |
+
_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]")
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _make_text_stream(
|
| 20 |
+
stream: t.BinaryIO,
|
| 21 |
+
encoding: str | None,
|
| 22 |
+
errors: str | None,
|
| 23 |
+
force_readable: bool = False,
|
| 24 |
+
force_writable: bool = False,
|
| 25 |
+
) -> t.TextIO:
|
| 26 |
+
if encoding is None:
|
| 27 |
+
encoding = get_best_encoding(stream)
|
| 28 |
+
if errors is None:
|
| 29 |
+
errors = "replace"
|
| 30 |
+
return _NonClosingTextIOWrapper(
|
| 31 |
+
stream,
|
| 32 |
+
encoding,
|
| 33 |
+
errors,
|
| 34 |
+
line_buffering=True,
|
| 35 |
+
force_readable=force_readable,
|
| 36 |
+
force_writable=force_writable,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def is_ascii_encoding(encoding: str) -> bool:
|
| 41 |
+
"""Checks if a given encoding is ascii."""
|
| 42 |
+
try:
|
| 43 |
+
return codecs.lookup(encoding).name == "ascii"
|
| 44 |
+
except LookupError:
|
| 45 |
+
return False
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def get_best_encoding(stream: t.IO[t.Any]) -> str:
|
| 49 |
+
"""Returns the default stream encoding if not found."""
|
| 50 |
+
rv = getattr(stream, "encoding", None) or sys.getdefaultencoding()
|
| 51 |
+
if is_ascii_encoding(rv):
|
| 52 |
+
return "utf-8"
|
| 53 |
+
return rv
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class _NonClosingTextIOWrapper(io.TextIOWrapper):
|
| 57 |
+
def __init__(
|
| 58 |
+
self,
|
| 59 |
+
stream: t.BinaryIO,
|
| 60 |
+
encoding: str | None,
|
| 61 |
+
errors: str | None,
|
| 62 |
+
force_readable: bool = False,
|
| 63 |
+
force_writable: bool = False,
|
| 64 |
+
**extra: t.Any,
|
| 65 |
+
) -> None:
|
| 66 |
+
self._stream = stream = t.cast(
|
| 67 |
+
t.BinaryIO, _FixupStream(stream, force_readable, force_writable)
|
| 68 |
+
)
|
| 69 |
+
super().__init__(stream, encoding, errors, **extra)
|
| 70 |
+
|
| 71 |
+
def __del__(self) -> None:
|
| 72 |
+
try:
|
| 73 |
+
self.detach()
|
| 74 |
+
except Exception:
|
| 75 |
+
pass
|
| 76 |
+
|
| 77 |
+
def isatty(self) -> bool:
|
| 78 |
+
# https://bitbucket.org/pypy/pypy/issue/1803
|
| 79 |
+
return self._stream.isatty()
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class _FixupStream:
|
| 83 |
+
"""The new io interface needs more from streams than streams
|
| 84 |
+
traditionally implement. As such, this fix-up code is necessary in
|
| 85 |
+
some circumstances.
|
| 86 |
+
|
| 87 |
+
The forcing of readable and writable flags are there because some tools
|
| 88 |
+
put badly patched objects on sys (one such offender are certain version
|
| 89 |
+
of jupyter notebook).
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
def __init__(
|
| 93 |
+
self,
|
| 94 |
+
stream: t.BinaryIO,
|
| 95 |
+
force_readable: bool = False,
|
| 96 |
+
force_writable: bool = False,
|
| 97 |
+
):
|
| 98 |
+
self._stream = stream
|
| 99 |
+
self._force_readable = force_readable
|
| 100 |
+
self._force_writable = force_writable
|
| 101 |
+
|
| 102 |
+
def __getattr__(self, name: str) -> t.Any:
|
| 103 |
+
return getattr(self._stream, name)
|
| 104 |
+
|
| 105 |
+
def read1(self, size: int) -> bytes:
|
| 106 |
+
f = getattr(self._stream, "read1", None)
|
| 107 |
+
|
| 108 |
+
if f is not None:
|
| 109 |
+
return t.cast(bytes, f(size))
|
| 110 |
+
|
| 111 |
+
return self._stream.read(size)
|
| 112 |
+
|
| 113 |
+
def readable(self) -> bool:
|
| 114 |
+
if self._force_readable:
|
| 115 |
+
return True
|
| 116 |
+
x = getattr(self._stream, "readable", None)
|
| 117 |
+
if x is not None:
|
| 118 |
+
return t.cast(bool, x())
|
| 119 |
+
try:
|
| 120 |
+
self._stream.read(0)
|
| 121 |
+
except Exception:
|
| 122 |
+
return False
|
| 123 |
+
return True
|
| 124 |
+
|
| 125 |
+
def writable(self) -> bool:
|
| 126 |
+
if self._force_writable:
|
| 127 |
+
return True
|
| 128 |
+
x = getattr(self._stream, "writable", None)
|
| 129 |
+
if x is not None:
|
| 130 |
+
return t.cast(bool, x())
|
| 131 |
+
try:
|
| 132 |
+
self._stream.write(b"")
|
| 133 |
+
except Exception:
|
| 134 |
+
try:
|
| 135 |
+
self._stream.write(b"")
|
| 136 |
+
except Exception:
|
| 137 |
+
return False
|
| 138 |
+
return True
|
| 139 |
+
|
| 140 |
+
def seekable(self) -> bool:
|
| 141 |
+
x = getattr(self._stream, "seekable", None)
|
| 142 |
+
if x is not None:
|
| 143 |
+
return t.cast(bool, x())
|
| 144 |
+
try:
|
| 145 |
+
self._stream.seek(self._stream.tell())
|
| 146 |
+
except Exception:
|
| 147 |
+
return False
|
| 148 |
+
return True
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool:
|
| 152 |
+
try:
|
| 153 |
+
return isinstance(stream.read(0), bytes)
|
| 154 |
+
except Exception:
|
| 155 |
+
return default
|
| 156 |
+
# This happens in some cases where the stream was already
|
| 157 |
+
# closed. In this case, we assume the default.
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool:
|
| 161 |
+
try:
|
| 162 |
+
stream.write(b"")
|
| 163 |
+
except Exception:
|
| 164 |
+
try:
|
| 165 |
+
stream.write("")
|
| 166 |
+
return False
|
| 167 |
+
except Exception:
|
| 168 |
+
pass
|
| 169 |
+
return default
|
| 170 |
+
return True
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def _find_binary_reader(stream: t.IO[t.Any]) -> t.BinaryIO | None:
|
| 174 |
+
# We need to figure out if the given stream is already binary.
|
| 175 |
+
# This can happen because the official docs recommend detaching
|
| 176 |
+
# the streams to get binary streams. Some code might do this, so
|
| 177 |
+
# we need to deal with this case explicitly.
|
| 178 |
+
if _is_binary_reader(stream, False):
|
| 179 |
+
return t.cast(t.BinaryIO, stream)
|
| 180 |
+
|
| 181 |
+
buf = getattr(stream, "buffer", None)
|
| 182 |
+
|
| 183 |
+
# Same situation here; this time we assume that the buffer is
|
| 184 |
+
# actually binary in case it's closed.
|
| 185 |
+
if buf is not None and _is_binary_reader(buf, True):
|
| 186 |
+
return t.cast(t.BinaryIO, buf)
|
| 187 |
+
|
| 188 |
+
return None
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def _find_binary_writer(stream: t.IO[t.Any]) -> t.BinaryIO | None:
|
| 192 |
+
# We need to figure out if the given stream is already binary.
|
| 193 |
+
# This can happen because the official docs recommend detaching
|
| 194 |
+
# the streams to get binary streams. Some code might do this, so
|
| 195 |
+
# we need to deal with this case explicitly.
|
| 196 |
+
if _is_binary_writer(stream, False):
|
| 197 |
+
return t.cast(t.BinaryIO, stream)
|
| 198 |
+
|
| 199 |
+
buf = getattr(stream, "buffer", None)
|
| 200 |
+
|
| 201 |
+
# Same situation here; this time we assume that the buffer is
|
| 202 |
+
# actually binary in case it's closed.
|
| 203 |
+
if buf is not None and _is_binary_writer(buf, True):
|
| 204 |
+
return t.cast(t.BinaryIO, buf)
|
| 205 |
+
|
| 206 |
+
return None
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def _stream_is_misconfigured(stream: t.TextIO) -> bool:
|
| 210 |
+
"""A stream is misconfigured if its encoding is ASCII."""
|
| 211 |
+
# If the stream does not have an encoding set, we assume it's set
|
| 212 |
+
# to ASCII. This appears to happen in certain unittest
|
| 213 |
+
# environments. It's not quite clear what the correct behavior is
|
| 214 |
+
# but this at least will force Click to recover somehow.
|
| 215 |
+
return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii")
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: str | None) -> bool:
|
| 219 |
+
"""A stream attribute is compatible if it is equal to the
|
| 220 |
+
desired value or the desired value is unset and the attribute
|
| 221 |
+
has a value.
|
| 222 |
+
"""
|
| 223 |
+
stream_value = getattr(stream, attr, None)
|
| 224 |
+
return stream_value == value or (value is None and stream_value is not None)
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def _is_compatible_text_stream(
|
| 228 |
+
stream: t.TextIO, encoding: str | None, errors: str | None
|
| 229 |
+
) -> bool:
|
| 230 |
+
"""Check if a stream's encoding and errors attributes are
|
| 231 |
+
compatible with the desired values.
|
| 232 |
+
"""
|
| 233 |
+
return _is_compat_stream_attr(
|
| 234 |
+
stream, "encoding", encoding
|
| 235 |
+
) and _is_compat_stream_attr(stream, "errors", errors)
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def _force_correct_text_stream(
|
| 239 |
+
text_stream: t.IO[t.Any],
|
| 240 |
+
encoding: str | None,
|
| 241 |
+
errors: str | None,
|
| 242 |
+
is_binary: t.Callable[[t.IO[t.Any], bool], bool],
|
| 243 |
+
find_binary: t.Callable[[t.IO[t.Any]], t.BinaryIO | None],
|
| 244 |
+
force_readable: bool = False,
|
| 245 |
+
force_writable: bool = False,
|
| 246 |
+
) -> t.TextIO:
|
| 247 |
+
if is_binary(text_stream, False):
|
| 248 |
+
binary_reader = t.cast(t.BinaryIO, text_stream)
|
| 249 |
+
else:
|
| 250 |
+
text_stream = t.cast(t.TextIO, text_stream)
|
| 251 |
+
# If the stream looks compatible, and won't default to a
|
| 252 |
+
# misconfigured ascii encoding, return it as-is.
|
| 253 |
+
if _is_compatible_text_stream(text_stream, encoding, errors) and not (
|
| 254 |
+
encoding is None and _stream_is_misconfigured(text_stream)
|
| 255 |
+
):
|
| 256 |
+
return text_stream
|
| 257 |
+
|
| 258 |
+
# Otherwise, get the underlying binary reader.
|
| 259 |
+
possible_binary_reader = find_binary(text_stream)
|
| 260 |
+
|
| 261 |
+
# If that's not possible, silently use the original reader
|
| 262 |
+
# and get mojibake instead of exceptions.
|
| 263 |
+
if possible_binary_reader is None:
|
| 264 |
+
return text_stream
|
| 265 |
+
|
| 266 |
+
binary_reader = possible_binary_reader
|
| 267 |
+
|
| 268 |
+
# Default errors to replace instead of strict in order to get
|
| 269 |
+
# something that works.
|
| 270 |
+
if errors is None:
|
| 271 |
+
errors = "replace"
|
| 272 |
+
|
| 273 |
+
# Wrap the binary stream in a text stream with the correct
|
| 274 |
+
# encoding parameters.
|
| 275 |
+
return _make_text_stream(
|
| 276 |
+
binary_reader,
|
| 277 |
+
encoding,
|
| 278 |
+
errors,
|
| 279 |
+
force_readable=force_readable,
|
| 280 |
+
force_writable=force_writable,
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def _force_correct_text_reader(
|
| 285 |
+
text_reader: t.IO[t.Any],
|
| 286 |
+
encoding: str | None,
|
| 287 |
+
errors: str | None,
|
| 288 |
+
force_readable: bool = False,
|
| 289 |
+
) -> t.TextIO:
|
| 290 |
+
return _force_correct_text_stream(
|
| 291 |
+
text_reader,
|
| 292 |
+
encoding,
|
| 293 |
+
errors,
|
| 294 |
+
_is_binary_reader,
|
| 295 |
+
_find_binary_reader,
|
| 296 |
+
force_readable=force_readable,
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def _force_correct_text_writer(
|
| 301 |
+
text_writer: t.IO[t.Any],
|
| 302 |
+
encoding: str | None,
|
| 303 |
+
errors: str | None,
|
| 304 |
+
force_writable: bool = False,
|
| 305 |
+
) -> t.TextIO:
|
| 306 |
+
return _force_correct_text_stream(
|
| 307 |
+
text_writer,
|
| 308 |
+
encoding,
|
| 309 |
+
errors,
|
| 310 |
+
_is_binary_writer,
|
| 311 |
+
_find_binary_writer,
|
| 312 |
+
force_writable=force_writable,
|
| 313 |
+
)
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def get_binary_stdin() -> t.BinaryIO:
|
| 317 |
+
reader = _find_binary_reader(sys.stdin)
|
| 318 |
+
if reader is None:
|
| 319 |
+
raise RuntimeError("Was not able to determine binary stream for sys.stdin.")
|
| 320 |
+
return reader
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
def get_binary_stdout() -> t.BinaryIO:
|
| 324 |
+
writer = _find_binary_writer(sys.stdout)
|
| 325 |
+
if writer is None:
|
| 326 |
+
raise RuntimeError("Was not able to determine binary stream for sys.stdout.")
|
| 327 |
+
return writer
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
def get_binary_stderr() -> t.BinaryIO:
|
| 331 |
+
writer = _find_binary_writer(sys.stderr)
|
| 332 |
+
if writer is None:
|
| 333 |
+
raise RuntimeError("Was not able to determine binary stream for sys.stderr.")
|
| 334 |
+
return writer
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
def get_text_stdin(encoding: str | None = None, errors: str | None = None) -> t.TextIO:
|
| 338 |
+
rv = _get_windows_console_stream(sys.stdin, encoding, errors)
|
| 339 |
+
if rv is not None:
|
| 340 |
+
return rv
|
| 341 |
+
return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True)
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
def get_text_stdout(encoding: str | None = None, errors: str | None = None) -> t.TextIO:
|
| 345 |
+
rv = _get_windows_console_stream(sys.stdout, encoding, errors)
|
| 346 |
+
if rv is not None:
|
| 347 |
+
return rv
|
| 348 |
+
return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True)
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
def get_text_stderr(encoding: str | None = None, errors: str | None = None) -> t.TextIO:
|
| 352 |
+
rv = _get_windows_console_stream(sys.stderr, encoding, errors)
|
| 353 |
+
if rv is not None:
|
| 354 |
+
return rv
|
| 355 |
+
return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True)
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
def _wrap_io_open(
|
| 359 |
+
file: str | os.PathLike[str] | int,
|
| 360 |
+
mode: str,
|
| 361 |
+
encoding: str | None,
|
| 362 |
+
errors: str | None,
|
| 363 |
+
) -> t.IO[t.Any]:
|
| 364 |
+
"""Handles not passing ``encoding`` and ``errors`` in binary mode."""
|
| 365 |
+
if "b" in mode:
|
| 366 |
+
return open(file, mode)
|
| 367 |
+
|
| 368 |
+
return open(file, mode, encoding=encoding, errors=errors)
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def open_stream(
|
| 372 |
+
filename: str | os.PathLike[str],
|
| 373 |
+
mode: str = "r",
|
| 374 |
+
encoding: str | None = None,
|
| 375 |
+
errors: str | None = "strict",
|
| 376 |
+
atomic: bool = False,
|
| 377 |
+
) -> tuple[t.IO[t.Any], bool]:
|
| 378 |
+
binary = "b" in mode
|
| 379 |
+
filename = os.fspath(filename)
|
| 380 |
+
|
| 381 |
+
# Standard streams first. These are simple because they ignore the
|
| 382 |
+
# atomic flag. Use fsdecode to handle Path("-").
|
| 383 |
+
if os.fsdecode(filename) == "-":
|
| 384 |
+
if any(m in mode for m in ["w", "a", "x"]):
|
| 385 |
+
if binary:
|
| 386 |
+
return get_binary_stdout(), False
|
| 387 |
+
return get_text_stdout(encoding=encoding, errors=errors), False
|
| 388 |
+
if binary:
|
| 389 |
+
return get_binary_stdin(), False
|
| 390 |
+
return get_text_stdin(encoding=encoding, errors=errors), False
|
| 391 |
+
|
| 392 |
+
# Non-atomic writes directly go out through the regular open functions.
|
| 393 |
+
if not atomic:
|
| 394 |
+
return _wrap_io_open(filename, mode, encoding, errors), True
|
| 395 |
+
|
| 396 |
+
# Some usability stuff for atomic writes
|
| 397 |
+
if "a" in mode:
|
| 398 |
+
raise ValueError(
|
| 399 |
+
"Appending to an existing file is not supported, because that"
|
| 400 |
+
" would involve an expensive `copy`-operation to a temporary"
|
| 401 |
+
" file. Open the file in normal `w`-mode and copy explicitly"
|
| 402 |
+
" if that's what you're after."
|
| 403 |
+
)
|
| 404 |
+
if "x" in mode:
|
| 405 |
+
raise ValueError("Use the `overwrite`-parameter instead.")
|
| 406 |
+
if "w" not in mode:
|
| 407 |
+
raise ValueError("Atomic writes only make sense with `w`-mode.")
|
| 408 |
+
|
| 409 |
+
# Atomic writes are more complicated. They work by opening a file
|
| 410 |
+
# as a proxy in the same folder and then using the fdopen
|
| 411 |
+
# functionality to wrap it in a Python file. Then we wrap it in an
|
| 412 |
+
# atomic file that moves the file over on close.
|
| 413 |
+
import errno
|
| 414 |
+
import random
|
| 415 |
+
|
| 416 |
+
try:
|
| 417 |
+
perm: int | None = os.stat(filename).st_mode
|
| 418 |
+
except OSError:
|
| 419 |
+
perm = None
|
| 420 |
+
|
| 421 |
+
flags = os.O_RDWR | os.O_CREAT | os.O_EXCL
|
| 422 |
+
|
| 423 |
+
if binary:
|
| 424 |
+
flags |= getattr(os, "O_BINARY", 0)
|
| 425 |
+
|
| 426 |
+
while True:
|
| 427 |
+
tmp_filename = os.path.join(
|
| 428 |
+
os.path.dirname(filename),
|
| 429 |
+
f".__atomic-write{random.randrange(1 << 32):08x}",
|
| 430 |
+
)
|
| 431 |
+
try:
|
| 432 |
+
fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm)
|
| 433 |
+
break
|
| 434 |
+
except OSError as e:
|
| 435 |
+
if e.errno == errno.EEXIST or (
|
| 436 |
+
os.name == "nt"
|
| 437 |
+
and e.errno == errno.EACCES
|
| 438 |
+
and os.path.isdir(e.filename)
|
| 439 |
+
and os.access(e.filename, os.W_OK)
|
| 440 |
+
):
|
| 441 |
+
continue
|
| 442 |
+
raise
|
| 443 |
+
|
| 444 |
+
if perm is not None:
|
| 445 |
+
os.chmod(tmp_filename, perm) # in case perm includes bits in umask
|
| 446 |
+
|
| 447 |
+
f = _wrap_io_open(fd, mode, encoding, errors)
|
| 448 |
+
af = _AtomicFile(f, tmp_filename, os.path.realpath(filename))
|
| 449 |
+
return t.cast(t.IO[t.Any], af), True
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
class _AtomicFile:
|
| 453 |
+
def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None:
|
| 454 |
+
self._f = f
|
| 455 |
+
self._tmp_filename = tmp_filename
|
| 456 |
+
self._real_filename = real_filename
|
| 457 |
+
self.closed = False
|
| 458 |
+
|
| 459 |
+
@property
|
| 460 |
+
def name(self) -> str:
|
| 461 |
+
return self._real_filename
|
| 462 |
+
|
| 463 |
+
def close(self, delete: bool = False) -> None:
|
| 464 |
+
if self.closed:
|
| 465 |
+
return
|
| 466 |
+
self._f.close()
|
| 467 |
+
os.replace(self._tmp_filename, self._real_filename)
|
| 468 |
+
self.closed = True
|
| 469 |
+
|
| 470 |
+
def __getattr__(self, name: str) -> t.Any:
|
| 471 |
+
return getattr(self._f, name)
|
| 472 |
+
|
| 473 |
+
def __enter__(self) -> _AtomicFile:
|
| 474 |
+
return self
|
| 475 |
+
|
| 476 |
+
def __exit__(
|
| 477 |
+
self,
|
| 478 |
+
exc_type: type[BaseException] | None,
|
| 479 |
+
exc_value: BaseException | None,
|
| 480 |
+
tb: TracebackType | None,
|
| 481 |
+
) -> None:
|
| 482 |
+
self.close(delete=exc_type is not None)
|
| 483 |
+
|
| 484 |
+
def __repr__(self) -> str:
|
| 485 |
+
return repr(self._f)
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
def strip_ansi(value: str) -> str:
|
| 489 |
+
return _ansi_re.sub("", value)
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool:
|
| 493 |
+
while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)):
|
| 494 |
+
stream = stream._stream
|
| 495 |
+
|
| 496 |
+
return stream.__class__.__module__.startswith("ipykernel.")
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
def should_strip_ansi(
|
| 500 |
+
stream: t.IO[t.Any] | None = None, color: bool | None = None
|
| 501 |
+
) -> bool:
|
| 502 |
+
if color is None:
|
| 503 |
+
if stream is None:
|
| 504 |
+
stream = sys.stdin
|
| 505 |
+
elif hasattr(stream, "color"):
|
| 506 |
+
# ._termui_impl.MaybeStripAnsi handles stripping ansi itself,
|
| 507 |
+
# so we don't need to strip it here
|
| 508 |
+
return False
|
| 509 |
+
return not isatty(stream) and not _is_jupyter_kernel_output(stream)
|
| 510 |
+
return not color
|
| 511 |
+
|
| 512 |
+
|
| 513 |
+
# On Windows, wrap the output streams with colorama to support ANSI
|
| 514 |
+
# color codes.
|
| 515 |
+
# NOTE: double check is needed so mypy does not analyze this on Linux
|
| 516 |
+
if sys.platform.startswith("win") and WIN:
|
| 517 |
+
from ._winconsole import _get_windows_console_stream
|
| 518 |
+
|
| 519 |
+
def _get_argv_encoding() -> str:
|
| 520 |
+
import locale
|
| 521 |
+
|
| 522 |
+
return locale.getpreferredencoding()
|
| 523 |
+
|
| 524 |
+
_ansi_stream_wrappers: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary()
|
| 525 |
+
|
| 526 |
+
def auto_wrap_for_ansi(stream: t.TextIO, color: bool | None = None) -> t.TextIO:
|
| 527 |
+
"""Support ANSI color and style codes on Windows by wrapping a
|
| 528 |
+
stream with colorama.
|
| 529 |
+
"""
|
| 530 |
+
try:
|
| 531 |
+
cached = _ansi_stream_wrappers.get(stream)
|
| 532 |
+
except Exception:
|
| 533 |
+
cached = None
|
| 534 |
+
|
| 535 |
+
if cached is not None:
|
| 536 |
+
return cached
|
| 537 |
+
|
| 538 |
+
import colorama
|
| 539 |
+
|
| 540 |
+
strip = should_strip_ansi(stream, color)
|
| 541 |
+
ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip)
|
| 542 |
+
rv = t.cast(t.TextIO, ansi_wrapper.stream)
|
| 543 |
+
_write = rv.write
|
| 544 |
+
|
| 545 |
+
def _safe_write(s: str) -> int:
|
| 546 |
+
try:
|
| 547 |
+
return _write(s)
|
| 548 |
+
except BaseException:
|
| 549 |
+
ansi_wrapper.reset_all()
|
| 550 |
+
raise
|
| 551 |
+
|
| 552 |
+
rv.write = _safe_write # type: ignore[method-assign]
|
| 553 |
+
|
| 554 |
+
try:
|
| 555 |
+
_ansi_stream_wrappers[stream] = rv
|
| 556 |
+
except Exception:
|
| 557 |
+
pass
|
| 558 |
+
|
| 559 |
+
return rv
|
| 560 |
+
|
| 561 |
+
else:
|
| 562 |
+
|
| 563 |
+
def _get_argv_encoding() -> str:
|
| 564 |
+
return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding()
|
| 565 |
+
|
| 566 |
+
def _get_windows_console_stream(
|
| 567 |
+
f: t.TextIO, encoding: str | None, errors: str | None
|
| 568 |
+
) -> t.TextIO | None:
|
| 569 |
+
return None
|
| 570 |
+
|
| 571 |
+
|
| 572 |
+
def term_len(x: str) -> int:
|
| 573 |
+
return len(strip_ansi(x))
|
| 574 |
+
|
| 575 |
+
|
| 576 |
+
def isatty(stream: t.IO[t.Any]) -> bool:
|
| 577 |
+
try:
|
| 578 |
+
return stream.isatty()
|
| 579 |
+
except Exception:
|
| 580 |
+
return False
|
| 581 |
+
|
| 582 |
+
|
| 583 |
+
def _make_cached_stream_func(
|
| 584 |
+
src_func: t.Callable[[], t.TextIO | None],
|
| 585 |
+
wrapper_func: t.Callable[[], t.TextIO],
|
| 586 |
+
) -> t.Callable[[], t.TextIO | None]:
|
| 587 |
+
cache: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary()
|
| 588 |
+
|
| 589 |
+
def func() -> t.TextIO | None:
|
| 590 |
+
stream = src_func()
|
| 591 |
+
|
| 592 |
+
if stream is None:
|
| 593 |
+
return None
|
| 594 |
+
|
| 595 |
+
try:
|
| 596 |
+
rv = cache.get(stream)
|
| 597 |
+
except Exception:
|
| 598 |
+
rv = None
|
| 599 |
+
if rv is not None:
|
| 600 |
+
return rv
|
| 601 |
+
rv = wrapper_func()
|
| 602 |
+
try:
|
| 603 |
+
cache[stream] = rv
|
| 604 |
+
except Exception:
|
| 605 |
+
pass
|
| 606 |
+
return rv
|
| 607 |
+
|
| 608 |
+
return func
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin)
|
| 612 |
+
_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout)
|
| 613 |
+
_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr)
|
| 614 |
+
|
| 615 |
+
|
| 616 |
+
binary_streams: cabc.Mapping[str, t.Callable[[], t.BinaryIO]] = {
|
| 617 |
+
"stdin": get_binary_stdin,
|
| 618 |
+
"stdout": get_binary_stdout,
|
| 619 |
+
"stderr": get_binary_stderr,
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
text_streams: cabc.Mapping[str, t.Callable[[str | None, str | None], t.TextIO]] = {
|
| 623 |
+
"stdin": get_text_stdin,
|
| 624 |
+
"stdout": get_text_stdout,
|
| 625 |
+
"stderr": get_text_stderr,
|
| 626 |
+
}
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_termui_impl.py
ADDED
|
@@ -0,0 +1,945 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This module contains implementations for the termui module. To keep the
|
| 3 |
+
import time of Click down, some infrequently used functionality is
|
| 4 |
+
placed in this module and only imported as needed.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import collections.abc as cabc
|
| 10 |
+
import contextlib
|
| 11 |
+
import io
|
| 12 |
+
import math
|
| 13 |
+
import os
|
| 14 |
+
import shlex
|
| 15 |
+
import sys
|
| 16 |
+
import time
|
| 17 |
+
import typing as t
|
| 18 |
+
from gettext import gettext as _
|
| 19 |
+
from io import StringIO
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
from types import TracebackType
|
| 22 |
+
|
| 23 |
+
from ._compat import _default_text_stdout
|
| 24 |
+
from ._compat import CYGWIN
|
| 25 |
+
from ._compat import get_best_encoding
|
| 26 |
+
from ._compat import isatty
|
| 27 |
+
from ._compat import strip_ansi
|
| 28 |
+
from ._compat import term_len
|
| 29 |
+
from ._compat import WIN
|
| 30 |
+
from .exceptions import ClickException
|
| 31 |
+
from .utils import echo
|
| 32 |
+
from .utils import KeepOpenFile
|
| 33 |
+
|
| 34 |
+
V = t.TypeVar("V")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class _BufferedTextPagerStream(t.Protocol):
|
| 38 |
+
buffer: t.BinaryIO
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _has_binary_buffer(
|
| 42 |
+
stream: t.BinaryIO | t.TextIO,
|
| 43 |
+
) -> t.TypeGuard[_BufferedTextPagerStream]:
|
| 44 |
+
# TextIO is wider than TextIOWrapper; text-only streams such as StringIO
|
| 45 |
+
# are valid TextIO values but do not expose a binary buffer to wrap.
|
| 46 |
+
return getattr(stream, "buffer", None) is not None
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
if os.name == "nt":
|
| 50 |
+
BEFORE_BAR = "\r"
|
| 51 |
+
AFTER_BAR = "\n"
|
| 52 |
+
else:
|
| 53 |
+
BEFORE_BAR = "\r\033[?25l"
|
| 54 |
+
AFTER_BAR = "\033[?25h\n"
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class ProgressBar(t.Generic[V]):
|
| 58 |
+
def __init__(
|
| 59 |
+
self,
|
| 60 |
+
iterable: cabc.Iterable[V] | None,
|
| 61 |
+
length: int | None = None,
|
| 62 |
+
fill_char: str = "#",
|
| 63 |
+
empty_char: str = " ",
|
| 64 |
+
bar_template: str = "%(bar)s",
|
| 65 |
+
info_sep: str = " ",
|
| 66 |
+
hidden: bool = False,
|
| 67 |
+
show_eta: bool = True,
|
| 68 |
+
show_percent: bool | None = None,
|
| 69 |
+
show_pos: bool = False,
|
| 70 |
+
item_show_func: t.Callable[[V | None], str | None] | None = None,
|
| 71 |
+
label: str | None = None,
|
| 72 |
+
file: t.TextIO | None = None,
|
| 73 |
+
color: bool | None = None,
|
| 74 |
+
update_min_steps: int = 1,
|
| 75 |
+
width: int = 30,
|
| 76 |
+
) -> None:
|
| 77 |
+
self.fill_char = fill_char
|
| 78 |
+
self.empty_char = empty_char
|
| 79 |
+
self.bar_template = bar_template
|
| 80 |
+
self.info_sep = info_sep
|
| 81 |
+
self.hidden = hidden
|
| 82 |
+
self.show_eta = show_eta
|
| 83 |
+
self.show_percent = show_percent
|
| 84 |
+
self.show_pos = show_pos
|
| 85 |
+
self.item_show_func = item_show_func
|
| 86 |
+
self.label: str = label or ""
|
| 87 |
+
|
| 88 |
+
if file is None:
|
| 89 |
+
file = _default_text_stdout()
|
| 90 |
+
|
| 91 |
+
# There are no standard streams attached to write to. For example,
|
| 92 |
+
# pythonw on Windows.
|
| 93 |
+
if file is None:
|
| 94 |
+
file = StringIO()
|
| 95 |
+
|
| 96 |
+
self.file = file
|
| 97 |
+
self.color = color
|
| 98 |
+
self.update_min_steps = update_min_steps
|
| 99 |
+
self._completed_intervals = 0
|
| 100 |
+
self.width: int = width
|
| 101 |
+
self.autowidth: bool = width == 0
|
| 102 |
+
|
| 103 |
+
if length is None:
|
| 104 |
+
from operator import length_hint
|
| 105 |
+
|
| 106 |
+
length = length_hint(iterable, -1)
|
| 107 |
+
|
| 108 |
+
if length == -1:
|
| 109 |
+
length = None
|
| 110 |
+
if iterable is None:
|
| 111 |
+
if length is None:
|
| 112 |
+
raise TypeError("iterable or length is required")
|
| 113 |
+
iterable = t.cast("cabc.Iterable[V]", range(length))
|
| 114 |
+
self.iter: cabc.Iterable[V] = iter(iterable)
|
| 115 |
+
self.length = length
|
| 116 |
+
self.pos: int = 0
|
| 117 |
+
self.avg: list[float] = []
|
| 118 |
+
self.last_eta: float
|
| 119 |
+
self.start: float
|
| 120 |
+
self.start = self.last_eta = time.time()
|
| 121 |
+
self.eta_known: bool = False
|
| 122 |
+
self.finished: bool = False
|
| 123 |
+
self.max_width: int | None = None
|
| 124 |
+
self.entered: bool = False
|
| 125 |
+
self.current_item: V | None = None
|
| 126 |
+
self._is_atty = isatty(self.file)
|
| 127 |
+
self._last_line: str | None = None
|
| 128 |
+
|
| 129 |
+
def __enter__(self) -> ProgressBar[V]:
|
| 130 |
+
self.entered = True
|
| 131 |
+
self.render_progress()
|
| 132 |
+
return self
|
| 133 |
+
|
| 134 |
+
def __exit__(
|
| 135 |
+
self,
|
| 136 |
+
exc_type: type[BaseException] | None,
|
| 137 |
+
exc_value: BaseException | None,
|
| 138 |
+
tb: TracebackType | None,
|
| 139 |
+
) -> None:
|
| 140 |
+
self.render_finish()
|
| 141 |
+
|
| 142 |
+
def __iter__(self) -> cabc.Iterator[V]:
|
| 143 |
+
if not self.entered:
|
| 144 |
+
raise RuntimeError("You need to use progress bars in a with block.")
|
| 145 |
+
self.render_progress()
|
| 146 |
+
return self.generator()
|
| 147 |
+
|
| 148 |
+
def __next__(self) -> V:
|
| 149 |
+
# Iteration is defined in terms of a generator function,
|
| 150 |
+
# returned by iter(self); use that to define next(). This works
|
| 151 |
+
# because `self.iter` is an iterable consumed by that generator,
|
| 152 |
+
# so it is re-entry safe. Calling `next(self.generator())`
|
| 153 |
+
# twice works and does "what you want".
|
| 154 |
+
return next(iter(self))
|
| 155 |
+
|
| 156 |
+
def render_finish(self) -> None:
|
| 157 |
+
if self.hidden or not self._is_atty:
|
| 158 |
+
return
|
| 159 |
+
self.file.write(AFTER_BAR)
|
| 160 |
+
self.file.flush()
|
| 161 |
+
|
| 162 |
+
@property
|
| 163 |
+
def pct(self) -> float:
|
| 164 |
+
if self.finished:
|
| 165 |
+
return 1.0
|
| 166 |
+
return min(self.pos / (float(self.length or 1) or 1), 1.0)
|
| 167 |
+
|
| 168 |
+
@property
|
| 169 |
+
def time_per_iteration(self) -> float:
|
| 170 |
+
if not self.avg:
|
| 171 |
+
return 0.0
|
| 172 |
+
return sum(self.avg) / float(len(self.avg))
|
| 173 |
+
|
| 174 |
+
@property
|
| 175 |
+
def eta(self) -> float:
|
| 176 |
+
if self.length is not None and not self.finished:
|
| 177 |
+
return self.time_per_iteration * (self.length - self.pos)
|
| 178 |
+
return 0.0
|
| 179 |
+
|
| 180 |
+
def format_eta(self) -> str:
|
| 181 |
+
if self.eta_known:
|
| 182 |
+
t = int(self.eta)
|
| 183 |
+
seconds = t % 60
|
| 184 |
+
t //= 60
|
| 185 |
+
minutes = t % 60
|
| 186 |
+
t //= 60
|
| 187 |
+
hours = t % 24
|
| 188 |
+
t //= 24
|
| 189 |
+
if t > 0:
|
| 190 |
+
return "{d}{day_label} {h:02}:{m:02}:{s:02}".format(
|
| 191 |
+
d=t,
|
| 192 |
+
day_label=_("d"),
|
| 193 |
+
h=hours,
|
| 194 |
+
m=minutes,
|
| 195 |
+
s=seconds,
|
| 196 |
+
)
|
| 197 |
+
else:
|
| 198 |
+
return f"{hours:02}:{minutes:02}:{seconds:02}"
|
| 199 |
+
return ""
|
| 200 |
+
|
| 201 |
+
def format_pos(self) -> str:
|
| 202 |
+
pos = str(self.pos)
|
| 203 |
+
if self.length is not None:
|
| 204 |
+
pos += f"/{self.length}"
|
| 205 |
+
return pos
|
| 206 |
+
|
| 207 |
+
def format_pct(self) -> str:
|
| 208 |
+
return f"{int(self.pct * 100): 4}%"[1:]
|
| 209 |
+
|
| 210 |
+
def format_bar(self) -> str:
|
| 211 |
+
if self.length is not None:
|
| 212 |
+
bar_length = int(self.pct * self.width)
|
| 213 |
+
bar = self.fill_char * bar_length
|
| 214 |
+
bar += self.empty_char * (self.width - bar_length)
|
| 215 |
+
elif self.finished:
|
| 216 |
+
bar = self.fill_char * self.width
|
| 217 |
+
else:
|
| 218 |
+
chars = list(self.empty_char * (self.width or 1))
|
| 219 |
+
if self.time_per_iteration != 0:
|
| 220 |
+
chars[
|
| 221 |
+
int(
|
| 222 |
+
(math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5)
|
| 223 |
+
* self.width
|
| 224 |
+
)
|
| 225 |
+
] = self.fill_char
|
| 226 |
+
bar = "".join(chars)
|
| 227 |
+
return bar
|
| 228 |
+
|
| 229 |
+
def format_progress_line(self) -> str:
|
| 230 |
+
show_percent = self.show_percent
|
| 231 |
+
|
| 232 |
+
info_bits = []
|
| 233 |
+
if self.length is not None and show_percent is None:
|
| 234 |
+
show_percent = not self.show_pos
|
| 235 |
+
|
| 236 |
+
if self.show_pos:
|
| 237 |
+
info_bits.append(self.format_pos())
|
| 238 |
+
if show_percent:
|
| 239 |
+
info_bits.append(self.format_pct())
|
| 240 |
+
if self.show_eta and self.eta_known and not self.finished:
|
| 241 |
+
info_bits.append(self.format_eta())
|
| 242 |
+
if self.item_show_func is not None:
|
| 243 |
+
item_info = self.item_show_func(self.current_item)
|
| 244 |
+
if item_info is not None:
|
| 245 |
+
info_bits.append(item_info)
|
| 246 |
+
|
| 247 |
+
return (
|
| 248 |
+
self.bar_template
|
| 249 |
+
% {
|
| 250 |
+
"label": self.label,
|
| 251 |
+
"bar": self.format_bar(),
|
| 252 |
+
"info": self.info_sep.join(info_bits),
|
| 253 |
+
}
|
| 254 |
+
).rstrip()
|
| 255 |
+
|
| 256 |
+
def render_progress(self) -> None:
|
| 257 |
+
if self.hidden:
|
| 258 |
+
return
|
| 259 |
+
|
| 260 |
+
if not self._is_atty:
|
| 261 |
+
# Only output the label once if the output is not a TTY.
|
| 262 |
+
if self._last_line != self.label:
|
| 263 |
+
self._last_line = self.label
|
| 264 |
+
echo(self.label, file=self.file, color=self.color)
|
| 265 |
+
return
|
| 266 |
+
|
| 267 |
+
buf = []
|
| 268 |
+
# Update width in case the terminal has been resized
|
| 269 |
+
if self.autowidth:
|
| 270 |
+
import shutil
|
| 271 |
+
|
| 272 |
+
old_width = self.width
|
| 273 |
+
self.width = 0
|
| 274 |
+
clutter_length = term_len(self.format_progress_line())
|
| 275 |
+
new_width = max(0, shutil.get_terminal_size().columns - clutter_length)
|
| 276 |
+
if new_width < old_width and self.max_width is not None:
|
| 277 |
+
buf.append(BEFORE_BAR)
|
| 278 |
+
buf.append(" " * self.max_width)
|
| 279 |
+
self.max_width = new_width
|
| 280 |
+
self.width = new_width
|
| 281 |
+
|
| 282 |
+
clear_width = self.width
|
| 283 |
+
if self.max_width is not None:
|
| 284 |
+
clear_width = self.max_width
|
| 285 |
+
|
| 286 |
+
buf.append(BEFORE_BAR)
|
| 287 |
+
line = self.format_progress_line()
|
| 288 |
+
line_len = term_len(line)
|
| 289 |
+
if self.max_width is None or self.max_width < line_len:
|
| 290 |
+
self.max_width = line_len
|
| 291 |
+
|
| 292 |
+
buf.append(line)
|
| 293 |
+
buf.append(" " * (clear_width - line_len))
|
| 294 |
+
line = "".join(buf)
|
| 295 |
+
# Render the line only if it changed.
|
| 296 |
+
|
| 297 |
+
if line != self._last_line:
|
| 298 |
+
self._last_line = line
|
| 299 |
+
echo(line, file=self.file, color=self.color, nl=False)
|
| 300 |
+
self.file.flush()
|
| 301 |
+
|
| 302 |
+
def make_step(self, n_steps: int) -> None:
|
| 303 |
+
self.pos += n_steps
|
| 304 |
+
if self.length is not None and self.pos >= self.length:
|
| 305 |
+
self.finished = True
|
| 306 |
+
|
| 307 |
+
if (time.time() - self.last_eta) < 1.0:
|
| 308 |
+
return
|
| 309 |
+
|
| 310 |
+
self.last_eta = time.time()
|
| 311 |
+
|
| 312 |
+
# self.avg is a rolling list of length <= 7 of steps where steps are
|
| 313 |
+
# defined as time elapsed divided by the total progress through
|
| 314 |
+
# self.length.
|
| 315 |
+
if self.pos:
|
| 316 |
+
step = (time.time() - self.start) / self.pos
|
| 317 |
+
else:
|
| 318 |
+
step = time.time() - self.start
|
| 319 |
+
|
| 320 |
+
self.avg = self.avg[-6:] + [step]
|
| 321 |
+
|
| 322 |
+
self.eta_known = self.length is not None
|
| 323 |
+
|
| 324 |
+
def update(self, n_steps: int, current_item: V | None = None) -> None:
|
| 325 |
+
"""Update the progress bar by advancing a specified number of
|
| 326 |
+
steps, and optionally set the ``current_item`` for this new
|
| 327 |
+
position.
|
| 328 |
+
|
| 329 |
+
:param n_steps: Number of steps to advance.
|
| 330 |
+
:param current_item: Optional item to set as ``current_item``
|
| 331 |
+
for the updated position.
|
| 332 |
+
|
| 333 |
+
.. versionchanged:: 8.0
|
| 334 |
+
Added the ``current_item`` optional parameter.
|
| 335 |
+
|
| 336 |
+
.. versionchanged:: 8.0
|
| 337 |
+
Only render when the number of steps meets the
|
| 338 |
+
``update_min_steps`` threshold.
|
| 339 |
+
"""
|
| 340 |
+
if current_item is not None:
|
| 341 |
+
self.current_item = current_item
|
| 342 |
+
|
| 343 |
+
self._completed_intervals += n_steps
|
| 344 |
+
|
| 345 |
+
if self._completed_intervals >= self.update_min_steps:
|
| 346 |
+
self.make_step(self._completed_intervals)
|
| 347 |
+
self.render_progress()
|
| 348 |
+
self._completed_intervals = 0
|
| 349 |
+
|
| 350 |
+
def finish(self) -> None:
|
| 351 |
+
self.eta_known = False
|
| 352 |
+
self.current_item = None
|
| 353 |
+
self.finished = True
|
| 354 |
+
|
| 355 |
+
def generator(self) -> cabc.Iterator[V]:
|
| 356 |
+
"""Return a generator which yields the items added to the bar
|
| 357 |
+
during construction, and updates the progress bar *after* the
|
| 358 |
+
yielded block returns.
|
| 359 |
+
"""
|
| 360 |
+
# WARNING: the iterator interface for `ProgressBar` relies on
|
| 361 |
+
# this and only works because this is a simple generator which
|
| 362 |
+
# doesn't create or manage additional state. If this function
|
| 363 |
+
# changes, the impact should be evaluated both against
|
| 364 |
+
# `iter(bar)` and `next(bar)`. `next()` in particular may call
|
| 365 |
+
# `self.generator()` repeatedly, and this must remain safe in
|
| 366 |
+
# order for that interface to work.
|
| 367 |
+
if not self.entered:
|
| 368 |
+
raise RuntimeError("You need to use progress bars in a with block.")
|
| 369 |
+
|
| 370 |
+
if not self._is_atty:
|
| 371 |
+
yield from self.iter
|
| 372 |
+
else:
|
| 373 |
+
for rv in self.iter:
|
| 374 |
+
self.current_item = rv
|
| 375 |
+
|
| 376 |
+
# This allows show_item_func to be updated before the
|
| 377 |
+
# item is processed. Only trigger at the beginning of
|
| 378 |
+
# the update interval.
|
| 379 |
+
if self._completed_intervals == 0:
|
| 380 |
+
self.render_progress()
|
| 381 |
+
|
| 382 |
+
yield rv
|
| 383 |
+
self.update(1)
|
| 384 |
+
|
| 385 |
+
self.finish()
|
| 386 |
+
self.render_progress()
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
class MaybeStripAnsi(io.TextIOWrapper):
|
| 390 |
+
def __init__(self, stream: t.IO[bytes], *, color: bool, **kwargs: t.Any):
|
| 391 |
+
super().__init__(stream, **kwargs)
|
| 392 |
+
self.color = color
|
| 393 |
+
|
| 394 |
+
def write(self, text: str) -> int:
|
| 395 |
+
if not self.color:
|
| 396 |
+
text = strip_ansi(text)
|
| 397 |
+
return super().write(text)
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
def _pager_contextmanager(
|
| 401 |
+
color: bool | None = None,
|
| 402 |
+
) -> t.ContextManager[tuple[t.BinaryIO | t.TextIO, str, bool]]:
|
| 403 |
+
"""Decide what method to use for paging through text."""
|
| 404 |
+
stdout = _default_text_stdout()
|
| 405 |
+
|
| 406 |
+
# There are no standard streams attached to write to. For example,
|
| 407 |
+
# pythonw on Windows.
|
| 408 |
+
if stdout is None:
|
| 409 |
+
stdout = StringIO()
|
| 410 |
+
|
| 411 |
+
if not isatty(sys.stdin) or not isatty(stdout):
|
| 412 |
+
return _nullpager(stdout, color)
|
| 413 |
+
|
| 414 |
+
# Split using POSIX mode (the default) so that quote characters are
|
| 415 |
+
# stripped from tokens and quoted Windows paths are preserved.
|
| 416 |
+
# Non-POSIX mode retains quotes in tokens, and wrapping tokens
|
| 417 |
+
# with shlex.quote re-introduces quoting issues on Windows.
|
| 418 |
+
pager_cmd_parts = shlex.split(os.environ.get("PAGER", ""))
|
| 419 |
+
if pager_cmd_parts:
|
| 420 |
+
if WIN:
|
| 421 |
+
return _tempfilepager(pager_cmd_parts, color)
|
| 422 |
+
return _pipepager(pager_cmd_parts, color)
|
| 423 |
+
|
| 424 |
+
if os.environ.get("TERM") in ("dumb", "emacs"):
|
| 425 |
+
return _nullpager(stdout, color)
|
| 426 |
+
if WIN or sys.platform.startswith("os2"):
|
| 427 |
+
return _tempfilepager(["more"], color)
|
| 428 |
+
return _pipepager(["less"], color)
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
@contextlib.contextmanager
|
| 432 |
+
def get_pager_file(color: bool | None = None) -> t.Generator[t.TextIO, None, None]:
|
| 433 |
+
"""Context manager.
|
| 434 |
+
|
| 435 |
+
Yields a writable file-like object which can be used as an output pager.
|
| 436 |
+
|
| 437 |
+
.. versionadded:: 8.4.0
|
| 438 |
+
|
| 439 |
+
:param color: controls if the pager supports ANSI colors or not. The
|
| 440 |
+
default is autodetection.
|
| 441 |
+
"""
|
| 442 |
+
with _pager_contextmanager(color=color) as (stream, encoding, color):
|
| 443 |
+
# Split streams by capabilities rather than the abstract TextIO /
|
| 444 |
+
# BinaryIO annotations: buffered text streams can be unwrapped to bytes,
|
| 445 |
+
# while other streams are yielded as-is.
|
| 446 |
+
wrapper: MaybeStripAnsi | None = None
|
| 447 |
+
if _has_binary_buffer(stream):
|
| 448 |
+
# Text stream backed by a binary buffer.
|
| 449 |
+
wrapper = MaybeStripAnsi(stream.buffer, color=color, encoding=encoding)
|
| 450 |
+
stream = wrapper
|
| 451 |
+
try:
|
| 452 |
+
# Narrow the BinaryIO | TextIO union that _pager_contextmanager
|
| 453 |
+
# yields; the caller writes text to the pager.
|
| 454 |
+
yield t.cast(t.TextIO, stream)
|
| 455 |
+
finally:
|
| 456 |
+
try:
|
| 457 |
+
stream.flush()
|
| 458 |
+
finally:
|
| 459 |
+
# Hand the binary buffer back to the pager that produced it
|
| 460 |
+
# rather than letting this TextIOWrapper close it on garbage
|
| 461 |
+
# collection. The pager owns the buffer's lifecycle: subprocess
|
| 462 |
+
# pipes and temp files are closed by their own helpers, while a
|
| 463 |
+
# borrowed stdout must stay open for the caller. detach() runs
|
| 464 |
+
# even if flush() raised, so the buffer is never closed here.
|
| 465 |
+
if wrapper is not None:
|
| 466 |
+
wrapper.detach()
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
@contextlib.contextmanager
|
| 470 |
+
def _pipepager(
|
| 471 |
+
cmd_parts: list[str], color: bool | None = None
|
| 472 |
+
) -> t.Iterator[tuple[t.BinaryIO | t.TextIO, str, bool]]:
|
| 473 |
+
"""Page through text by feeding it to another program.
|
| 474 |
+
|
| 475 |
+
Invokes the pager via :class:`subprocess.Popen` with an ``argv`` list
|
| 476 |
+
produced by :func:`shlex.split`. The command is resolved to an absolute
|
| 477 |
+
path with :func:`shutil.which` as recommended by the
|
| 478 |
+
:mod:`subprocess` docs for Windows compatibility.
|
| 479 |
+
|
| 480 |
+
Invoking a pager through this might support colors: if piping to
|
| 481 |
+
``less`` and the user hasn't decided on colors, ``LESS=-R`` is set
|
| 482 |
+
automatically.
|
| 483 |
+
"""
|
| 484 |
+
# Split the command into the invoked CLI and its parameters.
|
| 485 |
+
if not cmd_parts:
|
| 486 |
+
# No usable pager: fall back to stdout through _nullpager so it gets the
|
| 487 |
+
# same borrowed-stream handling and the caller's stream is not closed.
|
| 488 |
+
stdout = _default_text_stdout() or StringIO()
|
| 489 |
+
with _nullpager(stdout, color) as rv:
|
| 490 |
+
yield rv
|
| 491 |
+
return
|
| 492 |
+
|
| 493 |
+
import shutil
|
| 494 |
+
|
| 495 |
+
cmd = cmd_parts[0]
|
| 496 |
+
cmd_params = cmd_parts[1:]
|
| 497 |
+
|
| 498 |
+
cmd_filepath = shutil.which(cmd)
|
| 499 |
+
if not cmd_filepath:
|
| 500 |
+
# No usable pager: fall back to stdout through _nullpager so it gets the
|
| 501 |
+
# same borrowed-stream handling and the caller's stream is not closed.
|
| 502 |
+
stdout = _default_text_stdout() or StringIO()
|
| 503 |
+
with _nullpager(stdout, color) as rv:
|
| 504 |
+
yield rv
|
| 505 |
+
return
|
| 506 |
+
|
| 507 |
+
# Produces a normalized absolute path string.
|
| 508 |
+
# multi-call binaries such as busybox derive their identity from the symlink
|
| 509 |
+
# less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox)
|
| 510 |
+
cmd_path = Path(cmd_filepath).absolute()
|
| 511 |
+
cmd_name = cmd_path.name
|
| 512 |
+
|
| 513 |
+
import subprocess
|
| 514 |
+
|
| 515 |
+
# Make a local copy of the environment to not affect the global one.
|
| 516 |
+
env = dict(os.environ)
|
| 517 |
+
|
| 518 |
+
# If we're piping to less and the user hasn't decided on colors, we enable
|
| 519 |
+
# them by default we find the -R flag in the command line arguments.
|
| 520 |
+
if color is None and cmd_name == "less":
|
| 521 |
+
less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_params)}"
|
| 522 |
+
if not less_flags:
|
| 523 |
+
env["LESS"] = "-R"
|
| 524 |
+
color = True
|
| 525 |
+
elif "r" in less_flags or "R" in less_flags:
|
| 526 |
+
color = True
|
| 527 |
+
|
| 528 |
+
if color is None:
|
| 529 |
+
color = False
|
| 530 |
+
|
| 531 |
+
c = subprocess.Popen(
|
| 532 |
+
[str(cmd_path)] + cmd_params,
|
| 533 |
+
shell=False,
|
| 534 |
+
stdin=subprocess.PIPE,
|
| 535 |
+
env=env,
|
| 536 |
+
errors="replace",
|
| 537 |
+
text=True,
|
| 538 |
+
)
|
| 539 |
+
stdin = t.cast(t.BinaryIO, c.stdin)
|
| 540 |
+
encoding = get_best_encoding(stdin)
|
| 541 |
+
try:
|
| 542 |
+
yield stdin, encoding, color
|
| 543 |
+
except BrokenPipeError:
|
| 544 |
+
# In case the pager exited unexpectedly, ignore the broken pipe error.
|
| 545 |
+
pass
|
| 546 |
+
except Exception as e:
|
| 547 |
+
# In case there is an exception we want to close the pager immediately
|
| 548 |
+
# and let the caller handle it.
|
| 549 |
+
# Otherwise the pager will keep running, and the user may not notice
|
| 550 |
+
# the error message, or worse yet it may leave the terminal in a broken state.
|
| 551 |
+
c.terminate()
|
| 552 |
+
raise e
|
| 553 |
+
finally:
|
| 554 |
+
# We must close stdin and wait for the pager to exit before we continue
|
| 555 |
+
try:
|
| 556 |
+
stdin.close()
|
| 557 |
+
# Close implies flush, so it might throw a BrokenPipeError if the pager
|
| 558 |
+
# process exited already.
|
| 559 |
+
except BrokenPipeError:
|
| 560 |
+
pass
|
| 561 |
+
|
| 562 |
+
# Less doesn't respect ^C, but catches it for its own UI purposes (aborting
|
| 563 |
+
# search or other commands inside less).
|
| 564 |
+
#
|
| 565 |
+
# That means when the user hits ^C, the parent process (click) terminates,
|
| 566 |
+
# but less is still alive, paging the output and messing up the terminal.
|
| 567 |
+
#
|
| 568 |
+
# If the user wants to make the pager exit on ^C, they should set
|
| 569 |
+
# `LESS='-K'`. It's not our decision to make.
|
| 570 |
+
while True:
|
| 571 |
+
try:
|
| 572 |
+
c.wait()
|
| 573 |
+
except KeyboardInterrupt:
|
| 574 |
+
pass
|
| 575 |
+
else:
|
| 576 |
+
break
|
| 577 |
+
|
| 578 |
+
|
| 579 |
+
@contextlib.contextmanager
|
| 580 |
+
def _tempfilepager(
|
| 581 |
+
cmd_parts: list[str], color: bool | None = None
|
| 582 |
+
) -> t.Iterator[tuple[t.BinaryIO | t.TextIO, str, bool]]:
|
| 583 |
+
"""Page through text by invoking a program on a temporary file.
|
| 584 |
+
|
| 585 |
+
Used as the primary pager strategy on Windows (where piping to
|
| 586 |
+
``more`` adds spurious ``\\r\\n``), and as a fallback on other
|
| 587 |
+
platforms. The command is resolved to an absolute path with
|
| 588 |
+
:func:`shutil.which`.
|
| 589 |
+
"""
|
| 590 |
+
# Split the command into the invoked CLI and its parameters.
|
| 591 |
+
if not cmd_parts:
|
| 592 |
+
# No usable pager: fall back to stdout through _nullpager so it gets the
|
| 593 |
+
# same borrowed-stream handling and the caller's stream is not closed.
|
| 594 |
+
stdout = _default_text_stdout() or StringIO()
|
| 595 |
+
with _nullpager(stdout, color) as rv:
|
| 596 |
+
yield rv
|
| 597 |
+
return
|
| 598 |
+
|
| 599 |
+
import shutil
|
| 600 |
+
import subprocess
|
| 601 |
+
|
| 602 |
+
cmd = cmd_parts[0]
|
| 603 |
+
|
| 604 |
+
cmd_filepath = shutil.which(cmd)
|
| 605 |
+
if not cmd_filepath:
|
| 606 |
+
# No usable pager: fall back to stdout through _nullpager so it gets the
|
| 607 |
+
# same borrowed-stream handling and the caller's stream is not closed.
|
| 608 |
+
stdout = _default_text_stdout() or StringIO()
|
| 609 |
+
with _nullpager(stdout, color) as rv:
|
| 610 |
+
yield rv
|
| 611 |
+
return
|
| 612 |
+
|
| 613 |
+
# Produces a normalized absolute path string.
|
| 614 |
+
# multi-call binaries such as busybox derive their identity from the symlink
|
| 615 |
+
# less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox)
|
| 616 |
+
cmd_path = Path(cmd_filepath).absolute()
|
| 617 |
+
|
| 618 |
+
import tempfile
|
| 619 |
+
|
| 620 |
+
encoding = get_best_encoding(sys.stdout)
|
| 621 |
+
if color is None:
|
| 622 |
+
color = False
|
| 623 |
+
# On Windows, NamedTemporaryFile cannot be opened by another process
|
| 624 |
+
# while Python still has it open, so we use delete=False and clean up manually
|
| 625 |
+
# rather than using a contextmanager here.
|
| 626 |
+
f = tempfile.NamedTemporaryFile(mode="wb", delete=False)
|
| 627 |
+
try:
|
| 628 |
+
yield t.cast(t.BinaryIO, f), encoding, color
|
| 629 |
+
f.flush()
|
| 630 |
+
f.close()
|
| 631 |
+
subprocess.call([str(cmd_path), f.name])
|
| 632 |
+
finally:
|
| 633 |
+
os.unlink(f.name)
|
| 634 |
+
|
| 635 |
+
|
| 636 |
+
@contextlib.contextmanager
|
| 637 |
+
def _nullpager(
|
| 638 |
+
stream: t.TextIO, color: bool | None = None
|
| 639 |
+
) -> t.Iterator[tuple[t.TextIO, str, bool]]:
|
| 640 |
+
"""Simply print unformatted text. This is the ultimate fallback. Don't close the
|
| 641 |
+
output stream in this case, since it's coming from elsewhere rather than our
|
| 642 |
+
internal helpers.
|
| 643 |
+
|
| 644 |
+
The stream is wrapped in :class:`~click.utils.KeepOpenFile` so that, as a
|
| 645 |
+
borrowed stream, it is not closed by a ``with`` block. The wrapper that
|
| 646 |
+
:func:`get_pager_file` builds around it is detached rather than closed.
|
| 647 |
+
"""
|
| 648 |
+
encoding = get_best_encoding(stream)
|
| 649 |
+
|
| 650 |
+
if color is None:
|
| 651 |
+
color = False
|
| 652 |
+
|
| 653 |
+
yield KeepOpenFile(stream), encoding, color # type: ignore[misc]
|
| 654 |
+
|
| 655 |
+
|
| 656 |
+
class Editor:
|
| 657 |
+
def __init__(
|
| 658 |
+
self,
|
| 659 |
+
editor: str | None = None,
|
| 660 |
+
env: cabc.Mapping[str, str] | None = None,
|
| 661 |
+
require_save: bool = True,
|
| 662 |
+
extension: str = ".txt",
|
| 663 |
+
) -> None:
|
| 664 |
+
self.editor = editor
|
| 665 |
+
self.env = env
|
| 666 |
+
self.require_save = require_save
|
| 667 |
+
self.extension = extension
|
| 668 |
+
|
| 669 |
+
def get_editor(self) -> str:
|
| 670 |
+
if self.editor is not None:
|
| 671 |
+
return self.editor
|
| 672 |
+
for key in "VISUAL", "EDITOR":
|
| 673 |
+
rv = os.environ.get(key)
|
| 674 |
+
if rv:
|
| 675 |
+
return rv
|
| 676 |
+
if WIN:
|
| 677 |
+
return "notepad"
|
| 678 |
+
|
| 679 |
+
from shutil import which
|
| 680 |
+
|
| 681 |
+
for editor in "sensible-editor", "vim", "nano":
|
| 682 |
+
if which(editor) is not None:
|
| 683 |
+
return editor
|
| 684 |
+
return "vi"
|
| 685 |
+
|
| 686 |
+
def edit_files(self, filenames: cabc.Iterable[str]) -> None:
|
| 687 |
+
"""Open files in the user's editor."""
|
| 688 |
+
import shlex
|
| 689 |
+
import subprocess
|
| 690 |
+
|
| 691 |
+
editor = self.get_editor()
|
| 692 |
+
environ: dict[str, str] | None = None
|
| 693 |
+
|
| 694 |
+
if self.env:
|
| 695 |
+
environ = os.environ.copy()
|
| 696 |
+
environ.update(self.env)
|
| 697 |
+
|
| 698 |
+
try:
|
| 699 |
+
# Split in POSIX mode (the default) for the same reasons as
|
| 700 |
+
# in pager(): strips quotes from tokens and preserves quoted
|
| 701 |
+
# Windows paths.
|
| 702 |
+
c = subprocess.Popen(
|
| 703 |
+
args=shlex.split(editor) + list(filenames),
|
| 704 |
+
env=environ,
|
| 705 |
+
)
|
| 706 |
+
exit_code = c.wait()
|
| 707 |
+
if exit_code != 0:
|
| 708 |
+
raise ClickException(
|
| 709 |
+
_("{editor}: Editing failed").format(editor=editor)
|
| 710 |
+
)
|
| 711 |
+
except OSError as e:
|
| 712 |
+
raise ClickException(
|
| 713 |
+
_("{editor}: Editing failed: {e}").format(editor=editor, e=e)
|
| 714 |
+
) from e
|
| 715 |
+
|
| 716 |
+
@t.overload
|
| 717 |
+
def edit(self, text: bytes | bytearray) -> bytes | None: ...
|
| 718 |
+
|
| 719 |
+
# We cannot know whether or not the type expected is str or bytes when None
|
| 720 |
+
# is passed, so str is returned as that was what was done before.
|
| 721 |
+
@t.overload
|
| 722 |
+
def edit(self, text: str | None) -> str | None: ...
|
| 723 |
+
|
| 724 |
+
def edit(self, text: str | bytes | bytearray | None) -> str | bytes | None:
|
| 725 |
+
import tempfile
|
| 726 |
+
|
| 727 |
+
if text is None:
|
| 728 |
+
data: bytes | bytearray = b""
|
| 729 |
+
elif isinstance(text, (bytes, bytearray)):
|
| 730 |
+
data = text
|
| 731 |
+
else:
|
| 732 |
+
if text and not text.endswith("\n"):
|
| 733 |
+
text += "\n"
|
| 734 |
+
|
| 735 |
+
if WIN:
|
| 736 |
+
data = text.replace("\n", "\r\n").encode("utf-8-sig")
|
| 737 |
+
else:
|
| 738 |
+
data = text.encode("utf-8")
|
| 739 |
+
|
| 740 |
+
fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension)
|
| 741 |
+
f: t.BinaryIO
|
| 742 |
+
|
| 743 |
+
try:
|
| 744 |
+
with os.fdopen(fd, "wb") as f:
|
| 745 |
+
f.write(data)
|
| 746 |
+
|
| 747 |
+
# If the filesystem resolution is 1 second, like Mac OS
|
| 748 |
+
# 10.12 Extended, or 2 seconds, like FAT32, and the editor
|
| 749 |
+
# closes very fast, require_save can fail. Set the modified
|
| 750 |
+
# time to be 2 seconds in the past to work around this.
|
| 751 |
+
os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2))
|
| 752 |
+
# Depending on the resolution, the exact value might not be
|
| 753 |
+
# recorded, so get the new recorded value.
|
| 754 |
+
timestamp = os.path.getmtime(name)
|
| 755 |
+
|
| 756 |
+
self.edit_files((name,))
|
| 757 |
+
|
| 758 |
+
if self.require_save and os.path.getmtime(name) == timestamp:
|
| 759 |
+
return None
|
| 760 |
+
|
| 761 |
+
with open(name, "rb") as f:
|
| 762 |
+
rv = f.read()
|
| 763 |
+
|
| 764 |
+
if isinstance(text, (bytes, bytearray)):
|
| 765 |
+
return rv
|
| 766 |
+
|
| 767 |
+
return rv.decode("utf-8-sig").replace("\r\n", "\n")
|
| 768 |
+
finally:
|
| 769 |
+
os.unlink(name)
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
def open_url(url: str, wait: bool = False, locate: bool = False) -> int:
|
| 773 |
+
import subprocess
|
| 774 |
+
|
| 775 |
+
def _unquote_file(url: str) -> str:
|
| 776 |
+
from urllib.parse import unquote
|
| 777 |
+
|
| 778 |
+
if url.startswith("file://"):
|
| 779 |
+
url = unquote(url[7:])
|
| 780 |
+
|
| 781 |
+
return url
|
| 782 |
+
|
| 783 |
+
if sys.platform == "darwin":
|
| 784 |
+
args = ["open"]
|
| 785 |
+
if wait:
|
| 786 |
+
args.append("-W")
|
| 787 |
+
if locate:
|
| 788 |
+
args.append("-R")
|
| 789 |
+
args.append(_unquote_file(url))
|
| 790 |
+
null = open("/dev/null", "w")
|
| 791 |
+
try:
|
| 792 |
+
return subprocess.Popen(args, stderr=null).wait()
|
| 793 |
+
finally:
|
| 794 |
+
null.close()
|
| 795 |
+
elif WIN:
|
| 796 |
+
if locate:
|
| 797 |
+
url = _unquote_file(url)
|
| 798 |
+
args = ["explorer", "/select,", url]
|
| 799 |
+
try:
|
| 800 |
+
return subprocess.call(args)
|
| 801 |
+
except OSError:
|
| 802 |
+
return 127
|
| 803 |
+
else:
|
| 804 |
+
try:
|
| 805 |
+
os.startfile(url) # type: ignore[attr-defined]
|
| 806 |
+
except OSError:
|
| 807 |
+
return 127
|
| 808 |
+
return 0
|
| 809 |
+
elif CYGWIN:
|
| 810 |
+
if locate:
|
| 811 |
+
url = _unquote_file(url)
|
| 812 |
+
args = ["cygstart", os.path.dirname(url)]
|
| 813 |
+
else:
|
| 814 |
+
args = ["cygstart"]
|
| 815 |
+
if wait:
|
| 816 |
+
args.append("-w")
|
| 817 |
+
args.append(url)
|
| 818 |
+
try:
|
| 819 |
+
return subprocess.call(args)
|
| 820 |
+
except OSError:
|
| 821 |
+
# Command not found
|
| 822 |
+
return 127
|
| 823 |
+
|
| 824 |
+
try:
|
| 825 |
+
if locate:
|
| 826 |
+
url = os.path.dirname(_unquote_file(url)) or "."
|
| 827 |
+
else:
|
| 828 |
+
url = _unquote_file(url)
|
| 829 |
+
c = subprocess.Popen(["xdg-open", url])
|
| 830 |
+
if wait:
|
| 831 |
+
return c.wait()
|
| 832 |
+
return 0
|
| 833 |
+
except OSError:
|
| 834 |
+
if url.startswith(("http://", "https://")) and not locate and not wait:
|
| 835 |
+
import webbrowser
|
| 836 |
+
|
| 837 |
+
webbrowser.open(url)
|
| 838 |
+
return 0
|
| 839 |
+
return 1
|
| 840 |
+
|
| 841 |
+
|
| 842 |
+
def _translate_ch_to_exc(ch: str) -> None:
|
| 843 |
+
if ch == "\x03":
|
| 844 |
+
raise KeyboardInterrupt()
|
| 845 |
+
|
| 846 |
+
if ch == "\x04" and not WIN: # Unix-like, Ctrl+D
|
| 847 |
+
raise EOFError()
|
| 848 |
+
|
| 849 |
+
if ch == "\x1a" and WIN: # Windows, Ctrl+Z
|
| 850 |
+
raise EOFError()
|
| 851 |
+
|
| 852 |
+
|
| 853 |
+
if sys.platform == "win32":
|
| 854 |
+
import msvcrt
|
| 855 |
+
|
| 856 |
+
@contextlib.contextmanager
|
| 857 |
+
def raw_terminal() -> cabc.Iterator[int]:
|
| 858 |
+
yield -1
|
| 859 |
+
|
| 860 |
+
def getchar(echo: bool) -> str:
|
| 861 |
+
# The function `getch` will return a bytes object corresponding to
|
| 862 |
+
# the pressed character. Since Windows 10 build 1803, it will also
|
| 863 |
+
# return \x00 when called a second time after pressing a regular key.
|
| 864 |
+
#
|
| 865 |
+
# `getwch` does not share this probably-bugged behavior. Moreover, it
|
| 866 |
+
# returns a Unicode object by default, which is what we want.
|
| 867 |
+
#
|
| 868 |
+
# Either of these functions will return \x00 or \xe0 to indicate
|
| 869 |
+
# a special key, and you need to call the same function again to get
|
| 870 |
+
# the "rest" of the code. The fun part is that \u00e0 is
|
| 871 |
+
# "latin small letter a with grave", so if you type that on a French
|
| 872 |
+
# keyboard, you _also_ get a \xe0.
|
| 873 |
+
# E.g., consider the Up arrow. This returns \xe0 and then \x48. The
|
| 874 |
+
# resulting Unicode string reads as "a with grave" + "capital H".
|
| 875 |
+
# This is indistinguishable from when the user actually types
|
| 876 |
+
# "a with grave" and then "capital H".
|
| 877 |
+
#
|
| 878 |
+
# When \xe0 is returned, we assume it's part of a special-key sequence
|
| 879 |
+
# and call `getwch` again, but that means that when the user types
|
| 880 |
+
# the \u00e0 character, `getchar` doesn't return until a second
|
| 881 |
+
# character is typed.
|
| 882 |
+
# The alternative is returning immediately, but that would mess up
|
| 883 |
+
# cross-platform handling of arrow keys and others that start with
|
| 884 |
+
# \xe0. Another option is using `getch`, but then we can't reliably
|
| 885 |
+
# read non-ASCII characters, because return values of `getch` are
|
| 886 |
+
# limited to the current 8-bit codepage.
|
| 887 |
+
#
|
| 888 |
+
# Anyway, Click doesn't claim to do this Right(tm), and using `getwch`
|
| 889 |
+
# is doing the right thing in more situations than with `getch`.
|
| 890 |
+
|
| 891 |
+
if echo:
|
| 892 |
+
func = t.cast(t.Callable[[], str], msvcrt.getwche)
|
| 893 |
+
else:
|
| 894 |
+
func = t.cast(t.Callable[[], str], msvcrt.getwch)
|
| 895 |
+
|
| 896 |
+
rv = func()
|
| 897 |
+
|
| 898 |
+
if rv in ("\x00", "\xe0"):
|
| 899 |
+
# \x00 and \xe0 are control characters that indicate special key,
|
| 900 |
+
# see above.
|
| 901 |
+
rv += func()
|
| 902 |
+
|
| 903 |
+
_translate_ch_to_exc(rv)
|
| 904 |
+
return rv
|
| 905 |
+
|
| 906 |
+
else:
|
| 907 |
+
import termios
|
| 908 |
+
import tty
|
| 909 |
+
|
| 910 |
+
@contextlib.contextmanager
|
| 911 |
+
def raw_terminal() -> cabc.Iterator[int]:
|
| 912 |
+
f: t.TextIO | None
|
| 913 |
+
fd: int
|
| 914 |
+
|
| 915 |
+
if not isatty(sys.stdin):
|
| 916 |
+
f = open("/dev/tty")
|
| 917 |
+
fd = f.fileno()
|
| 918 |
+
else:
|
| 919 |
+
fd = sys.stdin.fileno()
|
| 920 |
+
f = None
|
| 921 |
+
|
| 922 |
+
try:
|
| 923 |
+
old_settings = termios.tcgetattr(fd)
|
| 924 |
+
|
| 925 |
+
try:
|
| 926 |
+
tty.setraw(fd)
|
| 927 |
+
yield fd
|
| 928 |
+
finally:
|
| 929 |
+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
| 930 |
+
sys.stdout.flush()
|
| 931 |
+
|
| 932 |
+
if f is not None:
|
| 933 |
+
f.close()
|
| 934 |
+
except termios.error:
|
| 935 |
+
pass
|
| 936 |
+
|
| 937 |
+
def getchar(echo: bool) -> str:
|
| 938 |
+
with raw_terminal() as fd:
|
| 939 |
+
ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace")
|
| 940 |
+
|
| 941 |
+
if echo and isatty(sys.stdout):
|
| 942 |
+
sys.stdout.write(ch)
|
| 943 |
+
|
| 944 |
+
_translate_ch_to_exc(ch)
|
| 945 |
+
return ch
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_textwrap.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections.abc as cabc
|
| 4 |
+
import textwrap
|
| 5 |
+
from contextlib import contextmanager
|
| 6 |
+
|
| 7 |
+
from ._compat import _ansi_re
|
| 8 |
+
from ._compat import term_len
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def _truncate_visible(text: str, n: int) -> str:
|
| 12 |
+
"""Return the longest prefix of ``text`` containing at most ``n`` visible
|
| 13 |
+
characters.
|
| 14 |
+
|
| 15 |
+
ANSI escape sequences inside the prefix are kept intact and do not count
|
| 16 |
+
toward the visible width. A cut is never placed inside an escape sequence.
|
| 17 |
+
"""
|
| 18 |
+
if n <= 0:
|
| 19 |
+
return ""
|
| 20 |
+
|
| 21 |
+
visible = 0
|
| 22 |
+
i = 0
|
| 23 |
+
cut = 0
|
| 24 |
+
end = len(text)
|
| 25 |
+
while i < end:
|
| 26 |
+
m = _ansi_re.match(text, i)
|
| 27 |
+
if m is not None:
|
| 28 |
+
i = m.end()
|
| 29 |
+
continue
|
| 30 |
+
visible += 1
|
| 31 |
+
i += 1
|
| 32 |
+
cut = i
|
| 33 |
+
if visible >= n:
|
| 34 |
+
break
|
| 35 |
+
return text[:cut]
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class TextWrapper(textwrap.TextWrapper):
|
| 39 |
+
"""``textwrap.TextWrapper`` variant that measures widths by visible
|
| 40 |
+
character count.
|
| 41 |
+
|
| 42 |
+
ANSI escape sequences embedded in chunks, indents, or the placeholder are
|
| 43 |
+
excluded from the width budget. Without this, styled help text (a styled
|
| 44 |
+
``Usage:`` prefix, a colorized option name, ...) would be wrapped earlier
|
| 45 |
+
than its visible length warrants and tokens would split mid-word.
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
def _handle_long_word(
|
| 49 |
+
self,
|
| 50 |
+
reversed_chunks: list[str],
|
| 51 |
+
cur_line: list[str],
|
| 52 |
+
cur_len: int,
|
| 53 |
+
width: int,
|
| 54 |
+
) -> None:
|
| 55 |
+
space_left = max(width - cur_len, 1)
|
| 56 |
+
|
| 57 |
+
if self.break_long_words:
|
| 58 |
+
last = reversed_chunks[-1]
|
| 59 |
+
cut = _truncate_visible(last, space_left)
|
| 60 |
+
res = last[len(cut) :]
|
| 61 |
+
cur_line.append(cut)
|
| 62 |
+
reversed_chunks[-1] = res
|
| 63 |
+
elif not cur_line:
|
| 64 |
+
cur_line.append(reversed_chunks.pop())
|
| 65 |
+
|
| 66 |
+
def _wrap_chunks(self, chunks: list[str]) -> list[str]:
|
| 67 |
+
"""Wrap chunks counting widths in visible characters.
|
| 68 |
+
|
| 69 |
+
Mirrors the algorithm of :meth:`textwrap.TextWrapper._wrap_chunks`
|
| 70 |
+
with every width measurement routed through
|
| 71 |
+
:func:`click._compat.term_len` instead of :func:`len`, so ANSI escape
|
| 72 |
+
bytes in chunks, indents, or the placeholder do not inflate the count.
|
| 73 |
+
|
| 74 |
+
.. seealso::
|
| 75 |
+
:class:`textwrap.TextWrapper` in the Python standard library documentation:
|
| 76 |
+
https://docs.python.org/3/library/textwrap.html#textwrap.TextWrapper
|
| 77 |
+
|
| 78 |
+
Reference implementation in CPython:
|
| 79 |
+
https://github.com/python/cpython/blob/main/Lib/textwrap.py
|
| 80 |
+
"""
|
| 81 |
+
lines: list[str] = []
|
| 82 |
+
if self.width <= 0:
|
| 83 |
+
raise ValueError(f"invalid width {self.width!r} (must be > 0)")
|
| 84 |
+
if self.max_lines is not None:
|
| 85 |
+
if self.max_lines > 1:
|
| 86 |
+
indent = self.subsequent_indent
|
| 87 |
+
else:
|
| 88 |
+
indent = self.initial_indent
|
| 89 |
+
if term_len(indent) + term_len(self.placeholder.lstrip()) > self.width:
|
| 90 |
+
raise ValueError("placeholder too large for max width")
|
| 91 |
+
|
| 92 |
+
chunks.reverse()
|
| 93 |
+
|
| 94 |
+
while chunks:
|
| 95 |
+
cur_line: list[str] = []
|
| 96 |
+
cur_len = 0
|
| 97 |
+
|
| 98 |
+
if lines:
|
| 99 |
+
indent = self.subsequent_indent
|
| 100 |
+
else:
|
| 101 |
+
indent = self.initial_indent
|
| 102 |
+
|
| 103 |
+
width = self.width - term_len(indent)
|
| 104 |
+
|
| 105 |
+
if self.drop_whitespace and chunks[-1].strip() == "" and lines:
|
| 106 |
+
del chunks[-1]
|
| 107 |
+
|
| 108 |
+
while chunks:
|
| 109 |
+
n = term_len(chunks[-1])
|
| 110 |
+
|
| 111 |
+
if cur_len + n <= width:
|
| 112 |
+
cur_line.append(chunks.pop())
|
| 113 |
+
cur_len += n
|
| 114 |
+
|
| 115 |
+
else:
|
| 116 |
+
break
|
| 117 |
+
|
| 118 |
+
if chunks and term_len(chunks[-1]) > width:
|
| 119 |
+
self._handle_long_word(chunks, cur_line, cur_len, width)
|
| 120 |
+
cur_len = sum(map(term_len, cur_line))
|
| 121 |
+
|
| 122 |
+
if self.drop_whitespace and cur_line and cur_line[-1].strip() == "":
|
| 123 |
+
cur_len -= term_len(cur_line[-1])
|
| 124 |
+
del cur_line[-1]
|
| 125 |
+
|
| 126 |
+
if cur_line:
|
| 127 |
+
if (
|
| 128 |
+
self.max_lines is None
|
| 129 |
+
or len(lines) + 1 < self.max_lines
|
| 130 |
+
or (
|
| 131 |
+
not chunks
|
| 132 |
+
or self.drop_whitespace
|
| 133 |
+
and len(chunks) == 1
|
| 134 |
+
and not chunks[0].strip()
|
| 135 |
+
)
|
| 136 |
+
and cur_len <= width
|
| 137 |
+
):
|
| 138 |
+
lines.append(indent + "".join(cur_line))
|
| 139 |
+
else:
|
| 140 |
+
while cur_line:
|
| 141 |
+
if (
|
| 142 |
+
cur_line[-1].strip()
|
| 143 |
+
and cur_len + term_len(self.placeholder) <= width
|
| 144 |
+
):
|
| 145 |
+
cur_line.append(self.placeholder)
|
| 146 |
+
lines.append(indent + "".join(cur_line))
|
| 147 |
+
break
|
| 148 |
+
cur_len -= term_len(cur_line[-1])
|
| 149 |
+
del cur_line[-1]
|
| 150 |
+
else:
|
| 151 |
+
if lines:
|
| 152 |
+
prev_line = lines[-1].rstrip()
|
| 153 |
+
if (
|
| 154 |
+
term_len(prev_line) + term_len(self.placeholder)
|
| 155 |
+
<= self.width
|
| 156 |
+
):
|
| 157 |
+
lines[-1] = prev_line + self.placeholder
|
| 158 |
+
break
|
| 159 |
+
lines.append(indent + self.placeholder.lstrip())
|
| 160 |
+
break
|
| 161 |
+
|
| 162 |
+
return lines
|
| 163 |
+
|
| 164 |
+
@contextmanager
|
| 165 |
+
def extra_indent(self, indent: str) -> cabc.Iterator[None]:
|
| 166 |
+
old_initial_indent = self.initial_indent
|
| 167 |
+
old_subsequent_indent = self.subsequent_indent
|
| 168 |
+
self.initial_indent += indent
|
| 169 |
+
self.subsequent_indent += indent
|
| 170 |
+
|
| 171 |
+
try:
|
| 172 |
+
yield
|
| 173 |
+
finally:
|
| 174 |
+
self.initial_indent = old_initial_indent
|
| 175 |
+
self.subsequent_indent = old_subsequent_indent
|
| 176 |
+
|
| 177 |
+
def indent_only(self, text: str) -> str:
|
| 178 |
+
rv = []
|
| 179 |
+
|
| 180 |
+
for idx, line in enumerate(text.splitlines()):
|
| 181 |
+
indent = self.initial_indent
|
| 182 |
+
|
| 183 |
+
if idx > 0:
|
| 184 |
+
indent = self.subsequent_indent
|
| 185 |
+
|
| 186 |
+
rv.append(f"{indent}{line}")
|
| 187 |
+
|
| 188 |
+
return "\n".join(rv)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_utils.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import enum
|
| 4 |
+
import typing as t
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class Sentinel(enum.Enum):
|
| 8 |
+
"""Enum used to define sentinel values.
|
| 9 |
+
|
| 10 |
+
.. seealso::
|
| 11 |
+
|
| 12 |
+
`PEP 661 - Sentinel Values <https://peps.python.org/pep-0661/>`_.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
UNSET = object()
|
| 16 |
+
FLAG_NEEDS_VALUE = object()
|
| 17 |
+
|
| 18 |
+
def __repr__(self) -> str:
|
| 19 |
+
return f"{self.__class__.__name__}.{self.name}"
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
UNSET: t.Literal[Sentinel.UNSET] = Sentinel.UNSET
|
| 23 |
+
"""Sentinel used to indicate that a value is not set."""
|
| 24 |
+
|
| 25 |
+
FLAG_NEEDS_VALUE: t.Literal[Sentinel.FLAG_NEEDS_VALUE] = Sentinel.FLAG_NEEDS_VALUE
|
| 26 |
+
"""Sentinel used to indicate an option was passed as a flag without a
|
| 27 |
+
value but is not a flag option.
|
| 28 |
+
|
| 29 |
+
``Option.consume_value`` uses this to prompt or use the ``flag_value``.
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
T_UNSET: t.TypeAlias = t.Literal[Sentinel.UNSET]
|
| 33 |
+
"""Type hint for the :data:`UNSET` sentinel value."""
|
| 34 |
+
|
| 35 |
+
T_FLAG_NEEDS_VALUE: t.TypeAlias = t.Literal[Sentinel.FLAG_NEEDS_VALUE]
|
| 36 |
+
"""Type hint for the :data:`FLAG_NEEDS_VALUE` sentinel value."""
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_winconsole.py
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This module is based on the excellent work by Adam Bartoš who
|
| 2 |
+
# provided a lot of what went into the implementation here in
|
| 3 |
+
# the discussion to issue1602 in the Python bug tracker.
|
| 4 |
+
#
|
| 5 |
+
# There are some general differences in regards to how this works
|
| 6 |
+
# compared to the original patches as we do not need to patch
|
| 7 |
+
# the entire interpreter but just work in our little world of
|
| 8 |
+
# echo and prompt.
|
| 9 |
+
from __future__ import annotations
|
| 10 |
+
|
| 11 |
+
import collections.abc as cabc
|
| 12 |
+
import io
|
| 13 |
+
import sys
|
| 14 |
+
import time
|
| 15 |
+
import typing as t
|
| 16 |
+
from ctypes import Array
|
| 17 |
+
from ctypes import byref
|
| 18 |
+
from ctypes import c_char
|
| 19 |
+
from ctypes import c_char_p
|
| 20 |
+
from ctypes import c_int
|
| 21 |
+
from ctypes import c_ssize_t
|
| 22 |
+
from ctypes import c_ulong
|
| 23 |
+
from ctypes import c_void_p
|
| 24 |
+
from ctypes import POINTER
|
| 25 |
+
from ctypes import py_object
|
| 26 |
+
from ctypes import Structure
|
| 27 |
+
from ctypes.wintypes import DWORD
|
| 28 |
+
from ctypes.wintypes import HANDLE
|
| 29 |
+
from ctypes.wintypes import LPCWSTR
|
| 30 |
+
from ctypes.wintypes import LPWSTR
|
| 31 |
+
from gettext import gettext as _
|
| 32 |
+
|
| 33 |
+
from ._compat import _NonClosingTextIOWrapper
|
| 34 |
+
|
| 35 |
+
assert sys.platform == "win32"
|
| 36 |
+
import msvcrt # noqa: E402
|
| 37 |
+
from ctypes import windll # noqa: E402
|
| 38 |
+
from ctypes import WINFUNCTYPE # noqa: E402
|
| 39 |
+
|
| 40 |
+
c_ssize_p = POINTER(c_ssize_t)
|
| 41 |
+
|
| 42 |
+
kernel32 = windll.kernel32
|
| 43 |
+
GetStdHandle = kernel32.GetStdHandle
|
| 44 |
+
ReadConsoleW = kernel32.ReadConsoleW
|
| 45 |
+
WriteConsoleW = kernel32.WriteConsoleW
|
| 46 |
+
GetConsoleMode = kernel32.GetConsoleMode
|
| 47 |
+
GetLastError = kernel32.GetLastError
|
| 48 |
+
GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32))
|
| 49 |
+
CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))(
|
| 50 |
+
("CommandLineToArgvW", windll.shell32)
|
| 51 |
+
)
|
| 52 |
+
LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32))
|
| 53 |
+
|
| 54 |
+
STDIN_HANDLE = GetStdHandle(-10)
|
| 55 |
+
STDOUT_HANDLE = GetStdHandle(-11)
|
| 56 |
+
STDERR_HANDLE = GetStdHandle(-12)
|
| 57 |
+
|
| 58 |
+
PyBUF_SIMPLE = 0
|
| 59 |
+
PyBUF_WRITABLE = 1
|
| 60 |
+
|
| 61 |
+
ERROR_SUCCESS = 0
|
| 62 |
+
ERROR_NOT_ENOUGH_MEMORY = 8
|
| 63 |
+
ERROR_OPERATION_ABORTED = 995
|
| 64 |
+
|
| 65 |
+
STDIN_FILENO = 0
|
| 66 |
+
STDOUT_FILENO = 1
|
| 67 |
+
STDERR_FILENO = 2
|
| 68 |
+
|
| 69 |
+
EOF = b"\x1a"
|
| 70 |
+
MAX_BYTES_WRITTEN = 32767
|
| 71 |
+
|
| 72 |
+
if t.TYPE_CHECKING:
|
| 73 |
+
try:
|
| 74 |
+
# Using `typing_extensions.Buffer` instead of `collections.abc`
|
| 75 |
+
# on Windows for some reason does not have `Sized` implemented.
|
| 76 |
+
from collections.abc import Buffer # type: ignore
|
| 77 |
+
except ImportError:
|
| 78 |
+
from typing_extensions import Buffer
|
| 79 |
+
|
| 80 |
+
try:
|
| 81 |
+
from ctypes import pythonapi
|
| 82 |
+
except ImportError:
|
| 83 |
+
# On PyPy we cannot get buffers so our ability to operate here is
|
| 84 |
+
# severely limited.
|
| 85 |
+
get_buffer = None
|
| 86 |
+
else:
|
| 87 |
+
|
| 88 |
+
class Py_buffer(Structure):
|
| 89 |
+
_fields_ = [ # noqa: RUF012
|
| 90 |
+
("buf", c_void_p),
|
| 91 |
+
("obj", py_object),
|
| 92 |
+
("len", c_ssize_t),
|
| 93 |
+
("itemsize", c_ssize_t),
|
| 94 |
+
("readonly", c_int),
|
| 95 |
+
("ndim", c_int),
|
| 96 |
+
("format", c_char_p),
|
| 97 |
+
("shape", c_ssize_p),
|
| 98 |
+
("strides", c_ssize_p),
|
| 99 |
+
("suboffsets", c_ssize_p),
|
| 100 |
+
("internal", c_void_p),
|
| 101 |
+
]
|
| 102 |
+
|
| 103 |
+
PyObject_GetBuffer = pythonapi.PyObject_GetBuffer
|
| 104 |
+
PyBuffer_Release = pythonapi.PyBuffer_Release
|
| 105 |
+
|
| 106 |
+
def get_buffer(obj: Buffer, writable: bool = False) -> Array[c_char]:
|
| 107 |
+
buf = Py_buffer()
|
| 108 |
+
flags: int = PyBUF_WRITABLE if writable else PyBUF_SIMPLE
|
| 109 |
+
PyObject_GetBuffer(py_object(obj), byref(buf), flags)
|
| 110 |
+
|
| 111 |
+
try:
|
| 112 |
+
buffer_type = c_char * buf.len
|
| 113 |
+
out: Array[c_char] = buffer_type.from_address(buf.buf)
|
| 114 |
+
return out
|
| 115 |
+
finally:
|
| 116 |
+
PyBuffer_Release(byref(buf))
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class _WindowsConsoleRawIOBase(io.RawIOBase):
|
| 120 |
+
def __init__(self, handle: int | None) -> None:
|
| 121 |
+
self.handle = handle
|
| 122 |
+
|
| 123 |
+
def isatty(self) -> t.Literal[True]:
|
| 124 |
+
super().isatty()
|
| 125 |
+
return True
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
class _WindowsConsoleReader(_WindowsConsoleRawIOBase):
|
| 129 |
+
def readable(self) -> t.Literal[True]:
|
| 130 |
+
return True
|
| 131 |
+
|
| 132 |
+
def readinto(self, b: Buffer) -> int:
|
| 133 |
+
bytes_to_be_read = len(b)
|
| 134 |
+
if not bytes_to_be_read:
|
| 135 |
+
return 0
|
| 136 |
+
elif bytes_to_be_read % 2:
|
| 137 |
+
raise ValueError(
|
| 138 |
+
"cannot read odd number of bytes from UTF-16-LE encoded console"
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
buffer = get_buffer(b, writable=True)
|
| 142 |
+
code_units_to_be_read = bytes_to_be_read // 2
|
| 143 |
+
code_units_read = c_ulong()
|
| 144 |
+
|
| 145 |
+
rv = ReadConsoleW(
|
| 146 |
+
HANDLE(self.handle),
|
| 147 |
+
buffer,
|
| 148 |
+
code_units_to_be_read,
|
| 149 |
+
byref(code_units_read),
|
| 150 |
+
None,
|
| 151 |
+
)
|
| 152 |
+
if GetLastError() == ERROR_OPERATION_ABORTED:
|
| 153 |
+
# wait for KeyboardInterrupt
|
| 154 |
+
time.sleep(0.1)
|
| 155 |
+
if not rv:
|
| 156 |
+
raise OSError(_("Windows error: {error}").format(error=GetLastError()))
|
| 157 |
+
|
| 158 |
+
if buffer[0] == EOF:
|
| 159 |
+
return 0
|
| 160 |
+
return 2 * code_units_read.value
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
class _WindowsConsoleWriter(_WindowsConsoleRawIOBase):
|
| 164 |
+
def writable(self) -> t.Literal[True]:
|
| 165 |
+
return True
|
| 166 |
+
|
| 167 |
+
@staticmethod
|
| 168 |
+
def _get_error_message(errno: int) -> str:
|
| 169 |
+
if errno == ERROR_SUCCESS:
|
| 170 |
+
return "ERROR_SUCCESS"
|
| 171 |
+
elif errno == ERROR_NOT_ENOUGH_MEMORY:
|
| 172 |
+
return "ERROR_NOT_ENOUGH_MEMORY"
|
| 173 |
+
return _("Windows error: {error}").format(error=errno)
|
| 174 |
+
|
| 175 |
+
def write(self, b: Buffer) -> int:
|
| 176 |
+
bytes_to_be_written = len(b)
|
| 177 |
+
buf = get_buffer(b)
|
| 178 |
+
code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2
|
| 179 |
+
code_units_written = c_ulong()
|
| 180 |
+
|
| 181 |
+
WriteConsoleW(
|
| 182 |
+
HANDLE(self.handle),
|
| 183 |
+
buf,
|
| 184 |
+
code_units_to_be_written,
|
| 185 |
+
byref(code_units_written),
|
| 186 |
+
None,
|
| 187 |
+
)
|
| 188 |
+
bytes_written = 2 * code_units_written.value
|
| 189 |
+
|
| 190 |
+
if bytes_written == 0 and bytes_to_be_written > 0:
|
| 191 |
+
raise OSError(self._get_error_message(GetLastError()))
|
| 192 |
+
return bytes_written
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
class ConsoleStream:
|
| 196 |
+
def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None:
|
| 197 |
+
self._text_stream = text_stream
|
| 198 |
+
self.buffer = byte_stream
|
| 199 |
+
|
| 200 |
+
@property
|
| 201 |
+
def name(self) -> str:
|
| 202 |
+
return self.buffer.name
|
| 203 |
+
|
| 204 |
+
def write(self, x: t.AnyStr) -> int:
|
| 205 |
+
if isinstance(x, str):
|
| 206 |
+
return self._text_stream.write(x)
|
| 207 |
+
try:
|
| 208 |
+
self.flush()
|
| 209 |
+
except Exception:
|
| 210 |
+
pass
|
| 211 |
+
return self.buffer.write(x)
|
| 212 |
+
|
| 213 |
+
def writelines(self, lines: cabc.Iterable[t.AnyStr]) -> None:
|
| 214 |
+
for line in lines:
|
| 215 |
+
self.write(line)
|
| 216 |
+
|
| 217 |
+
def __getattr__(self, name: str) -> t.Any:
|
| 218 |
+
return getattr(self._text_stream, name)
|
| 219 |
+
|
| 220 |
+
def isatty(self) -> bool:
|
| 221 |
+
return self.buffer.isatty()
|
| 222 |
+
|
| 223 |
+
def __repr__(self) -> str:
|
| 224 |
+
return f"<ConsoleStream name={self.name!r} encoding={self.encoding!r}>"
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO:
|
| 228 |
+
text_stream = _NonClosingTextIOWrapper(
|
| 229 |
+
io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)),
|
| 230 |
+
"utf-16-le",
|
| 231 |
+
"strict",
|
| 232 |
+
line_buffering=True,
|
| 233 |
+
)
|
| 234 |
+
return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream))
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO:
|
| 238 |
+
text_stream = _NonClosingTextIOWrapper(
|
| 239 |
+
io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)),
|
| 240 |
+
"utf-16-le",
|
| 241 |
+
"strict",
|
| 242 |
+
line_buffering=True,
|
| 243 |
+
)
|
| 244 |
+
return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream))
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO:
|
| 248 |
+
text_stream = _NonClosingTextIOWrapper(
|
| 249 |
+
io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)),
|
| 250 |
+
"utf-16-le",
|
| 251 |
+
"strict",
|
| 252 |
+
line_buffering=True,
|
| 253 |
+
)
|
| 254 |
+
return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream))
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
_stream_factories: cabc.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = {
|
| 258 |
+
0: _get_text_stdin,
|
| 259 |
+
1: _get_text_stdout,
|
| 260 |
+
2: _get_text_stderr,
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def _is_console(f: t.TextIO) -> bool:
|
| 265 |
+
if not hasattr(f, "fileno"):
|
| 266 |
+
return False
|
| 267 |
+
|
| 268 |
+
try:
|
| 269 |
+
fileno = f.fileno()
|
| 270 |
+
except (OSError, io.UnsupportedOperation):
|
| 271 |
+
return False
|
| 272 |
+
|
| 273 |
+
handle = msvcrt.get_osfhandle(fileno)
|
| 274 |
+
return bool(GetConsoleMode(handle, byref(DWORD())))
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def _get_windows_console_stream(
|
| 278 |
+
f: t.TextIO, encoding: str | None, errors: str | None
|
| 279 |
+
) -> t.TextIO | None:
|
| 280 |
+
if (
|
| 281 |
+
get_buffer is None
|
| 282 |
+
or encoding not in {"utf-16-le", None}
|
| 283 |
+
or errors not in {"strict", None}
|
| 284 |
+
or not _is_console(f)
|
| 285 |
+
):
|
| 286 |
+
return None
|
| 287 |
+
|
| 288 |
+
func = _stream_factories.get(f.fileno())
|
| 289 |
+
if func is None:
|
| 290 |
+
return None
|
| 291 |
+
|
| 292 |
+
b = getattr(f, "buffer", None)
|
| 293 |
+
|
| 294 |
+
if b is None:
|
| 295 |
+
return None
|
| 296 |
+
|
| 297 |
+
return func(b)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/core.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/decorators.py
ADDED
|
@@ -0,0 +1,575 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import inspect
|
| 4 |
+
import typing as t
|
| 5 |
+
from functools import update_wrapper
|
| 6 |
+
from gettext import gettext as _
|
| 7 |
+
|
| 8 |
+
from .core import Argument
|
| 9 |
+
from .core import Command
|
| 10 |
+
from .core import Context
|
| 11 |
+
from .core import Group
|
| 12 |
+
from .core import Option
|
| 13 |
+
from .core import Parameter
|
| 14 |
+
from .globals import get_current_context
|
| 15 |
+
from .utils import echo
|
| 16 |
+
|
| 17 |
+
if t.TYPE_CHECKING:
|
| 18 |
+
import typing_extensions as te
|
| 19 |
+
|
| 20 |
+
P = te.ParamSpec("P")
|
| 21 |
+
|
| 22 |
+
R = t.TypeVar("R")
|
| 23 |
+
T = t.TypeVar("T")
|
| 24 |
+
_AnyCallable = t.Callable[..., t.Any]
|
| 25 |
+
FC = t.TypeVar("FC", bound="_AnyCallable | Command")
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def pass_context(f: t.Callable[te.Concatenate[Context, P], R]) -> t.Callable[P, R]:
|
| 29 |
+
"""Marks a callback as wanting to receive the current context
|
| 30 |
+
object as first argument.
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
|
| 34 |
+
return f(get_current_context(), *args, **kwargs)
|
| 35 |
+
|
| 36 |
+
return update_wrapper(new_func, f)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def pass_obj(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]:
|
| 40 |
+
"""Similar to :func:`pass_context`, but only pass the object on the
|
| 41 |
+
context onwards (:attr:`Context.obj`). This is useful if that object
|
| 42 |
+
represents the state of a nested system.
|
| 43 |
+
"""
|
| 44 |
+
|
| 45 |
+
def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
|
| 46 |
+
return f(get_current_context().obj, *args, **kwargs)
|
| 47 |
+
|
| 48 |
+
return update_wrapper(new_func, f)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def make_pass_decorator(
|
| 52 |
+
object_type: type[T], ensure: bool = False
|
| 53 |
+
) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]:
|
| 54 |
+
"""Given an object type this creates a decorator that will work
|
| 55 |
+
similar to :func:`pass_obj` but instead of passing the object of the
|
| 56 |
+
current context, it will find the innermost context of type
|
| 57 |
+
:func:`object_type`.
|
| 58 |
+
|
| 59 |
+
This generates a decorator that works roughly like this::
|
| 60 |
+
|
| 61 |
+
from functools import update_wrapper
|
| 62 |
+
|
| 63 |
+
def decorator(f):
|
| 64 |
+
@pass_context
|
| 65 |
+
def new_func(ctx, *args, **kwargs):
|
| 66 |
+
obj = ctx.find_object(object_type)
|
| 67 |
+
return ctx.invoke(f, obj, *args, **kwargs)
|
| 68 |
+
return update_wrapper(new_func, f)
|
| 69 |
+
return decorator
|
| 70 |
+
|
| 71 |
+
:param object_type: the type of the object to pass.
|
| 72 |
+
:param ensure: if set to `True`, a new object will be created and
|
| 73 |
+
remembered on the context if it's not there yet.
|
| 74 |
+
"""
|
| 75 |
+
|
| 76 |
+
def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]:
|
| 77 |
+
def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
|
| 78 |
+
ctx = get_current_context()
|
| 79 |
+
|
| 80 |
+
obj: T | None
|
| 81 |
+
if ensure:
|
| 82 |
+
obj = ctx.ensure_object(object_type)
|
| 83 |
+
else:
|
| 84 |
+
obj = ctx.find_object(object_type)
|
| 85 |
+
|
| 86 |
+
if obj is None:
|
| 87 |
+
raise RuntimeError(
|
| 88 |
+
"Managed to invoke callback without a context"
|
| 89 |
+
f" object of type {object_type.__name__!r}"
|
| 90 |
+
" existing."
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
return ctx.invoke(f, obj, *args, **kwargs)
|
| 94 |
+
|
| 95 |
+
return update_wrapper(new_func, f)
|
| 96 |
+
|
| 97 |
+
return decorator
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def pass_meta_key(
|
| 101 |
+
key: str, *, doc_description: str | None = None
|
| 102 |
+
) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]:
|
| 103 |
+
"""Create a decorator that passes a key from
|
| 104 |
+
:attr:`click.Context.meta` as the first argument to the decorated
|
| 105 |
+
function.
|
| 106 |
+
|
| 107 |
+
:param key: Key in ``Context.meta`` to pass.
|
| 108 |
+
:param doc_description: Description of the object being passed,
|
| 109 |
+
inserted into the decorator's docstring. Defaults to "the 'key'
|
| 110 |
+
key from Context.meta".
|
| 111 |
+
|
| 112 |
+
.. versionadded:: 8.0
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]:
|
| 116 |
+
def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
|
| 117 |
+
ctx = get_current_context()
|
| 118 |
+
obj = ctx.meta[key]
|
| 119 |
+
return ctx.invoke(f, obj, *args, **kwargs)
|
| 120 |
+
|
| 121 |
+
return update_wrapper(new_func, f)
|
| 122 |
+
|
| 123 |
+
if doc_description is None:
|
| 124 |
+
doc_description = f"the {key!r} key from :attr:`click.Context.meta`"
|
| 125 |
+
|
| 126 |
+
decorator.__doc__ = (
|
| 127 |
+
f"Decorator that passes {doc_description} as the first argument"
|
| 128 |
+
" to the decorated function."
|
| 129 |
+
)
|
| 130 |
+
return decorator
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
CmdType = t.TypeVar("CmdType", bound=Command)
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
# variant: no call, directly as decorator for a function.
|
| 137 |
+
@t.overload
|
| 138 |
+
def command(name: _AnyCallable) -> Command: ...
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
# variant: with positional name and with positional or keyword cls argument:
|
| 142 |
+
# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...)
|
| 143 |
+
@t.overload
|
| 144 |
+
def command(
|
| 145 |
+
name: str | None,
|
| 146 |
+
cls: type[CmdType],
|
| 147 |
+
**attrs: t.Any,
|
| 148 |
+
) -> t.Callable[[_AnyCallable], CmdType]: ...
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...)
|
| 152 |
+
@t.overload
|
| 153 |
+
def command(
|
| 154 |
+
name: None = None,
|
| 155 |
+
*,
|
| 156 |
+
cls: type[CmdType],
|
| 157 |
+
**attrs: t.Any,
|
| 158 |
+
) -> t.Callable[[_AnyCallable], CmdType]: ...
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
# variant: with optional string name, no cls argument provided.
|
| 162 |
+
@t.overload
|
| 163 |
+
def command(
|
| 164 |
+
name: str | None = ..., cls: None = None, **attrs: t.Any
|
| 165 |
+
) -> t.Callable[[_AnyCallable], Command]: ...
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def command(
|
| 169 |
+
name: str | _AnyCallable | None = None,
|
| 170 |
+
cls: type[CmdType] | None = None,
|
| 171 |
+
**attrs: t.Any,
|
| 172 |
+
) -> Command | t.Callable[[_AnyCallable], Command | CmdType]:
|
| 173 |
+
r"""Creates a new :class:`Command` and uses the decorated function as
|
| 174 |
+
callback. This will also automatically attach all decorated
|
| 175 |
+
:func:`option`\s and :func:`argument`\s as parameters to the command.
|
| 176 |
+
|
| 177 |
+
The name of the command defaults to the name of the function, converted to
|
| 178 |
+
lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes
|
| 179 |
+
``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example,
|
| 180 |
+
``init_data_command`` becomes ``init-data``.
|
| 181 |
+
|
| 182 |
+
All keyword arguments are forwarded to the underlying command class.
|
| 183 |
+
For the ``params`` argument, any decorated params are appended to
|
| 184 |
+
the end of the list.
|
| 185 |
+
|
| 186 |
+
Once decorated the function turns into a :class:`Command` instance
|
| 187 |
+
that can be invoked as a command line utility or be attached to a
|
| 188 |
+
command :class:`Group`.
|
| 189 |
+
|
| 190 |
+
:param name: The name of the command. Defaults to modifying the function's
|
| 191 |
+
name as described above.
|
| 192 |
+
:param cls: The command class to create. Defaults to :class:`Command`.
|
| 193 |
+
|
| 194 |
+
.. versionchanged:: 8.2
|
| 195 |
+
The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are
|
| 196 |
+
removed when generating the name.
|
| 197 |
+
|
| 198 |
+
.. versionchanged:: 8.1
|
| 199 |
+
This decorator can be applied without parentheses.
|
| 200 |
+
|
| 201 |
+
.. versionchanged:: 8.1
|
| 202 |
+
The ``params`` argument can be used. Decorated params are
|
| 203 |
+
appended to the end of the list.
|
| 204 |
+
"""
|
| 205 |
+
|
| 206 |
+
func: t.Callable[[_AnyCallable], t.Any] | None = None
|
| 207 |
+
|
| 208 |
+
if callable(name):
|
| 209 |
+
func = name
|
| 210 |
+
name = None
|
| 211 |
+
assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class."
|
| 212 |
+
assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments."
|
| 213 |
+
|
| 214 |
+
if cls is None:
|
| 215 |
+
cls = t.cast("type[CmdType]", Command)
|
| 216 |
+
|
| 217 |
+
def decorator(f: _AnyCallable) -> CmdType:
|
| 218 |
+
if isinstance(f, Command):
|
| 219 |
+
raise TypeError("Attempted to convert a callback into a command twice.")
|
| 220 |
+
|
| 221 |
+
attr_params = attrs.pop("params", None)
|
| 222 |
+
params = attr_params if attr_params is not None else []
|
| 223 |
+
|
| 224 |
+
try:
|
| 225 |
+
decorator_params = f.__click_params__ # type: ignore
|
| 226 |
+
except AttributeError:
|
| 227 |
+
pass
|
| 228 |
+
else:
|
| 229 |
+
del f.__click_params__ # type: ignore
|
| 230 |
+
params.extend(reversed(decorator_params))
|
| 231 |
+
|
| 232 |
+
if attrs.get("help") is None:
|
| 233 |
+
attrs["help"] = f.__doc__
|
| 234 |
+
|
| 235 |
+
if t.TYPE_CHECKING:
|
| 236 |
+
assert cls is not None
|
| 237 |
+
assert not callable(name)
|
| 238 |
+
|
| 239 |
+
if name is not None:
|
| 240 |
+
cmd_name = name
|
| 241 |
+
else:
|
| 242 |
+
cmd_name = f.__name__.lower().replace("_", "-")
|
| 243 |
+
cmd_left, sep, suffix = cmd_name.rpartition("-")
|
| 244 |
+
|
| 245 |
+
if sep and suffix in {"command", "cmd", "group", "grp"}:
|
| 246 |
+
cmd_name = cmd_left
|
| 247 |
+
|
| 248 |
+
cmd = cls(name=cmd_name, callback=f, params=params, **attrs)
|
| 249 |
+
cmd.__doc__ = f.__doc__
|
| 250 |
+
return cmd
|
| 251 |
+
|
| 252 |
+
if func is not None:
|
| 253 |
+
return decorator(func)
|
| 254 |
+
|
| 255 |
+
return decorator
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
GrpType = t.TypeVar("GrpType", bound=Group)
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
# variant: no call, directly as decorator for a function.
|
| 262 |
+
@t.overload
|
| 263 |
+
def group(name: _AnyCallable) -> Group: ...
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
# variant: with positional name and with positional or keyword cls argument:
|
| 267 |
+
# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...)
|
| 268 |
+
@t.overload
|
| 269 |
+
def group(
|
| 270 |
+
name: str | None,
|
| 271 |
+
cls: type[GrpType],
|
| 272 |
+
**attrs: t.Any,
|
| 273 |
+
) -> t.Callable[[_AnyCallable], GrpType]: ...
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...)
|
| 277 |
+
@t.overload
|
| 278 |
+
def group(
|
| 279 |
+
name: None = None,
|
| 280 |
+
*,
|
| 281 |
+
cls: type[GrpType],
|
| 282 |
+
**attrs: t.Any,
|
| 283 |
+
) -> t.Callable[[_AnyCallable], GrpType]: ...
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
# variant: with optional string name, no cls argument provided.
|
| 287 |
+
@t.overload
|
| 288 |
+
def group(
|
| 289 |
+
name: str | None = ..., cls: None = None, **attrs: t.Any
|
| 290 |
+
) -> t.Callable[[_AnyCallable], Group]: ...
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def group(
|
| 294 |
+
name: str | _AnyCallable | None = None,
|
| 295 |
+
cls: type[GrpType] | None = None,
|
| 296 |
+
**attrs: t.Any,
|
| 297 |
+
) -> Group | t.Callable[[_AnyCallable], Group | GrpType]:
|
| 298 |
+
"""Creates a new :class:`Group` with a function as callback. This
|
| 299 |
+
works otherwise the same as :func:`command` just that the `cls`
|
| 300 |
+
parameter is set to :class:`Group`.
|
| 301 |
+
|
| 302 |
+
.. versionchanged:: 8.1
|
| 303 |
+
This decorator can be applied without parentheses.
|
| 304 |
+
"""
|
| 305 |
+
if cls is None:
|
| 306 |
+
cls = t.cast("type[GrpType]", Group)
|
| 307 |
+
|
| 308 |
+
if callable(name):
|
| 309 |
+
return command(cls=cls, **attrs)(name)
|
| 310 |
+
|
| 311 |
+
return command(name, cls, **attrs)
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None:
|
| 315 |
+
if isinstance(f, Command):
|
| 316 |
+
f.params.append(param)
|
| 317 |
+
else:
|
| 318 |
+
if not hasattr(f, "__click_params__"):
|
| 319 |
+
f.__click_params__ = [] # type: ignore
|
| 320 |
+
|
| 321 |
+
f.__click_params__.append(param) # type: ignore
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
def argument(
|
| 325 |
+
*param_decls: str, cls: type[Argument] | None = None, **attrs: t.Any
|
| 326 |
+
) -> t.Callable[[FC], FC]:
|
| 327 |
+
"""Attaches an argument to the command. All positional arguments are
|
| 328 |
+
passed as parameter declarations to :class:`Argument`; all keyword
|
| 329 |
+
arguments are forwarded unchanged (except ``cls``).
|
| 330 |
+
This is equivalent to creating an :class:`Argument` instance manually
|
| 331 |
+
and attaching it to the :attr:`Command.params` list.
|
| 332 |
+
|
| 333 |
+
For the default argument class, refer to :class:`Argument` and
|
| 334 |
+
:class:`Parameter` for descriptions of parameters.
|
| 335 |
+
|
| 336 |
+
:param cls: the argument class to instantiate. This defaults to
|
| 337 |
+
:class:`Argument`.
|
| 338 |
+
:param param_decls: Passed as positional arguments to the constructor of
|
| 339 |
+
``cls``.
|
| 340 |
+
:param attrs: Passed as keyword arguments to the constructor of ``cls``.
|
| 341 |
+
"""
|
| 342 |
+
if cls is None:
|
| 343 |
+
cls = Argument
|
| 344 |
+
|
| 345 |
+
def decorator(f: FC) -> FC:
|
| 346 |
+
_param_memo(f, cls(param_decls, **attrs))
|
| 347 |
+
return f
|
| 348 |
+
|
| 349 |
+
return decorator
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
def option(
|
| 353 |
+
*param_decls: str, cls: type[Option] | None = None, **attrs: t.Any
|
| 354 |
+
) -> t.Callable[[FC], FC]:
|
| 355 |
+
"""Attaches an option to the command. All positional arguments are
|
| 356 |
+
passed as parameter declarations to :class:`Option`; all keyword
|
| 357 |
+
arguments are forwarded unchanged (except ``cls``).
|
| 358 |
+
This is equivalent to creating an :class:`Option` instance manually
|
| 359 |
+
and attaching it to the :attr:`Command.params` list.
|
| 360 |
+
|
| 361 |
+
For the default option class, refer to :class:`Option` and
|
| 362 |
+
:class:`Parameter` for descriptions of parameters.
|
| 363 |
+
|
| 364 |
+
:param cls: the option class to instantiate. This defaults to
|
| 365 |
+
:class:`Option`.
|
| 366 |
+
:param param_decls: Passed as positional arguments to the constructor of
|
| 367 |
+
``cls``.
|
| 368 |
+
:param attrs: Passed as keyword arguments to the constructor of ``cls``.
|
| 369 |
+
"""
|
| 370 |
+
if cls is None:
|
| 371 |
+
cls = Option
|
| 372 |
+
|
| 373 |
+
def decorator(f: FC) -> FC:
|
| 374 |
+
_param_memo(f, cls(param_decls, **attrs))
|
| 375 |
+
return f
|
| 376 |
+
|
| 377 |
+
return decorator
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
|
| 381 |
+
"""Add a ``--yes`` option which shows a prompt before continuing if
|
| 382 |
+
not passed. If the prompt is declined, the program will exit.
|
| 383 |
+
|
| 384 |
+
:param param_decls: One or more option names. Defaults to the single
|
| 385 |
+
value ``"--yes"``.
|
| 386 |
+
:param kwargs: Extra arguments are passed to :func:`option`.
|
| 387 |
+
"""
|
| 388 |
+
|
| 389 |
+
def callback(ctx: Context, param: Parameter, value: bool) -> None:
|
| 390 |
+
if not value:
|
| 391 |
+
ctx.abort()
|
| 392 |
+
|
| 393 |
+
if not param_decls:
|
| 394 |
+
param_decls = ("--yes",)
|
| 395 |
+
|
| 396 |
+
kwargs.setdefault("is_flag", True)
|
| 397 |
+
kwargs.setdefault("callback", callback)
|
| 398 |
+
kwargs.setdefault("expose_value", False)
|
| 399 |
+
kwargs.setdefault("prompt", _("Do you want to continue?"))
|
| 400 |
+
kwargs.setdefault("help", _("Confirm the action without prompting."))
|
| 401 |
+
return option(*param_decls, **kwargs)
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
|
| 405 |
+
"""Add a ``--password`` option which prompts for a password, hiding
|
| 406 |
+
input and asking to enter the value again for confirmation.
|
| 407 |
+
|
| 408 |
+
:param param_decls: One or more option names. Defaults to the single
|
| 409 |
+
value ``"--password"``.
|
| 410 |
+
:param kwargs: Extra arguments are passed to :func:`option`.
|
| 411 |
+
"""
|
| 412 |
+
if not param_decls:
|
| 413 |
+
param_decls = ("--password",)
|
| 414 |
+
|
| 415 |
+
kwargs.setdefault("prompt", True)
|
| 416 |
+
kwargs.setdefault("confirmation_prompt", True)
|
| 417 |
+
kwargs.setdefault("hide_input", True)
|
| 418 |
+
return option(*param_decls, **kwargs)
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
def version_option(
|
| 422 |
+
version: str | None = None,
|
| 423 |
+
*param_decls: str,
|
| 424 |
+
package_name: str | None = None,
|
| 425 |
+
prog_name: str | None = None,
|
| 426 |
+
message: str | None = None,
|
| 427 |
+
**kwargs: t.Any,
|
| 428 |
+
) -> t.Callable[[FC], FC]:
|
| 429 |
+
"""Add a ``--version`` option which immediately prints the version
|
| 430 |
+
number and exits the program.
|
| 431 |
+
|
| 432 |
+
If ``version`` is not provided, Click will try to detect it using
|
| 433 |
+
:func:`importlib.metadata.version` to get the version for the
|
| 434 |
+
``package_name``.
|
| 435 |
+
|
| 436 |
+
If ``package_name`` is not provided, Click will try to detect it by
|
| 437 |
+
inspecting the stack frames. If the detected (or given) name does
|
| 438 |
+
not match an installed distribution, Click resolves it as an import
|
| 439 |
+
(top-level module) name via
|
| 440 |
+
:func:`importlib.metadata.packages_distributions`, so e.g. ``PIL``
|
| 441 |
+
resolves to the ``Pillow`` distribution.
|
| 442 |
+
|
| 443 |
+
:param version: The version number to show. If not provided, Click
|
| 444 |
+
will try to detect it.
|
| 445 |
+
:param param_decls: One or more option names. Defaults to the single
|
| 446 |
+
value ``"--version"``.
|
| 447 |
+
:param package_name: The package name to detect the version from. If
|
| 448 |
+
not provided, Click will try to detect it.
|
| 449 |
+
:param prog_name: The name of the CLI to show in the message. If not
|
| 450 |
+
provided, it will be detected from the command.
|
| 451 |
+
:param message: The message to show. The values ``%(prog)s``,
|
| 452 |
+
``%(package)s``, and ``%(version)s`` are available. Defaults to
|
| 453 |
+
``"%(prog)s, version %(version)s"``.
|
| 454 |
+
:param kwargs: Extra arguments are passed to :func:`option`.
|
| 455 |
+
:raise RuntimeError: ``version`` could not be detected.
|
| 456 |
+
|
| 457 |
+
.. versionchanged:: 8.0
|
| 458 |
+
Add the ``package_name`` parameter, and the ``%(package)s``
|
| 459 |
+
value for messages.
|
| 460 |
+
|
| 461 |
+
.. versionchanged:: 8.0
|
| 462 |
+
Use :mod:`importlib.metadata` instead of ``pkg_resources``. The
|
| 463 |
+
version is detected based on the package name, not the entry
|
| 464 |
+
point name. The Python package name must match the installed
|
| 465 |
+
package name, or be passed with ``package_name=``.
|
| 466 |
+
|
| 467 |
+
.. versionchanged:: 8.4.2
|
| 468 |
+
When ``package_name`` does not match an installed distribution,
|
| 469 |
+
Click now resolves it as an import (top-level module).
|
| 470 |
+
"""
|
| 471 |
+
if message is None:
|
| 472 |
+
message = _("%(prog)s, version %(version)s")
|
| 473 |
+
|
| 474 |
+
if version is None and package_name is None:
|
| 475 |
+
frame = inspect.currentframe()
|
| 476 |
+
f_back = frame.f_back if frame is not None else None
|
| 477 |
+
f_globals = f_back.f_globals if f_back is not None else None
|
| 478 |
+
# break reference cycle
|
| 479 |
+
# https://docs.python.org/3/library/inspect.html#the-interpreter-stack
|
| 480 |
+
del frame
|
| 481 |
+
|
| 482 |
+
if f_globals is not None:
|
| 483 |
+
package_name = f_globals.get("__name__")
|
| 484 |
+
|
| 485 |
+
if package_name == "__main__":
|
| 486 |
+
package_name = f_globals.get("__package__")
|
| 487 |
+
|
| 488 |
+
if package_name:
|
| 489 |
+
package_name = package_name.partition(".")[0]
|
| 490 |
+
|
| 491 |
+
def callback(ctx: Context, param: Parameter, value: bool) -> None:
|
| 492 |
+
if not value or ctx.resilient_parsing:
|
| 493 |
+
return
|
| 494 |
+
|
| 495 |
+
nonlocal prog_name
|
| 496 |
+
nonlocal version
|
| 497 |
+
nonlocal package_name
|
| 498 |
+
|
| 499 |
+
if prog_name is None:
|
| 500 |
+
prog_name = ctx.find_root().info_name
|
| 501 |
+
|
| 502 |
+
if version is None and package_name is not None:
|
| 503 |
+
import importlib.metadata
|
| 504 |
+
|
| 505 |
+
try:
|
| 506 |
+
version = importlib.metadata.version(package_name)
|
| 507 |
+
except importlib.metadata.PackageNotFoundError:
|
| 508 |
+
# The given name didn't match an installed distribution.
|
| 509 |
+
# Try resolving it as an import (top-level module) name,
|
| 510 |
+
# e.g. ``PIL`` is provided by the ``Pillow`` distribution.
|
| 511 |
+
distributions = importlib.metadata.packages_distributions().get(
|
| 512 |
+
package_name, []
|
| 513 |
+
)
|
| 514 |
+
if len(distributions) == 1:
|
| 515 |
+
package_name = distributions[0]
|
| 516 |
+
version = importlib.metadata.version(package_name)
|
| 517 |
+
elif len(distributions) > 1:
|
| 518 |
+
raise RuntimeError(
|
| 519 |
+
f"{package_name!r} maps to multiple installed"
|
| 520 |
+
f" distributions ({', '.join(distributions)})."
|
| 521 |
+
" Pass 'package_name' to disambiguate."
|
| 522 |
+
) from None
|
| 523 |
+
else:
|
| 524 |
+
raise RuntimeError(
|
| 525 |
+
f"{package_name!r} is not installed. Try passing"
|
| 526 |
+
" 'package_name' instead."
|
| 527 |
+
) from None
|
| 528 |
+
|
| 529 |
+
if version is None:
|
| 530 |
+
raise RuntimeError(
|
| 531 |
+
f"Could not determine the version for {package_name!r} automatically."
|
| 532 |
+
)
|
| 533 |
+
|
| 534 |
+
echo(
|
| 535 |
+
message % {"prog": prog_name, "package": package_name, "version": version},
|
| 536 |
+
color=ctx.color,
|
| 537 |
+
)
|
| 538 |
+
ctx.exit()
|
| 539 |
+
|
| 540 |
+
if not param_decls:
|
| 541 |
+
param_decls = ("--version",)
|
| 542 |
+
|
| 543 |
+
kwargs.setdefault("is_flag", True)
|
| 544 |
+
kwargs.setdefault("expose_value", False)
|
| 545 |
+
kwargs.setdefault("is_eager", True)
|
| 546 |
+
kwargs.setdefault("help", _("Show the version and exit."))
|
| 547 |
+
kwargs["callback"] = callback
|
| 548 |
+
return option(*param_decls, **kwargs)
|
| 549 |
+
|
| 550 |
+
|
| 551 |
+
def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
|
| 552 |
+
"""Pre-configured ``--help`` option which immediately prints the help page
|
| 553 |
+
and exits the program.
|
| 554 |
+
|
| 555 |
+
:param param_decls: One or more option names. Defaults to the single
|
| 556 |
+
value ``"--help"``.
|
| 557 |
+
:param kwargs: Extra arguments are passed to :func:`option`.
|
| 558 |
+
"""
|
| 559 |
+
|
| 560 |
+
def show_help(ctx: Context, param: Parameter, value: bool) -> None:
|
| 561 |
+
"""Callback that print the help page on ``<stdout>`` and exits."""
|
| 562 |
+
if value and not ctx.resilient_parsing:
|
| 563 |
+
echo(ctx.get_help(), color=ctx.color)
|
| 564 |
+
ctx.exit()
|
| 565 |
+
|
| 566 |
+
if not param_decls:
|
| 567 |
+
param_decls = ("--help",)
|
| 568 |
+
|
| 569 |
+
kwargs.setdefault("is_flag", True)
|
| 570 |
+
kwargs.setdefault("expose_value", False)
|
| 571 |
+
kwargs.setdefault("is_eager", True)
|
| 572 |
+
kwargs.setdefault("help", _("Show this message and exit."))
|
| 573 |
+
kwargs.setdefault("callback", show_help)
|
| 574 |
+
|
| 575 |
+
return option(*param_decls, **kwargs)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/exceptions.py
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections.abc as cabc
|
| 4 |
+
import typing as t
|
| 5 |
+
from gettext import gettext as _
|
| 6 |
+
from gettext import ngettext
|
| 7 |
+
|
| 8 |
+
from ._compat import get_text_stderr
|
| 9 |
+
from .globals import resolve_color_default
|
| 10 |
+
from .utils import echo
|
| 11 |
+
from .utils import format_filename
|
| 12 |
+
|
| 13 |
+
if t.TYPE_CHECKING:
|
| 14 |
+
from .core import Command
|
| 15 |
+
from .core import Context
|
| 16 |
+
from .core import Parameter
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None:
|
| 20 |
+
if param_hint is not None and not isinstance(param_hint, str):
|
| 21 |
+
return " / ".join(repr(x) for x in param_hint)
|
| 22 |
+
|
| 23 |
+
return param_hint
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def _format_possibilities(possibilities: list[str]) -> str:
|
| 27 |
+
possibility_str = ", ".join(repr(p) for p in sorted(possibilities))
|
| 28 |
+
return ngettext(
|
| 29 |
+
"Did you mean {possibility}?",
|
| 30 |
+
"(Did you mean one of: {possibilities}?)",
|
| 31 |
+
len(possibilities),
|
| 32 |
+
).format(possibility=possibility_str, possibilities=possibility_str)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class ClickException(Exception):
|
| 36 |
+
"""An exception that Click can handle and show to the user."""
|
| 37 |
+
|
| 38 |
+
#: The exit code for this exception.
|
| 39 |
+
exit_code: t.ClassVar[int] = 1
|
| 40 |
+
|
| 41 |
+
show_color: t.Final[bool | None]
|
| 42 |
+
message: t.Final[str]
|
| 43 |
+
|
| 44 |
+
def __init__(self, message: str) -> None:
|
| 45 |
+
super().__init__(message)
|
| 46 |
+
# The context will be removed by the time we print the message, so cache
|
| 47 |
+
# the color settings here to be used later on (in `show`)
|
| 48 |
+
self.show_color = resolve_color_default()
|
| 49 |
+
self.message = message
|
| 50 |
+
|
| 51 |
+
def format_message(self) -> str:
|
| 52 |
+
return self.message
|
| 53 |
+
|
| 54 |
+
def __str__(self) -> str:
|
| 55 |
+
return self.message
|
| 56 |
+
|
| 57 |
+
def show(self, file: t.IO[t.Any] | None = None) -> None:
|
| 58 |
+
if file is None:
|
| 59 |
+
file = get_text_stderr()
|
| 60 |
+
|
| 61 |
+
echo(
|
| 62 |
+
_("Error: {message}").format(message=self.format_message()),
|
| 63 |
+
file=file,
|
| 64 |
+
color=self.show_color,
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class UsageError(ClickException):
|
| 69 |
+
"""An internal exception that signals a usage error. This typically
|
| 70 |
+
aborts any further handling.
|
| 71 |
+
|
| 72 |
+
:param message: the error message to display.
|
| 73 |
+
:param ctx: optionally the context that caused this error. Click will
|
| 74 |
+
fill in the context automatically in some situations.
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
exit_code: t.ClassVar[int] = 2
|
| 78 |
+
|
| 79 |
+
ctx: Context | None
|
| 80 |
+
cmd: t.Final[Command | None]
|
| 81 |
+
|
| 82 |
+
def __init__(self, message: str, ctx: Context | None = None) -> None:
|
| 83 |
+
super().__init__(message)
|
| 84 |
+
self.ctx = ctx
|
| 85 |
+
self.cmd = self.ctx.command if self.ctx else None
|
| 86 |
+
|
| 87 |
+
def show(self, file: t.IO[t.Any] | None = None) -> None:
|
| 88 |
+
if file is None:
|
| 89 |
+
file = get_text_stderr()
|
| 90 |
+
color = None
|
| 91 |
+
hint = ""
|
| 92 |
+
if (
|
| 93 |
+
self.ctx is not None
|
| 94 |
+
and self.ctx.command.get_help_option(self.ctx) is not None
|
| 95 |
+
):
|
| 96 |
+
help_names = self.ctx.command.get_help_option_names(self.ctx)
|
| 97 |
+
# Pick the longest name (like ``--help`` over ``-h``) for
|
| 98 |
+
# readability in error messages.
|
| 99 |
+
hint = _("Try '{command} {option}' for help.").format(
|
| 100 |
+
command=self.ctx.command_path,
|
| 101 |
+
option=max(help_names, key=len),
|
| 102 |
+
)
|
| 103 |
+
hint = f"{hint}\n"
|
| 104 |
+
if self.ctx is not None:
|
| 105 |
+
color = self.ctx.color
|
| 106 |
+
echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color)
|
| 107 |
+
echo(
|
| 108 |
+
_("Error: {message}").format(message=self.format_message()),
|
| 109 |
+
file=file,
|
| 110 |
+
color=color,
|
| 111 |
+
)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
class BadParameter(UsageError):
|
| 115 |
+
"""An exception that formats out a standardized error message for a
|
| 116 |
+
bad parameter. This is useful when thrown from a callback or type as
|
| 117 |
+
Click will attach contextual information to it (for instance, which
|
| 118 |
+
parameter it is).
|
| 119 |
+
|
| 120 |
+
.. versionadded:: 2.0
|
| 121 |
+
|
| 122 |
+
:param param: the parameter object that caused this error. This can
|
| 123 |
+
be left out, and Click will attach this info itself
|
| 124 |
+
if possible.
|
| 125 |
+
:param param_hint: a string that shows up as parameter name. This
|
| 126 |
+
can be used as alternative to `param` in cases
|
| 127 |
+
where custom validation should happen. If it is
|
| 128 |
+
a string it's used as such, if it's a list then
|
| 129 |
+
each item is quoted and separated.
|
| 130 |
+
"""
|
| 131 |
+
|
| 132 |
+
param: Parameter | None
|
| 133 |
+
param_hint: cabc.Sequence[str] | str | None
|
| 134 |
+
|
| 135 |
+
def __init__(
|
| 136 |
+
self,
|
| 137 |
+
message: str,
|
| 138 |
+
ctx: Context | None = None,
|
| 139 |
+
param: Parameter | None = None,
|
| 140 |
+
param_hint: cabc.Sequence[str] | str | None = None,
|
| 141 |
+
) -> None:
|
| 142 |
+
super().__init__(message, ctx)
|
| 143 |
+
self.param = param
|
| 144 |
+
self.param_hint = param_hint
|
| 145 |
+
|
| 146 |
+
def format_message(self) -> str:
|
| 147 |
+
if self.param_hint is not None:
|
| 148 |
+
param_hint = self.param_hint
|
| 149 |
+
elif self.param is not None:
|
| 150 |
+
param_hint = self.param.get_error_hint(self.ctx)
|
| 151 |
+
else:
|
| 152 |
+
return _("Invalid value: {message}").format(message=self.message)
|
| 153 |
+
|
| 154 |
+
return _("Invalid value for {param_hint}: {message}").format(
|
| 155 |
+
param_hint=_join_param_hints(param_hint), message=self.message
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
class MissingParameter(BadParameter):
|
| 160 |
+
"""Raised if click required an option or argument but it was not
|
| 161 |
+
provided when invoking the script.
|
| 162 |
+
|
| 163 |
+
.. versionadded:: 4.0
|
| 164 |
+
|
| 165 |
+
:param param_type: a string that indicates the type of the parameter.
|
| 166 |
+
The default is to inherit the parameter type from
|
| 167 |
+
the given `param`. Valid values are ``'parameter'``,
|
| 168 |
+
``'option'`` or ``'argument'``.
|
| 169 |
+
"""
|
| 170 |
+
|
| 171 |
+
param_type: t.Final[str | None]
|
| 172 |
+
|
| 173 |
+
def __init__(
|
| 174 |
+
self,
|
| 175 |
+
message: str | None = None,
|
| 176 |
+
ctx: Context | None = None,
|
| 177 |
+
param: Parameter | None = None,
|
| 178 |
+
param_hint: cabc.Sequence[str] | str | None = None,
|
| 179 |
+
param_type: str | None = None,
|
| 180 |
+
) -> None:
|
| 181 |
+
super().__init__(message or "", ctx, param, param_hint)
|
| 182 |
+
self.param_type = param_type
|
| 183 |
+
|
| 184 |
+
def format_message(self) -> str:
|
| 185 |
+
if self.param_hint is not None:
|
| 186 |
+
param_hint: cabc.Sequence[str] | str | None = self.param_hint
|
| 187 |
+
elif self.param is not None:
|
| 188 |
+
param_hint = self.param.get_error_hint(self.ctx)
|
| 189 |
+
else:
|
| 190 |
+
param_hint = None
|
| 191 |
+
|
| 192 |
+
param_hint = _join_param_hints(param_hint)
|
| 193 |
+
param_hint = f" {param_hint}" if param_hint else ""
|
| 194 |
+
|
| 195 |
+
param_type = self.param_type
|
| 196 |
+
if param_type is None and self.param is not None:
|
| 197 |
+
param_type = self.param.param_type_name
|
| 198 |
+
|
| 199 |
+
msg = self.message
|
| 200 |
+
if self.param is not None:
|
| 201 |
+
msg_extra = self.param.type.get_missing_message(
|
| 202 |
+
param=self.param, ctx=self.ctx
|
| 203 |
+
)
|
| 204 |
+
if msg_extra:
|
| 205 |
+
if msg:
|
| 206 |
+
msg += f". {msg_extra}"
|
| 207 |
+
else:
|
| 208 |
+
msg = msg_extra
|
| 209 |
+
|
| 210 |
+
msg = f" {msg}" if msg else ""
|
| 211 |
+
|
| 212 |
+
# Translate param_type for known types.
|
| 213 |
+
if param_type == "argument":
|
| 214 |
+
missing = _("Missing argument")
|
| 215 |
+
elif param_type == "option":
|
| 216 |
+
missing = _("Missing option")
|
| 217 |
+
elif param_type == "parameter":
|
| 218 |
+
missing = _("Missing parameter")
|
| 219 |
+
else:
|
| 220 |
+
missing = _("Missing {param_type}").format(param_type=param_type)
|
| 221 |
+
|
| 222 |
+
return f"{missing}{param_hint}.{msg}"
|
| 223 |
+
|
| 224 |
+
def __str__(self) -> str:
|
| 225 |
+
if not self.message:
|
| 226 |
+
param_name = self.param.name if self.param else None
|
| 227 |
+
return _("Missing parameter: {param_name}").format(param_name=param_name)
|
| 228 |
+
else:
|
| 229 |
+
return self.message
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
class NoSuchOption(UsageError):
|
| 233 |
+
"""Raised if Click attempted to handle an option that does not exist.
|
| 234 |
+
|
| 235 |
+
.. versionadded:: 4.0
|
| 236 |
+
"""
|
| 237 |
+
|
| 238 |
+
option_name: t.Final[str]
|
| 239 |
+
possibilities: t.Final[list[str] | None]
|
| 240 |
+
|
| 241 |
+
def __init__(
|
| 242 |
+
self,
|
| 243 |
+
option_name: str,
|
| 244 |
+
message: str | None = None,
|
| 245 |
+
possibilities: cabc.Iterable[str] | None = None,
|
| 246 |
+
ctx: Context | None = None,
|
| 247 |
+
) -> None:
|
| 248 |
+
if message is None:
|
| 249 |
+
message = _("No such option {name!r}.").format(name=option_name)
|
| 250 |
+
|
| 251 |
+
super().__init__(message, ctx)
|
| 252 |
+
self.option_name = option_name
|
| 253 |
+
|
| 254 |
+
if possibilities:
|
| 255 |
+
from difflib import get_close_matches
|
| 256 |
+
|
| 257 |
+
possibilities_ = get_close_matches(option_name, possibilities)
|
| 258 |
+
else:
|
| 259 |
+
possibilities_ = None
|
| 260 |
+
self.possibilities = possibilities_
|
| 261 |
+
|
| 262 |
+
def format_message(self) -> str:
|
| 263 |
+
if not self.possibilities:
|
| 264 |
+
return self.message
|
| 265 |
+
return f"{self.message} {_format_possibilities(self.possibilities)}"
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
class NoSuchCommand(UsageError):
|
| 269 |
+
"""Raised if Click attempted to handle a command that does not exist.
|
| 270 |
+
|
| 271 |
+
.. versionadded:: 8.4.0
|
| 272 |
+
"""
|
| 273 |
+
|
| 274 |
+
command_name: t.Final[str]
|
| 275 |
+
possibilities: t.Final[list[str] | None]
|
| 276 |
+
|
| 277 |
+
def __init__(
|
| 278 |
+
self,
|
| 279 |
+
command_name: str,
|
| 280 |
+
message: str | None = None,
|
| 281 |
+
possibilities: cabc.Iterable[str] | None = None,
|
| 282 |
+
ctx: Context | None = None,
|
| 283 |
+
) -> None:
|
| 284 |
+
if message is None:
|
| 285 |
+
message = _("No such command {name!r}.").format(name=command_name)
|
| 286 |
+
|
| 287 |
+
super().__init__(message, ctx)
|
| 288 |
+
self.command_name = command_name
|
| 289 |
+
|
| 290 |
+
if possibilities:
|
| 291 |
+
from difflib import get_close_matches
|
| 292 |
+
|
| 293 |
+
possibilities_ = get_close_matches(command_name, possibilities)
|
| 294 |
+
else:
|
| 295 |
+
possibilities_ = None
|
| 296 |
+
self.possibilities = possibilities_
|
| 297 |
+
|
| 298 |
+
def format_message(self) -> str:
|
| 299 |
+
if not self.possibilities:
|
| 300 |
+
return self.message
|
| 301 |
+
return f"{self.message} {_format_possibilities(self.possibilities)}"
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
class BadOptionUsage(UsageError):
|
| 305 |
+
"""Raised if an option is generally supplied but the use of the option
|
| 306 |
+
was incorrect. This is for instance raised if the number of arguments
|
| 307 |
+
for an option is not correct.
|
| 308 |
+
|
| 309 |
+
.. versionadded:: 4.0
|
| 310 |
+
|
| 311 |
+
:param option_name: the name of the option being used incorrectly.
|
| 312 |
+
"""
|
| 313 |
+
|
| 314 |
+
option_name: t.Final[str]
|
| 315 |
+
|
| 316 |
+
def __init__(
|
| 317 |
+
self, option_name: str, message: str, ctx: Context | None = None
|
| 318 |
+
) -> None:
|
| 319 |
+
super().__init__(message, ctx)
|
| 320 |
+
self.option_name = option_name
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
class BadArgumentUsage(UsageError):
|
| 324 |
+
"""Raised if an argument is generally supplied but the use of the argument
|
| 325 |
+
was incorrect. This is for instance raised if the number of values
|
| 326 |
+
for an argument is not correct.
|
| 327 |
+
|
| 328 |
+
.. versionadded:: 6.0
|
| 329 |
+
"""
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
class NoArgsIsHelpError(UsageError):
|
| 333 |
+
ctx: Context
|
| 334 |
+
|
| 335 |
+
def __init__(self, ctx: Context) -> None:
|
| 336 |
+
super().__init__(ctx.get_help(), ctx=ctx)
|
| 337 |
+
|
| 338 |
+
def show(self, file: t.IO[t.Any] | None = None) -> None:
|
| 339 |
+
echo(self.format_message(), file=file, err=True, color=self.ctx.color)
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
class FileError(ClickException):
|
| 343 |
+
"""Raised if a file cannot be opened."""
|
| 344 |
+
|
| 345 |
+
ui_filename: t.Final[str]
|
| 346 |
+
filename: t.Final[str]
|
| 347 |
+
|
| 348 |
+
def __init__(self, filename: str, hint: str | None = None) -> None:
|
| 349 |
+
if hint is None:
|
| 350 |
+
hint = _("unknown error")
|
| 351 |
+
|
| 352 |
+
super().__init__(hint)
|
| 353 |
+
self.ui_filename = format_filename(filename)
|
| 354 |
+
self.filename = filename
|
| 355 |
+
|
| 356 |
+
def format_message(self) -> str:
|
| 357 |
+
return _("Could not open file {filename!r}: {message}").format(
|
| 358 |
+
filename=self.ui_filename, message=self.message
|
| 359 |
+
)
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
class Abort(RuntimeError):
|
| 363 |
+
"""An internal signalling exception that signals Click to abort."""
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
class Exit(RuntimeError):
|
| 367 |
+
"""An exception that indicates that the application should exit with some
|
| 368 |
+
status code.
|
| 369 |
+
|
| 370 |
+
:param code: the status code to exit with.
|
| 371 |
+
"""
|
| 372 |
+
|
| 373 |
+
__slots__ = ("exit_code",)
|
| 374 |
+
|
| 375 |
+
exit_code: t.Final[int]
|
| 376 |
+
|
| 377 |
+
def __init__(self, code: int = 0) -> None:
|
| 378 |
+
self.exit_code = code
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/formatting.py
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections.abc as cabc
|
| 4 |
+
from contextlib import contextmanager
|
| 5 |
+
from gettext import gettext as _
|
| 6 |
+
|
| 7 |
+
from ._compat import term_len
|
| 8 |
+
from .parser import _split_opt
|
| 9 |
+
|
| 10 |
+
# Can force a width. This is used by the test system
|
| 11 |
+
FORCED_WIDTH: int | None = None
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]:
|
| 15 |
+
widths: dict[int, int] = {}
|
| 16 |
+
|
| 17 |
+
for row in rows:
|
| 18 |
+
for idx, col in enumerate(row):
|
| 19 |
+
widths[idx] = max(widths.get(idx, 0), term_len(col))
|
| 20 |
+
|
| 21 |
+
return tuple(y for x, y in sorted(widths.items()))
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def iter_rows(
|
| 25 |
+
rows: cabc.Iterable[tuple[str, str]], col_count: int
|
| 26 |
+
) -> cabc.Iterator[tuple[str, ...]]:
|
| 27 |
+
for row in rows:
|
| 28 |
+
yield row + ("",) * (col_count - len(row))
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def wrap_text(
|
| 32 |
+
text: str,
|
| 33 |
+
width: int = 78,
|
| 34 |
+
initial_indent: str = "",
|
| 35 |
+
subsequent_indent: str = "",
|
| 36 |
+
preserve_paragraphs: bool = False,
|
| 37 |
+
) -> str:
|
| 38 |
+
"""A helper function that intelligently wraps text. By default, it
|
| 39 |
+
assumes that it operates on a single paragraph of text but if the
|
| 40 |
+
`preserve_paragraphs` parameter is provided it will intelligently
|
| 41 |
+
handle paragraphs (defined by two empty lines).
|
| 42 |
+
|
| 43 |
+
If paragraphs are handled, a paragraph can be prefixed with an empty
|
| 44 |
+
line containing the ``\\b`` character (``\\x08``) to indicate that
|
| 45 |
+
no rewrapping should happen in that block.
|
| 46 |
+
|
| 47 |
+
:param text: the text that should be rewrapped.
|
| 48 |
+
:param width: the maximum width for the text.
|
| 49 |
+
:param initial_indent: the initial indent that should be placed on the
|
| 50 |
+
first line as a string.
|
| 51 |
+
:param subsequent_indent: the indent string that should be placed on
|
| 52 |
+
each consecutive line.
|
| 53 |
+
:param preserve_paragraphs: if this flag is set then the wrapping will
|
| 54 |
+
intelligently handle paragraphs.
|
| 55 |
+
|
| 56 |
+
.. versionchanged:: 8.4.0
|
| 57 |
+
Width is measured in visible characters. ANSI escape sequences in
|
| 58 |
+
``text``, ``initial_indent``, or ``subsequent_indent`` no longer
|
| 59 |
+
count toward the width budget, so styled input wraps based on what
|
| 60 |
+
the user sees instead of raw byte length.
|
| 61 |
+
"""
|
| 62 |
+
from ._textwrap import TextWrapper
|
| 63 |
+
|
| 64 |
+
text = text.expandtabs()
|
| 65 |
+
wrapper = TextWrapper(
|
| 66 |
+
width,
|
| 67 |
+
initial_indent=initial_indent,
|
| 68 |
+
subsequent_indent=subsequent_indent,
|
| 69 |
+
replace_whitespace=False,
|
| 70 |
+
)
|
| 71 |
+
if not preserve_paragraphs:
|
| 72 |
+
return wrapper.fill(text)
|
| 73 |
+
|
| 74 |
+
p: list[tuple[int, bool, str]] = []
|
| 75 |
+
buf: list[str] = []
|
| 76 |
+
indent = None
|
| 77 |
+
|
| 78 |
+
def _flush_par() -> None:
|
| 79 |
+
if not buf:
|
| 80 |
+
return
|
| 81 |
+
if buf[0].strip() == "\b":
|
| 82 |
+
p.append((indent or 0, True, "\n".join(buf[1:])))
|
| 83 |
+
else:
|
| 84 |
+
p.append((indent or 0, False, " ".join(buf)))
|
| 85 |
+
del buf[:]
|
| 86 |
+
|
| 87 |
+
for line in text.splitlines():
|
| 88 |
+
if not line:
|
| 89 |
+
_flush_par()
|
| 90 |
+
indent = None
|
| 91 |
+
else:
|
| 92 |
+
if indent is None:
|
| 93 |
+
orig_len = term_len(line)
|
| 94 |
+
line = line.lstrip()
|
| 95 |
+
indent = orig_len - term_len(line)
|
| 96 |
+
buf.append(line)
|
| 97 |
+
_flush_par()
|
| 98 |
+
|
| 99 |
+
rv = []
|
| 100 |
+
for indent, raw, text in p:
|
| 101 |
+
with wrapper.extra_indent(" " * indent):
|
| 102 |
+
if raw:
|
| 103 |
+
rv.append(wrapper.indent_only(text))
|
| 104 |
+
else:
|
| 105 |
+
rv.append(wrapper.fill(text))
|
| 106 |
+
|
| 107 |
+
return "\n\n".join(rv)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
class HelpFormatter:
|
| 111 |
+
"""This class helps with formatting text-based help pages. It's
|
| 112 |
+
usually just needed for very special internal cases, but it's also
|
| 113 |
+
exposed so that developers can write their own fancy outputs.
|
| 114 |
+
|
| 115 |
+
At present, it always writes into memory.
|
| 116 |
+
|
| 117 |
+
:param indent_increment: the additional increment for each level.
|
| 118 |
+
:param width: the width for the text. This defaults to the terminal
|
| 119 |
+
width clamped to a maximum of 78.
|
| 120 |
+
"""
|
| 121 |
+
|
| 122 |
+
indent_increment: int
|
| 123 |
+
width: int
|
| 124 |
+
current_indent: int
|
| 125 |
+
buffer: list[str]
|
| 126 |
+
|
| 127 |
+
def __init__(
|
| 128 |
+
self,
|
| 129 |
+
indent_increment: int = 2,
|
| 130 |
+
width: int | None = None,
|
| 131 |
+
max_width: int | None = None,
|
| 132 |
+
) -> None:
|
| 133 |
+
self.indent_increment = indent_increment
|
| 134 |
+
if max_width is None:
|
| 135 |
+
max_width = 80
|
| 136 |
+
if width is None:
|
| 137 |
+
import shutil
|
| 138 |
+
|
| 139 |
+
width = FORCED_WIDTH
|
| 140 |
+
if width is None:
|
| 141 |
+
width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50)
|
| 142 |
+
self.width = width
|
| 143 |
+
self.current_indent = 0
|
| 144 |
+
self.buffer = []
|
| 145 |
+
|
| 146 |
+
def write(self, string: str) -> None:
|
| 147 |
+
"""Writes a unicode string into the internal buffer."""
|
| 148 |
+
self.buffer.append(string)
|
| 149 |
+
|
| 150 |
+
def indent(self) -> None:
|
| 151 |
+
"""Increases the indentation."""
|
| 152 |
+
self.current_indent += self.indent_increment
|
| 153 |
+
|
| 154 |
+
def dedent(self) -> None:
|
| 155 |
+
"""Decreases the indentation."""
|
| 156 |
+
self.current_indent -= self.indent_increment
|
| 157 |
+
|
| 158 |
+
def write_usage(self, prog: str, args: str = "", prefix: str | None = None) -> None:
|
| 159 |
+
"""Writes a usage line into the buffer.
|
| 160 |
+
|
| 161 |
+
:param prog: the program name.
|
| 162 |
+
:param args: whitespace separated list of arguments.
|
| 163 |
+
:param prefix: The prefix for the first line. Defaults to
|
| 164 |
+
``"Usage: "``.
|
| 165 |
+
"""
|
| 166 |
+
if prefix is None:
|
| 167 |
+
prefix = "{usage} ".format(usage=_("Usage:"))
|
| 168 |
+
|
| 169 |
+
usage_prefix = f"{prefix:>{self.current_indent}}{prog} "
|
| 170 |
+
text_width = self.width - self.current_indent
|
| 171 |
+
|
| 172 |
+
if not args:
|
| 173 |
+
# Without args, the prefix's trailing space and the wrap_text
|
| 174 |
+
# call that would normally place args on the line are both
|
| 175 |
+
# unnecessary. Emit just the prefix line.
|
| 176 |
+
self.write(usage_prefix.rstrip(" "))
|
| 177 |
+
self.write("\n")
|
| 178 |
+
return
|
| 179 |
+
|
| 180 |
+
if text_width >= (term_len(usage_prefix) + 20):
|
| 181 |
+
# The arguments will fit to the right of the prefix.
|
| 182 |
+
indent = " " * term_len(usage_prefix)
|
| 183 |
+
self.write(
|
| 184 |
+
wrap_text(
|
| 185 |
+
args,
|
| 186 |
+
text_width,
|
| 187 |
+
initial_indent=usage_prefix,
|
| 188 |
+
subsequent_indent=indent,
|
| 189 |
+
)
|
| 190 |
+
)
|
| 191 |
+
else:
|
| 192 |
+
# The prefix is too long, put the arguments on the next line.
|
| 193 |
+
self.write(usage_prefix)
|
| 194 |
+
self.write("\n")
|
| 195 |
+
indent = " " * (max(self.current_indent, term_len(prefix)) + 4)
|
| 196 |
+
self.write(
|
| 197 |
+
wrap_text(
|
| 198 |
+
args, text_width, initial_indent=indent, subsequent_indent=indent
|
| 199 |
+
)
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
self.write("\n")
|
| 203 |
+
|
| 204 |
+
def write_heading(self, heading: str) -> None:
|
| 205 |
+
"""Writes a heading into the buffer."""
|
| 206 |
+
self.write(f"{'':>{self.current_indent}}{heading}:\n")
|
| 207 |
+
|
| 208 |
+
def write_paragraph(self) -> None:
|
| 209 |
+
"""Writes a paragraph into the buffer."""
|
| 210 |
+
if self.buffer:
|
| 211 |
+
self.write("\n")
|
| 212 |
+
|
| 213 |
+
def write_text(self, text: str) -> None:
|
| 214 |
+
"""Writes re-indented text into the buffer. This rewraps and
|
| 215 |
+
preserves paragraphs.
|
| 216 |
+
"""
|
| 217 |
+
indent = " " * self.current_indent
|
| 218 |
+
self.write(
|
| 219 |
+
wrap_text(
|
| 220 |
+
text,
|
| 221 |
+
self.width,
|
| 222 |
+
initial_indent=indent,
|
| 223 |
+
subsequent_indent=indent,
|
| 224 |
+
preserve_paragraphs=True,
|
| 225 |
+
)
|
| 226 |
+
)
|
| 227 |
+
self.write("\n")
|
| 228 |
+
|
| 229 |
+
def write_dl(
|
| 230 |
+
self,
|
| 231 |
+
rows: cabc.Iterable[tuple[str, str]],
|
| 232 |
+
col_max: int = 30,
|
| 233 |
+
col_spacing: int = 2,
|
| 234 |
+
) -> None:
|
| 235 |
+
"""Writes a definition list into the buffer. This is how options
|
| 236 |
+
and commands are usually formatted.
|
| 237 |
+
|
| 238 |
+
:param rows: a list of two item tuples for the terms and values.
|
| 239 |
+
:param col_max: the maximum width of the first column.
|
| 240 |
+
:param col_spacing: the number of spaces between the first and
|
| 241 |
+
second column.
|
| 242 |
+
"""
|
| 243 |
+
rows = list(rows)
|
| 244 |
+
widths = measure_table(rows)
|
| 245 |
+
if len(widths) != 2:
|
| 246 |
+
raise TypeError("Expected two columns for definition list")
|
| 247 |
+
|
| 248 |
+
first_col = min(widths[0], col_max) + col_spacing
|
| 249 |
+
|
| 250 |
+
for first, second in iter_rows(rows, len(widths)):
|
| 251 |
+
self.write(f"{'':>{self.current_indent}}{first}")
|
| 252 |
+
if not second:
|
| 253 |
+
self.write("\n")
|
| 254 |
+
continue
|
| 255 |
+
if term_len(first) <= first_col - col_spacing:
|
| 256 |
+
self.write(" " * (first_col - term_len(first)))
|
| 257 |
+
else:
|
| 258 |
+
self.write("\n")
|
| 259 |
+
self.write(" " * (first_col + self.current_indent))
|
| 260 |
+
|
| 261 |
+
text_width = max(self.width - first_col - 2, 10)
|
| 262 |
+
wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True)
|
| 263 |
+
lines = wrapped_text.splitlines()
|
| 264 |
+
|
| 265 |
+
if lines:
|
| 266 |
+
self.write(f"{lines[0]}\n")
|
| 267 |
+
|
| 268 |
+
for line in lines[1:]:
|
| 269 |
+
self.write(f"{'':>{first_col + self.current_indent}}{line}\n")
|
| 270 |
+
else:
|
| 271 |
+
self.write("\n")
|
| 272 |
+
|
| 273 |
+
@contextmanager
|
| 274 |
+
def section(self, name: str) -> cabc.Generator[None]:
|
| 275 |
+
"""Helpful context manager that writes a paragraph, a heading,
|
| 276 |
+
and the indents.
|
| 277 |
+
|
| 278 |
+
:param name: the section name that is written as heading.
|
| 279 |
+
"""
|
| 280 |
+
self.write_paragraph()
|
| 281 |
+
self.write_heading(name)
|
| 282 |
+
self.indent()
|
| 283 |
+
try:
|
| 284 |
+
yield
|
| 285 |
+
finally:
|
| 286 |
+
self.dedent()
|
| 287 |
+
|
| 288 |
+
@contextmanager
|
| 289 |
+
def indentation(self) -> cabc.Generator[None]:
|
| 290 |
+
"""A context manager that increases the indentation."""
|
| 291 |
+
self.indent()
|
| 292 |
+
try:
|
| 293 |
+
yield
|
| 294 |
+
finally:
|
| 295 |
+
self.dedent()
|
| 296 |
+
|
| 297 |
+
def getvalue(self) -> str:
|
| 298 |
+
"""Returns the buffer contents."""
|
| 299 |
+
return "".join(self.buffer)
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
def join_options(options: cabc.Iterable[str]) -> tuple[str, bool]:
|
| 303 |
+
"""Given a list of option strings this joins them in the most appropriate
|
| 304 |
+
way and returns them in the form ``(formatted_string,
|
| 305 |
+
any_prefix_is_slash)`` where the second item in the tuple is a flag that
|
| 306 |
+
indicates if any of the option prefixes was a slash.
|
| 307 |
+
"""
|
| 308 |
+
rv = []
|
| 309 |
+
any_prefix_is_slash = False
|
| 310 |
+
|
| 311 |
+
for opt in options:
|
| 312 |
+
prefix = _split_opt(opt)[0]
|
| 313 |
+
|
| 314 |
+
if prefix == "/":
|
| 315 |
+
any_prefix_is_slash = True
|
| 316 |
+
|
| 317 |
+
rv.append((len(prefix), opt))
|
| 318 |
+
|
| 319 |
+
rv.sort(key=lambda x: x[0])
|
| 320 |
+
return ", ".join(x[1] for x in rv), any_prefix_is_slash
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/globals.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import typing as t
|
| 4 |
+
from threading import local
|
| 5 |
+
|
| 6 |
+
if t.TYPE_CHECKING:
|
| 7 |
+
from .core import Context
|
| 8 |
+
|
| 9 |
+
_local = local()
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@t.overload
|
| 13 |
+
def get_current_context(silent: t.Literal[False] = False) -> Context: ...
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@t.overload
|
| 17 |
+
def get_current_context(silent: bool = ...) -> Context | None: ...
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def get_current_context(silent: bool = False) -> Context | None:
|
| 21 |
+
"""Returns the current click context. This can be used as a way to
|
| 22 |
+
access the current context object from anywhere. This is a more implicit
|
| 23 |
+
alternative to the :func:`pass_context` decorator. This function is
|
| 24 |
+
primarily useful for helpers such as :func:`echo` which might be
|
| 25 |
+
interested in changing its behavior based on the current context.
|
| 26 |
+
|
| 27 |
+
To push the current context, :meth:`Context.scope` can be used.
|
| 28 |
+
|
| 29 |
+
.. versionadded:: 5.0
|
| 30 |
+
|
| 31 |
+
:param silent: if set to `True` the return value is `None` if no context
|
| 32 |
+
is available. The default behavior is to raise a
|
| 33 |
+
:exc:`RuntimeError`.
|
| 34 |
+
"""
|
| 35 |
+
try:
|
| 36 |
+
return t.cast("Context", _local.stack[-1])
|
| 37 |
+
except (AttributeError, IndexError) as e:
|
| 38 |
+
if not silent:
|
| 39 |
+
raise RuntimeError("There is no active click context.") from e
|
| 40 |
+
|
| 41 |
+
return None
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def push_context(ctx: Context) -> None:
|
| 45 |
+
"""Pushes a new context to the current stack."""
|
| 46 |
+
_local.__dict__.setdefault("stack", []).append(ctx)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def pop_context() -> None:
|
| 50 |
+
"""Removes the top level from the stack."""
|
| 51 |
+
_local.stack.pop()
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def resolve_color_default(color: bool | None = None) -> bool | None:
|
| 55 |
+
"""Internal helper to get the default value of the color flag. If a
|
| 56 |
+
value is passed it's returned unchanged, otherwise it's looked up from
|
| 57 |
+
the current context.
|
| 58 |
+
"""
|
| 59 |
+
if color is not None:
|
| 60 |
+
return color
|
| 61 |
+
|
| 62 |
+
ctx = get_current_context(silent=True)
|
| 63 |
+
|
| 64 |
+
if ctx is not None:
|
| 65 |
+
return ctx.color
|
| 66 |
+
|
| 67 |
+
return None
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/parser.py
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This module started out as largely a copy paste from the stdlib's
|
| 3 |
+
optparse module with the features removed that we do not need from
|
| 4 |
+
optparse because we implement them in Click on a higher level (for
|
| 5 |
+
instance type handling, help formatting and a lot more).
|
| 6 |
+
|
| 7 |
+
The plan is to remove more and more from here over time.
|
| 8 |
+
|
| 9 |
+
The reason this is a different module and not optparse from the stdlib
|
| 10 |
+
is that there are differences in 2.x and 3.x about the error messages
|
| 11 |
+
generated and optparse in the stdlib uses gettext for no good reason
|
| 12 |
+
and might cause us issues.
|
| 13 |
+
|
| 14 |
+
Click uses parts of optparse written by Gregory P. Ward and maintained
|
| 15 |
+
by the Python Software Foundation. This is limited to code in parser.py.
|
| 16 |
+
|
| 17 |
+
Copyright 2001-2006 Gregory P. Ward. All rights reserved.
|
| 18 |
+
Copyright 2002-2006 Python Software Foundation. All rights reserved.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
# This code uses parts of optparse written by Gregory P. Ward and
|
| 22 |
+
# maintained by the Python Software Foundation.
|
| 23 |
+
# Copyright 2001-2006 Gregory P. Ward
|
| 24 |
+
# Copyright 2002-2006 Python Software Foundation
|
| 25 |
+
from __future__ import annotations
|
| 26 |
+
|
| 27 |
+
import collections.abc as cabc
|
| 28 |
+
import typing as t
|
| 29 |
+
from collections import deque
|
| 30 |
+
from gettext import gettext as _
|
| 31 |
+
from gettext import ngettext
|
| 32 |
+
|
| 33 |
+
from ._utils import FLAG_NEEDS_VALUE
|
| 34 |
+
from ._utils import UNSET
|
| 35 |
+
from .exceptions import BadArgumentUsage
|
| 36 |
+
from .exceptions import BadOptionUsage
|
| 37 |
+
from .exceptions import NoSuchOption
|
| 38 |
+
from .exceptions import UsageError
|
| 39 |
+
|
| 40 |
+
if t.TYPE_CHECKING:
|
| 41 |
+
from ._utils import T_FLAG_NEEDS_VALUE
|
| 42 |
+
from ._utils import T_UNSET
|
| 43 |
+
from .core import Argument as CoreArgument
|
| 44 |
+
from .core import Context
|
| 45 |
+
from .core import Option as CoreOption
|
| 46 |
+
from .core import Parameter as CoreParameter
|
| 47 |
+
|
| 48 |
+
V = t.TypeVar("V")
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _unpack_args(
|
| 52 |
+
args: cabc.Sequence[str], nargs_spec: cabc.Sequence[int]
|
| 53 |
+
) -> tuple[cabc.Sequence[str | cabc.Sequence[str | T_UNSET] | T_UNSET], list[str]]:
|
| 54 |
+
"""Given an iterable of arguments and an iterable of nargs specifications,
|
| 55 |
+
it returns a tuple with all the unpacked arguments at the first index
|
| 56 |
+
and all remaining arguments as the second.
|
| 57 |
+
|
| 58 |
+
The nargs specification is the number of arguments that should be consumed
|
| 59 |
+
or `-1` to indicate that this position should eat up all the remainders.
|
| 60 |
+
|
| 61 |
+
Missing items are filled with ``UNSET``.
|
| 62 |
+
"""
|
| 63 |
+
args = deque(args)
|
| 64 |
+
nargs_spec = deque(nargs_spec)
|
| 65 |
+
rv: list[str | tuple[str | T_UNSET, ...] | T_UNSET] = []
|
| 66 |
+
spos: int | None = None
|
| 67 |
+
|
| 68 |
+
def _fetch(c: deque[str]) -> str | T_UNSET:
|
| 69 |
+
try:
|
| 70 |
+
if spos is None:
|
| 71 |
+
return c.popleft()
|
| 72 |
+
else:
|
| 73 |
+
return c.pop()
|
| 74 |
+
except IndexError:
|
| 75 |
+
return UNSET
|
| 76 |
+
|
| 77 |
+
while nargs_spec:
|
| 78 |
+
if spos is None:
|
| 79 |
+
nargs = nargs_spec.popleft()
|
| 80 |
+
else:
|
| 81 |
+
nargs = nargs_spec.pop()
|
| 82 |
+
|
| 83 |
+
if nargs == 1:
|
| 84 |
+
rv.append(_fetch(args))
|
| 85 |
+
elif nargs > 1:
|
| 86 |
+
x: list[str | T_UNSET] = [_fetch(args) for _ in range(nargs)]
|
| 87 |
+
|
| 88 |
+
# If we're reversed, we're pulling in the arguments in reverse,
|
| 89 |
+
# so we need to turn them around.
|
| 90 |
+
if spos is not None:
|
| 91 |
+
x.reverse()
|
| 92 |
+
|
| 93 |
+
rv.append(tuple(x))
|
| 94 |
+
elif nargs < 0:
|
| 95 |
+
if spos is not None:
|
| 96 |
+
raise TypeError("Cannot have two nargs < 0")
|
| 97 |
+
|
| 98 |
+
spos = len(rv)
|
| 99 |
+
rv.append(UNSET)
|
| 100 |
+
|
| 101 |
+
# spos is the position of the wildcard (star). If it's not `None`,
|
| 102 |
+
# we fill it with the remainder.
|
| 103 |
+
if spos is not None:
|
| 104 |
+
rv[spos] = tuple(args)
|
| 105 |
+
args = []
|
| 106 |
+
rv[spos + 1 :] = reversed(rv[spos + 1 :])
|
| 107 |
+
|
| 108 |
+
return tuple(rv), list(args)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def _split_opt(opt: str) -> tuple[str, str]:
|
| 112 |
+
first = opt[:1]
|
| 113 |
+
if first.isalnum():
|
| 114 |
+
return "", opt
|
| 115 |
+
if opt[1:2] == first:
|
| 116 |
+
return opt[:2], opt[2:]
|
| 117 |
+
return first, opt[1:]
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def _normalize_opt(opt: str, ctx: Context | None) -> str:
|
| 121 |
+
if ctx is None or ctx.token_normalize_func is None:
|
| 122 |
+
return opt
|
| 123 |
+
prefix, opt = _split_opt(opt)
|
| 124 |
+
return f"{prefix}{ctx.token_normalize_func(opt)}"
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
class _Option:
|
| 128 |
+
def __init__(
|
| 129 |
+
self,
|
| 130 |
+
obj: CoreOption,
|
| 131 |
+
opts: cabc.Sequence[str],
|
| 132 |
+
dest: str | None,
|
| 133 |
+
action: str | None = None,
|
| 134 |
+
nargs: int = 1,
|
| 135 |
+
const: t.Any | None = None,
|
| 136 |
+
):
|
| 137 |
+
self._short_opts = []
|
| 138 |
+
self._long_opts = []
|
| 139 |
+
self.prefixes: set[str] = set()
|
| 140 |
+
|
| 141 |
+
for opt in opts:
|
| 142 |
+
prefix, value = _split_opt(opt)
|
| 143 |
+
if not prefix:
|
| 144 |
+
raise ValueError(
|
| 145 |
+
_("Invalid start character for option ({option})").format(
|
| 146 |
+
option=opt
|
| 147 |
+
)
|
| 148 |
+
)
|
| 149 |
+
self.prefixes.add(prefix[0])
|
| 150 |
+
if len(prefix) == 1 and len(value) == 1:
|
| 151 |
+
self._short_opts.append(opt)
|
| 152 |
+
else:
|
| 153 |
+
self._long_opts.append(opt)
|
| 154 |
+
self.prefixes.add(prefix)
|
| 155 |
+
|
| 156 |
+
if action is None:
|
| 157 |
+
action = "store"
|
| 158 |
+
|
| 159 |
+
self.dest = dest
|
| 160 |
+
self.action = action
|
| 161 |
+
self.nargs = nargs
|
| 162 |
+
self.const = const
|
| 163 |
+
self.obj = obj
|
| 164 |
+
|
| 165 |
+
@property
|
| 166 |
+
def takes_value(self) -> bool:
|
| 167 |
+
return self.action in ("store", "append")
|
| 168 |
+
|
| 169 |
+
def process(self, value: t.Any, state: _ParsingState) -> None:
|
| 170 |
+
if self.action == "store":
|
| 171 |
+
state.opts[self.dest] = value # type: ignore
|
| 172 |
+
elif self.action == "store_const":
|
| 173 |
+
state.opts[self.dest] = self.const # type: ignore
|
| 174 |
+
elif self.action == "append":
|
| 175 |
+
state.opts.setdefault(self.dest, []).append(value) # type: ignore
|
| 176 |
+
elif self.action == "append_const":
|
| 177 |
+
state.opts.setdefault(self.dest, []).append(self.const) # type: ignore
|
| 178 |
+
elif self.action == "count":
|
| 179 |
+
state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore
|
| 180 |
+
else:
|
| 181 |
+
raise ValueError(f"unknown action '{self.action}'")
|
| 182 |
+
state.order.append(self.obj)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
class _Argument:
|
| 186 |
+
def __init__(self, obj: CoreArgument, dest: str | None, nargs: int = 1):
|
| 187 |
+
self.dest = dest
|
| 188 |
+
self.nargs = nargs
|
| 189 |
+
self.obj = obj
|
| 190 |
+
|
| 191 |
+
def process(
|
| 192 |
+
self,
|
| 193 |
+
value: str | cabc.Sequence[str | T_UNSET] | T_UNSET,
|
| 194 |
+
state: _ParsingState,
|
| 195 |
+
) -> None:
|
| 196 |
+
if self.nargs > 1:
|
| 197 |
+
assert isinstance(value, cabc.Sequence)
|
| 198 |
+
holes = sum(x is UNSET for x in value)
|
| 199 |
+
if holes == len(value):
|
| 200 |
+
value = UNSET
|
| 201 |
+
elif holes != 0:
|
| 202 |
+
raise BadArgumentUsage(
|
| 203 |
+
_("Argument {name!r} takes {nargs} values.").format(
|
| 204 |
+
name=self.dest, nargs=self.nargs
|
| 205 |
+
)
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
# We failed to collect any argument value so we consider the argument as unset.
|
| 209 |
+
if value == ():
|
| 210 |
+
value = UNSET
|
| 211 |
+
|
| 212 |
+
state.opts[self.dest] = value # type: ignore
|
| 213 |
+
state.order.append(self.obj)
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
class _ParsingState:
|
| 217 |
+
def __init__(self, rargs: list[str]) -> None:
|
| 218 |
+
self.opts: dict[str, t.Any] = {}
|
| 219 |
+
self.largs: list[str] = []
|
| 220 |
+
self.rargs = rargs
|
| 221 |
+
self.order: list[CoreParameter] = []
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
class _OptionParser:
|
| 225 |
+
"""The option parser is an internal class that is ultimately used to
|
| 226 |
+
parse options and arguments. It's modelled after optparse and brings
|
| 227 |
+
a similar but vastly simplified API. It should generally not be used
|
| 228 |
+
directly as the high level Click classes wrap it for you.
|
| 229 |
+
|
| 230 |
+
It's not nearly as extensible as optparse or argparse as it does not
|
| 231 |
+
implement features that are implemented on a higher level (such as
|
| 232 |
+
types or defaults).
|
| 233 |
+
|
| 234 |
+
:param ctx: optionally the :class:`~click.Context` where this parser
|
| 235 |
+
should go with.
|
| 236 |
+
|
| 237 |
+
.. deprecated:: 8.2
|
| 238 |
+
Will be removed in Click 9.0.
|
| 239 |
+
"""
|
| 240 |
+
|
| 241 |
+
def __init__(self, ctx: Context | None = None) -> None:
|
| 242 |
+
#: The :class:`~click.Context` for this parser. This might be
|
| 243 |
+
#: `None` for some advanced use cases.
|
| 244 |
+
self.ctx = ctx
|
| 245 |
+
#: This controls how the parser deals with interspersed arguments.
|
| 246 |
+
#: If this is set to `False`, the parser will stop on the first
|
| 247 |
+
#: non-option. Click uses this to implement nested subcommands
|
| 248 |
+
#: safely.
|
| 249 |
+
self.allow_interspersed_args: bool = True
|
| 250 |
+
#: This tells the parser how to deal with unknown options. By
|
| 251 |
+
#: default it will error out (which is sensible), but there is a
|
| 252 |
+
#: second mode where it will ignore it and continue processing
|
| 253 |
+
#: after shifting all the unknown options into the resulting args.
|
| 254 |
+
self.ignore_unknown_options: bool = False
|
| 255 |
+
|
| 256 |
+
if ctx is not None:
|
| 257 |
+
self.allow_interspersed_args = ctx.allow_interspersed_args
|
| 258 |
+
self.ignore_unknown_options = ctx.ignore_unknown_options
|
| 259 |
+
|
| 260 |
+
self._short_opt: dict[str, _Option] = {}
|
| 261 |
+
self._long_opt: dict[str, _Option] = {}
|
| 262 |
+
self._opt_prefixes = {"-", "--"}
|
| 263 |
+
self._args: list[_Argument] = []
|
| 264 |
+
|
| 265 |
+
def add_option(
|
| 266 |
+
self,
|
| 267 |
+
obj: CoreOption,
|
| 268 |
+
opts: cabc.Sequence[str],
|
| 269 |
+
dest: str | None,
|
| 270 |
+
action: str | None = None,
|
| 271 |
+
nargs: int = 1,
|
| 272 |
+
const: t.Any | None = None,
|
| 273 |
+
) -> None:
|
| 274 |
+
"""Adds a new option named `dest` to the parser. The destination
|
| 275 |
+
is not inferred (unlike with optparse) and needs to be explicitly
|
| 276 |
+
provided. Action can be any of ``store``, ``store_const``,
|
| 277 |
+
``append``, ``append_const`` or ``count``.
|
| 278 |
+
|
| 279 |
+
The `obj` can be used to identify the option in the order list
|
| 280 |
+
that is returned from the parser.
|
| 281 |
+
"""
|
| 282 |
+
opts = [_normalize_opt(opt, self.ctx) for opt in opts]
|
| 283 |
+
option = _Option(obj, opts, dest, action=action, nargs=nargs, const=const)
|
| 284 |
+
self._opt_prefixes.update(option.prefixes)
|
| 285 |
+
for opt in option._short_opts:
|
| 286 |
+
self._short_opt[opt] = option
|
| 287 |
+
for opt in option._long_opts:
|
| 288 |
+
self._long_opt[opt] = option
|
| 289 |
+
|
| 290 |
+
def add_argument(self, obj: CoreArgument, dest: str | None, nargs: int = 1) -> None:
|
| 291 |
+
"""Adds a positional argument named `dest` to the parser.
|
| 292 |
+
|
| 293 |
+
The `obj` can be used to identify the option in the order list
|
| 294 |
+
that is returned from the parser.
|
| 295 |
+
"""
|
| 296 |
+
self._args.append(_Argument(obj, dest=dest, nargs=nargs))
|
| 297 |
+
|
| 298 |
+
def parse_args(
|
| 299 |
+
self, args: list[str]
|
| 300 |
+
) -> tuple[dict[str, t.Any], list[str], list[CoreParameter]]:
|
| 301 |
+
"""Parses positional arguments and returns ``(values, args, order)``
|
| 302 |
+
for the parsed options and arguments as well as the leftover
|
| 303 |
+
arguments if there are any. The order is a list of objects as they
|
| 304 |
+
appear on the command line. If arguments appear multiple times they
|
| 305 |
+
will be memorized multiple times as well.
|
| 306 |
+
"""
|
| 307 |
+
state = _ParsingState(args)
|
| 308 |
+
try:
|
| 309 |
+
self._process_args_for_options(state)
|
| 310 |
+
self._process_args_for_args(state)
|
| 311 |
+
except UsageError:
|
| 312 |
+
if self.ctx is None or not self.ctx.resilient_parsing:
|
| 313 |
+
raise
|
| 314 |
+
return state.opts, state.largs, state.order
|
| 315 |
+
|
| 316 |
+
def _process_args_for_args(self, state: _ParsingState) -> None:
|
| 317 |
+
pargs, args = _unpack_args(
|
| 318 |
+
state.largs + state.rargs, [x.nargs for x in self._args]
|
| 319 |
+
)
|
| 320 |
+
|
| 321 |
+
for idx, arg in enumerate(self._args):
|
| 322 |
+
arg.process(pargs[idx], state)
|
| 323 |
+
|
| 324 |
+
state.largs = args
|
| 325 |
+
state.rargs = []
|
| 326 |
+
|
| 327 |
+
def _process_args_for_options(self, state: _ParsingState) -> None:
|
| 328 |
+
while state.rargs:
|
| 329 |
+
arg = state.rargs.pop(0)
|
| 330 |
+
arglen = len(arg)
|
| 331 |
+
# Double dashes always handled explicitly regardless of what
|
| 332 |
+
# prefixes are valid.
|
| 333 |
+
if arg == "--":
|
| 334 |
+
return
|
| 335 |
+
elif arg[:1] in self._opt_prefixes and arglen > 1:
|
| 336 |
+
self._process_opts(arg, state)
|
| 337 |
+
elif self.allow_interspersed_args:
|
| 338 |
+
state.largs.append(arg)
|
| 339 |
+
else:
|
| 340 |
+
state.rargs.insert(0, arg)
|
| 341 |
+
return
|
| 342 |
+
|
| 343 |
+
# Say this is the original argument list:
|
| 344 |
+
# [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
|
| 345 |
+
# ^
|
| 346 |
+
# (we are about to process arg(i)).
|
| 347 |
+
#
|
| 348 |
+
# Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
|
| 349 |
+
# [arg0, ..., arg(i-1)] (any options and their arguments will have
|
| 350 |
+
# been removed from largs).
|
| 351 |
+
#
|
| 352 |
+
# The while loop will usually consume 1 or more arguments per pass.
|
| 353 |
+
# If it consumes 1 (eg. arg is an option that takes no arguments),
|
| 354 |
+
# then after _process_arg() is done the situation is:
|
| 355 |
+
#
|
| 356 |
+
# largs = subset of [arg0, ..., arg(i)]
|
| 357 |
+
# rargs = [arg(i+1), ..., arg(N-1)]
|
| 358 |
+
#
|
| 359 |
+
# If allow_interspersed_args is false, largs will always be
|
| 360 |
+
# *empty* -- still a subset of [arg0, ..., arg(i-1)], but
|
| 361 |
+
# not a very interesting subset!
|
| 362 |
+
|
| 363 |
+
def _match_long_opt(
|
| 364 |
+
self, opt: str, explicit_value: str | None, state: _ParsingState
|
| 365 |
+
) -> None:
|
| 366 |
+
if opt not in self._long_opt:
|
| 367 |
+
raise NoSuchOption(opt, possibilities=self._long_opt, ctx=self.ctx)
|
| 368 |
+
|
| 369 |
+
option = self._long_opt[opt]
|
| 370 |
+
if option.takes_value:
|
| 371 |
+
# At this point it's safe to modify rargs by injecting the
|
| 372 |
+
# explicit value, because no exception is raised in this
|
| 373 |
+
# branch. This means that the inserted value will be fully
|
| 374 |
+
# consumed.
|
| 375 |
+
if explicit_value is not None:
|
| 376 |
+
state.rargs.insert(0, explicit_value)
|
| 377 |
+
|
| 378 |
+
value = self._get_value_from_state(opt, option, state)
|
| 379 |
+
|
| 380 |
+
elif explicit_value is not None:
|
| 381 |
+
raise BadOptionUsage(
|
| 382 |
+
opt, _("Option {name!r} does not take a value.").format(name=opt)
|
| 383 |
+
)
|
| 384 |
+
|
| 385 |
+
else:
|
| 386 |
+
value = UNSET
|
| 387 |
+
|
| 388 |
+
option.process(value, state)
|
| 389 |
+
|
| 390 |
+
def _match_short_opt(self, arg: str, state: _ParsingState) -> None:
|
| 391 |
+
stop = False
|
| 392 |
+
i = 1
|
| 393 |
+
prefix = arg[0]
|
| 394 |
+
unknown_options = []
|
| 395 |
+
|
| 396 |
+
for ch in arg[1:]:
|
| 397 |
+
opt = _normalize_opt(f"{prefix}{ch}", self.ctx)
|
| 398 |
+
option = self._short_opt.get(opt)
|
| 399 |
+
i += 1
|
| 400 |
+
|
| 401 |
+
if not option:
|
| 402 |
+
if self.ignore_unknown_options:
|
| 403 |
+
unknown_options.append(ch)
|
| 404 |
+
continue
|
| 405 |
+
raise NoSuchOption(opt, ctx=self.ctx)
|
| 406 |
+
if option.takes_value:
|
| 407 |
+
# Any characters left in arg? Pretend they're the
|
| 408 |
+
# next arg, and stop consuming characters of arg.
|
| 409 |
+
if i < len(arg):
|
| 410 |
+
state.rargs.insert(0, arg[i:])
|
| 411 |
+
stop = True
|
| 412 |
+
|
| 413 |
+
value = self._get_value_from_state(opt, option, state)
|
| 414 |
+
|
| 415 |
+
else:
|
| 416 |
+
value = UNSET
|
| 417 |
+
|
| 418 |
+
option.process(value, state)
|
| 419 |
+
|
| 420 |
+
if stop:
|
| 421 |
+
break
|
| 422 |
+
|
| 423 |
+
# If we got any unknown options we recombine the string of the
|
| 424 |
+
# remaining options and re-attach the prefix, then report that
|
| 425 |
+
# to the state as new large. This way there is basic combinatorics
|
| 426 |
+
# that can be achieved while still ignoring unknown arguments.
|
| 427 |
+
if self.ignore_unknown_options and unknown_options:
|
| 428 |
+
state.largs.append(f"{prefix}{''.join(unknown_options)}")
|
| 429 |
+
|
| 430 |
+
def _get_value_from_state(
|
| 431 |
+
self, option_name: str, option: _Option, state: _ParsingState
|
| 432 |
+
) -> str | cabc.Sequence[str] | T_UNSET | T_FLAG_NEEDS_VALUE:
|
| 433 |
+
nargs = option.nargs
|
| 434 |
+
|
| 435 |
+
value: str | cabc.Sequence[str] | T_UNSET | T_FLAG_NEEDS_VALUE
|
| 436 |
+
|
| 437 |
+
if len(state.rargs) < nargs:
|
| 438 |
+
if option.obj._flag_needs_value:
|
| 439 |
+
# Option allows omitting the value.
|
| 440 |
+
value = FLAG_NEEDS_VALUE
|
| 441 |
+
else:
|
| 442 |
+
raise BadOptionUsage(
|
| 443 |
+
option_name,
|
| 444 |
+
ngettext(
|
| 445 |
+
"Option {name!r} requires an argument.",
|
| 446 |
+
"Option {name!r} requires {nargs} arguments.",
|
| 447 |
+
nargs,
|
| 448 |
+
).format(name=option_name, nargs=nargs),
|
| 449 |
+
)
|
| 450 |
+
elif nargs == 1:
|
| 451 |
+
next_rarg = state.rargs[0]
|
| 452 |
+
|
| 453 |
+
if (
|
| 454 |
+
option.obj._flag_needs_value
|
| 455 |
+
and isinstance(next_rarg, str)
|
| 456 |
+
and next_rarg[:1] in self._opt_prefixes
|
| 457 |
+
and len(next_rarg) > 1
|
| 458 |
+
):
|
| 459 |
+
# The next arg looks like the start of an option, don't
|
| 460 |
+
# use it as the value if omitting the value is allowed.
|
| 461 |
+
value = FLAG_NEEDS_VALUE
|
| 462 |
+
else:
|
| 463 |
+
value = state.rargs.pop(0)
|
| 464 |
+
else:
|
| 465 |
+
value = tuple(state.rargs[:nargs])
|
| 466 |
+
del state.rargs[:nargs]
|
| 467 |
+
|
| 468 |
+
return value
|
| 469 |
+
|
| 470 |
+
def _process_opts(self, arg: str, state: _ParsingState) -> None:
|
| 471 |
+
explicit_value = None
|
| 472 |
+
# Long option handling happens in two parts. The first part is
|
| 473 |
+
# supporting explicitly attached values. In any case, we will try
|
| 474 |
+
# to long match the option first.
|
| 475 |
+
if "=" in arg:
|
| 476 |
+
long_opt, explicit_value = arg.split("=", 1)
|
| 477 |
+
else:
|
| 478 |
+
long_opt = arg
|
| 479 |
+
norm_long_opt = _normalize_opt(long_opt, self.ctx)
|
| 480 |
+
|
| 481 |
+
# At this point we will match the (assumed) long option through
|
| 482 |
+
# the long option matching code. Note that this allows options
|
| 483 |
+
# like "-foo" to be matched as long options.
|
| 484 |
+
try:
|
| 485 |
+
self._match_long_opt(norm_long_opt, explicit_value, state)
|
| 486 |
+
except NoSuchOption:
|
| 487 |
+
# At this point the long option matching failed, and we need
|
| 488 |
+
# to try with short options. However there is a special rule
|
| 489 |
+
# which says, that if we have a two character options prefix
|
| 490 |
+
# (applies to "--foo" for instance), we do not dispatch to the
|
| 491 |
+
# short option code and will instead raise the no option
|
| 492 |
+
# error.
|
| 493 |
+
if arg[:2] not in self._opt_prefixes:
|
| 494 |
+
self._match_short_opt(arg, state)
|
| 495 |
+
return
|
| 496 |
+
|
| 497 |
+
if not self.ignore_unknown_options:
|
| 498 |
+
raise
|
| 499 |
+
|
| 500 |
+
state.largs.append(arg)
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
def __getattr__(name: str) -> object:
|
| 504 |
+
import warnings
|
| 505 |
+
|
| 506 |
+
if name in {
|
| 507 |
+
"OptionParser",
|
| 508 |
+
"Argument",
|
| 509 |
+
"Option",
|
| 510 |
+
"split_opt",
|
| 511 |
+
"normalize_opt",
|
| 512 |
+
"ParsingState",
|
| 513 |
+
}:
|
| 514 |
+
warnings.warn(
|
| 515 |
+
f"'parser.{name}' is deprecated and will be removed in Click 9.0."
|
| 516 |
+
" The old parser is available in 'optparse'.",
|
| 517 |
+
DeprecationWarning,
|
| 518 |
+
stacklevel=2,
|
| 519 |
+
)
|
| 520 |
+
return globals()[f"_{name}"]
|
| 521 |
+
|
| 522 |
+
if name == "split_arg_string":
|
| 523 |
+
from .shell_completion import split_arg_string
|
| 524 |
+
|
| 525 |
+
warnings.warn(
|
| 526 |
+
"Importing 'parser.split_arg_string' is deprecated, it will only be"
|
| 527 |
+
" available in 'shell_completion' in Click 9.0.",
|
| 528 |
+
DeprecationWarning,
|
| 529 |
+
stacklevel=2,
|
| 530 |
+
)
|
| 531 |
+
return split_arg_string
|
| 532 |
+
|
| 533 |
+
raise AttributeError(name)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/py.typed
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/shell_completion.py
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections.abc as cabc
|
| 4 |
+
import os
|
| 5 |
+
import re
|
| 6 |
+
import typing as t
|
| 7 |
+
from gettext import gettext as _
|
| 8 |
+
|
| 9 |
+
from .core import Argument
|
| 10 |
+
from .core import Command
|
| 11 |
+
from .core import Context
|
| 12 |
+
from .core import Group
|
| 13 |
+
from .core import Option
|
| 14 |
+
from .core import Parameter
|
| 15 |
+
from .core import ParameterSource
|
| 16 |
+
from .utils import echo
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def shell_complete(
|
| 20 |
+
cli: Command,
|
| 21 |
+
ctx_args: cabc.MutableMapping[str, t.Any],
|
| 22 |
+
prog_name: str,
|
| 23 |
+
complete_var: str,
|
| 24 |
+
instruction: str,
|
| 25 |
+
) -> t.Literal[0, 1]:
|
| 26 |
+
"""Perform shell completion for the given CLI program.
|
| 27 |
+
|
| 28 |
+
:param cli: Command being called.
|
| 29 |
+
:param ctx_args: Extra arguments to pass to
|
| 30 |
+
``cli.make_context``.
|
| 31 |
+
:param prog_name: Name of the executable in the shell.
|
| 32 |
+
:param complete_var: Name of the environment variable that holds
|
| 33 |
+
the completion instruction.
|
| 34 |
+
:param instruction: Value of ``complete_var`` with the completion
|
| 35 |
+
instruction and shell, in the form ``instruction_shell``.
|
| 36 |
+
:return: Status code to exit with.
|
| 37 |
+
"""
|
| 38 |
+
shell, _, instruction = instruction.partition("_")
|
| 39 |
+
comp_cls = get_completion_class(shell)
|
| 40 |
+
|
| 41 |
+
if comp_cls is None:
|
| 42 |
+
return 1
|
| 43 |
+
|
| 44 |
+
comp = comp_cls(cli, ctx_args, prog_name, complete_var)
|
| 45 |
+
|
| 46 |
+
# Write bytes, otherwise Windows text stdout translates LF to CRLF and breaks.
|
| 47 |
+
if instruction == "source":
|
| 48 |
+
echo(comp.source().encode(), nl=False)
|
| 49 |
+
return 0
|
| 50 |
+
|
| 51 |
+
if instruction == "complete":
|
| 52 |
+
echo(comp.complete().encode())
|
| 53 |
+
return 0
|
| 54 |
+
|
| 55 |
+
return 1
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
if t.TYPE_CHECKING:
|
| 59 |
+
from typing_extensions import TypeVar
|
| 60 |
+
|
| 61 |
+
# `Any` is used as default for backwards compatibility (instead of e.g. `str`)
|
| 62 |
+
_ValueT_co = TypeVar("_ValueT_co", covariant=True, default=t.Any)
|
| 63 |
+
else:
|
| 64 |
+
_ValueT_co = t.TypeVar("_ValueT_co", covariant=True)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class CompletionItem(t.Generic[_ValueT_co]):
|
| 68 |
+
"""Represents a completion value and metadata about the value. The
|
| 69 |
+
default metadata is ``type`` to indicate special shell handling,
|
| 70 |
+
and ``help`` if a shell supports showing a help string next to the
|
| 71 |
+
value.
|
| 72 |
+
|
| 73 |
+
Arbitrary parameters can be passed when creating the object, and
|
| 74 |
+
accessed using ``item.attr``. If an attribute wasn't passed,
|
| 75 |
+
accessing it returns ``None``.
|
| 76 |
+
|
| 77 |
+
:param value: The completion suggestion.
|
| 78 |
+
:param type: Tells the shell script to provide special completion
|
| 79 |
+
support for the type. Click uses ``"dir"`` and ``"file"``.
|
| 80 |
+
:param help: String shown next to the value if supported.
|
| 81 |
+
:param kwargs: Arbitrary metadata. The built-in implementations
|
| 82 |
+
don't use this, but custom type completions paired with custom
|
| 83 |
+
shell support could use it.
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
__slots__ = ("value", "type", "help", "_info")
|
| 87 |
+
|
| 88 |
+
def __init__(
|
| 89 |
+
self,
|
| 90 |
+
value: _ValueT_co,
|
| 91 |
+
type: str = "plain",
|
| 92 |
+
help: str | None = None,
|
| 93 |
+
**kwargs: t.Any,
|
| 94 |
+
) -> None:
|
| 95 |
+
self.value: _ValueT_co = value
|
| 96 |
+
self.type: str = type
|
| 97 |
+
self.help: str | None = help
|
| 98 |
+
self._info = kwargs
|
| 99 |
+
|
| 100 |
+
def __getattr__(self, name: str) -> t.Any:
|
| 101 |
+
return self._info.get(name)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
# Only Bash >= 4.4 has the nosort option.
|
| 105 |
+
_SOURCE_BASH = """\
|
| 106 |
+
%(complete_func)s() {
|
| 107 |
+
local IFS=$'\\n'
|
| 108 |
+
local response
|
| 109 |
+
|
| 110 |
+
response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \
|
| 111 |
+
%(complete_var)s=bash_complete $1)
|
| 112 |
+
|
| 113 |
+
for completion in $response; do
|
| 114 |
+
IFS=',' read type value <<< "$completion"
|
| 115 |
+
|
| 116 |
+
if [[ $type == 'dir' ]]; then
|
| 117 |
+
COMPREPLY=()
|
| 118 |
+
compopt -o dirnames
|
| 119 |
+
elif [[ $type == 'file' ]]; then
|
| 120 |
+
COMPREPLY=()
|
| 121 |
+
compopt -o default
|
| 122 |
+
elif [[ $type == 'plain' ]]; then
|
| 123 |
+
COMPREPLY+=($value)
|
| 124 |
+
fi
|
| 125 |
+
done
|
| 126 |
+
|
| 127 |
+
return 0
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
%(complete_func)s_setup() {
|
| 131 |
+
complete -o nosort -F %(complete_func)s %(prog_name)s
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
%(complete_func)s_setup;
|
| 135 |
+
"""
|
| 136 |
+
|
| 137 |
+
# See ZshComplete.format_completion below, and issue #2703, before
|
| 138 |
+
# changing this script.
|
| 139 |
+
#
|
| 140 |
+
# (TL;DR: _describe is picky about the format, but this Zsh script snippet
|
| 141 |
+
# is already widely deployed. So freeze this script, and use clever-ish
|
| 142 |
+
# handling of colons in ZshComplet.format_completion.)
|
| 143 |
+
_SOURCE_ZSH = """\
|
| 144 |
+
#compdef %(prog_name)s
|
| 145 |
+
|
| 146 |
+
%(complete_func)s() {
|
| 147 |
+
local -a completions
|
| 148 |
+
local -a completions_with_descriptions
|
| 149 |
+
local -a response
|
| 150 |
+
(( ! $+commands[%(prog_name)s] )) && return 1
|
| 151 |
+
|
| 152 |
+
response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \
|
| 153 |
+
%(complete_var)s=zsh_complete %(prog_name)s)}")
|
| 154 |
+
|
| 155 |
+
for type key descr in ${response}; do
|
| 156 |
+
if [[ "$type" == "plain" ]]; then
|
| 157 |
+
if [[ "$descr" == "_" ]]; then
|
| 158 |
+
completions+=("$key")
|
| 159 |
+
else
|
| 160 |
+
completions_with_descriptions+=("$key":"$descr")
|
| 161 |
+
fi
|
| 162 |
+
elif [[ "$type" == "dir" ]]; then
|
| 163 |
+
_path_files -/
|
| 164 |
+
elif [[ "$type" == "file" ]]; then
|
| 165 |
+
_path_files -f
|
| 166 |
+
fi
|
| 167 |
+
done
|
| 168 |
+
|
| 169 |
+
if [ -n "$completions_with_descriptions" ]; then
|
| 170 |
+
_describe -V unsorted completions_with_descriptions -U
|
| 171 |
+
fi
|
| 172 |
+
|
| 173 |
+
if [ -n "$completions" ]; then
|
| 174 |
+
compadd -U -V unsorted -a completions
|
| 175 |
+
fi
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
|
| 179 |
+
# autoload from fpath, call function directly
|
| 180 |
+
%(complete_func)s "$@"
|
| 181 |
+
else
|
| 182 |
+
# eval/source/. command, register function for later
|
| 183 |
+
compdef %(complete_func)s %(prog_name)s
|
| 184 |
+
fi
|
| 185 |
+
"""
|
| 186 |
+
|
| 187 |
+
_SOURCE_FISH = """\
|
| 188 |
+
function %(complete_func)s;
|
| 189 |
+
set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \
|
| 190 |
+
COMP_CWORD=(commandline -t) %(prog_name)s);
|
| 191 |
+
|
| 192 |
+
for completion in $response;
|
| 193 |
+
set -l metadata (string split "," $completion);
|
| 194 |
+
|
| 195 |
+
if test $metadata[1] = "dir";
|
| 196 |
+
__fish_complete_directories $metadata[2];
|
| 197 |
+
else if test $metadata[1] = "file";
|
| 198 |
+
__fish_complete_path $metadata[2];
|
| 199 |
+
else if test $metadata[1] = "plain";
|
| 200 |
+
echo $metadata[2];
|
| 201 |
+
end;
|
| 202 |
+
end;
|
| 203 |
+
end;
|
| 204 |
+
|
| 205 |
+
complete --no-files --command %(prog_name)s --arguments \
|
| 206 |
+
"(%(complete_func)s)";
|
| 207 |
+
"""
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
class _SourceVarsDict(t.TypedDict):
|
| 211 |
+
complete_func: str
|
| 212 |
+
complete_var: str
|
| 213 |
+
prog_name: str
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
class ShellComplete:
|
| 217 |
+
"""Base class for providing shell completion support. A subclass for
|
| 218 |
+
a given shell will override attributes and methods to implement the
|
| 219 |
+
completion instructions (``source`` and ``complete``).
|
| 220 |
+
|
| 221 |
+
:param cli: Command being called.
|
| 222 |
+
:param prog_name: Name of the executable in the shell.
|
| 223 |
+
:param complete_var: Name of the environment variable that holds
|
| 224 |
+
the completion instruction.
|
| 225 |
+
|
| 226 |
+
.. versionadded:: 8.0
|
| 227 |
+
"""
|
| 228 |
+
|
| 229 |
+
name: t.ClassVar[str]
|
| 230 |
+
"""Name to register the shell as with :func:`add_completion_class`.
|
| 231 |
+
This is used in completion instructions (``{name}_source`` and
|
| 232 |
+
``{name}_complete``).
|
| 233 |
+
"""
|
| 234 |
+
|
| 235 |
+
source_template: t.ClassVar[str]
|
| 236 |
+
"""Completion script template formatted by :meth:`source`. This must
|
| 237 |
+
be provided by subclasses.
|
| 238 |
+
"""
|
| 239 |
+
|
| 240 |
+
cli: Command
|
| 241 |
+
ctx_args: cabc.MutableMapping[str, t.Any]
|
| 242 |
+
prog_name: str
|
| 243 |
+
complete_var: str
|
| 244 |
+
|
| 245 |
+
def __init__(
|
| 246 |
+
self,
|
| 247 |
+
cli: Command,
|
| 248 |
+
ctx_args: cabc.MutableMapping[str, t.Any],
|
| 249 |
+
prog_name: str,
|
| 250 |
+
complete_var: str,
|
| 251 |
+
) -> None:
|
| 252 |
+
self.cli = cli
|
| 253 |
+
self.ctx_args = ctx_args
|
| 254 |
+
self.prog_name = prog_name
|
| 255 |
+
self.complete_var = complete_var
|
| 256 |
+
|
| 257 |
+
@property
|
| 258 |
+
def func_name(self) -> str:
|
| 259 |
+
"""The name of the shell function defined by the completion
|
| 260 |
+
script.
|
| 261 |
+
"""
|
| 262 |
+
safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII)
|
| 263 |
+
return f"_{safe_name}_completion"
|
| 264 |
+
|
| 265 |
+
def source_vars(self) -> _SourceVarsDict:
|
| 266 |
+
"""Vars for formatting :attr:`source_template`.
|
| 267 |
+
|
| 268 |
+
By default this provides ``complete_func``, ``complete_var``,
|
| 269 |
+
and ``prog_name``.
|
| 270 |
+
"""
|
| 271 |
+
return {
|
| 272 |
+
"complete_func": self.func_name,
|
| 273 |
+
"complete_var": self.complete_var,
|
| 274 |
+
"prog_name": self.prog_name,
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
def source(self) -> str:
|
| 278 |
+
"""Produce the shell script that defines the completion
|
| 279 |
+
function. By default this ``%``-style formats
|
| 280 |
+
:attr:`source_template` with the dict returned by
|
| 281 |
+
:meth:`source_vars`.
|
| 282 |
+
"""
|
| 283 |
+
return self.source_template % self.source_vars()
|
| 284 |
+
|
| 285 |
+
def get_completion_args(self) -> tuple[list[str], str]:
|
| 286 |
+
"""Use the env vars defined by the shell script to return a
|
| 287 |
+
tuple of ``args, incomplete``. This must be implemented by
|
| 288 |
+
subclasses.
|
| 289 |
+
"""
|
| 290 |
+
raise NotImplementedError
|
| 291 |
+
|
| 292 |
+
def get_completions(
|
| 293 |
+
self, args: list[str], incomplete: str
|
| 294 |
+
) -> list[CompletionItem[str]]:
|
| 295 |
+
"""Determine the context and last complete command or parameter
|
| 296 |
+
from the complete args. Call that object's ``shell_complete``
|
| 297 |
+
method to get the completions for the incomplete value.
|
| 298 |
+
|
| 299 |
+
:param args: List of complete args before the incomplete value.
|
| 300 |
+
:param incomplete: Value being completed. May be empty.
|
| 301 |
+
"""
|
| 302 |
+
ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args)
|
| 303 |
+
obj, incomplete = _resolve_incomplete(ctx, args, incomplete)
|
| 304 |
+
return obj.shell_complete(ctx, incomplete)
|
| 305 |
+
|
| 306 |
+
def format_completion(self, item: CompletionItem[str]) -> str:
|
| 307 |
+
"""Format a completion item into the form recognized by the
|
| 308 |
+
shell script. This must be implemented by subclasses.
|
| 309 |
+
|
| 310 |
+
:param item: Completion item to format.
|
| 311 |
+
"""
|
| 312 |
+
raise NotImplementedError
|
| 313 |
+
|
| 314 |
+
def complete(self) -> str:
|
| 315 |
+
"""Produce the completion data to send back to the shell.
|
| 316 |
+
|
| 317 |
+
By default this calls :meth:`get_completion_args`, gets the
|
| 318 |
+
completions, then calls :meth:`format_completion` for each
|
| 319 |
+
completion.
|
| 320 |
+
"""
|
| 321 |
+
args, incomplete = self.get_completion_args()
|
| 322 |
+
completions = self.get_completions(args, incomplete)
|
| 323 |
+
out = [self.format_completion(item) for item in completions]
|
| 324 |
+
return "\n".join(out)
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
class BashComplete(ShellComplete):
|
| 328 |
+
"""Shell completion for Bash."""
|
| 329 |
+
|
| 330 |
+
name: t.ClassVar[str] = "bash"
|
| 331 |
+
source_template: t.ClassVar[str] = _SOURCE_BASH
|
| 332 |
+
|
| 333 |
+
@staticmethod
|
| 334 |
+
def _check_version() -> None:
|
| 335 |
+
import shutil
|
| 336 |
+
import subprocess
|
| 337 |
+
|
| 338 |
+
bash_exe = shutil.which("bash")
|
| 339 |
+
|
| 340 |
+
if bash_exe is None:
|
| 341 |
+
match = None
|
| 342 |
+
else:
|
| 343 |
+
output = subprocess.run(
|
| 344 |
+
[bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'],
|
| 345 |
+
stdout=subprocess.PIPE,
|
| 346 |
+
)
|
| 347 |
+
match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode())
|
| 348 |
+
|
| 349 |
+
if match is not None:
|
| 350 |
+
major, minor = match.groups()
|
| 351 |
+
|
| 352 |
+
if major < "4" or major == "4" and minor < "4":
|
| 353 |
+
echo(
|
| 354 |
+
_(
|
| 355 |
+
"Shell completion is not supported for Bash"
|
| 356 |
+
" versions older than 4.4."
|
| 357 |
+
),
|
| 358 |
+
err=True,
|
| 359 |
+
)
|
| 360 |
+
else:
|
| 361 |
+
echo(
|
| 362 |
+
_("Couldn't detect Bash version, shell completion is not supported."),
|
| 363 |
+
err=True,
|
| 364 |
+
)
|
| 365 |
+
|
| 366 |
+
def source(self) -> str:
|
| 367 |
+
self._check_version()
|
| 368 |
+
return super().source()
|
| 369 |
+
|
| 370 |
+
def get_completion_args(self) -> tuple[list[str], str]:
|
| 371 |
+
cwords = split_arg_string(os.environ["COMP_WORDS"])
|
| 372 |
+
cword = int(os.environ["COMP_CWORD"])
|
| 373 |
+
args = cwords[1:cword]
|
| 374 |
+
|
| 375 |
+
try:
|
| 376 |
+
incomplete = cwords[cword]
|
| 377 |
+
except IndexError:
|
| 378 |
+
incomplete = ""
|
| 379 |
+
|
| 380 |
+
return args, incomplete
|
| 381 |
+
|
| 382 |
+
def format_completion(self, item: CompletionItem[t.Any]) -> str:
|
| 383 |
+
return f"{item.type},{item.value}"
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
class ZshComplete(ShellComplete):
|
| 387 |
+
"""Shell completion for Zsh."""
|
| 388 |
+
|
| 389 |
+
name: t.ClassVar[str] = "zsh"
|
| 390 |
+
source_template: t.ClassVar[str] = _SOURCE_ZSH
|
| 391 |
+
|
| 392 |
+
def get_completion_args(self) -> tuple[list[str], str]:
|
| 393 |
+
cwords = split_arg_string(os.environ["COMP_WORDS"])
|
| 394 |
+
cword = int(os.environ["COMP_CWORD"])
|
| 395 |
+
args = cwords[1:cword]
|
| 396 |
+
|
| 397 |
+
try:
|
| 398 |
+
incomplete = cwords[cword]
|
| 399 |
+
except IndexError:
|
| 400 |
+
incomplete = ""
|
| 401 |
+
|
| 402 |
+
return args, incomplete
|
| 403 |
+
|
| 404 |
+
def format_completion(self, item: CompletionItem[str]) -> str:
|
| 405 |
+
help_ = item.help or "_"
|
| 406 |
+
# The zsh completion script uses `_describe` on items with help
|
| 407 |
+
# texts (which splits the item help from the item value at the
|
| 408 |
+
# first unescaped colon) and `compadd` on items without help
|
| 409 |
+
# text (which uses the item value as-is and does not support
|
| 410 |
+
# colon escaping). So escape colons in the item value if and
|
| 411 |
+
# only if the item help is not the sentinel "_" value, as used
|
| 412 |
+
# by the completion script.
|
| 413 |
+
#
|
| 414 |
+
# (The zsh completion script is potentially widely deployed, and
|
| 415 |
+
# thus harder to fix than this method.)
|
| 416 |
+
#
|
| 417 |
+
# See issue #1812 and issue #2703 for further context.
|
| 418 |
+
value = item.value.replace(":", r"\:") if help_ != "_" else item.value
|
| 419 |
+
return f"{item.type}\n{value}\n{help_}"
|
| 420 |
+
|
| 421 |
+
|
| 422 |
+
class FishComplete(ShellComplete):
|
| 423 |
+
"""Shell completion for Fish."""
|
| 424 |
+
|
| 425 |
+
name: t.ClassVar[str] = "fish"
|
| 426 |
+
source_template: t.ClassVar[str] = _SOURCE_FISH
|
| 427 |
+
|
| 428 |
+
def get_completion_args(self) -> tuple[list[str], str]:
|
| 429 |
+
cwords = split_arg_string(os.environ["COMP_WORDS"])
|
| 430 |
+
incomplete = os.environ["COMP_CWORD"]
|
| 431 |
+
if incomplete:
|
| 432 |
+
incomplete = split_arg_string(incomplete)[0]
|
| 433 |
+
args = cwords[1:]
|
| 434 |
+
|
| 435 |
+
# Fish stores the partial word in both COMP_WORDS and
|
| 436 |
+
# COMP_CWORD, remove it from complete args.
|
| 437 |
+
if incomplete and args and args[-1] == incomplete:
|
| 438 |
+
args.pop()
|
| 439 |
+
|
| 440 |
+
return args, incomplete
|
| 441 |
+
|
| 442 |
+
def format_completion(self, item: CompletionItem[str]) -> str:
|
| 443 |
+
"""
|
| 444 |
+
.. versionchanged:: 8.4.2
|
| 445 |
+
Escape newlines and replace tabs with spaces in the help text to
|
| 446 |
+
fix completion errors with multi-line help strings.
|
| 447 |
+
"""
|
| 448 |
+
# According to https://fishshell.com/docs/current/cmds/complete.html
|
| 449 |
+
# Command substitutions found in ARGUMENTS should return a newline-
|
| 450 |
+
# separated list of arguments, and each argument may optionally have a tab
|
| 451 |
+
# character followed by the argument description.
|
| 452 |
+
if item.help:
|
| 453 |
+
help_ = item.help.replace("\n", "\\n").replace("\t", " ")
|
| 454 |
+
return f"{item.type},{item.value}\t{help_}"
|
| 455 |
+
|
| 456 |
+
return f"{item.type},{item.value}"
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
_available_shells: t.Final[dict[str, type[ShellComplete]]] = {
|
| 460 |
+
"bash": BashComplete,
|
| 461 |
+
"fish": FishComplete,
|
| 462 |
+
"zsh": ZshComplete,
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
_ShellCompleteT = t.TypeVar("_ShellCompleteT", bound="ShellComplete")
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
def add_completion_class(
|
| 469 |
+
cls: type[_ShellCompleteT], name: str | None = None
|
| 470 |
+
) -> type[_ShellCompleteT]:
|
| 471 |
+
"""Register a :class:`ShellComplete` subclass under the given name.
|
| 472 |
+
The name will be provided by the completion instruction environment
|
| 473 |
+
variable during completion.
|
| 474 |
+
|
| 475 |
+
:param cls: The completion class that will handle completion for the
|
| 476 |
+
shell.
|
| 477 |
+
:param name: Name to register the class under. Defaults to the
|
| 478 |
+
class's ``name`` attribute.
|
| 479 |
+
"""
|
| 480 |
+
if name is None:
|
| 481 |
+
name = cls.name
|
| 482 |
+
|
| 483 |
+
_available_shells[name] = cls
|
| 484 |
+
|
| 485 |
+
return cls
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
@t.overload
|
| 489 |
+
def get_completion_class(shell: t.Literal["bash"]) -> type[BashComplete]: ...
|
| 490 |
+
@t.overload
|
| 491 |
+
def get_completion_class(shell: t.Literal["fish"]) -> type[FishComplete]: ...
|
| 492 |
+
@t.overload
|
| 493 |
+
def get_completion_class(shell: t.Literal["zsh"]) -> type[ZshComplete]: ...
|
| 494 |
+
@t.overload
|
| 495 |
+
def get_completion_class(shell: str) -> type[ShellComplete] | None: ...
|
| 496 |
+
def get_completion_class(shell: str) -> type[ShellComplete] | None:
|
| 497 |
+
"""Look up a registered :class:`ShellComplete` subclass by the name
|
| 498 |
+
provided by the completion instruction environment variable. If the
|
| 499 |
+
name isn't registered, returns ``None``.
|
| 500 |
+
|
| 501 |
+
:param shell: Name the class is registered under.
|
| 502 |
+
"""
|
| 503 |
+
return _available_shells.get(shell)
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
def split_arg_string(string: str) -> list[str]:
|
| 507 |
+
"""Split an argument string as with :func:`shlex.split`, but don't
|
| 508 |
+
fail if the string is incomplete. Ignores a missing closing quote or
|
| 509 |
+
incomplete escape sequence and uses the partial token as-is.
|
| 510 |
+
|
| 511 |
+
.. code-block:: python
|
| 512 |
+
|
| 513 |
+
split_arg_string("example 'my file")
|
| 514 |
+
["example", "my file"]
|
| 515 |
+
|
| 516 |
+
split_arg_string("example my\\")
|
| 517 |
+
["example", "my"]
|
| 518 |
+
|
| 519 |
+
:param string: String to split.
|
| 520 |
+
|
| 521 |
+
.. versionchanged:: 8.2
|
| 522 |
+
Moved to ``shell_completion`` from ``parser``.
|
| 523 |
+
"""
|
| 524 |
+
import shlex
|
| 525 |
+
|
| 526 |
+
lex = shlex.shlex(string, posix=True)
|
| 527 |
+
lex.whitespace_split = True
|
| 528 |
+
lex.commenters = ""
|
| 529 |
+
out = []
|
| 530 |
+
|
| 531 |
+
try:
|
| 532 |
+
for token in lex:
|
| 533 |
+
out.append(token)
|
| 534 |
+
except ValueError:
|
| 535 |
+
# Raised when end-of-string is reached in an invalid state. Use
|
| 536 |
+
# the partial token as-is. The quote or escape character is in
|
| 537 |
+
# lex.state, not lex.token.
|
| 538 |
+
out.append(lex.token)
|
| 539 |
+
|
| 540 |
+
return out
|
| 541 |
+
|
| 542 |
+
|
| 543 |
+
def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool:
|
| 544 |
+
"""Determine if the given parameter is an argument that can still
|
| 545 |
+
accept values.
|
| 546 |
+
|
| 547 |
+
:param ctx: Invocation context for the command represented by the
|
| 548 |
+
parsed complete args.
|
| 549 |
+
:param param: Argument object being checked.
|
| 550 |
+
"""
|
| 551 |
+
if not isinstance(param, Argument):
|
| 552 |
+
return False
|
| 553 |
+
|
| 554 |
+
value = ctx.params.get(param.name)
|
| 555 |
+
return (
|
| 556 |
+
param.nargs == -1
|
| 557 |
+
or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE
|
| 558 |
+
or (
|
| 559 |
+
param.nargs > 1
|
| 560 |
+
and isinstance(value, (tuple, list))
|
| 561 |
+
and len(value) < param.nargs
|
| 562 |
+
)
|
| 563 |
+
)
|
| 564 |
+
|
| 565 |
+
|
| 566 |
+
def _start_of_option(ctx: Context, value: str) -> bool:
|
| 567 |
+
"""Check if the value looks like the start of an option."""
|
| 568 |
+
if not value:
|
| 569 |
+
return False
|
| 570 |
+
|
| 571 |
+
c = value[0]
|
| 572 |
+
return c in ctx._opt_prefixes
|
| 573 |
+
|
| 574 |
+
|
| 575 |
+
def _is_incomplete_option(ctx: Context, args: list[str], param: Parameter) -> bool:
|
| 576 |
+
"""Determine if the given parameter is an option that needs a value.
|
| 577 |
+
|
| 578 |
+
:param args: List of complete args before the incomplete value.
|
| 579 |
+
:param param: Option object being checked.
|
| 580 |
+
"""
|
| 581 |
+
if not isinstance(param, Option):
|
| 582 |
+
return False
|
| 583 |
+
|
| 584 |
+
if param.is_flag or param.count:
|
| 585 |
+
return False
|
| 586 |
+
|
| 587 |
+
last_option = None
|
| 588 |
+
|
| 589 |
+
for index, arg in enumerate(reversed(args)):
|
| 590 |
+
if index + 1 > param.nargs:
|
| 591 |
+
break
|
| 592 |
+
|
| 593 |
+
if _start_of_option(ctx, arg):
|
| 594 |
+
last_option = arg
|
| 595 |
+
break
|
| 596 |
+
|
| 597 |
+
return last_option is not None and last_option in param.opts
|
| 598 |
+
|
| 599 |
+
|
| 600 |
+
def _resolve_context(
|
| 601 |
+
cli: Command,
|
| 602 |
+
ctx_args: cabc.MutableMapping[str, t.Any],
|
| 603 |
+
prog_name: str,
|
| 604 |
+
args: list[str],
|
| 605 |
+
) -> Context:
|
| 606 |
+
"""Produce the context hierarchy starting with the command and
|
| 607 |
+
traversing the complete arguments. This only follows the commands,
|
| 608 |
+
it doesn't trigger input prompts or callbacks.
|
| 609 |
+
|
| 610 |
+
:param cli: Command being called.
|
| 611 |
+
:param prog_name: Name of the executable in the shell.
|
| 612 |
+
:param args: List of complete args before the incomplete value.
|
| 613 |
+
"""
|
| 614 |
+
ctx_args["resilient_parsing"] = True
|
| 615 |
+
with cli.make_context(prog_name, args.copy(), **ctx_args) as ctx:
|
| 616 |
+
args = ctx._protected_args + ctx.args
|
| 617 |
+
|
| 618 |
+
while args:
|
| 619 |
+
command = ctx.command
|
| 620 |
+
|
| 621 |
+
if isinstance(command, Group):
|
| 622 |
+
if not command.chain:
|
| 623 |
+
name, cmd, args = command.resolve_command(ctx, args)
|
| 624 |
+
|
| 625 |
+
if cmd is None:
|
| 626 |
+
return ctx
|
| 627 |
+
|
| 628 |
+
with cmd.make_context(
|
| 629 |
+
name, args, parent=ctx, resilient_parsing=True
|
| 630 |
+
) as sub_ctx:
|
| 631 |
+
ctx = sub_ctx
|
| 632 |
+
args = ctx._protected_args + ctx.args
|
| 633 |
+
else:
|
| 634 |
+
sub_ctx = ctx
|
| 635 |
+
|
| 636 |
+
while args:
|
| 637 |
+
name, cmd, args = command.resolve_command(ctx, args)
|
| 638 |
+
|
| 639 |
+
if cmd is None:
|
| 640 |
+
return ctx
|
| 641 |
+
|
| 642 |
+
with cmd.make_context(
|
| 643 |
+
name,
|
| 644 |
+
args,
|
| 645 |
+
parent=ctx,
|
| 646 |
+
allow_extra_args=True,
|
| 647 |
+
allow_interspersed_args=False,
|
| 648 |
+
resilient_parsing=True,
|
| 649 |
+
) as sub_sub_ctx:
|
| 650 |
+
sub_ctx = sub_sub_ctx
|
| 651 |
+
args = sub_ctx.args
|
| 652 |
+
|
| 653 |
+
ctx = sub_ctx
|
| 654 |
+
args = [*sub_ctx._protected_args, *sub_ctx.args]
|
| 655 |
+
else:
|
| 656 |
+
break
|
| 657 |
+
|
| 658 |
+
return ctx
|
| 659 |
+
|
| 660 |
+
|
| 661 |
+
def _resolve_incomplete(
|
| 662 |
+
ctx: Context, args: list[str], incomplete: str
|
| 663 |
+
) -> tuple[Command | Parameter, str]:
|
| 664 |
+
"""Find the Click object that will handle the completion of the
|
| 665 |
+
incomplete value. Return the object and the incomplete value.
|
| 666 |
+
|
| 667 |
+
:param ctx: Invocation context for the command represented by
|
| 668 |
+
the parsed complete args.
|
| 669 |
+
:param args: List of complete args before the incomplete value.
|
| 670 |
+
:param incomplete: Value being completed. May be empty.
|
| 671 |
+
"""
|
| 672 |
+
# Different shells treat an "=" between a long option name and
|
| 673 |
+
# value differently. Might keep the value joined, return the "="
|
| 674 |
+
# as a separate item, or return the split name and value. Always
|
| 675 |
+
# split and discard the "=" to make completion easier.
|
| 676 |
+
if incomplete == "=":
|
| 677 |
+
incomplete = ""
|
| 678 |
+
elif "=" in incomplete and _start_of_option(ctx, incomplete):
|
| 679 |
+
name, _, incomplete = incomplete.partition("=")
|
| 680 |
+
args.append(name)
|
| 681 |
+
|
| 682 |
+
# The "--" marker tells Click to stop treating values as options
|
| 683 |
+
# even if they start with the option character. If it hasn't been
|
| 684 |
+
# given and the incomplete arg looks like an option, the current
|
| 685 |
+
# command will provide option name completions.
|
| 686 |
+
if "--" not in args and _start_of_option(ctx, incomplete):
|
| 687 |
+
return ctx.command, incomplete
|
| 688 |
+
|
| 689 |
+
params = ctx.command.get_params(ctx)
|
| 690 |
+
|
| 691 |
+
# If the last complete arg is an option name with an incomplete
|
| 692 |
+
# value, the option will provide value completions.
|
| 693 |
+
for param in params:
|
| 694 |
+
if _is_incomplete_option(ctx, args, param):
|
| 695 |
+
return param, incomplete
|
| 696 |
+
|
| 697 |
+
# It's not an option name or value. The first argument without a
|
| 698 |
+
# parsed value will provide value completions.
|
| 699 |
+
for param in params:
|
| 700 |
+
if _is_incomplete_argument(ctx, param):
|
| 701 |
+
return param, incomplete
|
| 702 |
+
|
| 703 |
+
# There were no unparsed arguments, the command may be a group that
|
| 704 |
+
# will provide command name completions.
|
| 705 |
+
return ctx.command, incomplete
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/termui.py
ADDED
|
@@ -0,0 +1,945 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections.abc as cabc
|
| 4 |
+
import inspect
|
| 5 |
+
import io
|
| 6 |
+
import itertools
|
| 7 |
+
import re
|
| 8 |
+
import sys
|
| 9 |
+
import typing as t
|
| 10 |
+
from contextlib import AbstractContextManager
|
| 11 |
+
from contextlib import redirect_stdout
|
| 12 |
+
from gettext import gettext as _
|
| 13 |
+
|
| 14 |
+
from ._compat import isatty
|
| 15 |
+
from ._compat import strip_ansi
|
| 16 |
+
from ._compat import WIN
|
| 17 |
+
from .exceptions import Abort
|
| 18 |
+
from .exceptions import UsageError
|
| 19 |
+
from .globals import resolve_color_default
|
| 20 |
+
from .types import Choice
|
| 21 |
+
from .types import convert_type
|
| 22 |
+
from .types import ParamType
|
| 23 |
+
from .utils import echo
|
| 24 |
+
from .utils import LazyFile
|
| 25 |
+
|
| 26 |
+
if t.TYPE_CHECKING:
|
| 27 |
+
from ._termui_impl import ProgressBar
|
| 28 |
+
|
| 29 |
+
V = t.TypeVar("V")
|
| 30 |
+
|
| 31 |
+
# The prompt functions to use. The doc tools currently override these
|
| 32 |
+
# functions to customize how they work.
|
| 33 |
+
visible_prompt_func: t.Callable[[str], str] = input
|
| 34 |
+
|
| 35 |
+
_ansi_colors = {
|
| 36 |
+
"black": 30,
|
| 37 |
+
"red": 31,
|
| 38 |
+
"green": 32,
|
| 39 |
+
"yellow": 33,
|
| 40 |
+
"blue": 34,
|
| 41 |
+
"magenta": 35,
|
| 42 |
+
"cyan": 36,
|
| 43 |
+
"white": 37,
|
| 44 |
+
"reset": 39,
|
| 45 |
+
"bright_black": 90,
|
| 46 |
+
"bright_red": 91,
|
| 47 |
+
"bright_green": 92,
|
| 48 |
+
"bright_yellow": 93,
|
| 49 |
+
"bright_blue": 94,
|
| 50 |
+
"bright_magenta": 95,
|
| 51 |
+
"bright_cyan": 96,
|
| 52 |
+
"bright_white": 97,
|
| 53 |
+
}
|
| 54 |
+
_ansi_reset_all = "\033[0m"
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
_HIDDEN_INPUT_MASK = "'***'"
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def _mask_hidden_input(message: str, value: str) -> str:
|
| 61 |
+
"""Replace occurrences of ``value`` in ``message`` with a fixed mask.
|
| 62 |
+
|
| 63 |
+
Both ``repr(value)`` (the form built-in :class:`ParamType` errors use
|
| 64 |
+
via ``{value!r}``) and the raw value are masked. The raw-value pass
|
| 65 |
+
uses word-boundary lookarounds so a substring like ``"1"`` does not
|
| 66 |
+
match inside ``"10"``, and ``"ent"`` does not match inside
|
| 67 |
+
``"Authentication"``. The empty string is skipped to avoid matching
|
| 68 |
+
at every boundary.
|
| 69 |
+
"""
|
| 70 |
+
message = message.replace(repr(value), _HIDDEN_INPUT_MASK)
|
| 71 |
+
if value:
|
| 72 |
+
message = re.sub(
|
| 73 |
+
rf"(?<!\w){re.escape(value)}(?!\w)", _HIDDEN_INPUT_MASK, message
|
| 74 |
+
)
|
| 75 |
+
return message
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def hidden_prompt_func(prompt: str) -> str:
|
| 79 |
+
import getpass
|
| 80 |
+
|
| 81 |
+
return getpass.getpass(prompt)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _readline_prompt(func: t.Callable[[str], str], text: str, err: bool) -> str:
|
| 85 |
+
"""Call a prompt function, passing the full prompt on non-Windows so
|
| 86 |
+
readline can handle line editing and cursor positioning correctly.
|
| 87 |
+
|
| 88 |
+
On Windows the prompt is written separately via :func:`echo` for
|
| 89 |
+
colorama support, with only the last character passed to *func*.
|
| 90 |
+
"""
|
| 91 |
+
if WIN:
|
| 92 |
+
# Write the prompt separately so that we get nice coloring
|
| 93 |
+
# through colorama on Windows.
|
| 94 |
+
echo(text[:-1], nl=False, err=err)
|
| 95 |
+
# Echo the last character to stdout to work around an issue
|
| 96 |
+
# where readline causes backspace to clear the whole line.
|
| 97 |
+
return func(text[-1:])
|
| 98 |
+
if err:
|
| 99 |
+
with redirect_stdout(sys.stderr):
|
| 100 |
+
return func(text)
|
| 101 |
+
return func(text)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def _build_prompt(
|
| 105 |
+
text: str,
|
| 106 |
+
suffix: str,
|
| 107 |
+
show_default: bool | str = False,
|
| 108 |
+
default: t.Any | None = None,
|
| 109 |
+
show_choices: bool = True,
|
| 110 |
+
type: ParamType[t.Any] | None = None,
|
| 111 |
+
) -> str:
|
| 112 |
+
prompt = text
|
| 113 |
+
if type is not None and show_choices and isinstance(type, Choice):
|
| 114 |
+
prompt += f" ({', '.join(map(str, type.choices))})"
|
| 115 |
+
if isinstance(show_default, str):
|
| 116 |
+
default = f"({show_default})"
|
| 117 |
+
if default is not None and show_default:
|
| 118 |
+
prompt = f"{prompt} [{_format_default(default)}]"
|
| 119 |
+
return f"{prompt}{suffix}"
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def _format_default(default: t.Any) -> t.Any:
|
| 123 |
+
if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"):
|
| 124 |
+
return default.name
|
| 125 |
+
|
| 126 |
+
return default
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def prompt(
|
| 130 |
+
text: str,
|
| 131 |
+
default: t.Any | None = None,
|
| 132 |
+
hide_input: bool = False,
|
| 133 |
+
confirmation_prompt: bool | str = False,
|
| 134 |
+
type: ParamType[t.Any] | t.Any | None = None,
|
| 135 |
+
value_proc: t.Callable[[str], t.Any] | None = None,
|
| 136 |
+
prompt_suffix: str = ": ",
|
| 137 |
+
show_default: bool | str = True,
|
| 138 |
+
err: bool = False,
|
| 139 |
+
show_choices: bool = True,
|
| 140 |
+
) -> t.Any:
|
| 141 |
+
"""Prompts a user for input. This is a convenience function that can
|
| 142 |
+
be used to prompt a user for input later.
|
| 143 |
+
|
| 144 |
+
If the user aborts the input by sending an interrupt signal, this
|
| 145 |
+
function will catch it and raise a :exc:`Abort` exception.
|
| 146 |
+
|
| 147 |
+
:param text: the text to show for the prompt.
|
| 148 |
+
:param default: the default value to use if no input happens. If this
|
| 149 |
+
is not given it will prompt until it's aborted.
|
| 150 |
+
:param hide_input: if this is set to true then the input value will
|
| 151 |
+
be hidden.
|
| 152 |
+
:param confirmation_prompt: Prompt a second time to confirm the
|
| 153 |
+
value. Can be set to a string instead of ``True`` to customize
|
| 154 |
+
the message.
|
| 155 |
+
:param type: the type to use to check the value against.
|
| 156 |
+
:param value_proc: if this parameter is provided it's a function that
|
| 157 |
+
is invoked instead of the type conversion to
|
| 158 |
+
convert a value.
|
| 159 |
+
:param prompt_suffix: a suffix that should be added to the prompt.
|
| 160 |
+
:param show_default: shows or hides the default value in the prompt.
|
| 161 |
+
If this value is a string, it shows that string
|
| 162 |
+
in parentheses instead of the actual value.
|
| 163 |
+
:param err: if set to true the file defaults to ``stderr`` instead of
|
| 164 |
+
``stdout``, the same as with echo.
|
| 165 |
+
:param show_choices: Show or hide choices if the passed type is a Choice.
|
| 166 |
+
For example if type is a Choice of either day or week,
|
| 167 |
+
show_choices is true and text is "Group by" then the
|
| 168 |
+
prompt will be "Group by (day, week): ".
|
| 169 |
+
|
| 170 |
+
.. versionchanged:: 8.3.3
|
| 171 |
+
``show_default`` can be a string to show a custom value instead
|
| 172 |
+
of the actual default, matching the help text behavior.
|
| 173 |
+
|
| 174 |
+
.. versionchanged:: 8.3.1
|
| 175 |
+
A space is no longer appended to the prompt.
|
| 176 |
+
|
| 177 |
+
.. versionadded:: 8.0
|
| 178 |
+
``confirmation_prompt`` can be a custom string.
|
| 179 |
+
|
| 180 |
+
.. versionadded:: 7.0
|
| 181 |
+
Added the ``show_choices`` parameter.
|
| 182 |
+
|
| 183 |
+
.. versionadded:: 6.0
|
| 184 |
+
Added unicode support for cmd.exe on Windows.
|
| 185 |
+
|
| 186 |
+
.. versionadded:: 4.0
|
| 187 |
+
Added the `err` parameter.
|
| 188 |
+
|
| 189 |
+
"""
|
| 190 |
+
|
| 191 |
+
def prompt_func(text: str) -> str:
|
| 192 |
+
f = hidden_prompt_func if hide_input else visible_prompt_func
|
| 193 |
+
try:
|
| 194 |
+
return _readline_prompt(f, text, err)
|
| 195 |
+
except (KeyboardInterrupt, EOFError):
|
| 196 |
+
# getpass doesn't print a newline if the user aborts input with ^C.
|
| 197 |
+
# Allegedly this behavior is inherited from getpass(3).
|
| 198 |
+
# A doc bug has been filed at https://bugs.python.org/issue24711
|
| 199 |
+
if hide_input:
|
| 200 |
+
echo(None, err=err)
|
| 201 |
+
raise Abort() from None
|
| 202 |
+
|
| 203 |
+
if value_proc is None:
|
| 204 |
+
value_proc = convert_type(type, default)
|
| 205 |
+
|
| 206 |
+
prompt = _build_prompt(
|
| 207 |
+
text, prompt_suffix, show_default, default, show_choices, type
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
if confirmation_prompt:
|
| 211 |
+
if confirmation_prompt is True:
|
| 212 |
+
confirmation_prompt = _("Repeat for confirmation")
|
| 213 |
+
|
| 214 |
+
confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix)
|
| 215 |
+
|
| 216 |
+
while True:
|
| 217 |
+
while True:
|
| 218 |
+
value = prompt_func(prompt)
|
| 219 |
+
if value:
|
| 220 |
+
break
|
| 221 |
+
elif default is not None:
|
| 222 |
+
value = default
|
| 223 |
+
break
|
| 224 |
+
try:
|
| 225 |
+
result = value_proc(value)
|
| 226 |
+
except UsageError as e:
|
| 227 |
+
message = _mask_hidden_input(e.message, value) if hide_input else e.message
|
| 228 |
+
echo(_("Error: {message}").format(message=message), err=err)
|
| 229 |
+
continue
|
| 230 |
+
if not confirmation_prompt:
|
| 231 |
+
return result
|
| 232 |
+
while True:
|
| 233 |
+
value2 = prompt_func(confirmation_prompt)
|
| 234 |
+
is_empty = not value and not value2
|
| 235 |
+
if value2 or is_empty:
|
| 236 |
+
break
|
| 237 |
+
if value == value2:
|
| 238 |
+
return result
|
| 239 |
+
echo(_("Error: The two entered values do not match."), err=err)
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def confirm(
|
| 243 |
+
text: str,
|
| 244 |
+
default: bool | None = False,
|
| 245 |
+
abort: bool = False,
|
| 246 |
+
prompt_suffix: str = ": ",
|
| 247 |
+
show_default: bool = True,
|
| 248 |
+
err: bool = False,
|
| 249 |
+
) -> bool:
|
| 250 |
+
"""Prompts for confirmation (yes/no question).
|
| 251 |
+
|
| 252 |
+
If the user aborts the input by sending a interrupt signal this
|
| 253 |
+
function will catch it and raise a :exc:`Abort` exception.
|
| 254 |
+
|
| 255 |
+
:param text: the question to ask.
|
| 256 |
+
:param default: The default value to use when no input is given. If
|
| 257 |
+
``None``, repeat until input is given.
|
| 258 |
+
:param abort: if this is set to `True` a negative answer aborts the
|
| 259 |
+
exception by raising :exc:`Abort`.
|
| 260 |
+
:param prompt_suffix: a suffix that should be added to the prompt.
|
| 261 |
+
:param show_default: shows or hides the default value in the prompt.
|
| 262 |
+
:param err: if set to true the file defaults to ``stderr`` instead of
|
| 263 |
+
``stdout``, the same as with echo.
|
| 264 |
+
|
| 265 |
+
.. versionchanged:: 8.3.1
|
| 266 |
+
A space is no longer appended to the prompt.
|
| 267 |
+
|
| 268 |
+
.. versionchanged:: 8.0
|
| 269 |
+
Repeat until input is given if ``default`` is ``None``.
|
| 270 |
+
|
| 271 |
+
.. versionadded:: 4.0
|
| 272 |
+
Added the ``err`` parameter.
|
| 273 |
+
"""
|
| 274 |
+
prompt = _build_prompt(
|
| 275 |
+
text,
|
| 276 |
+
prompt_suffix,
|
| 277 |
+
show_default,
|
| 278 |
+
"y/n" if default is None else ("Y/n" if default else "y/N"),
|
| 279 |
+
)
|
| 280 |
+
|
| 281 |
+
while True:
|
| 282 |
+
try:
|
| 283 |
+
value = _readline_prompt(visible_prompt_func, prompt, err).lower().strip()
|
| 284 |
+
except (KeyboardInterrupt, EOFError):
|
| 285 |
+
raise Abort() from None
|
| 286 |
+
if value in ("y", "yes"):
|
| 287 |
+
rv = True
|
| 288 |
+
elif value in ("n", "no"):
|
| 289 |
+
rv = False
|
| 290 |
+
elif default is not None and value == "":
|
| 291 |
+
rv = default
|
| 292 |
+
else:
|
| 293 |
+
echo(_("Error: invalid input"), err=err)
|
| 294 |
+
continue
|
| 295 |
+
break
|
| 296 |
+
if abort and not rv:
|
| 297 |
+
raise Abort()
|
| 298 |
+
return rv
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def get_pager_file(
|
| 302 |
+
color: bool | None = None,
|
| 303 |
+
) -> t.ContextManager[t.TextIO]:
|
| 304 |
+
"""Context manager.
|
| 305 |
+
|
| 306 |
+
Yields a writable file-like object which can be used as an output pager.
|
| 307 |
+
|
| 308 |
+
.. versionadded:: 8.4.0
|
| 309 |
+
|
| 310 |
+
:param color: controls if the pager supports ANSI colors or not. The
|
| 311 |
+
default is autodetection.
|
| 312 |
+
"""
|
| 313 |
+
from ._termui_impl import get_pager_file
|
| 314 |
+
|
| 315 |
+
color = resolve_color_default(color)
|
| 316 |
+
|
| 317 |
+
return get_pager_file(color=color)
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
def echo_via_pager(
|
| 321 |
+
text_or_generator: cabc.Iterable[str] | t.Callable[[], cabc.Iterable[str]] | str,
|
| 322 |
+
color: bool | None = None,
|
| 323 |
+
) -> None:
|
| 324 |
+
"""This function takes a text and shows it via an environment specific
|
| 325 |
+
pager on stdout.
|
| 326 |
+
|
| 327 |
+
.. versionchanged:: 3.0
|
| 328 |
+
Added the `color` flag.
|
| 329 |
+
|
| 330 |
+
:param text_or_generator: the text to page, or alternatively, a
|
| 331 |
+
generator emitting the text to page.
|
| 332 |
+
:param color: controls if the pager supports ANSI colors or not. The
|
| 333 |
+
default is autodetection.
|
| 334 |
+
"""
|
| 335 |
+
|
| 336 |
+
if inspect.isgeneratorfunction(text_or_generator):
|
| 337 |
+
i = t.cast("t.Callable[[], cabc.Iterable[str]]", text_or_generator)()
|
| 338 |
+
elif isinstance(text_or_generator, str):
|
| 339 |
+
i = [text_or_generator]
|
| 340 |
+
else:
|
| 341 |
+
i = iter(t.cast("cabc.Iterable[str]", text_or_generator))
|
| 342 |
+
|
| 343 |
+
# convert every element of i to a text type if necessary
|
| 344 |
+
text_generator = (el if isinstance(el, str) else str(el) for el in i)
|
| 345 |
+
|
| 346 |
+
with get_pager_file(color=color) as pager:
|
| 347 |
+
for text in itertools.chain(text_generator, "\n"):
|
| 348 |
+
pager.write(text)
|
| 349 |
+
# Flush after each write so a slow generator streams to the pager
|
| 350 |
+
# incrementally rather than staying invisible until the pipe buffer
|
| 351 |
+
# fills (~8 KB).
|
| 352 |
+
pager.flush()
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
@t.overload
|
| 356 |
+
def progressbar(
|
| 357 |
+
*,
|
| 358 |
+
length: int,
|
| 359 |
+
label: str | None = None,
|
| 360 |
+
hidden: bool = False,
|
| 361 |
+
show_eta: bool = True,
|
| 362 |
+
show_percent: bool | None = None,
|
| 363 |
+
show_pos: bool = False,
|
| 364 |
+
fill_char: str = "#",
|
| 365 |
+
empty_char: str = "-",
|
| 366 |
+
bar_template: str = "%(label)s [%(bar)s] %(info)s",
|
| 367 |
+
info_sep: str = " ",
|
| 368 |
+
width: int = 36,
|
| 369 |
+
file: t.TextIO | None = None,
|
| 370 |
+
color: bool | None = None,
|
| 371 |
+
update_min_steps: int = 1,
|
| 372 |
+
) -> ProgressBar[int]: ...
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
@t.overload
|
| 376 |
+
def progressbar(
|
| 377 |
+
iterable: cabc.Iterable[V] | None = None,
|
| 378 |
+
length: int | None = None,
|
| 379 |
+
label: str | None = None,
|
| 380 |
+
hidden: bool = False,
|
| 381 |
+
show_eta: bool = True,
|
| 382 |
+
show_percent: bool | None = None,
|
| 383 |
+
show_pos: bool = False,
|
| 384 |
+
item_show_func: t.Callable[[V | None], str | None] | None = None,
|
| 385 |
+
fill_char: str = "#",
|
| 386 |
+
empty_char: str = "-",
|
| 387 |
+
bar_template: str = "%(label)s [%(bar)s] %(info)s",
|
| 388 |
+
info_sep: str = " ",
|
| 389 |
+
width: int = 36,
|
| 390 |
+
file: t.TextIO | None = None,
|
| 391 |
+
color: bool | None = None,
|
| 392 |
+
update_min_steps: int = 1,
|
| 393 |
+
) -> ProgressBar[V]: ...
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
def progressbar(
|
| 397 |
+
iterable: cabc.Iterable[V] | None = None,
|
| 398 |
+
length: int | None = None,
|
| 399 |
+
label: str | None = None,
|
| 400 |
+
hidden: bool = False,
|
| 401 |
+
show_eta: bool = True,
|
| 402 |
+
show_percent: bool | None = None,
|
| 403 |
+
show_pos: bool = False,
|
| 404 |
+
item_show_func: t.Callable[[V | None], str | None] | None = None,
|
| 405 |
+
fill_char: str = "#",
|
| 406 |
+
empty_char: str = "-",
|
| 407 |
+
bar_template: str = "%(label)s [%(bar)s] %(info)s",
|
| 408 |
+
info_sep: str = " ",
|
| 409 |
+
width: int = 36,
|
| 410 |
+
file: t.TextIO | None = None,
|
| 411 |
+
color: bool | None = None,
|
| 412 |
+
update_min_steps: int = 1,
|
| 413 |
+
) -> ProgressBar[V]:
|
| 414 |
+
"""This function creates an iterable context manager that can be used
|
| 415 |
+
to iterate over something while showing a progress bar. It will
|
| 416 |
+
either iterate over the `iterable` or `length` items (that are counted
|
| 417 |
+
up). While iteration happens, this function will print a rendered
|
| 418 |
+
progress bar to the given `file` (defaults to stdout) and will attempt
|
| 419 |
+
to calculate remaining time and more. By default, this progress bar
|
| 420 |
+
will not be rendered if the file is not a terminal.
|
| 421 |
+
|
| 422 |
+
The context manager creates the progress bar. When the context
|
| 423 |
+
manager is entered the progress bar is already created. With every
|
| 424 |
+
iteration over the progress bar, the iterable passed to the bar is
|
| 425 |
+
advanced and the bar is updated. When the context manager exits,
|
| 426 |
+
a newline is printed and the progress bar is finalized on screen.
|
| 427 |
+
|
| 428 |
+
Note: The progress bar is currently designed for use cases where the
|
| 429 |
+
total progress can be expected to take at least several seconds.
|
| 430 |
+
Because of this, the ProgressBar class object won't display
|
| 431 |
+
progress that is considered too fast, and progress where the time
|
| 432 |
+
between steps is less than a second.
|
| 433 |
+
|
| 434 |
+
No printing must happen or the progress bar will be unintentionally
|
| 435 |
+
destroyed.
|
| 436 |
+
|
| 437 |
+
Example usage::
|
| 438 |
+
|
| 439 |
+
with progressbar(items) as bar:
|
| 440 |
+
for item in bar:
|
| 441 |
+
do_something_with(item)
|
| 442 |
+
|
| 443 |
+
Alternatively, if no iterable is specified, one can manually update the
|
| 444 |
+
progress bar through the `update()` method instead of directly
|
| 445 |
+
iterating over the progress bar. The update method accepts the number
|
| 446 |
+
of steps to increment the bar with::
|
| 447 |
+
|
| 448 |
+
with progressbar(length=chunks.total_bytes) as bar:
|
| 449 |
+
for chunk in chunks:
|
| 450 |
+
process_chunk(chunk)
|
| 451 |
+
bar.update(chunks.bytes)
|
| 452 |
+
|
| 453 |
+
The ``update()`` method also takes an optional value specifying the
|
| 454 |
+
``current_item`` at the new position. This is useful when used
|
| 455 |
+
together with ``item_show_func`` to customize the output for each
|
| 456 |
+
manual step::
|
| 457 |
+
|
| 458 |
+
with click.progressbar(
|
| 459 |
+
length=total_size,
|
| 460 |
+
label='Unzipping archive',
|
| 461 |
+
item_show_func=lambda a: a.filename
|
| 462 |
+
) as bar:
|
| 463 |
+
for archive in zip_file:
|
| 464 |
+
archive.extract()
|
| 465 |
+
bar.update(archive.size, archive)
|
| 466 |
+
|
| 467 |
+
:param iterable: an iterable to iterate over. If not provided the length
|
| 468 |
+
is required.
|
| 469 |
+
:param length: the number of items to iterate over. By default the
|
| 470 |
+
progressbar will attempt to ask the iterator about its
|
| 471 |
+
length, which might or might not work. If an iterable is
|
| 472 |
+
also provided this parameter can be used to override the
|
| 473 |
+
length. If an iterable is not provided the progress bar
|
| 474 |
+
will iterate over a range of that length.
|
| 475 |
+
:param label: the label to show next to the progress bar.
|
| 476 |
+
:param hidden: hide the progressbar. Defaults to ``False``. When no tty is
|
| 477 |
+
detected, it will only print the progressbar label. Setting this to
|
| 478 |
+
``False`` also disables that.
|
| 479 |
+
:param show_eta: enables or disables the estimated time display. This is
|
| 480 |
+
automatically disabled if the length cannot be
|
| 481 |
+
determined.
|
| 482 |
+
:param show_percent: enables or disables the percentage display. The
|
| 483 |
+
default is `True` if the iterable has a length or
|
| 484 |
+
`False` if not.
|
| 485 |
+
:param show_pos: enables or disables the absolute position display. The
|
| 486 |
+
default is `False`.
|
| 487 |
+
:param item_show_func: A function called with the current item which
|
| 488 |
+
can return a string to show next to the progress bar. If the
|
| 489 |
+
function returns ``None`` nothing is shown. The current item can
|
| 490 |
+
be ``None``, such as when entering and exiting the bar.
|
| 491 |
+
:param fill_char: the character to use to show the filled part of the
|
| 492 |
+
progress bar.
|
| 493 |
+
:param empty_char: the character to use to show the non-filled part of
|
| 494 |
+
the progress bar.
|
| 495 |
+
:param bar_template: the format string to use as template for the bar.
|
| 496 |
+
The parameters in it are ``label`` for the label,
|
| 497 |
+
``bar`` for the progress bar and ``info`` for the
|
| 498 |
+
info section.
|
| 499 |
+
:param info_sep: the separator between multiple info items (eta etc.)
|
| 500 |
+
:param width: the width of the progress bar in characters, 0 means full
|
| 501 |
+
terminal width
|
| 502 |
+
:param file: The file to write to. If this is not a terminal then
|
| 503 |
+
only the label is printed.
|
| 504 |
+
:param color: controls if the terminal supports ANSI colors or not. The
|
| 505 |
+
default is autodetection. This is only needed if ANSI
|
| 506 |
+
codes are included anywhere in the progress bar output
|
| 507 |
+
which is not the case by default.
|
| 508 |
+
:param update_min_steps: Render only when this many updates have
|
| 509 |
+
completed. This allows tuning for very fast iterators.
|
| 510 |
+
|
| 511 |
+
.. versionadded:: 8.2
|
| 512 |
+
The ``hidden`` argument.
|
| 513 |
+
|
| 514 |
+
.. versionchanged:: 8.0
|
| 515 |
+
Output is shown even if execution time is less than 0.5 seconds.
|
| 516 |
+
|
| 517 |
+
.. versionchanged:: 8.0
|
| 518 |
+
``item_show_func`` shows the current item, not the previous one.
|
| 519 |
+
|
| 520 |
+
.. versionchanged:: 8.0
|
| 521 |
+
Labels are echoed if the output is not a TTY. Reverts a change
|
| 522 |
+
in 7.0 that removed all output.
|
| 523 |
+
|
| 524 |
+
.. versionadded:: 8.0
|
| 525 |
+
The ``update_min_steps`` parameter.
|
| 526 |
+
|
| 527 |
+
.. versionadded:: 4.0
|
| 528 |
+
The ``color`` parameter and ``update`` method.
|
| 529 |
+
|
| 530 |
+
.. versionadded:: 2.0
|
| 531 |
+
"""
|
| 532 |
+
from ._termui_impl import ProgressBar
|
| 533 |
+
|
| 534 |
+
color = resolve_color_default(color)
|
| 535 |
+
return ProgressBar(
|
| 536 |
+
iterable=iterable,
|
| 537 |
+
length=length,
|
| 538 |
+
hidden=hidden,
|
| 539 |
+
show_eta=show_eta,
|
| 540 |
+
show_percent=show_percent,
|
| 541 |
+
show_pos=show_pos,
|
| 542 |
+
item_show_func=item_show_func,
|
| 543 |
+
fill_char=fill_char,
|
| 544 |
+
empty_char=empty_char,
|
| 545 |
+
bar_template=bar_template,
|
| 546 |
+
info_sep=info_sep,
|
| 547 |
+
file=file,
|
| 548 |
+
label=label,
|
| 549 |
+
width=width,
|
| 550 |
+
color=color,
|
| 551 |
+
update_min_steps=update_min_steps,
|
| 552 |
+
)
|
| 553 |
+
|
| 554 |
+
|
| 555 |
+
def clear() -> None:
|
| 556 |
+
"""Clears the terminal screen. This will have the effect of clearing
|
| 557 |
+
the whole visible space of the terminal and moving the cursor to the
|
| 558 |
+
top left. This does not do anything if not connected to a terminal.
|
| 559 |
+
|
| 560 |
+
.. versionadded:: 2.0
|
| 561 |
+
"""
|
| 562 |
+
if not isatty(sys.stdout):
|
| 563 |
+
return
|
| 564 |
+
|
| 565 |
+
# ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor
|
| 566 |
+
echo("\033[2J\033[1;1H", nl=False)
|
| 567 |
+
|
| 568 |
+
|
| 569 |
+
def _interpret_color(color: int | tuple[int, int, int] | str, offset: int = 0) -> str:
|
| 570 |
+
if isinstance(color, int):
|
| 571 |
+
return f"{38 + offset};5;{color:d}"
|
| 572 |
+
|
| 573 |
+
if isinstance(color, (tuple, list)):
|
| 574 |
+
r, g, b = color
|
| 575 |
+
return f"{38 + offset};2;{r:d};{g:d};{b:d}"
|
| 576 |
+
|
| 577 |
+
return str(_ansi_colors[color] + offset)
|
| 578 |
+
|
| 579 |
+
|
| 580 |
+
def style(
|
| 581 |
+
text: t.Any,
|
| 582 |
+
fg: int | tuple[int, int, int] | str | None = None,
|
| 583 |
+
bg: int | tuple[int, int, int] | str | None = None,
|
| 584 |
+
bold: bool | None = None,
|
| 585 |
+
dim: bool | None = None,
|
| 586 |
+
underline: bool | None = None,
|
| 587 |
+
overline: bool | None = None,
|
| 588 |
+
italic: bool | None = None,
|
| 589 |
+
blink: bool | None = None,
|
| 590 |
+
reverse: bool | None = None,
|
| 591 |
+
strikethrough: bool | None = None,
|
| 592 |
+
reset: bool = True,
|
| 593 |
+
) -> str:
|
| 594 |
+
"""Styles a text with ANSI styles and returns the new string. By
|
| 595 |
+
default the styling is self contained which means that at the end
|
| 596 |
+
of the string a reset code is issued. This can be prevented by
|
| 597 |
+
passing ``reset=False``.
|
| 598 |
+
|
| 599 |
+
Examples::
|
| 600 |
+
|
| 601 |
+
click.echo(click.style('Hello World!', fg='green'))
|
| 602 |
+
click.echo(click.style('ATTENTION!', blink=True))
|
| 603 |
+
click.echo(click.style('Some things', reverse=True, fg='cyan'))
|
| 604 |
+
click.echo(click.style('More colors', fg=(255, 12, 128), bg=117))
|
| 605 |
+
|
| 606 |
+
Supported color names:
|
| 607 |
+
|
| 608 |
+
* ``black`` (might be a gray)
|
| 609 |
+
* ``red``
|
| 610 |
+
* ``green``
|
| 611 |
+
* ``yellow`` (might be an orange)
|
| 612 |
+
* ``blue``
|
| 613 |
+
* ``magenta``
|
| 614 |
+
* ``cyan``
|
| 615 |
+
* ``white`` (might be light gray)
|
| 616 |
+
* ``bright_black``
|
| 617 |
+
* ``bright_red``
|
| 618 |
+
* ``bright_green``
|
| 619 |
+
* ``bright_yellow``
|
| 620 |
+
* ``bright_blue``
|
| 621 |
+
* ``bright_magenta``
|
| 622 |
+
* ``bright_cyan``
|
| 623 |
+
* ``bright_white``
|
| 624 |
+
* ``reset`` (reset the color code only)
|
| 625 |
+
|
| 626 |
+
If the terminal supports it, color may also be specified as:
|
| 627 |
+
|
| 628 |
+
- An integer in the interval [0, 255]. The terminal must support
|
| 629 |
+
8-bit/256-color mode.
|
| 630 |
+
- An RGB tuple of three integers in [0, 255]. The terminal must
|
| 631 |
+
support 24-bit/true-color mode.
|
| 632 |
+
|
| 633 |
+
See https://en.wikipedia.org/wiki/ANSI_color and
|
| 634 |
+
https://gist.github.com/XVilka/8346728 for more information.
|
| 635 |
+
|
| 636 |
+
:param text: the string to style with ansi codes.
|
| 637 |
+
:param fg: if provided this will become the foreground color.
|
| 638 |
+
:param bg: if provided this will become the background color.
|
| 639 |
+
:param bold: if provided this will enable or disable bold mode.
|
| 640 |
+
:param dim: if provided this will enable or disable dim mode. This is
|
| 641 |
+
badly supported.
|
| 642 |
+
:param underline: if provided this will enable or disable underline.
|
| 643 |
+
:param overline: if provided this will enable or disable overline.
|
| 644 |
+
:param italic: if provided this will enable or disable italic.
|
| 645 |
+
:param blink: if provided this will enable or disable blinking.
|
| 646 |
+
:param reverse: if provided this will enable or disable inverse
|
| 647 |
+
rendering (foreground becomes background and the
|
| 648 |
+
other way round).
|
| 649 |
+
:param strikethrough: if provided this will enable or disable
|
| 650 |
+
striking through text.
|
| 651 |
+
:param reset: by default a reset-all code is added at the end of the
|
| 652 |
+
string which means that styles do not carry over. This
|
| 653 |
+
can be disabled to compose styles.
|
| 654 |
+
|
| 655 |
+
.. versionchanged:: 8.0
|
| 656 |
+
A non-string ``message`` is converted to a string.
|
| 657 |
+
|
| 658 |
+
.. versionchanged:: 8.0
|
| 659 |
+
Added support for 256 and RGB color codes.
|
| 660 |
+
|
| 661 |
+
.. versionchanged:: 8.0
|
| 662 |
+
Added the ``strikethrough``, ``italic``, and ``overline``
|
| 663 |
+
parameters.
|
| 664 |
+
|
| 665 |
+
.. versionchanged:: 7.0
|
| 666 |
+
Added support for bright colors.
|
| 667 |
+
|
| 668 |
+
.. versionadded:: 2.0
|
| 669 |
+
"""
|
| 670 |
+
if not isinstance(text, str):
|
| 671 |
+
text = str(text)
|
| 672 |
+
|
| 673 |
+
bits = []
|
| 674 |
+
|
| 675 |
+
if fg:
|
| 676 |
+
try:
|
| 677 |
+
bits.append(f"\033[{_interpret_color(fg)}m")
|
| 678 |
+
except KeyError:
|
| 679 |
+
raise TypeError(_("Unknown color {colour!r}").format(colour=fg)) from None
|
| 680 |
+
|
| 681 |
+
if bg:
|
| 682 |
+
try:
|
| 683 |
+
bits.append(f"\033[{_interpret_color(bg, 10)}m")
|
| 684 |
+
except KeyError:
|
| 685 |
+
raise TypeError(_("Unknown color {colour!r}").format(colour=bg)) from None
|
| 686 |
+
|
| 687 |
+
if bold is not None:
|
| 688 |
+
bits.append(f"\033[{1 if bold else 22}m")
|
| 689 |
+
if dim is not None:
|
| 690 |
+
bits.append(f"\033[{2 if dim else 22}m")
|
| 691 |
+
if underline is not None:
|
| 692 |
+
bits.append(f"\033[{4 if underline else 24}m")
|
| 693 |
+
if overline is not None:
|
| 694 |
+
bits.append(f"\033[{53 if overline else 55}m")
|
| 695 |
+
if italic is not None:
|
| 696 |
+
bits.append(f"\033[{3 if italic else 23}m")
|
| 697 |
+
if blink is not None:
|
| 698 |
+
bits.append(f"\033[{5 if blink else 25}m")
|
| 699 |
+
if reverse is not None:
|
| 700 |
+
bits.append(f"\033[{7 if reverse else 27}m")
|
| 701 |
+
if strikethrough is not None:
|
| 702 |
+
bits.append(f"\033[{9 if strikethrough else 29}m")
|
| 703 |
+
bits.append(text)
|
| 704 |
+
if reset:
|
| 705 |
+
bits.append(_ansi_reset_all)
|
| 706 |
+
return "".join(bits)
|
| 707 |
+
|
| 708 |
+
|
| 709 |
+
def unstyle(text: str) -> str:
|
| 710 |
+
"""Removes ANSI styling information from a string. Usually it's not
|
| 711 |
+
necessary to use this function as Click's echo function will
|
| 712 |
+
automatically remove styling if necessary.
|
| 713 |
+
|
| 714 |
+
.. versionadded:: 2.0
|
| 715 |
+
|
| 716 |
+
:param text: the text to remove style information from.
|
| 717 |
+
"""
|
| 718 |
+
return strip_ansi(text)
|
| 719 |
+
|
| 720 |
+
|
| 721 |
+
def secho(
|
| 722 |
+
message: t.Any | None = None,
|
| 723 |
+
file: t.IO[t.AnyStr] | None = None,
|
| 724 |
+
nl: bool = True,
|
| 725 |
+
err: bool = False,
|
| 726 |
+
color: bool | None = None,
|
| 727 |
+
**styles: t.Any,
|
| 728 |
+
) -> None:
|
| 729 |
+
"""This function combines :func:`echo` and :func:`style` into one
|
| 730 |
+
call. As such the following two calls are the same::
|
| 731 |
+
|
| 732 |
+
click.secho('Hello World!', fg='green')
|
| 733 |
+
click.echo(click.style('Hello World!', fg='green'))
|
| 734 |
+
|
| 735 |
+
All keyword arguments are forwarded to the underlying functions
|
| 736 |
+
depending on which one they go with.
|
| 737 |
+
|
| 738 |
+
Non-string types will be converted to :class:`str`. However,
|
| 739 |
+
:class:`bytes` are passed directly to :meth:`echo` without applying
|
| 740 |
+
style. If you want to style bytes that represent text, call
|
| 741 |
+
:meth:`bytes.decode` first.
|
| 742 |
+
|
| 743 |
+
.. versionchanged:: 8.0
|
| 744 |
+
A non-string ``message`` is converted to a string. Bytes are
|
| 745 |
+
passed through without style applied.
|
| 746 |
+
|
| 747 |
+
.. versionadded:: 2.0
|
| 748 |
+
"""
|
| 749 |
+
if message is not None and not isinstance(message, (bytes, bytearray)):
|
| 750 |
+
message = style(message, **styles)
|
| 751 |
+
|
| 752 |
+
return echo(message, file=file, nl=nl, err=err, color=color)
|
| 753 |
+
|
| 754 |
+
|
| 755 |
+
@t.overload
|
| 756 |
+
def edit(
|
| 757 |
+
text: bytes | bytearray,
|
| 758 |
+
editor: str | None = None,
|
| 759 |
+
env: cabc.Mapping[str, str] | None = None,
|
| 760 |
+
require_save: bool = False,
|
| 761 |
+
extension: str = ".txt",
|
| 762 |
+
) -> bytes | None: ...
|
| 763 |
+
|
| 764 |
+
|
| 765 |
+
@t.overload
|
| 766 |
+
def edit(
|
| 767 |
+
text: str,
|
| 768 |
+
editor: str | None = None,
|
| 769 |
+
env: cabc.Mapping[str, str] | None = None,
|
| 770 |
+
require_save: bool = True,
|
| 771 |
+
extension: str = ".txt",
|
| 772 |
+
) -> str | None: ...
|
| 773 |
+
|
| 774 |
+
|
| 775 |
+
@t.overload
|
| 776 |
+
def edit(
|
| 777 |
+
text: None = None,
|
| 778 |
+
editor: str | None = None,
|
| 779 |
+
env: cabc.Mapping[str, str] | None = None,
|
| 780 |
+
require_save: bool = True,
|
| 781 |
+
extension: str = ".txt",
|
| 782 |
+
filename: str | cabc.Iterable[str] | None = None,
|
| 783 |
+
) -> None: ...
|
| 784 |
+
|
| 785 |
+
|
| 786 |
+
def edit(
|
| 787 |
+
text: str | bytes | bytearray | None = None,
|
| 788 |
+
editor: str | None = None,
|
| 789 |
+
env: cabc.Mapping[str, str] | None = None,
|
| 790 |
+
require_save: bool = True,
|
| 791 |
+
extension: str = ".txt",
|
| 792 |
+
filename: str | cabc.Iterable[str] | None = None,
|
| 793 |
+
) -> str | bytes | bytearray | None:
|
| 794 |
+
r"""Edits the given text in the defined editor. If an editor is given
|
| 795 |
+
(should be the full path to the executable but the regular operating
|
| 796 |
+
system search path is used for finding the executable) it overrides
|
| 797 |
+
the detected editor. Optionally, some environment variables can be
|
| 798 |
+
used. If the editor is closed without changes, `None` is returned. In
|
| 799 |
+
case a file is edited directly the return value is always `None` and
|
| 800 |
+
`require_save` and `extension` are ignored.
|
| 801 |
+
|
| 802 |
+
If the editor cannot be opened a :exc:`UsageError` is raised.
|
| 803 |
+
|
| 804 |
+
Note for Windows: to simplify cross-platform usage, the newlines are
|
| 805 |
+
automatically converted from POSIX to Windows and vice versa. As such,
|
| 806 |
+
the message here will have ``\n`` as newline markers.
|
| 807 |
+
|
| 808 |
+
:param text: the text to edit.
|
| 809 |
+
:param editor: optionally the editor to use. Defaults to automatic
|
| 810 |
+
detection.
|
| 811 |
+
:param env: environment variables to forward to the editor.
|
| 812 |
+
:param require_save: if this is true, then not saving in the editor
|
| 813 |
+
will make the return value become `None`.
|
| 814 |
+
:param extension: the extension to tell the editor about. This defaults
|
| 815 |
+
to `.txt` but changing this might change syntax
|
| 816 |
+
highlighting.
|
| 817 |
+
:param filename: if provided it will edit this file instead of the
|
| 818 |
+
provided text contents. It will not use a temporary
|
| 819 |
+
file as an indirection in that case. If the editor supports
|
| 820 |
+
editing multiple files at once, a sequence of files may be
|
| 821 |
+
passed as well. Invoke `click.file` once per file instead
|
| 822 |
+
if multiple files cannot be managed at once or editing the
|
| 823 |
+
files serially is desired.
|
| 824 |
+
|
| 825 |
+
.. versionchanged:: 8.2.0
|
| 826 |
+
``filename`` now accepts any ``Iterable[str]`` in addition to a ``str``
|
| 827 |
+
if the ``editor`` supports editing multiple files at once.
|
| 828 |
+
|
| 829 |
+
"""
|
| 830 |
+
from ._termui_impl import Editor
|
| 831 |
+
|
| 832 |
+
ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension)
|
| 833 |
+
|
| 834 |
+
if filename is None:
|
| 835 |
+
return ed.edit(text)
|
| 836 |
+
|
| 837 |
+
if isinstance(filename, str):
|
| 838 |
+
filename = (filename,)
|
| 839 |
+
|
| 840 |
+
ed.edit_files(filenames=filename)
|
| 841 |
+
return None
|
| 842 |
+
|
| 843 |
+
|
| 844 |
+
def launch(url: str, wait: bool = False, locate: bool = False) -> int:
|
| 845 |
+
"""This function launches the given URL (or filename) in the default
|
| 846 |
+
viewer application for this file type. If this is an executable, it
|
| 847 |
+
might launch the executable in a new session. The return value is
|
| 848 |
+
the exit code of the launched application. Usually, ``0`` indicates
|
| 849 |
+
success.
|
| 850 |
+
|
| 851 |
+
Examples::
|
| 852 |
+
|
| 853 |
+
click.launch('https://click.palletsprojects.com/')
|
| 854 |
+
click.launch('/my/downloaded/file', locate=True)
|
| 855 |
+
|
| 856 |
+
.. versionadded:: 2.0
|
| 857 |
+
|
| 858 |
+
:param url: URL or filename of the thing to launch.
|
| 859 |
+
:param wait: Wait for the program to exit before returning. This
|
| 860 |
+
only works if the launched program blocks. In particular,
|
| 861 |
+
``xdg-open`` on Linux does not block.
|
| 862 |
+
:param locate: if this is set to `True` then instead of launching the
|
| 863 |
+
application associated with the URL it will attempt to
|
| 864 |
+
launch a file manager with the file located. This
|
| 865 |
+
might have weird effects if the URL does not point to
|
| 866 |
+
the filesystem.
|
| 867 |
+
"""
|
| 868 |
+
from ._termui_impl import open_url
|
| 869 |
+
|
| 870 |
+
return open_url(url, wait=wait, locate=locate)
|
| 871 |
+
|
| 872 |
+
|
| 873 |
+
# If this is provided, getchar() calls into this instead. This is used
|
| 874 |
+
# for unittesting purposes.
|
| 875 |
+
_getchar: t.Callable[[bool], str] | None = None
|
| 876 |
+
|
| 877 |
+
|
| 878 |
+
def getchar(echo: bool = False) -> str:
|
| 879 |
+
"""Fetches a single character from the terminal and returns it. This
|
| 880 |
+
will always return a unicode character and under certain rare
|
| 881 |
+
circumstances this might return more than one character. The
|
| 882 |
+
situations which more than one character is returned is when for
|
| 883 |
+
whatever reason multiple characters end up in the terminal buffer or
|
| 884 |
+
standard input was not actually a terminal.
|
| 885 |
+
|
| 886 |
+
Note that this will always read from the terminal, even if something
|
| 887 |
+
is piped into the standard input.
|
| 888 |
+
|
| 889 |
+
Note for Windows: in rare cases when typing non-ASCII characters, this
|
| 890 |
+
function might wait for a second character and then return both at once.
|
| 891 |
+
This is because certain Unicode characters look like special-key markers.
|
| 892 |
+
|
| 893 |
+
.. versionadded:: 2.0
|
| 894 |
+
|
| 895 |
+
:param echo: if set to `True`, the character read will also show up on
|
| 896 |
+
the terminal. The default is to not show it.
|
| 897 |
+
"""
|
| 898 |
+
global _getchar
|
| 899 |
+
|
| 900 |
+
if _getchar is None:
|
| 901 |
+
from ._termui_impl import getchar as f
|
| 902 |
+
|
| 903 |
+
_getchar = f
|
| 904 |
+
|
| 905 |
+
return _getchar(echo)
|
| 906 |
+
|
| 907 |
+
|
| 908 |
+
def raw_terminal() -> AbstractContextManager[int]:
|
| 909 |
+
from ._termui_impl import raw_terminal as f
|
| 910 |
+
|
| 911 |
+
return f()
|
| 912 |
+
|
| 913 |
+
|
| 914 |
+
def pause(info: str | None = None, err: bool = False) -> None:
|
| 915 |
+
"""This command stops execution and waits for the user to press any
|
| 916 |
+
key to continue. This is similar to the Windows batch "pause"
|
| 917 |
+
command. If the program is not run through a terminal, this command
|
| 918 |
+
will instead do nothing.
|
| 919 |
+
|
| 920 |
+
.. versionadded:: 2.0
|
| 921 |
+
|
| 922 |
+
.. versionadded:: 4.0
|
| 923 |
+
Added the `err` parameter.
|
| 924 |
+
|
| 925 |
+
:param info: The message to print before pausing. Defaults to
|
| 926 |
+
``"Press any key to continue..."``.
|
| 927 |
+
:param err: if set to message goes to ``stderr`` instead of
|
| 928 |
+
``stdout``, the same as with echo.
|
| 929 |
+
"""
|
| 930 |
+
if not isatty(sys.stdin) or not isatty(sys.stdout):
|
| 931 |
+
return
|
| 932 |
+
|
| 933 |
+
if info is None:
|
| 934 |
+
info = _("Press any key to continue...")
|
| 935 |
+
|
| 936 |
+
try:
|
| 937 |
+
if info:
|
| 938 |
+
echo(info, nl=False, err=err)
|
| 939 |
+
try:
|
| 940 |
+
getchar()
|
| 941 |
+
except (KeyboardInterrupt, EOFError):
|
| 942 |
+
pass
|
| 943 |
+
finally:
|
| 944 |
+
if info:
|
| 945 |
+
echo(err=err)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/testing.py
ADDED
|
@@ -0,0 +1,772 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections.abc as cabc
|
| 4 |
+
import contextlib
|
| 5 |
+
import io
|
| 6 |
+
import os
|
| 7 |
+
import pdb
|
| 8 |
+
import shlex
|
| 9 |
+
import sys
|
| 10 |
+
import tempfile
|
| 11 |
+
import typing as t
|
| 12 |
+
from types import TracebackType
|
| 13 |
+
|
| 14 |
+
from . import _compat
|
| 15 |
+
from . import formatting
|
| 16 |
+
from . import termui
|
| 17 |
+
from . import utils
|
| 18 |
+
from ._compat import _find_binary_reader
|
| 19 |
+
|
| 20 |
+
if t.TYPE_CHECKING:
|
| 21 |
+
from _typeshed import ReadableBuffer
|
| 22 |
+
|
| 23 |
+
from .core import Command
|
| 24 |
+
|
| 25 |
+
if sys.platform == "win32":
|
| 26 |
+
CaptureMode: t.TypeAlias = t.Literal["sys"] # pyright: ignore[reportRedeclaration]
|
| 27 |
+
else:
|
| 28 |
+
CaptureMode: t.TypeAlias = t.Literal["sys", "fd"] # pyright: ignore[reportRedeclaration]
|
| 29 |
+
ExceptionInfo: t.TypeAlias = tuple[type[BaseException], BaseException, TracebackType]
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class EchoingStdin:
|
| 33 |
+
_input: t.BinaryIO
|
| 34 |
+
_output: t.BinaryIO
|
| 35 |
+
_paused: bool
|
| 36 |
+
|
| 37 |
+
def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None:
|
| 38 |
+
self._input = input
|
| 39 |
+
self._output = output
|
| 40 |
+
self._paused = False
|
| 41 |
+
|
| 42 |
+
def __getattr__(self, x: str) -> t.Any:
|
| 43 |
+
return getattr(self._input, x)
|
| 44 |
+
|
| 45 |
+
def _echo(self, rv: bytes) -> bytes:
|
| 46 |
+
if not self._paused:
|
| 47 |
+
self._output.write(rv)
|
| 48 |
+
|
| 49 |
+
return rv
|
| 50 |
+
|
| 51 |
+
def read(self, n: int = -1) -> bytes:
|
| 52 |
+
return self._echo(self._input.read(n))
|
| 53 |
+
|
| 54 |
+
def read1(self, n: int = -1) -> bytes:
|
| 55 |
+
return self._echo(self._input.read1(n)) # type: ignore
|
| 56 |
+
|
| 57 |
+
def readline(self, n: int = -1) -> bytes:
|
| 58 |
+
return self._echo(self._input.readline(n))
|
| 59 |
+
|
| 60 |
+
def readlines(self) -> list[bytes]:
|
| 61 |
+
return [self._echo(x) for x in self._input.readlines()]
|
| 62 |
+
|
| 63 |
+
def __iter__(self) -> cabc.Iterator[bytes]:
|
| 64 |
+
return iter(self._echo(x) for x in self._input)
|
| 65 |
+
|
| 66 |
+
def __repr__(self) -> str:
|
| 67 |
+
return repr(self._input)
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
@contextlib.contextmanager
|
| 71 |
+
def _pause_echo(stream: EchoingStdin | None) -> cabc.Generator[None]:
|
| 72 |
+
if stream is None:
|
| 73 |
+
yield
|
| 74 |
+
else:
|
| 75 |
+
stream._paused = True
|
| 76 |
+
yield
|
| 77 |
+
stream._paused = False
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class _FDCapture:
|
| 81 |
+
"""Redirect a file descriptor to a temporary file for capture.
|
| 82 |
+
|
| 83 |
+
Saves the current target of *targetfd* via :func:`os.dup`, then
|
| 84 |
+
redirects it to a temporary file via :func:`os.dup2`. On
|
| 85 |
+
:meth:`stop`, restores the original ``fd`` and returns the captured
|
| 86 |
+
bytes. Inspired by Pytest's ``FDCapture``.
|
| 87 |
+
|
| 88 |
+
.. versionadded:: 8.4.0
|
| 89 |
+
"""
|
| 90 |
+
|
| 91 |
+
_targetfd: int
|
| 92 |
+
saved_fd: int
|
| 93 |
+
_tmpfile: t.BinaryIO | None
|
| 94 |
+
|
| 95 |
+
def __init__(self, targetfd: int) -> None:
|
| 96 |
+
self._targetfd = targetfd
|
| 97 |
+
self.saved_fd = -1
|
| 98 |
+
self._tmpfile = None
|
| 99 |
+
|
| 100 |
+
def start(self) -> None:
|
| 101 |
+
self.saved_fd = os.dup(self._targetfd)
|
| 102 |
+
self._tmpfile = tempfile.TemporaryFile(buffering=0)
|
| 103 |
+
os.dup2(self._tmpfile.fileno(), self._targetfd)
|
| 104 |
+
|
| 105 |
+
def stop(self) -> bytes:
|
| 106 |
+
assert self._tmpfile is not None, "_FDCapture.start() was not called"
|
| 107 |
+
os.dup2(self.saved_fd, self._targetfd)
|
| 108 |
+
os.close(self.saved_fd)
|
| 109 |
+
self.saved_fd = -1
|
| 110 |
+
self._tmpfile.seek(0)
|
| 111 |
+
data = self._tmpfile.read()
|
| 112 |
+
self._tmpfile.close()
|
| 113 |
+
self._tmpfile = None
|
| 114 |
+
return data
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
class BytesIOCopy(io.BytesIO):
|
| 118 |
+
"""Patch ``io.BytesIO`` to let the written stream be copied to another.
|
| 119 |
+
|
| 120 |
+
.. versionadded:: 8.2
|
| 121 |
+
"""
|
| 122 |
+
|
| 123 |
+
copy_to: io.BytesIO
|
| 124 |
+
|
| 125 |
+
def __init__(self, copy_to: io.BytesIO) -> None:
|
| 126 |
+
super().__init__()
|
| 127 |
+
self.copy_to = copy_to
|
| 128 |
+
|
| 129 |
+
def flush(self) -> None:
|
| 130 |
+
super().flush()
|
| 131 |
+
self.copy_to.flush()
|
| 132 |
+
|
| 133 |
+
def write(self, b: ReadableBuffer) -> int:
|
| 134 |
+
self.copy_to.write(b)
|
| 135 |
+
return super().write(b)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class StreamMixer:
|
| 139 |
+
"""Mixes `<stdout>` and `<stderr>` streams.
|
| 140 |
+
|
| 141 |
+
The result is available in the ``output`` attribute.
|
| 142 |
+
|
| 143 |
+
.. versionadded:: 8.2
|
| 144 |
+
"""
|
| 145 |
+
|
| 146 |
+
output: io.BytesIO
|
| 147 |
+
stdout: BytesIOCopy
|
| 148 |
+
stderr: BytesIOCopy
|
| 149 |
+
|
| 150 |
+
def __init__(self) -> None:
|
| 151 |
+
self.output = io.BytesIO()
|
| 152 |
+
self.stdout = BytesIOCopy(copy_to=self.output)
|
| 153 |
+
self.stderr = BytesIOCopy(copy_to=self.output)
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class _NamedTextIOWrapper(io.TextIOWrapper):
|
| 157 |
+
"""A :class:`~io.TextIOWrapper` with custom ``name`` and ``mode``
|
| 158 |
+
that does not close its underlying buffer.
|
| 159 |
+
|
| 160 |
+
When ``CliRunner`` runs in ``fd`` mode, ``_original_fd`` is patched to
|
| 161 |
+
point at the saved (pre-redirection) ``fd``, so C-level consumers that call
|
| 162 |
+
:meth:`fileno` (like ``faulthandler`` or ``subprocess``) keep working. In
|
| 163 |
+
the default ``sys`` mode ``_original_fd`` stays at ``-1`` and
|
| 164 |
+
:meth:`fileno` raises :exc:`io.UnsupportedOperation`, matching the
|
| 165 |
+
pre-``8.3.3`` behavior.
|
| 166 |
+
"""
|
| 167 |
+
|
| 168 |
+
_name: str
|
| 169 |
+
_mode: str
|
| 170 |
+
_original_fd: int
|
| 171 |
+
|
| 172 |
+
def __init__(
|
| 173 |
+
self,
|
| 174 |
+
buffer: t.BinaryIO,
|
| 175 |
+
name: str,
|
| 176 |
+
mode: str,
|
| 177 |
+
**kwargs: t.Any,
|
| 178 |
+
) -> None:
|
| 179 |
+
super().__init__(buffer, **kwargs)
|
| 180 |
+
self._name = name
|
| 181 |
+
self._mode = mode
|
| 182 |
+
self._original_fd = -1
|
| 183 |
+
|
| 184 |
+
def close(self) -> None:
|
| 185 |
+
"""The buffer this object contains belongs to some other object,
|
| 186 |
+
so prevent the default ``__del__`` implementation from closing
|
| 187 |
+
that buffer.
|
| 188 |
+
|
| 189 |
+
.. versionadded:: 8.3.2
|
| 190 |
+
"""
|
| 191 |
+
|
| 192 |
+
def fileno(self) -> int:
|
| 193 |
+
"""Return the file descriptor of the saved original stream when
|
| 194 |
+
``CliRunner`` runs in ``fd`` mode. Otherwise delegate to
|
| 195 |
+
:class:`~io.TextIOWrapper`, which raises
|
| 196 |
+
:exc:`io.UnsupportedOperation` for a ``BytesIO``-backed buffer.
|
| 197 |
+
"""
|
| 198 |
+
if self._original_fd >= 0:
|
| 199 |
+
return self._original_fd
|
| 200 |
+
return super().fileno()
|
| 201 |
+
|
| 202 |
+
@property
|
| 203 |
+
def name(self) -> str:
|
| 204 |
+
return self._name
|
| 205 |
+
|
| 206 |
+
@property
|
| 207 |
+
def mode(self) -> str:
|
| 208 |
+
return self._mode
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def make_input_stream(
|
| 212 |
+
input: str | bytes | t.IO[t.Any] | None, charset: str
|
| 213 |
+
) -> t.BinaryIO:
|
| 214 |
+
# Is already an input stream.
|
| 215 |
+
if hasattr(input, "read"):
|
| 216 |
+
rv = _find_binary_reader(t.cast("t.IO[t.Any]", input))
|
| 217 |
+
|
| 218 |
+
if rv is not None:
|
| 219 |
+
return rv
|
| 220 |
+
|
| 221 |
+
raise TypeError("Could not find binary reader for input stream.")
|
| 222 |
+
|
| 223 |
+
if input is None:
|
| 224 |
+
input = b""
|
| 225 |
+
elif isinstance(input, str):
|
| 226 |
+
input = input.encode(charset)
|
| 227 |
+
|
| 228 |
+
return io.BytesIO(input)
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
class Result:
|
| 232 |
+
"""Holds the captured result of an invoked CLI script.
|
| 233 |
+
|
| 234 |
+
:param runner: The runner that created the result
|
| 235 |
+
:param stdout_bytes: The standard output as bytes.
|
| 236 |
+
:param stderr_bytes: The standard error as bytes.
|
| 237 |
+
:param output_bytes: A mix of ``stdout_bytes`` and ``stderr_bytes``, as the
|
| 238 |
+
user would see it in its terminal.
|
| 239 |
+
:param return_value: The value returned from the invoked command.
|
| 240 |
+
:param exit_code: The exit code as integer.
|
| 241 |
+
:param exception: The exception that happened if one did.
|
| 242 |
+
:param exc_info: Exception information (exception type, exception instance,
|
| 243 |
+
traceback type).
|
| 244 |
+
|
| 245 |
+
.. versionchanged:: 8.2
|
| 246 |
+
``stderr_bytes`` no longer optional, ``output_bytes`` introduced and
|
| 247 |
+
``mix_stderr`` has been removed.
|
| 248 |
+
|
| 249 |
+
.. versionadded:: 8.0
|
| 250 |
+
Added ``return_value``.
|
| 251 |
+
"""
|
| 252 |
+
|
| 253 |
+
runner: CliRunner
|
| 254 |
+
stdout_bytes: bytes
|
| 255 |
+
stderr_bytes: bytes
|
| 256 |
+
output_bytes: bytes
|
| 257 |
+
return_value: t.Any
|
| 258 |
+
exit_code: int
|
| 259 |
+
exception: BaseException | None
|
| 260 |
+
exc_info: ExceptionInfo | None
|
| 261 |
+
|
| 262 |
+
def __init__(
|
| 263 |
+
self,
|
| 264 |
+
runner: CliRunner,
|
| 265 |
+
stdout_bytes: bytes,
|
| 266 |
+
stderr_bytes: bytes,
|
| 267 |
+
output_bytes: bytes,
|
| 268 |
+
return_value: t.Any,
|
| 269 |
+
exit_code: int,
|
| 270 |
+
exception: BaseException | None,
|
| 271 |
+
exc_info: ExceptionInfo | None = None,
|
| 272 |
+
) -> None:
|
| 273 |
+
self.runner = runner
|
| 274 |
+
self.stdout_bytes = stdout_bytes
|
| 275 |
+
self.stderr_bytes = stderr_bytes
|
| 276 |
+
self.output_bytes = output_bytes
|
| 277 |
+
self.return_value = return_value
|
| 278 |
+
self.exit_code = exit_code
|
| 279 |
+
self.exception = exception
|
| 280 |
+
self.exc_info = exc_info
|
| 281 |
+
|
| 282 |
+
@property
|
| 283 |
+
def output(self) -> str:
|
| 284 |
+
"""The terminal output as unicode string, as the user would see it.
|
| 285 |
+
|
| 286 |
+
.. versionchanged:: 8.2
|
| 287 |
+
No longer a proxy for ``self.stdout``. Now has its own independent stream
|
| 288 |
+
that is mixing `<stdout>` and `<stderr>`, in the order they were written.
|
| 289 |
+
"""
|
| 290 |
+
return self.output_bytes.decode(self.runner.charset, "replace").replace(
|
| 291 |
+
"\r\n", "\n"
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
@property
|
| 295 |
+
def stdout(self) -> str:
|
| 296 |
+
"""The standard output as unicode string."""
|
| 297 |
+
return self.stdout_bytes.decode(self.runner.charset, "replace").replace(
|
| 298 |
+
"\r\n", "\n"
|
| 299 |
+
)
|
| 300 |
+
|
| 301 |
+
@property
|
| 302 |
+
def stderr(self) -> str:
|
| 303 |
+
"""The standard error as unicode string.
|
| 304 |
+
|
| 305 |
+
.. versionchanged:: 8.2
|
| 306 |
+
No longer raise an exception, always returns the `<stderr>` string.
|
| 307 |
+
"""
|
| 308 |
+
return self.stderr_bytes.decode(self.runner.charset, "replace").replace(
|
| 309 |
+
"\r\n", "\n"
|
| 310 |
+
)
|
| 311 |
+
|
| 312 |
+
def __repr__(self) -> str:
|
| 313 |
+
exc_str = repr(self.exception) if self.exception else "okay"
|
| 314 |
+
return f"<{type(self).__name__} {exc_str}>"
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
class CliRunner:
|
| 318 |
+
"""The CLI runner provides functionality to invoke a Click command line
|
| 319 |
+
script for unittesting purposes in a isolated environment. This only
|
| 320 |
+
works in single-threaded systems without any concurrency as it changes the
|
| 321 |
+
global interpreter state.
|
| 322 |
+
|
| 323 |
+
:param charset: the character set for the input and output data.
|
| 324 |
+
:param env: a dictionary with environment variables for overriding.
|
| 325 |
+
:param echo_stdin: if this is set to `True`, then reading from `<stdin>` writes
|
| 326 |
+
to `<stdout>`. This is useful for showing examples in
|
| 327 |
+
some circumstances. Note that regular prompts
|
| 328 |
+
will automatically echo the input.
|
| 329 |
+
:param catch_exceptions: Whether to catch any exceptions other than
|
| 330 |
+
``SystemExit`` when running :meth:`~CliRunner.invoke`.
|
| 331 |
+
:param capture: Selects the output capture strategy. ``sys`` (default)
|
| 332 |
+
captures Python-level writes only and leaves
|
| 333 |
+
:meth:`sys.stdout.fileno` raising :exc:`io.UnsupportedOperation`, so
|
| 334 |
+
user code that calls :func:`os.dup2` on ``sys.stdout.fileno()`` cannot
|
| 335 |
+
clobber the host runner's stdout. ``fd`` redirects file descriptors
|
| 336 |
+
``1`` and ``2`` via :func:`os.dup2` to a temporary file, also catching
|
| 337 |
+
output from stale stream references, C extensions, and subprocesses.
|
| 338 |
+
``fd`` is not supported on Windows.
|
| 339 |
+
|
| 340 |
+
.. versionchanged:: 8.4.0
|
| 341 |
+
Added the ``capture`` parameter. The default ``sys`` mode no longer
|
| 342 |
+
exposes the original fd through :meth:`fileno`, reverting the change
|
| 343 |
+
introduced in ``8.3.3`` that broke Pytest's ``fd``-level capture
|
| 344 |
+
teardown. Use ``capture="fd"`` to restore that behavior with proper
|
| 345 |
+
isolation. :issue:`3384`
|
| 346 |
+
|
| 347 |
+
.. versionchanged:: 8.2
|
| 348 |
+
Added the ``catch_exceptions`` parameter.
|
| 349 |
+
|
| 350 |
+
.. versionchanged:: 8.2
|
| 351 |
+
``mix_stderr`` parameter has been removed.
|
| 352 |
+
"""
|
| 353 |
+
|
| 354 |
+
charset: str
|
| 355 |
+
env: cabc.Mapping[str, str | None]
|
| 356 |
+
echo_stdin: bool
|
| 357 |
+
catch_exceptions: bool
|
| 358 |
+
capture: CaptureMode
|
| 359 |
+
|
| 360 |
+
def __init__(
|
| 361 |
+
self,
|
| 362 |
+
charset: str = "utf-8",
|
| 363 |
+
env: cabc.Mapping[str, str | None] | None = None,
|
| 364 |
+
echo_stdin: bool = False,
|
| 365 |
+
catch_exceptions: bool = True,
|
| 366 |
+
capture: CaptureMode = "sys",
|
| 367 |
+
) -> None:
|
| 368 |
+
if capture not in {"sys", "fd"}:
|
| 369 |
+
raise ValueError(
|
| 370 |
+
f"capture={capture!r} is not valid. Choose from 'sys' or 'fd'."
|
| 371 |
+
)
|
| 372 |
+
if capture == "fd" and sys.platform == "win32":
|
| 373 |
+
raise ValueError(
|
| 374 |
+
f"capture={capture!r} is not supported on Windows. Use 'sys'."
|
| 375 |
+
)
|
| 376 |
+
self.charset = charset
|
| 377 |
+
self.env = env or {}
|
| 378 |
+
self.echo_stdin = echo_stdin
|
| 379 |
+
self.catch_exceptions = catch_exceptions
|
| 380 |
+
self.capture = capture
|
| 381 |
+
|
| 382 |
+
def get_default_prog_name(self, cli: Command) -> str:
|
| 383 |
+
"""Given a command object it will return the default program name
|
| 384 |
+
for it. The default is the `name` attribute or ``"root"`` if not
|
| 385 |
+
set.
|
| 386 |
+
"""
|
| 387 |
+
return cli.name or "root"
|
| 388 |
+
|
| 389 |
+
def make_env(
|
| 390 |
+
self, overrides: cabc.Mapping[str, str | None] | None = None
|
| 391 |
+
) -> cabc.Mapping[str, str | None]:
|
| 392 |
+
"""Returns the environment overrides for invoking a script."""
|
| 393 |
+
rv = dict(self.env)
|
| 394 |
+
if overrides:
|
| 395 |
+
rv.update(overrides)
|
| 396 |
+
return rv
|
| 397 |
+
|
| 398 |
+
@contextlib.contextmanager
|
| 399 |
+
def isolation(
|
| 400 |
+
self,
|
| 401 |
+
input: str | bytes | t.IO[t.Any] | None = None,
|
| 402 |
+
env: cabc.Mapping[str, str | None] | None = None,
|
| 403 |
+
color: bool = False,
|
| 404 |
+
) -> cabc.Generator[tuple[io.BytesIO, io.BytesIO, io.BytesIO]]:
|
| 405 |
+
"""A context manager that sets up the isolation for invoking of a
|
| 406 |
+
command line tool. This sets up `<stdin>` with the given input data
|
| 407 |
+
and `os.environ` with the overrides from the given dictionary.
|
| 408 |
+
This also rebinds some internals in Click to be mocked (like the
|
| 409 |
+
prompt functionality).
|
| 410 |
+
|
| 411 |
+
This is automatically done in the :meth:`invoke` method.
|
| 412 |
+
|
| 413 |
+
:param input: the input stream to put into `sys.stdin`.
|
| 414 |
+
:param env: the environment overrides as dictionary.
|
| 415 |
+
:param color: whether the output should contain color codes. The
|
| 416 |
+
application can still override this explicitly.
|
| 417 |
+
|
| 418 |
+
.. versionadded:: 8.2
|
| 419 |
+
An additional output stream is returned, which is a mix of
|
| 420 |
+
`<stdout>` and `<stderr>` streams.
|
| 421 |
+
|
| 422 |
+
.. versionchanged:: 8.2
|
| 423 |
+
Always returns the `<stderr>` stream.
|
| 424 |
+
|
| 425 |
+
.. versionchanged:: 8.0
|
| 426 |
+
`<stderr>` is opened with ``errors="backslashreplace"``
|
| 427 |
+
instead of the default ``"strict"``.
|
| 428 |
+
|
| 429 |
+
.. versionchanged:: 4.0
|
| 430 |
+
Added the ``color`` parameter.
|
| 431 |
+
"""
|
| 432 |
+
bytes_input = make_input_stream(input, self.charset)
|
| 433 |
+
echo_input = None
|
| 434 |
+
|
| 435 |
+
old_stdin = sys.stdin
|
| 436 |
+
old_stdout = sys.stdout
|
| 437 |
+
old_stderr = sys.stderr
|
| 438 |
+
old_forced_width = formatting.FORCED_WIDTH
|
| 439 |
+
formatting.FORCED_WIDTH = 80
|
| 440 |
+
|
| 441 |
+
env = self.make_env(env)
|
| 442 |
+
|
| 443 |
+
stream_mixer = StreamMixer()
|
| 444 |
+
|
| 445 |
+
if self.echo_stdin:
|
| 446 |
+
bytes_input = echo_input = t.cast(
|
| 447 |
+
t.BinaryIO, EchoingStdin(bytes_input, stream_mixer.stdout)
|
| 448 |
+
)
|
| 449 |
+
|
| 450 |
+
sys.stdin = text_input = _NamedTextIOWrapper(
|
| 451 |
+
bytes_input, encoding=self.charset, name="<stdin>", mode="r"
|
| 452 |
+
)
|
| 453 |
+
|
| 454 |
+
if self.echo_stdin:
|
| 455 |
+
# Force unbuffered reads, otherwise TextIOWrapper reads a
|
| 456 |
+
# large chunk which is echoed early.
|
| 457 |
+
text_input._CHUNK_SIZE = 1 # type: ignore
|
| 458 |
+
|
| 459 |
+
sys.stdout = _NamedTextIOWrapper(
|
| 460 |
+
stream_mixer.stdout,
|
| 461 |
+
encoding=self.charset,
|
| 462 |
+
name="<stdout>",
|
| 463 |
+
mode="w",
|
| 464 |
+
)
|
| 465 |
+
|
| 466 |
+
sys.stderr = _NamedTextIOWrapper(
|
| 467 |
+
stream_mixer.stderr,
|
| 468 |
+
encoding=self.charset,
|
| 469 |
+
name="<stderr>",
|
| 470 |
+
mode="w",
|
| 471 |
+
errors="backslashreplace",
|
| 472 |
+
)
|
| 473 |
+
|
| 474 |
+
@_pause_echo(echo_input) # type: ignore
|
| 475 |
+
def visible_input(prompt: str | None = None) -> str:
|
| 476 |
+
sys.stdout.write(prompt or "")
|
| 477 |
+
try:
|
| 478 |
+
val = next(text_input).rstrip("\r\n")
|
| 479 |
+
except StopIteration as e:
|
| 480 |
+
raise EOFError() from e
|
| 481 |
+
sys.stdout.write(f"{val}\n")
|
| 482 |
+
sys.stdout.flush()
|
| 483 |
+
return val
|
| 484 |
+
|
| 485 |
+
@_pause_echo(echo_input) # type: ignore
|
| 486 |
+
def hidden_input(prompt: str | None = None) -> str:
|
| 487 |
+
sys.stdout.write(f"{prompt or ''}\n")
|
| 488 |
+
sys.stdout.flush()
|
| 489 |
+
try:
|
| 490 |
+
return next(text_input).rstrip("\r\n")
|
| 491 |
+
except StopIteration as e:
|
| 492 |
+
raise EOFError() from e
|
| 493 |
+
|
| 494 |
+
@_pause_echo(echo_input) # type: ignore
|
| 495 |
+
def _getchar(echo: bool) -> str:
|
| 496 |
+
char = sys.stdin.read(1)
|
| 497 |
+
|
| 498 |
+
if echo:
|
| 499 |
+
sys.stdout.write(char)
|
| 500 |
+
|
| 501 |
+
sys.stdout.flush()
|
| 502 |
+
return char
|
| 503 |
+
|
| 504 |
+
default_color = color
|
| 505 |
+
|
| 506 |
+
def should_strip_ansi(
|
| 507 |
+
stream: t.IO[t.Any] | None = None, color: bool | None = None
|
| 508 |
+
) -> bool:
|
| 509 |
+
if color is None:
|
| 510 |
+
return not default_color
|
| 511 |
+
return not color
|
| 512 |
+
|
| 513 |
+
old_visible_prompt_func = termui.visible_prompt_func
|
| 514 |
+
old_hidden_prompt_func = termui.hidden_prompt_func
|
| 515 |
+
old__getchar_func = termui._getchar
|
| 516 |
+
old_should_strip_ansi = utils.should_strip_ansi # type: ignore
|
| 517 |
+
old__compat_should_strip_ansi = _compat.should_strip_ansi
|
| 518 |
+
old_pdb_init = pdb.Pdb.__init__
|
| 519 |
+
termui.visible_prompt_func = visible_input
|
| 520 |
+
termui.hidden_prompt_func = hidden_input
|
| 521 |
+
termui._getchar = _getchar
|
| 522 |
+
utils.should_strip_ansi = should_strip_ansi # type: ignore
|
| 523 |
+
_compat.should_strip_ansi = should_strip_ansi
|
| 524 |
+
|
| 525 |
+
def _patched_pdb_init(
|
| 526 |
+
self: pdb.Pdb,
|
| 527 |
+
completekey: str = "tab",
|
| 528 |
+
stdin: t.IO[str] | None = None,
|
| 529 |
+
stdout: t.IO[str] | None = None,
|
| 530 |
+
**kwargs: t.Any,
|
| 531 |
+
) -> None:
|
| 532 |
+
"""Default ``pdb.Pdb`` to real terminal streams during
|
| 533 |
+
``CliRunner`` isolation.
|
| 534 |
+
|
| 535 |
+
Without this patch, ``pdb.Pdb.__init__`` inherits from
|
| 536 |
+
``cmd.Cmd`` which falls back to ``sys.stdin``/``sys.stdout``
|
| 537 |
+
when no explicit streams are provided. During isolation
|
| 538 |
+
those are ``BytesIO``-backed wrappers, so the debugger
|
| 539 |
+
reads from an empty buffer and writes to captured output,
|
| 540 |
+
making interactive debugging impossible.
|
| 541 |
+
|
| 542 |
+
By defaulting to ``sys.__stdin__``/``sys.__stdout__`` (the
|
| 543 |
+
original terminal streams Python preserves regardless of
|
| 544 |
+
redirection), debuggers can interact with the user while
|
| 545 |
+
``click.echo`` output is still captured normally.
|
| 546 |
+
|
| 547 |
+
This covers ``pdb.set_trace()``, ``breakpoint()``,
|
| 548 |
+
``pdb.post_mortem()``, and debuggers that subclass
|
| 549 |
+
``pdb.Pdb`` (ipdb, pdbpp). Explicit ``stdin``/``stdout``
|
| 550 |
+
arguments are honored and not overridden. Debuggers that
|
| 551 |
+
do not subclass ``pdb.Pdb`` (pudb, debugpy) are not
|
| 552 |
+
covered.
|
| 553 |
+
"""
|
| 554 |
+
if stdin is None:
|
| 555 |
+
stdin = sys.__stdin__
|
| 556 |
+
if stdout is None:
|
| 557 |
+
stdout = sys.__stdout__
|
| 558 |
+
old_pdb_init(
|
| 559 |
+
self, completekey=completekey, stdin=stdin, stdout=stdout, **kwargs
|
| 560 |
+
)
|
| 561 |
+
|
| 562 |
+
pdb.Pdb.__init__ = _patched_pdb_init # type: ignore[assignment]
|
| 563 |
+
|
| 564 |
+
old_env = {}
|
| 565 |
+
try:
|
| 566 |
+
for key, value in env.items():
|
| 567 |
+
old_env[key] = os.environ.get(key)
|
| 568 |
+
if value is None:
|
| 569 |
+
try:
|
| 570 |
+
del os.environ[key]
|
| 571 |
+
except Exception:
|
| 572 |
+
pass
|
| 573 |
+
else:
|
| 574 |
+
os.environ[key] = value
|
| 575 |
+
yield (stream_mixer.stdout, stream_mixer.stderr, stream_mixer.output)
|
| 576 |
+
finally:
|
| 577 |
+
for key, value in old_env.items():
|
| 578 |
+
if value is None:
|
| 579 |
+
try:
|
| 580 |
+
del os.environ[key]
|
| 581 |
+
except Exception:
|
| 582 |
+
pass
|
| 583 |
+
else:
|
| 584 |
+
os.environ[key] = value
|
| 585 |
+
sys.stdout = old_stdout
|
| 586 |
+
sys.stderr = old_stderr
|
| 587 |
+
sys.stdin = old_stdin
|
| 588 |
+
termui.visible_prompt_func = old_visible_prompt_func
|
| 589 |
+
termui.hidden_prompt_func = old_hidden_prompt_func
|
| 590 |
+
termui._getchar = old__getchar_func
|
| 591 |
+
utils.should_strip_ansi = old_should_strip_ansi # type: ignore
|
| 592 |
+
_compat.should_strip_ansi = old__compat_should_strip_ansi
|
| 593 |
+
formatting.FORCED_WIDTH = old_forced_width
|
| 594 |
+
pdb.Pdb.__init__ = old_pdb_init # type: ignore[method-assign]
|
| 595 |
+
|
| 596 |
+
def invoke(
|
| 597 |
+
self,
|
| 598 |
+
cli: Command,
|
| 599 |
+
args: str | cabc.Sequence[str] | None = None,
|
| 600 |
+
input: str | bytes | t.IO[t.Any] | None = None,
|
| 601 |
+
env: cabc.Mapping[str, str | None] | None = None,
|
| 602 |
+
catch_exceptions: bool | None = None,
|
| 603 |
+
color: bool = False,
|
| 604 |
+
**extra: t.Any,
|
| 605 |
+
) -> Result:
|
| 606 |
+
"""Invokes a command in an isolated environment. The arguments are
|
| 607 |
+
forwarded directly to the command line script, the `extra` keyword
|
| 608 |
+
arguments are passed to the :meth:`~clickpkg.Command.main` function of
|
| 609 |
+
the command.
|
| 610 |
+
|
| 611 |
+
This returns a :class:`Result` object.
|
| 612 |
+
|
| 613 |
+
:param cli: the command to invoke
|
| 614 |
+
:param args: the arguments to invoke. It may be given as an iterable
|
| 615 |
+
or a string. When given as string it will be interpreted
|
| 616 |
+
as a Unix shell command. More details at
|
| 617 |
+
:func:`shlex.split`.
|
| 618 |
+
:param input: the input data for `sys.stdin`.
|
| 619 |
+
:param env: the environment overrides.
|
| 620 |
+
:param catch_exceptions: Whether to catch any other exceptions than
|
| 621 |
+
``SystemExit``. If :data:`None`, the value
|
| 622 |
+
from :class:`CliRunner` is used.
|
| 623 |
+
:param extra: the keyword arguments to pass to :meth:`main`.
|
| 624 |
+
:param color: whether the output should contain color codes. The
|
| 625 |
+
application can still override this explicitly.
|
| 626 |
+
|
| 627 |
+
.. versionadded:: 8.2
|
| 628 |
+
The result object has the ``output_bytes`` attribute with
|
| 629 |
+
the mix of ``stdout_bytes`` and ``stderr_bytes``, as the user would
|
| 630 |
+
see it in its terminal.
|
| 631 |
+
|
| 632 |
+
.. versionchanged:: 8.2
|
| 633 |
+
The result object always returns the ``stderr_bytes`` stream.
|
| 634 |
+
|
| 635 |
+
.. versionchanged:: 8.0
|
| 636 |
+
The result object has the ``return_value`` attribute with
|
| 637 |
+
the value returned from the invoked command.
|
| 638 |
+
|
| 639 |
+
.. versionchanged:: 4.0
|
| 640 |
+
Added the ``color`` parameter.
|
| 641 |
+
|
| 642 |
+
.. versionchanged:: 3.0
|
| 643 |
+
Added the ``catch_exceptions`` parameter.
|
| 644 |
+
|
| 645 |
+
.. versionchanged:: 3.0
|
| 646 |
+
The result object has the ``exc_info`` attribute with the
|
| 647 |
+
traceback if available.
|
| 648 |
+
"""
|
| 649 |
+
exc_info = None
|
| 650 |
+
if catch_exceptions is None:
|
| 651 |
+
catch_exceptions = self.catch_exceptions
|
| 652 |
+
|
| 653 |
+
# Set up fd capture before isolation replaces sys.stdout and sys.stderr.
|
| 654 |
+
cap_out: _FDCapture | None = None
|
| 655 |
+
cap_err: _FDCapture | None = None
|
| 656 |
+
|
| 657 |
+
if self.capture == "fd":
|
| 658 |
+
cap_out = _FDCapture(1)
|
| 659 |
+
cap_err = _FDCapture(2)
|
| 660 |
+
try:
|
| 661 |
+
cap_out.start()
|
| 662 |
+
cap_err.start()
|
| 663 |
+
except OSError:
|
| 664 |
+
cap_out = cap_err = None
|
| 665 |
+
|
| 666 |
+
with self.isolation(input=input, env=env, color=color) as outstreams:
|
| 667 |
+
# Point the captured streams' fileno() at the saved (original)
|
| 668 |
+
# fd so that C-level consumers like faulthandler keep working
|
| 669 |
+
# while fd 1/2 are redirected to the capture tmpfile.
|
| 670 |
+
if cap_out is not None and cap_err is not None:
|
| 671 |
+
sys.stdout._original_fd = cap_out.saved_fd # type: ignore[union-attr]
|
| 672 |
+
sys.stderr._original_fd = cap_err.saved_fd # type: ignore[union-attr]
|
| 673 |
+
|
| 674 |
+
return_value = None
|
| 675 |
+
exception: BaseException | None = None
|
| 676 |
+
exit_code = 0
|
| 677 |
+
|
| 678 |
+
if isinstance(args, str):
|
| 679 |
+
args = shlex.split(args)
|
| 680 |
+
|
| 681 |
+
try:
|
| 682 |
+
prog_name = extra.pop("prog_name")
|
| 683 |
+
except KeyError:
|
| 684 |
+
prog_name = self.get_default_prog_name(cli)
|
| 685 |
+
|
| 686 |
+
try:
|
| 687 |
+
return_value = cli.main(args=args or (), prog_name=prog_name, **extra)
|
| 688 |
+
except SystemExit as e:
|
| 689 |
+
exc_info = sys.exc_info()
|
| 690 |
+
e_code = t.cast("int | t.Any | None", e.code)
|
| 691 |
+
|
| 692 |
+
if e_code is None:
|
| 693 |
+
e_code = 0
|
| 694 |
+
|
| 695 |
+
if e_code != 0:
|
| 696 |
+
exception = e
|
| 697 |
+
|
| 698 |
+
if not isinstance(e_code, int):
|
| 699 |
+
sys.stdout.write(str(e_code))
|
| 700 |
+
sys.stdout.write("\n")
|
| 701 |
+
e_code = 1
|
| 702 |
+
|
| 703 |
+
exit_code = e_code
|
| 704 |
+
|
| 705 |
+
except Exception as e:
|
| 706 |
+
if not catch_exceptions:
|
| 707 |
+
raise
|
| 708 |
+
exception = e
|
| 709 |
+
exit_code = 1
|
| 710 |
+
exc_info = sys.exc_info()
|
| 711 |
+
finally:
|
| 712 |
+
sys.stdout.flush()
|
| 713 |
+
sys.stderr.flush()
|
| 714 |
+
|
| 715 |
+
# Stop fd capture and merge the captured bytes into
|
| 716 |
+
# the stdout/stderr BytesIO streams. BytesIOCopy mirrors
|
| 717 |
+
# those writes into outstreams[2] automatically.
|
| 718 |
+
if cap_out is not None and cap_err is not None:
|
| 719 |
+
fd_out = cap_out.stop()
|
| 720 |
+
fd_err = cap_err.stop()
|
| 721 |
+
if fd_out:
|
| 722 |
+
outstreams[0].write(fd_out)
|
| 723 |
+
if fd_err:
|
| 724 |
+
outstreams[1].write(fd_err)
|
| 725 |
+
|
| 726 |
+
stdout = outstreams[0].getvalue()
|
| 727 |
+
stderr = outstreams[1].getvalue()
|
| 728 |
+
output = outstreams[2].getvalue()
|
| 729 |
+
|
| 730 |
+
return Result(
|
| 731 |
+
runner=self,
|
| 732 |
+
stdout_bytes=stdout,
|
| 733 |
+
stderr_bytes=stderr,
|
| 734 |
+
output_bytes=output,
|
| 735 |
+
return_value=return_value,
|
| 736 |
+
exit_code=exit_code,
|
| 737 |
+
exception=exception,
|
| 738 |
+
exc_info=exc_info, # type: ignore
|
| 739 |
+
)
|
| 740 |
+
|
| 741 |
+
@contextlib.contextmanager
|
| 742 |
+
def isolated_filesystem(
|
| 743 |
+
self, temp_dir: str | os.PathLike[str] | None = None
|
| 744 |
+
) -> cabc.Generator[str]:
|
| 745 |
+
"""A context manager that creates a temporary directory and
|
| 746 |
+
changes the current working directory to it. This isolates tests
|
| 747 |
+
that affect the contents of the CWD to prevent them from
|
| 748 |
+
interfering with each other.
|
| 749 |
+
|
| 750 |
+
:param temp_dir: Create the temporary directory under this
|
| 751 |
+
directory. If given, the created directory is not removed
|
| 752 |
+
when exiting.
|
| 753 |
+
|
| 754 |
+
.. versionchanged:: 8.0
|
| 755 |
+
Added the ``temp_dir`` parameter.
|
| 756 |
+
"""
|
| 757 |
+
cwd = os.getcwd()
|
| 758 |
+
dt = tempfile.mkdtemp(dir=temp_dir)
|
| 759 |
+
os.chdir(dt)
|
| 760 |
+
|
| 761 |
+
try:
|
| 762 |
+
yield dt
|
| 763 |
+
finally:
|
| 764 |
+
os.chdir(cwd)
|
| 765 |
+
|
| 766 |
+
if temp_dir is None:
|
| 767 |
+
import shutil
|
| 768 |
+
|
| 769 |
+
try:
|
| 770 |
+
shutil.rmtree(dt)
|
| 771 |
+
except OSError:
|
| 772 |
+
pass
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/types.py
ADDED
|
@@ -0,0 +1,1374 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import abc
|
| 4 |
+
import collections.abc as cabc
|
| 5 |
+
import enum
|
| 6 |
+
import os
|
| 7 |
+
import stat
|
| 8 |
+
import sys
|
| 9 |
+
import typing as t
|
| 10 |
+
import uuid
|
| 11 |
+
from datetime import datetime
|
| 12 |
+
from gettext import gettext as _
|
| 13 |
+
from gettext import ngettext
|
| 14 |
+
|
| 15 |
+
from ._compat import _get_argv_encoding
|
| 16 |
+
from ._compat import open_stream
|
| 17 |
+
from .exceptions import BadParameter
|
| 18 |
+
from .utils import format_filename
|
| 19 |
+
from .utils import LazyFile
|
| 20 |
+
from .utils import safecall
|
| 21 |
+
|
| 22 |
+
if t.TYPE_CHECKING:
|
| 23 |
+
import typing_extensions as te
|
| 24 |
+
|
| 25 |
+
from .core import Context
|
| 26 |
+
from .core import Parameter
|
| 27 |
+
from .shell_completion import CompletionItem
|
| 28 |
+
|
| 29 |
+
_ValueT = t.TypeVar("_ValueT")
|
| 30 |
+
_ValueT_contra = t.TypeVar("_ValueT_contra", contravariant=True)
|
| 31 |
+
_ValueT_co = t.TypeVar("_ValueT_co", covariant=True)
|
| 32 |
+
|
| 33 |
+
_FloatValueT = t.TypeVar("_FloatValueT", bound=float)
|
| 34 |
+
_FloatValueT_co = t.TypeVar("_FloatValueT_co", bound=float, covariant=True)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class ParamTypeInfoDict(t.TypedDict):
|
| 38 |
+
param_type: str
|
| 39 |
+
name: str
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class ParamType(t.Generic[_ValueT_co], abc.ABC):
|
| 43 |
+
"""Represents the type of a parameter. Validates and converts values
|
| 44 |
+
from the command line or Python into the correct type.
|
| 45 |
+
|
| 46 |
+
To implement a custom type, subclass and implement at least the
|
| 47 |
+
following:
|
| 48 |
+
|
| 49 |
+
- The :attr:`name` class attribute must be set.
|
| 50 |
+
- Calling an instance of the type with ``None`` must return
|
| 51 |
+
``None``. This is already implemented by default.
|
| 52 |
+
- :meth:`convert` must convert string values to the correct type.
|
| 53 |
+
- :meth:`convert` must accept values that are already the correct
|
| 54 |
+
type.
|
| 55 |
+
- It must be able to convert a value if the ``ctx`` and ``param``
|
| 56 |
+
arguments are ``None``. This can occur when converting prompt
|
| 57 |
+
input.
|
| 58 |
+
|
| 59 |
+
.. versionchanged:: 8.4.0
|
| 60 |
+
Now a generic abstract base class. Parameterize with the
|
| 61 |
+
converted value type (``ParamType[int]`` for an integer-returning
|
| 62 |
+
type) so that :meth:`convert` and downstream consumers carry the
|
| 63 |
+
narrowed return type.
|
| 64 |
+
"""
|
| 65 |
+
|
| 66 |
+
is_composite: t.ClassVar[bool] = False
|
| 67 |
+
arity: int = 1 # read-only
|
| 68 |
+
|
| 69 |
+
#: the descriptive name of this type
|
| 70 |
+
name: str
|
| 71 |
+
|
| 72 |
+
#: if a list of this type is expected and the value is pulled from a
|
| 73 |
+
#: string environment variable, this is what splits it up. `None`
|
| 74 |
+
#: means any whitespace. For all parameters the general rule is that
|
| 75 |
+
#: whitespace splits them up. The exception are paths and files which
|
| 76 |
+
#: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on
|
| 77 |
+
#: Windows).
|
| 78 |
+
envvar_list_splitter: t.ClassVar[str | None] = None
|
| 79 |
+
|
| 80 |
+
def to_info_dict(self) -> ParamTypeInfoDict:
|
| 81 |
+
"""Gather information that could be useful for a tool generating
|
| 82 |
+
user-facing documentation.
|
| 83 |
+
|
| 84 |
+
Use :meth:`click.Context.to_info_dict` to traverse the entire
|
| 85 |
+
CLI structure.
|
| 86 |
+
|
| 87 |
+
.. versionadded:: 8.0
|
| 88 |
+
"""
|
| 89 |
+
# The class name without the "ParamType" suffix.
|
| 90 |
+
param_type = type(self).__name__.partition("ParamType")[0]
|
| 91 |
+
param_type = param_type.partition("ParameterType")[0]
|
| 92 |
+
|
| 93 |
+
# Custom subclasses might not remember to set a name.
|
| 94 |
+
if hasattr(self, "name"):
|
| 95 |
+
name = self.name
|
| 96 |
+
else:
|
| 97 |
+
name = param_type
|
| 98 |
+
|
| 99 |
+
return {"param_type": param_type, "name": name}
|
| 100 |
+
|
| 101 |
+
def __call__(
|
| 102 |
+
self,
|
| 103 |
+
value: t.Any,
|
| 104 |
+
param: Parameter | None = None,
|
| 105 |
+
ctx: Context | None = None,
|
| 106 |
+
) -> _ValueT_co | None:
|
| 107 |
+
if value is not None:
|
| 108 |
+
return self.convert(value, param, ctx)
|
| 109 |
+
return None
|
| 110 |
+
|
| 111 |
+
def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
|
| 112 |
+
"""Returns the metavar default for this param if it provides one."""
|
| 113 |
+
|
| 114 |
+
def get_missing_message(self, param: Parameter, ctx: Context | None) -> str | None:
|
| 115 |
+
"""Optionally might return extra information about a missing
|
| 116 |
+
parameter.
|
| 117 |
+
|
| 118 |
+
.. versionadded:: 2.0
|
| 119 |
+
"""
|
| 120 |
+
|
| 121 |
+
def convert(
|
| 122 |
+
self, value: t.Any, param: Parameter | None, ctx: Context | None
|
| 123 |
+
) -> _ValueT_co:
|
| 124 |
+
"""Convert the value to the correct type. This is not called if
|
| 125 |
+
the value is ``None`` (the missing value).
|
| 126 |
+
|
| 127 |
+
This must accept string values from the command line, as well as
|
| 128 |
+
values that are already the correct type. It may also convert
|
| 129 |
+
other compatible types.
|
| 130 |
+
|
| 131 |
+
The ``param`` and ``ctx`` arguments may be ``None`` in certain
|
| 132 |
+
situations, such as when converting prompt input.
|
| 133 |
+
|
| 134 |
+
If the value cannot be converted, call :meth:`fail` with a
|
| 135 |
+
descriptive message.
|
| 136 |
+
|
| 137 |
+
:param value: The value to convert.
|
| 138 |
+
:param param: The parameter that is using this type to convert
|
| 139 |
+
its value. May be ``None``.
|
| 140 |
+
:param ctx: The current context that arrived at this value. May
|
| 141 |
+
be ``None``.
|
| 142 |
+
"""
|
| 143 |
+
# The default returns the value as-is so subclasses that only customize
|
| 144 |
+
# metadata are not forced to redeclare ``convert``.
|
| 145 |
+
return t.cast("_ValueT_co", value)
|
| 146 |
+
|
| 147 |
+
def split_envvar_value(self, rv: str) -> cabc.Sequence[str]:
|
| 148 |
+
"""Given a value from an environment variable this splits it up
|
| 149 |
+
into small chunks depending on the defined envvar list splitter.
|
| 150 |
+
|
| 151 |
+
If the splitter is set to `None`, which means that whitespace splits,
|
| 152 |
+
then leading and trailing whitespace is ignored. Otherwise, leading
|
| 153 |
+
and trailing splitters usually lead to empty items being included.
|
| 154 |
+
"""
|
| 155 |
+
return (rv or "").split(self.envvar_list_splitter)
|
| 156 |
+
|
| 157 |
+
def fail(
|
| 158 |
+
self,
|
| 159 |
+
message: str,
|
| 160 |
+
param: Parameter | None = None,
|
| 161 |
+
ctx: Context | None = None,
|
| 162 |
+
) -> t.NoReturn:
|
| 163 |
+
"""Helper method to fail with an invalid value message."""
|
| 164 |
+
raise BadParameter(message, ctx=ctx, param=param)
|
| 165 |
+
|
| 166 |
+
def shell_complete(
|
| 167 |
+
self, ctx: Context, param: Parameter, incomplete: str
|
| 168 |
+
) -> list[CompletionItem]:
|
| 169 |
+
"""Return a list of
|
| 170 |
+
:class:`~click.shell_completion.CompletionItem` objects for the
|
| 171 |
+
incomplete value. Most types do not provide completions, but
|
| 172 |
+
some do, and this allows custom types to provide custom
|
| 173 |
+
completions as well.
|
| 174 |
+
|
| 175 |
+
:param ctx: Invocation context for this command.
|
| 176 |
+
:param param: The parameter that is requesting completion.
|
| 177 |
+
:param incomplete: Value being completed. May be empty.
|
| 178 |
+
|
| 179 |
+
.. versionadded:: 8.0
|
| 180 |
+
"""
|
| 181 |
+
return []
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
class CompositeParamType(ParamType[_ValueT_co]):
|
| 185 |
+
is_composite: t.ClassVar[bool] = True
|
| 186 |
+
|
| 187 |
+
@property
|
| 188 |
+
@abc.abstractmethod
|
| 189 |
+
def arity(self) -> int: ... # type: ignore[override]
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
if t.TYPE_CHECKING:
|
| 193 |
+
# on Python 3.10 this will raise a TypeError
|
| 194 |
+
|
| 195 |
+
class FuncParamTypeInfoDict(
|
| 196 |
+
ParamTypeInfoDict,
|
| 197 |
+
t.Generic[_ValueT_contra, _ValueT_co],
|
| 198 |
+
):
|
| 199 |
+
func: t.Callable[[_ValueT_contra], _ValueT_co]
|
| 200 |
+
else:
|
| 201 |
+
|
| 202 |
+
class FuncParamTypeInfoDict(ParamTypeInfoDict):
|
| 203 |
+
func: t.Callable[[t.Any], t.Any]
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
class FuncParamType(ParamType[_ValueT_co], t.Generic[_ValueT_contra, _ValueT_co]):
|
| 207 |
+
name: str
|
| 208 |
+
func: t.Callable[[_ValueT_contra], _ValueT_co]
|
| 209 |
+
|
| 210 |
+
def __init__(self, func: t.Callable[[_ValueT_contra], _ValueT_co]) -> None:
|
| 211 |
+
self.name = func.__name__
|
| 212 |
+
self.func = func
|
| 213 |
+
|
| 214 |
+
def to_info_dict(self) -> FuncParamTypeInfoDict[_ValueT_contra, _ValueT_co]:
|
| 215 |
+
return {"func": self.func, **super().to_info_dict()}
|
| 216 |
+
|
| 217 |
+
def convert(
|
| 218 |
+
self, value: _ValueT_contra, param: Parameter | None, ctx: Context | None
|
| 219 |
+
) -> _ValueT_co:
|
| 220 |
+
try:
|
| 221 |
+
return self.func(value)
|
| 222 |
+
except ValueError as exc:
|
| 223 |
+
message = str(exc)
|
| 224 |
+
|
| 225 |
+
if not message:
|
| 226 |
+
try:
|
| 227 |
+
message = str(value)
|
| 228 |
+
except UnicodeError:
|
| 229 |
+
message = t.cast("bytes", value).decode("utf-8", "replace")
|
| 230 |
+
|
| 231 |
+
self.fail(message, param, ctx)
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
class UnprocessedParamType(ParamType[t.Any]):
|
| 235 |
+
name = "text"
|
| 236 |
+
|
| 237 |
+
def convert(
|
| 238 |
+
self, value: _ValueT, param: Parameter | None, ctx: Context | None
|
| 239 |
+
) -> _ValueT:
|
| 240 |
+
return value
|
| 241 |
+
|
| 242 |
+
def __repr__(self) -> str:
|
| 243 |
+
return "UNPROCESSED"
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
class StringParamType(ParamType[str]):
|
| 247 |
+
name = "text"
|
| 248 |
+
|
| 249 |
+
def convert(
|
| 250 |
+
self, value: t.Any, param: Parameter | None, ctx: Context | None
|
| 251 |
+
) -> str:
|
| 252 |
+
if isinstance(value, bytes):
|
| 253 |
+
enc = _get_argv_encoding()
|
| 254 |
+
try:
|
| 255 |
+
return value.decode(enc)
|
| 256 |
+
except UnicodeError:
|
| 257 |
+
fs_enc = sys.getfilesystemencoding()
|
| 258 |
+
if fs_enc != enc:
|
| 259 |
+
try:
|
| 260 |
+
return value.decode(fs_enc)
|
| 261 |
+
except UnicodeError:
|
| 262 |
+
return value.decode("utf-8", "replace")
|
| 263 |
+
else:
|
| 264 |
+
return value.decode("utf-8", "replace")
|
| 265 |
+
return str(value)
|
| 266 |
+
|
| 267 |
+
def __repr__(self) -> str:
|
| 268 |
+
return "STRING"
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
if t.TYPE_CHECKING:
|
| 272 |
+
# on Python 3.10 this will raise a TypeError
|
| 273 |
+
|
| 274 |
+
class ChoiceInfoDict(ParamTypeInfoDict, t.Generic[_ValueT_co]):
|
| 275 |
+
choices: tuple[_ValueT_co, ...]
|
| 276 |
+
case_sensitive: bool
|
| 277 |
+
else:
|
| 278 |
+
|
| 279 |
+
class ChoiceInfoDict(ParamTypeInfoDict):
|
| 280 |
+
choices: tuple[t.Any, ...]
|
| 281 |
+
case_sensitive: bool
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
class Choice(ParamType[_ValueT_co], t.Generic[_ValueT_co]):
|
| 285 |
+
"""The choice type allows a value to be checked against a fixed set
|
| 286 |
+
of supported values.
|
| 287 |
+
|
| 288 |
+
You may pass any iterable value which will be converted to a tuple
|
| 289 |
+
and thus will only be iterated once.
|
| 290 |
+
|
| 291 |
+
The resulting value will always be one of the originally passed choices.
|
| 292 |
+
See :meth:`normalize_choice` for more info on the mapping of strings
|
| 293 |
+
to choices. See :ref:`choice-opts` for an example.
|
| 294 |
+
|
| 295 |
+
:param case_sensitive: Set to false to make choices case
|
| 296 |
+
insensitive. Defaults to true.
|
| 297 |
+
|
| 298 |
+
.. versionchanged:: 8.4.0
|
| 299 |
+
Now generic in the choice value type. Parameterize with the type of
|
| 300 |
+
the choice values (``Choice[HashType]`` for an enum, ``Choice[str]``
|
| 301 |
+
for plain strings) to enable type-checked consumers.
|
| 302 |
+
|
| 303 |
+
.. versionchanged:: 8.2.0
|
| 304 |
+
Non-``str`` ``choices`` are now supported. It can additionally be any
|
| 305 |
+
iterable. Before you were not recommended to pass anything but a list or
|
| 306 |
+
tuple.
|
| 307 |
+
|
| 308 |
+
.. versionadded:: 8.2.0
|
| 309 |
+
Choice normalization can be overridden via :meth:`normalize_choice`.
|
| 310 |
+
"""
|
| 311 |
+
|
| 312 |
+
name: str = "choice"
|
| 313 |
+
|
| 314 |
+
choices: tuple[_ValueT_co, ...]
|
| 315 |
+
case_sensitive: bool
|
| 316 |
+
|
| 317 |
+
def __init__(
|
| 318 |
+
self, choices: cabc.Iterable[_ValueT_co], case_sensitive: bool = True
|
| 319 |
+
) -> None:
|
| 320 |
+
self.choices = tuple(choices)
|
| 321 |
+
self.case_sensitive = case_sensitive
|
| 322 |
+
|
| 323 |
+
def to_info_dict(self) -> ChoiceInfoDict[_ValueT_co]:
|
| 324 |
+
return {
|
| 325 |
+
"choices": self.choices,
|
| 326 |
+
"case_sensitive": self.case_sensitive,
|
| 327 |
+
**super().to_info_dict(),
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
def _normalized_mapping(
|
| 331 |
+
self, ctx: Context | None = None
|
| 332 |
+
) -> cabc.Mapping[_ValueT_co, str]:
|
| 333 |
+
"""
|
| 334 |
+
Returns mapping where keys are the original choices and the values are
|
| 335 |
+
the normalized values that are accepted via the command line.
|
| 336 |
+
|
| 337 |
+
This is a simple wrapper around :meth:`normalize_choice`, use that
|
| 338 |
+
instead which is supported.
|
| 339 |
+
"""
|
| 340 |
+
return {
|
| 341 |
+
choice: self.normalize_choice(
|
| 342 |
+
choice=choice,
|
| 343 |
+
ctx=ctx,
|
| 344 |
+
)
|
| 345 |
+
for choice in self.choices
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
def normalize_choice(self, choice: object, ctx: Context | None) -> str:
|
| 349 |
+
"""
|
| 350 |
+
Normalize a choice value, used to map a passed string to a choice.
|
| 351 |
+
Each choice must have a unique normalized value.
|
| 352 |
+
|
| 353 |
+
By default uses :meth:`Context.token_normalize_func` and if not case
|
| 354 |
+
sensitive, convert it to a casefolded value.
|
| 355 |
+
|
| 356 |
+
.. versionadded:: 8.2.0
|
| 357 |
+
"""
|
| 358 |
+
normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice)
|
| 359 |
+
|
| 360 |
+
if ctx is not None and ctx.token_normalize_func is not None:
|
| 361 |
+
normed_value = ctx.token_normalize_func(normed_value)
|
| 362 |
+
|
| 363 |
+
if not self.case_sensitive:
|
| 364 |
+
normed_value = normed_value.casefold()
|
| 365 |
+
|
| 366 |
+
return normed_value
|
| 367 |
+
|
| 368 |
+
def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
|
| 369 |
+
if param.param_type_name == "option" and not param.show_choices: # type: ignore[attr-defined]
|
| 370 |
+
choice_metavars = [
|
| 371 |
+
convert_type(type(choice)).name.upper() for choice in self.choices
|
| 372 |
+
]
|
| 373 |
+
choices_str = "|".join([*dict.fromkeys(choice_metavars)])
|
| 374 |
+
else:
|
| 375 |
+
choices_str = "|".join(
|
| 376 |
+
[str(i) for i in self._normalized_mapping(ctx=ctx).values()]
|
| 377 |
+
)
|
| 378 |
+
|
| 379 |
+
# Use curly braces to indicate a required argument.
|
| 380 |
+
if param.required and param.param_type_name == "argument":
|
| 381 |
+
return f"{{{choices_str}}}"
|
| 382 |
+
|
| 383 |
+
# Use square braces to indicate an option or optional argument.
|
| 384 |
+
return f"[{choices_str}]"
|
| 385 |
+
|
| 386 |
+
def get_missing_message(self, param: Parameter, ctx: Context | None) -> str:
|
| 387 |
+
"""
|
| 388 |
+
Message shown when no choice is passed.
|
| 389 |
+
|
| 390 |
+
.. versionchanged:: 8.2.0 Added ``ctx`` argument.
|
| 391 |
+
"""
|
| 392 |
+
return _("Choose from:\n\t{choices}").format(
|
| 393 |
+
choices=",\n\t".join(self._normalized_mapping(ctx=ctx).values())
|
| 394 |
+
)
|
| 395 |
+
|
| 396 |
+
def convert(
|
| 397 |
+
self, value: t.Any, param: Parameter | None, ctx: Context | None
|
| 398 |
+
) -> _ValueT_co:
|
| 399 |
+
"""
|
| 400 |
+
For a given value from the parser, normalize it and find its
|
| 401 |
+
matching normalized value in the list of choices. Then return the
|
| 402 |
+
matched "original" choice.
|
| 403 |
+
"""
|
| 404 |
+
normed_value = self.normalize_choice(choice=value, ctx=ctx)
|
| 405 |
+
normalized_mapping = self._normalized_mapping(ctx=ctx)
|
| 406 |
+
|
| 407 |
+
try:
|
| 408 |
+
return next(
|
| 409 |
+
original
|
| 410 |
+
for original, normalized in normalized_mapping.items()
|
| 411 |
+
if normalized == normed_value
|
| 412 |
+
)
|
| 413 |
+
except StopIteration:
|
| 414 |
+
self.fail(
|
| 415 |
+
self.get_invalid_choice_message(value=value, ctx=ctx),
|
| 416 |
+
param=param,
|
| 417 |
+
ctx=ctx,
|
| 418 |
+
)
|
| 419 |
+
|
| 420 |
+
def get_invalid_choice_message(self, value: t.Any, ctx: Context | None) -> str:
|
| 421 |
+
"""Get the error message when the given choice is invalid.
|
| 422 |
+
|
| 423 |
+
:param value: The invalid value.
|
| 424 |
+
|
| 425 |
+
.. versionadded:: 8.2
|
| 426 |
+
"""
|
| 427 |
+
choices_str = ", ".join(map(repr, self._normalized_mapping(ctx=ctx).values()))
|
| 428 |
+
return ngettext(
|
| 429 |
+
"{value!r} is not {choice}.",
|
| 430 |
+
"{value!r} is not one of {choices}.",
|
| 431 |
+
len(self.choices),
|
| 432 |
+
).format(value=value, choice=choices_str, choices=choices_str)
|
| 433 |
+
|
| 434 |
+
def __repr__(self) -> str:
|
| 435 |
+
return _("Choice({choices})").format(choices=list(self.choices))
|
| 436 |
+
|
| 437 |
+
def shell_complete(
|
| 438 |
+
self, ctx: Context, param: Parameter, incomplete: str
|
| 439 |
+
) -> list[CompletionItem]:
|
| 440 |
+
"""Complete choices that start with the incomplete value.
|
| 441 |
+
|
| 442 |
+
:param ctx: Invocation context for this command.
|
| 443 |
+
:param param: The parameter that is requesting completion.
|
| 444 |
+
:param incomplete: Value being completed. May be empty.
|
| 445 |
+
|
| 446 |
+
.. versionadded:: 8.0
|
| 447 |
+
"""
|
| 448 |
+
from click.shell_completion import CompletionItem
|
| 449 |
+
|
| 450 |
+
str_choices = [self.normalize_choice(choice, ctx) for choice in self.choices]
|
| 451 |
+
if self.case_sensitive:
|
| 452 |
+
matched = (c for c in str_choices if c.startswith(incomplete))
|
| 453 |
+
else:
|
| 454 |
+
incomplete = incomplete.lower()
|
| 455 |
+
matched = (c for c in str_choices if c.lower().startswith(incomplete))
|
| 456 |
+
|
| 457 |
+
return [CompletionItem(c) for c in matched]
|
| 458 |
+
|
| 459 |
+
|
| 460 |
+
class DateTimeInfoDict(ParamTypeInfoDict):
|
| 461 |
+
formats: cabc.Sequence[str]
|
| 462 |
+
|
| 463 |
+
|
| 464 |
+
class DateTime(ParamType[datetime]):
|
| 465 |
+
"""The DateTime type converts date strings into `datetime` objects.
|
| 466 |
+
|
| 467 |
+
The format strings which are checked are configurable, but default to some
|
| 468 |
+
common (non-timezone aware) ISO 8601 formats.
|
| 469 |
+
|
| 470 |
+
When specifying *DateTime* formats, you should only pass a list or a tuple.
|
| 471 |
+
Other iterables, like generators, may lead to surprising results.
|
| 472 |
+
|
| 473 |
+
The format strings are processed using ``datetime.strptime``, and this
|
| 474 |
+
consequently defines the format strings which are allowed.
|
| 475 |
+
|
| 476 |
+
Parsing is tried using each format, in order, and the first format which
|
| 477 |
+
parses successfully is used.
|
| 478 |
+
|
| 479 |
+
:param formats: A list or tuple of date format strings, in the order in
|
| 480 |
+
which they should be tried. Defaults to
|
| 481 |
+
``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``,
|
| 482 |
+
``'%Y-%m-%d %H:%M:%S'``.
|
| 483 |
+
"""
|
| 484 |
+
|
| 485 |
+
name = "datetime"
|
| 486 |
+
|
| 487 |
+
formats: cabc.Sequence[str]
|
| 488 |
+
|
| 489 |
+
def __init__(self, formats: cabc.Sequence[str] | None = None):
|
| 490 |
+
self.formats = formats or [
|
| 491 |
+
"%Y-%m-%d",
|
| 492 |
+
"%Y-%m-%dT%H:%M:%S",
|
| 493 |
+
"%Y-%m-%d %H:%M:%S",
|
| 494 |
+
]
|
| 495 |
+
|
| 496 |
+
def to_info_dict(self) -> DateTimeInfoDict:
|
| 497 |
+
return {"formats": self.formats, **super().to_info_dict()}
|
| 498 |
+
|
| 499 |
+
def get_metavar(self, param: Parameter, ctx: Context) -> str:
|
| 500 |
+
return f"[{'|'.join(self.formats)}]"
|
| 501 |
+
|
| 502 |
+
def _try_to_convert_date(self, value: t.Any, format: str) -> datetime | None:
|
| 503 |
+
try:
|
| 504 |
+
return datetime.strptime(value, format)
|
| 505 |
+
except ValueError:
|
| 506 |
+
return None
|
| 507 |
+
|
| 508 |
+
def convert(
|
| 509 |
+
self, value: t.Any, param: Parameter | None, ctx: Context | None
|
| 510 |
+
) -> datetime:
|
| 511 |
+
if isinstance(value, datetime):
|
| 512 |
+
return value
|
| 513 |
+
|
| 514 |
+
for format in self.formats:
|
| 515 |
+
converted = self._try_to_convert_date(value, format)
|
| 516 |
+
|
| 517 |
+
if converted is not None:
|
| 518 |
+
return converted
|
| 519 |
+
|
| 520 |
+
formats_str = ", ".join(map(repr, self.formats))
|
| 521 |
+
self.fail(
|
| 522 |
+
ngettext(
|
| 523 |
+
"{value!r} does not match the format {format}.",
|
| 524 |
+
"{value!r} does not match the formats {formats}.",
|
| 525 |
+
len(self.formats),
|
| 526 |
+
).format(value=value, format=formats_str, formats=formats_str),
|
| 527 |
+
param,
|
| 528 |
+
ctx,
|
| 529 |
+
)
|
| 530 |
+
|
| 531 |
+
def __repr__(self) -> str:
|
| 532 |
+
return "DateTime"
|
| 533 |
+
|
| 534 |
+
|
| 535 |
+
class _NumberParamTypeBase(
|
| 536 |
+
ParamType[_ValueT_co], t.Generic[_ValueT_contra, _ValueT_co]
|
| 537 |
+
):
|
| 538 |
+
_number_class: t.Callable[[_ValueT_contra], _ValueT_co]
|
| 539 |
+
|
| 540 |
+
def convert(
|
| 541 |
+
self, value: _ValueT_contra, param: Parameter | None, ctx: Context | None
|
| 542 |
+
) -> _ValueT_co:
|
| 543 |
+
try:
|
| 544 |
+
return self._number_class(value)
|
| 545 |
+
except ValueError:
|
| 546 |
+
self.fail(
|
| 547 |
+
_("{value!r} is not a valid {number_type}.").format(
|
| 548 |
+
value=value, number_type=self.name
|
| 549 |
+
),
|
| 550 |
+
param,
|
| 551 |
+
ctx,
|
| 552 |
+
)
|
| 553 |
+
|
| 554 |
+
|
| 555 |
+
if t.TYPE_CHECKING:
|
| 556 |
+
# on Python 3.10 this will raise a TypeError
|
| 557 |
+
|
| 558 |
+
class NumberRangeInfoDict(ParamTypeInfoDict, t.Generic[_FloatValueT_co]):
|
| 559 |
+
min: _FloatValueT_co | None
|
| 560 |
+
max: _FloatValueT_co | None
|
| 561 |
+
min_open: bool
|
| 562 |
+
max_open: bool
|
| 563 |
+
clamp: bool
|
| 564 |
+
else:
|
| 565 |
+
|
| 566 |
+
class NumberRangeInfoDict(ParamTypeInfoDict):
|
| 567 |
+
min: t.Any | None
|
| 568 |
+
max: t.Any | None
|
| 569 |
+
min_open: bool
|
| 570 |
+
max_open: bool
|
| 571 |
+
clamp: bool
|
| 572 |
+
|
| 573 |
+
|
| 574 |
+
class _NumberRangeBase(
|
| 575 |
+
_NumberParamTypeBase[_ValueT_contra, _FloatValueT_co],
|
| 576 |
+
t.Generic[_ValueT_contra, _FloatValueT_co],
|
| 577 |
+
):
|
| 578 |
+
min: _FloatValueT_co | None
|
| 579 |
+
max: _FloatValueT_co | None
|
| 580 |
+
min_open: bool
|
| 581 |
+
max_open: bool
|
| 582 |
+
clamp: bool
|
| 583 |
+
|
| 584 |
+
def __init__(
|
| 585 |
+
self,
|
| 586 |
+
min: _FloatValueT_co | None = None,
|
| 587 |
+
max: _FloatValueT_co | None = None,
|
| 588 |
+
min_open: bool = False,
|
| 589 |
+
max_open: bool = False,
|
| 590 |
+
clamp: bool = False,
|
| 591 |
+
) -> None:
|
| 592 |
+
self.min = min
|
| 593 |
+
self.max = max
|
| 594 |
+
self.min_open = min_open
|
| 595 |
+
self.max_open = max_open
|
| 596 |
+
self.clamp = clamp
|
| 597 |
+
|
| 598 |
+
def to_info_dict(self) -> NumberRangeInfoDict[_FloatValueT_co]:
|
| 599 |
+
return {
|
| 600 |
+
"min": self.min,
|
| 601 |
+
"max": self.max,
|
| 602 |
+
"min_open": self.min_open,
|
| 603 |
+
"max_open": self.max_open,
|
| 604 |
+
"clamp": self.clamp,
|
| 605 |
+
**super().to_info_dict(),
|
| 606 |
+
}
|
| 607 |
+
|
| 608 |
+
def convert(
|
| 609 |
+
self, value: _ValueT_contra, param: Parameter | None, ctx: Context | None
|
| 610 |
+
) -> _FloatValueT_co:
|
| 611 |
+
import operator
|
| 612 |
+
|
| 613 |
+
rv = super().convert(value, param, ctx)
|
| 614 |
+
min = self.min
|
| 615 |
+
max = self.max
|
| 616 |
+
lt_min: bool = min is not None and (
|
| 617 |
+
operator.le if self.min_open else operator.lt
|
| 618 |
+
)(rv, min)
|
| 619 |
+
gt_max: bool = max is not None and (
|
| 620 |
+
operator.ge if self.max_open else operator.gt
|
| 621 |
+
)(rv, max)
|
| 622 |
+
|
| 623 |
+
if self.clamp:
|
| 624 |
+
if min is not None and lt_min:
|
| 625 |
+
return self._clamp(min, 1, self.min_open)
|
| 626 |
+
|
| 627 |
+
if max is not None and gt_max:
|
| 628 |
+
return self._clamp(max, -1, self.max_open)
|
| 629 |
+
|
| 630 |
+
if lt_min or gt_max:
|
| 631 |
+
self.fail(
|
| 632 |
+
_("{value} is not in the range {range}.").format(
|
| 633 |
+
value=rv, range=self._describe_range()
|
| 634 |
+
),
|
| 635 |
+
param,
|
| 636 |
+
ctx,
|
| 637 |
+
)
|
| 638 |
+
|
| 639 |
+
return rv
|
| 640 |
+
|
| 641 |
+
@abc.abstractmethod
|
| 642 |
+
def _clamp(
|
| 643 |
+
# Covariant type variables cannot be used in input positions, so we use a
|
| 644 |
+
# separate method-scoped type variable instead.
|
| 645 |
+
self: _NumberRangeBase[t.Any, _FloatValueT],
|
| 646 |
+
bound: _FloatValueT,
|
| 647 |
+
dir: t.Literal[1, -1],
|
| 648 |
+
open: bool,
|
| 649 |
+
) -> _FloatValueT:
|
| 650 |
+
"""Find the valid value to clamp to bound in the given
|
| 651 |
+
direction.
|
| 652 |
+
|
| 653 |
+
:param bound: The boundary value.
|
| 654 |
+
:param dir: 1 or -1 indicating the direction to move.
|
| 655 |
+
:param open: If true, the range does not include the bound.
|
| 656 |
+
"""
|
| 657 |
+
...
|
| 658 |
+
|
| 659 |
+
def _describe_range(self) -> str:
|
| 660 |
+
"""Describe the range for use in help text."""
|
| 661 |
+
if self.min is None:
|
| 662 |
+
op = "<" if self.max_open else "<="
|
| 663 |
+
return f"x{op}{self.max}"
|
| 664 |
+
|
| 665 |
+
if self.max is None:
|
| 666 |
+
op = ">" if self.min_open else ">="
|
| 667 |
+
return f"x{op}{self.min}"
|
| 668 |
+
|
| 669 |
+
lop = "<" if self.min_open else "<="
|
| 670 |
+
rop = "<" if self.max_open else "<="
|
| 671 |
+
return f"{self.min}{lop}x{rop}{self.max}"
|
| 672 |
+
|
| 673 |
+
def __repr__(self) -> str:
|
| 674 |
+
clamp = " clamped" if self.clamp else ""
|
| 675 |
+
return f"<{type(self).__name__} {self._describe_range()}{clamp}>"
|
| 676 |
+
|
| 677 |
+
|
| 678 |
+
class IntParamType(_NumberParamTypeBase[t.SupportsInt | t.SupportsIndex, int]):
|
| 679 |
+
name = "integer"
|
| 680 |
+
_number_class = int
|
| 681 |
+
|
| 682 |
+
def __repr__(self) -> str:
|
| 683 |
+
return "INT"
|
| 684 |
+
|
| 685 |
+
|
| 686 |
+
class IntRange(_NumberRangeBase[int, int], IntParamType):
|
| 687 |
+
"""Restrict an :data:`click.INT` value to a range of accepted
|
| 688 |
+
values. See :ref:`ranges`.
|
| 689 |
+
|
| 690 |
+
If ``min`` or ``max`` are not passed, any value is accepted in that
|
| 691 |
+
direction. If ``min_open`` or ``max_open`` are enabled, the
|
| 692 |
+
corresponding boundary is not included in the range.
|
| 693 |
+
|
| 694 |
+
If ``clamp`` is enabled, a value outside the range is clamped to the
|
| 695 |
+
boundary instead of failing.
|
| 696 |
+
|
| 697 |
+
.. versionchanged:: 8.0
|
| 698 |
+
Added the ``min_open`` and ``max_open`` parameters.
|
| 699 |
+
"""
|
| 700 |
+
|
| 701 |
+
name = "integer range"
|
| 702 |
+
|
| 703 |
+
def _clamp(self, bound: int, dir: t.Literal[1, -1], open: bool) -> int:
|
| 704 |
+
if not open:
|
| 705 |
+
return bound
|
| 706 |
+
|
| 707 |
+
return bound + dir
|
| 708 |
+
|
| 709 |
+
|
| 710 |
+
class FloatParamType(_NumberParamTypeBase[t.SupportsFloat | t.SupportsIndex, float]):
|
| 711 |
+
name = "float"
|
| 712 |
+
_number_class = float
|
| 713 |
+
|
| 714 |
+
def __repr__(self) -> str:
|
| 715 |
+
return "FLOAT"
|
| 716 |
+
|
| 717 |
+
|
| 718 |
+
class FloatRange(_NumberRangeBase[float, float], FloatParamType):
|
| 719 |
+
"""Restrict a :data:`click.FLOAT` value to a range of accepted
|
| 720 |
+
values. See :ref:`ranges`.
|
| 721 |
+
|
| 722 |
+
If ``min`` or ``max`` are not passed, any value is accepted in that
|
| 723 |
+
direction. If ``min_open`` or ``max_open`` are enabled, the
|
| 724 |
+
corresponding boundary is not included in the range.
|
| 725 |
+
|
| 726 |
+
If ``clamp`` is enabled, a value outside the range is clamped to the
|
| 727 |
+
boundary instead of failing. This is not supported if either
|
| 728 |
+
boundary is marked ``open``.
|
| 729 |
+
|
| 730 |
+
.. versionchanged:: 8.0
|
| 731 |
+
Added the ``min_open`` and ``max_open`` parameters.
|
| 732 |
+
"""
|
| 733 |
+
|
| 734 |
+
name = "float range"
|
| 735 |
+
|
| 736 |
+
def __init__(
|
| 737 |
+
self,
|
| 738 |
+
min: float | None = None,
|
| 739 |
+
max: float | None = None,
|
| 740 |
+
min_open: bool = False,
|
| 741 |
+
max_open: bool = False,
|
| 742 |
+
clamp: bool = False,
|
| 743 |
+
) -> None:
|
| 744 |
+
super().__init__(
|
| 745 |
+
min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp
|
| 746 |
+
)
|
| 747 |
+
|
| 748 |
+
if (min_open or max_open) and clamp:
|
| 749 |
+
raise TypeError("Clamping is not supported for open bounds.")
|
| 750 |
+
|
| 751 |
+
def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float:
|
| 752 |
+
if not open:
|
| 753 |
+
return bound
|
| 754 |
+
|
| 755 |
+
# Could use math.nextafter here, but clamping an
|
| 756 |
+
# open float range doesn't seem to be particularly useful. It's
|
| 757 |
+
# left up to the user to write a callback to do it if needed.
|
| 758 |
+
raise RuntimeError("Clamping is not supported for open bounds.")
|
| 759 |
+
|
| 760 |
+
|
| 761 |
+
class BoolParamType(ParamType[bool]):
|
| 762 |
+
name = "boolean"
|
| 763 |
+
|
| 764 |
+
bool_states: dict[str, bool] = {
|
| 765 |
+
"1": True,
|
| 766 |
+
"0": False,
|
| 767 |
+
"yes": True,
|
| 768 |
+
"no": False,
|
| 769 |
+
"true": True,
|
| 770 |
+
"false": False,
|
| 771 |
+
"on": True,
|
| 772 |
+
"off": False,
|
| 773 |
+
"t": True,
|
| 774 |
+
"f": False,
|
| 775 |
+
"y": True,
|
| 776 |
+
"n": False,
|
| 777 |
+
# Absence of value is considered False.
|
| 778 |
+
"": False,
|
| 779 |
+
}
|
| 780 |
+
"""A mapping of string values to boolean states.
|
| 781 |
+
|
| 782 |
+
Mapping is inspired by :py:attr:`configparser.ConfigParser.BOOLEAN_STATES`
|
| 783 |
+
and extends it.
|
| 784 |
+
|
| 785 |
+
.. caution::
|
| 786 |
+
String values are lower-cased, as the ``str_to_bool`` comparison function
|
| 787 |
+
below is case-insensitive.
|
| 788 |
+
|
| 789 |
+
.. warning::
|
| 790 |
+
The mapping is not exhaustive, and does not cover all possible boolean strings
|
| 791 |
+
representations. It will remains as it is to avoid endless bikeshedding.
|
| 792 |
+
|
| 793 |
+
Future work my be considered to make this mapping user-configurable from public
|
| 794 |
+
API.
|
| 795 |
+
"""
|
| 796 |
+
|
| 797 |
+
@staticmethod
|
| 798 |
+
def str_to_bool(value: str | bool) -> bool | None:
|
| 799 |
+
"""Convert a string to a boolean value.
|
| 800 |
+
|
| 801 |
+
If the value is already a boolean, it is returned as-is. If the value is a
|
| 802 |
+
string, it is stripped of whitespaces and lower-cased, then checked against
|
| 803 |
+
the known boolean states pre-defined in the `BoolParamType.bool_states` mapping
|
| 804 |
+
above.
|
| 805 |
+
|
| 806 |
+
Returns `None` if the value does not match any known boolean state.
|
| 807 |
+
"""
|
| 808 |
+
if isinstance(value, bool):
|
| 809 |
+
return value
|
| 810 |
+
return BoolParamType.bool_states.get(value.strip().lower())
|
| 811 |
+
|
| 812 |
+
def convert(
|
| 813 |
+
self, value: t.Any, param: Parameter | None, ctx: Context | None
|
| 814 |
+
) -> bool:
|
| 815 |
+
normalized = self.str_to_bool(value)
|
| 816 |
+
if normalized is None:
|
| 817 |
+
self.fail(
|
| 818 |
+
_(
|
| 819 |
+
"{value!r} is not a valid boolean. Recognized values: {states}"
|
| 820 |
+
).format(value=value, states=", ".join(sorted(self.bool_states))),
|
| 821 |
+
param,
|
| 822 |
+
ctx,
|
| 823 |
+
)
|
| 824 |
+
return normalized
|
| 825 |
+
|
| 826 |
+
def __repr__(self) -> str:
|
| 827 |
+
return "BOOL"
|
| 828 |
+
|
| 829 |
+
|
| 830 |
+
class UUIDParameterType(ParamType[uuid.UUID]):
|
| 831 |
+
name = "uuid"
|
| 832 |
+
|
| 833 |
+
def convert(
|
| 834 |
+
self, value: uuid.UUID | str, param: Parameter | None, ctx: Context | None
|
| 835 |
+
) -> uuid.UUID:
|
| 836 |
+
if isinstance(value, uuid.UUID):
|
| 837 |
+
return value
|
| 838 |
+
|
| 839 |
+
value = value.strip()
|
| 840 |
+
|
| 841 |
+
try:
|
| 842 |
+
return uuid.UUID(value)
|
| 843 |
+
except ValueError:
|
| 844 |
+
self.fail(
|
| 845 |
+
_("{value!r} is not a valid UUID.").format(value=value), param, ctx
|
| 846 |
+
)
|
| 847 |
+
|
| 848 |
+
def __repr__(self) -> str:
|
| 849 |
+
return "UUID"
|
| 850 |
+
|
| 851 |
+
|
| 852 |
+
class FileInfoDict(ParamTypeInfoDict):
|
| 853 |
+
mode: str
|
| 854 |
+
encoding: str | None
|
| 855 |
+
|
| 856 |
+
|
| 857 |
+
class File(ParamType[t.IO[t.Any]]):
|
| 858 |
+
"""Declares a parameter to be a file for reading or writing. The file
|
| 859 |
+
is automatically closed once the context tears down (after the command
|
| 860 |
+
finished working).
|
| 861 |
+
|
| 862 |
+
Files can be opened for reading or writing. The special value ``-``
|
| 863 |
+
indicates stdin or stdout depending on the mode.
|
| 864 |
+
|
| 865 |
+
By default, the file is opened for reading text data, but it can also be
|
| 866 |
+
opened in binary mode or for writing. The encoding parameter can be used
|
| 867 |
+
to force a specific encoding.
|
| 868 |
+
|
| 869 |
+
The `lazy` flag controls if the file should be opened immediately or upon
|
| 870 |
+
first IO. The default is to be non-lazy for standard input and output
|
| 871 |
+
streams as well as files opened for reading, `lazy` otherwise. When opening a
|
| 872 |
+
file lazily for reading, it is still opened temporarily for validation, but
|
| 873 |
+
will not be held open until first IO. lazy is mainly useful when opening
|
| 874 |
+
for writing to avoid creating the file until it is needed.
|
| 875 |
+
|
| 876 |
+
Files can also be opened atomically in which case all writes go into a
|
| 877 |
+
separate file in the same folder and upon completion the file will
|
| 878 |
+
be moved over to the original location. This is useful if a file
|
| 879 |
+
regularly read by other users is modified.
|
| 880 |
+
|
| 881 |
+
See :ref:`file-args` for more information.
|
| 882 |
+
|
| 883 |
+
.. versionchanged:: 2.0
|
| 884 |
+
Added the ``atomic`` parameter.
|
| 885 |
+
"""
|
| 886 |
+
|
| 887 |
+
name = "filename"
|
| 888 |
+
envvar_list_splitter: t.ClassVar[str] = os.path.pathsep
|
| 889 |
+
|
| 890 |
+
mode: str
|
| 891 |
+
encoding: str | None
|
| 892 |
+
errors: str | None
|
| 893 |
+
lazy: bool | None
|
| 894 |
+
atomic: bool
|
| 895 |
+
|
| 896 |
+
def __init__(
|
| 897 |
+
self,
|
| 898 |
+
mode: str = "r",
|
| 899 |
+
encoding: str | None = None,
|
| 900 |
+
errors: str | None = "strict",
|
| 901 |
+
lazy: bool | None = None,
|
| 902 |
+
atomic: bool = False,
|
| 903 |
+
) -> None:
|
| 904 |
+
self.mode = mode
|
| 905 |
+
self.encoding = encoding
|
| 906 |
+
self.errors = errors
|
| 907 |
+
self.lazy = lazy
|
| 908 |
+
self.atomic = atomic
|
| 909 |
+
|
| 910 |
+
def to_info_dict(self) -> FileInfoDict:
|
| 911 |
+
return {
|
| 912 |
+
"mode": self.mode,
|
| 913 |
+
"encoding": self.encoding,
|
| 914 |
+
**super().to_info_dict(),
|
| 915 |
+
}
|
| 916 |
+
|
| 917 |
+
def resolve_lazy_flag(self, value: str | os.PathLike[str]) -> bool:
|
| 918 |
+
if self.lazy is not None:
|
| 919 |
+
return self.lazy
|
| 920 |
+
if os.fspath(value) == "-":
|
| 921 |
+
return False
|
| 922 |
+
elif "w" in self.mode:
|
| 923 |
+
return True
|
| 924 |
+
return False
|
| 925 |
+
|
| 926 |
+
def convert(
|
| 927 |
+
self,
|
| 928 |
+
value: str | os.PathLike[str] | t.IO[t.Any],
|
| 929 |
+
param: Parameter | None,
|
| 930 |
+
ctx: Context | None,
|
| 931 |
+
) -> t.IO[t.Any]:
|
| 932 |
+
if _is_file_like(value):
|
| 933 |
+
return value
|
| 934 |
+
|
| 935 |
+
try:
|
| 936 |
+
lazy = self.resolve_lazy_flag(value)
|
| 937 |
+
|
| 938 |
+
if lazy:
|
| 939 |
+
lf = LazyFile(
|
| 940 |
+
value, self.mode, self.encoding, self.errors, atomic=self.atomic
|
| 941 |
+
)
|
| 942 |
+
|
| 943 |
+
if ctx is not None:
|
| 944 |
+
ctx.call_on_close(lf.close_intelligently)
|
| 945 |
+
|
| 946 |
+
return t.cast("t.IO[t.Any]", lf)
|
| 947 |
+
|
| 948 |
+
f, should_close = open_stream(
|
| 949 |
+
value, self.mode, self.encoding, self.errors, atomic=self.atomic
|
| 950 |
+
)
|
| 951 |
+
|
| 952 |
+
# If a context is provided, we automatically close the file
|
| 953 |
+
# at the end of the context execution (or flush out). If a
|
| 954 |
+
# context does not exist, it's the caller's responsibility to
|
| 955 |
+
# properly close the file. This for instance happens when the
|
| 956 |
+
# type is used with prompts.
|
| 957 |
+
if ctx is not None:
|
| 958 |
+
if should_close:
|
| 959 |
+
ctx.call_on_close(safecall(f.close))
|
| 960 |
+
else:
|
| 961 |
+
ctx.call_on_close(safecall(f.flush))
|
| 962 |
+
|
| 963 |
+
return f
|
| 964 |
+
except OSError as e:
|
| 965 |
+
self.fail(
|
| 966 |
+
f"'{format_filename(value)}': {e.strerror}",
|
| 967 |
+
param,
|
| 968 |
+
ctx,
|
| 969 |
+
)
|
| 970 |
+
|
| 971 |
+
def shell_complete(
|
| 972 |
+
self, ctx: Context, param: Parameter, incomplete: str
|
| 973 |
+
) -> list[CompletionItem]:
|
| 974 |
+
"""Return a special completion marker that tells the completion
|
| 975 |
+
system to use the shell to provide file path completions.
|
| 976 |
+
|
| 977 |
+
:param ctx: Invocation context for this command.
|
| 978 |
+
:param param: The parameter that is requesting completion.
|
| 979 |
+
:param incomplete: Value being completed. May be empty.
|
| 980 |
+
|
| 981 |
+
.. versionadded:: 8.0
|
| 982 |
+
"""
|
| 983 |
+
from click.shell_completion import CompletionItem
|
| 984 |
+
|
| 985 |
+
return [CompletionItem(incomplete, type="file")]
|
| 986 |
+
|
| 987 |
+
|
| 988 |
+
def _is_file_like(value: t.Any) -> te.TypeIs[t.IO[t.Any]]:
|
| 989 |
+
return hasattr(value, "read") or hasattr(value, "write")
|
| 990 |
+
|
| 991 |
+
|
| 992 |
+
class PathInfoDict(ParamTypeInfoDict):
|
| 993 |
+
exists: bool
|
| 994 |
+
file_okay: bool
|
| 995 |
+
dir_okay: bool
|
| 996 |
+
writable: bool
|
| 997 |
+
readable: bool
|
| 998 |
+
allow_dash: bool
|
| 999 |
+
|
| 1000 |
+
|
| 1001 |
+
class Path(ParamType[str | bytes | os.PathLike[str]]):
|
| 1002 |
+
"""The ``Path`` type is similar to the :class:`File` type, but
|
| 1003 |
+
returns the filename instead of an open file. Various checks can be
|
| 1004 |
+
enabled to validate the type of file and permissions.
|
| 1005 |
+
|
| 1006 |
+
:param exists: The file or directory needs to exist for the value to
|
| 1007 |
+
be valid. If this is not set to ``True``, and the file does not
|
| 1008 |
+
exist, then all further checks are silently skipped.
|
| 1009 |
+
:param file_okay: Allow a file as a value.
|
| 1010 |
+
:param dir_okay: Allow a directory as a value.
|
| 1011 |
+
:param readable: if true, a readable check is performed.
|
| 1012 |
+
:param writable: if true, a writable check is performed.
|
| 1013 |
+
:param executable: if true, an executable check is performed.
|
| 1014 |
+
:param resolve_path: Make the value absolute and resolve any
|
| 1015 |
+
symlinks. A ``~`` is not expanded, as this is supposed to be
|
| 1016 |
+
done by the shell only.
|
| 1017 |
+
:param allow_dash: Allow a single dash as a value, which indicates
|
| 1018 |
+
a standard stream (but does not open it). Use
|
| 1019 |
+
:func:`~click.open_file` to handle opening this value.
|
| 1020 |
+
:param path_type: Convert the incoming path value to this type. If
|
| 1021 |
+
``None``, keep Python's default, which is ``str``. Useful to
|
| 1022 |
+
convert to :class:`pathlib.Path`.
|
| 1023 |
+
|
| 1024 |
+
.. versionchanged:: 8.1
|
| 1025 |
+
Added the ``executable`` parameter.
|
| 1026 |
+
|
| 1027 |
+
.. versionchanged:: 8.0
|
| 1028 |
+
Allow passing ``path_type=pathlib.Path``.
|
| 1029 |
+
|
| 1030 |
+
.. versionchanged:: 6.0
|
| 1031 |
+
Added the ``allow_dash`` parameter.
|
| 1032 |
+
"""
|
| 1033 |
+
|
| 1034 |
+
envvar_list_splitter: t.ClassVar[str] = os.path.pathsep
|
| 1035 |
+
|
| 1036 |
+
exists: bool
|
| 1037 |
+
file_okay: bool
|
| 1038 |
+
dir_okay: bool
|
| 1039 |
+
readable: bool
|
| 1040 |
+
writable: bool
|
| 1041 |
+
executable: bool
|
| 1042 |
+
resolve_path: bool
|
| 1043 |
+
allow_dash: bool
|
| 1044 |
+
name: str
|
| 1045 |
+
|
| 1046 |
+
def __init__(
|
| 1047 |
+
self,
|
| 1048 |
+
exists: bool = False,
|
| 1049 |
+
file_okay: bool = True,
|
| 1050 |
+
dir_okay: bool = True,
|
| 1051 |
+
writable: bool = False,
|
| 1052 |
+
readable: bool = True,
|
| 1053 |
+
resolve_path: bool = False,
|
| 1054 |
+
allow_dash: bool = False,
|
| 1055 |
+
path_type: type | None = None,
|
| 1056 |
+
executable: bool = False,
|
| 1057 |
+
) -> None:
|
| 1058 |
+
self.exists = exists
|
| 1059 |
+
self.file_okay = file_okay
|
| 1060 |
+
self.dir_okay = dir_okay
|
| 1061 |
+
self.readable = readable
|
| 1062 |
+
self.writable = writable
|
| 1063 |
+
self.executable = executable
|
| 1064 |
+
self.resolve_path = resolve_path
|
| 1065 |
+
self.allow_dash = allow_dash
|
| 1066 |
+
self.type: type | None = path_type
|
| 1067 |
+
|
| 1068 |
+
if self.file_okay and not self.dir_okay:
|
| 1069 |
+
self.name = _("file")
|
| 1070 |
+
elif self.dir_okay and not self.file_okay:
|
| 1071 |
+
self.name = _("directory")
|
| 1072 |
+
else:
|
| 1073 |
+
self.name = _("path")
|
| 1074 |
+
|
| 1075 |
+
def to_info_dict(self) -> PathInfoDict:
|
| 1076 |
+
return {
|
| 1077 |
+
"exists": self.exists,
|
| 1078 |
+
"file_okay": self.file_okay,
|
| 1079 |
+
"dir_okay": self.dir_okay,
|
| 1080 |
+
"writable": self.writable,
|
| 1081 |
+
"readable": self.readable,
|
| 1082 |
+
"allow_dash": self.allow_dash,
|
| 1083 |
+
**super().to_info_dict(),
|
| 1084 |
+
}
|
| 1085 |
+
|
| 1086 |
+
def coerce_path_result(
|
| 1087 |
+
self, value: str | os.PathLike[str]
|
| 1088 |
+
) -> str | bytes | os.PathLike[str]:
|
| 1089 |
+
if self.type is not None and not isinstance(value, self.type):
|
| 1090 |
+
if self.type is str:
|
| 1091 |
+
return os.fsdecode(value)
|
| 1092 |
+
elif self.type is bytes:
|
| 1093 |
+
return os.fsencode(value)
|
| 1094 |
+
else:
|
| 1095 |
+
return t.cast("os.PathLike[str]", self.type(value))
|
| 1096 |
+
|
| 1097 |
+
return value
|
| 1098 |
+
|
| 1099 |
+
def convert(
|
| 1100 |
+
self,
|
| 1101 |
+
value: str | os.PathLike[str],
|
| 1102 |
+
param: Parameter | None,
|
| 1103 |
+
ctx: Context | None,
|
| 1104 |
+
) -> str | bytes | os.PathLike[str]:
|
| 1105 |
+
rv = value
|
| 1106 |
+
|
| 1107 |
+
is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-")
|
| 1108 |
+
|
| 1109 |
+
if not is_dash:
|
| 1110 |
+
if self.resolve_path:
|
| 1111 |
+
rv = os.path.realpath(rv)
|
| 1112 |
+
|
| 1113 |
+
try:
|
| 1114 |
+
st = os.stat(rv)
|
| 1115 |
+
except OSError:
|
| 1116 |
+
if not self.exists:
|
| 1117 |
+
return self.coerce_path_result(rv)
|
| 1118 |
+
self.fail(
|
| 1119 |
+
_("{name} {filename!r} does not exist.").format(
|
| 1120 |
+
name=self.name.title(), filename=format_filename(value)
|
| 1121 |
+
),
|
| 1122 |
+
param,
|
| 1123 |
+
ctx,
|
| 1124 |
+
)
|
| 1125 |
+
|
| 1126 |
+
if not self.file_okay and stat.S_ISREG(st.st_mode):
|
| 1127 |
+
self.fail(
|
| 1128 |
+
_("{name} {filename!r} is a file.").format(
|
| 1129 |
+
name=self.name.title(), filename=format_filename(value)
|
| 1130 |
+
),
|
| 1131 |
+
param,
|
| 1132 |
+
ctx,
|
| 1133 |
+
)
|
| 1134 |
+
if not self.dir_okay and stat.S_ISDIR(st.st_mode):
|
| 1135 |
+
self.fail(
|
| 1136 |
+
_("{name} {filename!r} is a directory.").format(
|
| 1137 |
+
name=self.name.title(), filename=format_filename(value)
|
| 1138 |
+
),
|
| 1139 |
+
param,
|
| 1140 |
+
ctx,
|
| 1141 |
+
)
|
| 1142 |
+
|
| 1143 |
+
if self.readable and not os.access(rv, os.R_OK):
|
| 1144 |
+
self.fail(
|
| 1145 |
+
_("{name} {filename!r} is not readable.").format(
|
| 1146 |
+
name=self.name.title(), filename=format_filename(value)
|
| 1147 |
+
),
|
| 1148 |
+
param,
|
| 1149 |
+
ctx,
|
| 1150 |
+
)
|
| 1151 |
+
|
| 1152 |
+
if self.writable and not os.access(rv, os.W_OK):
|
| 1153 |
+
self.fail(
|
| 1154 |
+
_("{name} {filename!r} is not writable.").format(
|
| 1155 |
+
name=self.name.title(), filename=format_filename(value)
|
| 1156 |
+
),
|
| 1157 |
+
param,
|
| 1158 |
+
ctx,
|
| 1159 |
+
)
|
| 1160 |
+
|
| 1161 |
+
if self.executable and not os.access(value, os.X_OK):
|
| 1162 |
+
self.fail(
|
| 1163 |
+
_("{name} {filename!r} is not executable.").format(
|
| 1164 |
+
name=self.name.title(), filename=format_filename(value)
|
| 1165 |
+
),
|
| 1166 |
+
param,
|
| 1167 |
+
ctx,
|
| 1168 |
+
)
|
| 1169 |
+
|
| 1170 |
+
return self.coerce_path_result(rv)
|
| 1171 |
+
|
| 1172 |
+
def shell_complete(
|
| 1173 |
+
self, ctx: Context, param: Parameter, incomplete: str
|
| 1174 |
+
) -> list[CompletionItem]:
|
| 1175 |
+
"""Return a special completion marker that tells the completion
|
| 1176 |
+
system to use the shell to provide path completions for only
|
| 1177 |
+
directories or any paths.
|
| 1178 |
+
|
| 1179 |
+
:param ctx: Invocation context for this command.
|
| 1180 |
+
:param param: The parameter that is requesting completion.
|
| 1181 |
+
:param incomplete: Value being completed. May be empty.
|
| 1182 |
+
|
| 1183 |
+
.. versionadded:: 8.0
|
| 1184 |
+
"""
|
| 1185 |
+
from click.shell_completion import CompletionItem
|
| 1186 |
+
|
| 1187 |
+
type = "dir" if self.dir_okay and not self.file_okay else "file"
|
| 1188 |
+
return [CompletionItem(incomplete, type=type)]
|
| 1189 |
+
|
| 1190 |
+
|
| 1191 |
+
class TupleInfoDict(ParamTypeInfoDict):
|
| 1192 |
+
types: cabc.Sequence[ParamTypeInfoDict]
|
| 1193 |
+
|
| 1194 |
+
|
| 1195 |
+
class Tuple(CompositeParamType[tuple[t.Any, ...]]):
|
| 1196 |
+
"""The default behavior of Click is to apply a type on a value directly.
|
| 1197 |
+
This works well in most cases, except for when `nargs` is set to a fixed
|
| 1198 |
+
count and different types should be used for different items. In this
|
| 1199 |
+
case the :class:`Tuple` type can be used. This type can only be used
|
| 1200 |
+
if `nargs` is set to a fixed number.
|
| 1201 |
+
|
| 1202 |
+
For more information see :ref:`tuple-type`.
|
| 1203 |
+
|
| 1204 |
+
This can be selected by using a Python tuple literal as a type.
|
| 1205 |
+
|
| 1206 |
+
:param types: a list of types that should be used for the tuple items.
|
| 1207 |
+
"""
|
| 1208 |
+
|
| 1209 |
+
def __init__(self, types: cabc.Sequence[type[t.Any] | ParamType[t.Any]]) -> None:
|
| 1210 |
+
self.types: cabc.Sequence[ParamType[t.Any]] = [convert_type(ty) for ty in types]
|
| 1211 |
+
|
| 1212 |
+
def to_info_dict(self) -> TupleInfoDict:
|
| 1213 |
+
return {
|
| 1214 |
+
"types": [ty.to_info_dict() for ty in self.types],
|
| 1215 |
+
**super().to_info_dict(),
|
| 1216 |
+
}
|
| 1217 |
+
|
| 1218 |
+
@property
|
| 1219 |
+
def name(self) -> str: # type: ignore[override]
|
| 1220 |
+
return f"<{' '.join(ty.name for ty in self.types)}>"
|
| 1221 |
+
|
| 1222 |
+
@property
|
| 1223 |
+
def arity(self) -> int: # type: ignore[override]
|
| 1224 |
+
return len(self.types)
|
| 1225 |
+
|
| 1226 |
+
def convert(
|
| 1227 |
+
self, value: t.Any, param: Parameter | None, ctx: Context | None
|
| 1228 |
+
) -> tuple[t.Any, ...]:
|
| 1229 |
+
len_type = len(self.types)
|
| 1230 |
+
len_value = len(value)
|
| 1231 |
+
|
| 1232 |
+
if len_value != len_type:
|
| 1233 |
+
self.fail(
|
| 1234 |
+
ngettext(
|
| 1235 |
+
"{len_type} values are required, but {len_value} was given.",
|
| 1236 |
+
"{len_type} values are required, but {len_value} were given.",
|
| 1237 |
+
len_value,
|
| 1238 |
+
).format(len_type=len_type, len_value=len_value),
|
| 1239 |
+
param=param,
|
| 1240 |
+
ctx=ctx,
|
| 1241 |
+
)
|
| 1242 |
+
|
| 1243 |
+
return tuple(
|
| 1244 |
+
ty(x, param, ctx) for ty, x in zip(self.types, value, strict=False)
|
| 1245 |
+
)
|
| 1246 |
+
|
| 1247 |
+
|
| 1248 |
+
def _guess_type(
|
| 1249 |
+
ty: type[t.Any] | ParamType[t.Any] | None,
|
| 1250 |
+
default: t.Any | None,
|
| 1251 |
+
) -> type[t.Any] | tuple[type[t.Any], ...] | ParamType[t.Any] | None:
|
| 1252 |
+
"""Infer a type from *ty* or *default*.
|
| 1253 |
+
|
| 1254 |
+
Returns *ty* unchanged when it is not ``None``. Otherwise inspects
|
| 1255 |
+
*default* to produce a ``type``, a ``tuple`` of types (for tuple
|
| 1256 |
+
defaults), or ``None``.
|
| 1257 |
+
"""
|
| 1258 |
+
if ty is not None:
|
| 1259 |
+
return ty
|
| 1260 |
+
|
| 1261 |
+
if default is None:
|
| 1262 |
+
return None
|
| 1263 |
+
|
| 1264 |
+
if not isinstance(default, (tuple, list)):
|
| 1265 |
+
return type(default)
|
| 1266 |
+
|
| 1267 |
+
# If the default is empty, return None so convert_type falls
|
| 1268 |
+
# through to STRING.
|
| 1269 |
+
if not default:
|
| 1270 |
+
return None
|
| 1271 |
+
|
| 1272 |
+
item = default[0]
|
| 1273 |
+
|
| 1274 |
+
# A sequence of iterables needs to detect the inner types.
|
| 1275 |
+
# Can't call convert_type recursively because that would
|
| 1276 |
+
# incorrectly unwind the tuple to a single type.
|
| 1277 |
+
if isinstance(item, (tuple, list)):
|
| 1278 |
+
return tuple(map(type, item))
|
| 1279 |
+
|
| 1280 |
+
return type(item)
|
| 1281 |
+
|
| 1282 |
+
|
| 1283 |
+
@t.overload
|
| 1284 |
+
def convert_type(ty: None, default: None = None) -> StringParamType: ...
|
| 1285 |
+
@t.overload
|
| 1286 |
+
def convert_type(
|
| 1287 |
+
ty: type | ParamType[t.Any], default: t.Any | None = None
|
| 1288 |
+
) -> ParamType[t.Any]: ...
|
| 1289 |
+
@t.overload
|
| 1290 |
+
def convert_type(
|
| 1291 |
+
ty: t.Any | None, default: t.Any | None = None
|
| 1292 |
+
) -> ParamType[t.Any]: ...
|
| 1293 |
+
def convert_type(
|
| 1294 |
+
ty: t.Any | None = None, default: t.Any | None = None
|
| 1295 |
+
) -> ParamType[t.Any]:
|
| 1296 |
+
"""Find the most appropriate :class:`ParamType` for the given Python
|
| 1297 |
+
type. If the type isn't provided, it can be inferred from a default
|
| 1298 |
+
value.
|
| 1299 |
+
"""
|
| 1300 |
+
guessed = _guess_type(ty, default)
|
| 1301 |
+
is_guessed = guessed is not ty
|
| 1302 |
+
|
| 1303 |
+
if isinstance(guessed, tuple):
|
| 1304 |
+
return Tuple(guessed)
|
| 1305 |
+
|
| 1306 |
+
if isinstance(guessed, ParamType):
|
| 1307 |
+
return guessed
|
| 1308 |
+
|
| 1309 |
+
if guessed is str or guessed is None:
|
| 1310 |
+
return STRING
|
| 1311 |
+
|
| 1312 |
+
if guessed is int:
|
| 1313 |
+
return INT
|
| 1314 |
+
|
| 1315 |
+
if guessed is float:
|
| 1316 |
+
return FLOAT
|
| 1317 |
+
|
| 1318 |
+
if guessed is bool:
|
| 1319 |
+
return BOOL
|
| 1320 |
+
|
| 1321 |
+
if is_guessed:
|
| 1322 |
+
return STRING
|
| 1323 |
+
|
| 1324 |
+
if __debug__:
|
| 1325 |
+
try:
|
| 1326 |
+
if issubclass(guessed, ParamType):
|
| 1327 |
+
raise AssertionError(
|
| 1328 |
+
f"Attempted to use an uninstantiated parameter type ({guessed})."
|
| 1329 |
+
)
|
| 1330 |
+
except TypeError:
|
| 1331 |
+
# guessed is an instance (correct), so issubclass fails.
|
| 1332 |
+
pass
|
| 1333 |
+
|
| 1334 |
+
return FuncParamType(guessed)
|
| 1335 |
+
|
| 1336 |
+
|
| 1337 |
+
#: A dummy parameter type that just does nothing. From a user's
|
| 1338 |
+
#: perspective this appears to just be the same as `STRING` but
|
| 1339 |
+
#: internally no string conversion takes place if the input was bytes.
|
| 1340 |
+
#: This is usually useful when working with file paths as they can
|
| 1341 |
+
#: appear in bytes and unicode.
|
| 1342 |
+
#:
|
| 1343 |
+
#: For path related uses the :class:`Path` type is a better choice but
|
| 1344 |
+
#: there are situations where an unprocessed type is useful which is why
|
| 1345 |
+
#: it is provided.
|
| 1346 |
+
#:
|
| 1347 |
+
#: .. versionadded:: 4.0
|
| 1348 |
+
UNPROCESSED: t.Final[UnprocessedParamType] = UnprocessedParamType()
|
| 1349 |
+
|
| 1350 |
+
#: A unicode string parameter type which is the implicit default. This
|
| 1351 |
+
#: can also be selected by using ``str`` as type.
|
| 1352 |
+
STRING: t.Final[StringParamType] = StringParamType()
|
| 1353 |
+
|
| 1354 |
+
#: An integer parameter. This can also be selected by using ``int`` as
|
| 1355 |
+
#: type.
|
| 1356 |
+
INT: t.Final[IntParamType] = IntParamType()
|
| 1357 |
+
|
| 1358 |
+
#: A floating point value parameter. This can also be selected by using
|
| 1359 |
+
#: ``float`` as type.
|
| 1360 |
+
FLOAT: t.Final[FloatParamType] = FloatParamType()
|
| 1361 |
+
|
| 1362 |
+
#: A boolean parameter. This is the default for boolean flags. This can
|
| 1363 |
+
#: also be selected by using ``bool`` as a type.
|
| 1364 |
+
BOOL: t.Final[BoolParamType] = BoolParamType()
|
| 1365 |
+
|
| 1366 |
+
#: A UUID parameter.
|
| 1367 |
+
UUID: t.Final[UUIDParameterType] = UUIDParameterType()
|
| 1368 |
+
|
| 1369 |
+
|
| 1370 |
+
class OptionHelpExtra(t.TypedDict, total=False):
|
| 1371 |
+
envvars: tuple[str, ...]
|
| 1372 |
+
default: str
|
| 1373 |
+
range: str
|
| 1374 |
+
required: str
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/utils.py
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections.abc as cabc
|
| 4 |
+
import os
|
| 5 |
+
import re
|
| 6 |
+
import sys
|
| 7 |
+
import typing as t
|
| 8 |
+
from functools import update_wrapper
|
| 9 |
+
from gettext import gettext as _
|
| 10 |
+
from types import ModuleType
|
| 11 |
+
from types import TracebackType
|
| 12 |
+
|
| 13 |
+
from ._compat import _default_text_stderr
|
| 14 |
+
from ._compat import _default_text_stdout
|
| 15 |
+
from ._compat import _find_binary_writer
|
| 16 |
+
from ._compat import auto_wrap_for_ansi
|
| 17 |
+
from ._compat import binary_streams
|
| 18 |
+
from ._compat import open_stream
|
| 19 |
+
from ._compat import should_strip_ansi
|
| 20 |
+
from ._compat import strip_ansi
|
| 21 |
+
from ._compat import text_streams
|
| 22 |
+
from ._compat import WIN
|
| 23 |
+
from .globals import resolve_color_default
|
| 24 |
+
|
| 25 |
+
if t.TYPE_CHECKING:
|
| 26 |
+
import typing_extensions as te
|
| 27 |
+
|
| 28 |
+
P = te.ParamSpec("P")
|
| 29 |
+
|
| 30 |
+
R = t.TypeVar("R")
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def _posixify(name: str) -> str:
|
| 34 |
+
return "-".join(name.split()).lower()
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def safecall(func: t.Callable[P, R]) -> t.Callable[P, R | None]:
|
| 38 |
+
"""Wraps a function so that it swallows exceptions."""
|
| 39 |
+
|
| 40 |
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | None:
|
| 41 |
+
try:
|
| 42 |
+
return func(*args, **kwargs)
|
| 43 |
+
except Exception:
|
| 44 |
+
pass
|
| 45 |
+
return None
|
| 46 |
+
|
| 47 |
+
return update_wrapper(wrapper, func)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def make_str(value: t.Any) -> str:
|
| 51 |
+
"""Converts a value into a valid string."""
|
| 52 |
+
if isinstance(value, bytes):
|
| 53 |
+
try:
|
| 54 |
+
return value.decode(sys.getfilesystemencoding())
|
| 55 |
+
except UnicodeError:
|
| 56 |
+
return value.decode("utf-8", "replace")
|
| 57 |
+
return str(value)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def make_default_short_help(help: str, max_length: int = 45) -> str:
|
| 61 |
+
"""Returns a condensed version of help string.
|
| 62 |
+
|
| 63 |
+
:meta private:
|
| 64 |
+
"""
|
| 65 |
+
# Consider only the first paragraph.
|
| 66 |
+
paragraph_end = help.find("\n\n")
|
| 67 |
+
|
| 68 |
+
if paragraph_end != -1:
|
| 69 |
+
help = help[:paragraph_end]
|
| 70 |
+
|
| 71 |
+
# Collapse newlines, tabs, and spaces.
|
| 72 |
+
words = help.split()
|
| 73 |
+
|
| 74 |
+
if not words:
|
| 75 |
+
return ""
|
| 76 |
+
|
| 77 |
+
# The first paragraph started with a "no rewrap" marker, ignore it.
|
| 78 |
+
if words[0] == "\b":
|
| 79 |
+
words = words[1:]
|
| 80 |
+
|
| 81 |
+
total_length = 0
|
| 82 |
+
last_index = len(words) - 1
|
| 83 |
+
|
| 84 |
+
for i, word in enumerate(words):
|
| 85 |
+
total_length += len(word) + (i > 0)
|
| 86 |
+
|
| 87 |
+
if total_length > max_length: # too long, truncate
|
| 88 |
+
break
|
| 89 |
+
|
| 90 |
+
if word[-1] == ".": # sentence end, truncate without "..."
|
| 91 |
+
return " ".join(words[: i + 1])
|
| 92 |
+
|
| 93 |
+
if total_length == max_length and i != last_index:
|
| 94 |
+
break # not at sentence end, truncate with "..."
|
| 95 |
+
else:
|
| 96 |
+
return " ".join(words) # no truncation needed
|
| 97 |
+
|
| 98 |
+
# Account for the length of the suffix.
|
| 99 |
+
total_length += len("...")
|
| 100 |
+
|
| 101 |
+
# remove words until the length is short enough
|
| 102 |
+
while i > 0:
|
| 103 |
+
total_length -= len(words[i]) + (i > 0)
|
| 104 |
+
|
| 105 |
+
if total_length <= max_length:
|
| 106 |
+
break
|
| 107 |
+
|
| 108 |
+
i -= 1
|
| 109 |
+
|
| 110 |
+
return " ".join(words[:i]) + "..."
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
class LazyFile:
|
| 114 |
+
"""A lazy file works like a regular file but it does not fully open
|
| 115 |
+
the file but it does perform some basic checks early to see if the
|
| 116 |
+
filename parameter does make sense. This is useful for safely opening
|
| 117 |
+
files for writing.
|
| 118 |
+
"""
|
| 119 |
+
|
| 120 |
+
name: str
|
| 121 |
+
mode: str
|
| 122 |
+
encoding: str | None
|
| 123 |
+
errors: str | None
|
| 124 |
+
atomic: bool
|
| 125 |
+
_f: t.IO[t.Any] | None
|
| 126 |
+
should_close: bool
|
| 127 |
+
|
| 128 |
+
def __init__(
|
| 129 |
+
self,
|
| 130 |
+
filename: str | os.PathLike[str],
|
| 131 |
+
mode: str = "r",
|
| 132 |
+
encoding: str | None = None,
|
| 133 |
+
errors: str | None = "strict",
|
| 134 |
+
atomic: bool = False,
|
| 135 |
+
) -> None:
|
| 136 |
+
self.name = os.fspath(filename)
|
| 137 |
+
self.mode = mode
|
| 138 |
+
self.encoding = encoding
|
| 139 |
+
self.errors = errors
|
| 140 |
+
self.atomic = atomic
|
| 141 |
+
|
| 142 |
+
if self.name == "-":
|
| 143 |
+
self._f, self.should_close = open_stream(filename, mode, encoding, errors)
|
| 144 |
+
else:
|
| 145 |
+
if "r" in mode:
|
| 146 |
+
# Open and close the file in case we're opening it for
|
| 147 |
+
# reading so that we can catch at least some errors in
|
| 148 |
+
# some cases early.
|
| 149 |
+
open(filename, mode).close()
|
| 150 |
+
self._f = None
|
| 151 |
+
self.should_close = True
|
| 152 |
+
|
| 153 |
+
def __getattr__(self, name: str) -> t.Any:
|
| 154 |
+
return getattr(self.open(), name)
|
| 155 |
+
|
| 156 |
+
def __repr__(self) -> str:
|
| 157 |
+
if self._f is not None:
|
| 158 |
+
return repr(self._f)
|
| 159 |
+
return f"<unopened file '{format_filename(self.name)}' {self.mode}>"
|
| 160 |
+
|
| 161 |
+
def open(self) -> t.IO[t.Any]:
|
| 162 |
+
"""Opens the file if it's not yet open. This call might fail with
|
| 163 |
+
a :exc:`FileError`. Not handling this error will produce an error
|
| 164 |
+
that Click shows.
|
| 165 |
+
"""
|
| 166 |
+
if self._f is not None:
|
| 167 |
+
return self._f
|
| 168 |
+
try:
|
| 169 |
+
rv, self.should_close = open_stream(
|
| 170 |
+
self.name, self.mode, self.encoding, self.errors, atomic=self.atomic
|
| 171 |
+
)
|
| 172 |
+
except OSError as e:
|
| 173 |
+
from .exceptions import FileError
|
| 174 |
+
|
| 175 |
+
raise FileError(self.name, hint=e.strerror) from e
|
| 176 |
+
self._f = rv
|
| 177 |
+
return rv
|
| 178 |
+
|
| 179 |
+
def close(self) -> None:
|
| 180 |
+
"""Closes the underlying file, no matter what."""
|
| 181 |
+
if self._f is not None:
|
| 182 |
+
self._f.close()
|
| 183 |
+
|
| 184 |
+
def close_intelligently(self) -> None:
|
| 185 |
+
"""This function only closes the file if it was opened by the lazy
|
| 186 |
+
file wrapper. For instance this will never close stdin.
|
| 187 |
+
"""
|
| 188 |
+
if self.should_close:
|
| 189 |
+
self.close()
|
| 190 |
+
|
| 191 |
+
def __enter__(self) -> LazyFile:
|
| 192 |
+
return self
|
| 193 |
+
|
| 194 |
+
def __exit__(
|
| 195 |
+
self,
|
| 196 |
+
exc_type: type[BaseException] | None,
|
| 197 |
+
exc_value: BaseException | None,
|
| 198 |
+
tb: TracebackType | None,
|
| 199 |
+
) -> None:
|
| 200 |
+
self.close_intelligently()
|
| 201 |
+
|
| 202 |
+
def __iter__(self) -> cabc.Iterator[t.AnyStr]:
|
| 203 |
+
self.open()
|
| 204 |
+
return iter(self._f) # type: ignore
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
class KeepOpenFile:
|
| 208 |
+
"""Proxy a file object but keep it open across a ``with`` block.
|
| 209 |
+
|
| 210 |
+
Wraps a borrowed file (such as ``sys.stdin`` or ``sys.stdout``) so that
|
| 211 |
+
leaving a ``with`` block does not close it, as used by :func:`open_file`
|
| 212 |
+
for the ``-`` filename. The caller stays responsible for the file: an
|
| 213 |
+
explicit :meth:`close` still passes through to the wrapped object.
|
| 214 |
+
|
| 215 |
+
Dunder methods are proxied explicitly: implicit special-method lookups
|
| 216 |
+
bypass :meth:`__getattr__`, because Python resolves them on the type rather
|
| 217 |
+
than the instance.
|
| 218 |
+
"""
|
| 219 |
+
|
| 220 |
+
_file: t.IO[t.Any]
|
| 221 |
+
|
| 222 |
+
def __init__(self, file: t.IO[t.Any]) -> None:
|
| 223 |
+
self._file = file
|
| 224 |
+
|
| 225 |
+
def __getattr__(self, name: str) -> t.Any:
|
| 226 |
+
return getattr(self._file, name)
|
| 227 |
+
|
| 228 |
+
def __enter__(self) -> KeepOpenFile:
|
| 229 |
+
return self
|
| 230 |
+
|
| 231 |
+
def __exit__(
|
| 232 |
+
self,
|
| 233 |
+
exc_type: type[BaseException] | None,
|
| 234 |
+
exc_value: BaseException | None,
|
| 235 |
+
tb: TracebackType | None,
|
| 236 |
+
) -> None:
|
| 237 |
+
pass
|
| 238 |
+
|
| 239 |
+
def __repr__(self) -> str:
|
| 240 |
+
return repr(self._file)
|
| 241 |
+
|
| 242 |
+
def __iter__(self) -> cabc.Iterator[t.AnyStr]:
|
| 243 |
+
return iter(self._file)
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
def echo(
|
| 247 |
+
message: object = None,
|
| 248 |
+
file: t.IO[t.Any] | None = None,
|
| 249 |
+
nl: bool = True,
|
| 250 |
+
err: bool = False,
|
| 251 |
+
color: bool | None = None,
|
| 252 |
+
) -> None:
|
| 253 |
+
"""Print a message and newline to stdout or a file. This should be
|
| 254 |
+
used instead of :func:`print` because it provides better support
|
| 255 |
+
for different data, files, and environments.
|
| 256 |
+
|
| 257 |
+
Compared to :func:`print`, this does the following:
|
| 258 |
+
|
| 259 |
+
- Ensures that the output encoding is not misconfigured on Linux.
|
| 260 |
+
- Supports Unicode in the Windows console.
|
| 261 |
+
- Supports writing to binary outputs, and supports writing bytes
|
| 262 |
+
to text outputs.
|
| 263 |
+
- Supports colors and styles on Windows.
|
| 264 |
+
- Removes ANSI color and style codes if the output does not look
|
| 265 |
+
like an interactive terminal.
|
| 266 |
+
- Always flushes the output.
|
| 267 |
+
|
| 268 |
+
:param message: The string or bytes to output. Other objects are
|
| 269 |
+
converted to strings.
|
| 270 |
+
:param file: The file to write to. Defaults to ``stdout``.
|
| 271 |
+
:param err: Write to ``stderr`` instead of ``stdout``.
|
| 272 |
+
:param nl: Print a newline after the message. Enabled by default.
|
| 273 |
+
:param color: Force showing or hiding colors and other styles. By
|
| 274 |
+
default Click will remove color if the output does not look like
|
| 275 |
+
an interactive terminal.
|
| 276 |
+
|
| 277 |
+
.. versionchanged:: 6.0
|
| 278 |
+
Support Unicode output on the Windows console. Click does not
|
| 279 |
+
modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()``
|
| 280 |
+
will still not support Unicode.
|
| 281 |
+
|
| 282 |
+
.. versionchanged:: 4.0
|
| 283 |
+
Added the ``color`` parameter.
|
| 284 |
+
|
| 285 |
+
.. versionadded:: 3.0
|
| 286 |
+
Added the ``err`` parameter.
|
| 287 |
+
|
| 288 |
+
.. versionchanged:: 2.0
|
| 289 |
+
Support colors on Windows if colorama is installed.
|
| 290 |
+
"""
|
| 291 |
+
if file is None:
|
| 292 |
+
if err:
|
| 293 |
+
file = _default_text_stderr()
|
| 294 |
+
else:
|
| 295 |
+
file = _default_text_stdout()
|
| 296 |
+
|
| 297 |
+
# There are no standard streams attached to write to. For example,
|
| 298 |
+
# pythonw on Windows.
|
| 299 |
+
if file is None:
|
| 300 |
+
return
|
| 301 |
+
|
| 302 |
+
match message:
|
| 303 |
+
case str() | bytes() | bytearray():
|
| 304 |
+
out = message
|
| 305 |
+
case None:
|
| 306 |
+
out = ""
|
| 307 |
+
case _:
|
| 308 |
+
out = str(message)
|
| 309 |
+
|
| 310 |
+
if nl:
|
| 311 |
+
if isinstance(out, str):
|
| 312 |
+
out += "\n"
|
| 313 |
+
else:
|
| 314 |
+
out += b"\n"
|
| 315 |
+
|
| 316 |
+
if not out:
|
| 317 |
+
file.flush()
|
| 318 |
+
return
|
| 319 |
+
|
| 320 |
+
# If there is a message and the value looks like bytes, we manually
|
| 321 |
+
# need to find the binary stream and write the message in there.
|
| 322 |
+
# This is done separately so that most stream types will work as you
|
| 323 |
+
# would expect. Eg: you can write to StringIO for other cases.
|
| 324 |
+
if isinstance(out, (bytes, bytearray)):
|
| 325 |
+
binary_file = _find_binary_writer(file)
|
| 326 |
+
if binary_file is not None:
|
| 327 |
+
file.flush()
|
| 328 |
+
binary_file.write(out)
|
| 329 |
+
binary_file.flush()
|
| 330 |
+
return
|
| 331 |
+
|
| 332 |
+
# ANSI style code support. For no message or bytes, nothing happens.
|
| 333 |
+
# When outputting to a file instead of a terminal, strip codes.
|
| 334 |
+
else:
|
| 335 |
+
color = resolve_color_default(color)
|
| 336 |
+
|
| 337 |
+
if should_strip_ansi(file, color):
|
| 338 |
+
out = strip_ansi(out)
|
| 339 |
+
elif WIN:
|
| 340 |
+
if auto_wrap_for_ansi is not None:
|
| 341 |
+
file = auto_wrap_for_ansi(file, color) # type: ignore
|
| 342 |
+
elif not color:
|
| 343 |
+
out = strip_ansi(out)
|
| 344 |
+
|
| 345 |
+
file.write(out) # type: ignore
|
| 346 |
+
file.flush()
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
def get_binary_stream(name: t.Literal["stdin", "stdout", "stderr"]) -> t.BinaryIO:
|
| 350 |
+
"""Returns a system stream for byte processing.
|
| 351 |
+
|
| 352 |
+
:param name: the name of the stream to open. Valid names are ``'stdin'``,
|
| 353 |
+
``'stdout'`` and ``'stderr'``
|
| 354 |
+
"""
|
| 355 |
+
opener = binary_streams.get(name)
|
| 356 |
+
if opener is None:
|
| 357 |
+
raise TypeError(_("Unknown standard stream '{name}'").format(name=name))
|
| 358 |
+
return opener()
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
def get_text_stream(
|
| 362 |
+
name: t.Literal["stdin", "stdout", "stderr"],
|
| 363 |
+
encoding: str | None = None,
|
| 364 |
+
errors: str | None = "strict",
|
| 365 |
+
) -> t.TextIO:
|
| 366 |
+
"""Returns a system stream for text processing. This usually returns
|
| 367 |
+
a wrapped stream around a binary stream returned from
|
| 368 |
+
:func:`get_binary_stream` but it also can take shortcuts for already
|
| 369 |
+
correctly configured streams.
|
| 370 |
+
|
| 371 |
+
:param name: the name of the stream to open. Valid names are ``'stdin'``,
|
| 372 |
+
``'stdout'`` and ``'stderr'``
|
| 373 |
+
:param encoding: overrides the detected default encoding.
|
| 374 |
+
:param errors: overrides the default error mode.
|
| 375 |
+
"""
|
| 376 |
+
opener = text_streams.get(name)
|
| 377 |
+
if opener is None:
|
| 378 |
+
raise TypeError(_("Unknown standard stream '{name}'").format(name=name))
|
| 379 |
+
return opener(encoding, errors)
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
def open_file(
|
| 383 |
+
filename: str | os.PathLike[str],
|
| 384 |
+
mode: str = "r",
|
| 385 |
+
encoding: str | None = None,
|
| 386 |
+
errors: str | None = "strict",
|
| 387 |
+
lazy: bool = False,
|
| 388 |
+
atomic: bool = False,
|
| 389 |
+
) -> t.IO[t.Any]:
|
| 390 |
+
"""Open a file, with extra behavior to handle ``'-'`` to indicate
|
| 391 |
+
a standard stream, lazy open on write, and atomic write. Similar to
|
| 392 |
+
the behavior of the :class:`~click.File` param type.
|
| 393 |
+
|
| 394 |
+
If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is
|
| 395 |
+
wrapped so that using it in a context manager will not close it.
|
| 396 |
+
This makes it possible to use the function without accidentally
|
| 397 |
+
closing a standard stream:
|
| 398 |
+
|
| 399 |
+
.. code-block:: python
|
| 400 |
+
|
| 401 |
+
with open_file(filename) as f:
|
| 402 |
+
...
|
| 403 |
+
|
| 404 |
+
:param filename: The name or Path of the file to open, or ``'-'`` for
|
| 405 |
+
``stdin``/``stdout``.
|
| 406 |
+
:param mode: The mode in which to open the file.
|
| 407 |
+
:param encoding: The encoding to decode or encode a file opened in
|
| 408 |
+
text mode.
|
| 409 |
+
:param errors: The error handling mode.
|
| 410 |
+
:param lazy: Wait to open the file until it is accessed. For read
|
| 411 |
+
mode, the file is temporarily opened to raise access errors
|
| 412 |
+
early, then closed until it is read again.
|
| 413 |
+
:param atomic: Write to a temporary file and replace the given file
|
| 414 |
+
on close.
|
| 415 |
+
|
| 416 |
+
.. versionadded:: 3.0
|
| 417 |
+
"""
|
| 418 |
+
if lazy:
|
| 419 |
+
return t.cast(
|
| 420 |
+
"t.IO[t.Any]", LazyFile(filename, mode, encoding, errors, atomic=atomic)
|
| 421 |
+
)
|
| 422 |
+
|
| 423 |
+
f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic)
|
| 424 |
+
|
| 425 |
+
if not should_close:
|
| 426 |
+
f = t.cast("t.IO[t.Any]", KeepOpenFile(f))
|
| 427 |
+
|
| 428 |
+
return f
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
def format_filename(
|
| 432 |
+
filename: str | bytes | os.PathLike[str] | os.PathLike[bytes],
|
| 433 |
+
shorten: bool = False,
|
| 434 |
+
) -> str:
|
| 435 |
+
"""Format a filename as a string for display. Ensures the filename can be
|
| 436 |
+
displayed by replacing any invalid bytes or surrogate escapes in the name
|
| 437 |
+
with the replacement character ``�``.
|
| 438 |
+
|
| 439 |
+
Invalid bytes or surrogate escapes will raise an error when written to a
|
| 440 |
+
stream with ``errors="strict"``. This will typically happen with ``stdout``
|
| 441 |
+
when the locale is something like ``en_GB.UTF-8``.
|
| 442 |
+
|
| 443 |
+
Many scenarios *are* safe to write surrogates though, due to PEP 538 and
|
| 444 |
+
PEP 540, including:
|
| 445 |
+
|
| 446 |
+
- Writing to ``stderr``, which uses ``errors="backslashreplace"``.
|
| 447 |
+
- The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens
|
| 448 |
+
stdout and stderr with ``errors="surrogateescape"``.
|
| 449 |
+
- None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``.
|
| 450 |
+
- Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``.
|
| 451 |
+
Python opens stdout and stderr with ``errors="surrogateescape"``.
|
| 452 |
+
|
| 453 |
+
:param filename: formats a filename for UI display. This will also convert
|
| 454 |
+
the filename into unicode without failing.
|
| 455 |
+
:param shorten: this optionally shortens the filename to strip of the
|
| 456 |
+
path that leads up to it.
|
| 457 |
+
"""
|
| 458 |
+
if shorten:
|
| 459 |
+
filename = os.path.basename(filename)
|
| 460 |
+
else:
|
| 461 |
+
filename = os.fspath(filename)
|
| 462 |
+
|
| 463 |
+
if isinstance(filename, bytes):
|
| 464 |
+
filename = filename.decode(sys.getfilesystemencoding(), "replace")
|
| 465 |
+
else:
|
| 466 |
+
filename = filename.encode("utf-8", "surrogateescape").decode(
|
| 467 |
+
"utf-8", "replace"
|
| 468 |
+
)
|
| 469 |
+
|
| 470 |
+
return filename
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str:
|
| 474 |
+
r"""Returns the config folder for the application. The default behavior
|
| 475 |
+
is to return whatever is most appropriate for the operating system.
|
| 476 |
+
|
| 477 |
+
To give you an idea, for an app called ``"Foo Bar"``, something like
|
| 478 |
+
the following folders could be returned:
|
| 479 |
+
|
| 480 |
+
Mac OS X:
|
| 481 |
+
``~/Library/Application Support/Foo Bar``
|
| 482 |
+
Mac OS X (POSIX):
|
| 483 |
+
``~/.foo-bar``
|
| 484 |
+
Unix:
|
| 485 |
+
``~/.config/foo-bar``
|
| 486 |
+
Unix (POSIX):
|
| 487 |
+
``~/.foo-bar``
|
| 488 |
+
Windows (roaming):
|
| 489 |
+
``C:\Users\<user>\AppData\Roaming\Foo Bar``
|
| 490 |
+
Windows (not roaming):
|
| 491 |
+
``C:\Users\<user>\AppData\Local\Foo Bar``
|
| 492 |
+
|
| 493 |
+
.. versionadded:: 2.0
|
| 494 |
+
|
| 495 |
+
:param app_name: the application name. This should be properly capitalized
|
| 496 |
+
and can contain whitespace.
|
| 497 |
+
:param roaming: controls if the folder should be roaming or not on Windows.
|
| 498 |
+
Has no effect otherwise.
|
| 499 |
+
:param force_posix: if this is set to `True` then on any POSIX system the
|
| 500 |
+
folder will be stored in the home folder with a leading
|
| 501 |
+
dot instead of the XDG config home or darwin's
|
| 502 |
+
application support folder.
|
| 503 |
+
"""
|
| 504 |
+
if WIN:
|
| 505 |
+
key = "APPDATA" if roaming else "LOCALAPPDATA"
|
| 506 |
+
folder = os.environ.get(key)
|
| 507 |
+
if folder is None:
|
| 508 |
+
folder = os.path.expanduser("~")
|
| 509 |
+
return os.path.join(folder, app_name)
|
| 510 |
+
if force_posix:
|
| 511 |
+
return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}"))
|
| 512 |
+
if sys.platform == "darwin":
|
| 513 |
+
return os.path.join(
|
| 514 |
+
os.path.expanduser("~/Library/Application Support"), app_name
|
| 515 |
+
)
|
| 516 |
+
return os.path.join(
|
| 517 |
+
os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")),
|
| 518 |
+
_posixify(app_name),
|
| 519 |
+
)
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
class PacifyFlushWrapper:
|
| 523 |
+
"""This wrapper is used to catch and suppress BrokenPipeErrors resulting
|
| 524 |
+
from ``.flush()`` being called on broken pipe during the shutdown/final-GC
|
| 525 |
+
of the Python interpreter. Notably ``.flush()`` is always called on
|
| 526 |
+
``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any
|
| 527 |
+
other cleanup code, and the case where the underlying file is not a broken
|
| 528 |
+
pipe, all calls and attributes are proxied.
|
| 529 |
+
"""
|
| 530 |
+
|
| 531 |
+
wrapped: t.IO[t.Any]
|
| 532 |
+
|
| 533 |
+
def __init__(self, wrapped: t.IO[t.Any]) -> None:
|
| 534 |
+
self.wrapped = wrapped
|
| 535 |
+
|
| 536 |
+
def flush(self) -> None:
|
| 537 |
+
try:
|
| 538 |
+
self.wrapped.flush()
|
| 539 |
+
except OSError as e:
|
| 540 |
+
import errno
|
| 541 |
+
|
| 542 |
+
if e.errno != errno.EPIPE:
|
| 543 |
+
raise
|
| 544 |
+
|
| 545 |
+
def __getattr__(self, attr: str) -> t.Any:
|
| 546 |
+
return getattr(self.wrapped, attr)
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
def _detect_program_name(
|
| 550 |
+
path: str | None = None, _main: ModuleType | None = None
|
| 551 |
+
) -> str:
|
| 552 |
+
"""Determine the command used to run the program, for use in help
|
| 553 |
+
text. If a file or entry point was executed, the file name is
|
| 554 |
+
returned. If ``python -m`` was used to execute a module or package,
|
| 555 |
+
``python -m name`` is returned.
|
| 556 |
+
|
| 557 |
+
This doesn't try to be too precise, the goal is to give a concise
|
| 558 |
+
name for help text. Files are only shown as their name without the
|
| 559 |
+
path. ``python`` is only shown for modules, and the full path to
|
| 560 |
+
``sys.executable`` is not shown.
|
| 561 |
+
|
| 562 |
+
:param path: The Python file being executed. Python puts this in
|
| 563 |
+
``sys.argv[0]``, which is used by default.
|
| 564 |
+
:param _main: The ``__main__`` module. This should only be passed
|
| 565 |
+
during internal testing.
|
| 566 |
+
|
| 567 |
+
.. versionadded:: 8.0
|
| 568 |
+
Based on command args detection in the Werkzeug reloader.
|
| 569 |
+
|
| 570 |
+
:meta private:
|
| 571 |
+
"""
|
| 572 |
+
if _main is None:
|
| 573 |
+
_main = sys.modules["__main__"]
|
| 574 |
+
|
| 575 |
+
if not path:
|
| 576 |
+
path = sys.argv[0]
|
| 577 |
+
|
| 578 |
+
# The value of __package__ indicates how Python was called. It may
|
| 579 |
+
# not exist if a setuptools script is installed as an egg. It may be
|
| 580 |
+
# set incorrectly for entry points created with pip on Windows.
|
| 581 |
+
# It is set to "" inside a Shiv or PEX zipapp.
|
| 582 |
+
if getattr(_main, "__package__", None) in {None, ""} or (
|
| 583 |
+
os.name == "nt"
|
| 584 |
+
and _main.__package__ == ""
|
| 585 |
+
and not os.path.exists(path)
|
| 586 |
+
and os.path.exists(f"{path}.exe")
|
| 587 |
+
):
|
| 588 |
+
# Executed a file, like "python app.py".
|
| 589 |
+
return os.path.basename(path)
|
| 590 |
+
|
| 591 |
+
# Executed a module, like "python -m example".
|
| 592 |
+
# Rewritten by Python from "-m script" to "/path/to/script.py".
|
| 593 |
+
# Need to look at main module to determine how it was executed.
|
| 594 |
+
py_module = t.cast(str, _main.__package__)
|
| 595 |
+
name = os.path.splitext(os.path.basename(path))[0]
|
| 596 |
+
|
| 597 |
+
# A submodule like "example.cli".
|
| 598 |
+
if name != "__main__":
|
| 599 |
+
py_module = f"{py_module}.{name}"
|
| 600 |
+
|
| 601 |
+
return f"python -m {py_module.lstrip('.')}"
|
| 602 |
+
|
| 603 |
+
|
| 604 |
+
def _expand_args(
|
| 605 |
+
args: cabc.Iterable[str],
|
| 606 |
+
*,
|
| 607 |
+
user: bool = True,
|
| 608 |
+
env: bool = True,
|
| 609 |
+
glob_recursive: bool = True,
|
| 610 |
+
) -> list[str]:
|
| 611 |
+
"""Simulate Unix shell expansion with Python functions.
|
| 612 |
+
|
| 613 |
+
See :func:`glob.glob`, :func:`os.path.expanduser`, and
|
| 614 |
+
:func:`os.path.expandvars`.
|
| 615 |
+
|
| 616 |
+
This is intended for use on Windows, where the shell does not do any
|
| 617 |
+
expansion. It may not exactly match what a Unix shell would do.
|
| 618 |
+
|
| 619 |
+
:param args: List of command line arguments to expand.
|
| 620 |
+
:param user: Expand user home directory.
|
| 621 |
+
:param env: Expand environment variables.
|
| 622 |
+
:param glob_recursive: ``**`` matches directories recursively.
|
| 623 |
+
|
| 624 |
+
.. versionchanged:: 8.1
|
| 625 |
+
Invalid glob patterns are treated as empty expansions rather
|
| 626 |
+
than raising an error.
|
| 627 |
+
|
| 628 |
+
.. versionadded:: 8.0
|
| 629 |
+
|
| 630 |
+
:meta private:
|
| 631 |
+
"""
|
| 632 |
+
from glob import glob
|
| 633 |
+
|
| 634 |
+
out = []
|
| 635 |
+
|
| 636 |
+
for arg in args:
|
| 637 |
+
if user:
|
| 638 |
+
arg = os.path.expanduser(arg)
|
| 639 |
+
|
| 640 |
+
if env:
|
| 641 |
+
arg = os.path.expandvars(arg)
|
| 642 |
+
|
| 643 |
+
try:
|
| 644 |
+
matches = glob(arg, recursive=glob_recursive)
|
| 645 |
+
except re.error:
|
| 646 |
+
matches = []
|
| 647 |
+
|
| 648 |
+
if not matches:
|
| 649 |
+
out.append(arg)
|
| 650 |
+
else:
|
| 651 |
+
out.extend(matches)
|
| 652 |
+
|
| 653 |
+
return out
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
uv
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: cryptography
|
| 3 |
+
Version: 49.0.0
|
| 4 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 5 |
+
Classifier: Intended Audience :: Developers
|
| 6 |
+
Classifier: Natural Language :: English
|
| 7 |
+
Classifier: Operating System :: MacOS :: MacOS X
|
| 8 |
+
Classifier: Operating System :: POSIX
|
| 9 |
+
Classifier: Operating System :: POSIX :: BSD
|
| 10 |
+
Classifier: Operating System :: POSIX :: Linux
|
| 11 |
+
Classifier: Operating System :: Microsoft :: Windows
|
| 12 |
+
Classifier: Programming Language :: Python
|
| 13 |
+
Classifier: Programming Language :: Python :: 3
|
| 14 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 15 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.14
|
| 21 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 22 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 23 |
+
Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable
|
| 24 |
+
Classifier: Topic :: Security :: Cryptography
|
| 25 |
+
Requires-Dist: cffi>=2.0.0 ; platform_python_implementation != 'PyPy'
|
| 26 |
+
Requires-Dist: typing-extensions>=4.13.2 ; python_full_version < '3.11'
|
| 27 |
+
Requires-Dist: bcrypt>=3.1.5 ; extra == 'ssh'
|
| 28 |
+
Provides-Extra: ssh
|
| 29 |
+
License-File: LICENSE
|
| 30 |
+
License-File: LICENSE.APACHE
|
| 31 |
+
License-File: LICENSE.BSD
|
| 32 |
+
Summary: cryptography is a package which provides cryptographic recipes and primitives to Python developers.
|
| 33 |
+
Author-email: The Python Cryptographic Authority and individual contributors <cryptography-dev@python.org>
|
| 34 |
+
License-Expression: Apache-2.0 OR BSD-3-Clause
|
| 35 |
+
Requires-Python: >=3.9, !=3.9.0, !=3.9.1
|
| 36 |
+
Description-Content-Type: text/x-rst; charset=UTF-8
|
| 37 |
+
Project-URL: changelog, https://cryptography.io/en/latest/changelog/
|
| 38 |
+
Project-URL: documentation, https://cryptography.io/
|
| 39 |
+
Project-URL: homepage, https://github.com/pyca/cryptography
|
| 40 |
+
Project-URL: issues, https://github.com/pyca/cryptography/issues
|
| 41 |
+
Project-URL: source, https://github.com/pyca/cryptography/
|
| 42 |
+
|
| 43 |
+
pyca/cryptography
|
| 44 |
+
=================
|
| 45 |
+
|
| 46 |
+
.. image:: https://img.shields.io/pypi/v/cryptography.svg
|
| 47 |
+
:target: https://pypi.org/project/cryptography/
|
| 48 |
+
:alt: Latest Version
|
| 49 |
+
|
| 50 |
+
.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest
|
| 51 |
+
:target: https://cryptography.io
|
| 52 |
+
:alt: Latest Docs
|
| 53 |
+
|
| 54 |
+
.. image:: https://github.com/pyca/cryptography/actions/workflows/ci.yml/badge.svg
|
| 55 |
+
:target: https://github.com/pyca/cryptography/actions/workflows/ci.yml?query=branch%3Amain
|
| 56 |
+
|
| 57 |
+
``cryptography`` is a package which provides cryptographic recipes and
|
| 58 |
+
primitives to Python developers. Our goal is for it to be your "cryptographic
|
| 59 |
+
standard library". It supports Python 3.9+ and PyPy3 7.3.11+.
|
| 60 |
+
|
| 61 |
+
``cryptography`` includes both high level recipes and low level interfaces to
|
| 62 |
+
common cryptographic algorithms such as symmetric ciphers, message digests, and
|
| 63 |
+
key derivation functions. For example, to encrypt something with
|
| 64 |
+
``cryptography``'s high level symmetric encryption recipe:
|
| 65 |
+
|
| 66 |
+
.. code-block:: pycon
|
| 67 |
+
|
| 68 |
+
>>> from cryptography.fernet import Fernet
|
| 69 |
+
>>> # Put this somewhere safe!
|
| 70 |
+
>>> key = Fernet.generate_key()
|
| 71 |
+
>>> f = Fernet(key)
|
| 72 |
+
>>> token = f.encrypt(b"A really secret message. Not for prying eyes.")
|
| 73 |
+
>>> token
|
| 74 |
+
b'...'
|
| 75 |
+
>>> f.decrypt(token)
|
| 76 |
+
b'A really secret message. Not for prying eyes.'
|
| 77 |
+
|
| 78 |
+
You can find more information in the `documentation`_.
|
| 79 |
+
|
| 80 |
+
You can install ``cryptography`` with:
|
| 81 |
+
|
| 82 |
+
.. code-block:: console
|
| 83 |
+
|
| 84 |
+
$ pip install cryptography
|
| 85 |
+
|
| 86 |
+
For full details see `the installation documentation`_.
|
| 87 |
+
|
| 88 |
+
Discussion
|
| 89 |
+
~~~~~~~~~~
|
| 90 |
+
|
| 91 |
+
If you run into bugs, you can file them in our `issue tracker`_.
|
| 92 |
+
|
| 93 |
+
We maintain a `cryptography-dev`_ mailing list for development discussion.
|
| 94 |
+
|
| 95 |
+
You can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get
|
| 96 |
+
involved.
|
| 97 |
+
|
| 98 |
+
Security
|
| 99 |
+
~~~~~~~~
|
| 100 |
+
|
| 101 |
+
Need to report a security issue? Please consult our `security reporting`_
|
| 102 |
+
documentation.
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
.. _`documentation`: https://cryptography.io/
|
| 106 |
+
.. _`the installation documentation`: https://cryptography.io/en/latest/installation/
|
| 107 |
+
.. _`issue tracker`: https://github.com/pyca/cryptography/issues
|
| 108 |
+
.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev
|
| 109 |
+
.. _`security reporting`: https://cryptography.io/en/latest/security/
|
| 110 |
+
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/RECORD
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cryptography-49.0.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
| 2 |
+
cryptography-49.0.0.dist-info/METADATA,sha256=H8g50k78NV8OXFB6qAeCK8pDcecyYDttPpKyQDje3tI,4343
|
| 3 |
+
cryptography-49.0.0.dist-info/RECORD,,
|
| 4 |
+
cryptography-49.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 5 |
+
cryptography-49.0.0.dist-info/WHEEL,sha256=yds3s-EOIh3Qh5fy-EThwQlavlFqkvmruLJ9hoo7cKU,109
|
| 6 |
+
cryptography-49.0.0.dist-info/licenses/LICENSE,sha256=Pgx8CRqUi4JTO6mP18u0BDLW8amsv4X1ki0vmak65rs,197
|
| 7 |
+
cryptography-49.0.0.dist-info/licenses/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360
|
| 8 |
+
cryptography-49.0.0.dist-info/licenses/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532
|
| 9 |
+
cryptography-49.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json,sha256=4S_yV9ChMHszuAcR5zumAi5pcTbH7o7cniXi7w2bOHM,45648
|
| 10 |
+
cryptography-49.0.0.dist-info/sboms/sbom.json,sha256=WYwNSGvh_69UziImCyow4F7_yrWzpuiwmUyKIHhkK0E,1206
|
| 11 |
+
cryptography/__about__.py,sha256=p2cyvtrX_xcEkq3BVREzUdakMwPxE5Npl9xNKBvay-k,445
|
| 12 |
+
cryptography/__init__.py,sha256=mthuUrTd4FROCpUYrTIqhjz6s6T9djAZrV7nZ1oMm2o,364
|
| 13 |
+
cryptography/exceptions.py,sha256=835EWILc2fwxw-gyFMriciC2SqhViETB10LBSytnDIc,1087
|
| 14 |
+
cryptography/fernet.py,sha256=3Cvxkh0KJSbX8HbnCHu4wfCW7U0GgfUA3v_qQ8a8iWc,6963
|
| 15 |
+
cryptography/hazmat/__init__.py,sha256=5IwrLWrVp0AjEr_4FdWG_V057NSJGY_W4egNNsuct0g,455
|
| 16 |
+
cryptography/hazmat/_oid.py,sha256=mHJL2m4LnXNqetu_yX8QnNb8UaOuX4y59uKZEGukT1o,17879
|
| 17 |
+
cryptography/hazmat/asn1/__init__.py,sha256=30QNQSTZyAfiM8l9SStLnV20d6efznwm4e43MkTHAVY,776
|
| 18 |
+
cryptography/hazmat/asn1/asn1.py,sha256=9mcfrmdvK7KtjhRYofMzAjW0u7nvqbfsvPfbhNQK1BQ,19152
|
| 19 |
+
cryptography/hazmat/backends/__init__.py,sha256=O5jvKFQdZnXhKeqJ-HtulaEL9Ni7mr1mDzZY5kHlYhI,361
|
| 20 |
+
cryptography/hazmat/backends/openssl/__init__.py,sha256=p3jmJfnCag9iE5sdMrN6VvVEu55u46xaS_IjoI0SrmA,305
|
| 21 |
+
cryptography/hazmat/backends/openssl/backend.py,sha256=3ReyMe3ee_A8-3fSroGu7xu6pPevXQLt9Wd5O9xxecY,10591
|
| 22 |
+
cryptography/hazmat/bindings/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180
|
| 23 |
+
cryptography/hazmat/bindings/_rust.abi3.so,sha256=tm1LJ1tYFJ8dOu7SXx9hKMX8xw-IlolUB9pv6ka-Et8,13847936
|
| 24 |
+
cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=uUnnvOq_uameYnBNpmsv3SRAVO0WQGpjI58aR1lL4Kw,2285
|
| 25 |
+
cryptography/hazmat/bindings/_rust/_openssl.pyi,sha256=T_5TpYO8bghIPjAQ3BtAABqw-CcYskHp92wm5PNY6dc,228
|
| 26 |
+
cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=BrGjC8J6nwuS-r3EVcdXJB8ndotfY9mbQYOfpbPG0HA,354
|
| 27 |
+
cryptography/hazmat/bindings/_rust/declarative_asn1.pyi,sha256=MBmOZzLTdmPJ6-1K6Aq3PTuHJ1MGgRfIu0hIgag7ocE,3866
|
| 28 |
+
cryptography/hazmat/bindings/_rust/exceptions.pyi,sha256=exXr2xw_0pB1kk93cYbM3MohbzoUkjOms1ZMUi0uQZE,640
|
| 29 |
+
cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=VPVWuKHI9EMs09ZLRYAGvR0Iz0mCMmEzXAkgJHovpoM,4020
|
| 30 |
+
cryptography/hazmat/bindings/_rust/openssl/__init__.pyi,sha256=JDCef29vXnrEMvF13JH9z1-W9ahiTZQAk5xhu_KH6po,1550
|
| 31 |
+
cryptography/hazmat/bindings/_rust/openssl/aead.pyi,sha256=7d--xdc0vuzRe4S2uuQaGm-9GGKv4NcSLQcLyWcaD6s,4582
|
| 32 |
+
cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi,sha256=LhPzHWSXJq4grAJXn6zSvSSdV-aYIIscHDwIPlJGGPs,1315
|
| 33 |
+
cryptography/hazmat/bindings/_rust/openssl/cmac.pyi,sha256=nPH0X57RYpsAkRowVpjQiHE566ThUTx7YXrsadmrmHk,564
|
| 34 |
+
cryptography/hazmat/bindings/_rust/openssl/dh.pyi,sha256=Z3TC-G04-THtSdAOPLM1h2G7ml5bda1ElZUcn5wpuhk,1564
|
| 35 |
+
cryptography/hazmat/bindings/_rust/openssl/dsa.pyi,sha256=qBtkgj2albt2qFcnZ9UDrhzoNhCVO7HTby5VSf1EXMI,1299
|
| 36 |
+
cryptography/hazmat/bindings/_rust/openssl/ec.pyi,sha256=zJy0pRa5n-_p2dm45PxECB_-B6SVZyNKfjxFDpPqT38,1691
|
| 37 |
+
cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi,sha256=VXfXd5G6hUivg399R1DYdmW3eTb0EebzDTqjRC2gaRw,532
|
| 38 |
+
cryptography/hazmat/bindings/_rust/openssl/ed448.pyi,sha256=Yx49lqdnjsD7bxiDV1kcaMrDktug5evi5a6zerMiy2s,514
|
| 39 |
+
cryptography/hazmat/bindings/_rust/openssl/hashes.pyi,sha256=bFe3T13UeyRInUJMD-R5ArGTGLqWDiWo2Lv2KGzUbf4,1076
|
| 40 |
+
cryptography/hazmat/bindings/_rust/openssl/hmac.pyi,sha256=BXZn7NDjL3JAbYW0SQ8pg1iyC5DbQXVhUAiwsi8DFR8,702
|
| 41 |
+
cryptography/hazmat/bindings/_rust/openssl/hpke.pyi,sha256=p_hGLn6YH-EarJF1V5bUsUtjipviEApcR5O0sUHJLtc,2914
|
| 42 |
+
cryptography/hazmat/bindings/_rust/openssl/kdf.pyi,sha256=HiiLdEB9nqMsTvMwiFppQpfPz6fHUz9_gpjEB_XAOXA,6619
|
| 43 |
+
cryptography/hazmat/bindings/_rust/openssl/keys.pyi,sha256=teIt8M6ZEMJrn4s3W0UnW0DZ-30Jd68WnSsKKG124l0,912
|
| 44 |
+
cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi,sha256=HChNsjW0C_vO9OVD4FhEZrXO3_FiEq2ZTF59hlfcEdM,1073
|
| 45 |
+
cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi,sha256=byoeus4-lIOlWlEiRQdCc00qCkXpOB5peBY6yo9ik8s,835
|
| 46 |
+
cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi,sha256=_SW9NtQ5FDlAbdclFtWpT4lGmxKIKHpN-4j8J2BzYfQ,585
|
| 47 |
+
cryptography/hazmat/bindings/_rust/openssl/rsa.pyi,sha256=2OQCNSXkxgc-3uw1xiCCloIQTV6p9_kK79Yu0rhZgPc,1364
|
| 48 |
+
cryptography/hazmat/bindings/_rust/openssl/x25519.pyi,sha256=ewn4GpQyb7zPwE-ni7GtyQgMC0A1mLuqYsSyqv6nI_s,523
|
| 49 |
+
cryptography/hazmat/bindings/_rust/openssl/x448.pyi,sha256=juTZTmli8jO_5Vcufg-vHvx_tCyezmSLIh_9PU3TczI,505
|
| 50 |
+
cryptography/hazmat/bindings/_rust/pkcs12.pyi,sha256=vEEd5wDiZvb8ZGFaziLCaWLzAwoG_tvPUxLQw5_uOl8,1605
|
| 51 |
+
cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=txGBJijqZshEcqra6byPNbnisIdlxzOSIHP2hl9arPs,1601
|
| 52 |
+
cryptography/hazmat/bindings/_rust/test_support.pyi,sha256=PPhld-WkO743iXFPebeG0LtgK0aTzGdjcIsay1Gm5GE,757
|
| 53 |
+
cryptography/hazmat/bindings/_rust/x509.pyi,sha256=AjnoHl5C31c5AWSAmw2XvGc2kosUpzeV8ALtMlBxYOg,10204
|
| 54 |
+
cryptography/hazmat/bindings/openssl/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180
|
| 55 |
+
cryptography/hazmat/bindings/openssl/_conditional.py,sha256=vfzi-xHdStAcJQjLqqOlSos94L8D_tqFtBe7Ovw-KtY,5611
|
| 56 |
+
cryptography/hazmat/bindings/openssl/binding.py,sha256=FsuLxbTnrqW60-dAWtakgufeq7TnqaQai1Pc7Dowrjo,3706
|
| 57 |
+
cryptography/hazmat/decrepit/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
|
| 58 |
+
cryptography/hazmat/decrepit/ciphers/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216
|
| 59 |
+
cryptography/hazmat/decrepit/ciphers/algorithms.py,sha256=yqHt7k5OzF_KuvO8M9vTlNLtG7EotbvShyYKIaUJqhg,3566
|
| 60 |
+
cryptography/hazmat/decrepit/ciphers/modes.py,sha256=Oq_PEwCke5OLczOfr_vzAOJ6wPx-rlsvHAXPBuh5b9o,1649
|
| 61 |
+
cryptography/hazmat/primitives/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180
|
| 62 |
+
cryptography/hazmat/primitives/_asymmetric.py,sha256=RhgcouUB6HTiFDBrR1LxqkMjpUxIiNvQ1r_zJjRG6qQ,532
|
| 63 |
+
cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=Eh3i7lwedHfi0eLSsH93PZxQKzY9I6lkK67vL4V5tOc,1522
|
| 64 |
+
cryptography/hazmat/primitives/_modes.py,sha256=_EiAOD8Jb6WpFXluwkcUT3ECz_TH8xjGvbQlGkNm59Q,3075
|
| 65 |
+
cryptography/hazmat/primitives/_serialization.py,sha256=hi0xJblBAZ8pmZx2lWa-lruphxvvvG3g9DNK0G8Odfs,4440
|
| 66 |
+
cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180
|
| 67 |
+
cryptography/hazmat/primitives/asymmetric/dh.py,sha256=klvHVaxAFkUyaWXLytqYjBTh81Zx1ByCJNZVRcSDYX8,3912
|
| 68 |
+
cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=kQzTViqAI9PiELNOBco_n6MfP3N9ehIPkSduOpupt0M,4482
|
| 69 |
+
cryptography/hazmat/primitives/asymmetric/ec.py,sha256=LklVCLLxbbQzqiE6HRaYTRauLMphYUw3tE6c8oBIxYQ,10183
|
| 70 |
+
cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=mbffLvs-3PgCDeg1rQXVXh7GLntlVL4ATjkIEc4pt5M,2998
|
| 71 |
+
cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=n0dK6y-VLxuw-zvrDxuPT4598hymgX-1Ry_jq9aUQhE,4002
|
| 72 |
+
cryptography/hazmat/primitives/asymmetric/mldsa.py,sha256=ih4Z__s7bRAuZKs9qKcuNQ7z9W6q3xrVcw5Oc81_9X0,13750
|
| 73 |
+
cryptography/hazmat/primitives/asymmetric/mlkem.py,sha256=wvVkeFT-4of-VcUAER2-zI_5hXT6rwTJPMCQ8gF9GTA,7901
|
| 74 |
+
cryptography/hazmat/primitives/asymmetric/padding.py,sha256=vQ6l6gOg9HqcbOsvHrSiJRVLdEj9L4m4HkRGYziTyFA,2854
|
| 75 |
+
cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=lIE7lGe449W5URLTKCpvFGerITouKoZV9GwuTzScsSo,8492
|
| 76 |
+
cryptography/hazmat/primitives/asymmetric/types.py,sha256=s8-WqjOntaN1oAOV5bHczLFM2mpSi0JJuyED_9FoF5w,2409
|
| 77 |
+
cryptography/hazmat/primitives/asymmetric/utils.py,sha256=Qs8Re9GFPjW_tNp_73IeJEjPCf0slOIsWOxo6qymT6k,821
|
| 78 |
+
cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=lQcgUk-Piubj8ynFNWQeYme3tYZMgHQqkRKkIDOHLzo,3888
|
| 79 |
+
cryptography/hazmat/primitives/asymmetric/x448.py,sha256=b37ig7k7poG6SJDry42j-ElCj5q4eQ0X5CFmN0Sn8z8,3913
|
| 80 |
+
cryptography/hazmat/primitives/ciphers/__init__.py,sha256=eyEXmjk6_CZXaOPYDr7vAYGXr29QvzgWL2-4CSolLFs,680
|
| 81 |
+
cryptography/hazmat/primitives/ciphers/aead.py,sha256=Fzlyx7w8KYQakzDp1zWgJnIr62zgZrgVh1u2h4exB54,634
|
| 82 |
+
cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=IGfCycYJOh1TvoI34rfw8KfJ_xmt1p_DpBht2Fghceo,3496
|
| 83 |
+
cryptography/hazmat/primitives/ciphers/base.py,sha256=aBC7HHBBoixebmparVr0UlODs3VD0A7B6oz_AaRjDv8,4253
|
| 84 |
+
cryptography/hazmat/primitives/ciphers/modes.py,sha256=EIDdFACuIPhwOaDeUouSroxtdFi7vOrXwJbB4ZPxRLs,5999
|
| 85 |
+
cryptography/hazmat/primitives/cmac.py,sha256=sz_s6H_cYnOvx-VNWdIKhRhe3Ymp8z8J0D3CBqOX3gg,338
|
| 86 |
+
cryptography/hazmat/primitives/constant_time.py,sha256=xdunWT0nf8OvKdcqUhhlFKayGp4_PgVJRU2W1wLSr_A,422
|
| 87 |
+
cryptography/hazmat/primitives/hashes.py,sha256=M8BrlKB3U6DEtHvWTV5VRjpteHv1kS3Zxm_Bsk04cr8,5184
|
| 88 |
+
cryptography/hazmat/primitives/hmac.py,sha256=RpB3z9z5skirCQrm7zQbtnp9pLMnAjrlTUvKqF5aDDc,423
|
| 89 |
+
cryptography/hazmat/primitives/hpke.py,sha256=RsHissC5l-dTPn1p2JbcrIrAnDBrLqM5ugBFSGpmKu4,865
|
| 90 |
+
cryptography/hazmat/primitives/kdf/__init__.py,sha256=v3yiYBGU272EojNXbwfYZdbbfI9cVOCCG3nXhTDda3k,1037
|
| 91 |
+
cryptography/hazmat/primitives/kdf/argon2.py,sha256=ZJx-enUlAA4o7b46C1wlzG_XAQQW0wIkFgUKkrM_wA8,632
|
| 92 |
+
cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=BFnOKS72txKF0yQvASr0Na7uhzvLHpUCckK5iNAC_aQ,591
|
| 93 |
+
cryptography/hazmat/primitives/kdf/hkdf.py,sha256=M0lAEfRoc4kpp4-nwDj9yB-vNZukIOYEQrUlWsBNn9o,543
|
| 94 |
+
cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=C3w99vjFqhqzAxzFOH4zzvVoHkb5AC6Lz6AEQGV7d_g,737
|
| 95 |
+
cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=Xchkk99s-Mk6b6KSCouqdk8FVyiNVOSFIenmnW3tLgQ,468
|
| 96 |
+
cryptography/hazmat/primitives/kdf/scrypt.py,sha256=XyWUdUUmhuI9V6TqAPOvujCSMGv1XQdg0a21IWCmO-U,590
|
| 97 |
+
cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=QKjhRehuTsAstL384bKfvjuv1yfdamAHVhsBRWen2Vw,456
|
| 98 |
+
cryptography/hazmat/primitives/keywrap.py,sha256=UI-0UESQxBXTKU1HrrRoUwDiFsrWif81qyXlV1e7kyY,5776
|
| 99 |
+
cryptography/hazmat/primitives/padding.py,sha256=QT-U-NvV2eQGO1wVPbDiNGNSc9keRDS-ig5cQOrLz0E,1865
|
| 100 |
+
cryptography/hazmat/primitives/poly1305.py,sha256=P5EPQV-RB_FJPahpg01u0Ts4S_PnAmsroxIGXbGeRRo,355
|
| 101 |
+
cryptography/hazmat/primitives/serialization/__init__.py,sha256=Q7uTgDlt7n3WfsMT6jYwutC6DIg_7SEeoAm1GHZ5B5E,1705
|
| 102 |
+
cryptography/hazmat/primitives/serialization/base.py,sha256=ikq5MJIwp_oUnjiaBco_PmQwOTYuGi-XkYUYHKy8Vo0,615
|
| 103 |
+
cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=mS9cFNG4afzvseoc5e1MWoY2VskfL8N8Y_OFjl67luY,5104
|
| 104 |
+
cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=mFM7OFuZ8cCxUGUoo4Cof6BnCHc_Ibx1AQjjfxnPlxo,13998
|
| 105 |
+
cryptography/hazmat/primitives/serialization/ssh.py,sha256=HV6ZqIjqNNaNUL6M4gQyNJJtZ75EsiSZs6yxddqBrWI,53789
|
| 106 |
+
cryptography/hazmat/primitives/twofactor/__init__.py,sha256=tmMZGB-g4IU1r7lIFqASU019zr0uPp_wEBYcwdDCKCA,258
|
| 107 |
+
cryptography/hazmat/primitives/twofactor/hotp.py,sha256=ivZo5BrcCGWLsqql4nZV0XXCjyGPi_iHfDFltGlOJwk,3256
|
| 108 |
+
cryptography/hazmat/primitives/twofactor/totp.py,sha256=m5LPpRL00kp4zY8gTjr55Hfz9aMlPS53kHmVkSQCmdY,1652
|
| 109 |
+
cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 110 |
+
cryptography/utils.py,sha256=wCnykfmWmp6L5zUFou715snYL9_YLLHjpj4_NHmlhwU,4281
|
| 111 |
+
cryptography/x509/__init__.py,sha256=zciyadRgnCp2DNPPm6ujEKd6EX-8_zOnwtCjJbZBatg,8273
|
| 112 |
+
cryptography/x509/base.py,sha256=k9zEFC7H7Gys58AmJNeEfpTeJCvog8p8oDEa8Dg0CO0,27056
|
| 113 |
+
cryptography/x509/certificate_transparency.py,sha256=JqoOIDhlwInrYMFW6IFn77WJ0viF-PB_rlZV3vs9MYc,797
|
| 114 |
+
cryptography/x509/extensions.py,sha256=0AUgutLe26SLg1KaxSeo0fG7fUBAyK2tEgJl9c_AQRM,77968
|
| 115 |
+
cryptography/x509/general_name.py,sha256=sP_rV11Qlpsk4x3XXGJY_Mv0Q_s9dtjeLckHsjpLQoQ,7836
|
| 116 |
+
cryptography/x509/name.py,sha256=oI2w6VdY8zgw7qpNhlxEx512W0AhWDXQrDYf5EsM2PY,15566
|
| 117 |
+
cryptography/x509/ocsp.py,sha256=Yey6NdFV1MPjop24Mj_VenjEpg3kUaMopSWOK0AbeBs,12699
|
| 118 |
+
cryptography/x509/oid.py,sha256=BUzgXXGVWilkBkdKPTm9R4qElE9gAGHgdYPMZAp7PJo,931
|
| 119 |
+
cryptography/x509/verification.py,sha256=gR2C2c-XZQtblZhT5T5vjSKOtCb74ef2alPVmEcwFlM,958
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/REQUESTED
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: maturin (1.14.0)
|
| 3 |
+
Root-Is-Purelib: false
|
| 4 |
+
Tag: cp311-abi3-manylinux_2_34_x86_64
|
| 5 |
+
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
This software is made available under the terms of *either* of the licenses
|
| 2 |
+
found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made
|
| 3 |
+
under the terms of *both* these licenses.
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.APACHE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
https://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
| 178 |
+
|
| 179 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 180 |
+
|
| 181 |
+
To apply the Apache License to your work, attach the following
|
| 182 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 183 |
+
replaced with your own identifying information. (Don't include
|
| 184 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 185 |
+
comment syntax for the file format. We also recommend that a
|
| 186 |
+
file or class name and description of purpose be included on the
|
| 187 |
+
same "printed page" as the copyright notice for easier
|
| 188 |
+
identification within third-party archives.
|
| 189 |
+
|
| 190 |
+
Copyright [yyyy] [name of copyright owner]
|
| 191 |
+
|
| 192 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 193 |
+
you may not use this file except in compliance with the License.
|
| 194 |
+
You may obtain a copy of the License at
|
| 195 |
+
|
| 196 |
+
https://www.apache.org/licenses/LICENSE-2.0
|
| 197 |
+
|
| 198 |
+
Unless required by applicable law or agreed to in writing, software
|
| 199 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 200 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 201 |
+
See the License for the specific language governing permissions and
|
| 202 |
+
limitations under the License.
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.BSD
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) Individual contributors.
|
| 2 |
+
All rights reserved.
|
| 3 |
+
|
| 4 |
+
Redistribution and use in source and binary forms, with or without
|
| 5 |
+
modification, are permitted provided that the following conditions are met:
|
| 6 |
+
|
| 7 |
+
1. Redistributions of source code must retain the above copyright notice,
|
| 8 |
+
this list of conditions and the following disclaimer.
|
| 9 |
+
|
| 10 |
+
2. Redistributions in binary form must reproduce the above copyright
|
| 11 |
+
notice, this list of conditions and the following disclaimer in the
|
| 12 |
+
documentation and/or other materials provided with the distribution.
|
| 13 |
+
|
| 14 |
+
3. Neither the name of PyCA Cryptography nor the names of its contributors
|
| 15 |
+
may be used to endorse or promote products derived from this software
|
| 16 |
+
without specific prior written permission.
|
| 17 |
+
|
| 18 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
| 19 |
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| 20 |
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| 21 |
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
| 22 |
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
| 23 |
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 24 |
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
| 25 |
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 26 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 27 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json
ADDED
|
@@ -0,0 +1,1365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bomFormat": "CycloneDX",
|
| 3 |
+
"specVersion": "1.5",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"serialNumber": "urn:uuid:72a04af1-8dfd-48d1-a4ec-977a2d3d087f",
|
| 6 |
+
"metadata": {
|
| 7 |
+
"timestamp": "2026-06-12T19:52:54.204652182Z",
|
| 8 |
+
"tools": [
|
| 9 |
+
{
|
| 10 |
+
"vendor": "CycloneDX",
|
| 11 |
+
"name": "cargo-cyclonedx",
|
| 12 |
+
"version": "0.5.9"
|
| 13 |
+
}
|
| 14 |
+
],
|
| 15 |
+
"authors": [
|
| 16 |
+
{
|
| 17 |
+
"name": "The cryptography developers",
|
| 18 |
+
"email": "cryptography-dev@python.org"
|
| 19 |
+
}
|
| 20 |
+
],
|
| 21 |
+
"component": {
|
| 22 |
+
"type": "library",
|
| 23 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust#cryptography-rust@0.49.0",
|
| 24 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 25 |
+
"name": "cryptography-rust",
|
| 26 |
+
"version": "0.49.0",
|
| 27 |
+
"scope": "required",
|
| 28 |
+
"licenses": [
|
| 29 |
+
{
|
| 30 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 31 |
+
}
|
| 32 |
+
],
|
| 33 |
+
"purl": "pkg:cargo/cryptography-rust@0.49.0?download_url=file://.",
|
| 34 |
+
"components": [
|
| 35 |
+
{
|
| 36 |
+
"type": "library",
|
| 37 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust#cryptography-rust@0.49.0 bin-target-0",
|
| 38 |
+
"name": "cryptography_rust",
|
| 39 |
+
"version": "0.49.0",
|
| 40 |
+
"purl": "pkg:cargo/cryptography-rust@0.49.0?download_url=file://.#src/lib.rs"
|
| 41 |
+
}
|
| 42 |
+
]
|
| 43 |
+
},
|
| 44 |
+
"properties": [
|
| 45 |
+
{
|
| 46 |
+
"name": "cdx:rustc:sbom:target:all_targets",
|
| 47 |
+
"value": "true"
|
| 48 |
+
}
|
| 49 |
+
]
|
| 50 |
+
},
|
| 51 |
+
"components": [
|
| 52 |
+
{
|
| 53 |
+
"type": "library",
|
| 54 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-cffi#0.49.0",
|
| 55 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 56 |
+
"name": "cryptography-cffi",
|
| 57 |
+
"version": "0.49.0",
|
| 58 |
+
"scope": "required",
|
| 59 |
+
"licenses": [
|
| 60 |
+
{
|
| 61 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 62 |
+
}
|
| 63 |
+
],
|
| 64 |
+
"purl": "pkg:cargo/cryptography-cffi@0.49.0?download_url=file://cryptography-cffi"
|
| 65 |
+
},
|
| 66 |
+
{
|
| 67 |
+
"type": "library",
|
| 68 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-crypto#0.49.0",
|
| 69 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 70 |
+
"name": "cryptography-crypto",
|
| 71 |
+
"version": "0.49.0",
|
| 72 |
+
"scope": "required",
|
| 73 |
+
"licenses": [
|
| 74 |
+
{
|
| 75 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 76 |
+
}
|
| 77 |
+
],
|
| 78 |
+
"purl": "pkg:cargo/cryptography-crypto@0.49.0?download_url=file://cryptography-crypto"
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"type": "library",
|
| 82 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-keepalive#0.49.0",
|
| 83 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 84 |
+
"name": "cryptography-keepalive",
|
| 85 |
+
"version": "0.49.0",
|
| 86 |
+
"scope": "required",
|
| 87 |
+
"licenses": [
|
| 88 |
+
{
|
| 89 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 90 |
+
}
|
| 91 |
+
],
|
| 92 |
+
"purl": "pkg:cargo/cryptography-keepalive@0.49.0?download_url=file://cryptography-keepalive"
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"type": "library",
|
| 96 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-key-parsing#0.49.0",
|
| 97 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 98 |
+
"name": "cryptography-key-parsing",
|
| 99 |
+
"version": "0.49.0",
|
| 100 |
+
"scope": "required",
|
| 101 |
+
"licenses": [
|
| 102 |
+
{
|
| 103 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 104 |
+
}
|
| 105 |
+
],
|
| 106 |
+
"purl": "pkg:cargo/cryptography-key-parsing@0.49.0?download_url=file://cryptography-key-parsing"
|
| 107 |
+
},
|
| 108 |
+
{
|
| 109 |
+
"type": "library",
|
| 110 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-openssl#0.49.0",
|
| 111 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 112 |
+
"name": "cryptography-openssl",
|
| 113 |
+
"version": "0.49.0",
|
| 114 |
+
"scope": "required",
|
| 115 |
+
"licenses": [
|
| 116 |
+
{
|
| 117 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 118 |
+
}
|
| 119 |
+
],
|
| 120 |
+
"purl": "pkg:cargo/cryptography-openssl@0.49.0?download_url=file://cryptography-openssl"
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"type": "library",
|
| 124 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0",
|
| 125 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 126 |
+
"name": "cryptography-x509",
|
| 127 |
+
"version": "0.49.0",
|
| 128 |
+
"scope": "required",
|
| 129 |
+
"licenses": [
|
| 130 |
+
{
|
| 131 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 132 |
+
}
|
| 133 |
+
],
|
| 134 |
+
"purl": "pkg:cargo/cryptography-x509@0.49.0?download_url=file://cryptography-x509"
|
| 135 |
+
},
|
| 136 |
+
{
|
| 137 |
+
"type": "library",
|
| 138 |
+
"bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-x509-verification#0.49.0",
|
| 139 |
+
"author": "The cryptography developers <cryptography-dev@python.org>",
|
| 140 |
+
"name": "cryptography-x509-verification",
|
| 141 |
+
"version": "0.49.0",
|
| 142 |
+
"scope": "required",
|
| 143 |
+
"licenses": [
|
| 144 |
+
{
|
| 145 |
+
"expression": "Apache-2.0 OR BSD-3-Clause"
|
| 146 |
+
}
|
| 147 |
+
],
|
| 148 |
+
"purl": "pkg:cargo/cryptography-x509-verification@0.49.0?download_url=file://cryptography-x509-verification"
|
| 149 |
+
},
|
| 150 |
+
{
|
| 151 |
+
"type": "library",
|
| 152 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 153 |
+
"author": "Alex Gaynor <alex.gaynor@gmail.com>",
|
| 154 |
+
"name": "asn1",
|
| 155 |
+
"version": "0.24.1",
|
| 156 |
+
"description": "ASN.1 (DER) parser and writer for Rust.",
|
| 157 |
+
"scope": "required",
|
| 158 |
+
"hashes": [
|
| 159 |
+
{
|
| 160 |
+
"alg": "SHA-256",
|
| 161 |
+
"content": "c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1"
|
| 162 |
+
}
|
| 163 |
+
],
|
| 164 |
+
"licenses": [
|
| 165 |
+
{
|
| 166 |
+
"expression": "BSD-3-Clause"
|
| 167 |
+
}
|
| 168 |
+
],
|
| 169 |
+
"purl": "pkg:cargo/asn1@0.24.1",
|
| 170 |
+
"externalReferences": [
|
| 171 |
+
{
|
| 172 |
+
"type": "vcs",
|
| 173 |
+
"url": "https://github.com/alex/rust-asn1"
|
| 174 |
+
}
|
| 175 |
+
]
|
| 176 |
+
},
|
| 177 |
+
{
|
| 178 |
+
"type": "library",
|
| 179 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1",
|
| 180 |
+
"author": "Alex Gaynor <alex.gaynor@gmail.com>",
|
| 181 |
+
"name": "asn1_derive",
|
| 182 |
+
"version": "0.24.1",
|
| 183 |
+
"description": "#[derive] support for asn1",
|
| 184 |
+
"scope": "required",
|
| 185 |
+
"hashes": [
|
| 186 |
+
{
|
| 187 |
+
"alg": "SHA-256",
|
| 188 |
+
"content": "909e307f1cc32bb8bccbd98f446e6d1bf03fa30f7b53a4337da7181ad30fa11a"
|
| 189 |
+
}
|
| 190 |
+
],
|
| 191 |
+
"licenses": [
|
| 192 |
+
{
|
| 193 |
+
"expression": "BSD-3-Clause"
|
| 194 |
+
}
|
| 195 |
+
],
|
| 196 |
+
"purl": "pkg:cargo/asn1_derive@0.24.1",
|
| 197 |
+
"externalReferences": [
|
| 198 |
+
{
|
| 199 |
+
"type": "vcs",
|
| 200 |
+
"url": "https://github.com/alex/rust-asn1"
|
| 201 |
+
}
|
| 202 |
+
]
|
| 203 |
+
},
|
| 204 |
+
{
|
| 205 |
+
"type": "library",
|
| 206 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1",
|
| 207 |
+
"author": "Marshall Pierce <marshall@mpierce.org>",
|
| 208 |
+
"name": "base64",
|
| 209 |
+
"version": "0.22.1",
|
| 210 |
+
"description": "encodes and decodes base64 as bytes or utf8",
|
| 211 |
+
"scope": "required",
|
| 212 |
+
"hashes": [
|
| 213 |
+
{
|
| 214 |
+
"alg": "SHA-256",
|
| 215 |
+
"content": "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
| 216 |
+
}
|
| 217 |
+
],
|
| 218 |
+
"licenses": [
|
| 219 |
+
{
|
| 220 |
+
"expression": "MIT OR Apache-2.0"
|
| 221 |
+
}
|
| 222 |
+
],
|
| 223 |
+
"purl": "pkg:cargo/base64@0.22.1",
|
| 224 |
+
"externalReferences": [
|
| 225 |
+
{
|
| 226 |
+
"type": "documentation",
|
| 227 |
+
"url": "https://docs.rs/base64"
|
| 228 |
+
},
|
| 229 |
+
{
|
| 230 |
+
"type": "vcs",
|
| 231 |
+
"url": "https://github.com/marshallpierce/rust-base64"
|
| 232 |
+
}
|
| 233 |
+
]
|
| 234 |
+
},
|
| 235 |
+
{
|
| 236 |
+
"type": "library",
|
| 237 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0",
|
| 238 |
+
"author": "The Rust Project Developers",
|
| 239 |
+
"name": "bitflags",
|
| 240 |
+
"version": "2.13.0",
|
| 241 |
+
"description": "A macro to generate structures which behave like bitflags. ",
|
| 242 |
+
"scope": "required",
|
| 243 |
+
"hashes": [
|
| 244 |
+
{
|
| 245 |
+
"alg": "SHA-256",
|
| 246 |
+
"content": "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
| 247 |
+
}
|
| 248 |
+
],
|
| 249 |
+
"licenses": [
|
| 250 |
+
{
|
| 251 |
+
"expression": "MIT OR Apache-2.0"
|
| 252 |
+
}
|
| 253 |
+
],
|
| 254 |
+
"purl": "pkg:cargo/bitflags@2.13.0",
|
| 255 |
+
"externalReferences": [
|
| 256 |
+
{
|
| 257 |
+
"type": "documentation",
|
| 258 |
+
"url": "https://docs.rs/bitflags"
|
| 259 |
+
},
|
| 260 |
+
{
|
| 261 |
+
"type": "website",
|
| 262 |
+
"url": "https://github.com/bitflags/bitflags"
|
| 263 |
+
},
|
| 264 |
+
{
|
| 265 |
+
"type": "vcs",
|
| 266 |
+
"url": "https://github.com/bitflags/bitflags"
|
| 267 |
+
}
|
| 268 |
+
]
|
| 269 |
+
},
|
| 270 |
+
{
|
| 271 |
+
"type": "library",
|
| 272 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64",
|
| 273 |
+
"author": "Alex Crichton <alex@alexcrichton.com>",
|
| 274 |
+
"name": "cc",
|
| 275 |
+
"version": "1.2.64",
|
| 276 |
+
"description": "A build-time dependency for Cargo build scripts to assist in invoking the native C compiler to compile native C code into a static archive to be linked into Rust code. ",
|
| 277 |
+
"scope": "excluded",
|
| 278 |
+
"hashes": [
|
| 279 |
+
{
|
| 280 |
+
"alg": "SHA-256",
|
| 281 |
+
"content": "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f"
|
| 282 |
+
}
|
| 283 |
+
],
|
| 284 |
+
"licenses": [
|
| 285 |
+
{
|
| 286 |
+
"expression": "MIT OR Apache-2.0"
|
| 287 |
+
}
|
| 288 |
+
],
|
| 289 |
+
"purl": "pkg:cargo/cc@1.2.64",
|
| 290 |
+
"externalReferences": [
|
| 291 |
+
{
|
| 292 |
+
"type": "documentation",
|
| 293 |
+
"url": "https://docs.rs/cc"
|
| 294 |
+
},
|
| 295 |
+
{
|
| 296 |
+
"type": "website",
|
| 297 |
+
"url": "https://github.com/rust-lang/cc-rs"
|
| 298 |
+
},
|
| 299 |
+
{
|
| 300 |
+
"type": "vcs",
|
| 301 |
+
"url": "https://github.com/rust-lang/cc-rs"
|
| 302 |
+
}
|
| 303 |
+
]
|
| 304 |
+
},
|
| 305 |
+
{
|
| 306 |
+
"type": "library",
|
| 307 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 308 |
+
"author": "Alex Crichton <alex@alexcrichton.com>",
|
| 309 |
+
"name": "cfg-if",
|
| 310 |
+
"version": "1.0.4",
|
| 311 |
+
"description": "A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted. ",
|
| 312 |
+
"scope": "required",
|
| 313 |
+
"hashes": [
|
| 314 |
+
{
|
| 315 |
+
"alg": "SHA-256",
|
| 316 |
+
"content": "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
| 317 |
+
}
|
| 318 |
+
],
|
| 319 |
+
"licenses": [
|
| 320 |
+
{
|
| 321 |
+
"expression": "MIT OR Apache-2.0"
|
| 322 |
+
}
|
| 323 |
+
],
|
| 324 |
+
"purl": "pkg:cargo/cfg-if@1.0.4",
|
| 325 |
+
"externalReferences": [
|
| 326 |
+
{
|
| 327 |
+
"type": "vcs",
|
| 328 |
+
"url": "https://github.com/rust-lang/cfg-if"
|
| 329 |
+
}
|
| 330 |
+
]
|
| 331 |
+
},
|
| 332 |
+
{
|
| 333 |
+
"type": "library",
|
| 334 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9",
|
| 335 |
+
"name": "find-msvc-tools",
|
| 336 |
+
"version": "0.1.9",
|
| 337 |
+
"description": "Find windows-specific tools, read MSVC versions from the registry and from COM interfaces",
|
| 338 |
+
"scope": "excluded",
|
| 339 |
+
"hashes": [
|
| 340 |
+
{
|
| 341 |
+
"alg": "SHA-256",
|
| 342 |
+
"content": "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
| 343 |
+
}
|
| 344 |
+
],
|
| 345 |
+
"licenses": [
|
| 346 |
+
{
|
| 347 |
+
"expression": "MIT OR Apache-2.0"
|
| 348 |
+
}
|
| 349 |
+
],
|
| 350 |
+
"purl": "pkg:cargo/find-msvc-tools@0.1.9",
|
| 351 |
+
"externalReferences": [
|
| 352 |
+
{
|
| 353 |
+
"type": "documentation",
|
| 354 |
+
"url": "https://docs.rs/find-msvc-tools"
|
| 355 |
+
},
|
| 356 |
+
{
|
| 357 |
+
"type": "vcs",
|
| 358 |
+
"url": "https://github.com/rust-lang/cc-rs"
|
| 359 |
+
}
|
| 360 |
+
]
|
| 361 |
+
},
|
| 362 |
+
{
|
| 363 |
+
"type": "library",
|
| 364 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1",
|
| 365 |
+
"author": "Steven Fackler <sfackler@gmail.com>",
|
| 366 |
+
"name": "foreign-types-shared",
|
| 367 |
+
"version": "0.1.1",
|
| 368 |
+
"description": "An internal crate used by foreign-types",
|
| 369 |
+
"scope": "required",
|
| 370 |
+
"hashes": [
|
| 371 |
+
{
|
| 372 |
+
"alg": "SHA-256",
|
| 373 |
+
"content": "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
| 374 |
+
}
|
| 375 |
+
],
|
| 376 |
+
"licenses": [
|
| 377 |
+
{
|
| 378 |
+
"expression": "MIT OR Apache-2.0"
|
| 379 |
+
}
|
| 380 |
+
],
|
| 381 |
+
"purl": "pkg:cargo/foreign-types-shared@0.1.1",
|
| 382 |
+
"externalReferences": [
|
| 383 |
+
{
|
| 384 |
+
"type": "vcs",
|
| 385 |
+
"url": "https://github.com/sfackler/foreign-types"
|
| 386 |
+
}
|
| 387 |
+
]
|
| 388 |
+
},
|
| 389 |
+
{
|
| 390 |
+
"type": "library",
|
| 391 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2",
|
| 392 |
+
"author": "Steven Fackler <sfackler@gmail.com>",
|
| 393 |
+
"name": "foreign-types",
|
| 394 |
+
"version": "0.3.2",
|
| 395 |
+
"description": "A framework for Rust wrappers over C APIs",
|
| 396 |
+
"scope": "required",
|
| 397 |
+
"hashes": [
|
| 398 |
+
{
|
| 399 |
+
"alg": "SHA-256",
|
| 400 |
+
"content": "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
| 401 |
+
}
|
| 402 |
+
],
|
| 403 |
+
"licenses": [
|
| 404 |
+
{
|
| 405 |
+
"expression": "MIT OR Apache-2.0"
|
| 406 |
+
}
|
| 407 |
+
],
|
| 408 |
+
"purl": "pkg:cargo/foreign-types@0.3.2",
|
| 409 |
+
"externalReferences": [
|
| 410 |
+
{
|
| 411 |
+
"type": "vcs",
|
| 412 |
+
"url": "https://github.com/sfackler/foreign-types"
|
| 413 |
+
}
|
| 414 |
+
]
|
| 415 |
+
},
|
| 416 |
+
{
|
| 417 |
+
"type": "library",
|
| 418 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0",
|
| 419 |
+
"name": "heck",
|
| 420 |
+
"version": "0.5.0",
|
| 421 |
+
"description": "heck is a case conversion library.",
|
| 422 |
+
"scope": "required",
|
| 423 |
+
"hashes": [
|
| 424 |
+
{
|
| 425 |
+
"alg": "SHA-256",
|
| 426 |
+
"content": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
| 427 |
+
}
|
| 428 |
+
],
|
| 429 |
+
"licenses": [
|
| 430 |
+
{
|
| 431 |
+
"expression": "MIT OR Apache-2.0"
|
| 432 |
+
}
|
| 433 |
+
],
|
| 434 |
+
"purl": "pkg:cargo/heck@0.5.0",
|
| 435 |
+
"externalReferences": [
|
| 436 |
+
{
|
| 437 |
+
"type": "vcs",
|
| 438 |
+
"url": "https://github.com/withoutboats/heck"
|
| 439 |
+
}
|
| 440 |
+
]
|
| 441 |
+
},
|
| 442 |
+
{
|
| 443 |
+
"type": "library",
|
| 444 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18",
|
| 445 |
+
"author": "David Tolnay <dtolnay@gmail.com>",
|
| 446 |
+
"name": "itoa",
|
| 447 |
+
"version": "1.0.18",
|
| 448 |
+
"description": "Fast integer primitive to string conversion",
|
| 449 |
+
"scope": "required",
|
| 450 |
+
"hashes": [
|
| 451 |
+
{
|
| 452 |
+
"alg": "SHA-256",
|
| 453 |
+
"content": "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
| 454 |
+
}
|
| 455 |
+
],
|
| 456 |
+
"licenses": [
|
| 457 |
+
{
|
| 458 |
+
"expression": "MIT OR Apache-2.0"
|
| 459 |
+
}
|
| 460 |
+
],
|
| 461 |
+
"purl": "pkg:cargo/itoa@1.0.18",
|
| 462 |
+
"externalReferences": [
|
| 463 |
+
{
|
| 464 |
+
"type": "documentation",
|
| 465 |
+
"url": "https://docs.rs/itoa"
|
| 466 |
+
},
|
| 467 |
+
{
|
| 468 |
+
"type": "vcs",
|
| 469 |
+
"url": "https://github.com/dtolnay/itoa"
|
| 470 |
+
}
|
| 471 |
+
]
|
| 472 |
+
},
|
| 473 |
+
{
|
| 474 |
+
"type": "library",
|
| 475 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186",
|
| 476 |
+
"author": "The Rust Project Developers",
|
| 477 |
+
"name": "libc",
|
| 478 |
+
"version": "0.2.186",
|
| 479 |
+
"description": "Raw FFI bindings to platform libraries like libc.",
|
| 480 |
+
"scope": "required",
|
| 481 |
+
"hashes": [
|
| 482 |
+
{
|
| 483 |
+
"alg": "SHA-256",
|
| 484 |
+
"content": "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
| 485 |
+
}
|
| 486 |
+
],
|
| 487 |
+
"licenses": [
|
| 488 |
+
{
|
| 489 |
+
"expression": "MIT OR Apache-2.0"
|
| 490 |
+
}
|
| 491 |
+
],
|
| 492 |
+
"purl": "pkg:cargo/libc@0.2.186",
|
| 493 |
+
"externalReferences": [
|
| 494 |
+
{
|
| 495 |
+
"type": "vcs",
|
| 496 |
+
"url": "https://github.com/rust-lang/libc"
|
| 497 |
+
}
|
| 498 |
+
]
|
| 499 |
+
},
|
| 500 |
+
{
|
| 501 |
+
"type": "library",
|
| 502 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4",
|
| 503 |
+
"author": "Aleksey Kladov <aleksey.kladov@gmail.com>",
|
| 504 |
+
"name": "once_cell",
|
| 505 |
+
"version": "1.21.4",
|
| 506 |
+
"description": "Single assignment cells and lazy values.",
|
| 507 |
+
"scope": "required",
|
| 508 |
+
"hashes": [
|
| 509 |
+
{
|
| 510 |
+
"alg": "SHA-256",
|
| 511 |
+
"content": "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
| 512 |
+
}
|
| 513 |
+
],
|
| 514 |
+
"licenses": [
|
| 515 |
+
{
|
| 516 |
+
"expression": "MIT OR Apache-2.0"
|
| 517 |
+
}
|
| 518 |
+
],
|
| 519 |
+
"purl": "pkg:cargo/once_cell@1.21.4",
|
| 520 |
+
"externalReferences": [
|
| 521 |
+
{
|
| 522 |
+
"type": "documentation",
|
| 523 |
+
"url": "https://docs.rs/once_cell"
|
| 524 |
+
},
|
| 525 |
+
{
|
| 526 |
+
"type": "vcs",
|
| 527 |
+
"url": "https://github.com/matklad/once_cell"
|
| 528 |
+
}
|
| 529 |
+
]
|
| 530 |
+
},
|
| 531 |
+
{
|
| 532 |
+
"type": "library",
|
| 533 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1",
|
| 534 |
+
"name": "openssl-macros",
|
| 535 |
+
"version": "0.1.1",
|
| 536 |
+
"description": "Internal macros used by the openssl crate.",
|
| 537 |
+
"scope": "required",
|
| 538 |
+
"hashes": [
|
| 539 |
+
{
|
| 540 |
+
"alg": "SHA-256",
|
| 541 |
+
"content": "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
| 542 |
+
}
|
| 543 |
+
],
|
| 544 |
+
"licenses": [
|
| 545 |
+
{
|
| 546 |
+
"expression": "MIT OR Apache-2.0"
|
| 547 |
+
}
|
| 548 |
+
],
|
| 549 |
+
"purl": "pkg:cargo/openssl-macros@0.1.1"
|
| 550 |
+
},
|
| 551 |
+
{
|
| 552 |
+
"type": "library",
|
| 553 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 554 |
+
"author": "Alex Crichton <alex@alexcrichton.com>, Steven Fackler <sfackler@gmail.com>",
|
| 555 |
+
"name": "openssl-sys",
|
| 556 |
+
"version": "0.9.117",
|
| 557 |
+
"description": "FFI bindings to OpenSSL",
|
| 558 |
+
"scope": "required",
|
| 559 |
+
"hashes": [
|
| 560 |
+
{
|
| 561 |
+
"alg": "SHA-256",
|
| 562 |
+
"content": "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695"
|
| 563 |
+
}
|
| 564 |
+
],
|
| 565 |
+
"licenses": [
|
| 566 |
+
{
|
| 567 |
+
"expression": "MIT"
|
| 568 |
+
}
|
| 569 |
+
],
|
| 570 |
+
"purl": "pkg:cargo/openssl-sys@0.9.117",
|
| 571 |
+
"externalReferences": [
|
| 572 |
+
{
|
| 573 |
+
"type": "other",
|
| 574 |
+
"url": "openssl"
|
| 575 |
+
},
|
| 576 |
+
{
|
| 577 |
+
"type": "vcs",
|
| 578 |
+
"url": "https://github.com/rust-openssl/rust-openssl"
|
| 579 |
+
}
|
| 580 |
+
]
|
| 581 |
+
},
|
| 582 |
+
{
|
| 583 |
+
"type": "library",
|
| 584 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 585 |
+
"author": "Steven Fackler <sfackler@gmail.com>",
|
| 586 |
+
"name": "openssl",
|
| 587 |
+
"version": "0.10.81",
|
| 588 |
+
"description": "OpenSSL bindings",
|
| 589 |
+
"scope": "required",
|
| 590 |
+
"hashes": [
|
| 591 |
+
{
|
| 592 |
+
"alg": "SHA-256",
|
| 593 |
+
"content": "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45"
|
| 594 |
+
}
|
| 595 |
+
],
|
| 596 |
+
"licenses": [
|
| 597 |
+
{
|
| 598 |
+
"expression": "Apache-2.0"
|
| 599 |
+
}
|
| 600 |
+
],
|
| 601 |
+
"purl": "pkg:cargo/openssl@0.10.81",
|
| 602 |
+
"externalReferences": [
|
| 603 |
+
{
|
| 604 |
+
"type": "vcs",
|
| 605 |
+
"url": "https://github.com/rust-openssl/rust-openssl"
|
| 606 |
+
}
|
| 607 |
+
]
|
| 608 |
+
},
|
| 609 |
+
{
|
| 610 |
+
"type": "library",
|
| 611 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6",
|
| 612 |
+
"author": "Jonathan Creekmore <jonathan@thecreekmores.org>",
|
| 613 |
+
"name": "pem",
|
| 614 |
+
"version": "3.0.6",
|
| 615 |
+
"description": "Parse and encode PEM-encoded data.",
|
| 616 |
+
"scope": "required",
|
| 617 |
+
"hashes": [
|
| 618 |
+
{
|
| 619 |
+
"alg": "SHA-256",
|
| 620 |
+
"content": "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
|
| 621 |
+
}
|
| 622 |
+
],
|
| 623 |
+
"licenses": [
|
| 624 |
+
{
|
| 625 |
+
"expression": "MIT"
|
| 626 |
+
}
|
| 627 |
+
],
|
| 628 |
+
"purl": "pkg:cargo/pem@3.0.6",
|
| 629 |
+
"externalReferences": [
|
| 630 |
+
{
|
| 631 |
+
"type": "documentation",
|
| 632 |
+
"url": "https://docs.rs/pem/"
|
| 633 |
+
},
|
| 634 |
+
{
|
| 635 |
+
"type": "website",
|
| 636 |
+
"url": "https://github.com/jcreekmore/pem-rs.git"
|
| 637 |
+
},
|
| 638 |
+
{
|
| 639 |
+
"type": "vcs",
|
| 640 |
+
"url": "https://github.com/jcreekmore/pem-rs.git"
|
| 641 |
+
}
|
| 642 |
+
]
|
| 643 |
+
},
|
| 644 |
+
{
|
| 645 |
+
"type": "library",
|
| 646 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33",
|
| 647 |
+
"author": "Alex Crichton <alex@alexcrichton.com>",
|
| 648 |
+
"name": "pkg-config",
|
| 649 |
+
"version": "0.3.33",
|
| 650 |
+
"description": "A library to run the pkg-config system tool at build time in order to be used in Cargo build scripts. ",
|
| 651 |
+
"scope": "excluded",
|
| 652 |
+
"hashes": [
|
| 653 |
+
{
|
| 654 |
+
"alg": "SHA-256",
|
| 655 |
+
"content": "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
| 656 |
+
}
|
| 657 |
+
],
|
| 658 |
+
"licenses": [
|
| 659 |
+
{
|
| 660 |
+
"expression": "MIT OR Apache-2.0"
|
| 661 |
+
}
|
| 662 |
+
],
|
| 663 |
+
"purl": "pkg:cargo/pkg-config@0.3.33",
|
| 664 |
+
"externalReferences": [
|
| 665 |
+
{
|
| 666 |
+
"type": "documentation",
|
| 667 |
+
"url": "https://docs.rs/pkg-config"
|
| 668 |
+
},
|
| 669 |
+
{
|
| 670 |
+
"type": "vcs",
|
| 671 |
+
"url": "https://github.com/rust-lang/pkg-config-rs"
|
| 672 |
+
}
|
| 673 |
+
]
|
| 674 |
+
},
|
| 675 |
+
{
|
| 676 |
+
"type": "library",
|
| 677 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.13.1",
|
| 678 |
+
"name": "portable-atomic",
|
| 679 |
+
"version": "1.13.1",
|
| 680 |
+
"description": "Portable atomic types including support for 128-bit atomics, atomic float, etc. ",
|
| 681 |
+
"scope": "required",
|
| 682 |
+
"hashes": [
|
| 683 |
+
{
|
| 684 |
+
"alg": "SHA-256",
|
| 685 |
+
"content": "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
| 686 |
+
}
|
| 687 |
+
],
|
| 688 |
+
"licenses": [
|
| 689 |
+
{
|
| 690 |
+
"expression": "Apache-2.0 OR MIT"
|
| 691 |
+
}
|
| 692 |
+
],
|
| 693 |
+
"purl": "pkg:cargo/portable-atomic@1.13.1",
|
| 694 |
+
"externalReferences": [
|
| 695 |
+
{
|
| 696 |
+
"type": "vcs",
|
| 697 |
+
"url": "https://github.com/taiki-e/portable-atomic"
|
| 698 |
+
}
|
| 699 |
+
]
|
| 700 |
+
},
|
| 701 |
+
{
|
| 702 |
+
"type": "library",
|
| 703 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106",
|
| 704 |
+
"author": "David Tolnay <dtolnay@gmail.com>, Alex Crichton <alex@alexcrichton.com>",
|
| 705 |
+
"name": "proc-macro2",
|
| 706 |
+
"version": "1.0.106",
|
| 707 |
+
"description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.",
|
| 708 |
+
"scope": "required",
|
| 709 |
+
"hashes": [
|
| 710 |
+
{
|
| 711 |
+
"alg": "SHA-256",
|
| 712 |
+
"content": "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
| 713 |
+
}
|
| 714 |
+
],
|
| 715 |
+
"licenses": [
|
| 716 |
+
{
|
| 717 |
+
"expression": "MIT OR Apache-2.0"
|
| 718 |
+
}
|
| 719 |
+
],
|
| 720 |
+
"purl": "pkg:cargo/proc-macro2@1.0.106",
|
| 721 |
+
"externalReferences": [
|
| 722 |
+
{
|
| 723 |
+
"type": "documentation",
|
| 724 |
+
"url": "https://docs.rs/proc-macro2"
|
| 725 |
+
},
|
| 726 |
+
{
|
| 727 |
+
"type": "vcs",
|
| 728 |
+
"url": "https://github.com/dtolnay/proc-macro2"
|
| 729 |
+
}
|
| 730 |
+
]
|
| 731 |
+
},
|
| 732 |
+
{
|
| 733 |
+
"type": "library",
|
| 734 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0",
|
| 735 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 736 |
+
"name": "pyo3-build-config",
|
| 737 |
+
"version": "0.29.0",
|
| 738 |
+
"description": "Build configuration for the PyO3 ecosystem",
|
| 739 |
+
"scope": "excluded",
|
| 740 |
+
"hashes": [
|
| 741 |
+
{
|
| 742 |
+
"alg": "SHA-256",
|
| 743 |
+
"content": "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
|
| 744 |
+
}
|
| 745 |
+
],
|
| 746 |
+
"licenses": [
|
| 747 |
+
{
|
| 748 |
+
"expression": "MIT OR Apache-2.0"
|
| 749 |
+
}
|
| 750 |
+
],
|
| 751 |
+
"purl": "pkg:cargo/pyo3-build-config@0.29.0",
|
| 752 |
+
"externalReferences": [
|
| 753 |
+
{
|
| 754 |
+
"type": "website",
|
| 755 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 756 |
+
},
|
| 757 |
+
{
|
| 758 |
+
"type": "vcs",
|
| 759 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 760 |
+
}
|
| 761 |
+
]
|
| 762 |
+
},
|
| 763 |
+
{
|
| 764 |
+
"type": "library",
|
| 765 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0",
|
| 766 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 767 |
+
"name": "pyo3-ffi",
|
| 768 |
+
"version": "0.29.0",
|
| 769 |
+
"description": "Python-API bindings for the PyO3 ecosystem",
|
| 770 |
+
"scope": "required",
|
| 771 |
+
"hashes": [
|
| 772 |
+
{
|
| 773 |
+
"alg": "SHA-256",
|
| 774 |
+
"content": "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
|
| 775 |
+
}
|
| 776 |
+
],
|
| 777 |
+
"licenses": [
|
| 778 |
+
{
|
| 779 |
+
"expression": "MIT OR Apache-2.0"
|
| 780 |
+
}
|
| 781 |
+
],
|
| 782 |
+
"purl": "pkg:cargo/pyo3-ffi@0.29.0",
|
| 783 |
+
"externalReferences": [
|
| 784 |
+
{
|
| 785 |
+
"type": "website",
|
| 786 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 787 |
+
},
|
| 788 |
+
{
|
| 789 |
+
"type": "other",
|
| 790 |
+
"url": "python"
|
| 791 |
+
},
|
| 792 |
+
{
|
| 793 |
+
"type": "vcs",
|
| 794 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 795 |
+
}
|
| 796 |
+
]
|
| 797 |
+
},
|
| 798 |
+
{
|
| 799 |
+
"type": "library",
|
| 800 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0",
|
| 801 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 802 |
+
"name": "pyo3-macros-backend",
|
| 803 |
+
"version": "0.29.0",
|
| 804 |
+
"description": "Code generation for PyO3 package",
|
| 805 |
+
"scope": "required",
|
| 806 |
+
"hashes": [
|
| 807 |
+
{
|
| 808 |
+
"alg": "SHA-256",
|
| 809 |
+
"content": "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
|
| 810 |
+
}
|
| 811 |
+
],
|
| 812 |
+
"licenses": [
|
| 813 |
+
{
|
| 814 |
+
"expression": "MIT OR Apache-2.0"
|
| 815 |
+
}
|
| 816 |
+
],
|
| 817 |
+
"purl": "pkg:cargo/pyo3-macros-backend@0.29.0",
|
| 818 |
+
"externalReferences": [
|
| 819 |
+
{
|
| 820 |
+
"type": "website",
|
| 821 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 822 |
+
},
|
| 823 |
+
{
|
| 824 |
+
"type": "vcs",
|
| 825 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 826 |
+
}
|
| 827 |
+
]
|
| 828 |
+
},
|
| 829 |
+
{
|
| 830 |
+
"type": "library",
|
| 831 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0",
|
| 832 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 833 |
+
"name": "pyo3-macros",
|
| 834 |
+
"version": "0.29.0",
|
| 835 |
+
"description": "Proc macros for PyO3 package",
|
| 836 |
+
"scope": "required",
|
| 837 |
+
"hashes": [
|
| 838 |
+
{
|
| 839 |
+
"alg": "SHA-256",
|
| 840 |
+
"content": "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
|
| 841 |
+
}
|
| 842 |
+
],
|
| 843 |
+
"licenses": [
|
| 844 |
+
{
|
| 845 |
+
"expression": "MIT OR Apache-2.0"
|
| 846 |
+
}
|
| 847 |
+
],
|
| 848 |
+
"purl": "pkg:cargo/pyo3-macros@0.29.0",
|
| 849 |
+
"externalReferences": [
|
| 850 |
+
{
|
| 851 |
+
"type": "website",
|
| 852 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 853 |
+
},
|
| 854 |
+
{
|
| 855 |
+
"type": "vcs",
|
| 856 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 857 |
+
}
|
| 858 |
+
]
|
| 859 |
+
},
|
| 860 |
+
{
|
| 861 |
+
"type": "library",
|
| 862 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0",
|
| 863 |
+
"author": "PyO3 Project and Contributors <https://github.com/PyO3>",
|
| 864 |
+
"name": "pyo3",
|
| 865 |
+
"version": "0.29.0",
|
| 866 |
+
"description": "Bindings to Python interpreter",
|
| 867 |
+
"scope": "required",
|
| 868 |
+
"hashes": [
|
| 869 |
+
{
|
| 870 |
+
"alg": "SHA-256",
|
| 871 |
+
"content": "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
|
| 872 |
+
}
|
| 873 |
+
],
|
| 874 |
+
"licenses": [
|
| 875 |
+
{
|
| 876 |
+
"expression": "MIT OR Apache-2.0"
|
| 877 |
+
}
|
| 878 |
+
],
|
| 879 |
+
"purl": "pkg:cargo/pyo3@0.29.0",
|
| 880 |
+
"externalReferences": [
|
| 881 |
+
{
|
| 882 |
+
"type": "documentation",
|
| 883 |
+
"url": "https://docs.rs/crate/pyo3/"
|
| 884 |
+
},
|
| 885 |
+
{
|
| 886 |
+
"type": "website",
|
| 887 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 888 |
+
},
|
| 889 |
+
{
|
| 890 |
+
"type": "other",
|
| 891 |
+
"url": "pyo3-python"
|
| 892 |
+
},
|
| 893 |
+
{
|
| 894 |
+
"type": "vcs",
|
| 895 |
+
"url": "https://github.com/pyo3/pyo3"
|
| 896 |
+
}
|
| 897 |
+
]
|
| 898 |
+
},
|
| 899 |
+
{
|
| 900 |
+
"type": "library",
|
| 901 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45",
|
| 902 |
+
"author": "David Tolnay <dtolnay@gmail.com>",
|
| 903 |
+
"name": "quote",
|
| 904 |
+
"version": "1.0.45",
|
| 905 |
+
"description": "Quasi-quoting macro quote!(...)",
|
| 906 |
+
"scope": "required",
|
| 907 |
+
"hashes": [
|
| 908 |
+
{
|
| 909 |
+
"alg": "SHA-256",
|
| 910 |
+
"content": "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
| 911 |
+
}
|
| 912 |
+
],
|
| 913 |
+
"licenses": [
|
| 914 |
+
{
|
| 915 |
+
"expression": "MIT OR Apache-2.0"
|
| 916 |
+
}
|
| 917 |
+
],
|
| 918 |
+
"purl": "pkg:cargo/quote@1.0.45",
|
| 919 |
+
"externalReferences": [
|
| 920 |
+
{
|
| 921 |
+
"type": "documentation",
|
| 922 |
+
"url": "https://docs.rs/quote/"
|
| 923 |
+
},
|
| 924 |
+
{
|
| 925 |
+
"type": "vcs",
|
| 926 |
+
"url": "https://github.com/dtolnay/quote"
|
| 927 |
+
}
|
| 928 |
+
]
|
| 929 |
+
},
|
| 930 |
+
{
|
| 931 |
+
"type": "library",
|
| 932 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.2.2",
|
| 933 |
+
"author": "Lukas Bergdoll <lukas.bergdoll@gmail.com>",
|
| 934 |
+
"name": "self_cell",
|
| 935 |
+
"version": "1.2.2",
|
| 936 |
+
"description": "Safe-to-use proc-macro-free self-referential structs in stable Rust.",
|
| 937 |
+
"scope": "required",
|
| 938 |
+
"hashes": [
|
| 939 |
+
{
|
| 940 |
+
"alg": "SHA-256",
|
| 941 |
+
"content": "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
|
| 942 |
+
}
|
| 943 |
+
],
|
| 944 |
+
"licenses": [
|
| 945 |
+
{
|
| 946 |
+
"expression": "Apache-2.0 OR GPL-2.0-only"
|
| 947 |
+
}
|
| 948 |
+
],
|
| 949 |
+
"purl": "pkg:cargo/self_cell@1.2.2",
|
| 950 |
+
"externalReferences": [
|
| 951 |
+
{
|
| 952 |
+
"type": "documentation",
|
| 953 |
+
"url": "https://docs.rs/self_cell"
|
| 954 |
+
},
|
| 955 |
+
{
|
| 956 |
+
"type": "vcs",
|
| 957 |
+
"url": "https://github.com/Voultapher/self_cell"
|
| 958 |
+
}
|
| 959 |
+
]
|
| 960 |
+
},
|
| 961 |
+
{
|
| 962 |
+
"type": "library",
|
| 963 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1",
|
| 964 |
+
"author": "comex <comexk@gmail.com>, Fenhl <fenhl@fenhl.net>, Adrian Taylor <adetaylor@chromium.org>, Alex Touchet <alextouchet@outlook.com>, Daniel Parks <dp+git@oxidized.org>, Garrett Berg <googberg@gmail.com>",
|
| 965 |
+
"name": "shlex",
|
| 966 |
+
"version": "2.0.1",
|
| 967 |
+
"description": "Split a string into shell words, like Python's shlex.",
|
| 968 |
+
"scope": "excluded",
|
| 969 |
+
"hashes": [
|
| 970 |
+
{
|
| 971 |
+
"alg": "SHA-256",
|
| 972 |
+
"content": "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
| 973 |
+
}
|
| 974 |
+
],
|
| 975 |
+
"licenses": [
|
| 976 |
+
{
|
| 977 |
+
"expression": "MIT OR Apache-2.0"
|
| 978 |
+
}
|
| 979 |
+
],
|
| 980 |
+
"purl": "pkg:cargo/shlex@2.0.1",
|
| 981 |
+
"externalReferences": [
|
| 982 |
+
{
|
| 983 |
+
"type": "vcs",
|
| 984 |
+
"url": "https://github.com/comex/rust-shlex"
|
| 985 |
+
}
|
| 986 |
+
]
|
| 987 |
+
},
|
| 988 |
+
{
|
| 989 |
+
"type": "library",
|
| 990 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117",
|
| 991 |
+
"author": "David Tolnay <dtolnay@gmail.com>",
|
| 992 |
+
"name": "syn",
|
| 993 |
+
"version": "2.0.117",
|
| 994 |
+
"description": "Parser for Rust source code",
|
| 995 |
+
"scope": "required",
|
| 996 |
+
"hashes": [
|
| 997 |
+
{
|
| 998 |
+
"alg": "SHA-256",
|
| 999 |
+
"content": "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
| 1000 |
+
}
|
| 1001 |
+
],
|
| 1002 |
+
"licenses": [
|
| 1003 |
+
{
|
| 1004 |
+
"expression": "MIT OR Apache-2.0"
|
| 1005 |
+
}
|
| 1006 |
+
],
|
| 1007 |
+
"purl": "pkg:cargo/syn@2.0.117",
|
| 1008 |
+
"externalReferences": [
|
| 1009 |
+
{
|
| 1010 |
+
"type": "documentation",
|
| 1011 |
+
"url": "https://docs.rs/syn"
|
| 1012 |
+
},
|
| 1013 |
+
{
|
| 1014 |
+
"type": "vcs",
|
| 1015 |
+
"url": "https://github.com/dtolnay/syn"
|
| 1016 |
+
}
|
| 1017 |
+
]
|
| 1018 |
+
},
|
| 1019 |
+
{
|
| 1020 |
+
"type": "library",
|
| 1021 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5",
|
| 1022 |
+
"author": "Dan Gohman <sunfish@mozilla.com>",
|
| 1023 |
+
"name": "target-lexicon",
|
| 1024 |
+
"version": "0.13.5",
|
| 1025 |
+
"description": "LLVM target triple types",
|
| 1026 |
+
"scope": "excluded",
|
| 1027 |
+
"hashes": [
|
| 1028 |
+
{
|
| 1029 |
+
"alg": "SHA-256",
|
| 1030 |
+
"content": "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
| 1031 |
+
}
|
| 1032 |
+
],
|
| 1033 |
+
"licenses": [
|
| 1034 |
+
{
|
| 1035 |
+
"expression": "Apache-2.0 WITH LLVM-exception"
|
| 1036 |
+
}
|
| 1037 |
+
],
|
| 1038 |
+
"purl": "pkg:cargo/target-lexicon@0.13.5",
|
| 1039 |
+
"externalReferences": [
|
| 1040 |
+
{
|
| 1041 |
+
"type": "documentation",
|
| 1042 |
+
"url": "https://docs.rs/target-lexicon/"
|
| 1043 |
+
},
|
| 1044 |
+
{
|
| 1045 |
+
"type": "vcs",
|
| 1046 |
+
"url": "https://github.com/bytecodealliance/target-lexicon"
|
| 1047 |
+
}
|
| 1048 |
+
]
|
| 1049 |
+
},
|
| 1050 |
+
{
|
| 1051 |
+
"type": "library",
|
| 1052 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24",
|
| 1053 |
+
"author": "David Tolnay <dtolnay@gmail.com>",
|
| 1054 |
+
"name": "unicode-ident",
|
| 1055 |
+
"version": "1.0.24",
|
| 1056 |
+
"description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
|
| 1057 |
+
"scope": "required",
|
| 1058 |
+
"hashes": [
|
| 1059 |
+
{
|
| 1060 |
+
"alg": "SHA-256",
|
| 1061 |
+
"content": "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
| 1062 |
+
}
|
| 1063 |
+
],
|
| 1064 |
+
"licenses": [
|
| 1065 |
+
{
|
| 1066 |
+
"expression": "(MIT OR Apache-2.0) AND Unicode-3.0"
|
| 1067 |
+
}
|
| 1068 |
+
],
|
| 1069 |
+
"purl": "pkg:cargo/unicode-ident@1.0.24",
|
| 1070 |
+
"externalReferences": [
|
| 1071 |
+
{
|
| 1072 |
+
"type": "documentation",
|
| 1073 |
+
"url": "https://docs.rs/unicode-ident"
|
| 1074 |
+
},
|
| 1075 |
+
{
|
| 1076 |
+
"type": "vcs",
|
| 1077 |
+
"url": "https://github.com/dtolnay/unicode-ident"
|
| 1078 |
+
}
|
| 1079 |
+
]
|
| 1080 |
+
},
|
| 1081 |
+
{
|
| 1082 |
+
"type": "library",
|
| 1083 |
+
"bom-ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15",
|
| 1084 |
+
"author": "Jim McGrath <jimmc2@gmail.com>",
|
| 1085 |
+
"name": "vcpkg",
|
| 1086 |
+
"version": "0.2.15",
|
| 1087 |
+
"description": "A library to find native dependencies in a vcpkg tree at build time in order to be used in Cargo build scripts. ",
|
| 1088 |
+
"scope": "excluded",
|
| 1089 |
+
"hashes": [
|
| 1090 |
+
{
|
| 1091 |
+
"alg": "SHA-256",
|
| 1092 |
+
"content": "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
| 1093 |
+
}
|
| 1094 |
+
],
|
| 1095 |
+
"licenses": [
|
| 1096 |
+
{
|
| 1097 |
+
"expression": "MIT OR Apache-2.0"
|
| 1098 |
+
}
|
| 1099 |
+
],
|
| 1100 |
+
"purl": "pkg:cargo/vcpkg@0.2.15",
|
| 1101 |
+
"externalReferences": [
|
| 1102 |
+
{
|
| 1103 |
+
"type": "documentation",
|
| 1104 |
+
"url": "https://docs.rs/vcpkg"
|
| 1105 |
+
},
|
| 1106 |
+
{
|
| 1107 |
+
"type": "vcs",
|
| 1108 |
+
"url": "https://github.com/mcgoo/vcpkg-rs"
|
| 1109 |
+
}
|
| 1110 |
+
]
|
| 1111 |
+
}
|
| 1112 |
+
],
|
| 1113 |
+
"dependencies": [
|
| 1114 |
+
{
|
| 1115 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust#cryptography-rust@0.49.0",
|
| 1116 |
+
"dependsOn": [
|
| 1117 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 1118 |
+
"registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1",
|
| 1119 |
+
"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 1120 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-cffi#0.49.0",
|
| 1121 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-crypto#0.49.0",
|
| 1122 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-keepalive#0.49.0",
|
| 1123 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-key-parsing#0.49.0",
|
| 1124 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-openssl#0.49.0",
|
| 1125 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0",
|
| 1126 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-x509-verification#0.49.0",
|
| 1127 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1",
|
| 1128 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 1129 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 1130 |
+
"registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6",
|
| 1131 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0",
|
| 1132 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0",
|
| 1133 |
+
"registry+https://github.com/rust-lang/crates.io-index#self_cell@1.2.2"
|
| 1134 |
+
]
|
| 1135 |
+
},
|
| 1136 |
+
{
|
| 1137 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-cffi#0.49.0",
|
| 1138 |
+
"dependsOn": [
|
| 1139 |
+
"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64",
|
| 1140 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 1141 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0"
|
| 1142 |
+
]
|
| 1143 |
+
},
|
| 1144 |
+
{
|
| 1145 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-crypto#0.49.0",
|
| 1146 |
+
"dependsOn": [
|
| 1147 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81"
|
| 1148 |
+
]
|
| 1149 |
+
},
|
| 1150 |
+
{
|
| 1151 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-keepalive#0.49.0",
|
| 1152 |
+
"dependsOn": [
|
| 1153 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0"
|
| 1154 |
+
]
|
| 1155 |
+
},
|
| 1156 |
+
{
|
| 1157 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-key-parsing#0.49.0",
|
| 1158 |
+
"dependsOn": [
|
| 1159 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 1160 |
+
"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 1161 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-crypto#0.49.0",
|
| 1162 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-openssl#0.49.0",
|
| 1163 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0",
|
| 1164 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 1165 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 1166 |
+
"registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6"
|
| 1167 |
+
]
|
| 1168 |
+
},
|
| 1169 |
+
{
|
| 1170 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-openssl#0.49.0",
|
| 1171 |
+
"dependsOn": [
|
| 1172 |
+
"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 1173 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2",
|
| 1174 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1",
|
| 1175 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 1176 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117"
|
| 1177 |
+
]
|
| 1178 |
+
},
|
| 1179 |
+
{
|
| 1180 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0",
|
| 1181 |
+
"dependsOn": [
|
| 1182 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1"
|
| 1183 |
+
]
|
| 1184 |
+
},
|
| 1185 |
+
{
|
| 1186 |
+
"ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-x509-verification#0.49.0",
|
| 1187 |
+
"dependsOn": [
|
| 1188 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 1189 |
+
"path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpZ6keea/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0"
|
| 1190 |
+
]
|
| 1191 |
+
},
|
| 1192 |
+
{
|
| 1193 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1",
|
| 1194 |
+
"dependsOn": [
|
| 1195 |
+
"registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1",
|
| 1196 |
+
"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18"
|
| 1197 |
+
]
|
| 1198 |
+
},
|
| 1199 |
+
{
|
| 1200 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1",
|
| 1201 |
+
"dependsOn": [
|
| 1202 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106",
|
| 1203 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45",
|
| 1204 |
+
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"
|
| 1205 |
+
]
|
| 1206 |
+
},
|
| 1207 |
+
{
|
| 1208 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1"
|
| 1209 |
+
},
|
| 1210 |
+
{
|
| 1211 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0"
|
| 1212 |
+
},
|
| 1213 |
+
{
|
| 1214 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64",
|
| 1215 |
+
"dependsOn": [
|
| 1216 |
+
"registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9",
|
| 1217 |
+
"registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1"
|
| 1218 |
+
]
|
| 1219 |
+
},
|
| 1220 |
+
{
|
| 1221 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4"
|
| 1222 |
+
},
|
| 1223 |
+
{
|
| 1224 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9"
|
| 1225 |
+
},
|
| 1226 |
+
{
|
| 1227 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1"
|
| 1228 |
+
},
|
| 1229 |
+
{
|
| 1230 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2",
|
| 1231 |
+
"dependsOn": [
|
| 1232 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1"
|
| 1233 |
+
]
|
| 1234 |
+
},
|
| 1235 |
+
{
|
| 1236 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0"
|
| 1237 |
+
},
|
| 1238 |
+
{
|
| 1239 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18"
|
| 1240 |
+
},
|
| 1241 |
+
{
|
| 1242 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186"
|
| 1243 |
+
},
|
| 1244 |
+
{
|
| 1245 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4"
|
| 1246 |
+
},
|
| 1247 |
+
{
|
| 1248 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1",
|
| 1249 |
+
"dependsOn": [
|
| 1250 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106",
|
| 1251 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45",
|
| 1252 |
+
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"
|
| 1253 |
+
]
|
| 1254 |
+
},
|
| 1255 |
+
{
|
| 1256 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117",
|
| 1257 |
+
"dependsOn": [
|
| 1258 |
+
"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64",
|
| 1259 |
+
"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186",
|
| 1260 |
+
"registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33",
|
| 1261 |
+
"registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15"
|
| 1262 |
+
]
|
| 1263 |
+
},
|
| 1264 |
+
{
|
| 1265 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81",
|
| 1266 |
+
"dependsOn": [
|
| 1267 |
+
"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0",
|
| 1268 |
+
"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4",
|
| 1269 |
+
"registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2",
|
| 1270 |
+
"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186",
|
| 1271 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1",
|
| 1272 |
+
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117"
|
| 1273 |
+
]
|
| 1274 |
+
},
|
| 1275 |
+
{
|
| 1276 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6",
|
| 1277 |
+
"dependsOn": [
|
| 1278 |
+
"registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1"
|
| 1279 |
+
]
|
| 1280 |
+
},
|
| 1281 |
+
{
|
| 1282 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33"
|
| 1283 |
+
},
|
| 1284 |
+
{
|
| 1285 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.13.1"
|
| 1286 |
+
},
|
| 1287 |
+
{
|
| 1288 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106",
|
| 1289 |
+
"dependsOn": [
|
| 1290 |
+
"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"
|
| 1291 |
+
]
|
| 1292 |
+
},
|
| 1293 |
+
{
|
| 1294 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0",
|
| 1295 |
+
"dependsOn": [
|
| 1296 |
+
"registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5"
|
| 1297 |
+
]
|
| 1298 |
+
},
|
| 1299 |
+
{
|
| 1300 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0",
|
| 1301 |
+
"dependsOn": [
|
| 1302 |
+
"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186",
|
| 1303 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0"
|
| 1304 |
+
]
|
| 1305 |
+
},
|
| 1306 |
+
{
|
| 1307 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0",
|
| 1308 |
+
"dependsOn": [
|
| 1309 |
+
"registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0",
|
| 1310 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106",
|
| 1311 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45",
|
| 1312 |
+
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"
|
| 1313 |
+
]
|
| 1314 |
+
},
|
| 1315 |
+
{
|
| 1316 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0",
|
| 1317 |
+
"dependsOn": [
|
| 1318 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106",
|
| 1319 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0",
|
| 1320 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45",
|
| 1321 |
+
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117"
|
| 1322 |
+
]
|
| 1323 |
+
},
|
| 1324 |
+
{
|
| 1325 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0",
|
| 1326 |
+
"dependsOn": [
|
| 1327 |
+
"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186",
|
| 1328 |
+
"registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4",
|
| 1329 |
+
"registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.13.1",
|
| 1330 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0",
|
| 1331 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0",
|
| 1332 |
+
"registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0"
|
| 1333 |
+
]
|
| 1334 |
+
},
|
| 1335 |
+
{
|
| 1336 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45",
|
| 1337 |
+
"dependsOn": [
|
| 1338 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106"
|
| 1339 |
+
]
|
| 1340 |
+
},
|
| 1341 |
+
{
|
| 1342 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.2.2"
|
| 1343 |
+
},
|
| 1344 |
+
{
|
| 1345 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1"
|
| 1346 |
+
},
|
| 1347 |
+
{
|
| 1348 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117",
|
| 1349 |
+
"dependsOn": [
|
| 1350 |
+
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106",
|
| 1351 |
+
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45",
|
| 1352 |
+
"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"
|
| 1353 |
+
]
|
| 1354 |
+
},
|
| 1355 |
+
{
|
| 1356 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5"
|
| 1357 |
+
},
|
| 1358 |
+
{
|
| 1359 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24"
|
| 1360 |
+
},
|
| 1361 |
+
{
|
| 1362 |
+
"ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15"
|
| 1363 |
+
}
|
| 1364 |
+
]
|
| 1365 |
+
}
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/sboms/sbom.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bomFormat": "CycloneDX",
|
| 3 |
+
"specVersion": "1.5",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"serialNumber": "urn:uuid:eea42558-a3d3-4d7c-8bcc-d777faffe5ca",
|
| 6 |
+
"metadata": {
|
| 7 |
+
"timestamp": "2026-06-12T00:58:31Z"
|
| 8 |
+
},
|
| 9 |
+
"components": [
|
| 10 |
+
{
|
| 11 |
+
"type": "library",
|
| 12 |
+
"name": "openssl",
|
| 13 |
+
"version": "4.0.1",
|
| 14 |
+
"purl": "pkg:generic/openssl@4.0.1?download_url=https://github.com/openssl/openssl/releases/download/openssl-4.0.1/openssl-4.0.1.tar.gz",
|
| 15 |
+
"hashes": [
|
| 16 |
+
{
|
| 17 |
+
"alg": "SHA-256",
|
| 18 |
+
"content": "2db3f3a0d6ea4b59e1f094ace2c8cd536dffb87cdc39084c5afa1e6f7f37dd09"
|
| 19 |
+
}
|
| 20 |
+
],
|
| 21 |
+
"externalReferences": [
|
| 22 |
+
{
|
| 23 |
+
"type": "distribution",
|
| 24 |
+
"url": "https://github.com/openssl/openssl/releases/download/openssl-4.0.1/openssl-4.0.1.tar.gz"
|
| 25 |
+
}
|
| 26 |
+
],
|
| 27 |
+
"properties": [
|
| 28 |
+
{
|
| 29 |
+
"name": "build:operating-system",
|
| 30 |
+
"value": "linux"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"name": "build:architecture",
|
| 34 |
+
"value": "x86_64"
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"name": "build:flags",
|
| 38 |
+
"value": "no-zlib no-shared no-module no-comp no-apps no-docs no-sm2-precomp no-atexit enable-ec_nistp_64_gcc_128"
|
| 39 |
+
}
|
| 40 |
+
]
|
| 41 |
+
}
|
| 42 |
+
]
|
| 43 |
+
}
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/__about__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
__all__ = [
|
| 8 |
+
"__author__",
|
| 9 |
+
"__copyright__",
|
| 10 |
+
"__version__",
|
| 11 |
+
]
|
| 12 |
+
|
| 13 |
+
__version__ = "49.0.0"
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
__author__ = "The Python Cryptographic Authority and individual contributors"
|
| 17 |
+
__copyright__ = f"Copyright 2013-2026 {__author__}"
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from cryptography.__about__ import __author__, __copyright__, __version__
|
| 8 |
+
|
| 9 |
+
__all__ = [
|
| 10 |
+
"__author__",
|
| 11 |
+
"__copyright__",
|
| 12 |
+
"__version__",
|
| 13 |
+
]
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/exceptions.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is dual licensed under the terms of the Apache License, Version
|
| 2 |
+
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
| 3 |
+
# for complete details.
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import typing
|
| 8 |
+
|
| 9 |
+
from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions
|
| 10 |
+
|
| 11 |
+
if typing.TYPE_CHECKING:
|
| 12 |
+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
|
| 13 |
+
|
| 14 |
+
_Reasons = rust_exceptions._Reasons
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class UnsupportedAlgorithm(Exception):
|
| 18 |
+
def __init__(self, message: str, reason: _Reasons | None = None) -> None:
|
| 19 |
+
super().__init__(message)
|
| 20 |
+
self._reason = reason
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class AlreadyFinalized(Exception):
|
| 24 |
+
pass
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class AlreadyUpdated(Exception):
|
| 28 |
+
pass
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class NotYetFinalized(Exception):
|
| 32 |
+
pass
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class InvalidTag(Exception):
|
| 36 |
+
pass
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class InvalidSignature(Exception):
|
| 40 |
+
pass
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class InternalError(Exception):
|
| 44 |
+
def __init__(
|
| 45 |
+
self, msg: str, err_code: list[rust_openssl.OpenSSLError]
|
| 46 |
+
) -> None:
|
| 47 |
+
super().__init__(msg)
|
| 48 |
+
self.err_code = err_code
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class InvalidKey(Exception):
|
| 52 |
+
pass
|