smlflg commited on
Commit
d8355ff
·
verified ·
1 Parent(s): 9597b44

Initial public upload from Projekte/CodingAgent (part 2)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/INSTALLER +1 -0
  3. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/METADATA +67 -0
  4. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/RECORD +34 -0
  5. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/REQUESTED +0 -0
  6. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/WHEEL +6 -0
  7. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/entry_points.txt +5 -0
  8. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/licenses/LICENSE +23 -0
  9. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi-2.1.0.dist-info/top_level.txt +2 -0
  10. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/setuptools_ext.py +231 -0
  11. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/vengine_cpy.py +1058 -0
  12. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/vengine_gen.py +679 -0
  13. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cffi/verifier.py +306 -0
  14. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/INSTALLER +1 -0
  15. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/METADATA +84 -0
  16. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/RECORD +24 -0
  17. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/REQUESTED +0 -0
  18. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/WHEEL +4 -0
  19. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click-8.4.2.dist-info/licenses/LICENSE.txt +28 -0
  20. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/__init__.py +126 -0
  21. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_compat.py +626 -0
  22. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_termui_impl.py +945 -0
  23. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_textwrap.py +188 -0
  24. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_utils.py +36 -0
  25. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/_winconsole.py +297 -0
  26. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/core.py +0 -0
  27. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/decorators.py +575 -0
  28. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/exceptions.py +378 -0
  29. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/formatting.py +320 -0
  30. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/globals.py +67 -0
  31. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/parser.py +533 -0
  32. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/py.typed +0 -0
  33. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/shell_completion.py +705 -0
  34. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/termui.py +945 -0
  35. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/testing.py +772 -0
  36. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/types.py +1374 -0
  37. hai-mcp-pr7/.venv/lib/python3.13/site-packages/click/utils.py +653 -0
  38. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/INSTALLER +1 -0
  39. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/METADATA +110 -0
  40. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/RECORD +119 -0
  41. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/REQUESTED +0 -0
  42. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/WHEEL +5 -0
  43. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE +3 -0
  44. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.APACHE +202 -0
  45. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.BSD +27 -0
  46. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json +1365 -0
  47. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography-49.0.0.dist-info/sboms/sbom.json +43 -0
  48. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/__about__.py +17 -0
  49. hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/__init__.py +13 -0
  50. 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
+ [![GitHub Actions Status](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml?query=branch%3Amain++)
31
+ [![PyPI version](https://img.shields.io/pypi/v/cffi.svg)](https://pypi.org/project/cffi)
32
+ [![Read the Docs](https://img.shields.io/badge/docs-latest-blue.svg)][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