vera6 commited on
Commit
d5fa278
·
verified ·
1 Parent(s): e5aa2c7

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +162 -0
  2. .venv/Lib/site-packages/PyYAML-6.0.2.dist-info/INSTALLER +1 -0
  3. .venv/Lib/site-packages/PyYAML-6.0.2.dist-info/LICENSE +20 -0
  4. .venv/Lib/site-packages/PyYAML-6.0.2.dist-info/METADATA +46 -0
  5. .venv/Lib/site-packages/PyYAML-6.0.2.dist-info/RECORD +38 -0
  6. .venv/Lib/site-packages/PyYAML-6.0.2.dist-info/WHEEL +5 -0
  7. .venv/Lib/site-packages/PyYAML-6.0.2.dist-info/top_level.txt +2 -0
  8. .venv/Lib/site-packages/certifi-2025.8.3.dist-info/INSTALLER +1 -0
  9. .venv/Lib/site-packages/certifi-2025.8.3.dist-info/top_level.txt +1 -0
  10. .venv/Lib/site-packages/certifi/__main__.py +12 -0
  11. .venv/Lib/site-packages/certifi/cacert.pem +0 -0
  12. .venv/Lib/site-packages/certifi/core.py +83 -0
  13. .venv/Lib/site-packages/certifi/py.typed +0 -0
  14. .venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/INSTALLER +1 -0
  15. .venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/METADATA +170 -0
  16. .venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/RECORD +271 -0
  17. .venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/WHEEL +5 -0
  18. .venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/top_level.txt +1 -0
  19. .venv/Lib/site-packages/prompt_toolkit/__init__.py +53 -0
  20. .venv/Lib/site-packages/prompt_toolkit/document.py +1182 -0
  21. .venv/Lib/site-packages/prompt_toolkit/enums.py +19 -0
  22. .venv/Lib/site-packages/prompt_toolkit/history.py +306 -0
  23. .venv/Lib/site-packages/prompt_toolkit/keys.py +222 -0
  24. .venv/Lib/site-packages/prompt_toolkit/log.py +13 -0
  25. .venv/Lib/site-packages/prompt_toolkit/mouse_events.py +85 -0
  26. .venv/Lib/site-packages/prompt_toolkit/patch_stdout.py +297 -0
  27. .venv/Lib/site-packages/prompt_toolkit/py.typed +0 -0
  28. .venv/Lib/site-packages/prompt_toolkit/renderer.py +820 -0
  29. .venv/Lib/site-packages/prompt_toolkit/search.py +226 -0
  30. .venv/Lib/site-packages/prompt_toolkit/selection.py +58 -0
  31. .venv/Lib/site-packages/prompt_toolkit/token.py +9 -0
  32. .venv/Lib/site-packages/prompt_toolkit/utils.py +327 -0
  33. .venv/Lib/site-packages/prompt_toolkit/validation.py +192 -0
  34. .venv/Lib/site-packages/prompt_toolkit/win32_types.py +229 -0
  35. .venv/Lib/site-packages/requests-2.32.5.dist-info/INSTALLER +1 -0
  36. .venv/Lib/site-packages/requests-2.32.5.dist-info/METADATA +133 -0
  37. .venv/Lib/site-packages/requests-2.32.5.dist-info/RECORD +37 -0
  38. .venv/Lib/site-packages/requests-2.32.5.dist-info/WHEEL +5 -0
  39. .venv/Lib/site-packages/requests-2.32.5.dist-info/top_level.txt +1 -0
  40. .venv/Lib/site-packages/requests/__init__.py +184 -0
  41. .venv/Lib/site-packages/requests/__version__.py +14 -0
  42. .venv/Lib/site-packages/requests/_internal_utils.py +50 -0
  43. .venv/Lib/site-packages/requests/adapters.py +696 -0
  44. .venv/Lib/site-packages/requests/api.py +157 -0
  45. .venv/Lib/site-packages/requests/auth.py +314 -0
  46. .venv/Lib/site-packages/requests/certs.py +17 -0
  47. .venv/Lib/site-packages/requests/compat.py +106 -0
  48. .venv/Lib/site-packages/requests/cookies.py +561 -0
  49. .venv/Lib/site-packages/requests/exceptions.py +151 -0
  50. .venv/Lib/site-packages/requests/help.py +134 -0
.gitignore ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110
+ .pdm.toml
111
+ .pdm-python
112
+ .pdm-build/
113
+
114
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115
+ __pypackages__/
116
+
117
+ # Celery stuff
118
+ celerybeat-schedule
119
+ celerybeat.pid
120
+
121
+ # SageMath parsed files
122
+ *.sage.py
123
+
124
+ # Environments
125
+ .env
126
+ .venv
127
+ env/
128
+ venv/
129
+ ENV/
130
+ env.bak/
131
+ venv.bak/
132
+
133
+ # Spyder project settings
134
+ .spyderproject
135
+ .spyproject
136
+
137
+ # Rope project settings
138
+ .ropeproject
139
+
140
+ # mkdocs documentation
141
+ /site
142
+
143
+ # mypy
144
+ .mypy_cache/
145
+ .dmypy.json
146
+ dmypy.json
147
+
148
+ # Pyre type checker
149
+ .pyre/
150
+
151
+ # pytype static type analyzer
152
+ .pytype/
153
+
154
+ # Cython debug symbols
155
+ cython_debug/
156
+
157
+ # PyCharm
158
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
161
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
+ #.idea/
.venv/Lib/site-packages/PyYAML-6.0.2.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
.venv/Lib/site-packages/PyYAML-6.0.2.dist-info/LICENSE ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2017-2021 Ingy döt Net
2
+ Copyright (c) 2006-2016 Kirill Simonov
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ of the Software, and to permit persons to whom the Software is furnished to do
9
+ so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
.venv/Lib/site-packages/PyYAML-6.0.2.dist-info/METADATA ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.1
2
+ Name: PyYAML
3
+ Version: 6.0.2
4
+ Summary: YAML parser and emitter for Python
5
+ Home-page: https://pyyaml.org/
6
+ Download-URL: https://pypi.org/project/PyYAML/
7
+ Author: Kirill Simonov
8
+ Author-email: xi@resolvent.net
9
+ License: MIT
10
+ Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues
11
+ Project-URL: CI, https://github.com/yaml/pyyaml/actions
12
+ Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation
13
+ Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core
14
+ Project-URL: Source Code, https://github.com/yaml/pyyaml
15
+ Platform: Any
16
+ Classifier: Development Status :: 5 - Production/Stable
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Cython
21
+ Classifier: Programming Language :: Python
22
+ Classifier: Programming Language :: Python :: 3
23
+ Classifier: Programming Language :: Python :: 3.8
24
+ Classifier: Programming Language :: Python :: 3.9
25
+ Classifier: Programming Language :: Python :: 3.10
26
+ Classifier: Programming Language :: Python :: 3.11
27
+ Classifier: Programming Language :: Python :: 3.12
28
+ Classifier: Programming Language :: Python :: 3.13
29
+ Classifier: Programming Language :: Python :: Implementation :: CPython
30
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
31
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
32
+ Classifier: Topic :: Text Processing :: Markup
33
+ Requires-Python: >=3.8
34
+ License-File: LICENSE
35
+
36
+ YAML is a data serialization format designed for human readability
37
+ and interaction with scripting languages. PyYAML is a YAML parser
38
+ and emitter for Python.
39
+
40
+ PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
41
+ support, capable extension API, and sensible error messages. PyYAML
42
+ supports standard YAML tags and provides Python-specific tags that
43
+ allow to represent an arbitrary Python object.
44
+
45
+ PyYAML is applicable for a broad range of tasks from complex
46
+ configuration files to object serialization and persistence.
.venv/Lib/site-packages/PyYAML-6.0.2.dist-info/RECORD ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ PyYAML-6.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2
+ PyYAML-6.0.2.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101
3
+ PyYAML-6.0.2.dist-info/METADATA,sha256=9lwXqTOrXPts-jI2Lo5UwuaAYo0hiRA0BZqjch0WjAk,2106
4
+ PyYAML-6.0.2.dist-info/RECORD,,
5
+ PyYAML-6.0.2.dist-info/WHEEL,sha256=c7SWG1_hRvc9HXHEkmWlTu1Jr4WpzRucfzqTP-_8q0s,102
6
+ PyYAML-6.0.2.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11
7
+ _yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402
8
+ _yaml/__pycache__/__init__.cpython-312.pyc,,
9
+ yaml/__init__.py,sha256=N35S01HMesFTe0aRRMWkPj0Pa8IEbHpE9FK7cr5Bdtw,12311
10
+ yaml/__pycache__/__init__.cpython-312.pyc,,
11
+ yaml/__pycache__/composer.cpython-312.pyc,,
12
+ yaml/__pycache__/constructor.cpython-312.pyc,,
13
+ yaml/__pycache__/dumper.cpython-312.pyc,,
14
+ yaml/__pycache__/emitter.cpython-312.pyc,,
15
+ yaml/__pycache__/error.cpython-312.pyc,,
16
+ yaml/__pycache__/events.cpython-312.pyc,,
17
+ yaml/__pycache__/loader.cpython-312.pyc,,
18
+ yaml/__pycache__/nodes.cpython-312.pyc,,
19
+ yaml/__pycache__/reader.cpython-312.pyc,,
20
+ yaml/__pycache__/representer.cpython-312.pyc,,
21
+ yaml/__pycache__/resolver.cpython-312.pyc,,
22
+ yaml/_yaml.cp312-win_amd64.pyd,sha256=Bx7e_LEQx7cnd1_A9_nClp3X77g-_Lw1aoAAtYZbwWk,263680
23
+ yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883
24
+ yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639
25
+ yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851
26
+ yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837
27
+ yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006
28
+ yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533
29
+ yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445
30
+ yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061
31
+ yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440
32
+ yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495
33
+ yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794
34
+ yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190
35
+ yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004
36
+ yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279
37
+ yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165
38
+ yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573
.venv/Lib/site-packages/PyYAML-6.0.2.dist-info/WHEEL ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.44.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-cp312-win_amd64
5
+
.venv/Lib/site-packages/PyYAML-6.0.2.dist-info/top_level.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ _yaml
2
+ yaml
.venv/Lib/site-packages/certifi-2025.8.3.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
.venv/Lib/site-packages/certifi-2025.8.3.dist-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ certifi
.venv/Lib/site-packages/certifi/__main__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+
3
+ from certifi import contents, where
4
+
5
+ parser = argparse.ArgumentParser()
6
+ parser.add_argument("-c", "--contents", action="store_true")
7
+ args = parser.parse_args()
8
+
9
+ if args.contents:
10
+ print(contents())
11
+ else:
12
+ print(where())
.venv/Lib/site-packages/certifi/cacert.pem ADDED
The diff for this file is too large to render. See raw diff
 
.venv/Lib/site-packages/certifi/core.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ certifi.py
3
+ ~~~~~~~~~~
4
+
5
+ This module returns the installation location of cacert.pem or its contents.
6
+ """
7
+ import sys
8
+ import atexit
9
+
10
+ def exit_cacert_ctx() -> None:
11
+ _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr]
12
+
13
+
14
+ if sys.version_info >= (3, 11):
15
+
16
+ from importlib.resources import as_file, files
17
+
18
+ _CACERT_CTX = None
19
+ _CACERT_PATH = None
20
+
21
+ def where() -> str:
22
+ # This is slightly terrible, but we want to delay extracting the file
23
+ # in cases where we're inside of a zipimport situation until someone
24
+ # actually calls where(), but we don't want to re-extract the file
25
+ # on every call of where(), so we'll do it once then store it in a
26
+ # global variable.
27
+ global _CACERT_CTX
28
+ global _CACERT_PATH
29
+ if _CACERT_PATH is None:
30
+ # This is slightly janky, the importlib.resources API wants you to
31
+ # manage the cleanup of this file, so it doesn't actually return a
32
+ # path, it returns a context manager that will give you the path
33
+ # when you enter it and will do any cleanup when you leave it. In
34
+ # the common case of not needing a temporary file, it will just
35
+ # return the file system location and the __exit__() is a no-op.
36
+ #
37
+ # We also have to hold onto the actual context manager, because
38
+ # it will do the cleanup whenever it gets garbage collected, so
39
+ # we will also store that at the global level as well.
40
+ _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem"))
41
+ _CACERT_PATH = str(_CACERT_CTX.__enter__())
42
+ atexit.register(exit_cacert_ctx)
43
+
44
+ return _CACERT_PATH
45
+
46
+ def contents() -> str:
47
+ return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii")
48
+
49
+ else:
50
+
51
+ from importlib.resources import path as get_path, read_text
52
+
53
+ _CACERT_CTX = None
54
+ _CACERT_PATH = None
55
+
56
+ def where() -> str:
57
+ # This is slightly terrible, but we want to delay extracting the
58
+ # file in cases where we're inside of a zipimport situation until
59
+ # someone actually calls where(), but we don't want to re-extract
60
+ # the file on every call of where(), so we'll do it once then store
61
+ # it in a global variable.
62
+ global _CACERT_CTX
63
+ global _CACERT_PATH
64
+ if _CACERT_PATH is None:
65
+ # This is slightly janky, the importlib.resources API wants you
66
+ # to manage the cleanup of this file, so it doesn't actually
67
+ # return a path, it returns a context manager that will give
68
+ # you the path when you enter it and will do any cleanup when
69
+ # you leave it. In the common case of not needing a temporary
70
+ # file, it will just return the file system location and the
71
+ # __exit__() is a no-op.
72
+ #
73
+ # We also have to hold onto the actual context manager, because
74
+ # it will do the cleanup whenever it gets garbage collected, so
75
+ # we will also store that at the global level as well.
76
+ _CACERT_CTX = get_path("certifi", "cacert.pem")
77
+ _CACERT_PATH = str(_CACERT_CTX.__enter__())
78
+ atexit.register(exit_cacert_ctx)
79
+
80
+ return _CACERT_PATH
81
+
82
+ def contents() -> str:
83
+ return read_text("certifi", "cacert.pem", encoding="ascii")
.venv/Lib/site-packages/certifi/py.typed ADDED
File without changes
.venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
.venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/METADATA ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: prompt_toolkit
3
+ Version: 3.0.51
4
+ Summary: Library for building powerful interactive command lines in Python
5
+ Author: Jonathan Slenders
6
+ Classifier: Development Status :: 5 - Production/Stable
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: License :: OSI Approved :: BSD License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python
19
+ Classifier: Topic :: Software Development
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/x-rst
22
+ License-File: LICENSE
23
+ License-File: AUTHORS.rst
24
+ Requires-Dist: wcwidth
25
+ Dynamic: license-file
26
+
27
+ Python Prompt Toolkit
28
+ =====================
29
+
30
+ |AppVeyor| |PyPI| |RTD| |License| |Codecov|
31
+
32
+ .. image :: https://github.com/prompt-toolkit/python-prompt-toolkit/raw/master/docs/images/logo_400px.png
33
+
34
+ ``prompt_toolkit`` *is a library for building powerful interactive command line applications in Python.*
35
+
36
+ Read the `documentation on readthedocs
37
+ <http://python-prompt-toolkit.readthedocs.io/en/stable/>`_.
38
+
39
+
40
+ Gallery
41
+ *******
42
+
43
+ `ptpython <http://github.com/prompt-toolkit/ptpython/>`_ is an interactive
44
+ Python Shell, build on top of ``prompt_toolkit``.
45
+
46
+ .. image :: https://github.com/prompt-toolkit/python-prompt-toolkit/raw/master/docs/images/ptpython.png
47
+
48
+ `More examples <https://python-prompt-toolkit.readthedocs.io/en/stable/pages/gallery.html>`_
49
+
50
+
51
+ prompt_toolkit features
52
+ ***********************
53
+
54
+ ``prompt_toolkit`` could be a replacement for `GNU readline
55
+ <https://tiswww.case.edu/php/chet/readline/rltop.html>`_, but it can be much
56
+ more than that.
57
+
58
+ Some features:
59
+
60
+ - **Pure Python**.
61
+ - Syntax highlighting of the input while typing. (For instance, with a Pygments lexer.)
62
+ - Multi-line input editing.
63
+ - Advanced code completion.
64
+ - Both Emacs and Vi key bindings. (Similar to readline.)
65
+ - Even some advanced Vi functionality, like named registers and digraphs.
66
+ - Reverse and forward incremental search.
67
+ - Works well with Unicode double width characters. (Chinese input.)
68
+ - Selecting text for copy/paste. (Both Emacs and Vi style.)
69
+ - Support for `bracketed paste <https://cirw.in/blog/bracketed-paste>`_.
70
+ - Mouse support for cursor positioning and scrolling.
71
+ - Auto suggestions. (Like `fish shell <http://fishshell.com/>`_.)
72
+ - Multiple input buffers.
73
+ - No global state.
74
+ - Lightweight, the only dependencies are Pygments and wcwidth.
75
+ - Runs on Linux, OS X, FreeBSD, OpenBSD and Windows systems.
76
+ - And much more...
77
+
78
+ Feel free to create tickets for bugs and feature requests, and create pull
79
+ requests if you have nice patches that you would like to share with others.
80
+
81
+
82
+ Installation
83
+ ************
84
+
85
+ ::
86
+
87
+ pip install prompt_toolkit
88
+
89
+ For Conda, do:
90
+
91
+ ::
92
+
93
+ conda install -c https://conda.anaconda.org/conda-forge prompt_toolkit
94
+
95
+
96
+ About Windows support
97
+ *********************
98
+
99
+ ``prompt_toolkit`` is cross platform, and everything that you build on top
100
+ should run fine on both Unix and Windows systems. Windows support is best on
101
+ recent Windows 10 builds, for which the command line window supports vt100
102
+ escape sequences. (If not supported, we fall back to using Win32 APIs for color
103
+ and cursor movements).
104
+
105
+ It's worth noting that the implementation is a "best effort of what is
106
+ possible". Both Unix and Windows terminals have their limitations. But in
107
+ general, the Unix experience will still be a little better.
108
+
109
+
110
+ Getting started
111
+ ***************
112
+
113
+ The most simple example of the library would look like this:
114
+
115
+ .. code:: python
116
+
117
+ from prompt_toolkit import prompt
118
+
119
+ if __name__ == '__main__':
120
+ answer = prompt('Give me some input: ')
121
+ print('You said: %s' % answer)
122
+
123
+ For more complex examples, have a look in the ``examples`` directory. All
124
+ examples are chosen to demonstrate only one thing. Also, don't be afraid to
125
+ look at the source code. The implementation of the ``prompt`` function could be
126
+ a good start.
127
+
128
+ Philosophy
129
+ **********
130
+
131
+ The source code of ``prompt_toolkit`` should be **readable**, **concise** and
132
+ **efficient**. We prefer short functions focusing each on one task and for which
133
+ the input and output types are clearly specified. We mostly prefer composition
134
+ over inheritance, because inheritance can result in too much functionality in
135
+ the same object. We prefer immutable objects where possible (objects don't
136
+ change after initialization). Reusability is important. We absolutely refrain
137
+ from having a changing global state, it should be possible to have multiple
138
+ independent instances of the same code in the same process. The architecture
139
+ should be layered: the lower levels operate on primitive operations and data
140
+ structures giving -- when correctly combined -- all the possible flexibility;
141
+ while at the higher level, there should be a simpler API, ready-to-use and
142
+ sufficient for most use cases. Thinking about algorithms and efficiency is
143
+ important, but avoid premature optimization.
144
+
145
+
146
+ `Projects using prompt_toolkit <PROJECTS.rst>`_
147
+ ***********************************************
148
+
149
+ Special thanks to
150
+ *****************
151
+
152
+ - `Pygments <http://pygments.org/>`_: Syntax highlighter.
153
+ - `wcwidth <https://github.com/jquast/wcwidth>`_: Determine columns needed for a wide characters.
154
+
155
+ .. |PyPI| image:: https://img.shields.io/pypi/v/prompt_toolkit.svg
156
+ :target: https://pypi.python.org/pypi/prompt-toolkit/
157
+ :alt: Latest Version
158
+
159
+ .. |AppVeyor| image:: https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true
160
+ :target: https://ci.appveyor.com/project/prompt-toolkit/python-prompt-toolkit/
161
+
162
+ .. |RTD| image:: https://readthedocs.org/projects/python-prompt-toolkit/badge/
163
+ :target: https://python-prompt-toolkit.readthedocs.io/en/master/
164
+
165
+ .. |License| image:: https://img.shields.io/github/license/prompt-toolkit/python-prompt-toolkit.svg
166
+ :target: https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/LICENSE
167
+
168
+ .. |Codecov| image:: https://codecov.io/gh/prompt-toolkit/python-prompt-toolkit/branch/master/graphs/badge.svg?style=flat
169
+ :target: https://codecov.io/gh/prompt-toolkit/python-prompt-toolkit/
170
+
.venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/RECORD ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ prompt_toolkit-3.0.51.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2
+ prompt_toolkit-3.0.51.dist-info/METADATA,sha256=lewVXm0ZlAORaErFwfRKkNDPtq-1hlXd_c58vyT3fvY,6383
3
+ prompt_toolkit-3.0.51.dist-info/RECORD,,
4
+ prompt_toolkit-3.0.51.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
5
+ prompt_toolkit-3.0.51.dist-info/licenses/AUTHORS.rst,sha256=09xixryENmWElauJrqN1Eef6k5HSgmVyOcnPuA29QuU,148
6
+ prompt_toolkit-3.0.51.dist-info/licenses/LICENSE,sha256=MDV02b3YXHV9YCUBeUK_F7ru3yd49ivX9CXQfYgPTEo,1493
7
+ prompt_toolkit-3.0.51.dist-info/top_level.txt,sha256=5rJXrEGx6st4KkmhOPR6l0ITDbV53x_Xy6MurOukXfA,15
8
+ prompt_toolkit/__init__.py,sha256=bZ8Mrn9XS4WaevxVwEwKLKZ9EpPI5IPA1x6rRR7tkVI,1354
9
+ prompt_toolkit/__pycache__/__init__.cpython-312.pyc,,
10
+ prompt_toolkit/__pycache__/cache.cpython-312.pyc,,
11
+ prompt_toolkit/__pycache__/cursor_shapes.cpython-312.pyc,,
12
+ prompt_toolkit/__pycache__/data_structures.cpython-312.pyc,,
13
+ prompt_toolkit/__pycache__/document.cpython-312.pyc,,
14
+ prompt_toolkit/__pycache__/enums.cpython-312.pyc,,
15
+ prompt_toolkit/__pycache__/history.cpython-312.pyc,,
16
+ prompt_toolkit/__pycache__/log.cpython-312.pyc,,
17
+ prompt_toolkit/__pycache__/mouse_events.cpython-312.pyc,,
18
+ prompt_toolkit/__pycache__/patch_stdout.cpython-312.pyc,,
19
+ prompt_toolkit/__pycache__/renderer.cpython-312.pyc,,
20
+ prompt_toolkit/__pycache__/search.cpython-312.pyc,,
21
+ prompt_toolkit/__pycache__/selection.cpython-312.pyc,,
22
+ prompt_toolkit/__pycache__/token.cpython-312.pyc,,
23
+ prompt_toolkit/__pycache__/utils.cpython-312.pyc,,
24
+ prompt_toolkit/__pycache__/validation.cpython-312.pyc,,
25
+ prompt_toolkit/__pycache__/win32_types.cpython-312.pyc,,
26
+ prompt_toolkit/application/__init__.py,sha256=rat9iPhYTmo7nd2BU8xZSU_-AfJpjnnBmxe9y3TQivM,657
27
+ prompt_toolkit/application/__pycache__/__init__.cpython-312.pyc,,
28
+ prompt_toolkit/application/__pycache__/current.cpython-312.pyc,,
29
+ prompt_toolkit/application/__pycache__/run_in_terminal.cpython-312.pyc,,
30
+ prompt_toolkit/application/application.py,sha256=oziKsiHNmGJQhMj1ODQIkJSeOI3ejRWWQ9XduVHYOAE,63046
31
+ prompt_toolkit/application/current.py,sha256=uCJz0tx0fnj39xMrV3R_-iJEAA4-s8ik9dC5x21NQgk,6207
32
+ prompt_toolkit/application/dummy.py,sha256=BCfThUgz5Eb5fWJSKBVeJaA5kwksw8jJQtN6g61xMXM,1619
33
+ prompt_toolkit/application/run_in_terminal.py,sha256=qgTfpG3qGP4wRy9l_7zU8P7s59CARIvagulTyPn_oEg,3767
34
+ prompt_toolkit/auto_suggest.py,sha256=qSiaxlaKjLyNEJ8bJN0641gqsIzZ3LB2cOyq88xBQb4,5798
35
+ prompt_toolkit/buffer.py,sha256=VkAbKTJV7441aO4Ei-ozqc8IBlNPEnLGAt5t42tB6jg,74513
36
+ prompt_toolkit/cache.py,sha256=Lo3ewsEIgn-LQBYNni79w74u5LSvvuVYF0e6giEArQg,3823
37
+ prompt_toolkit/clipboard/__init__.py,sha256=yK0LonIfEZRyoXqcgLdh8kjOhechjO-Ej2-C1g3VegQ,439
38
+ prompt_toolkit/clipboard/__pycache__/__init__.cpython-312.pyc,,
39
+ prompt_toolkit/clipboard/__pycache__/base.cpython-312.pyc,,
40
+ prompt_toolkit/clipboard/__pycache__/in_memory.cpython-312.pyc,,
41
+ prompt_toolkit/clipboard/__pycache__/pyperclip.cpython-312.pyc,,
42
+ prompt_toolkit/clipboard/base.py,sha256=rucEv1kKfvZUj6bwGRz04uSSTZie7rvnKUnyVXb2vv4,2515
43
+ prompt_toolkit/clipboard/in_memory.py,sha256=U_iY6UUevkKMfTvir_XMsD0qwuLVKuoTeRdjkZW-A6I,1060
44
+ prompt_toolkit/clipboard/pyperclip.py,sha256=H9HOlyGW0XItvx_Ji64zBQkiQPfLb6DFAw5J5irzK28,1160
45
+ prompt_toolkit/completion/__init__.py,sha256=8Hm2yJ1nqBkaC-R9ugELgjhU32U308V89F6bJG0QDYo,992
46
+ prompt_toolkit/completion/__pycache__/__init__.cpython-312.pyc,,
47
+ prompt_toolkit/completion/__pycache__/deduplicate.cpython-312.pyc,,
48
+ prompt_toolkit/completion/__pycache__/filesystem.cpython-312.pyc,,
49
+ prompt_toolkit/completion/__pycache__/fuzzy_completer.cpython-312.pyc,,
50
+ prompt_toolkit/completion/__pycache__/nested.cpython-312.pyc,,
51
+ prompt_toolkit/completion/__pycache__/word_completer.cpython-312.pyc,,
52
+ prompt_toolkit/completion/base.py,sha256=T7212aScNaGMaSrDIwsJIXeXLIM_eVCIcScNcDPZYwI,16103
53
+ prompt_toolkit/completion/deduplicate.py,sha256=QibqYI23GPjsbyxaxiNoqAbKawzHmfYOlxeW2HPFbTE,1436
54
+ prompt_toolkit/completion/filesystem.py,sha256=Z_RR72e14bVavdWnbxECw23qCt_TWTY9R6DpVqW7vxE,3949
55
+ prompt_toolkit/completion/fuzzy_completer.py,sha256=RnREvA7y6nC7LKGqZUEvtuSm8eXVQYheJTsnhUvRbmM,7738
56
+ prompt_toolkit/completion/nested.py,sha256=ig2Qy4dLyDvOT6O8Qb-iRZLWzlT11S5tVQ3GFZmpm-U,3844
57
+ prompt_toolkit/completion/word_completer.py,sha256=VF1S7YCxYqI3pKmVXJaD82eMW1ZMq8_zAAIS1XKGU5M,3435
58
+ prompt_toolkit/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
+ prompt_toolkit/contrib/__pycache__/__init__.cpython-312.pyc,,
60
+ prompt_toolkit/contrib/completers/__init__.py,sha256=qJB_xNFGbhfiDv_zUaox9mkQEGqBYqP_jfByQDb93hA,103
61
+ prompt_toolkit/contrib/completers/__pycache__/__init__.cpython-312.pyc,,
62
+ prompt_toolkit/contrib/completers/__pycache__/system.cpython-312.pyc,,
63
+ prompt_toolkit/contrib/completers/system.py,sha256=0Hc2dziheEx2qNog4YOl-4Tu8Fg5Dx2xjNURTx09BDg,2057
64
+ prompt_toolkit/contrib/regular_languages/__init__.py,sha256=cgMQkakD4FbvLUozDGucRRFOk8yScfcKfqOMpCtvAPo,3279
65
+ prompt_toolkit/contrib/regular_languages/__pycache__/__init__.cpython-312.pyc,,
66
+ prompt_toolkit/contrib/regular_languages/__pycache__/completion.cpython-312.pyc,,
67
+ prompt_toolkit/contrib/regular_languages/__pycache__/lexer.cpython-312.pyc,,
68
+ prompt_toolkit/contrib/regular_languages/__pycache__/regex_parser.cpython-312.pyc,,
69
+ prompt_toolkit/contrib/regular_languages/__pycache__/validation.cpython-312.pyc,,
70
+ prompt_toolkit/contrib/regular_languages/compiler.py,sha256=3tnUJCE2jCcVI63vcpI0kG4KfuqIatSQRb8-F5UCgsI,21948
71
+ prompt_toolkit/contrib/regular_languages/completion.py,sha256=jESF35RaYWj_rnT-OZc_zC9QZXYvPao4JZ8wx7yS3KM,3468
72
+ prompt_toolkit/contrib/regular_languages/lexer.py,sha256=DBgyek9LkfJv6hz24eOaVM--w9Qaw4zIMWusMvGHBts,3415
73
+ prompt_toolkit/contrib/regular_languages/regex_parser.py,sha256=zWGJfQSjomvdj2rD7MPpn2pWOUR7VMv4su5iAV0jzM4,7732
74
+ prompt_toolkit/contrib/regular_languages/validation.py,sha256=4k5wxgUFc_KTOW5PmmZOrWb-Z-HjX8fjjKqul-oR8uc,2059
75
+ prompt_toolkit/contrib/ssh/__init__.py,sha256=UcRG2wc28EEKtFEudoIXz_DFzWKKQjAVSv6cf-ufPiM,180
76
+ prompt_toolkit/contrib/ssh/__pycache__/__init__.cpython-312.pyc,,
77
+ prompt_toolkit/contrib/ssh/__pycache__/server.cpython-312.pyc,,
78
+ prompt_toolkit/contrib/ssh/server.py,sha256=81McNn6r0Cbu9SPceH7fa5QirAnteHmNh1Gk4dFpgvI,6130
79
+ prompt_toolkit/contrib/telnet/__init__.py,sha256=NyUfsmJdafGiUxD9gzYQNlVdHu_ILDH7F57VJw8efUM,104
80
+ prompt_toolkit/contrib/telnet/__pycache__/__init__.cpython-312.pyc,,
81
+ prompt_toolkit/contrib/telnet/__pycache__/log.cpython-312.pyc,,
82
+ prompt_toolkit/contrib/telnet/__pycache__/server.cpython-312.pyc,,
83
+ prompt_toolkit/contrib/telnet/log.py,sha256=LcFRDyRxoRKSZsVRVpBOrEgsEt_LQLyUHKtgVZklopI,167
84
+ prompt_toolkit/contrib/telnet/protocol.py,sha256=2i-JYfaAse-uFWtNdVEoP_Q-OMbkl3YbUfv_wvaaS3k,5584
85
+ prompt_toolkit/contrib/telnet/server.py,sha256=dKHpEhXkIef_iuvZCbumwotapx6i03t6Gk01zkAoNIU,13477
86
+ prompt_toolkit/cursor_shapes.py,sha256=k5g5yJONGl1ITgy29KX9yzspJvIJ6Jbbwd7WkYC9Z-4,3721
87
+ prompt_toolkit/data_structures.py,sha256=w0BZy6Fpx4se-kAI9Kj8Q7lAKLln8U_Em_ncpqnC1xY,212
88
+ prompt_toolkit/document.py,sha256=vzg3U2Zzd95l1pkZnIFjAA88ygFYSdybhRKwJcxvvr0,40579
89
+ prompt_toolkit/enums.py,sha256=F3q9JmH9vhpMLA2OKKN7RrNQu_YDlNWoPU-0qsTUuAs,358
90
+ prompt_toolkit/eventloop/__init__.py,sha256=pxSkV_zybeoj6Ff3lgNHhbD5ENmBW9mk_XkiyeRL_OY,730
91
+ prompt_toolkit/eventloop/__pycache__/inputhook.cpython-312.pyc,,
92
+ prompt_toolkit/eventloop/__pycache__/utils.cpython-312.pyc,,
93
+ prompt_toolkit/eventloop/async_generator.py,sha256=nozLJR4z2MJKV7Qi0hsknA2mb1Jcp7XJx-AdUEDhDhw,3933
94
+ prompt_toolkit/eventloop/inputhook.py,sha256=LDElZtmg-kLQiItMS8CFPxtLzxV8QzohWHsWUvw3h00,6130
95
+ prompt_toolkit/eventloop/utils.py,sha256=VhYmsDZmRwVXnEPBF_C2LpiW-ranPn6EIXWIuMa6XaU,3200
96
+ prompt_toolkit/eventloop/win32.py,sha256=wrLJVOtOw_tqVOeK6ttNF47Sk2oX342dLN1pxKBLCL4,2286
97
+ prompt_toolkit/filters/__init__.py,sha256=2YSVwf4EnLf1VOXYmb8Dr0WoA93XGGO0iCUIr14KGXQ,1990
98
+ prompt_toolkit/filters/__pycache__/__init__.cpython-312.pyc,,
99
+ prompt_toolkit/filters/__pycache__/app.cpython-312.pyc,,
100
+ prompt_toolkit/filters/__pycache__/base.cpython-312.pyc,,
101
+ prompt_toolkit/filters/__pycache__/cli.cpython-312.pyc,,
102
+ prompt_toolkit/filters/__pycache__/utils.cpython-312.pyc,,
103
+ prompt_toolkit/filters/app.py,sha256=QVJMjR6Zf-BxlmGaUd-WbtEaGlxMKYMFVwj3qcwo7ns,10408
104
+ prompt_toolkit/filters/base.py,sha256=asrgKE-gzYlRLrS4w3kMFimvZtXQ9pk252Vs5ShVeeM,6855
105
+ prompt_toolkit/filters/cli.py,sha256=QGV7JT7-BUXpPXNzBLUcNH3GI69ugFZCDV1nylOjq78,1867
106
+ prompt_toolkit/filters/utils.py,sha256=4nOjHPEd451Pj3qpfg40fq3XSnt1kmq3WoAbhu2NV-8,859
107
+ prompt_toolkit/formatted_text/__init__.py,sha256=aQtNhxOhIa_HmvlNOQ2RGGpplg-KX3sYFJWiXgNfQxY,1509
108
+ prompt_toolkit/formatted_text/__pycache__/__init__.cpython-312.pyc,,
109
+ prompt_toolkit/formatted_text/__pycache__/base.cpython-312.pyc,,
110
+ prompt_toolkit/formatted_text/__pycache__/html.cpython-312.pyc,,
111
+ prompt_toolkit/formatted_text/__pycache__/pygments.cpython-312.pyc,,
112
+ prompt_toolkit/formatted_text/__pycache__/utils.cpython-312.pyc,,
113
+ prompt_toolkit/formatted_text/ansi.py,sha256=5uERUQxVrXAQfbTE5R9oWTIqLnDfNmsNpWPtfH5nLSo,9679
114
+ prompt_toolkit/formatted_text/base.py,sha256=X3y5QIPH2IS9LesYzXneELtT4zGpik8gd-UQVh6I2bE,5162
115
+ prompt_toolkit/formatted_text/html.py,sha256=-88VwuuCLRNkzEgK8FJKOHT9NDh939BxH8vGivvILdU,4374
116
+ prompt_toolkit/formatted_text/pygments.py,sha256=sK-eFFzOnD2sgadVLgNkW-xOuTw_uIf8_z06DZ4CA8g,780
117
+ prompt_toolkit/formatted_text/utils.py,sha256=r6tPtwo6dqvqf9gqZ7ARyvtNUjDDq6QZqrTWg6EMFuQ,3044
118
+ prompt_toolkit/history.py,sha256=S9W9SgL83QftMQANdjdjBMm-yGmeM51_qCRRC1H4Mr8,9441
119
+ prompt_toolkit/input/__init__.py,sha256=7g6kwNanG4Ml12FFdj9E1ivChpXWcfRUMUJzmTQMS7U,273
120
+ prompt_toolkit/input/__pycache__/__init__.cpython-312.pyc,,
121
+ prompt_toolkit/input/__pycache__/ansi_escape_sequences.cpython-312.pyc,,
122
+ prompt_toolkit/input/__pycache__/base.cpython-312.pyc,,
123
+ prompt_toolkit/input/__pycache__/defaults.cpython-312.pyc,,
124
+ prompt_toolkit/input/__pycache__/posix_pipe.cpython-312.pyc,,
125
+ prompt_toolkit/input/__pycache__/posix_utils.cpython-312.pyc,,
126
+ prompt_toolkit/input/__pycache__/typeahead.cpython-312.pyc,,
127
+ prompt_toolkit/input/__pycache__/vt100.cpython-312.pyc,,
128
+ prompt_toolkit/input/__pycache__/win32.cpython-312.pyc,,
129
+ prompt_toolkit/input/__pycache__/win32_pipe.cpython-312.pyc,,
130
+ prompt_toolkit/input/ansi_escape_sequences.py,sha256=h7714SZgs2z80PZRVxsCfHHJjtEUmWlToVCBtFFvfR4,13663
131
+ prompt_toolkit/input/base.py,sha256=pItwaKXtVZwemrKpoltmRskwoeXnSaBUZ_6iFZBdQf8,4036
132
+ prompt_toolkit/input/defaults.py,sha256=a-vczSh7kngFArLhFsJ2CXNdkx5WQlzilxHLdzGDkFw,2500
133
+ prompt_toolkit/input/posix_pipe.py,sha256=B_JS2-FB6Sk0da9gSH0NnhcUCkp3bw0m1-ogMOHmmcE,3158
134
+ prompt_toolkit/input/posix_utils.py,sha256=ySaEGnt_IwG5nzxcpILgEXC60mbrIAbC3ZZ6kuE9zCw,3973
135
+ prompt_toolkit/input/typeahead.py,sha256=mAaf5_XKTLpao1kw9ORIrhGGEz9gvu4oc-iZKKMQz3k,2545
136
+ prompt_toolkit/input/vt100.py,sha256=soxxSLU7fwp6yn77j5gCYUZroEp7KBKm4a3Zn4vRAsk,10514
137
+ prompt_toolkit/input/vt100_parser.py,sha256=qDrNbsnPukZblfyjgfjCvzMv8xQKRz0M84UvUWq7P44,8407
138
+ prompt_toolkit/input/win32.py,sha256=9UKo8f_W4AU8P4Luc7G3vWBWSCbpUIQDE0xlKp76mH8,30769
139
+ prompt_toolkit/input/win32_pipe.py,sha256=OvjKHN5xfEoGHLygWwayyeB0RolHL6YHLNeOMK-54LU,4700
140
+ prompt_toolkit/key_binding/__init__.py,sha256=IZWqJLBjQaQMfo0SJTjqJKQH0TZcSNa2Cdln-M4z8JI,447
141
+ prompt_toolkit/key_binding/__pycache__/__init__.cpython-312.pyc,,
142
+ prompt_toolkit/key_binding/__pycache__/defaults.cpython-312.pyc,,
143
+ prompt_toolkit/key_binding/__pycache__/emacs_state.cpython-312.pyc,,
144
+ prompt_toolkit/key_binding/__pycache__/key_bindings.cpython-312.pyc,,
145
+ prompt_toolkit/key_binding/__pycache__/key_processor.cpython-312.pyc,,
146
+ prompt_toolkit/key_binding/__pycache__/vi_state.cpython-312.pyc,,
147
+ prompt_toolkit/key_binding/bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
148
+ prompt_toolkit/key_binding/bindings/__pycache__/__init__.cpython-312.pyc,,
149
+ prompt_toolkit/key_binding/bindings/__pycache__/auto_suggest.cpython-312.pyc,,
150
+ prompt_toolkit/key_binding/bindings/__pycache__/basic.cpython-312.pyc,,
151
+ prompt_toolkit/key_binding/bindings/__pycache__/cpr.cpython-312.pyc,,
152
+ prompt_toolkit/key_binding/bindings/__pycache__/emacs.cpython-312.pyc,,
153
+ prompt_toolkit/key_binding/bindings/__pycache__/focus.cpython-312.pyc,,
154
+ prompt_toolkit/key_binding/bindings/__pycache__/mouse.cpython-312.pyc,,
155
+ prompt_toolkit/key_binding/bindings/__pycache__/named_commands.cpython-312.pyc,,
156
+ prompt_toolkit/key_binding/bindings/__pycache__/open_in_editor.cpython-312.pyc,,
157
+ prompt_toolkit/key_binding/bindings/__pycache__/page_navigation.cpython-312.pyc,,
158
+ prompt_toolkit/key_binding/bindings/__pycache__/search.cpython-312.pyc,,
159
+ prompt_toolkit/key_binding/bindings/__pycache__/vi.cpython-312.pyc,,
160
+ prompt_toolkit/key_binding/bindings/auto_suggest.py,sha256=4PrJVgIK_Nt2o3RtVtuRm2aFPGrackhuMCBVNtjPj7M,1855
161
+ prompt_toolkit/key_binding/bindings/basic.py,sha256=Fp9mj-RYZlGmAU9UV9wIIEnlxELN7NJ0qakMVH7MuRU,7229
162
+ prompt_toolkit/key_binding/bindings/completion.py,sha256=6nR3WfGe7FDsjq1xTsDazeajkV9KBLpCYQi3klujdLU,6903
163
+ prompt_toolkit/key_binding/bindings/cpr.py,sha256=181XQNZ0-sgL-vV2B67aRitTFHadogvMUh6LWVMUTV0,786
164
+ prompt_toolkit/key_binding/bindings/emacs.py,sha256=trIZUu8e5kJGSaq6Ndb-Exz4NdHV9SjUsfsw_UM8c6o,19634
165
+ prompt_toolkit/key_binding/bindings/focus.py,sha256=LIP4InccUUvD7I4NZrqtY9WjVfO_wJLyrVcoxAw92uU,507
166
+ prompt_toolkit/key_binding/bindings/mouse.py,sha256=6JPr0BqzFfLEVb7Ek_WO0CejUcwq0jIrrNwvSGkHeus,18586
167
+ prompt_toolkit/key_binding/bindings/named_commands.py,sha256=jdkqQ-ltNYC2BgIW1QdG7Qx4mWIod2Ps6C2TpL6NJ-Y,18407
168
+ prompt_toolkit/key_binding/bindings/open_in_editor.py,sha256=bgVmeDmVtHsgzJnc59b-dOZ-nO6WydBYI_7aOWMpe5c,1356
169
+ prompt_toolkit/key_binding/bindings/page_navigation.py,sha256=RPLUEZuZvekErPazex7pK0c6LxeV9LshewBHp012iMI,2392
170
+ prompt_toolkit/key_binding/bindings/scroll.py,sha256=hQeQ0m2AStUKjVNDXfa9DTMw3WS5qzW1n3gU0fkfWFk,5613
171
+ prompt_toolkit/key_binding/bindings/search.py,sha256=rU6VYra1IDzN6mG4mrbGivrZ-hjy_kZcjsKqmdVJKAE,2632
172
+ prompt_toolkit/key_binding/bindings/vi.py,sha256=TSglqzPZU9VMernOvn09GVxObFXpXuyCSiH9i1MpIIo,75602
173
+ prompt_toolkit/key_binding/defaults.py,sha256=JZJTshyBV39cWH2AT7xDP9AXOiyXQpjaI-ckePTi7os,1975
174
+ prompt_toolkit/key_binding/digraphs.py,sha256=rZvh9AdY5Te6bSlIHRQNskJYVIONYahYuu-w9Pti5yM,32785
175
+ prompt_toolkit/key_binding/emacs_state.py,sha256=ZJBWcLTzgtRkUW9UiDuI-SRrnlLsxu3IrTOK0_UQt5Y,884
176
+ prompt_toolkit/key_binding/key_bindings.py,sha256=0QDWvFuct2vAIHK-hrQmEipmiRMBQbWP4JB1PsXVZKY,20927
177
+ prompt_toolkit/key_binding/key_processor.py,sha256=0WLK4dcU8klL2Xna_RKxOpsW7t8ld67Y9Xmto3U-n0E,17555
178
+ prompt_toolkit/key_binding/vi_state.py,sha256=p-JuzwYtWl25tMmfRZ6e7UQWDi7RlXnAggir7ZSi07I,3341
179
+ prompt_toolkit/keys.py,sha256=nDkIqJbm_dRsVjArp7oItGKIFAAnSxcSniSwc1O-BYA,4916
180
+ prompt_toolkit/layout/__init__.py,sha256=gNbniLmlvkWwPE6Kg2ykyZJRTOKsWnHbwUjyO-VFDP8,3603
181
+ prompt_toolkit/layout/__pycache__/__init__.cpython-312.pyc,,
182
+ prompt_toolkit/layout/__pycache__/containers.cpython-312.pyc,,
183
+ prompt_toolkit/layout/__pycache__/controls.cpython-312.pyc,,
184
+ prompt_toolkit/layout/__pycache__/dimension.cpython-312.pyc,,
185
+ prompt_toolkit/layout/__pycache__/dummy.cpython-312.pyc,,
186
+ prompt_toolkit/layout/__pycache__/layout.cpython-312.pyc,,
187
+ prompt_toolkit/layout/__pycache__/margins.cpython-312.pyc,,
188
+ prompt_toolkit/layout/__pycache__/menus.cpython-312.pyc,,
189
+ prompt_toolkit/layout/__pycache__/mouse_handlers.cpython-312.pyc,,
190
+ prompt_toolkit/layout/__pycache__/processors.cpython-312.pyc,,
191
+ prompt_toolkit/layout/__pycache__/scrollable_pane.cpython-312.pyc,,
192
+ prompt_toolkit/layout/__pycache__/utils.cpython-312.pyc,,
193
+ prompt_toolkit/layout/containers.py,sha256=ZdpJEFJT11_CDWJEV6fDv5w0NmjnG8kpB4s_JMay-_s,99206
194
+ prompt_toolkit/layout/controls.py,sha256=9h6425oGeBwLO85MBNdHSh6XsrtEay5JwuIX-fuzsVI,35993
195
+ prompt_toolkit/layout/dimension.py,sha256=e1Zbptz3dRcG7khlC3I3DbIhXnFfpLxYOOBoELAiZ20,7052
196
+ prompt_toolkit/layout/dummy.py,sha256=8zB3MwDDd4RpI880WUKhv719tTzy5bXS9gm9zdkBZ10,1047
197
+ prompt_toolkit/layout/layout.py,sha256=VXqWAoL3EviGn4CxtOrFJekMALvl9xff1bTSnE-gXF8,13960
198
+ prompt_toolkit/layout/margins.py,sha256=bt-IvD03uQvmLVYvGZLqPLluR6kUlBRBAGJwCc8F7II,10375
199
+ prompt_toolkit/layout/menus.py,sha256=B4H2oCPF48gLy9cB0vkdGIoH_8gGyj95TDHtfxXRVSw,27195
200
+ prompt_toolkit/layout/mouse_handlers.py,sha256=lwbGSdpn6_pcK7HQWJ6IvHsxTf1_ahBew4pkmtU6zUM,1589
201
+ prompt_toolkit/layout/processors.py,sha256=0VE4UIGRzyXvDO4XqCB7LXNG9WkSxLz7FW7toOvHDSE,34296
202
+ prompt_toolkit/layout/screen.py,sha256=2PWdPDkQxtJrMSv9oqdZrWa7ChCnC7J4SvfVIithi5E,10113
203
+ prompt_toolkit/layout/scrollable_pane.py,sha256=JQtPfafU61RJt3MzGW2wsw96o1sjJH0g2DSVyO7J6qA,19264
204
+ prompt_toolkit/layout/utils.py,sha256=qot9clyeG3FoplfAS2O6QxmnnA_PDln4-dUJ5Hu76fQ,2371
205
+ prompt_toolkit/lexers/__init__.py,sha256=QldV9b8J2Kb9Exyv2fDss-YRzP07z2FYAhwPN4coWn8,409
206
+ prompt_toolkit/lexers/__pycache__/__init__.cpython-312.pyc,,
207
+ prompt_toolkit/lexers/base.py,sha256=XdyKLj4rD25CVCqSCfElWE3ppBN1LGQ9fRLPi1oYfl0,2350
208
+ prompt_toolkit/lexers/pygments.py,sha256=it89LjsltZpzlQJPb95GX4GdMu7gq1J1QzWC29lCQi4,11922
209
+ prompt_toolkit/log.py,sha256=6typpL_jnewdUc3j2OoplVLwnP9dSMOkIsJy_sgR9IY,153
210
+ prompt_toolkit/mouse_events.py,sha256=4mUHJbG8WrrQznw7z_jsOrdmldC5ZMRM4gDDUy51pyk,2473
211
+ prompt_toolkit/output/__init__.py,sha256=GVlT-U_W0EuIP-c1Qjyp0DN6Fl2PsCEhFzjUMRHsGWI,280
212
+ prompt_toolkit/output/__pycache__/__init__.cpython-312.pyc,,
213
+ prompt_toolkit/output/__pycache__/color_depth.cpython-312.pyc,,
214
+ prompt_toolkit/output/__pycache__/conemu.cpython-312.pyc,,
215
+ prompt_toolkit/output/__pycache__/defaults.cpython-312.pyc,,
216
+ prompt_toolkit/output/__pycache__/flush_stdout.cpython-312.pyc,,
217
+ prompt_toolkit/output/__pycache__/plain_text.cpython-312.pyc,,
218
+ prompt_toolkit/output/__pycache__/windows10.cpython-312.pyc,,
219
+ prompt_toolkit/output/base.py,sha256=o74Vok7cXLxgHoAaqKHQAGcNZILn5B5g6Z0pUXU6x7s,8348
220
+ prompt_toolkit/output/color_depth.py,sha256=KEFTlxCYTqOvA-VDx4wUb8G6HaYD5Hbf5GKmPZwssCs,1569
221
+ prompt_toolkit/output/conemu.py,sha256=_w2IEFR-mXsaMFINgZITiJNRCS9QowLUxeskPEpz2GE,1865
222
+ prompt_toolkit/output/defaults.py,sha256=72RecTuugrjvfZinbvsFRYDwMcczE9Zw3ttmmiG0Ivg,3689
223
+ prompt_toolkit/output/flush_stdout.py,sha256=ReT0j0IwVJEcth7VJj2zE6UcY0OVz5Ut1rpANnbCyYQ,3236
224
+ prompt_toolkit/output/plain_text.py,sha256=VnjoDmy0pKQoubXXQJQ_MljoDYi1FcLdNZB2KN_TQIs,3296
225
+ prompt_toolkit/output/vt100.py,sha256=db6G9uoSDaNbGVejjnYNffZ4nPsDDcL_X70yrJCb9Ds,23383
226
+ prompt_toolkit/output/win32.py,sha256=d5LG_3dLLcHomJ_eJiFAcmdSe1VSjgneWR9xg2EHz-M,22622
227
+ prompt_toolkit/output/windows10.py,sha256=yf0i1xAs-mbqOCwq25K78hkJjju1jXZ5b0e-w9aSBBA,4362
228
+ prompt_toolkit/patch_stdout.py,sha256=8gEaQdqykdBczlvp3FrOjDlEG02yeXoYKrDAGqj48Wg,9477
229
+ prompt_toolkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
230
+ prompt_toolkit/renderer.py,sha256=h4r7bShanQyvh9nrSQfxvHZUPT6ZPUH1kD5Nbeu2RwY,29398
231
+ prompt_toolkit/search.py,sha256=6Go_LtBeBlIMkdUCqb-WFCBKLchd70kgtccqP5dyv08,6951
232
+ prompt_toolkit/selection.py,sha256=P6zQOahBqqt1YZmfQ2-V9iJjOo4cxl0bdmU_-0jezJI,1274
233
+ prompt_toolkit/shortcuts/__init__.py,sha256=AOdDyiuu4t2itpHhFcBdvY-Tgzzi1HwQNnu2la3yaXw,949
234
+ prompt_toolkit/shortcuts/__pycache__/__init__.cpython-312.pyc,,
235
+ prompt_toolkit/shortcuts/__pycache__/utils.cpython-312.pyc,,
236
+ prompt_toolkit/shortcuts/dialogs.py,sha256=gFibLlbaii8ijuurk9TpbNi5fMTHu99T6m1wfFilbE8,9007
237
+ prompt_toolkit/shortcuts/progress_bar/__init__.py,sha256=QeAssmFBDPCC5VRoObAp4UkebwETP3qS7-na4acstWM,540
238
+ prompt_toolkit/shortcuts/progress_bar/__pycache__/__init__.cpython-312.pyc,,
239
+ prompt_toolkit/shortcuts/progress_bar/__pycache__/base.cpython-312.pyc,,
240
+ prompt_toolkit/shortcuts/progress_bar/__pycache__/formatters.cpython-312.pyc,,
241
+ prompt_toolkit/shortcuts/progress_bar/base.py,sha256=_cqp7coZMFDc7ZoAUL1iz3fL1Dt5hw3hi1HEfBvUpK8,14402
242
+ prompt_toolkit/shortcuts/progress_bar/formatters.py,sha256=VfRADwUm8op-DzoM51UrKI8pSa1T1LAz5q9VMUW2siI,11739
243
+ prompt_toolkit/shortcuts/prompt.py,sha256=IbbpTaV71ER8c920U5cf9CTkGHudfHtWnWuu4A_TjPE,60235
244
+ prompt_toolkit/shortcuts/utils.py,sha256=NNjBY0Brkcb13Gxhh7Yc72_YpDFsQbkIlm7ZXvW3rK0,6950
245
+ prompt_toolkit/styles/__init__.py,sha256=7N1NNE1gTQo5mjT9f7mRwRodkrBoNpT9pmqWK-lrSeY,1640
246
+ prompt_toolkit/styles/__pycache__/__init__.cpython-312.pyc,,
247
+ prompt_toolkit/styles/__pycache__/base.cpython-312.pyc,,
248
+ prompt_toolkit/styles/__pycache__/defaults.cpython-312.pyc,,
249
+ prompt_toolkit/styles/__pycache__/named_colors.cpython-312.pyc,,
250
+ prompt_toolkit/styles/__pycache__/pygments.cpython-312.pyc,,
251
+ prompt_toolkit/styles/__pycache__/style.cpython-312.pyc,,
252
+ prompt_toolkit/styles/__pycache__/style_transformation.cpython-312.pyc,,
253
+ prompt_toolkit/styles/base.py,sha256=9oTmvqg0Rxy9VEVbRxq_4_P_NnPWVr9QedK56kea2Ro,5014
254
+ prompt_toolkit/styles/defaults.py,sha256=TRnP1PeuauYa_Ru1PpJ_ImsfaldvLE1JjmPV8tvfJjs,8699
255
+ prompt_toolkit/styles/named_colors.py,sha256=yZ30oKB-fCRk6RMASYg8q3Uz2zgdfy_YNbuQWYpyYas,4367
256
+ prompt_toolkit/styles/pygments.py,sha256=yWJEcvYCFo1e2EN9IF5HWpxHQ104J0HOJg1LUsSA9oM,1974
257
+ prompt_toolkit/styles/style.py,sha256=ve7MBciSq6cBOXhboC_RLrlrEqQlq5kWn0XgFI6wNVU,13043
258
+ prompt_toolkit/styles/style_transformation.py,sha256=cGaOo-jqhP79QoEHLQxrOZo9QMrxWxtXgfXKsHlx1Jg,12427
259
+ prompt_toolkit/token.py,sha256=do3EnxLrCDVbq47MzJ2vqSYps-CjVKWNCWzCZgdf5Jo,121
260
+ prompt_toolkit/utils.py,sha256=7O8hILpI2VZb0KoC7J-5z1S2aXICf_kwtmRq5xdfDTg,8631
261
+ prompt_toolkit/validation.py,sha256=XTdmExMgaqj-Whym9yYyQxOAaKce97KYyyGXwCxMr-A,5807
262
+ prompt_toolkit/widgets/__init__.py,sha256=RZXj6UzZWFuxOQXc1TwHLIwwZYJU-YBAaV4oLrC2dCA,1218
263
+ prompt_toolkit/widgets/__pycache__/base.cpython-312.pyc,,
264
+ prompt_toolkit/widgets/__pycache__/dialogs.cpython-312.pyc,,
265
+ prompt_toolkit/widgets/__pycache__/menus.cpython-312.pyc,,
266
+ prompt_toolkit/widgets/__pycache__/toolbars.cpython-312.pyc,,
267
+ prompt_toolkit/widgets/base.py,sha256=zdfJ9-kMGRpocEi-GqDIOgt9prxRkN8GB8D1uT0409U,32351
268
+ prompt_toolkit/widgets/dialogs.py,sha256=K2ACcf0rKXwpBQGQcjSTq2aNeSInGmklzZRPnhdtZTc,3380
269
+ prompt_toolkit/widgets/menus.py,sha256=SeX-llaTpF1pVak2lw37mAP0SFDONIRZT5oq23mARg8,13419
270
+ prompt_toolkit/widgets/toolbars.py,sha256=MoxOxaa8Yi3nJvH4G8OCwlNuwx3XWUJ07J0a7D17_w0,12178
271
+ prompt_toolkit/win32_types.py,sha256=3xVjabRA3Q-RN2x3DLqTOrstuYj4_uCq6w2i8t6LZ6E,5551
.venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/WHEEL ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
.venv/Lib/site-packages/prompt_toolkit-3.0.51.dist-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ prompt_toolkit
.venv/Lib/site-packages/prompt_toolkit/__init__.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ prompt_toolkit
3
+ ==============
4
+
5
+ Author: Jonathan Slenders
6
+
7
+ Description: prompt_toolkit is a Library for building powerful interactive
8
+ command lines in Python. It can be a replacement for GNU
9
+ Readline, but it can be much more than that.
10
+
11
+ See the examples directory to learn about the usage.
12
+
13
+ Probably, to get started, you might also want to have a look at
14
+ `prompt_toolkit.shortcuts.prompt`.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import re
20
+ from importlib import metadata
21
+
22
+ # note: this is a bit more lax than the actual pep 440 to allow for a/b/rc/dev without a number
23
+ pep440 = re.compile(
24
+ r"^([1-9]\d*!)?(0|[1-9]\d*)(\.(0|[1-9]\d*))*((a|b|rc)(0|[1-9]\d*)?)?(\.post(0|[1-9]\d*))?(\.dev(0|[1-9]\d*)?)?$",
25
+ re.UNICODE,
26
+ )
27
+ from .application import Application
28
+ from .formatted_text import ANSI, HTML
29
+ from .shortcuts import PromptSession, print_formatted_text, prompt
30
+
31
+ # Don't forget to update in `docs/conf.py`!
32
+ __version__ = metadata.version("prompt_toolkit")
33
+
34
+ assert pep440.match(__version__)
35
+
36
+ # Version tuple.
37
+ VERSION = tuple(int(v.rstrip("abrc")) for v in __version__.split(".")[:3])
38
+
39
+
40
+ __all__ = [
41
+ # Application.
42
+ "Application",
43
+ # Shortcuts.
44
+ "prompt",
45
+ "PromptSession",
46
+ "print_formatted_text",
47
+ # Formatted text.
48
+ "HTML",
49
+ "ANSI",
50
+ # Version info.
51
+ "__version__",
52
+ "VERSION",
53
+ ]
.venv/Lib/site-packages/prompt_toolkit/document.py ADDED
@@ -0,0 +1,1182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ The `Document` that implements all the text operations/querying.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import bisect
8
+ import re
9
+ import string
10
+ import weakref
11
+ from typing import Callable, Dict, Iterable, List, NoReturn, Pattern, cast
12
+
13
+ from .clipboard import ClipboardData
14
+ from .filters import vi_mode
15
+ from .selection import PasteMode, SelectionState, SelectionType
16
+
17
+ __all__ = [
18
+ "Document",
19
+ ]
20
+
21
+
22
+ # Regex for finding "words" in documents. (We consider a group of alnum
23
+ # characters a word, but also a group of special characters a word, as long as
24
+ # it doesn't contain a space.)
25
+ # (This is a 'word' in Vi.)
26
+ _FIND_WORD_RE = re.compile(r"([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)")
27
+ _FIND_CURRENT_WORD_RE = re.compile(r"^([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)")
28
+ _FIND_CURRENT_WORD_INCLUDE_TRAILING_WHITESPACE_RE = re.compile(
29
+ r"^(([a-zA-Z0-9_]+|[^a-zA-Z0-9_\s]+)\s*)"
30
+ )
31
+
32
+ # Regex for finding "WORDS" in documents.
33
+ # (This is a 'WORD in Vi.)
34
+ _FIND_BIG_WORD_RE = re.compile(r"([^\s]+)")
35
+ _FIND_CURRENT_BIG_WORD_RE = re.compile(r"^([^\s]+)")
36
+ _FIND_CURRENT_BIG_WORD_INCLUDE_TRAILING_WHITESPACE_RE = re.compile(r"^([^\s]+\s*)")
37
+
38
+ # Share the Document._cache between all Document instances.
39
+ # (Document instances are considered immutable. That means that if another
40
+ # `Document` is constructed with the same text, it should have the same
41
+ # `_DocumentCache`.)
42
+ _text_to_document_cache: dict[str, _DocumentCache] = cast(
43
+ Dict[str, "_DocumentCache"],
44
+ weakref.WeakValueDictionary(), # Maps document.text to DocumentCache instance.
45
+ )
46
+
47
+
48
+ class _ImmutableLineList(List[str]):
49
+ """
50
+ Some protection for our 'lines' list, which is assumed to be immutable in the cache.
51
+ (Useful for detecting obvious bugs.)
52
+ """
53
+
54
+ def _error(self, *a: object, **kw: object) -> NoReturn:
55
+ raise NotImplementedError("Attempt to modify an immutable list.")
56
+
57
+ __setitem__ = _error # type: ignore
58
+ append = _error
59
+ clear = _error
60
+ extend = _error
61
+ insert = _error
62
+ pop = _error
63
+ remove = _error
64
+ reverse = _error
65
+ sort = _error # type: ignore
66
+
67
+
68
+ class _DocumentCache:
69
+ def __init__(self) -> None:
70
+ #: List of lines for the Document text.
71
+ self.lines: _ImmutableLineList | None = None
72
+
73
+ #: List of index positions, pointing to the start of all the lines.
74
+ self.line_indexes: list[int] | None = None
75
+
76
+
77
+ class Document:
78
+ """
79
+ This is a immutable class around the text and cursor position, and contains
80
+ methods for querying this data, e.g. to give the text before the cursor.
81
+
82
+ This class is usually instantiated by a :class:`~prompt_toolkit.buffer.Buffer`
83
+ object, and accessed as the `document` property of that class.
84
+
85
+ :param text: string
86
+ :param cursor_position: int
87
+ :param selection: :class:`.SelectionState`
88
+ """
89
+
90
+ __slots__ = ("_text", "_cursor_position", "_selection", "_cache")
91
+
92
+ def __init__(
93
+ self,
94
+ text: str = "",
95
+ cursor_position: int | None = None,
96
+ selection: SelectionState | None = None,
97
+ ) -> None:
98
+ # Check cursor position. It can also be right after the end. (Where we
99
+ # insert text.)
100
+ assert cursor_position is None or cursor_position <= len(text), AssertionError(
101
+ f"cursor_position={cursor_position!r}, len_text={len(text)!r}"
102
+ )
103
+
104
+ # By default, if no cursor position was given, make sure to put the
105
+ # cursor position is at the end of the document. This is what makes
106
+ # sense in most places.
107
+ if cursor_position is None:
108
+ cursor_position = len(text)
109
+
110
+ # Keep these attributes private. A `Document` really has to be
111
+ # considered to be immutable, because otherwise the caching will break
112
+ # things. Because of that, we wrap these into read-only properties.
113
+ self._text = text
114
+ self._cursor_position = cursor_position
115
+ self._selection = selection
116
+
117
+ # Cache for lines/indexes. (Shared with other Document instances that
118
+ # contain the same text.
119
+ try:
120
+ self._cache = _text_to_document_cache[self.text]
121
+ except KeyError:
122
+ self._cache = _DocumentCache()
123
+ _text_to_document_cache[self.text] = self._cache
124
+
125
+ # XX: For some reason, above, we can't use 'WeakValueDictionary.setdefault'.
126
+ # This fails in Pypy3. `self._cache` becomes None, because that's what
127
+ # 'setdefault' returns.
128
+ # self._cache = _text_to_document_cache.setdefault(self.text, _DocumentCache())
129
+ # assert self._cache
130
+
131
+ def __repr__(self) -> str:
132
+ return f"{self.__class__.__name__}({self.text!r}, {self.cursor_position!r})"
133
+
134
+ def __eq__(self, other: object) -> bool:
135
+ if not isinstance(other, Document):
136
+ return False
137
+
138
+ return (
139
+ self.text == other.text
140
+ and self.cursor_position == other.cursor_position
141
+ and self.selection == other.selection
142
+ )
143
+
144
+ @property
145
+ def text(self) -> str:
146
+ "The document text."
147
+ return self._text
148
+
149
+ @property
150
+ def cursor_position(self) -> int:
151
+ "The document cursor position."
152
+ return self._cursor_position
153
+
154
+ @property
155
+ def selection(self) -> SelectionState | None:
156
+ ":class:`.SelectionState` object."
157
+ return self._selection
158
+
159
+ @property
160
+ def current_char(self) -> str:
161
+ """Return character under cursor or an empty string."""
162
+ return self._get_char_relative_to_cursor(0) or ""
163
+
164
+ @property
165
+ def char_before_cursor(self) -> str:
166
+ """Return character before the cursor or an empty string."""
167
+ return self._get_char_relative_to_cursor(-1) or ""
168
+
169
+ @property
170
+ def text_before_cursor(self) -> str:
171
+ return self.text[: self.cursor_position :]
172
+
173
+ @property
174
+ def text_after_cursor(self) -> str:
175
+ return self.text[self.cursor_position :]
176
+
177
+ @property
178
+ def current_line_before_cursor(self) -> str:
179
+ """Text from the start of the line until the cursor."""
180
+ _, _, text = self.text_before_cursor.rpartition("\n")
181
+ return text
182
+
183
+ @property
184
+ def current_line_after_cursor(self) -> str:
185
+ """Text from the cursor until the end of the line."""
186
+ text, _, _ = self.text_after_cursor.partition("\n")
187
+ return text
188
+
189
+ @property
190
+ def lines(self) -> list[str]:
191
+ """
192
+ Array of all the lines.
193
+ """
194
+ # Cache, because this one is reused very often.
195
+ if self._cache.lines is None:
196
+ self._cache.lines = _ImmutableLineList(self.text.split("\n"))
197
+
198
+ return self._cache.lines
199
+
200
+ @property
201
+ def _line_start_indexes(self) -> list[int]:
202
+ """
203
+ Array pointing to the start indexes of all the lines.
204
+ """
205
+ # Cache, because this is often reused. (If it is used, it's often used
206
+ # many times. And this has to be fast for editing big documents!)
207
+ if self._cache.line_indexes is None:
208
+ # Create list of line lengths.
209
+ line_lengths = map(len, self.lines)
210
+
211
+ # Calculate cumulative sums.
212
+ indexes = [0]
213
+ append = indexes.append
214
+ pos = 0
215
+
216
+ for line_length in line_lengths:
217
+ pos += line_length + 1
218
+ append(pos)
219
+
220
+ # Remove the last item. (This is not a new line.)
221
+ if len(indexes) > 1:
222
+ indexes.pop()
223
+
224
+ self._cache.line_indexes = indexes
225
+
226
+ return self._cache.line_indexes
227
+
228
+ @property
229
+ def lines_from_current(self) -> list[str]:
230
+ """
231
+ Array of the lines starting from the current line, until the last line.
232
+ """
233
+ return self.lines[self.cursor_position_row :]
234
+
235
+ @property
236
+ def line_count(self) -> int:
237
+ r"""Return the number of lines in this document. If the document ends
238
+ with a trailing \n, that counts as the beginning of a new line."""
239
+ return len(self.lines)
240
+
241
+ @property
242
+ def current_line(self) -> str:
243
+ """Return the text on the line where the cursor is. (when the input
244
+ consists of just one line, it equals `text`."""
245
+ return self.current_line_before_cursor + self.current_line_after_cursor
246
+
247
+ @property
248
+ def leading_whitespace_in_current_line(self) -> str:
249
+ """The leading whitespace in the left margin of the current line."""
250
+ current_line = self.current_line
251
+ length = len(current_line) - len(current_line.lstrip())
252
+ return current_line[:length]
253
+
254
+ def _get_char_relative_to_cursor(self, offset: int = 0) -> str:
255
+ """
256
+ Return character relative to cursor position, or empty string
257
+ """
258
+ try:
259
+ return self.text[self.cursor_position + offset]
260
+ except IndexError:
261
+ return ""
262
+
263
+ @property
264
+ def on_first_line(self) -> bool:
265
+ """
266
+ True when we are at the first line.
267
+ """
268
+ return self.cursor_position_row == 0
269
+
270
+ @property
271
+ def on_last_line(self) -> bool:
272
+ """
273
+ True when we are at the last line.
274
+ """
275
+ return self.cursor_position_row == self.line_count - 1
276
+
277
+ @property
278
+ def cursor_position_row(self) -> int:
279
+ """
280
+ Current row. (0-based.)
281
+ """
282
+ row, _ = self._find_line_start_index(self.cursor_position)
283
+ return row
284
+
285
+ @property
286
+ def cursor_position_col(self) -> int:
287
+ """
288
+ Current column. (0-based.)
289
+ """
290
+ # (Don't use self.text_before_cursor to calculate this. Creating
291
+ # substrings and doing rsplit is too expensive for getting the cursor
292
+ # position.)
293
+ _, line_start_index = self._find_line_start_index(self.cursor_position)
294
+ return self.cursor_position - line_start_index
295
+
296
+ def _find_line_start_index(self, index: int) -> tuple[int, int]:
297
+ """
298
+ For the index of a character at a certain line, calculate the index of
299
+ the first character on that line.
300
+
301
+ Return (row, index) tuple.
302
+ """
303
+ indexes = self._line_start_indexes
304
+
305
+ pos = bisect.bisect_right(indexes, index) - 1
306
+ return pos, indexes[pos]
307
+
308
+ def translate_index_to_position(self, index: int) -> tuple[int, int]:
309
+ """
310
+ Given an index for the text, return the corresponding (row, col) tuple.
311
+ (0-based. Returns (0, 0) for index=0.)
312
+ """
313
+ # Find start of this line.
314
+ row, row_index = self._find_line_start_index(index)
315
+ col = index - row_index
316
+
317
+ return row, col
318
+
319
+ def translate_row_col_to_index(self, row: int, col: int) -> int:
320
+ """
321
+ Given a (row, col) tuple, return the corresponding index.
322
+ (Row and col params are 0-based.)
323
+
324
+ Negative row/col values are turned into zero.
325
+ """
326
+ try:
327
+ result = self._line_start_indexes[row]
328
+ line = self.lines[row]
329
+ except IndexError:
330
+ if row < 0:
331
+ result = self._line_start_indexes[0]
332
+ line = self.lines[0]
333
+ else:
334
+ result = self._line_start_indexes[-1]
335
+ line = self.lines[-1]
336
+
337
+ result += max(0, min(col, len(line)))
338
+
339
+ # Keep in range. (len(self.text) is included, because the cursor can be
340
+ # right after the end of the text as well.)
341
+ result = max(0, min(result, len(self.text)))
342
+ return result
343
+
344
+ @property
345
+ def is_cursor_at_the_end(self) -> bool:
346
+ """True when the cursor is at the end of the text."""
347
+ return self.cursor_position == len(self.text)
348
+
349
+ @property
350
+ def is_cursor_at_the_end_of_line(self) -> bool:
351
+ """True when the cursor is at the end of this line."""
352
+ return self.current_char in ("\n", "")
353
+
354
+ def has_match_at_current_position(self, sub: str) -> bool:
355
+ """
356
+ `True` when this substring is found at the cursor position.
357
+ """
358
+ return self.text.find(sub, self.cursor_position) == self.cursor_position
359
+
360
+ def find(
361
+ self,
362
+ sub: str,
363
+ in_current_line: bool = False,
364
+ include_current_position: bool = False,
365
+ ignore_case: bool = False,
366
+ count: int = 1,
367
+ ) -> int | None:
368
+ """
369
+ Find `text` after the cursor, return position relative to the cursor
370
+ position. Return `None` if nothing was found.
371
+
372
+ :param count: Find the n-th occurrence.
373
+ """
374
+ assert isinstance(ignore_case, bool)
375
+
376
+ if in_current_line:
377
+ text = self.current_line_after_cursor
378
+ else:
379
+ text = self.text_after_cursor
380
+
381
+ if not include_current_position:
382
+ if len(text) == 0:
383
+ return None # (Otherwise, we always get a match for the empty string.)
384
+ else:
385
+ text = text[1:]
386
+
387
+ flags = re.IGNORECASE if ignore_case else 0
388
+ iterator = re.finditer(re.escape(sub), text, flags)
389
+
390
+ try:
391
+ for i, match in enumerate(iterator):
392
+ if i + 1 == count:
393
+ if include_current_position:
394
+ return match.start(0)
395
+ else:
396
+ return match.start(0) + 1
397
+ except StopIteration:
398
+ pass
399
+ return None
400
+
401
+ def find_all(self, sub: str, ignore_case: bool = False) -> list[int]:
402
+ """
403
+ Find all occurrences of the substring. Return a list of absolute
404
+ positions in the document.
405
+ """
406
+ flags = re.IGNORECASE if ignore_case else 0
407
+ return [a.start() for a in re.finditer(re.escape(sub), self.text, flags)]
408
+
409
+ def find_backwards(
410
+ self,
411
+ sub: str,
412
+ in_current_line: bool = False,
413
+ ignore_case: bool = False,
414
+ count: int = 1,
415
+ ) -> int | None:
416
+ """
417
+ Find `text` before the cursor, return position relative to the cursor
418
+ position. Return `None` if nothing was found.
419
+
420
+ :param count: Find the n-th occurrence.
421
+ """
422
+ if in_current_line:
423
+ before_cursor = self.current_line_before_cursor[::-1]
424
+ else:
425
+ before_cursor = self.text_before_cursor[::-1]
426
+
427
+ flags = re.IGNORECASE if ignore_case else 0
428
+ iterator = re.finditer(re.escape(sub[::-1]), before_cursor, flags)
429
+
430
+ try:
431
+ for i, match in enumerate(iterator):
432
+ if i + 1 == count:
433
+ return -match.start(0) - len(sub)
434
+ except StopIteration:
435
+ pass
436
+ return None
437
+
438
+ def get_word_before_cursor(
439
+ self, WORD: bool = False, pattern: Pattern[str] | None = None
440
+ ) -> str:
441
+ """
442
+ Give the word before the cursor.
443
+ If we have whitespace before the cursor this returns an empty string.
444
+
445
+ :param pattern: (None or compiled regex). When given, use this regex
446
+ pattern.
447
+ """
448
+ if self._is_word_before_cursor_complete(WORD=WORD, pattern=pattern):
449
+ # Space before the cursor or no text before cursor.
450
+ return ""
451
+
452
+ text_before_cursor = self.text_before_cursor
453
+ start = self.find_start_of_previous_word(WORD=WORD, pattern=pattern) or 0
454
+
455
+ return text_before_cursor[len(text_before_cursor) + start :]
456
+
457
+ def _is_word_before_cursor_complete(
458
+ self, WORD: bool = False, pattern: Pattern[str] | None = None
459
+ ) -> bool:
460
+ if pattern:
461
+ return self.find_start_of_previous_word(WORD=WORD, pattern=pattern) is None
462
+ else:
463
+ return (
464
+ self.text_before_cursor == "" or self.text_before_cursor[-1:].isspace()
465
+ )
466
+
467
+ def find_start_of_previous_word(
468
+ self, count: int = 1, WORD: bool = False, pattern: Pattern[str] | None = None
469
+ ) -> int | None:
470
+ """
471
+ Return an index relative to the cursor position pointing to the start
472
+ of the previous word. Return `None` if nothing was found.
473
+
474
+ :param pattern: (None or compiled regex). When given, use this regex
475
+ pattern.
476
+ """
477
+ assert not (WORD and pattern)
478
+
479
+ # Reverse the text before the cursor, in order to do an efficient
480
+ # backwards search.
481
+ text_before_cursor = self.text_before_cursor[::-1]
482
+
483
+ if pattern:
484
+ regex = pattern
485
+ elif WORD:
486
+ regex = _FIND_BIG_WORD_RE
487
+ else:
488
+ regex = _FIND_WORD_RE
489
+
490
+ iterator = regex.finditer(text_before_cursor)
491
+
492
+ try:
493
+ for i, match in enumerate(iterator):
494
+ if i + 1 == count:
495
+ return -match.end(0)
496
+ except StopIteration:
497
+ pass
498
+ return None
499
+
500
+ def find_boundaries_of_current_word(
501
+ self,
502
+ WORD: bool = False,
503
+ include_leading_whitespace: bool = False,
504
+ include_trailing_whitespace: bool = False,
505
+ ) -> tuple[int, int]:
506
+ """
507
+ Return the relative boundaries (startpos, endpos) of the current word under the
508
+ cursor. (This is at the current line, because line boundaries obviously
509
+ don't belong to any word.)
510
+ If not on a word, this returns (0,0)
511
+ """
512
+ text_before_cursor = self.current_line_before_cursor[::-1]
513
+ text_after_cursor = self.current_line_after_cursor
514
+
515
+ def get_regex(include_whitespace: bool) -> Pattern[str]:
516
+ return {
517
+ (False, False): _FIND_CURRENT_WORD_RE,
518
+ (False, True): _FIND_CURRENT_WORD_INCLUDE_TRAILING_WHITESPACE_RE,
519
+ (True, False): _FIND_CURRENT_BIG_WORD_RE,
520
+ (True, True): _FIND_CURRENT_BIG_WORD_INCLUDE_TRAILING_WHITESPACE_RE,
521
+ }[(WORD, include_whitespace)]
522
+
523
+ match_before = get_regex(include_leading_whitespace).search(text_before_cursor)
524
+ match_after = get_regex(include_trailing_whitespace).search(text_after_cursor)
525
+
526
+ # When there is a match before and after, and we're not looking for
527
+ # WORDs, make sure that both the part before and after the cursor are
528
+ # either in the [a-zA-Z_] alphabet or not. Otherwise, drop the part
529
+ # before the cursor.
530
+ if not WORD and match_before and match_after:
531
+ c1 = self.text[self.cursor_position - 1]
532
+ c2 = self.text[self.cursor_position]
533
+ alphabet = string.ascii_letters + "0123456789_"
534
+
535
+ if (c1 in alphabet) != (c2 in alphabet):
536
+ match_before = None
537
+
538
+ return (
539
+ -match_before.end(1) if match_before else 0,
540
+ match_after.end(1) if match_after else 0,
541
+ )
542
+
543
+ def get_word_under_cursor(self, WORD: bool = False) -> str:
544
+ """
545
+ Return the word, currently below the cursor.
546
+ This returns an empty string when the cursor is on a whitespace region.
547
+ """
548
+ start, end = self.find_boundaries_of_current_word(WORD=WORD)
549
+ return self.text[self.cursor_position + start : self.cursor_position + end]
550
+
551
+ def find_next_word_beginning(
552
+ self, count: int = 1, WORD: bool = False
553
+ ) -> int | None:
554
+ """
555
+ Return an index relative to the cursor position pointing to the start
556
+ of the next word. Return `None` if nothing was found.
557
+ """
558
+ if count < 0:
559
+ return self.find_previous_word_beginning(count=-count, WORD=WORD)
560
+
561
+ regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
562
+ iterator = regex.finditer(self.text_after_cursor)
563
+
564
+ try:
565
+ for i, match in enumerate(iterator):
566
+ # Take first match, unless it's the word on which we're right now.
567
+ if i == 0 and match.start(1) == 0:
568
+ count += 1
569
+
570
+ if i + 1 == count:
571
+ return match.start(1)
572
+ except StopIteration:
573
+ pass
574
+ return None
575
+
576
+ def find_next_word_ending(
577
+ self, include_current_position: bool = False, count: int = 1, WORD: bool = False
578
+ ) -> int | None:
579
+ """
580
+ Return an index relative to the cursor position pointing to the end
581
+ of the next word. Return `None` if nothing was found.
582
+ """
583
+ if count < 0:
584
+ return self.find_previous_word_ending(count=-count, WORD=WORD)
585
+
586
+ if include_current_position:
587
+ text = self.text_after_cursor
588
+ else:
589
+ text = self.text_after_cursor[1:]
590
+
591
+ regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
592
+ iterable = regex.finditer(text)
593
+
594
+ try:
595
+ for i, match in enumerate(iterable):
596
+ if i + 1 == count:
597
+ value = match.end(1)
598
+
599
+ if include_current_position:
600
+ return value
601
+ else:
602
+ return value + 1
603
+
604
+ except StopIteration:
605
+ pass
606
+ return None
607
+
608
+ def find_previous_word_beginning(
609
+ self, count: int = 1, WORD: bool = False
610
+ ) -> int | None:
611
+ """
612
+ Return an index relative to the cursor position pointing to the start
613
+ of the previous word. Return `None` if nothing was found.
614
+ """
615
+ if count < 0:
616
+ return self.find_next_word_beginning(count=-count, WORD=WORD)
617
+
618
+ regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
619
+ iterator = regex.finditer(self.text_before_cursor[::-1])
620
+
621
+ try:
622
+ for i, match in enumerate(iterator):
623
+ if i + 1 == count:
624
+ return -match.end(1)
625
+ except StopIteration:
626
+ pass
627
+ return None
628
+
629
+ def find_previous_word_ending(
630
+ self, count: int = 1, WORD: bool = False
631
+ ) -> int | None:
632
+ """
633
+ Return an index relative to the cursor position pointing to the end
634
+ of the previous word. Return `None` if nothing was found.
635
+ """
636
+ if count < 0:
637
+ return self.find_next_word_ending(count=-count, WORD=WORD)
638
+
639
+ text_before_cursor = self.text_after_cursor[:1] + self.text_before_cursor[::-1]
640
+
641
+ regex = _FIND_BIG_WORD_RE if WORD else _FIND_WORD_RE
642
+ iterator = regex.finditer(text_before_cursor)
643
+
644
+ try:
645
+ for i, match in enumerate(iterator):
646
+ # Take first match, unless it's the word on which we're right now.
647
+ if i == 0 and match.start(1) == 0:
648
+ count += 1
649
+
650
+ if i + 1 == count:
651
+ return -match.start(1) + 1
652
+ except StopIteration:
653
+ pass
654
+ return None
655
+
656
+ def find_next_matching_line(
657
+ self, match_func: Callable[[str], bool], count: int = 1
658
+ ) -> int | None:
659
+ """
660
+ Look downwards for empty lines.
661
+ Return the line index, relative to the current line.
662
+ """
663
+ result = None
664
+
665
+ for index, line in enumerate(self.lines[self.cursor_position_row + 1 :]):
666
+ if match_func(line):
667
+ result = 1 + index
668
+ count -= 1
669
+
670
+ if count == 0:
671
+ break
672
+
673
+ return result
674
+
675
+ def find_previous_matching_line(
676
+ self, match_func: Callable[[str], bool], count: int = 1
677
+ ) -> int | None:
678
+ """
679
+ Look upwards for empty lines.
680
+ Return the line index, relative to the current line.
681
+ """
682
+ result = None
683
+
684
+ for index, line in enumerate(self.lines[: self.cursor_position_row][::-1]):
685
+ if match_func(line):
686
+ result = -1 - index
687
+ count -= 1
688
+
689
+ if count == 0:
690
+ break
691
+
692
+ return result
693
+
694
+ def get_cursor_left_position(self, count: int = 1) -> int:
695
+ """
696
+ Relative position for cursor left.
697
+ """
698
+ if count < 0:
699
+ return self.get_cursor_right_position(-count)
700
+
701
+ return -min(self.cursor_position_col, count)
702
+
703
+ def get_cursor_right_position(self, count: int = 1) -> int:
704
+ """
705
+ Relative position for cursor_right.
706
+ """
707
+ if count < 0:
708
+ return self.get_cursor_left_position(-count)
709
+
710
+ return min(count, len(self.current_line_after_cursor))
711
+
712
+ def get_cursor_up_position(
713
+ self, count: int = 1, preferred_column: int | None = None
714
+ ) -> int:
715
+ """
716
+ Return the relative cursor position (character index) where we would be if the
717
+ user pressed the arrow-up button.
718
+
719
+ :param preferred_column: When given, go to this column instead of
720
+ staying at the current column.
721
+ """
722
+ assert count >= 1
723
+ column = (
724
+ self.cursor_position_col if preferred_column is None else preferred_column
725
+ )
726
+
727
+ return (
728
+ self.translate_row_col_to_index(
729
+ max(0, self.cursor_position_row - count), column
730
+ )
731
+ - self.cursor_position
732
+ )
733
+
734
+ def get_cursor_down_position(
735
+ self, count: int = 1, preferred_column: int | None = None
736
+ ) -> int:
737
+ """
738
+ Return the relative cursor position (character index) where we would be if the
739
+ user pressed the arrow-down button.
740
+
741
+ :param preferred_column: When given, go to this column instead of
742
+ staying at the current column.
743
+ """
744
+ assert count >= 1
745
+ column = (
746
+ self.cursor_position_col if preferred_column is None else preferred_column
747
+ )
748
+
749
+ return (
750
+ self.translate_row_col_to_index(self.cursor_position_row + count, column)
751
+ - self.cursor_position
752
+ )
753
+
754
+ def find_enclosing_bracket_right(
755
+ self, left_ch: str, right_ch: str, end_pos: int | None = None
756
+ ) -> int | None:
757
+ """
758
+ Find the right bracket enclosing current position. Return the relative
759
+ position to the cursor position.
760
+
761
+ When `end_pos` is given, don't look past the position.
762
+ """
763
+ if self.current_char == right_ch:
764
+ return 0
765
+
766
+ if end_pos is None:
767
+ end_pos = len(self.text)
768
+ else:
769
+ end_pos = min(len(self.text), end_pos)
770
+
771
+ stack = 1
772
+
773
+ # Look forward.
774
+ for i in range(self.cursor_position + 1, end_pos):
775
+ c = self.text[i]
776
+
777
+ if c == left_ch:
778
+ stack += 1
779
+ elif c == right_ch:
780
+ stack -= 1
781
+
782
+ if stack == 0:
783
+ return i - self.cursor_position
784
+
785
+ return None
786
+
787
+ def find_enclosing_bracket_left(
788
+ self, left_ch: str, right_ch: str, start_pos: int | None = None
789
+ ) -> int | None:
790
+ """
791
+ Find the left bracket enclosing current position. Return the relative
792
+ position to the cursor position.
793
+
794
+ When `start_pos` is given, don't look past the position.
795
+ """
796
+ if self.current_char == left_ch:
797
+ return 0
798
+
799
+ if start_pos is None:
800
+ start_pos = 0
801
+ else:
802
+ start_pos = max(0, start_pos)
803
+
804
+ stack = 1
805
+
806
+ # Look backward.
807
+ for i in range(self.cursor_position - 1, start_pos - 1, -1):
808
+ c = self.text[i]
809
+
810
+ if c == right_ch:
811
+ stack += 1
812
+ elif c == left_ch:
813
+ stack -= 1
814
+
815
+ if stack == 0:
816
+ return i - self.cursor_position
817
+
818
+ return None
819
+
820
+ def find_matching_bracket_position(
821
+ self, start_pos: int | None = None, end_pos: int | None = None
822
+ ) -> int:
823
+ """
824
+ Return relative cursor position of matching [, (, { or < bracket.
825
+
826
+ When `start_pos` or `end_pos` are given. Don't look past the positions.
827
+ """
828
+
829
+ # Look for a match.
830
+ for pair in "()", "[]", "{}", "<>":
831
+ A = pair[0]
832
+ B = pair[1]
833
+ if self.current_char == A:
834
+ return self.find_enclosing_bracket_right(A, B, end_pos=end_pos) or 0
835
+ elif self.current_char == B:
836
+ return self.find_enclosing_bracket_left(A, B, start_pos=start_pos) or 0
837
+
838
+ return 0
839
+
840
+ def get_start_of_document_position(self) -> int:
841
+ """Relative position for the start of the document."""
842
+ return -self.cursor_position
843
+
844
+ def get_end_of_document_position(self) -> int:
845
+ """Relative position for the end of the document."""
846
+ return len(self.text) - self.cursor_position
847
+
848
+ def get_start_of_line_position(self, after_whitespace: bool = False) -> int:
849
+ """Relative position for the start of this line."""
850
+ if after_whitespace:
851
+ current_line = self.current_line
852
+ return (
853
+ len(current_line)
854
+ - len(current_line.lstrip())
855
+ - self.cursor_position_col
856
+ )
857
+ else:
858
+ return -len(self.current_line_before_cursor)
859
+
860
+ def get_end_of_line_position(self) -> int:
861
+ """Relative position for the end of this line."""
862
+ return len(self.current_line_after_cursor)
863
+
864
+ def last_non_blank_of_current_line_position(self) -> int:
865
+ """
866
+ Relative position for the last non blank character of this line.
867
+ """
868
+ return len(self.current_line.rstrip()) - self.cursor_position_col - 1
869
+
870
+ def get_column_cursor_position(self, column: int) -> int:
871
+ """
872
+ Return the relative cursor position for this column at the current
873
+ line. (It will stay between the boundaries of the line in case of a
874
+ larger number.)
875
+ """
876
+ line_length = len(self.current_line)
877
+ current_column = self.cursor_position_col
878
+ column = max(0, min(line_length, column))
879
+
880
+ return column - current_column
881
+
882
+ def selection_range(
883
+ self,
884
+ ) -> tuple[
885
+ int, int
886
+ ]: # XXX: shouldn't this return `None` if there is no selection???
887
+ """
888
+ Return (from, to) tuple of the selection.
889
+ start and end position are included.
890
+
891
+ This doesn't take the selection type into account. Use
892
+ `selection_ranges` instead.
893
+ """
894
+ if self.selection:
895
+ from_, to = sorted(
896
+ [self.cursor_position, self.selection.original_cursor_position]
897
+ )
898
+ else:
899
+ from_, to = self.cursor_position, self.cursor_position
900
+
901
+ return from_, to
902
+
903
+ def selection_ranges(self) -> Iterable[tuple[int, int]]:
904
+ """
905
+ Return a list of `(from, to)` tuples for the selection or none if
906
+ nothing was selected. The upper boundary is not included.
907
+
908
+ This will yield several (from, to) tuples in case of a BLOCK selection.
909
+ This will return zero ranges, like (8,8) for empty lines in a block
910
+ selection.
911
+ """
912
+ if self.selection:
913
+ from_, to = sorted(
914
+ [self.cursor_position, self.selection.original_cursor_position]
915
+ )
916
+
917
+ if self.selection.type == SelectionType.BLOCK:
918
+ from_line, from_column = self.translate_index_to_position(from_)
919
+ to_line, to_column = self.translate_index_to_position(to)
920
+ from_column, to_column = sorted([from_column, to_column])
921
+ lines = self.lines
922
+
923
+ if vi_mode():
924
+ to_column += 1
925
+
926
+ for l in range(from_line, to_line + 1):
927
+ line_length = len(lines[l])
928
+
929
+ if from_column <= line_length:
930
+ yield (
931
+ self.translate_row_col_to_index(l, from_column),
932
+ self.translate_row_col_to_index(
933
+ l, min(line_length, to_column)
934
+ ),
935
+ )
936
+ else:
937
+ # In case of a LINES selection, go to the start/end of the lines.
938
+ if self.selection.type == SelectionType.LINES:
939
+ from_ = max(0, self.text.rfind("\n", 0, from_) + 1)
940
+
941
+ if self.text.find("\n", to) >= 0:
942
+ to = self.text.find("\n", to)
943
+ else:
944
+ to = len(self.text) - 1
945
+
946
+ # In Vi mode, the upper boundary is always included. For Emacs,
947
+ # that's not the case.
948
+ if vi_mode():
949
+ to += 1
950
+
951
+ yield from_, to
952
+
953
+ def selection_range_at_line(self, row: int) -> tuple[int, int] | None:
954
+ """
955
+ If the selection spans a portion of the given line, return a (from, to) tuple.
956
+
957
+ The returned upper boundary is not included in the selection, so
958
+ `(0, 0)` is an empty selection. `(0, 1)`, is a one character selection.
959
+
960
+ Returns None if the selection doesn't cover this line at all.
961
+ """
962
+ if self.selection:
963
+ line = self.lines[row]
964
+
965
+ row_start = self.translate_row_col_to_index(row, 0)
966
+ row_end = self.translate_row_col_to_index(row, len(line))
967
+
968
+ from_, to = sorted(
969
+ [self.cursor_position, self.selection.original_cursor_position]
970
+ )
971
+
972
+ # Take the intersection of the current line and the selection.
973
+ intersection_start = max(row_start, from_)
974
+ intersection_end = min(row_end, to)
975
+
976
+ if intersection_start <= intersection_end:
977
+ if self.selection.type == SelectionType.LINES:
978
+ intersection_start = row_start
979
+ intersection_end = row_end
980
+
981
+ elif self.selection.type == SelectionType.BLOCK:
982
+ _, col1 = self.translate_index_to_position(from_)
983
+ _, col2 = self.translate_index_to_position(to)
984
+ col1, col2 = sorted([col1, col2])
985
+
986
+ if col1 > len(line):
987
+ return None # Block selection doesn't cross this line.
988
+
989
+ intersection_start = self.translate_row_col_to_index(row, col1)
990
+ intersection_end = self.translate_row_col_to_index(row, col2)
991
+
992
+ _, from_column = self.translate_index_to_position(intersection_start)
993
+ _, to_column = self.translate_index_to_position(intersection_end)
994
+
995
+ # In Vi mode, the upper boundary is always included. For Emacs
996
+ # mode, that's not the case.
997
+ if vi_mode():
998
+ to_column += 1
999
+
1000
+ return from_column, to_column
1001
+ return None
1002
+
1003
+ def cut_selection(self) -> tuple[Document, ClipboardData]:
1004
+ """
1005
+ Return a (:class:`.Document`, :class:`.ClipboardData`) tuple, where the
1006
+ document represents the new document when the selection is cut, and the
1007
+ clipboard data, represents whatever has to be put on the clipboard.
1008
+ """
1009
+ if self.selection:
1010
+ cut_parts = []
1011
+ remaining_parts = []
1012
+ new_cursor_position = self.cursor_position
1013
+
1014
+ last_to = 0
1015
+ for from_, to in self.selection_ranges():
1016
+ if last_to == 0:
1017
+ new_cursor_position = from_
1018
+
1019
+ remaining_parts.append(self.text[last_to:from_])
1020
+ cut_parts.append(self.text[from_:to])
1021
+ last_to = to
1022
+
1023
+ remaining_parts.append(self.text[last_to:])
1024
+
1025
+ cut_text = "\n".join(cut_parts)
1026
+ remaining_text = "".join(remaining_parts)
1027
+
1028
+ # In case of a LINES selection, don't include the trailing newline.
1029
+ if self.selection.type == SelectionType.LINES and cut_text.endswith("\n"):
1030
+ cut_text = cut_text[:-1]
1031
+
1032
+ return (
1033
+ Document(text=remaining_text, cursor_position=new_cursor_position),
1034
+ ClipboardData(cut_text, self.selection.type),
1035
+ )
1036
+ else:
1037
+ return self, ClipboardData("")
1038
+
1039
+ def paste_clipboard_data(
1040
+ self,
1041
+ data: ClipboardData,
1042
+ paste_mode: PasteMode = PasteMode.EMACS,
1043
+ count: int = 1,
1044
+ ) -> Document:
1045
+ """
1046
+ Return a new :class:`.Document` instance which contains the result if
1047
+ we would paste this data at the current cursor position.
1048
+
1049
+ :param paste_mode: Where to paste. (Before/after/emacs.)
1050
+ :param count: When >1, Paste multiple times.
1051
+ """
1052
+ before = paste_mode == PasteMode.VI_BEFORE
1053
+ after = paste_mode == PasteMode.VI_AFTER
1054
+
1055
+ if data.type == SelectionType.CHARACTERS:
1056
+ if after:
1057
+ new_text = (
1058
+ self.text[: self.cursor_position + 1]
1059
+ + data.text * count
1060
+ + self.text[self.cursor_position + 1 :]
1061
+ )
1062
+ else:
1063
+ new_text = (
1064
+ self.text_before_cursor + data.text * count + self.text_after_cursor
1065
+ )
1066
+
1067
+ new_cursor_position = self.cursor_position + len(data.text) * count
1068
+ if before:
1069
+ new_cursor_position -= 1
1070
+
1071
+ elif data.type == SelectionType.LINES:
1072
+ l = self.cursor_position_row
1073
+ if before:
1074
+ lines = self.lines[:l] + [data.text] * count + self.lines[l:]
1075
+ new_text = "\n".join(lines)
1076
+ new_cursor_position = len("".join(self.lines[:l])) + l
1077
+ else:
1078
+ lines = self.lines[: l + 1] + [data.text] * count + self.lines[l + 1 :]
1079
+ new_cursor_position = len("".join(self.lines[: l + 1])) + l + 1
1080
+ new_text = "\n".join(lines)
1081
+
1082
+ elif data.type == SelectionType.BLOCK:
1083
+ lines = self.lines[:]
1084
+ start_line = self.cursor_position_row
1085
+ start_column = self.cursor_position_col + (0 if before else 1)
1086
+
1087
+ for i, line in enumerate(data.text.split("\n")):
1088
+ index = i + start_line
1089
+ if index >= len(lines):
1090
+ lines.append("")
1091
+
1092
+ lines[index] = lines[index].ljust(start_column)
1093
+ lines[index] = (
1094
+ lines[index][:start_column]
1095
+ + line * count
1096
+ + lines[index][start_column:]
1097
+ )
1098
+
1099
+ new_text = "\n".join(lines)
1100
+ new_cursor_position = self.cursor_position + (0 if before else 1)
1101
+
1102
+ return Document(text=new_text, cursor_position=new_cursor_position)
1103
+
1104
+ def empty_line_count_at_the_end(self) -> int:
1105
+ """
1106
+ Return number of empty lines at the end of the document.
1107
+ """
1108
+ count = 0
1109
+ for line in self.lines[::-1]:
1110
+ if not line or line.isspace():
1111
+ count += 1
1112
+ else:
1113
+ break
1114
+
1115
+ return count
1116
+
1117
+ def start_of_paragraph(self, count: int = 1, before: bool = False) -> int:
1118
+ """
1119
+ Return the start of the current paragraph. (Relative cursor position.)
1120
+ """
1121
+
1122
+ def match_func(text: str) -> bool:
1123
+ return not text or text.isspace()
1124
+
1125
+ line_index = self.find_previous_matching_line(
1126
+ match_func=match_func, count=count
1127
+ )
1128
+
1129
+ if line_index:
1130
+ add = 0 if before else 1
1131
+ return min(0, self.get_cursor_up_position(count=-line_index) + add)
1132
+ else:
1133
+ return -self.cursor_position
1134
+
1135
+ def end_of_paragraph(self, count: int = 1, after: bool = False) -> int:
1136
+ """
1137
+ Return the end of the current paragraph. (Relative cursor position.)
1138
+ """
1139
+
1140
+ def match_func(text: str) -> bool:
1141
+ return not text or text.isspace()
1142
+
1143
+ line_index = self.find_next_matching_line(match_func=match_func, count=count)
1144
+
1145
+ if line_index:
1146
+ add = 0 if after else 1
1147
+ return max(0, self.get_cursor_down_position(count=line_index) - add)
1148
+ else:
1149
+ return len(self.text_after_cursor)
1150
+
1151
+ # Modifiers.
1152
+
1153
+ def insert_after(self, text: str) -> Document:
1154
+ """
1155
+ Create a new document, with this text inserted after the buffer.
1156
+ It keeps selection ranges and cursor position in sync.
1157
+ """
1158
+ return Document(
1159
+ text=self.text + text,
1160
+ cursor_position=self.cursor_position,
1161
+ selection=self.selection,
1162
+ )
1163
+
1164
+ def insert_before(self, text: str) -> Document:
1165
+ """
1166
+ Create a new document, with this text inserted before the buffer.
1167
+ It keeps selection ranges and cursor position in sync.
1168
+ """
1169
+ selection_state = self.selection
1170
+
1171
+ if selection_state:
1172
+ selection_state = SelectionState(
1173
+ original_cursor_position=selection_state.original_cursor_position
1174
+ + len(text),
1175
+ type=selection_state.type,
1176
+ )
1177
+
1178
+ return Document(
1179
+ text=text + self.text,
1180
+ cursor_position=self.cursor_position + len(text),
1181
+ selection=selection_state,
1182
+ )
.venv/Lib/site-packages/prompt_toolkit/enums.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class EditingMode(Enum):
7
+ # The set of key bindings that is active.
8
+ VI = "VI"
9
+ EMACS = "EMACS"
10
+
11
+
12
+ #: Name of the search buffer.
13
+ SEARCH_BUFFER = "SEARCH_BUFFER"
14
+
15
+ #: Name of the default buffer.
16
+ DEFAULT_BUFFER = "DEFAULT_BUFFER"
17
+
18
+ #: Name of the system buffer.
19
+ SYSTEM_BUFFER = "SYSTEM_BUFFER"
.venv/Lib/site-packages/prompt_toolkit/history.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Implementations for the history of a `Buffer`.
3
+
4
+ NOTE: There is no `DynamicHistory`:
5
+ This doesn't work well, because the `Buffer` needs to be able to attach
6
+ an event handler to the event when a history entry is loaded. This
7
+ loading can be done asynchronously and making the history swappable would
8
+ probably break this.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import datetime
14
+ import os
15
+ import threading
16
+ from abc import ABCMeta, abstractmethod
17
+ from asyncio import get_running_loop
18
+ from typing import AsyncGenerator, Iterable, Sequence, Union
19
+
20
+ __all__ = [
21
+ "History",
22
+ "ThreadedHistory",
23
+ "DummyHistory",
24
+ "FileHistory",
25
+ "InMemoryHistory",
26
+ ]
27
+
28
+
29
+ class History(metaclass=ABCMeta):
30
+ """
31
+ Base ``History`` class.
32
+
33
+ This also includes abstract methods for loading/storing history.
34
+ """
35
+
36
+ def __init__(self) -> None:
37
+ # In memory storage for strings.
38
+ self._loaded = False
39
+
40
+ # History that's loaded already, in reverse order. Latest, most recent
41
+ # item first.
42
+ self._loaded_strings: list[str] = []
43
+
44
+ #
45
+ # Methods expected by `Buffer`.
46
+ #
47
+
48
+ async def load(self) -> AsyncGenerator[str, None]:
49
+ """
50
+ Load the history and yield all the entries in reverse order (latest,
51
+ most recent history entry first).
52
+
53
+ This method can be called multiple times from the `Buffer` to
54
+ repopulate the history when prompting for a new input. So we are
55
+ responsible here for both caching, and making sure that strings that
56
+ were were appended to the history will be incorporated next time this
57
+ method is called.
58
+ """
59
+ if not self._loaded:
60
+ self._loaded_strings = list(self.load_history_strings())
61
+ self._loaded = True
62
+
63
+ for item in self._loaded_strings:
64
+ yield item
65
+
66
+ def get_strings(self) -> list[str]:
67
+ """
68
+ Get the strings from the history that are loaded so far.
69
+ (In order. Oldest item first.)
70
+ """
71
+ return self._loaded_strings[::-1]
72
+
73
+ def append_string(self, string: str) -> None:
74
+ "Add string to the history."
75
+ self._loaded_strings.insert(0, string)
76
+ self.store_string(string)
77
+
78
+ #
79
+ # Implementation for specific backends.
80
+ #
81
+
82
+ @abstractmethod
83
+ def load_history_strings(self) -> Iterable[str]:
84
+ """
85
+ This should be a generator that yields `str` instances.
86
+
87
+ It should yield the most recent items first, because they are the most
88
+ important. (The history can already be used, even when it's only
89
+ partially loaded.)
90
+ """
91
+ while False:
92
+ yield
93
+
94
+ @abstractmethod
95
+ def store_string(self, string: str) -> None:
96
+ """
97
+ Store the string in persistent storage.
98
+ """
99
+
100
+
101
+ class ThreadedHistory(History):
102
+ """
103
+ Wrapper around `History` implementations that run the `load()` generator in
104
+ a thread.
105
+
106
+ Use this to increase the start-up time of prompt_toolkit applications.
107
+ History entries are available as soon as they are loaded. We don't have to
108
+ wait for everything to be loaded.
109
+ """
110
+
111
+ def __init__(self, history: History) -> None:
112
+ super().__init__()
113
+
114
+ self.history = history
115
+
116
+ self._load_thread: threading.Thread | None = None
117
+
118
+ # Lock for accessing/manipulating `_loaded_strings` and `_loaded`
119
+ # together in a consistent state.
120
+ self._lock = threading.Lock()
121
+
122
+ # Events created by each `load()` call. Used to wait for new history
123
+ # entries from the loader thread.
124
+ self._string_load_events: list[threading.Event] = []
125
+
126
+ async def load(self) -> AsyncGenerator[str, None]:
127
+ """
128
+ Like `History.load(), but call `self.load_history_strings()` in a
129
+ background thread.
130
+ """
131
+ # Start the load thread, if this is called for the first time.
132
+ if not self._load_thread:
133
+ self._load_thread = threading.Thread(
134
+ target=self._in_load_thread,
135
+ daemon=True,
136
+ )
137
+ self._load_thread.start()
138
+
139
+ # Consume the `_loaded_strings` list, using asyncio.
140
+ loop = get_running_loop()
141
+
142
+ # Create threading Event so that we can wait for new items.
143
+ event = threading.Event()
144
+ event.set()
145
+ self._string_load_events.append(event)
146
+
147
+ items_yielded = 0
148
+
149
+ try:
150
+ while True:
151
+ # Wait for new items to be available.
152
+ # (Use a timeout, because the executor thread is not a daemon
153
+ # thread. The "slow-history.py" example would otherwise hang if
154
+ # Control-C is pressed before the history is fully loaded,
155
+ # because there's still this non-daemon executor thread waiting
156
+ # for this event.)
157
+ got_timeout = await loop.run_in_executor(
158
+ None, lambda: event.wait(timeout=0.5)
159
+ )
160
+ if not got_timeout:
161
+ continue
162
+
163
+ # Read new items (in lock).
164
+ def in_executor() -> tuple[list[str], bool]:
165
+ with self._lock:
166
+ new_items = self._loaded_strings[items_yielded:]
167
+ done = self._loaded
168
+ event.clear()
169
+ return new_items, done
170
+
171
+ new_items, done = await loop.run_in_executor(None, in_executor)
172
+
173
+ items_yielded += len(new_items)
174
+
175
+ for item in new_items:
176
+ yield item
177
+
178
+ if done:
179
+ break
180
+ finally:
181
+ self._string_load_events.remove(event)
182
+
183
+ def _in_load_thread(self) -> None:
184
+ try:
185
+ # Start with an empty list. In case `append_string()` was called
186
+ # before `load()` happened. Then `.store_string()` will have
187
+ # written these entries back to disk and we will reload it.
188
+ self._loaded_strings = []
189
+
190
+ for item in self.history.load_history_strings():
191
+ with self._lock:
192
+ self._loaded_strings.append(item)
193
+
194
+ for event in self._string_load_events:
195
+ event.set()
196
+ finally:
197
+ with self._lock:
198
+ self._loaded = True
199
+ for event in self._string_load_events:
200
+ event.set()
201
+
202
+ def append_string(self, string: str) -> None:
203
+ with self._lock:
204
+ self._loaded_strings.insert(0, string)
205
+ self.store_string(string)
206
+
207
+ # All of the following are proxied to `self.history`.
208
+
209
+ def load_history_strings(self) -> Iterable[str]:
210
+ return self.history.load_history_strings()
211
+
212
+ def store_string(self, string: str) -> None:
213
+ self.history.store_string(string)
214
+
215
+ def __repr__(self) -> str:
216
+ return f"ThreadedHistory({self.history!r})"
217
+
218
+
219
+ class InMemoryHistory(History):
220
+ """
221
+ :class:`.History` class that keeps a list of all strings in memory.
222
+
223
+ In order to prepopulate the history, it's possible to call either
224
+ `append_string` for all items or pass a list of strings to `__init__` here.
225
+ """
226
+
227
+ def __init__(self, history_strings: Sequence[str] | None = None) -> None:
228
+ super().__init__()
229
+ # Emulating disk storage.
230
+ if history_strings is None:
231
+ self._storage = []
232
+ else:
233
+ self._storage = list(history_strings)
234
+
235
+ def load_history_strings(self) -> Iterable[str]:
236
+ yield from self._storage[::-1]
237
+
238
+ def store_string(self, string: str) -> None:
239
+ self._storage.append(string)
240
+
241
+
242
+ class DummyHistory(History):
243
+ """
244
+ :class:`.History` object that doesn't remember anything.
245
+ """
246
+
247
+ def load_history_strings(self) -> Iterable[str]:
248
+ return []
249
+
250
+ def store_string(self, string: str) -> None:
251
+ pass
252
+
253
+ def append_string(self, string: str) -> None:
254
+ # Don't remember this.
255
+ pass
256
+
257
+
258
+ _StrOrBytesPath = Union[str, bytes, "os.PathLike[str]", "os.PathLike[bytes]"]
259
+
260
+
261
+ class FileHistory(History):
262
+ """
263
+ :class:`.History` class that stores all strings in a file.
264
+ """
265
+
266
+ def __init__(self, filename: _StrOrBytesPath) -> None:
267
+ self.filename = filename
268
+ super().__init__()
269
+
270
+ def load_history_strings(self) -> Iterable[str]:
271
+ strings: list[str] = []
272
+ lines: list[str] = []
273
+
274
+ def add() -> None:
275
+ if lines:
276
+ # Join and drop trailing newline.
277
+ string = "".join(lines)[:-1]
278
+
279
+ strings.append(string)
280
+
281
+ if os.path.exists(self.filename):
282
+ with open(self.filename, "rb") as f:
283
+ for line_bytes in f:
284
+ line = line_bytes.decode("utf-8", errors="replace")
285
+
286
+ if line.startswith("+"):
287
+ lines.append(line[1:])
288
+ else:
289
+ add()
290
+ lines = []
291
+
292
+ add()
293
+
294
+ # Reverse the order, because newest items have to go first.
295
+ return reversed(strings)
296
+
297
+ def store_string(self, string: str) -> None:
298
+ # Save to file.
299
+ with open(self.filename, "ab") as f:
300
+
301
+ def write(t: str) -> None:
302
+ f.write(t.encode("utf-8"))
303
+
304
+ write(f"\n# {datetime.datetime.now()}\n")
305
+ for line in string.split("\n"):
306
+ write(f"+{line}\n")
.venv/Lib/site-packages/prompt_toolkit/keys.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+
5
+ __all__ = [
6
+ "Keys",
7
+ "ALL_KEYS",
8
+ ]
9
+
10
+
11
+ class Keys(str, Enum):
12
+ """
13
+ List of keys for use in key bindings.
14
+
15
+ Note that this is an "StrEnum", all values can be compared against
16
+ strings.
17
+ """
18
+
19
+ value: str
20
+
21
+ Escape = "escape" # Also Control-[
22
+ ShiftEscape = "s-escape"
23
+
24
+ ControlAt = "c-@" # Also Control-Space.
25
+
26
+ ControlA = "c-a"
27
+ ControlB = "c-b"
28
+ ControlC = "c-c"
29
+ ControlD = "c-d"
30
+ ControlE = "c-e"
31
+ ControlF = "c-f"
32
+ ControlG = "c-g"
33
+ ControlH = "c-h"
34
+ ControlI = "c-i" # Tab
35
+ ControlJ = "c-j" # Newline
36
+ ControlK = "c-k"
37
+ ControlL = "c-l"
38
+ ControlM = "c-m" # Carriage return
39
+ ControlN = "c-n"
40
+ ControlO = "c-o"
41
+ ControlP = "c-p"
42
+ ControlQ = "c-q"
43
+ ControlR = "c-r"
44
+ ControlS = "c-s"
45
+ ControlT = "c-t"
46
+ ControlU = "c-u"
47
+ ControlV = "c-v"
48
+ ControlW = "c-w"
49
+ ControlX = "c-x"
50
+ ControlY = "c-y"
51
+ ControlZ = "c-z"
52
+
53
+ Control1 = "c-1"
54
+ Control2 = "c-2"
55
+ Control3 = "c-3"
56
+ Control4 = "c-4"
57
+ Control5 = "c-5"
58
+ Control6 = "c-6"
59
+ Control7 = "c-7"
60
+ Control8 = "c-8"
61
+ Control9 = "c-9"
62
+ Control0 = "c-0"
63
+
64
+ ControlShift1 = "c-s-1"
65
+ ControlShift2 = "c-s-2"
66
+ ControlShift3 = "c-s-3"
67
+ ControlShift4 = "c-s-4"
68
+ ControlShift5 = "c-s-5"
69
+ ControlShift6 = "c-s-6"
70
+ ControlShift7 = "c-s-7"
71
+ ControlShift8 = "c-s-8"
72
+ ControlShift9 = "c-s-9"
73
+ ControlShift0 = "c-s-0"
74
+
75
+ ControlBackslash = "c-\\"
76
+ ControlSquareClose = "c-]"
77
+ ControlCircumflex = "c-^"
78
+ ControlUnderscore = "c-_"
79
+
80
+ Left = "left"
81
+ Right = "right"
82
+ Up = "up"
83
+ Down = "down"
84
+ Home = "home"
85
+ End = "end"
86
+ Insert = "insert"
87
+ Delete = "delete"
88
+ PageUp = "pageup"
89
+ PageDown = "pagedown"
90
+
91
+ ControlLeft = "c-left"
92
+ ControlRight = "c-right"
93
+ ControlUp = "c-up"
94
+ ControlDown = "c-down"
95
+ ControlHome = "c-home"
96
+ ControlEnd = "c-end"
97
+ ControlInsert = "c-insert"
98
+ ControlDelete = "c-delete"
99
+ ControlPageUp = "c-pageup"
100
+ ControlPageDown = "c-pagedown"
101
+
102
+ ShiftLeft = "s-left"
103
+ ShiftRight = "s-right"
104
+ ShiftUp = "s-up"
105
+ ShiftDown = "s-down"
106
+ ShiftHome = "s-home"
107
+ ShiftEnd = "s-end"
108
+ ShiftInsert = "s-insert"
109
+ ShiftDelete = "s-delete"
110
+ ShiftPageUp = "s-pageup"
111
+ ShiftPageDown = "s-pagedown"
112
+
113
+ ControlShiftLeft = "c-s-left"
114
+ ControlShiftRight = "c-s-right"
115
+ ControlShiftUp = "c-s-up"
116
+ ControlShiftDown = "c-s-down"
117
+ ControlShiftHome = "c-s-home"
118
+ ControlShiftEnd = "c-s-end"
119
+ ControlShiftInsert = "c-s-insert"
120
+ ControlShiftDelete = "c-s-delete"
121
+ ControlShiftPageUp = "c-s-pageup"
122
+ ControlShiftPageDown = "c-s-pagedown"
123
+
124
+ BackTab = "s-tab" # shift + tab
125
+
126
+ F1 = "f1"
127
+ F2 = "f2"
128
+ F3 = "f3"
129
+ F4 = "f4"
130
+ F5 = "f5"
131
+ F6 = "f6"
132
+ F7 = "f7"
133
+ F8 = "f8"
134
+ F9 = "f9"
135
+ F10 = "f10"
136
+ F11 = "f11"
137
+ F12 = "f12"
138
+ F13 = "f13"
139
+ F14 = "f14"
140
+ F15 = "f15"
141
+ F16 = "f16"
142
+ F17 = "f17"
143
+ F18 = "f18"
144
+ F19 = "f19"
145
+ F20 = "f20"
146
+ F21 = "f21"
147
+ F22 = "f22"
148
+ F23 = "f23"
149
+ F24 = "f24"
150
+
151
+ ControlF1 = "c-f1"
152
+ ControlF2 = "c-f2"
153
+ ControlF3 = "c-f3"
154
+ ControlF4 = "c-f4"
155
+ ControlF5 = "c-f5"
156
+ ControlF6 = "c-f6"
157
+ ControlF7 = "c-f7"
158
+ ControlF8 = "c-f8"
159
+ ControlF9 = "c-f9"
160
+ ControlF10 = "c-f10"
161
+ ControlF11 = "c-f11"
162
+ ControlF12 = "c-f12"
163
+ ControlF13 = "c-f13"
164
+ ControlF14 = "c-f14"
165
+ ControlF15 = "c-f15"
166
+ ControlF16 = "c-f16"
167
+ ControlF17 = "c-f17"
168
+ ControlF18 = "c-f18"
169
+ ControlF19 = "c-f19"
170
+ ControlF20 = "c-f20"
171
+ ControlF21 = "c-f21"
172
+ ControlF22 = "c-f22"
173
+ ControlF23 = "c-f23"
174
+ ControlF24 = "c-f24"
175
+
176
+ # Matches any key.
177
+ Any = "<any>"
178
+
179
+ # Special.
180
+ ScrollUp = "<scroll-up>"
181
+ ScrollDown = "<scroll-down>"
182
+
183
+ CPRResponse = "<cursor-position-response>"
184
+ Vt100MouseEvent = "<vt100-mouse-event>"
185
+ WindowsMouseEvent = "<windows-mouse-event>"
186
+ BracketedPaste = "<bracketed-paste>"
187
+
188
+ SIGINT = "<sigint>"
189
+
190
+ # For internal use: key which is ignored.
191
+ # (The key binding for this key should not do anything.)
192
+ Ignore = "<ignore>"
193
+
194
+ # Some 'Key' aliases (for backwards-compatibility).
195
+ ControlSpace = ControlAt
196
+ Tab = ControlI
197
+ Enter = ControlM
198
+ Backspace = ControlH
199
+
200
+ # ShiftControl was renamed to ControlShift in
201
+ # 888fcb6fa4efea0de8333177e1bbc792f3ff3c24 (20 Feb 2020).
202
+ ShiftControlLeft = ControlShiftLeft
203
+ ShiftControlRight = ControlShiftRight
204
+ ShiftControlHome = ControlShiftHome
205
+ ShiftControlEnd = ControlShiftEnd
206
+
207
+
208
+ ALL_KEYS: list[str] = [k.value for k in Keys]
209
+
210
+
211
+ # Aliases.
212
+ KEY_ALIASES: dict[str, str] = {
213
+ "backspace": "c-h",
214
+ "c-space": "c-@",
215
+ "enter": "c-m",
216
+ "tab": "c-i",
217
+ # ShiftControl was renamed to ControlShift.
218
+ "s-c-left": "c-s-left",
219
+ "s-c-right": "c-s-right",
220
+ "s-c-home": "c-s-home",
221
+ "s-c-end": "c-s-end",
222
+ }
.venv/Lib/site-packages/prompt_toolkit/log.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Logging configuration.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+
9
+ __all__ = [
10
+ "logger",
11
+ ]
12
+
13
+ logger = logging.getLogger(__package__)
.venv/Lib/site-packages/prompt_toolkit/mouse_events.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Mouse events.
3
+
4
+
5
+ How it works
6
+ ------------
7
+
8
+ The renderer has a 2 dimensional grid of mouse event handlers.
9
+ (`prompt_toolkit.layout.MouseHandlers`.) When the layout is rendered, the
10
+ `Window` class will make sure that this grid will also be filled with
11
+ callbacks. For vt100 terminals, mouse events are received through stdin, just
12
+ like any other key press. There is a handler among the key bindings that
13
+ catches these events and forwards them to such a mouse event handler. It passes
14
+ through the `Window` class where the coordinates are translated from absolute
15
+ coordinates to coordinates relative to the user control, and there
16
+ `UIControl.mouse_handler` is called.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from enum import Enum
22
+
23
+ from .data_structures import Point
24
+
25
+ __all__ = ["MouseEventType", "MouseButton", "MouseModifier", "MouseEvent"]
26
+
27
+
28
+ class MouseEventType(Enum):
29
+ # Mouse up: This same event type is fired for all three events: left mouse
30
+ # up, right mouse up, or middle mouse up
31
+ MOUSE_UP = "MOUSE_UP"
32
+
33
+ # Mouse down: This implicitly refers to the left mouse down (this event is
34
+ # not fired upon pressing the middle or right mouse buttons).
35
+ MOUSE_DOWN = "MOUSE_DOWN"
36
+
37
+ SCROLL_UP = "SCROLL_UP"
38
+ SCROLL_DOWN = "SCROLL_DOWN"
39
+
40
+ # Triggered when the left mouse button is held down, and the mouse moves
41
+ MOUSE_MOVE = "MOUSE_MOVE"
42
+
43
+
44
+ class MouseButton(Enum):
45
+ LEFT = "LEFT"
46
+ MIDDLE = "MIDDLE"
47
+ RIGHT = "RIGHT"
48
+
49
+ # When we're scrolling, or just moving the mouse and not pressing a button.
50
+ NONE = "NONE"
51
+
52
+ # This is for when we don't know which mouse button was pressed, but we do
53
+ # know that one has been pressed during this mouse event (as opposed to
54
+ # scrolling, for example)
55
+ UNKNOWN = "UNKNOWN"
56
+
57
+
58
+ class MouseModifier(Enum):
59
+ SHIFT = "SHIFT"
60
+ ALT = "ALT"
61
+ CONTROL = "CONTROL"
62
+
63
+
64
+ class MouseEvent:
65
+ """
66
+ Mouse event, sent to `UIControl.mouse_handler`.
67
+
68
+ :param position: `Point` instance.
69
+ :param event_type: `MouseEventType`.
70
+ """
71
+
72
+ def __init__(
73
+ self,
74
+ position: Point,
75
+ event_type: MouseEventType,
76
+ button: MouseButton,
77
+ modifiers: frozenset[MouseModifier],
78
+ ) -> None:
79
+ self.position = position
80
+ self.event_type = event_type
81
+ self.button = button
82
+ self.modifiers = modifiers
83
+
84
+ def __repr__(self) -> str:
85
+ return f"MouseEvent({self.position!r},{self.event_type!r},{self.button!r},{self.modifiers!r})"
.venv/Lib/site-packages/prompt_toolkit/patch_stdout.py ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ patch_stdout
3
+ ============
4
+
5
+ This implements a context manager that ensures that print statements within
6
+ it won't destroy the user interface. The context manager will replace
7
+ `sys.stdout` by something that draws the output above the current prompt,
8
+ rather than overwriting the UI.
9
+
10
+ Usage::
11
+
12
+ with patch_stdout(application):
13
+ ...
14
+ application.run()
15
+ ...
16
+
17
+ Multiple applications can run in the body of the context manager, one after the
18
+ other.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import asyncio
24
+ import queue
25
+ import sys
26
+ import threading
27
+ import time
28
+ from contextlib import contextmanager
29
+ from typing import Generator, TextIO, cast
30
+
31
+ from .application import get_app_session, run_in_terminal
32
+ from .output import Output
33
+
34
+ __all__ = [
35
+ "patch_stdout",
36
+ "StdoutProxy",
37
+ ]
38
+
39
+
40
+ @contextmanager
41
+ def patch_stdout(raw: bool = False) -> Generator[None, None, None]:
42
+ """
43
+ Replace `sys.stdout` by an :class:`_StdoutProxy` instance.
44
+
45
+ Writing to this proxy will make sure that the text appears above the
46
+ prompt, and that it doesn't destroy the output from the renderer. If no
47
+ application is curring, the behavior should be identical to writing to
48
+ `sys.stdout` directly.
49
+
50
+ Warning: If a new event loop is installed using `asyncio.set_event_loop()`,
51
+ then make sure that the context manager is applied after the event loop
52
+ is changed. Printing to stdout will be scheduled in the event loop
53
+ that's active when the context manager is created.
54
+
55
+ :param raw: (`bool`) When True, vt100 terminal escape sequences are not
56
+ removed/escaped.
57
+ """
58
+ with StdoutProxy(raw=raw) as proxy:
59
+ original_stdout = sys.stdout
60
+ original_stderr = sys.stderr
61
+
62
+ # Enter.
63
+ sys.stdout = cast(TextIO, proxy)
64
+ sys.stderr = cast(TextIO, proxy)
65
+
66
+ try:
67
+ yield
68
+ finally:
69
+ sys.stdout = original_stdout
70
+ sys.stderr = original_stderr
71
+
72
+
73
+ class _Done:
74
+ "Sentinel value for stopping the stdout proxy."
75
+
76
+
77
+ class StdoutProxy:
78
+ """
79
+ File-like object, which prints everything written to it, output above the
80
+ current application/prompt. This class is compatible with other file
81
+ objects and can be used as a drop-in replacement for `sys.stdout` or can
82
+ for instance be passed to `logging.StreamHandler`.
83
+
84
+ The current application, above which we print, is determined by looking
85
+ what application currently runs in the `AppSession` that is active during
86
+ the creation of this instance.
87
+
88
+ This class can be used as a context manager.
89
+
90
+ In order to avoid having to repaint the prompt continuously for every
91
+ little write, a short delay of `sleep_between_writes` seconds will be added
92
+ between writes in order to bundle many smaller writes in a short timespan.
93
+ """
94
+
95
+ def __init__(
96
+ self,
97
+ sleep_between_writes: float = 0.2,
98
+ raw: bool = False,
99
+ ) -> None:
100
+ self.sleep_between_writes = sleep_between_writes
101
+ self.raw = raw
102
+
103
+ self._lock = threading.RLock()
104
+ self._buffer: list[str] = []
105
+
106
+ # Keep track of the curret app session.
107
+ self.app_session = get_app_session()
108
+
109
+ # See what output is active *right now*. We should do it at this point,
110
+ # before this `StdoutProxy` instance is possibly assigned to `sys.stdout`.
111
+ # Otherwise, if `patch_stdout` is used, and no `Output` instance has
112
+ # been created, then the default output creation code will see this
113
+ # proxy object as `sys.stdout`, and get in a recursive loop trying to
114
+ # access `StdoutProxy.isatty()` which will again retrieve the output.
115
+ self._output: Output = self.app_session.output
116
+
117
+ # Flush thread
118
+ self._flush_queue: queue.Queue[str | _Done] = queue.Queue()
119
+ self._flush_thread = self._start_write_thread()
120
+ self.closed = False
121
+
122
+ def __enter__(self) -> StdoutProxy:
123
+ return self
124
+
125
+ def __exit__(self, *args: object) -> None:
126
+ self.close()
127
+
128
+ def close(self) -> None:
129
+ """
130
+ Stop `StdoutProxy` proxy.
131
+
132
+ This will terminate the write thread, make sure everything is flushed
133
+ and wait for the write thread to finish.
134
+ """
135
+ if not self.closed:
136
+ self._flush_queue.put(_Done())
137
+ self._flush_thread.join()
138
+ self.closed = True
139
+
140
+ def _start_write_thread(self) -> threading.Thread:
141
+ thread = threading.Thread(
142
+ target=self._write_thread,
143
+ name="patch-stdout-flush-thread",
144
+ daemon=True,
145
+ )
146
+ thread.start()
147
+ return thread
148
+
149
+ def _write_thread(self) -> None:
150
+ done = False
151
+
152
+ while not done:
153
+ item = self._flush_queue.get()
154
+
155
+ if isinstance(item, _Done):
156
+ break
157
+
158
+ # Don't bother calling when we got an empty string.
159
+ if not item:
160
+ continue
161
+
162
+ text = []
163
+ text.append(item)
164
+
165
+ # Read the rest of the queue if more data was queued up.
166
+ while True:
167
+ try:
168
+ item = self._flush_queue.get_nowait()
169
+ except queue.Empty:
170
+ break
171
+ else:
172
+ if isinstance(item, _Done):
173
+ done = True
174
+ else:
175
+ text.append(item)
176
+
177
+ app_loop = self._get_app_loop()
178
+ self._write_and_flush(app_loop, "".join(text))
179
+
180
+ # If an application was running that requires repainting, then wait
181
+ # for a very short time, in order to bundle actual writes and avoid
182
+ # having to repaint to often.
183
+ if app_loop is not None:
184
+ time.sleep(self.sleep_between_writes)
185
+
186
+ def _get_app_loop(self) -> asyncio.AbstractEventLoop | None:
187
+ """
188
+ Return the event loop for the application currently running in our
189
+ `AppSession`.
190
+ """
191
+ app = self.app_session.app
192
+
193
+ if app is None:
194
+ return None
195
+
196
+ return app.loop
197
+
198
+ def _write_and_flush(
199
+ self, loop: asyncio.AbstractEventLoop | None, text: str
200
+ ) -> None:
201
+ """
202
+ Write the given text to stdout and flush.
203
+ If an application is running, use `run_in_terminal`.
204
+ """
205
+
206
+ def write_and_flush() -> None:
207
+ # Ensure that autowrap is enabled before calling `write`.
208
+ # XXX: On Windows, the `Windows10_Output` enables/disables VT
209
+ # terminal processing for every flush. It turns out that this
210
+ # causes autowrap to be reset (disabled) after each flush. So,
211
+ # we have to enable it again before writing text.
212
+ self._output.enable_autowrap()
213
+
214
+ if self.raw:
215
+ self._output.write_raw(text)
216
+ else:
217
+ self._output.write(text)
218
+
219
+ self._output.flush()
220
+
221
+ def write_and_flush_in_loop() -> None:
222
+ # If an application is running, use `run_in_terminal`, otherwise
223
+ # call it directly.
224
+ run_in_terminal(write_and_flush, in_executor=False)
225
+
226
+ if loop is None:
227
+ # No loop, write immediately.
228
+ write_and_flush()
229
+ else:
230
+ # Make sure `write_and_flush` is executed *in* the event loop, not
231
+ # in another thread.
232
+ loop.call_soon_threadsafe(write_and_flush_in_loop)
233
+
234
+ def _write(self, data: str) -> None:
235
+ """
236
+ Note: print()-statements cause to multiple write calls.
237
+ (write('line') and write('\n')). Of course we don't want to call
238
+ `run_in_terminal` for every individual call, because that's too
239
+ expensive, and as long as the newline hasn't been written, the
240
+ text itself is again overwritten by the rendering of the input
241
+ command line. Therefor, we have a little buffer which holds the
242
+ text until a newline is written to stdout.
243
+ """
244
+ if "\n" in data:
245
+ # When there is a newline in the data, write everything before the
246
+ # newline, including the newline itself.
247
+ before, after = data.rsplit("\n", 1)
248
+ to_write = self._buffer + [before, "\n"]
249
+ self._buffer = [after]
250
+
251
+ text = "".join(to_write)
252
+ self._flush_queue.put(text)
253
+ else:
254
+ # Otherwise, cache in buffer.
255
+ self._buffer.append(data)
256
+
257
+ def _flush(self) -> None:
258
+ text = "".join(self._buffer)
259
+ self._buffer = []
260
+ self._flush_queue.put(text)
261
+
262
+ def write(self, data: str) -> int:
263
+ with self._lock:
264
+ self._write(data)
265
+
266
+ return len(data) # Pretend everything was written.
267
+
268
+ def flush(self) -> None:
269
+ """
270
+ Flush buffered output.
271
+ """
272
+ with self._lock:
273
+ self._flush()
274
+
275
+ @property
276
+ def original_stdout(self) -> TextIO | None:
277
+ return self._output.stdout or sys.__stdout__
278
+
279
+ # Attributes for compatibility with sys.__stdout__:
280
+
281
+ def fileno(self) -> int:
282
+ return self._output.fileno()
283
+
284
+ def isatty(self) -> bool:
285
+ stdout = self._output.stdout
286
+ if stdout is None:
287
+ return False
288
+
289
+ return stdout.isatty()
290
+
291
+ @property
292
+ def encoding(self) -> str:
293
+ return self._output.encoding()
294
+
295
+ @property
296
+ def errors(self) -> str:
297
+ return "strict"
.venv/Lib/site-packages/prompt_toolkit/py.typed ADDED
File without changes
.venv/Lib/site-packages/prompt_toolkit/renderer.py ADDED
@@ -0,0 +1,820 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Renders the command line on the console.
3
+ (Redraws parts of the input line that were changed.)
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from asyncio import FIRST_COMPLETED, Future, ensure_future, sleep, wait
9
+ from collections import deque
10
+ from enum import Enum
11
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Hashable
12
+
13
+ from prompt_toolkit.application.current import get_app
14
+ from prompt_toolkit.cursor_shapes import CursorShape
15
+ from prompt_toolkit.data_structures import Point, Size
16
+ from prompt_toolkit.filters import FilterOrBool, to_filter
17
+ from prompt_toolkit.formatted_text import AnyFormattedText, to_formatted_text
18
+ from prompt_toolkit.layout.mouse_handlers import MouseHandlers
19
+ from prompt_toolkit.layout.screen import Char, Screen, WritePosition
20
+ from prompt_toolkit.output import ColorDepth, Output
21
+ from prompt_toolkit.styles import (
22
+ Attrs,
23
+ BaseStyle,
24
+ DummyStyleTransformation,
25
+ StyleTransformation,
26
+ )
27
+
28
+ if TYPE_CHECKING:
29
+ from prompt_toolkit.application import Application
30
+ from prompt_toolkit.layout.layout import Layout
31
+
32
+
33
+ __all__ = [
34
+ "Renderer",
35
+ "print_formatted_text",
36
+ ]
37
+
38
+
39
+ def _output_screen_diff(
40
+ app: Application[Any],
41
+ output: Output,
42
+ screen: Screen,
43
+ current_pos: Point,
44
+ color_depth: ColorDepth,
45
+ previous_screen: Screen | None,
46
+ last_style: str | None,
47
+ is_done: bool, # XXX: drop is_done
48
+ full_screen: bool,
49
+ attrs_for_style_string: _StyleStringToAttrsCache,
50
+ style_string_has_style: _StyleStringHasStyleCache,
51
+ size: Size,
52
+ previous_width: int,
53
+ ) -> tuple[Point, str | None]:
54
+ """
55
+ Render the diff between this screen and the previous screen.
56
+
57
+ This takes two `Screen` instances. The one that represents the output like
58
+ it was during the last rendering and one that represents the current
59
+ output raster. Looking at these two `Screen` instances, this function will
60
+ render the difference by calling the appropriate methods of the `Output`
61
+ object that only paint the changes to the terminal.
62
+
63
+ This is some performance-critical code which is heavily optimized.
64
+ Don't change things without profiling first.
65
+
66
+ :param current_pos: Current cursor position.
67
+ :param last_style: The style string, used for drawing the last drawn
68
+ character. (Color/attributes.)
69
+ :param attrs_for_style_string: :class:`._StyleStringToAttrsCache` instance.
70
+ :param width: The width of the terminal.
71
+ :param previous_width: The width of the terminal during the last rendering.
72
+ """
73
+ width, height = size.columns, size.rows
74
+
75
+ #: Variable for capturing the output.
76
+ write = output.write
77
+ write_raw = output.write_raw
78
+
79
+ # Create locals for the most used output methods.
80
+ # (Save expensive attribute lookups.)
81
+ _output_set_attributes = output.set_attributes
82
+ _output_reset_attributes = output.reset_attributes
83
+ _output_cursor_forward = output.cursor_forward
84
+ _output_cursor_up = output.cursor_up
85
+ _output_cursor_backward = output.cursor_backward
86
+
87
+ # Hide cursor before rendering. (Avoid flickering.)
88
+ output.hide_cursor()
89
+
90
+ def reset_attributes() -> None:
91
+ "Wrapper around Output.reset_attributes."
92
+ nonlocal last_style
93
+ _output_reset_attributes()
94
+ last_style = None # Forget last char after resetting attributes.
95
+
96
+ def move_cursor(new: Point) -> Point:
97
+ "Move cursor to this `new` point. Returns the given Point."
98
+ current_x, current_y = current_pos.x, current_pos.y
99
+
100
+ if new.y > current_y:
101
+ # Use newlines instead of CURSOR_DOWN, because this might add new lines.
102
+ # CURSOR_DOWN will never create new lines at the bottom.
103
+ # Also reset attributes, otherwise the newline could draw a
104
+ # background color.
105
+ reset_attributes()
106
+ write("\r\n" * (new.y - current_y))
107
+ current_x = 0
108
+ _output_cursor_forward(new.x)
109
+ return new
110
+ elif new.y < current_y:
111
+ _output_cursor_up(current_y - new.y)
112
+
113
+ if current_x >= width - 1:
114
+ write("\r")
115
+ _output_cursor_forward(new.x)
116
+ elif new.x < current_x or current_x >= width - 1:
117
+ _output_cursor_backward(current_x - new.x)
118
+ elif new.x > current_x:
119
+ _output_cursor_forward(new.x - current_x)
120
+
121
+ return new
122
+
123
+ def output_char(char: Char) -> None:
124
+ """
125
+ Write the output of this character.
126
+ """
127
+ nonlocal last_style
128
+
129
+ # If the last printed character has the same style, don't output the
130
+ # style again.
131
+ if last_style == char.style:
132
+ write(char.char)
133
+ else:
134
+ # Look up `Attr` for this style string. Only set attributes if different.
135
+ # (Two style strings can still have the same formatting.)
136
+ # Note that an empty style string can have formatting that needs to
137
+ # be applied, because of style transformations.
138
+ new_attrs = attrs_for_style_string[char.style]
139
+ if not last_style or new_attrs != attrs_for_style_string[last_style]:
140
+ _output_set_attributes(new_attrs, color_depth)
141
+
142
+ write(char.char)
143
+ last_style = char.style
144
+
145
+ def get_max_column_index(row: dict[int, Char]) -> int:
146
+ """
147
+ Return max used column index, ignoring whitespace (without style) at
148
+ the end of the line. This is important for people that copy/paste
149
+ terminal output.
150
+
151
+ There are two reasons we are sometimes seeing whitespace at the end:
152
+ - `BufferControl` adds a trailing space to each line, because it's a
153
+ possible cursor position, so that the line wrapping won't change if
154
+ the cursor position moves around.
155
+ - The `Window` adds a style class to the current line for highlighting
156
+ (cursor-line).
157
+ """
158
+ numbers = (
159
+ index
160
+ for index, cell in row.items()
161
+ if cell.char != " " or style_string_has_style[cell.style]
162
+ )
163
+ return max(numbers, default=0)
164
+
165
+ # Render for the first time: reset styling.
166
+ if not previous_screen:
167
+ reset_attributes()
168
+
169
+ # Disable autowrap. (When entering a the alternate screen, or anytime when
170
+ # we have a prompt. - In the case of a REPL, like IPython, people can have
171
+ # background threads, and it's hard for debugging if their output is not
172
+ # wrapped.)
173
+ if not previous_screen or not full_screen:
174
+ output.disable_autowrap()
175
+
176
+ # When the previous screen has a different size, redraw everything anyway.
177
+ # Also when we are done. (We might take up less rows, so clearing is important.)
178
+ if (
179
+ is_done or not previous_screen or previous_width != width
180
+ ): # XXX: also consider height??
181
+ current_pos = move_cursor(Point(x=0, y=0))
182
+ reset_attributes()
183
+ output.erase_down()
184
+
185
+ previous_screen = Screen()
186
+
187
+ # Get height of the screen.
188
+ # (height changes as we loop over data_buffer, so remember the current value.)
189
+ # (Also make sure to clip the height to the size of the output.)
190
+ current_height = min(screen.height, height)
191
+
192
+ # Loop over the rows.
193
+ row_count = min(max(screen.height, previous_screen.height), height)
194
+
195
+ for y in range(row_count):
196
+ new_row = screen.data_buffer[y]
197
+ previous_row = previous_screen.data_buffer[y]
198
+ zero_width_escapes_row = screen.zero_width_escapes[y]
199
+
200
+ new_max_line_len = min(width - 1, get_max_column_index(new_row))
201
+ previous_max_line_len = min(width - 1, get_max_column_index(previous_row))
202
+
203
+ # Loop over the columns.
204
+ c = 0 # Column counter.
205
+ while c <= new_max_line_len:
206
+ new_char = new_row[c]
207
+ old_char = previous_row[c]
208
+ char_width = new_char.width or 1
209
+
210
+ # When the old and new character at this position are different,
211
+ # draw the output. (Because of the performance, we don't call
212
+ # `Char.__ne__`, but inline the same expression.)
213
+ if new_char.char != old_char.char or new_char.style != old_char.style:
214
+ current_pos = move_cursor(Point(x=c, y=y))
215
+
216
+ # Send injected escape sequences to output.
217
+ if c in zero_width_escapes_row:
218
+ write_raw(zero_width_escapes_row[c])
219
+
220
+ output_char(new_char)
221
+ current_pos = Point(x=current_pos.x + char_width, y=current_pos.y)
222
+
223
+ c += char_width
224
+
225
+ # If the new line is shorter, trim it.
226
+ if previous_screen and new_max_line_len < previous_max_line_len:
227
+ current_pos = move_cursor(Point(x=new_max_line_len + 1, y=y))
228
+ reset_attributes()
229
+ output.erase_end_of_line()
230
+
231
+ # Correctly reserve vertical space as required by the layout.
232
+ # When this is a new screen (drawn for the first time), or for some reason
233
+ # higher than the previous one. Move the cursor once to the bottom of the
234
+ # output. That way, we're sure that the terminal scrolls up, even when the
235
+ # lower lines of the canvas just contain whitespace.
236
+
237
+ # The most obvious reason that we actually want this behavior is the avoid
238
+ # the artifact of the input scrolling when the completion menu is shown.
239
+ # (If the scrolling is actually wanted, the layout can still be build in a
240
+ # way to behave that way by setting a dynamic height.)
241
+ if current_height > previous_screen.height:
242
+ current_pos = move_cursor(Point(x=0, y=current_height - 1))
243
+
244
+ # Move cursor:
245
+ if is_done:
246
+ current_pos = move_cursor(Point(x=0, y=current_height))
247
+ output.erase_down()
248
+ else:
249
+ current_pos = move_cursor(screen.get_cursor_position(app.layout.current_window))
250
+
251
+ if is_done or not full_screen:
252
+ output.enable_autowrap()
253
+
254
+ # Always reset the color attributes. This is important because a background
255
+ # thread could print data to stdout and we want that to be displayed in the
256
+ # default colors. (Also, if a background color has been set, many terminals
257
+ # give weird artifacts on resize events.)
258
+ reset_attributes()
259
+
260
+ if screen.show_cursor:
261
+ output.show_cursor()
262
+
263
+ return current_pos, last_style
264
+
265
+
266
+ class HeightIsUnknownError(Exception):
267
+ "Information unavailable. Did not yet receive the CPR response."
268
+
269
+
270
+ class _StyleStringToAttrsCache(Dict[str, Attrs]):
271
+ """
272
+ A cache structure that maps style strings to :class:`.Attr`.
273
+ (This is an important speed up.)
274
+ """
275
+
276
+ def __init__(
277
+ self,
278
+ get_attrs_for_style_str: Callable[[str], Attrs],
279
+ style_transformation: StyleTransformation,
280
+ ) -> None:
281
+ self.get_attrs_for_style_str = get_attrs_for_style_str
282
+ self.style_transformation = style_transformation
283
+
284
+ def __missing__(self, style_str: str) -> Attrs:
285
+ attrs = self.get_attrs_for_style_str(style_str)
286
+ attrs = self.style_transformation.transform_attrs(attrs)
287
+
288
+ self[style_str] = attrs
289
+ return attrs
290
+
291
+
292
+ class _StyleStringHasStyleCache(Dict[str, bool]):
293
+ """
294
+ Cache for remember which style strings don't render the default output
295
+ style (default fg/bg, no underline and no reverse and no blink). That way
296
+ we know that we should render these cells, even when they're empty (when
297
+ they contain a space).
298
+
299
+ Note: we don't consider bold/italic/hidden because they don't change the
300
+ output if there's no text in the cell.
301
+ """
302
+
303
+ def __init__(self, style_string_to_attrs: dict[str, Attrs]) -> None:
304
+ self.style_string_to_attrs = style_string_to_attrs
305
+
306
+ def __missing__(self, style_str: str) -> bool:
307
+ attrs = self.style_string_to_attrs[style_str]
308
+ is_default = bool(
309
+ attrs.color
310
+ or attrs.bgcolor
311
+ or attrs.underline
312
+ or attrs.strike
313
+ or attrs.blink
314
+ or attrs.reverse
315
+ )
316
+
317
+ self[style_str] = is_default
318
+ return is_default
319
+
320
+
321
+ class CPR_Support(Enum):
322
+ "Enum: whether or not CPR is supported."
323
+
324
+ SUPPORTED = "SUPPORTED"
325
+ NOT_SUPPORTED = "NOT_SUPPORTED"
326
+ UNKNOWN = "UNKNOWN"
327
+
328
+
329
+ class Renderer:
330
+ """
331
+ Typical usage:
332
+
333
+ ::
334
+
335
+ output = Vt100_Output.from_pty(sys.stdout)
336
+ r = Renderer(style, output)
337
+ r.render(app, layout=...)
338
+ """
339
+
340
+ CPR_TIMEOUT = 2 # Time to wait until we consider CPR to be not supported.
341
+
342
+ def __init__(
343
+ self,
344
+ style: BaseStyle,
345
+ output: Output,
346
+ full_screen: bool = False,
347
+ mouse_support: FilterOrBool = False,
348
+ cpr_not_supported_callback: Callable[[], None] | None = None,
349
+ ) -> None:
350
+ self.style = style
351
+ self.output = output
352
+ self.full_screen = full_screen
353
+ self.mouse_support = to_filter(mouse_support)
354
+ self.cpr_not_supported_callback = cpr_not_supported_callback
355
+
356
+ # TODO: Move following state flags into `Vt100_Output`, similar to
357
+ # `_cursor_shape_changed` and `_cursor_visible`. But then also
358
+ # adjust the `Win32Output` to not call win32 APIs if nothing has
359
+ # to be changed.
360
+
361
+ self._in_alternate_screen = False
362
+ self._mouse_support_enabled = False
363
+ self._bracketed_paste_enabled = False
364
+ self._cursor_key_mode_reset = False
365
+
366
+ # Future set when we are waiting for a CPR flag.
367
+ self._waiting_for_cpr_futures: deque[Future[None]] = deque()
368
+ self.cpr_support = CPR_Support.UNKNOWN
369
+
370
+ if not output.responds_to_cpr:
371
+ self.cpr_support = CPR_Support.NOT_SUPPORTED
372
+
373
+ # Cache for the style.
374
+ self._attrs_for_style: _StyleStringToAttrsCache | None = None
375
+ self._style_string_has_style: _StyleStringHasStyleCache | None = None
376
+ self._last_style_hash: Hashable | None = None
377
+ self._last_transformation_hash: Hashable | None = None
378
+ self._last_color_depth: ColorDepth | None = None
379
+
380
+ self.reset(_scroll=True)
381
+
382
+ def reset(self, _scroll: bool = False, leave_alternate_screen: bool = True) -> None:
383
+ # Reset position
384
+ self._cursor_pos = Point(x=0, y=0)
385
+
386
+ # Remember the last screen instance between renderers. This way,
387
+ # we can create a `diff` between two screens and only output the
388
+ # difference. It's also to remember the last height. (To show for
389
+ # instance a toolbar at the bottom position.)
390
+ self._last_screen: Screen | None = None
391
+ self._last_size: Size | None = None
392
+ self._last_style: str | None = None
393
+ self._last_cursor_shape: CursorShape | None = None
394
+
395
+ # Default MouseHandlers. (Just empty.)
396
+ self.mouse_handlers = MouseHandlers()
397
+
398
+ #: Space from the top of the layout, until the bottom of the terminal.
399
+ #: We don't know this until a `report_absolute_cursor_row` call.
400
+ self._min_available_height = 0
401
+
402
+ # In case of Windows, also make sure to scroll to the current cursor
403
+ # position. (Only when rendering the first time.)
404
+ # It does nothing for vt100 terminals.
405
+ if _scroll:
406
+ self.output.scroll_buffer_to_prompt()
407
+
408
+ # Quit alternate screen.
409
+ if self._in_alternate_screen and leave_alternate_screen:
410
+ self.output.quit_alternate_screen()
411
+ self._in_alternate_screen = False
412
+
413
+ # Disable mouse support.
414
+ if self._mouse_support_enabled:
415
+ self.output.disable_mouse_support()
416
+ self._mouse_support_enabled = False
417
+
418
+ # Disable bracketed paste.
419
+ if self._bracketed_paste_enabled:
420
+ self.output.disable_bracketed_paste()
421
+ self._bracketed_paste_enabled = False
422
+
423
+ self.output.reset_cursor_shape()
424
+ self.output.show_cursor()
425
+
426
+ # NOTE: No need to set/reset cursor key mode here.
427
+
428
+ # Flush output. `disable_mouse_support` needs to write to stdout.
429
+ self.output.flush()
430
+
431
+ @property
432
+ def last_rendered_screen(self) -> Screen | None:
433
+ """
434
+ The `Screen` class that was generated during the last rendering.
435
+ This can be `None`.
436
+ """
437
+ return self._last_screen
438
+
439
+ @property
440
+ def height_is_known(self) -> bool:
441
+ """
442
+ True when the height from the cursor until the bottom of the terminal
443
+ is known. (It's often nicer to draw bottom toolbars only if the height
444
+ is known, in order to avoid flickering when the CPR response arrives.)
445
+ """
446
+ if self.full_screen or self._min_available_height > 0:
447
+ return True
448
+ try:
449
+ self._min_available_height = self.output.get_rows_below_cursor_position()
450
+ return True
451
+ except NotImplementedError:
452
+ return False
453
+
454
+ @property
455
+ def rows_above_layout(self) -> int:
456
+ """
457
+ Return the number of rows visible in the terminal above the layout.
458
+ """
459
+ if self._in_alternate_screen:
460
+ return 0
461
+ elif self._min_available_height > 0:
462
+ total_rows = self.output.get_size().rows
463
+ last_screen_height = self._last_screen.height if self._last_screen else 0
464
+ return total_rows - max(self._min_available_height, last_screen_height)
465
+ else:
466
+ raise HeightIsUnknownError("Rows above layout is unknown.")
467
+
468
+ def request_absolute_cursor_position(self) -> None:
469
+ """
470
+ Get current cursor position.
471
+
472
+ We do this to calculate the minimum available height that we can
473
+ consume for rendering the prompt. This is the available space below te
474
+ cursor.
475
+
476
+ For vt100: Do CPR request. (answer will arrive later.)
477
+ For win32: Do API call. (Answer comes immediately.)
478
+ """
479
+ # Only do this request when the cursor is at the top row. (after a
480
+ # clear or reset). We will rely on that in `report_absolute_cursor_row`.
481
+ assert self._cursor_pos.y == 0
482
+
483
+ # In full-screen mode, always use the total height as min-available-height.
484
+ if self.full_screen:
485
+ self._min_available_height = self.output.get_size().rows
486
+ return
487
+
488
+ # For Win32, we have an API call to get the number of rows below the
489
+ # cursor.
490
+ try:
491
+ self._min_available_height = self.output.get_rows_below_cursor_position()
492
+ return
493
+ except NotImplementedError:
494
+ pass
495
+
496
+ # Use CPR.
497
+ if self.cpr_support == CPR_Support.NOT_SUPPORTED:
498
+ return
499
+
500
+ def do_cpr() -> None:
501
+ # Asks for a cursor position report (CPR).
502
+ self._waiting_for_cpr_futures.append(Future())
503
+ self.output.ask_for_cpr()
504
+
505
+ if self.cpr_support == CPR_Support.SUPPORTED:
506
+ do_cpr()
507
+ return
508
+
509
+ # If we don't know whether CPR is supported, only do a request if
510
+ # none is pending, and test it, using a timer.
511
+ if self.waiting_for_cpr:
512
+ return
513
+
514
+ do_cpr()
515
+
516
+ async def timer() -> None:
517
+ await sleep(self.CPR_TIMEOUT)
518
+
519
+ # Not set in the meantime -> not supported.
520
+ if self.cpr_support == CPR_Support.UNKNOWN:
521
+ self.cpr_support = CPR_Support.NOT_SUPPORTED
522
+
523
+ if self.cpr_not_supported_callback:
524
+ # Make sure to call this callback in the main thread.
525
+ self.cpr_not_supported_callback()
526
+
527
+ get_app().create_background_task(timer())
528
+
529
+ def report_absolute_cursor_row(self, row: int) -> None:
530
+ """
531
+ To be called when we know the absolute cursor position.
532
+ (As an answer of a "Cursor Position Request" response.)
533
+ """
534
+ self.cpr_support = CPR_Support.SUPPORTED
535
+
536
+ # Calculate the amount of rows from the cursor position until the
537
+ # bottom of the terminal.
538
+ total_rows = self.output.get_size().rows
539
+ rows_below_cursor = total_rows - row + 1
540
+
541
+ # Set the minimum available height.
542
+ self._min_available_height = rows_below_cursor
543
+
544
+ # Pop and set waiting for CPR future.
545
+ try:
546
+ f = self._waiting_for_cpr_futures.popleft()
547
+ except IndexError:
548
+ pass # Received CPR response without having a CPR.
549
+ else:
550
+ f.set_result(None)
551
+
552
+ @property
553
+ def waiting_for_cpr(self) -> bool:
554
+ """
555
+ Waiting for CPR flag. True when we send the request, but didn't got a
556
+ response.
557
+ """
558
+ return bool(self._waiting_for_cpr_futures)
559
+
560
+ async def wait_for_cpr_responses(self, timeout: int = 1) -> None:
561
+ """
562
+ Wait for a CPR response.
563
+ """
564
+ cpr_futures = list(self._waiting_for_cpr_futures) # Make copy.
565
+
566
+ # When there are no CPRs in the queue. Don't do anything.
567
+ if not cpr_futures or self.cpr_support == CPR_Support.NOT_SUPPORTED:
568
+ return None
569
+
570
+ async def wait_for_responses() -> None:
571
+ for response_f in cpr_futures:
572
+ await response_f
573
+
574
+ async def wait_for_timeout() -> None:
575
+ await sleep(timeout)
576
+
577
+ # Got timeout, erase queue.
578
+ for response_f in cpr_futures:
579
+ response_f.cancel()
580
+ self._waiting_for_cpr_futures = deque()
581
+
582
+ tasks = {
583
+ ensure_future(wait_for_responses()),
584
+ ensure_future(wait_for_timeout()),
585
+ }
586
+ _, pending = await wait(tasks, return_when=FIRST_COMPLETED)
587
+ for task in pending:
588
+ task.cancel()
589
+
590
+ def render(
591
+ self, app: Application[Any], layout: Layout, is_done: bool = False
592
+ ) -> None:
593
+ """
594
+ Render the current interface to the output.
595
+
596
+ :param is_done: When True, put the cursor at the end of the interface. We
597
+ won't print any changes to this part.
598
+ """
599
+ output = self.output
600
+
601
+ # Enter alternate screen.
602
+ if self.full_screen and not self._in_alternate_screen:
603
+ self._in_alternate_screen = True
604
+ output.enter_alternate_screen()
605
+
606
+ # Enable bracketed paste.
607
+ if not self._bracketed_paste_enabled:
608
+ self.output.enable_bracketed_paste()
609
+ self._bracketed_paste_enabled = True
610
+
611
+ # Reset cursor key mode.
612
+ if not self._cursor_key_mode_reset:
613
+ self.output.reset_cursor_key_mode()
614
+ self._cursor_key_mode_reset = True
615
+
616
+ # Enable/disable mouse support.
617
+ needs_mouse_support = self.mouse_support()
618
+
619
+ if needs_mouse_support and not self._mouse_support_enabled:
620
+ output.enable_mouse_support()
621
+ self._mouse_support_enabled = True
622
+
623
+ elif not needs_mouse_support and self._mouse_support_enabled:
624
+ output.disable_mouse_support()
625
+ self._mouse_support_enabled = False
626
+
627
+ # Create screen and write layout to it.
628
+ size = output.get_size()
629
+ screen = Screen()
630
+ screen.show_cursor = False # Hide cursor by default, unless one of the
631
+ # containers decides to display it.
632
+ mouse_handlers = MouseHandlers()
633
+
634
+ # Calculate height.
635
+ if self.full_screen:
636
+ height = size.rows
637
+ elif is_done:
638
+ # When we are done, we don't necessary want to fill up until the bottom.
639
+ height = layout.container.preferred_height(
640
+ size.columns, size.rows
641
+ ).preferred
642
+ else:
643
+ last_height = self._last_screen.height if self._last_screen else 0
644
+ height = max(
645
+ self._min_available_height,
646
+ last_height,
647
+ layout.container.preferred_height(size.columns, size.rows).preferred,
648
+ )
649
+
650
+ height = min(height, size.rows)
651
+
652
+ # When the size changes, don't consider the previous screen.
653
+ if self._last_size != size:
654
+ self._last_screen = None
655
+
656
+ # When we render using another style or another color depth, do a full
657
+ # repaint. (Forget about the previous rendered screen.)
658
+ # (But note that we still use _last_screen to calculate the height.)
659
+ if (
660
+ self.style.invalidation_hash() != self._last_style_hash
661
+ or app.style_transformation.invalidation_hash()
662
+ != self._last_transformation_hash
663
+ or app.color_depth != self._last_color_depth
664
+ ):
665
+ self._last_screen = None
666
+ self._attrs_for_style = None
667
+ self._style_string_has_style = None
668
+
669
+ if self._attrs_for_style is None:
670
+ self._attrs_for_style = _StyleStringToAttrsCache(
671
+ self.style.get_attrs_for_style_str, app.style_transformation
672
+ )
673
+ if self._style_string_has_style is None:
674
+ self._style_string_has_style = _StyleStringHasStyleCache(
675
+ self._attrs_for_style
676
+ )
677
+
678
+ self._last_style_hash = self.style.invalidation_hash()
679
+ self._last_transformation_hash = app.style_transformation.invalidation_hash()
680
+ self._last_color_depth = app.color_depth
681
+
682
+ layout.container.write_to_screen(
683
+ screen,
684
+ mouse_handlers,
685
+ WritePosition(xpos=0, ypos=0, width=size.columns, height=height),
686
+ parent_style="",
687
+ erase_bg=False,
688
+ z_index=None,
689
+ )
690
+ screen.draw_all_floats()
691
+
692
+ # When grayed. Replace all styles in the new screen.
693
+ if app.exit_style:
694
+ screen.append_style_to_content(app.exit_style)
695
+
696
+ # Process diff and write to output.
697
+ self._cursor_pos, self._last_style = _output_screen_diff(
698
+ app,
699
+ output,
700
+ screen,
701
+ self._cursor_pos,
702
+ app.color_depth,
703
+ self._last_screen,
704
+ self._last_style,
705
+ is_done,
706
+ full_screen=self.full_screen,
707
+ attrs_for_style_string=self._attrs_for_style,
708
+ style_string_has_style=self._style_string_has_style,
709
+ size=size,
710
+ previous_width=(self._last_size.columns if self._last_size else 0),
711
+ )
712
+ self._last_screen = screen
713
+ self._last_size = size
714
+ self.mouse_handlers = mouse_handlers
715
+
716
+ # Handle cursor shapes.
717
+ new_cursor_shape = app.cursor.get_cursor_shape(app)
718
+ if (
719
+ self._last_cursor_shape is None
720
+ or self._last_cursor_shape != new_cursor_shape
721
+ ):
722
+ output.set_cursor_shape(new_cursor_shape)
723
+ self._last_cursor_shape = new_cursor_shape
724
+
725
+ # Flush buffered output.
726
+ output.flush()
727
+
728
+ # Set visible windows in layout.
729
+ app.layout.visible_windows = screen.visible_windows
730
+
731
+ if is_done:
732
+ self.reset()
733
+
734
+ def erase(self, leave_alternate_screen: bool = True) -> None:
735
+ """
736
+ Hide all output and put the cursor back at the first line. This is for
737
+ instance used for running a system command (while hiding the CLI) and
738
+ later resuming the same CLI.)
739
+
740
+ :param leave_alternate_screen: When True, and when inside an alternate
741
+ screen buffer, quit the alternate screen.
742
+ """
743
+ output = self.output
744
+
745
+ output.cursor_backward(self._cursor_pos.x)
746
+ output.cursor_up(self._cursor_pos.y)
747
+ output.erase_down()
748
+ output.reset_attributes()
749
+ output.enable_autowrap()
750
+
751
+ output.flush()
752
+
753
+ self.reset(leave_alternate_screen=leave_alternate_screen)
754
+
755
+ def clear(self) -> None:
756
+ """
757
+ Clear screen and go to 0,0
758
+ """
759
+ # Erase current output first.
760
+ self.erase()
761
+
762
+ # Send "Erase Screen" command and go to (0, 0).
763
+ output = self.output
764
+
765
+ output.erase_screen()
766
+ output.cursor_goto(0, 0)
767
+ output.flush()
768
+
769
+ self.request_absolute_cursor_position()
770
+
771
+
772
+ def print_formatted_text(
773
+ output: Output,
774
+ formatted_text: AnyFormattedText,
775
+ style: BaseStyle,
776
+ style_transformation: StyleTransformation | None = None,
777
+ color_depth: ColorDepth | None = None,
778
+ ) -> None:
779
+ """
780
+ Print a list of (style_str, text) tuples in the given style to the output.
781
+ """
782
+ fragments = to_formatted_text(formatted_text)
783
+ style_transformation = style_transformation or DummyStyleTransformation()
784
+ color_depth = color_depth or output.get_default_color_depth()
785
+
786
+ # Reset first.
787
+ output.reset_attributes()
788
+ output.enable_autowrap()
789
+ last_attrs: Attrs | None = None
790
+
791
+ # Print all (style_str, text) tuples.
792
+ attrs_for_style_string = _StyleStringToAttrsCache(
793
+ style.get_attrs_for_style_str, style_transformation
794
+ )
795
+
796
+ for style_str, text, *_ in fragments:
797
+ attrs = attrs_for_style_string[style_str]
798
+
799
+ # Set style attributes if something changed.
800
+ if attrs != last_attrs:
801
+ if attrs:
802
+ output.set_attributes(attrs, color_depth)
803
+ else:
804
+ output.reset_attributes()
805
+ last_attrs = attrs
806
+
807
+ # Print escape sequences as raw output
808
+ if "[ZeroWidthEscape]" in style_str:
809
+ output.write_raw(text)
810
+ else:
811
+ # Eliminate carriage returns
812
+ text = text.replace("\r", "")
813
+ # Insert a carriage return before every newline (important when the
814
+ # front-end is a telnet client).
815
+ text = text.replace("\n", "\r\n")
816
+ output.write(text)
817
+
818
+ # Reset again.
819
+ output.reset_attributes()
820
+ output.flush()
.venv/Lib/site-packages/prompt_toolkit/search.py ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Search operations.
3
+
4
+ For the key bindings implementation with attached filters, check
5
+ `prompt_toolkit.key_binding.bindings.search`. (Use these for new key bindings
6
+ instead of calling these function directly.)
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from enum import Enum
12
+ from typing import TYPE_CHECKING
13
+
14
+ from .application.current import get_app
15
+ from .filters import FilterOrBool, is_searching, to_filter
16
+ from .key_binding.vi_state import InputMode
17
+
18
+ if TYPE_CHECKING:
19
+ from prompt_toolkit.layout.controls import BufferControl, SearchBufferControl
20
+ from prompt_toolkit.layout.layout import Layout
21
+
22
+ __all__ = [
23
+ "SearchDirection",
24
+ "start_search",
25
+ "stop_search",
26
+ ]
27
+
28
+
29
+ class SearchDirection(Enum):
30
+ FORWARD = "FORWARD"
31
+ BACKWARD = "BACKWARD"
32
+
33
+
34
+ class SearchState:
35
+ """
36
+ A search 'query', associated with a search field (like a SearchToolbar).
37
+
38
+ Every searchable `BufferControl` points to a `search_buffer_control`
39
+ (another `BufferControls`) which represents the search field. The
40
+ `SearchState` attached to that search field is used for storing the current
41
+ search query.
42
+
43
+ It is possible to have one searchfield for multiple `BufferControls`. In
44
+ that case, they'll share the same `SearchState`.
45
+ If there are multiple `BufferControls` that display the same `Buffer`, then
46
+ they can have a different `SearchState` each (if they have a different
47
+ search control).
48
+ """
49
+
50
+ __slots__ = ("text", "direction", "ignore_case")
51
+
52
+ def __init__(
53
+ self,
54
+ text: str = "",
55
+ direction: SearchDirection = SearchDirection.FORWARD,
56
+ ignore_case: FilterOrBool = False,
57
+ ) -> None:
58
+ self.text = text
59
+ self.direction = direction
60
+ self.ignore_case = to_filter(ignore_case)
61
+
62
+ def __repr__(self) -> str:
63
+ return f"{self.__class__.__name__}({self.text!r}, direction={self.direction!r}, ignore_case={self.ignore_case!r})"
64
+
65
+ def __invert__(self) -> SearchState:
66
+ """
67
+ Create a new SearchState where backwards becomes forwards and the other
68
+ way around.
69
+ """
70
+ if self.direction == SearchDirection.BACKWARD:
71
+ direction = SearchDirection.FORWARD
72
+ else:
73
+ direction = SearchDirection.BACKWARD
74
+
75
+ return SearchState(
76
+ text=self.text, direction=direction, ignore_case=self.ignore_case
77
+ )
78
+
79
+
80
+ def start_search(
81
+ buffer_control: BufferControl | None = None,
82
+ direction: SearchDirection = SearchDirection.FORWARD,
83
+ ) -> None:
84
+ """
85
+ Start search through the given `buffer_control` using the
86
+ `search_buffer_control`.
87
+
88
+ :param buffer_control: Start search for this `BufferControl`. If not given,
89
+ search through the current control.
90
+ """
91
+ from prompt_toolkit.layout.controls import BufferControl
92
+
93
+ assert buffer_control is None or isinstance(buffer_control, BufferControl)
94
+
95
+ layout = get_app().layout
96
+
97
+ # When no control is given, use the current control if that's a BufferControl.
98
+ if buffer_control is None:
99
+ if not isinstance(layout.current_control, BufferControl):
100
+ return
101
+ buffer_control = layout.current_control
102
+
103
+ # Only if this control is searchable.
104
+ search_buffer_control = buffer_control.search_buffer_control
105
+
106
+ if search_buffer_control:
107
+ buffer_control.search_state.direction = direction
108
+
109
+ # Make sure to focus the search BufferControl
110
+ layout.focus(search_buffer_control)
111
+
112
+ # Remember search link.
113
+ layout.search_links[search_buffer_control] = buffer_control
114
+
115
+ # If we're in Vi mode, make sure to go into insert mode.
116
+ get_app().vi_state.input_mode = InputMode.INSERT
117
+
118
+
119
+ def stop_search(buffer_control: BufferControl | None = None) -> None:
120
+ """
121
+ Stop search through the given `buffer_control`.
122
+ """
123
+ layout = get_app().layout
124
+
125
+ if buffer_control is None:
126
+ buffer_control = layout.search_target_buffer_control
127
+ if buffer_control is None:
128
+ # (Should not happen, but possible when `stop_search` is called
129
+ # when we're not searching.)
130
+ return
131
+ search_buffer_control = buffer_control.search_buffer_control
132
+ else:
133
+ assert buffer_control in layout.search_links.values()
134
+ search_buffer_control = _get_reverse_search_links(layout)[buffer_control]
135
+
136
+ # Focus the original buffer again.
137
+ layout.focus(buffer_control)
138
+
139
+ if search_buffer_control is not None:
140
+ # Remove the search link.
141
+ del layout.search_links[search_buffer_control]
142
+
143
+ # Reset content of search control.
144
+ search_buffer_control.buffer.reset()
145
+
146
+ # If we're in Vi mode, go back to navigation mode.
147
+ get_app().vi_state.input_mode = InputMode.NAVIGATION
148
+
149
+
150
+ def do_incremental_search(direction: SearchDirection, count: int = 1) -> None:
151
+ """
152
+ Apply search, but keep search buffer focused.
153
+ """
154
+ assert is_searching()
155
+
156
+ layout = get_app().layout
157
+
158
+ # Only search if the current control is a `BufferControl`.
159
+ from prompt_toolkit.layout.controls import BufferControl
160
+
161
+ search_control = layout.current_control
162
+ if not isinstance(search_control, BufferControl):
163
+ return
164
+
165
+ prev_control = layout.search_target_buffer_control
166
+ if prev_control is None:
167
+ return
168
+ search_state = prev_control.search_state
169
+
170
+ # Update search_state.
171
+ direction_changed = search_state.direction != direction
172
+
173
+ search_state.text = search_control.buffer.text
174
+ search_state.direction = direction
175
+
176
+ # Apply search to current buffer.
177
+ if not direction_changed:
178
+ prev_control.buffer.apply_search(
179
+ search_state, include_current_position=False, count=count
180
+ )
181
+
182
+
183
+ def accept_search() -> None:
184
+ """
185
+ Accept current search query. Focus original `BufferControl` again.
186
+ """
187
+ layout = get_app().layout
188
+
189
+ search_control = layout.current_control
190
+ target_buffer_control = layout.search_target_buffer_control
191
+
192
+ from prompt_toolkit.layout.controls import BufferControl
193
+
194
+ if not isinstance(search_control, BufferControl):
195
+ return
196
+ if target_buffer_control is None:
197
+ return
198
+
199
+ search_state = target_buffer_control.search_state
200
+
201
+ # Update search state.
202
+ if search_control.buffer.text:
203
+ search_state.text = search_control.buffer.text
204
+
205
+ # Apply search.
206
+ target_buffer_control.buffer.apply_search(
207
+ search_state, include_current_position=True
208
+ )
209
+
210
+ # Add query to history of search line.
211
+ search_control.buffer.append_to_history()
212
+
213
+ # Stop search and focus previous control again.
214
+ stop_search(target_buffer_control)
215
+
216
+
217
+ def _get_reverse_search_links(
218
+ layout: Layout,
219
+ ) -> dict[BufferControl, SearchBufferControl]:
220
+ """
221
+ Return mapping from BufferControl to SearchBufferControl.
222
+ """
223
+ return {
224
+ buffer_control: search_buffer_control
225
+ for search_buffer_control, buffer_control in layout.search_links.items()
226
+ }
.venv/Lib/site-packages/prompt_toolkit/selection.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Data structures for the selection.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from enum import Enum
8
+
9
+ __all__ = [
10
+ "SelectionType",
11
+ "PasteMode",
12
+ "SelectionState",
13
+ ]
14
+
15
+
16
+ class SelectionType(Enum):
17
+ """
18
+ Type of selection.
19
+ """
20
+
21
+ #: Characters. (Visual in Vi.)
22
+ CHARACTERS = "CHARACTERS"
23
+
24
+ #: Whole lines. (Visual-Line in Vi.)
25
+ LINES = "LINES"
26
+
27
+ #: A block selection. (Visual-Block in Vi.)
28
+ BLOCK = "BLOCK"
29
+
30
+
31
+ class PasteMode(Enum):
32
+ EMACS = "EMACS" # Yank like emacs.
33
+ VI_AFTER = "VI_AFTER" # When pressing 'p' in Vi.
34
+ VI_BEFORE = "VI_BEFORE" # When pressing 'P' in Vi.
35
+
36
+
37
+ class SelectionState:
38
+ """
39
+ State of the current selection.
40
+
41
+ :param original_cursor_position: int
42
+ :param type: :class:`~.SelectionType`
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ original_cursor_position: int = 0,
48
+ type: SelectionType = SelectionType.CHARACTERS,
49
+ ) -> None:
50
+ self.original_cursor_position = original_cursor_position
51
+ self.type = type
52
+ self.shift_mode = False
53
+
54
+ def enter_shift_mode(self) -> None:
55
+ self.shift_mode = True
56
+
57
+ def __repr__(self) -> str:
58
+ return f"{self.__class__.__name__}(original_cursor_position={self.original_cursor_position!r}, type={self.type!r})"
.venv/Lib/site-packages/prompt_toolkit/token.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ """ """
2
+
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "ZeroWidthEscape",
7
+ ]
8
+
9
+ ZeroWidthEscape = "[ZeroWidthEscape]"
.venv/Lib/site-packages/prompt_toolkit/utils.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import signal
5
+ import sys
6
+ import threading
7
+ from collections import deque
8
+ from typing import (
9
+ Callable,
10
+ ContextManager,
11
+ Dict,
12
+ Generator,
13
+ Generic,
14
+ TypeVar,
15
+ Union,
16
+ )
17
+
18
+ from wcwidth import wcwidth
19
+
20
+ __all__ = [
21
+ "Event",
22
+ "DummyContext",
23
+ "get_cwidth",
24
+ "suspend_to_background_supported",
25
+ "is_conemu_ansi",
26
+ "is_windows",
27
+ "in_main_thread",
28
+ "get_bell_environment_variable",
29
+ "get_term_environment_variable",
30
+ "take_using_weights",
31
+ "to_str",
32
+ "to_int",
33
+ "AnyFloat",
34
+ "to_float",
35
+ "is_dumb_terminal",
36
+ ]
37
+
38
+ # Used to ensure sphinx autodoc does not try to import platform-specific
39
+ # stuff when documenting win32.py modules.
40
+ SPHINX_AUTODOC_RUNNING = "sphinx.ext.autodoc" in sys.modules
41
+
42
+ _Sender = TypeVar("_Sender", covariant=True)
43
+
44
+
45
+ class Event(Generic[_Sender]):
46
+ """
47
+ Simple event to which event handlers can be attached. For instance::
48
+
49
+ class Cls:
50
+ def __init__(self):
51
+ # Define event. The first parameter is the sender.
52
+ self.event = Event(self)
53
+
54
+ obj = Cls()
55
+
56
+ def handler(sender):
57
+ pass
58
+
59
+ # Add event handler by using the += operator.
60
+ obj.event += handler
61
+
62
+ # Fire event.
63
+ obj.event()
64
+ """
65
+
66
+ def __init__(
67
+ self, sender: _Sender, handler: Callable[[_Sender], None] | None = None
68
+ ) -> None:
69
+ self.sender = sender
70
+ self._handlers: list[Callable[[_Sender], None]] = []
71
+
72
+ if handler is not None:
73
+ self += handler
74
+
75
+ def __call__(self) -> None:
76
+ "Fire event."
77
+ for handler in self._handlers:
78
+ handler(self.sender)
79
+
80
+ def fire(self) -> None:
81
+ "Alias for just calling the event."
82
+ self()
83
+
84
+ def add_handler(self, handler: Callable[[_Sender], None]) -> None:
85
+ """
86
+ Add another handler to this callback.
87
+ (Handler should be a callable that takes exactly one parameter: the
88
+ sender object.)
89
+ """
90
+ # Add to list of event handlers.
91
+ self._handlers.append(handler)
92
+
93
+ def remove_handler(self, handler: Callable[[_Sender], None]) -> None:
94
+ """
95
+ Remove a handler from this callback.
96
+ """
97
+ if handler in self._handlers:
98
+ self._handlers.remove(handler)
99
+
100
+ def __iadd__(self, handler: Callable[[_Sender], None]) -> Event[_Sender]:
101
+ """
102
+ `event += handler` notation for adding a handler.
103
+ """
104
+ self.add_handler(handler)
105
+ return self
106
+
107
+ def __isub__(self, handler: Callable[[_Sender], None]) -> Event[_Sender]:
108
+ """
109
+ `event -= handler` notation for removing a handler.
110
+ """
111
+ self.remove_handler(handler)
112
+ return self
113
+
114
+
115
+ class DummyContext(ContextManager[None]):
116
+ """
117
+ (contextlib.nested is not available on Py3)
118
+ """
119
+
120
+ def __enter__(self) -> None:
121
+ pass
122
+
123
+ def __exit__(self, *a: object) -> None:
124
+ pass
125
+
126
+
127
+ class _CharSizesCache(Dict[str, int]):
128
+ """
129
+ Cache for wcwidth sizes.
130
+ """
131
+
132
+ LONG_STRING_MIN_LEN = 64 # Minimum string length for considering it long.
133
+ MAX_LONG_STRINGS = 16 # Maximum number of long strings to remember.
134
+
135
+ def __init__(self) -> None:
136
+ super().__init__()
137
+ # Keep track of the "long" strings in this cache.
138
+ self._long_strings: deque[str] = deque()
139
+
140
+ def __missing__(self, string: str) -> int:
141
+ # Note: We use the `max(0, ...` because some non printable control
142
+ # characters, like e.g. Ctrl-underscore get a -1 wcwidth value.
143
+ # It can be possible that these characters end up in the input
144
+ # text.
145
+ result: int
146
+ if len(string) == 1:
147
+ result = max(0, wcwidth(string))
148
+ else:
149
+ result = sum(self[c] for c in string)
150
+
151
+ # Store in cache.
152
+ self[string] = result
153
+
154
+ # Rotate long strings.
155
+ # (It's hard to tell what we can consider short...)
156
+ if len(string) > self.LONG_STRING_MIN_LEN:
157
+ long_strings = self._long_strings
158
+ long_strings.append(string)
159
+
160
+ if len(long_strings) > self.MAX_LONG_STRINGS:
161
+ key_to_remove = long_strings.popleft()
162
+ if key_to_remove in self:
163
+ del self[key_to_remove]
164
+
165
+ return result
166
+
167
+
168
+ _CHAR_SIZES_CACHE = _CharSizesCache()
169
+
170
+
171
+ def get_cwidth(string: str) -> int:
172
+ """
173
+ Return width of a string. Wrapper around ``wcwidth``.
174
+ """
175
+ return _CHAR_SIZES_CACHE[string]
176
+
177
+
178
+ def suspend_to_background_supported() -> bool:
179
+ """
180
+ Returns `True` when the Python implementation supports
181
+ suspend-to-background. This is typically `False' on Windows systems.
182
+ """
183
+ return hasattr(signal, "SIGTSTP")
184
+
185
+
186
+ def is_windows() -> bool:
187
+ """
188
+ True when we are using Windows.
189
+ """
190
+ return sys.platform == "win32" # Not 'darwin' or 'linux2'
191
+
192
+
193
+ def is_windows_vt100_supported() -> bool:
194
+ """
195
+ True when we are using Windows, but VT100 escape sequences are supported.
196
+ """
197
+ if sys.platform == "win32":
198
+ # Import needs to be inline. Windows libraries are not always available.
199
+ from prompt_toolkit.output.windows10 import is_win_vt100_enabled
200
+
201
+ return is_win_vt100_enabled()
202
+
203
+ return False
204
+
205
+
206
+ def is_conemu_ansi() -> bool:
207
+ """
208
+ True when the ConEmu Windows console is used.
209
+ """
210
+ return sys.platform == "win32" and os.environ.get("ConEmuANSI", "OFF") == "ON"
211
+
212
+
213
+ def in_main_thread() -> bool:
214
+ """
215
+ True when the current thread is the main thread.
216
+ """
217
+ return threading.current_thread().__class__.__name__ == "_MainThread"
218
+
219
+
220
+ def get_bell_environment_variable() -> bool:
221
+ """
222
+ True if env variable is set to true (true, TRUE, True, 1).
223
+ """
224
+ value = os.environ.get("PROMPT_TOOLKIT_BELL", "true")
225
+ return value.lower() in ("1", "true")
226
+
227
+
228
+ def get_term_environment_variable() -> str:
229
+ "Return the $TERM environment variable."
230
+ return os.environ.get("TERM", "")
231
+
232
+
233
+ _T = TypeVar("_T")
234
+
235
+
236
+ def take_using_weights(
237
+ items: list[_T], weights: list[int]
238
+ ) -> Generator[_T, None, None]:
239
+ """
240
+ Generator that keeps yielding items from the items list, in proportion to
241
+ their weight. For instance::
242
+
243
+ # Getting the first 70 items from this generator should have yielded 10
244
+ # times A, 20 times B and 40 times C, all distributed equally..
245
+ take_using_weights(['A', 'B', 'C'], [5, 10, 20])
246
+
247
+ :param items: List of items to take from.
248
+ :param weights: Integers representing the weight. (Numbers have to be
249
+ integers, not floats.)
250
+ """
251
+ assert len(items) == len(weights)
252
+ assert len(items) > 0
253
+
254
+ # Remove items with zero-weight.
255
+ items2 = []
256
+ weights2 = []
257
+ for item, w in zip(items, weights):
258
+ if w > 0:
259
+ items2.append(item)
260
+ weights2.append(w)
261
+
262
+ items = items2
263
+ weights = weights2
264
+
265
+ # Make sure that we have some items left.
266
+ if not items:
267
+ raise ValueError("Did't got any items with a positive weight.")
268
+
269
+ #
270
+ already_taken = [0 for i in items]
271
+ item_count = len(items)
272
+ max_weight = max(weights)
273
+
274
+ i = 0
275
+ while True:
276
+ # Each iteration of this loop, we fill up until by (total_weight/max_weight).
277
+ adding = True
278
+ while adding:
279
+ adding = False
280
+
281
+ for item_i, item, weight in zip(range(item_count), items, weights):
282
+ if already_taken[item_i] < i * weight / float(max_weight):
283
+ yield item
284
+ already_taken[item_i] += 1
285
+ adding = True
286
+
287
+ i += 1
288
+
289
+
290
+ def to_str(value: Callable[[], str] | str) -> str:
291
+ "Turn callable or string into string."
292
+ if callable(value):
293
+ return to_str(value())
294
+ else:
295
+ return str(value)
296
+
297
+
298
+ def to_int(value: Callable[[], int] | int) -> int:
299
+ "Turn callable or int into int."
300
+ if callable(value):
301
+ return to_int(value())
302
+ else:
303
+ return int(value)
304
+
305
+
306
+ AnyFloat = Union[Callable[[], float], float]
307
+
308
+
309
+ def to_float(value: AnyFloat) -> float:
310
+ "Turn callable or float into float."
311
+ if callable(value):
312
+ return to_float(value())
313
+ else:
314
+ return float(value)
315
+
316
+
317
+ def is_dumb_terminal(term: str | None = None) -> bool:
318
+ """
319
+ True if this terminal type is considered "dumb".
320
+
321
+ If so, we should fall back to the simplest possible form of line editing,
322
+ without cursor positioning and color support.
323
+ """
324
+ if term is None:
325
+ return is_dumb_terminal(os.environ.get("TERM", ""))
326
+
327
+ return term.lower() in ["dumb", "unknown"]
.venv/Lib/site-packages/prompt_toolkit/validation.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Input validation for a `Buffer`.
3
+ (Validators will be called before accepting input.)
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from abc import ABCMeta, abstractmethod
9
+ from typing import Callable
10
+
11
+ from prompt_toolkit.eventloop import run_in_executor_with_context
12
+
13
+ from .document import Document
14
+ from .filters import FilterOrBool, to_filter
15
+
16
+ __all__ = [
17
+ "ConditionalValidator",
18
+ "ValidationError",
19
+ "Validator",
20
+ "ThreadedValidator",
21
+ "DummyValidator",
22
+ "DynamicValidator",
23
+ ]
24
+
25
+
26
+ class ValidationError(Exception):
27
+ """
28
+ Error raised by :meth:`.Validator.validate`.
29
+
30
+ :param cursor_position: The cursor position where the error occurred.
31
+ :param message: Text.
32
+ """
33
+
34
+ def __init__(self, cursor_position: int = 0, message: str = "") -> None:
35
+ super().__init__(message)
36
+ self.cursor_position = cursor_position
37
+ self.message = message
38
+
39
+ def __repr__(self) -> str:
40
+ return f"{self.__class__.__name__}(cursor_position={self.cursor_position!r}, message={self.message!r})"
41
+
42
+
43
+ class Validator(metaclass=ABCMeta):
44
+ """
45
+ Abstract base class for an input validator.
46
+
47
+ A validator is typically created in one of the following two ways:
48
+
49
+ - Either by overriding this class and implementing the `validate` method.
50
+ - Or by passing a callable to `Validator.from_callable`.
51
+
52
+ If the validation takes some time and needs to happen in a background
53
+ thread, this can be wrapped in a :class:`.ThreadedValidator`.
54
+ """
55
+
56
+ @abstractmethod
57
+ def validate(self, document: Document) -> None:
58
+ """
59
+ Validate the input.
60
+ If invalid, this should raise a :class:`.ValidationError`.
61
+
62
+ :param document: :class:`~prompt_toolkit.document.Document` instance.
63
+ """
64
+ pass
65
+
66
+ async def validate_async(self, document: Document) -> None:
67
+ """
68
+ Return a `Future` which is set when the validation is ready.
69
+ This function can be overloaded in order to provide an asynchronous
70
+ implementation.
71
+ """
72
+ try:
73
+ self.validate(document)
74
+ except ValidationError:
75
+ raise
76
+
77
+ @classmethod
78
+ def from_callable(
79
+ cls,
80
+ validate_func: Callable[[str], bool],
81
+ error_message: str = "Invalid input",
82
+ move_cursor_to_end: bool = False,
83
+ ) -> Validator:
84
+ """
85
+ Create a validator from a simple validate callable. E.g.:
86
+
87
+ .. code:: python
88
+
89
+ def is_valid(text):
90
+ return text in ['hello', 'world']
91
+ Validator.from_callable(is_valid, error_message='Invalid input')
92
+
93
+ :param validate_func: Callable that takes the input string, and returns
94
+ `True` if the input is valid input.
95
+ :param error_message: Message to be displayed if the input is invalid.
96
+ :param move_cursor_to_end: Move the cursor to the end of the input, if
97
+ the input is invalid.
98
+ """
99
+ return _ValidatorFromCallable(validate_func, error_message, move_cursor_to_end)
100
+
101
+
102
+ class _ValidatorFromCallable(Validator):
103
+ """
104
+ Validate input from a simple callable.
105
+ """
106
+
107
+ def __init__(
108
+ self, func: Callable[[str], bool], error_message: str, move_cursor_to_end: bool
109
+ ) -> None:
110
+ self.func = func
111
+ self.error_message = error_message
112
+ self.move_cursor_to_end = move_cursor_to_end
113
+
114
+ def __repr__(self) -> str:
115
+ return f"Validator.from_callable({self.func!r})"
116
+
117
+ def validate(self, document: Document) -> None:
118
+ if not self.func(document.text):
119
+ if self.move_cursor_to_end:
120
+ index = len(document.text)
121
+ else:
122
+ index = 0
123
+
124
+ raise ValidationError(cursor_position=index, message=self.error_message)
125
+
126
+
127
+ class ThreadedValidator(Validator):
128
+ """
129
+ Wrapper that runs input validation in a thread.
130
+ (Use this to prevent the user interface from becoming unresponsive if the
131
+ input validation takes too much time.)
132
+ """
133
+
134
+ def __init__(self, validator: Validator) -> None:
135
+ self.validator = validator
136
+
137
+ def validate(self, document: Document) -> None:
138
+ self.validator.validate(document)
139
+
140
+ async def validate_async(self, document: Document) -> None:
141
+ """
142
+ Run the `validate` function in a thread.
143
+ """
144
+
145
+ def run_validation_thread() -> None:
146
+ return self.validate(document)
147
+
148
+ await run_in_executor_with_context(run_validation_thread)
149
+
150
+
151
+ class DummyValidator(Validator):
152
+ """
153
+ Validator class that accepts any input.
154
+ """
155
+
156
+ def validate(self, document: Document) -> None:
157
+ pass # Don't raise any exception.
158
+
159
+
160
+ class ConditionalValidator(Validator):
161
+ """
162
+ Validator that can be switched on/off according to
163
+ a filter. (This wraps around another validator.)
164
+ """
165
+
166
+ def __init__(self, validator: Validator, filter: FilterOrBool) -> None:
167
+ self.validator = validator
168
+ self.filter = to_filter(filter)
169
+
170
+ def validate(self, document: Document) -> None:
171
+ # Call the validator only if the filter is active.
172
+ if self.filter():
173
+ self.validator.validate(document)
174
+
175
+
176
+ class DynamicValidator(Validator):
177
+ """
178
+ Validator class that can dynamically returns any Validator.
179
+
180
+ :param get_validator: Callable that returns a :class:`.Validator` instance.
181
+ """
182
+
183
+ def __init__(self, get_validator: Callable[[], Validator | None]) -> None:
184
+ self.get_validator = get_validator
185
+
186
+ def validate(self, document: Document) -> None:
187
+ validator = self.get_validator() or DummyValidator()
188
+ validator.validate(document)
189
+
190
+ async def validate_async(self, document: Document) -> None:
191
+ validator = self.get_validator() or DummyValidator()
192
+ await validator.validate_async(document)
.venv/Lib/site-packages/prompt_toolkit/win32_types.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from ctypes import Structure, Union, c_char, c_long, c_short, c_ulong
4
+ from ctypes.wintypes import BOOL, DWORD, LPVOID, WCHAR, WORD
5
+ from typing import TYPE_CHECKING
6
+
7
+ # Input/Output standard device numbers. Note that these are not handle objects.
8
+ # It's the `windll.kernel32.GetStdHandle` system call that turns them into a
9
+ # real handle object.
10
+ STD_INPUT_HANDLE = c_ulong(-10)
11
+ STD_OUTPUT_HANDLE = c_ulong(-11)
12
+ STD_ERROR_HANDLE = c_ulong(-12)
13
+
14
+
15
+ class COORD(Structure):
16
+ """
17
+ Struct in wincon.h
18
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx
19
+ """
20
+
21
+ if TYPE_CHECKING:
22
+ X: int
23
+ Y: int
24
+
25
+ _fields_ = [
26
+ ("X", c_short), # Short
27
+ ("Y", c_short), # Short
28
+ ]
29
+
30
+ def __repr__(self) -> str:
31
+ return "{}(X={!r}, Y={!r}, type_x={!r}, type_y={!r})".format(
32
+ self.__class__.__name__,
33
+ self.X,
34
+ self.Y,
35
+ type(self.X),
36
+ type(self.Y),
37
+ )
38
+
39
+
40
+ class UNICODE_OR_ASCII(Union):
41
+ if TYPE_CHECKING:
42
+ AsciiChar: bytes
43
+ UnicodeChar: str
44
+
45
+ _fields_ = [
46
+ ("AsciiChar", c_char),
47
+ ("UnicodeChar", WCHAR),
48
+ ]
49
+
50
+
51
+ class KEY_EVENT_RECORD(Structure):
52
+ """
53
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms684166(v=vs.85).aspx
54
+ """
55
+
56
+ if TYPE_CHECKING:
57
+ KeyDown: int
58
+ RepeatCount: int
59
+ VirtualKeyCode: int
60
+ VirtualScanCode: int
61
+ uChar: UNICODE_OR_ASCII
62
+ ControlKeyState: int
63
+
64
+ _fields_ = [
65
+ ("KeyDown", c_long), # bool
66
+ ("RepeatCount", c_short), # word
67
+ ("VirtualKeyCode", c_short), # word
68
+ ("VirtualScanCode", c_short), # word
69
+ ("uChar", UNICODE_OR_ASCII), # Unicode or ASCII.
70
+ ("ControlKeyState", c_long), # double word
71
+ ]
72
+
73
+
74
+ class MOUSE_EVENT_RECORD(Structure):
75
+ """
76
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms684239(v=vs.85).aspx
77
+ """
78
+
79
+ if TYPE_CHECKING:
80
+ MousePosition: COORD
81
+ ButtonState: int
82
+ ControlKeyState: int
83
+ EventFlags: int
84
+
85
+ _fields_ = [
86
+ ("MousePosition", COORD),
87
+ ("ButtonState", c_long), # dword
88
+ ("ControlKeyState", c_long), # dword
89
+ ("EventFlags", c_long), # dword
90
+ ]
91
+
92
+
93
+ class WINDOW_BUFFER_SIZE_RECORD(Structure):
94
+ """
95
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms687093(v=vs.85).aspx
96
+ """
97
+
98
+ if TYPE_CHECKING:
99
+ Size: COORD
100
+
101
+ _fields_ = [("Size", COORD)]
102
+
103
+
104
+ class MENU_EVENT_RECORD(Structure):
105
+ """
106
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms684213(v=vs.85).aspx
107
+ """
108
+
109
+ if TYPE_CHECKING:
110
+ CommandId: int
111
+
112
+ _fields_ = [("CommandId", c_long)] # uint
113
+
114
+
115
+ class FOCUS_EVENT_RECORD(Structure):
116
+ """
117
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683149(v=vs.85).aspx
118
+ """
119
+
120
+ if TYPE_CHECKING:
121
+ SetFocus: int
122
+
123
+ _fields_ = [("SetFocus", c_long)] # bool
124
+
125
+
126
+ class EVENT_RECORD(Union):
127
+ if TYPE_CHECKING:
128
+ KeyEvent: KEY_EVENT_RECORD
129
+ MouseEvent: MOUSE_EVENT_RECORD
130
+ WindowBufferSizeEvent: WINDOW_BUFFER_SIZE_RECORD
131
+ MenuEvent: MENU_EVENT_RECORD
132
+ FocusEvent: FOCUS_EVENT_RECORD
133
+
134
+ _fields_ = [
135
+ ("KeyEvent", KEY_EVENT_RECORD),
136
+ ("MouseEvent", MOUSE_EVENT_RECORD),
137
+ ("WindowBufferSizeEvent", WINDOW_BUFFER_SIZE_RECORD),
138
+ ("MenuEvent", MENU_EVENT_RECORD),
139
+ ("FocusEvent", FOCUS_EVENT_RECORD),
140
+ ]
141
+
142
+
143
+ class INPUT_RECORD(Structure):
144
+ """
145
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx
146
+ """
147
+
148
+ if TYPE_CHECKING:
149
+ EventType: int
150
+ Event: EVENT_RECORD
151
+
152
+ _fields_ = [("EventType", c_short), ("Event", EVENT_RECORD)] # word # Union.
153
+
154
+
155
+ EventTypes = {
156
+ 1: "KeyEvent",
157
+ 2: "MouseEvent",
158
+ 4: "WindowBufferSizeEvent",
159
+ 8: "MenuEvent",
160
+ 16: "FocusEvent",
161
+ }
162
+
163
+
164
+ class SMALL_RECT(Structure):
165
+ """struct in wincon.h."""
166
+
167
+ if TYPE_CHECKING:
168
+ Left: int
169
+ Top: int
170
+ Right: int
171
+ Bottom: int
172
+
173
+ _fields_ = [
174
+ ("Left", c_short),
175
+ ("Top", c_short),
176
+ ("Right", c_short),
177
+ ("Bottom", c_short),
178
+ ]
179
+
180
+
181
+ class CONSOLE_SCREEN_BUFFER_INFO(Structure):
182
+ """struct in wincon.h."""
183
+
184
+ if TYPE_CHECKING:
185
+ dwSize: COORD
186
+ dwCursorPosition: COORD
187
+ wAttributes: int
188
+ srWindow: SMALL_RECT
189
+ dwMaximumWindowSize: COORD
190
+
191
+ _fields_ = [
192
+ ("dwSize", COORD),
193
+ ("dwCursorPosition", COORD),
194
+ ("wAttributes", WORD),
195
+ ("srWindow", SMALL_RECT),
196
+ ("dwMaximumWindowSize", COORD),
197
+ ]
198
+
199
+ def __repr__(self) -> str:
200
+ return "CONSOLE_SCREEN_BUFFER_INFO({!r},{!r},{!r},{!r},{!r},{!r},{!r},{!r},{!r},{!r},{!r})".format(
201
+ self.dwSize.Y,
202
+ self.dwSize.X,
203
+ self.dwCursorPosition.Y,
204
+ self.dwCursorPosition.X,
205
+ self.wAttributes,
206
+ self.srWindow.Top,
207
+ self.srWindow.Left,
208
+ self.srWindow.Bottom,
209
+ self.srWindow.Right,
210
+ self.dwMaximumWindowSize.Y,
211
+ self.dwMaximumWindowSize.X,
212
+ )
213
+
214
+
215
+ class SECURITY_ATTRIBUTES(Structure):
216
+ """
217
+ http://msdn.microsoft.com/en-us/library/windows/desktop/aa379560(v=vs.85).aspx
218
+ """
219
+
220
+ if TYPE_CHECKING:
221
+ nLength: int
222
+ lpSecurityDescriptor: int
223
+ bInheritHandle: int # BOOL comes back as 'int'.
224
+
225
+ _fields_ = [
226
+ ("nLength", DWORD),
227
+ ("lpSecurityDescriptor", LPVOID),
228
+ ("bInheritHandle", BOOL),
229
+ ]
.venv/Lib/site-packages/requests-2.32.5.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
.venv/Lib/site-packages/requests-2.32.5.dist-info/METADATA ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: requests
3
+ Version: 2.32.5
4
+ Summary: Python HTTP for Humans.
5
+ Home-page: https://requests.readthedocs.io
6
+ Author: Kenneth Reitz
7
+ Author-email: me@kennethreitz.org
8
+ License: Apache-2.0
9
+ Project-URL: Documentation, https://requests.readthedocs.io
10
+ Project-URL: Source, https://github.com/psf/requests
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Web Environment
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Natural Language :: English
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Programming Language :: Python :: 3 :: Only
26
+ Classifier: Programming Language :: Python :: Implementation :: CPython
27
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
28
+ Classifier: Topic :: Internet :: WWW/HTTP
29
+ Classifier: Topic :: Software Development :: Libraries
30
+ Requires-Python: >=3.9
31
+ Description-Content-Type: text/markdown
32
+ License-File: LICENSE
33
+ Requires-Dist: charset_normalizer<4,>=2
34
+ Requires-Dist: idna<4,>=2.5
35
+ Requires-Dist: urllib3<3,>=1.21.1
36
+ Requires-Dist: certifi>=2017.4.17
37
+ Provides-Extra: security
38
+ Provides-Extra: socks
39
+ Requires-Dist: PySocks!=1.5.7,>=1.5.6; extra == "socks"
40
+ Provides-Extra: use-chardet-on-py3
41
+ Requires-Dist: chardet<6,>=3.0.2; extra == "use-chardet-on-py3"
42
+ Dynamic: author
43
+ Dynamic: author-email
44
+ Dynamic: classifier
45
+ Dynamic: description
46
+ Dynamic: description-content-type
47
+ Dynamic: home-page
48
+ Dynamic: license
49
+ Dynamic: license-file
50
+ Dynamic: project-url
51
+ Dynamic: provides-extra
52
+ Dynamic: requires-dist
53
+ Dynamic: requires-python
54
+ Dynamic: summary
55
+
56
+ # Requests
57
+
58
+ **Requests** is a simple, yet elegant, HTTP library.
59
+
60
+ ```python
61
+ >>> import requests
62
+ >>> r = requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass'))
63
+ >>> r.status_code
64
+ 200
65
+ >>> r.headers['content-type']
66
+ 'application/json; charset=utf8'
67
+ >>> r.encoding
68
+ 'utf-8'
69
+ >>> r.text
70
+ '{"authenticated": true, ...'
71
+ >>> r.json()
72
+ {'authenticated': True, ...}
73
+ ```
74
+
75
+ Requests allows you to send HTTP/1.1 requests extremely easily. There’s no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method!
76
+
77
+ Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code.
78
+
79
+ [![Downloads](https://static.pepy.tech/badge/requests/month)](https://pepy.tech/project/requests)
80
+ [![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests)
81
+ [![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors)
82
+
83
+ ## Installing Requests and Supported Versions
84
+
85
+ Requests is available on PyPI:
86
+
87
+ ```console
88
+ $ python -m pip install requests
89
+ ```
90
+
91
+ Requests officially supports Python 3.9+.
92
+
93
+ ## Supported Features & Best–Practices
94
+
95
+ Requests is ready for the demands of building robust and reliable HTTP–speaking applications, for the needs of today.
96
+
97
+ - Keep-Alive & Connection Pooling
98
+ - International Domains and URLs
99
+ - Sessions with Cookie Persistence
100
+ - Browser-style TLS/SSL Verification
101
+ - Basic & Digest Authentication
102
+ - Familiar `dict`–like Cookies
103
+ - Automatic Content Decompression and Decoding
104
+ - Multi-part File Uploads
105
+ - SOCKS Proxy Support
106
+ - Connection Timeouts
107
+ - Streaming Downloads
108
+ - Automatic honoring of `.netrc`
109
+ - Chunked HTTP Requests
110
+
111
+ ## API Reference and User Guide available on [Read the Docs](https://requests.readthedocs.io)
112
+
113
+ [![Read the Docs](https://raw.githubusercontent.com/psf/requests/main/ext/ss.png)](https://requests.readthedocs.io)
114
+
115
+ ## Cloning the repository
116
+
117
+ When cloning the Requests repository, you may need to add the `-c
118
+ fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit timestamp (see
119
+ [this issue](https://github.com/psf/requests/issues/2690) for more background):
120
+
121
+ ```shell
122
+ git clone -c fetch.fsck.badTimezone=ignore https://github.com/psf/requests.git
123
+ ```
124
+
125
+ You can also apply this setting to your global Git config:
126
+
127
+ ```shell
128
+ git config --global fetch.fsck.badTimezone ignore
129
+ ```
130
+
131
+ ---
132
+
133
+ [![Kenneth Reitz](https://raw.githubusercontent.com/psf/requests/main/ext/kr.png)](https://kennethreitz.org) [![Python Software Foundation](https://raw.githubusercontent.com/psf/requests/main/ext/psf.png)](https://www.python.org/psf)
.venv/Lib/site-packages/requests-2.32.5.dist-info/RECORD ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ requests-2.32.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2
+ requests-2.32.5.dist-info/METADATA,sha256=ZbWgjagfSRVRPnYJZf8Ut1GPZbe7Pv4NqzZLvMTUDLA,4945
3
+ requests-2.32.5.dist-info/RECORD,,
4
+ requests-2.32.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ requests-2.32.5.dist-info/licenses/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
6
+ requests-2.32.5.dist-info/top_level.txt,sha256=fMSVmHfb5rbGOo6xv-O_tUX6j-WyixssE-SnwcDRxNQ,9
7
+ requests/__init__.py,sha256=4xaAERmPDIBPsa2PsjpU9r06yooK-2mZKHTZAhWRWts,5072
8
+ requests/__pycache__/__version__.cpython-312.pyc,,
9
+ requests/__pycache__/_internal_utils.cpython-312.pyc,,
10
+ requests/__pycache__/adapters.cpython-312.pyc,,
11
+ requests/__pycache__/api.cpython-312.pyc,,
12
+ requests/__pycache__/certs.cpython-312.pyc,,
13
+ requests/__pycache__/cookies.cpython-312.pyc,,
14
+ requests/__pycache__/exceptions.cpython-312.pyc,,
15
+ requests/__pycache__/help.cpython-312.pyc,,
16
+ requests/__pycache__/hooks.cpython-312.pyc,,
17
+ requests/__pycache__/packages.cpython-312.pyc,,
18
+ requests/__pycache__/sessions.cpython-312.pyc,,
19
+ requests/__pycache__/structures.cpython-312.pyc,,
20
+ requests/__pycache__/utils.cpython-312.pyc,,
21
+ requests/__version__.py,sha256=QKDceK8K_ujqwDDc3oYrR0odOBYgKVOQQ5vFap_G_cg,435
22
+ requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495
23
+ requests/adapters.py,sha256=8nX113gbb123aUtx2ETkAN_6IsYX-M2fRoLGluTEcRk,26285
24
+ requests/api.py,sha256=_Zb9Oa7tzVIizTKwFrPjDEY9ejtm_OnSRERnADxGsQs,6449
25
+ requests/auth.py,sha256=kF75tqnLctZ9Mf_hm9TZIj4cQWnN5uxRz8oWsx5wmR0,10186
26
+ requests/certs.py,sha256=Z9Sb410Anv6jUFTyss0jFFhU6xst8ctELqfy8Ev23gw,429
27
+ requests/compat.py,sha256=J7sIjR6XoDGp5JTVzOxkK5fSoUVUa_Pjc7iRZhAWGmI,2142
28
+ requests/cookies.py,sha256=bNi-iqEj4NPZ00-ob-rHvzkvObzN3lEpgw3g6paS3Xw,18590
29
+ requests/exceptions.py,sha256=jJPS1UWATs86ShVUaLorTiJb1SaGuoNEWgICJep-VkY,4260
30
+ requests/help.py,sha256=gPX5d_H7Xd88aDABejhqGgl9B1VFRTt5BmiYvL3PzIQ,3875
31
+ requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733
32
+ requests/models.py,sha256=MjZdZ4k7tnw-1nz5PKShjmPmqyk0L6DciwnFngb_Vk4,35510
33
+ requests/packages.py,sha256=_g0gZ681UyAlKHRjH6kanbaoxx2eAb6qzcXiODyTIoc,904
34
+ requests/sessions.py,sha256=Cl1dpEnOfwrzzPbku-emepNeN4Rt_0_58Iy2x-JGTm8,30503
35
+ requests/status_codes.py,sha256=iJUAeA25baTdw-6PfD0eF4qhpINDJRJI-yaMqxs4LEI,4322
36
+ requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912
37
+ requests/utils.py,sha256=WqU86rZ3wvhC-tQjWcjtH_HEKZwWB3iWCZV6SW5DEdQ,33213
.venv/Lib/site-packages/requests-2.32.5.dist-info/WHEEL ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
.venv/Lib/site-packages/requests-2.32.5.dist-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ requests
.venv/Lib/site-packages/requests/__init__.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # __
2
+ # /__) _ _ _ _ _/ _
3
+ # / ( (- (/ (/ (- _) / _)
4
+ # /
5
+
6
+ """
7
+ Requests HTTP Library
8
+ ~~~~~~~~~~~~~~~~~~~~~
9
+
10
+ Requests is an HTTP library, written in Python, for human beings.
11
+ Basic GET usage:
12
+
13
+ >>> import requests
14
+ >>> r = requests.get('https://www.python.org')
15
+ >>> r.status_code
16
+ 200
17
+ >>> b'Python is a programming language' in r.content
18
+ True
19
+
20
+ ... or POST:
21
+
22
+ >>> payload = dict(key1='value1', key2='value2')
23
+ >>> r = requests.post('https://httpbin.org/post', data=payload)
24
+ >>> print(r.text)
25
+ {
26
+ ...
27
+ "form": {
28
+ "key1": "value1",
29
+ "key2": "value2"
30
+ },
31
+ ...
32
+ }
33
+
34
+ The other HTTP methods are supported - see `requests.api`. Full documentation
35
+ is at <https://requests.readthedocs.io>.
36
+
37
+ :copyright: (c) 2017 by Kenneth Reitz.
38
+ :license: Apache 2.0, see LICENSE for more details.
39
+ """
40
+
41
+ import warnings
42
+
43
+ import urllib3
44
+
45
+ from .exceptions import RequestsDependencyWarning
46
+
47
+ try:
48
+ from charset_normalizer import __version__ as charset_normalizer_version
49
+ except ImportError:
50
+ charset_normalizer_version = None
51
+
52
+ try:
53
+ from chardet import __version__ as chardet_version
54
+ except ImportError:
55
+ chardet_version = None
56
+
57
+
58
+ def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version):
59
+ urllib3_version = urllib3_version.split(".")
60
+ assert urllib3_version != ["dev"] # Verify urllib3 isn't installed from git.
61
+
62
+ # Sometimes, urllib3 only reports its version as 16.1.
63
+ if len(urllib3_version) == 2:
64
+ urllib3_version.append("0")
65
+
66
+ # Check urllib3 for compatibility.
67
+ major, minor, patch = urllib3_version # noqa: F811
68
+ major, minor, patch = int(major), int(minor), int(patch)
69
+ # urllib3 >= 1.21.1
70
+ assert major >= 1
71
+ if major == 1:
72
+ assert minor >= 21
73
+
74
+ # Check charset_normalizer for compatibility.
75
+ if chardet_version:
76
+ major, minor, patch = chardet_version.split(".")[:3]
77
+ major, minor, patch = int(major), int(minor), int(patch)
78
+ # chardet_version >= 3.0.2, < 6.0.0
79
+ assert (3, 0, 2) <= (major, minor, patch) < (6, 0, 0)
80
+ elif charset_normalizer_version:
81
+ major, minor, patch = charset_normalizer_version.split(".")[:3]
82
+ major, minor, patch = int(major), int(minor), int(patch)
83
+ # charset_normalizer >= 2.0.0 < 4.0.0
84
+ assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0)
85
+ else:
86
+ warnings.warn(
87
+ "Unable to find acceptable character detection dependency "
88
+ "(chardet or charset_normalizer).",
89
+ RequestsDependencyWarning,
90
+ )
91
+
92
+
93
+ def _check_cryptography(cryptography_version):
94
+ # cryptography < 1.3.4
95
+ try:
96
+ cryptography_version = list(map(int, cryptography_version.split(".")))
97
+ except ValueError:
98
+ return
99
+
100
+ if cryptography_version < [1, 3, 4]:
101
+ warning = "Old version of cryptography ({}) may cause slowdown.".format(
102
+ cryptography_version
103
+ )
104
+ warnings.warn(warning, RequestsDependencyWarning)
105
+
106
+
107
+ # Check imported dependencies for compatibility.
108
+ try:
109
+ check_compatibility(
110
+ urllib3.__version__, chardet_version, charset_normalizer_version
111
+ )
112
+ except (AssertionError, ValueError):
113
+ warnings.warn(
114
+ "urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported "
115
+ "version!".format(
116
+ urllib3.__version__, chardet_version, charset_normalizer_version
117
+ ),
118
+ RequestsDependencyWarning,
119
+ )
120
+
121
+ # Attempt to enable urllib3's fallback for SNI support
122
+ # if the standard library doesn't support SNI or the
123
+ # 'ssl' library isn't available.
124
+ try:
125
+ try:
126
+ import ssl
127
+ except ImportError:
128
+ ssl = None
129
+
130
+ if not getattr(ssl, "HAS_SNI", False):
131
+ from urllib3.contrib import pyopenssl
132
+
133
+ pyopenssl.inject_into_urllib3()
134
+
135
+ # Check cryptography version
136
+ from cryptography import __version__ as cryptography_version
137
+
138
+ _check_cryptography(cryptography_version)
139
+ except ImportError:
140
+ pass
141
+
142
+ # urllib3's DependencyWarnings should be silenced.
143
+ from urllib3.exceptions import DependencyWarning
144
+
145
+ warnings.simplefilter("ignore", DependencyWarning)
146
+
147
+ # Set default logging handler to avoid "No handler found" warnings.
148
+ import logging
149
+ from logging import NullHandler
150
+
151
+ from . import packages, utils
152
+ from .__version__ import (
153
+ __author__,
154
+ __author_email__,
155
+ __build__,
156
+ __cake__,
157
+ __copyright__,
158
+ __description__,
159
+ __license__,
160
+ __title__,
161
+ __url__,
162
+ __version__,
163
+ )
164
+ from .api import delete, get, head, options, patch, post, put, request
165
+ from .exceptions import (
166
+ ConnectionError,
167
+ ConnectTimeout,
168
+ FileModeWarning,
169
+ HTTPError,
170
+ JSONDecodeError,
171
+ ReadTimeout,
172
+ RequestException,
173
+ Timeout,
174
+ TooManyRedirects,
175
+ URLRequired,
176
+ )
177
+ from .models import PreparedRequest, Request, Response
178
+ from .sessions import Session, session
179
+ from .status_codes import codes
180
+
181
+ logging.getLogger(__name__).addHandler(NullHandler())
182
+
183
+ # FileModeWarnings go off per the default.
184
+ warnings.simplefilter("default", FileModeWarning, append=True)
.venv/Lib/site-packages/requests/__version__.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # .-. .-. .-. . . .-. .-. .-. .-.
2
+ # |( |- |.| | | |- `-. | `-.
3
+ # ' ' `-' `-`.`-' `-' `-' ' `-'
4
+
5
+ __title__ = "requests"
6
+ __description__ = "Python HTTP for Humans."
7
+ __url__ = "https://requests.readthedocs.io"
8
+ __version__ = "2.32.5"
9
+ __build__ = 0x023205
10
+ __author__ = "Kenneth Reitz"
11
+ __author_email__ = "me@kennethreitz.org"
12
+ __license__ = "Apache-2.0"
13
+ __copyright__ = "Copyright Kenneth Reitz"
14
+ __cake__ = "\u2728 \U0001f370 \u2728"
.venv/Lib/site-packages/requests/_internal_utils.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ requests._internal_utils
3
+ ~~~~~~~~~~~~~~
4
+
5
+ Provides utility functions that are consumed internally by Requests
6
+ which depend on extremely few external helpers (such as compat)
7
+ """
8
+ import re
9
+
10
+ from .compat import builtin_str
11
+
12
+ _VALID_HEADER_NAME_RE_BYTE = re.compile(rb"^[^:\s][^:\r\n]*$")
13
+ _VALID_HEADER_NAME_RE_STR = re.compile(r"^[^:\s][^:\r\n]*$")
14
+ _VALID_HEADER_VALUE_RE_BYTE = re.compile(rb"^\S[^\r\n]*$|^$")
15
+ _VALID_HEADER_VALUE_RE_STR = re.compile(r"^\S[^\r\n]*$|^$")
16
+
17
+ _HEADER_VALIDATORS_STR = (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR)
18
+ _HEADER_VALIDATORS_BYTE = (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE)
19
+ HEADER_VALIDATORS = {
20
+ bytes: _HEADER_VALIDATORS_BYTE,
21
+ str: _HEADER_VALIDATORS_STR,
22
+ }
23
+
24
+
25
+ def to_native_string(string, encoding="ascii"):
26
+ """Given a string object, regardless of type, returns a representation of
27
+ that string in the native string type, encoding and decoding where
28
+ necessary. This assumes ASCII unless told otherwise.
29
+ """
30
+ if isinstance(string, builtin_str):
31
+ out = string
32
+ else:
33
+ out = string.decode(encoding)
34
+
35
+ return out
36
+
37
+
38
+ def unicode_is_ascii(u_string):
39
+ """Determine if unicode string only contains ASCII characters.
40
+
41
+ :param str u_string: unicode string to check. Must be unicode
42
+ and not Python 2 `str`.
43
+ :rtype: bool
44
+ """
45
+ assert isinstance(u_string, str)
46
+ try:
47
+ u_string.encode("ascii")
48
+ return True
49
+ except UnicodeEncodeError:
50
+ return False
.venv/Lib/site-packages/requests/adapters.py ADDED
@@ -0,0 +1,696 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ requests.adapters
3
+ ~~~~~~~~~~~~~~~~~
4
+
5
+ This module contains the transport adapters that Requests uses to define
6
+ and maintain connections.
7
+ """
8
+
9
+ import os.path
10
+ import socket # noqa: F401
11
+ import typing
12
+ import warnings
13
+
14
+ from urllib3.exceptions import ClosedPoolError, ConnectTimeoutError
15
+ from urllib3.exceptions import HTTPError as _HTTPError
16
+ from urllib3.exceptions import InvalidHeader as _InvalidHeader
17
+ from urllib3.exceptions import (
18
+ LocationValueError,
19
+ MaxRetryError,
20
+ NewConnectionError,
21
+ ProtocolError,
22
+ )
23
+ from urllib3.exceptions import ProxyError as _ProxyError
24
+ from urllib3.exceptions import ReadTimeoutError, ResponseError
25
+ from urllib3.exceptions import SSLError as _SSLError
26
+ from urllib3.poolmanager import PoolManager, proxy_from_url
27
+ from urllib3.util import Timeout as TimeoutSauce
28
+ from urllib3.util import parse_url
29
+ from urllib3.util.retry import Retry
30
+
31
+ from .auth import _basic_auth_str
32
+ from .compat import basestring, urlparse
33
+ from .cookies import extract_cookies_to_jar
34
+ from .exceptions import (
35
+ ConnectionError,
36
+ ConnectTimeout,
37
+ InvalidHeader,
38
+ InvalidProxyURL,
39
+ InvalidSchema,
40
+ InvalidURL,
41
+ ProxyError,
42
+ ReadTimeout,
43
+ RetryError,
44
+ SSLError,
45
+ )
46
+ from .models import Response
47
+ from .structures import CaseInsensitiveDict
48
+ from .utils import (
49
+ DEFAULT_CA_BUNDLE_PATH,
50
+ extract_zipped_paths,
51
+ get_auth_from_url,
52
+ get_encoding_from_headers,
53
+ prepend_scheme_if_needed,
54
+ select_proxy,
55
+ urldefragauth,
56
+ )
57
+
58
+ try:
59
+ from urllib3.contrib.socks import SOCKSProxyManager
60
+ except ImportError:
61
+
62
+ def SOCKSProxyManager(*args, **kwargs):
63
+ raise InvalidSchema("Missing dependencies for SOCKS support.")
64
+
65
+
66
+ if typing.TYPE_CHECKING:
67
+ from .models import PreparedRequest
68
+
69
+
70
+ DEFAULT_POOLBLOCK = False
71
+ DEFAULT_POOLSIZE = 10
72
+ DEFAULT_RETRIES = 0
73
+ DEFAULT_POOL_TIMEOUT = None
74
+
75
+
76
+ def _urllib3_request_context(
77
+ request: "PreparedRequest",
78
+ verify: "bool | str | None",
79
+ client_cert: "typing.Tuple[str, str] | str | None",
80
+ poolmanager: "PoolManager",
81
+ ) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])":
82
+ host_params = {}
83
+ pool_kwargs = {}
84
+ parsed_request_url = urlparse(request.url)
85
+ scheme = parsed_request_url.scheme.lower()
86
+ port = parsed_request_url.port
87
+
88
+ cert_reqs = "CERT_REQUIRED"
89
+ if verify is False:
90
+ cert_reqs = "CERT_NONE"
91
+ elif isinstance(verify, str):
92
+ if not os.path.isdir(verify):
93
+ pool_kwargs["ca_certs"] = verify
94
+ else:
95
+ pool_kwargs["ca_cert_dir"] = verify
96
+ pool_kwargs["cert_reqs"] = cert_reqs
97
+ if client_cert is not None:
98
+ if isinstance(client_cert, tuple) and len(client_cert) == 2:
99
+ pool_kwargs["cert_file"] = client_cert[0]
100
+ pool_kwargs["key_file"] = client_cert[1]
101
+ else:
102
+ # According to our docs, we allow users to specify just the client
103
+ # cert path
104
+ pool_kwargs["cert_file"] = client_cert
105
+ host_params = {
106
+ "scheme": scheme,
107
+ "host": parsed_request_url.hostname,
108
+ "port": port,
109
+ }
110
+ return host_params, pool_kwargs
111
+
112
+
113
+ class BaseAdapter:
114
+ """The Base Transport Adapter"""
115
+
116
+ def __init__(self):
117
+ super().__init__()
118
+
119
+ def send(
120
+ self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None
121
+ ):
122
+ """Sends PreparedRequest object. Returns Response object.
123
+
124
+ :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
125
+ :param stream: (optional) Whether to stream the request content.
126
+ :param timeout: (optional) How long to wait for the server to send
127
+ data before giving up, as a float, or a :ref:`(connect timeout,
128
+ read timeout) <timeouts>` tuple.
129
+ :type timeout: float or tuple
130
+ :param verify: (optional) Either a boolean, in which case it controls whether we verify
131
+ the server's TLS certificate, or a string, in which case it must be a path
132
+ to a CA bundle to use
133
+ :param cert: (optional) Any user-provided SSL certificate to be trusted.
134
+ :param proxies: (optional) The proxies dictionary to apply to the request.
135
+ """
136
+ raise NotImplementedError
137
+
138
+ def close(self):
139
+ """Cleans up adapter specific items."""
140
+ raise NotImplementedError
141
+
142
+
143
+ class HTTPAdapter(BaseAdapter):
144
+ """The built-in HTTP Adapter for urllib3.
145
+
146
+ Provides a general-case interface for Requests sessions to contact HTTP and
147
+ HTTPS urls by implementing the Transport Adapter interface. This class will
148
+ usually be created by the :class:`Session <Session>` class under the
149
+ covers.
150
+
151
+ :param pool_connections: The number of urllib3 connection pools to cache.
152
+ :param pool_maxsize: The maximum number of connections to save in the pool.
153
+ :param max_retries: The maximum number of retries each connection
154
+ should attempt. Note, this applies only to failed DNS lookups, socket
155
+ connections and connection timeouts, never to requests where data has
156
+ made it to the server. By default, Requests does not retry failed
157
+ connections. If you need granular control over the conditions under
158
+ which we retry a request, import urllib3's ``Retry`` class and pass
159
+ that instead.
160
+ :param pool_block: Whether the connection pool should block for connections.
161
+
162
+ Usage::
163
+
164
+ >>> import requests
165
+ >>> s = requests.Session()
166
+ >>> a = requests.adapters.HTTPAdapter(max_retries=3)
167
+ >>> s.mount('http://', a)
168
+ """
169
+
170
+ __attrs__ = [
171
+ "max_retries",
172
+ "config",
173
+ "_pool_connections",
174
+ "_pool_maxsize",
175
+ "_pool_block",
176
+ ]
177
+
178
+ def __init__(
179
+ self,
180
+ pool_connections=DEFAULT_POOLSIZE,
181
+ pool_maxsize=DEFAULT_POOLSIZE,
182
+ max_retries=DEFAULT_RETRIES,
183
+ pool_block=DEFAULT_POOLBLOCK,
184
+ ):
185
+ if max_retries == DEFAULT_RETRIES:
186
+ self.max_retries = Retry(0, read=False)
187
+ else:
188
+ self.max_retries = Retry.from_int(max_retries)
189
+ self.config = {}
190
+ self.proxy_manager = {}
191
+
192
+ super().__init__()
193
+
194
+ self._pool_connections = pool_connections
195
+ self._pool_maxsize = pool_maxsize
196
+ self._pool_block = pool_block
197
+
198
+ self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)
199
+
200
+ def __getstate__(self):
201
+ return {attr: getattr(self, attr, None) for attr in self.__attrs__}
202
+
203
+ def __setstate__(self, state):
204
+ # Can't handle by adding 'proxy_manager' to self.__attrs__ because
205
+ # self.poolmanager uses a lambda function, which isn't pickleable.
206
+ self.proxy_manager = {}
207
+ self.config = {}
208
+
209
+ for attr, value in state.items():
210
+ setattr(self, attr, value)
211
+
212
+ self.init_poolmanager(
213
+ self._pool_connections, self._pool_maxsize, block=self._pool_block
214
+ )
215
+
216
+ def init_poolmanager(
217
+ self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs
218
+ ):
219
+ """Initializes a urllib3 PoolManager.
220
+
221
+ This method should not be called from user code, and is only
222
+ exposed for use when subclassing the
223
+ :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
224
+
225
+ :param connections: The number of urllib3 connection pools to cache.
226
+ :param maxsize: The maximum number of connections to save in the pool.
227
+ :param block: Block when no free connections are available.
228
+ :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager.
229
+ """
230
+ # save these values for pickling
231
+ self._pool_connections = connections
232
+ self._pool_maxsize = maxsize
233
+ self._pool_block = block
234
+
235
+ self.poolmanager = PoolManager(
236
+ num_pools=connections,
237
+ maxsize=maxsize,
238
+ block=block,
239
+ **pool_kwargs,
240
+ )
241
+
242
+ def proxy_manager_for(self, proxy, **proxy_kwargs):
243
+ """Return urllib3 ProxyManager for the given proxy.
244
+
245
+ This method should not be called from user code, and is only
246
+ exposed for use when subclassing the
247
+ :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
248
+
249
+ :param proxy: The proxy to return a urllib3 ProxyManager for.
250
+ :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager.
251
+ :returns: ProxyManager
252
+ :rtype: urllib3.ProxyManager
253
+ """
254
+ if proxy in self.proxy_manager:
255
+ manager = self.proxy_manager[proxy]
256
+ elif proxy.lower().startswith("socks"):
257
+ username, password = get_auth_from_url(proxy)
258
+ manager = self.proxy_manager[proxy] = SOCKSProxyManager(
259
+ proxy,
260
+ username=username,
261
+ password=password,
262
+ num_pools=self._pool_connections,
263
+ maxsize=self._pool_maxsize,
264
+ block=self._pool_block,
265
+ **proxy_kwargs,
266
+ )
267
+ else:
268
+ proxy_headers = self.proxy_headers(proxy)
269
+ manager = self.proxy_manager[proxy] = proxy_from_url(
270
+ proxy,
271
+ proxy_headers=proxy_headers,
272
+ num_pools=self._pool_connections,
273
+ maxsize=self._pool_maxsize,
274
+ block=self._pool_block,
275
+ **proxy_kwargs,
276
+ )
277
+
278
+ return manager
279
+
280
+ def cert_verify(self, conn, url, verify, cert):
281
+ """Verify a SSL certificate. This method should not be called from user
282
+ code, and is only exposed for use when subclassing the
283
+ :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
284
+
285
+ :param conn: The urllib3 connection object associated with the cert.
286
+ :param url: The requested URL.
287
+ :param verify: Either a boolean, in which case it controls whether we verify
288
+ the server's TLS certificate, or a string, in which case it must be a path
289
+ to a CA bundle to use
290
+ :param cert: The SSL certificate to verify.
291
+ """
292
+ if url.lower().startswith("https") and verify:
293
+ cert_loc = None
294
+
295
+ # Allow self-specified cert location.
296
+ if verify is not True:
297
+ cert_loc = verify
298
+
299
+ if not cert_loc:
300
+ cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
301
+
302
+ if not cert_loc or not os.path.exists(cert_loc):
303
+ raise OSError(
304
+ f"Could not find a suitable TLS CA certificate bundle, "
305
+ f"invalid path: {cert_loc}"
306
+ )
307
+
308
+ conn.cert_reqs = "CERT_REQUIRED"
309
+
310
+ if not os.path.isdir(cert_loc):
311
+ conn.ca_certs = cert_loc
312
+ else:
313
+ conn.ca_cert_dir = cert_loc
314
+ else:
315
+ conn.cert_reqs = "CERT_NONE"
316
+ conn.ca_certs = None
317
+ conn.ca_cert_dir = None
318
+
319
+ if cert:
320
+ if not isinstance(cert, basestring):
321
+ conn.cert_file = cert[0]
322
+ conn.key_file = cert[1]
323
+ else:
324
+ conn.cert_file = cert
325
+ conn.key_file = None
326
+ if conn.cert_file and not os.path.exists(conn.cert_file):
327
+ raise OSError(
328
+ f"Could not find the TLS certificate file, "
329
+ f"invalid path: {conn.cert_file}"
330
+ )
331
+ if conn.key_file and not os.path.exists(conn.key_file):
332
+ raise OSError(
333
+ f"Could not find the TLS key file, invalid path: {conn.key_file}"
334
+ )
335
+
336
+ def build_response(self, req, resp):
337
+ """Builds a :class:`Response <requests.Response>` object from a urllib3
338
+ response. This should not be called from user code, and is only exposed
339
+ for use when subclassing the
340
+ :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`
341
+
342
+ :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response.
343
+ :param resp: The urllib3 response object.
344
+ :rtype: requests.Response
345
+ """
346
+ response = Response()
347
+
348
+ # Fallback to None if there's no status_code, for whatever reason.
349
+ response.status_code = getattr(resp, "status", None)
350
+
351
+ # Make headers case-insensitive.
352
+ response.headers = CaseInsensitiveDict(getattr(resp, "headers", {}))
353
+
354
+ # Set encoding.
355
+ response.encoding = get_encoding_from_headers(response.headers)
356
+ response.raw = resp
357
+ response.reason = response.raw.reason
358
+
359
+ if isinstance(req.url, bytes):
360
+ response.url = req.url.decode("utf-8")
361
+ else:
362
+ response.url = req.url
363
+
364
+ # Add new cookies from the server.
365
+ extract_cookies_to_jar(response.cookies, req, resp)
366
+
367
+ # Give the Response some context.
368
+ response.request = req
369
+ response.connection = self
370
+
371
+ return response
372
+
373
+ def build_connection_pool_key_attributes(self, request, verify, cert=None):
374
+ """Build the PoolKey attributes used by urllib3 to return a connection.
375
+
376
+ This looks at the PreparedRequest, the user-specified verify value,
377
+ and the value of the cert parameter to determine what PoolKey values
378
+ to use to select a connection from a given urllib3 Connection Pool.
379
+
380
+ The SSL related pool key arguments are not consistently set. As of
381
+ this writing, use the following to determine what keys may be in that
382
+ dictionary:
383
+
384
+ * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the
385
+ default Requests SSL Context
386
+ * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but
387
+ ``"cert_reqs"`` will be set
388
+ * If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
389
+ ``"ca_certs"`` will be set if the string is not a directory recognized
390
+ by :py:func:`os.path.isdir`, otherwise ``"ca_cert_dir"`` will be
391
+ set.
392
+ * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
393
+ ``"cert"`` is a tuple with a second item, ``"key_file"`` will also
394
+ be present
395
+
396
+ To override these settings, one may subclass this class, call this
397
+ method and use the above logic to change parameters as desired. For
398
+ example, if one wishes to use a custom :py:class:`ssl.SSLContext` one
399
+ must both set ``"ssl_context"`` and based on what else they require,
400
+ alter the other keys to ensure the desired behaviour.
401
+
402
+ :param request:
403
+ The PreparedReqest being sent over the connection.
404
+ :type request:
405
+ :class:`~requests.models.PreparedRequest`
406
+ :param verify:
407
+ Either a boolean, in which case it controls whether
408
+ we verify the server's TLS certificate, or a string, in which case it
409
+ must be a path to a CA bundle to use.
410
+ :param cert:
411
+ (optional) Any user-provided SSL certificate for client
412
+ authentication (a.k.a., mTLS). This may be a string (i.e., just
413
+ the path to a file which holds both certificate and key) or a
414
+ tuple of length 2 with the certificate file path and key file
415
+ path.
416
+ :returns:
417
+ A tuple of two dictionaries. The first is the "host parameters"
418
+ portion of the Pool Key including scheme, hostname, and port. The
419
+ second is a dictionary of SSLContext related parameters.
420
+ """
421
+ return _urllib3_request_context(request, verify, cert, self.poolmanager)
422
+
423
+ def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
424
+ """Returns a urllib3 connection for the given request and TLS settings.
425
+ This should not be called from user code, and is only exposed for use
426
+ when subclassing the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
427
+
428
+ :param request:
429
+ The :class:`PreparedRequest <PreparedRequest>` object to be sent
430
+ over the connection.
431
+ :param verify:
432
+ Either a boolean, in which case it controls whether we verify the
433
+ server's TLS certificate, or a string, in which case it must be a
434
+ path to a CA bundle to use.
435
+ :param proxies:
436
+ (optional) The proxies dictionary to apply to the request.
437
+ :param cert:
438
+ (optional) Any user-provided SSL certificate to be used for client
439
+ authentication (a.k.a., mTLS).
440
+ :rtype:
441
+ urllib3.ConnectionPool
442
+ """
443
+ proxy = select_proxy(request.url, proxies)
444
+ try:
445
+ host_params, pool_kwargs = self.build_connection_pool_key_attributes(
446
+ request,
447
+ verify,
448
+ cert,
449
+ )
450
+ except ValueError as e:
451
+ raise InvalidURL(e, request=request)
452
+ if proxy:
453
+ proxy = prepend_scheme_if_needed(proxy, "http")
454
+ proxy_url = parse_url(proxy)
455
+ if not proxy_url.host:
456
+ raise InvalidProxyURL(
457
+ "Please check proxy URL. It is malformed "
458
+ "and could be missing the host."
459
+ )
460
+ proxy_manager = self.proxy_manager_for(proxy)
461
+ conn = proxy_manager.connection_from_host(
462
+ **host_params, pool_kwargs=pool_kwargs
463
+ )
464
+ else:
465
+ # Only scheme should be lower case
466
+ conn = self.poolmanager.connection_from_host(
467
+ **host_params, pool_kwargs=pool_kwargs
468
+ )
469
+
470
+ return conn
471
+
472
+ def get_connection(self, url, proxies=None):
473
+ """DEPRECATED: Users should move to `get_connection_with_tls_context`
474
+ for all subclasses of HTTPAdapter using Requests>=2.32.2.
475
+
476
+ Returns a urllib3 connection for the given URL. This should not be
477
+ called from user code, and is only exposed for use when subclassing the
478
+ :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
479
+
480
+ :param url: The URL to connect to.
481
+ :param proxies: (optional) A Requests-style dictionary of proxies used on this request.
482
+ :rtype: urllib3.ConnectionPool
483
+ """
484
+ warnings.warn(
485
+ (
486
+ "`get_connection` has been deprecated in favor of "
487
+ "`get_connection_with_tls_context`. Custom HTTPAdapter subclasses "
488
+ "will need to migrate for Requests>=2.32.2. Please see "
489
+ "https://github.com/psf/requests/pull/6710 for more details."
490
+ ),
491
+ DeprecationWarning,
492
+ )
493
+ proxy = select_proxy(url, proxies)
494
+
495
+ if proxy:
496
+ proxy = prepend_scheme_if_needed(proxy, "http")
497
+ proxy_url = parse_url(proxy)
498
+ if not proxy_url.host:
499
+ raise InvalidProxyURL(
500
+ "Please check proxy URL. It is malformed "
501
+ "and could be missing the host."
502
+ )
503
+ proxy_manager = self.proxy_manager_for(proxy)
504
+ conn = proxy_manager.connection_from_url(url)
505
+ else:
506
+ # Only scheme should be lower case
507
+ parsed = urlparse(url)
508
+ url = parsed.geturl()
509
+ conn = self.poolmanager.connection_from_url(url)
510
+
511
+ return conn
512
+
513
+ def close(self):
514
+ """Disposes of any internal state.
515
+
516
+ Currently, this closes the PoolManager and any active ProxyManager,
517
+ which closes any pooled connections.
518
+ """
519
+ self.poolmanager.clear()
520
+ for proxy in self.proxy_manager.values():
521
+ proxy.clear()
522
+
523
+ def request_url(self, request, proxies):
524
+ """Obtain the url to use when making the final request.
525
+
526
+ If the message is being sent through a HTTP proxy, the full URL has to
527
+ be used. Otherwise, we should only use the path portion of the URL.
528
+
529
+ This should not be called from user code, and is only exposed for use
530
+ when subclassing the
531
+ :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
532
+
533
+ :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
534
+ :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs.
535
+ :rtype: str
536
+ """
537
+ proxy = select_proxy(request.url, proxies)
538
+ scheme = urlparse(request.url).scheme
539
+
540
+ is_proxied_http_request = proxy and scheme != "https"
541
+ using_socks_proxy = False
542
+ if proxy:
543
+ proxy_scheme = urlparse(proxy).scheme.lower()
544
+ using_socks_proxy = proxy_scheme.startswith("socks")
545
+
546
+ url = request.path_url
547
+ if url.startswith("//"): # Don't confuse urllib3
548
+ url = f"/{url.lstrip('/')}"
549
+
550
+ if is_proxied_http_request and not using_socks_proxy:
551
+ url = urldefragauth(request.url)
552
+
553
+ return url
554
+
555
+ def add_headers(self, request, **kwargs):
556
+ """Add any headers needed by the connection. As of v2.0 this does
557
+ nothing by default, but is left for overriding by users that subclass
558
+ the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
559
+
560
+ This should not be called from user code, and is only exposed for use
561
+ when subclassing the
562
+ :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
563
+
564
+ :param request: The :class:`PreparedRequest <PreparedRequest>` to add headers to.
565
+ :param kwargs: The keyword arguments from the call to send().
566
+ """
567
+ pass
568
+
569
+ def proxy_headers(self, proxy):
570
+ """Returns a dictionary of the headers to add to any request sent
571
+ through a proxy. This works with urllib3 magic to ensure that they are
572
+ correctly sent to the proxy, rather than in a tunnelled request if
573
+ CONNECT is being used.
574
+
575
+ This should not be called from user code, and is only exposed for use
576
+ when subclassing the
577
+ :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
578
+
579
+ :param proxy: The url of the proxy being used for this request.
580
+ :rtype: dict
581
+ """
582
+ headers = {}
583
+ username, password = get_auth_from_url(proxy)
584
+
585
+ if username:
586
+ headers["Proxy-Authorization"] = _basic_auth_str(username, password)
587
+
588
+ return headers
589
+
590
+ def send(
591
+ self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None
592
+ ):
593
+ """Sends PreparedRequest object. Returns Response object.
594
+
595
+ :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
596
+ :param stream: (optional) Whether to stream the request content.
597
+ :param timeout: (optional) How long to wait for the server to send
598
+ data before giving up, as a float, or a :ref:`(connect timeout,
599
+ read timeout) <timeouts>` tuple.
600
+ :type timeout: float or tuple or urllib3 Timeout object
601
+ :param verify: (optional) Either a boolean, in which case it controls whether
602
+ we verify the server's TLS certificate, or a string, in which case it
603
+ must be a path to a CA bundle to use
604
+ :param cert: (optional) Any user-provided SSL certificate to be trusted.
605
+ :param proxies: (optional) The proxies dictionary to apply to the request.
606
+ :rtype: requests.Response
607
+ """
608
+
609
+ try:
610
+ conn = self.get_connection_with_tls_context(
611
+ request, verify, proxies=proxies, cert=cert
612
+ )
613
+ except LocationValueError as e:
614
+ raise InvalidURL(e, request=request)
615
+
616
+ self.cert_verify(conn, request.url, verify, cert)
617
+ url = self.request_url(request, proxies)
618
+ self.add_headers(
619
+ request,
620
+ stream=stream,
621
+ timeout=timeout,
622
+ verify=verify,
623
+ cert=cert,
624
+ proxies=proxies,
625
+ )
626
+
627
+ chunked = not (request.body is None or "Content-Length" in request.headers)
628
+
629
+ if isinstance(timeout, tuple):
630
+ try:
631
+ connect, read = timeout
632
+ timeout = TimeoutSauce(connect=connect, read=read)
633
+ except ValueError:
634
+ raise ValueError(
635
+ f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, "
636
+ f"or a single float to set both timeouts to the same value."
637
+ )
638
+ elif isinstance(timeout, TimeoutSauce):
639
+ pass
640
+ else:
641
+ timeout = TimeoutSauce(connect=timeout, read=timeout)
642
+
643
+ try:
644
+ resp = conn.urlopen(
645
+ method=request.method,
646
+ url=url,
647
+ body=request.body,
648
+ headers=request.headers,
649
+ redirect=False,
650
+ assert_same_host=False,
651
+ preload_content=False,
652
+ decode_content=False,
653
+ retries=self.max_retries,
654
+ timeout=timeout,
655
+ chunked=chunked,
656
+ )
657
+
658
+ except (ProtocolError, OSError) as err:
659
+ raise ConnectionError(err, request=request)
660
+
661
+ except MaxRetryError as e:
662
+ if isinstance(e.reason, ConnectTimeoutError):
663
+ # TODO: Remove this in 3.0.0: see #2811
664
+ if not isinstance(e.reason, NewConnectionError):
665
+ raise ConnectTimeout(e, request=request)
666
+
667
+ if isinstance(e.reason, ResponseError):
668
+ raise RetryError(e, request=request)
669
+
670
+ if isinstance(e.reason, _ProxyError):
671
+ raise ProxyError(e, request=request)
672
+
673
+ if isinstance(e.reason, _SSLError):
674
+ # This branch is for urllib3 v1.22 and later.
675
+ raise SSLError(e, request=request)
676
+
677
+ raise ConnectionError(e, request=request)
678
+
679
+ except ClosedPoolError as e:
680
+ raise ConnectionError(e, request=request)
681
+
682
+ except _ProxyError as e:
683
+ raise ProxyError(e)
684
+
685
+ except (_SSLError, _HTTPError) as e:
686
+ if isinstance(e, _SSLError):
687
+ # This branch is for urllib3 versions earlier than v1.22
688
+ raise SSLError(e, request=request)
689
+ elif isinstance(e, ReadTimeoutError):
690
+ raise ReadTimeout(e, request=request)
691
+ elif isinstance(e, _InvalidHeader):
692
+ raise InvalidHeader(e, request=request)
693
+ else:
694
+ raise
695
+
696
+ return self.build_response(request, resp)
.venv/Lib/site-packages/requests/api.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ requests.api
3
+ ~~~~~~~~~~~~
4
+
5
+ This module implements the Requests API.
6
+
7
+ :copyright: (c) 2012 by Kenneth Reitz.
8
+ :license: Apache2, see LICENSE for more details.
9
+ """
10
+
11
+ from . import sessions
12
+
13
+
14
+ def request(method, url, **kwargs):
15
+ """Constructs and sends a :class:`Request <Request>`.
16
+
17
+ :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
18
+ :param url: URL for the new :class:`Request` object.
19
+ :param params: (optional) Dictionary, list of tuples or bytes to send
20
+ in the query string for the :class:`Request`.
21
+ :param data: (optional) Dictionary, list of tuples, bytes, or file-like
22
+ object to send in the body of the :class:`Request`.
23
+ :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
24
+ :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
25
+ :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
26
+ :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
27
+ ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
28
+ or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string
29
+ defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
30
+ to add for the file.
31
+ :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
32
+ :param timeout: (optional) How many seconds to wait for the server to send data
33
+ before giving up, as a float, or a :ref:`(connect timeout, read
34
+ timeout) <timeouts>` tuple.
35
+ :type timeout: float or tuple
36
+ :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``.
37
+ :type allow_redirects: bool
38
+ :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
39
+ :param verify: (optional) Either a boolean, in which case it controls whether we verify
40
+ the server's TLS certificate, or a string, in which case it must be a path
41
+ to a CA bundle to use. Defaults to ``True``.
42
+ :param stream: (optional) if ``False``, the response content will be immediately downloaded.
43
+ :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
44
+ :return: :class:`Response <Response>` object
45
+ :rtype: requests.Response
46
+
47
+ Usage::
48
+
49
+ >>> import requests
50
+ >>> req = requests.request('GET', 'https://httpbin.org/get')
51
+ >>> req
52
+ <Response [200]>
53
+ """
54
+
55
+ # By using the 'with' statement we are sure the session is closed, thus we
56
+ # avoid leaving sockets open which can trigger a ResourceWarning in some
57
+ # cases, and look like a memory leak in others.
58
+ with sessions.Session() as session:
59
+ return session.request(method=method, url=url, **kwargs)
60
+
61
+
62
+ def get(url, params=None, **kwargs):
63
+ r"""Sends a GET request.
64
+
65
+ :param url: URL for the new :class:`Request` object.
66
+ :param params: (optional) Dictionary, list of tuples or bytes to send
67
+ in the query string for the :class:`Request`.
68
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
69
+ :return: :class:`Response <Response>` object
70
+ :rtype: requests.Response
71
+ """
72
+
73
+ return request("get", url, params=params, **kwargs)
74
+
75
+
76
+ def options(url, **kwargs):
77
+ r"""Sends an OPTIONS request.
78
+
79
+ :param url: URL for the new :class:`Request` object.
80
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
81
+ :return: :class:`Response <Response>` object
82
+ :rtype: requests.Response
83
+ """
84
+
85
+ return request("options", url, **kwargs)
86
+
87
+
88
+ def head(url, **kwargs):
89
+ r"""Sends a HEAD request.
90
+
91
+ :param url: URL for the new :class:`Request` object.
92
+ :param \*\*kwargs: Optional arguments that ``request`` takes. If
93
+ `allow_redirects` is not provided, it will be set to `False` (as
94
+ opposed to the default :meth:`request` behavior).
95
+ :return: :class:`Response <Response>` object
96
+ :rtype: requests.Response
97
+ """
98
+
99
+ kwargs.setdefault("allow_redirects", False)
100
+ return request("head", url, **kwargs)
101
+
102
+
103
+ def post(url, data=None, json=None, **kwargs):
104
+ r"""Sends a POST request.
105
+
106
+ :param url: URL for the new :class:`Request` object.
107
+ :param data: (optional) Dictionary, list of tuples, bytes, or file-like
108
+ object to send in the body of the :class:`Request`.
109
+ :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
110
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
111
+ :return: :class:`Response <Response>` object
112
+ :rtype: requests.Response
113
+ """
114
+
115
+ return request("post", url, data=data, json=json, **kwargs)
116
+
117
+
118
+ def put(url, data=None, **kwargs):
119
+ r"""Sends a PUT request.
120
+
121
+ :param url: URL for the new :class:`Request` object.
122
+ :param data: (optional) Dictionary, list of tuples, bytes, or file-like
123
+ object to send in the body of the :class:`Request`.
124
+ :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
125
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
126
+ :return: :class:`Response <Response>` object
127
+ :rtype: requests.Response
128
+ """
129
+
130
+ return request("put", url, data=data, **kwargs)
131
+
132
+
133
+ def patch(url, data=None, **kwargs):
134
+ r"""Sends a PATCH request.
135
+
136
+ :param url: URL for the new :class:`Request` object.
137
+ :param data: (optional) Dictionary, list of tuples, bytes, or file-like
138
+ object to send in the body of the :class:`Request`.
139
+ :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
140
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
141
+ :return: :class:`Response <Response>` object
142
+ :rtype: requests.Response
143
+ """
144
+
145
+ return request("patch", url, data=data, **kwargs)
146
+
147
+
148
+ def delete(url, **kwargs):
149
+ r"""Sends a DELETE request.
150
+
151
+ :param url: URL for the new :class:`Request` object.
152
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
153
+ :return: :class:`Response <Response>` object
154
+ :rtype: requests.Response
155
+ """
156
+
157
+ return request("delete", url, **kwargs)
.venv/Lib/site-packages/requests/auth.py ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ requests.auth
3
+ ~~~~~~~~~~~~~
4
+
5
+ This module contains the authentication handlers for Requests.
6
+ """
7
+
8
+ import hashlib
9
+ import os
10
+ import re
11
+ import threading
12
+ import time
13
+ import warnings
14
+ from base64 import b64encode
15
+
16
+ from ._internal_utils import to_native_string
17
+ from .compat import basestring, str, urlparse
18
+ from .cookies import extract_cookies_to_jar
19
+ from .utils import parse_dict_header
20
+
21
+ CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded"
22
+ CONTENT_TYPE_MULTI_PART = "multipart/form-data"
23
+
24
+
25
+ def _basic_auth_str(username, password):
26
+ """Returns a Basic Auth string."""
27
+
28
+ # "I want us to put a big-ol' comment on top of it that
29
+ # says that this behaviour is dumb but we need to preserve
30
+ # it because people are relying on it."
31
+ # - Lukasa
32
+ #
33
+ # These are here solely to maintain backwards compatibility
34
+ # for things like ints. This will be removed in 3.0.0.
35
+ if not isinstance(username, basestring):
36
+ warnings.warn(
37
+ "Non-string usernames will no longer be supported in Requests "
38
+ "3.0.0. Please convert the object you've passed in ({!r}) to "
39
+ "a string or bytes object in the near future to avoid "
40
+ "problems.".format(username),
41
+ category=DeprecationWarning,
42
+ )
43
+ username = str(username)
44
+
45
+ if not isinstance(password, basestring):
46
+ warnings.warn(
47
+ "Non-string passwords will no longer be supported in Requests "
48
+ "3.0.0. Please convert the object you've passed in ({!r}) to "
49
+ "a string or bytes object in the near future to avoid "
50
+ "problems.".format(type(password)),
51
+ category=DeprecationWarning,
52
+ )
53
+ password = str(password)
54
+ # -- End Removal --
55
+
56
+ if isinstance(username, str):
57
+ username = username.encode("latin1")
58
+
59
+ if isinstance(password, str):
60
+ password = password.encode("latin1")
61
+
62
+ authstr = "Basic " + to_native_string(
63
+ b64encode(b":".join((username, password))).strip()
64
+ )
65
+
66
+ return authstr
67
+
68
+
69
+ class AuthBase:
70
+ """Base class that all auth implementations derive from"""
71
+
72
+ def __call__(self, r):
73
+ raise NotImplementedError("Auth hooks must be callable.")
74
+
75
+
76
+ class HTTPBasicAuth(AuthBase):
77
+ """Attaches HTTP Basic Authentication to the given Request object."""
78
+
79
+ def __init__(self, username, password):
80
+ self.username = username
81
+ self.password = password
82
+
83
+ def __eq__(self, other):
84
+ return all(
85
+ [
86
+ self.username == getattr(other, "username", None),
87
+ self.password == getattr(other, "password", None),
88
+ ]
89
+ )
90
+
91
+ def __ne__(self, other):
92
+ return not self == other
93
+
94
+ def __call__(self, r):
95
+ r.headers["Authorization"] = _basic_auth_str(self.username, self.password)
96
+ return r
97
+
98
+
99
+ class HTTPProxyAuth(HTTPBasicAuth):
100
+ """Attaches HTTP Proxy Authentication to a given Request object."""
101
+
102
+ def __call__(self, r):
103
+ r.headers["Proxy-Authorization"] = _basic_auth_str(self.username, self.password)
104
+ return r
105
+
106
+
107
+ class HTTPDigestAuth(AuthBase):
108
+ """Attaches HTTP Digest Authentication to the given Request object."""
109
+
110
+ def __init__(self, username, password):
111
+ self.username = username
112
+ self.password = password
113
+ # Keep state in per-thread local storage
114
+ self._thread_local = threading.local()
115
+
116
+ def init_per_thread_state(self):
117
+ # Ensure state is initialized just once per-thread
118
+ if not hasattr(self._thread_local, "init"):
119
+ self._thread_local.init = True
120
+ self._thread_local.last_nonce = ""
121
+ self._thread_local.nonce_count = 0
122
+ self._thread_local.chal = {}
123
+ self._thread_local.pos = None
124
+ self._thread_local.num_401_calls = None
125
+
126
+ def build_digest_header(self, method, url):
127
+ """
128
+ :rtype: str
129
+ """
130
+
131
+ realm = self._thread_local.chal["realm"]
132
+ nonce = self._thread_local.chal["nonce"]
133
+ qop = self._thread_local.chal.get("qop")
134
+ algorithm = self._thread_local.chal.get("algorithm")
135
+ opaque = self._thread_local.chal.get("opaque")
136
+ hash_utf8 = None
137
+
138
+ if algorithm is None:
139
+ _algorithm = "MD5"
140
+ else:
141
+ _algorithm = algorithm.upper()
142
+ # lambdas assume digest modules are imported at the top level
143
+ if _algorithm == "MD5" or _algorithm == "MD5-SESS":
144
+
145
+ def md5_utf8(x):
146
+ if isinstance(x, str):
147
+ x = x.encode("utf-8")
148
+ return hashlib.md5(x).hexdigest()
149
+
150
+ hash_utf8 = md5_utf8
151
+ elif _algorithm == "SHA":
152
+
153
+ def sha_utf8(x):
154
+ if isinstance(x, str):
155
+ x = x.encode("utf-8")
156
+ return hashlib.sha1(x).hexdigest()
157
+
158
+ hash_utf8 = sha_utf8
159
+ elif _algorithm == "SHA-256":
160
+
161
+ def sha256_utf8(x):
162
+ if isinstance(x, str):
163
+ x = x.encode("utf-8")
164
+ return hashlib.sha256(x).hexdigest()
165
+
166
+ hash_utf8 = sha256_utf8
167
+ elif _algorithm == "SHA-512":
168
+
169
+ def sha512_utf8(x):
170
+ if isinstance(x, str):
171
+ x = x.encode("utf-8")
172
+ return hashlib.sha512(x).hexdigest()
173
+
174
+ hash_utf8 = sha512_utf8
175
+
176
+ KD = lambda s, d: hash_utf8(f"{s}:{d}") # noqa:E731
177
+
178
+ if hash_utf8 is None:
179
+ return None
180
+
181
+ # XXX not implemented yet
182
+ entdig = None
183
+ p_parsed = urlparse(url)
184
+ #: path is request-uri defined in RFC 2616 which should not be empty
185
+ path = p_parsed.path or "/"
186
+ if p_parsed.query:
187
+ path += f"?{p_parsed.query}"
188
+
189
+ A1 = f"{self.username}:{realm}:{self.password}"
190
+ A2 = f"{method}:{path}"
191
+
192
+ HA1 = hash_utf8(A1)
193
+ HA2 = hash_utf8(A2)
194
+
195
+ if nonce == self._thread_local.last_nonce:
196
+ self._thread_local.nonce_count += 1
197
+ else:
198
+ self._thread_local.nonce_count = 1
199
+ ncvalue = f"{self._thread_local.nonce_count:08x}"
200
+ s = str(self._thread_local.nonce_count).encode("utf-8")
201
+ s += nonce.encode("utf-8")
202
+ s += time.ctime().encode("utf-8")
203
+ s += os.urandom(8)
204
+
205
+ cnonce = hashlib.sha1(s).hexdigest()[:16]
206
+ if _algorithm == "MD5-SESS":
207
+ HA1 = hash_utf8(f"{HA1}:{nonce}:{cnonce}")
208
+
209
+ if not qop:
210
+ respdig = KD(HA1, f"{nonce}:{HA2}")
211
+ elif qop == "auth" or "auth" in qop.split(","):
212
+ noncebit = f"{nonce}:{ncvalue}:{cnonce}:auth:{HA2}"
213
+ respdig = KD(HA1, noncebit)
214
+ else:
215
+ # XXX handle auth-int.
216
+ return None
217
+
218
+ self._thread_local.last_nonce = nonce
219
+
220
+ # XXX should the partial digests be encoded too?
221
+ base = (
222
+ f'username="{self.username}", realm="{realm}", nonce="{nonce}", '
223
+ f'uri="{path}", response="{respdig}"'
224
+ )
225
+ if opaque:
226
+ base += f', opaque="{opaque}"'
227
+ if algorithm:
228
+ base += f', algorithm="{algorithm}"'
229
+ if entdig:
230
+ base += f', digest="{entdig}"'
231
+ if qop:
232
+ base += f', qop="auth", nc={ncvalue}, cnonce="{cnonce}"'
233
+
234
+ return f"Digest {base}"
235
+
236
+ def handle_redirect(self, r, **kwargs):
237
+ """Reset num_401_calls counter on redirects."""
238
+ if r.is_redirect:
239
+ self._thread_local.num_401_calls = 1
240
+
241
+ def handle_401(self, r, **kwargs):
242
+ """
243
+ Takes the given response and tries digest-auth, if needed.
244
+
245
+ :rtype: requests.Response
246
+ """
247
+
248
+ # If response is not 4xx, do not auth
249
+ # See https://github.com/psf/requests/issues/3772
250
+ if not 400 <= r.status_code < 500:
251
+ self._thread_local.num_401_calls = 1
252
+ return r
253
+
254
+ if self._thread_local.pos is not None:
255
+ # Rewind the file position indicator of the body to where
256
+ # it was to resend the request.
257
+ r.request.body.seek(self._thread_local.pos)
258
+ s_auth = r.headers.get("www-authenticate", "")
259
+
260
+ if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2:
261
+ self._thread_local.num_401_calls += 1
262
+ pat = re.compile(r"digest ", flags=re.IGNORECASE)
263
+ self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1))
264
+
265
+ # Consume content and release the original connection
266
+ # to allow our new request to reuse the same one.
267
+ r.content
268
+ r.close()
269
+ prep = r.request.copy()
270
+ extract_cookies_to_jar(prep._cookies, r.request, r.raw)
271
+ prep.prepare_cookies(prep._cookies)
272
+
273
+ prep.headers["Authorization"] = self.build_digest_header(
274
+ prep.method, prep.url
275
+ )
276
+ _r = r.connection.send(prep, **kwargs)
277
+ _r.history.append(r)
278
+ _r.request = prep
279
+
280
+ return _r
281
+
282
+ self._thread_local.num_401_calls = 1
283
+ return r
284
+
285
+ def __call__(self, r):
286
+ # Initialize per-thread state, if needed
287
+ self.init_per_thread_state()
288
+ # If we have a saved nonce, skip the 401
289
+ if self._thread_local.last_nonce:
290
+ r.headers["Authorization"] = self.build_digest_header(r.method, r.url)
291
+ try:
292
+ self._thread_local.pos = r.body.tell()
293
+ except AttributeError:
294
+ # In the case of HTTPDigestAuth being reused and the body of
295
+ # the previous request was a file-like object, pos has the
296
+ # file position of the previous body. Ensure it's set to
297
+ # None.
298
+ self._thread_local.pos = None
299
+ r.register_hook("response", self.handle_401)
300
+ r.register_hook("response", self.handle_redirect)
301
+ self._thread_local.num_401_calls = 1
302
+
303
+ return r
304
+
305
+ def __eq__(self, other):
306
+ return all(
307
+ [
308
+ self.username == getattr(other, "username", None),
309
+ self.password == getattr(other, "password", None),
310
+ ]
311
+ )
312
+
313
+ def __ne__(self, other):
314
+ return not self == other
.venv/Lib/site-packages/requests/certs.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+
3
+ """
4
+ requests.certs
5
+ ~~~~~~~~~~~~~~
6
+
7
+ This module returns the preferred default CA certificate bundle. There is
8
+ only one — the one from the certifi package.
9
+
10
+ If you are packaging Requests, e.g., for a Linux distribution or a managed
11
+ environment, you can change the definition of where() to return a separately
12
+ packaged CA bundle.
13
+ """
14
+ from certifi import where
15
+
16
+ if __name__ == "__main__":
17
+ print(where())
.venv/Lib/site-packages/requests/compat.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ requests.compat
3
+ ~~~~~~~~~~~~~~~
4
+
5
+ This module previously handled import compatibility issues
6
+ between Python 2 and Python 3. It remains for backwards
7
+ compatibility until the next major version.
8
+ """
9
+
10
+ import importlib
11
+ import sys
12
+
13
+ # -------
14
+ # urllib3
15
+ # -------
16
+ from urllib3 import __version__ as urllib3_version
17
+
18
+ # Detect which major version of urllib3 is being used.
19
+ try:
20
+ is_urllib3_1 = int(urllib3_version.split(".")[0]) == 1
21
+ except (TypeError, AttributeError):
22
+ # If we can't discern a version, prefer old functionality.
23
+ is_urllib3_1 = True
24
+
25
+ # -------------------
26
+ # Character Detection
27
+ # -------------------
28
+
29
+
30
+ def _resolve_char_detection():
31
+ """Find supported character detection libraries."""
32
+ chardet = None
33
+ for lib in ("chardet", "charset_normalizer"):
34
+ if chardet is None:
35
+ try:
36
+ chardet = importlib.import_module(lib)
37
+ except ImportError:
38
+ pass
39
+ return chardet
40
+
41
+
42
+ chardet = _resolve_char_detection()
43
+
44
+ # -------
45
+ # Pythons
46
+ # -------
47
+
48
+ # Syntax sugar.
49
+ _ver = sys.version_info
50
+
51
+ #: Python 2.x?
52
+ is_py2 = _ver[0] == 2
53
+
54
+ #: Python 3.x?
55
+ is_py3 = _ver[0] == 3
56
+
57
+ # json/simplejson module import resolution
58
+ has_simplejson = False
59
+ try:
60
+ import simplejson as json
61
+
62
+ has_simplejson = True
63
+ except ImportError:
64
+ import json
65
+
66
+ if has_simplejson:
67
+ from simplejson import JSONDecodeError
68
+ else:
69
+ from json import JSONDecodeError
70
+
71
+ # Keep OrderedDict for backwards compatibility.
72
+ from collections import OrderedDict
73
+ from collections.abc import Callable, Mapping, MutableMapping
74
+ from http import cookiejar as cookielib
75
+ from http.cookies import Morsel
76
+ from io import StringIO
77
+
78
+ # --------------
79
+ # Legacy Imports
80
+ # --------------
81
+ from urllib.parse import (
82
+ quote,
83
+ quote_plus,
84
+ unquote,
85
+ unquote_plus,
86
+ urldefrag,
87
+ urlencode,
88
+ urljoin,
89
+ urlparse,
90
+ urlsplit,
91
+ urlunparse,
92
+ )
93
+ from urllib.request import (
94
+ getproxies,
95
+ getproxies_environment,
96
+ parse_http_list,
97
+ proxy_bypass,
98
+ proxy_bypass_environment,
99
+ )
100
+
101
+ builtin_str = str
102
+ str = str
103
+ bytes = bytes
104
+ basestring = (str, bytes)
105
+ numeric_types = (int, float)
106
+ integer_types = (int,)
.venv/Lib/site-packages/requests/cookies.py ADDED
@@ -0,0 +1,561 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ requests.cookies
3
+ ~~~~~~~~~~~~~~~~
4
+
5
+ Compatibility code to be able to use `http.cookiejar.CookieJar` with requests.
6
+
7
+ requests.utils imports from here, so be careful with imports.
8
+ """
9
+
10
+ import calendar
11
+ import copy
12
+ import time
13
+
14
+ from ._internal_utils import to_native_string
15
+ from .compat import Morsel, MutableMapping, cookielib, urlparse, urlunparse
16
+
17
+ try:
18
+ import threading
19
+ except ImportError:
20
+ import dummy_threading as threading
21
+
22
+
23
+ class MockRequest:
24
+ """Wraps a `requests.Request` to mimic a `urllib2.Request`.
25
+
26
+ The code in `http.cookiejar.CookieJar` expects this interface in order to correctly
27
+ manage cookie policies, i.e., determine whether a cookie can be set, given the
28
+ domains of the request and the cookie.
29
+
30
+ The original request object is read-only. The client is responsible for collecting
31
+ the new headers via `get_new_headers()` and interpreting them appropriately. You
32
+ probably want `get_cookie_header`, defined below.
33
+ """
34
+
35
+ def __init__(self, request):
36
+ self._r = request
37
+ self._new_headers = {}
38
+ self.type = urlparse(self._r.url).scheme
39
+
40
+ def get_type(self):
41
+ return self.type
42
+
43
+ def get_host(self):
44
+ return urlparse(self._r.url).netloc
45
+
46
+ def get_origin_req_host(self):
47
+ return self.get_host()
48
+
49
+ def get_full_url(self):
50
+ # Only return the response's URL if the user hadn't set the Host
51
+ # header
52
+ if not self._r.headers.get("Host"):
53
+ return self._r.url
54
+ # If they did set it, retrieve it and reconstruct the expected domain
55
+ host = to_native_string(self._r.headers["Host"], encoding="utf-8")
56
+ parsed = urlparse(self._r.url)
57
+ # Reconstruct the URL as we expect it
58
+ return urlunparse(
59
+ [
60
+ parsed.scheme,
61
+ host,
62
+ parsed.path,
63
+ parsed.params,
64
+ parsed.query,
65
+ parsed.fragment,
66
+ ]
67
+ )
68
+
69
+ def is_unverifiable(self):
70
+ return True
71
+
72
+ def has_header(self, name):
73
+ return name in self._r.headers or name in self._new_headers
74
+
75
+ def get_header(self, name, default=None):
76
+ return self._r.headers.get(name, self._new_headers.get(name, default))
77
+
78
+ def add_header(self, key, val):
79
+ """cookiejar has no legitimate use for this method; add it back if you find one."""
80
+ raise NotImplementedError(
81
+ "Cookie headers should be added with add_unredirected_header()"
82
+ )
83
+
84
+ def add_unredirected_header(self, name, value):
85
+ self._new_headers[name] = value
86
+
87
+ def get_new_headers(self):
88
+ return self._new_headers
89
+
90
+ @property
91
+ def unverifiable(self):
92
+ return self.is_unverifiable()
93
+
94
+ @property
95
+ def origin_req_host(self):
96
+ return self.get_origin_req_host()
97
+
98
+ @property
99
+ def host(self):
100
+ return self.get_host()
101
+
102
+
103
+ class MockResponse:
104
+ """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.
105
+
106
+ ...what? Basically, expose the parsed HTTP headers from the server response
107
+ the way `http.cookiejar` expects to see them.
108
+ """
109
+
110
+ def __init__(self, headers):
111
+ """Make a MockResponse for `cookiejar` to read.
112
+
113
+ :param headers: a httplib.HTTPMessage or analogous carrying the headers
114
+ """
115
+ self._headers = headers
116
+
117
+ def info(self):
118
+ return self._headers
119
+
120
+ def getheaders(self, name):
121
+ self._headers.getheaders(name)
122
+
123
+
124
+ def extract_cookies_to_jar(jar, request, response):
125
+ """Extract the cookies from the response into a CookieJar.
126
+
127
+ :param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar)
128
+ :param request: our own requests.Request object
129
+ :param response: urllib3.HTTPResponse object
130
+ """
131
+ if not (hasattr(response, "_original_response") and response._original_response):
132
+ return
133
+ # the _original_response field is the wrapped httplib.HTTPResponse object,
134
+ req = MockRequest(request)
135
+ # pull out the HTTPMessage with the headers and put it in the mock:
136
+ res = MockResponse(response._original_response.msg)
137
+ jar.extract_cookies(res, req)
138
+
139
+
140
+ def get_cookie_header(jar, request):
141
+ """
142
+ Produce an appropriate Cookie header string to be sent with `request`, or None.
143
+
144
+ :rtype: str
145
+ """
146
+ r = MockRequest(request)
147
+ jar.add_cookie_header(r)
148
+ return r.get_new_headers().get("Cookie")
149
+
150
+
151
+ def remove_cookie_by_name(cookiejar, name, domain=None, path=None):
152
+ """Unsets a cookie by name, by default over all domains and paths.
153
+
154
+ Wraps CookieJar.clear(), is O(n).
155
+ """
156
+ clearables = []
157
+ for cookie in cookiejar:
158
+ if cookie.name != name:
159
+ continue
160
+ if domain is not None and domain != cookie.domain:
161
+ continue
162
+ if path is not None and path != cookie.path:
163
+ continue
164
+ clearables.append((cookie.domain, cookie.path, cookie.name))
165
+
166
+ for domain, path, name in clearables:
167
+ cookiejar.clear(domain, path, name)
168
+
169
+
170
+ class CookieConflictError(RuntimeError):
171
+ """There are two cookies that meet the criteria specified in the cookie jar.
172
+ Use .get and .set and include domain and path args in order to be more specific.
173
+ """
174
+
175
+
176
+ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
177
+ """Compatibility class; is a http.cookiejar.CookieJar, but exposes a dict
178
+ interface.
179
+
180
+ This is the CookieJar we create by default for requests and sessions that
181
+ don't specify one, since some clients may expect response.cookies and
182
+ session.cookies to support dict operations.
183
+
184
+ Requests does not use the dict interface internally; it's just for
185
+ compatibility with external client code. All requests code should work
186
+ out of the box with externally provided instances of ``CookieJar``, e.g.
187
+ ``LWPCookieJar`` and ``FileCookieJar``.
188
+
189
+ Unlike a regular CookieJar, this class is pickleable.
190
+
191
+ .. warning:: dictionary operations that are normally O(1) may be O(n).
192
+ """
193
+
194
+ def get(self, name, default=None, domain=None, path=None):
195
+ """Dict-like get() that also supports optional domain and path args in
196
+ order to resolve naming collisions from using one cookie jar over
197
+ multiple domains.
198
+
199
+ .. warning:: operation is O(n), not O(1).
200
+ """
201
+ try:
202
+ return self._find_no_duplicates(name, domain, path)
203
+ except KeyError:
204
+ return default
205
+
206
+ def set(self, name, value, **kwargs):
207
+ """Dict-like set() that also supports optional domain and path args in
208
+ order to resolve naming collisions from using one cookie jar over
209
+ multiple domains.
210
+ """
211
+ # support client code that unsets cookies by assignment of a None value:
212
+ if value is None:
213
+ remove_cookie_by_name(
214
+ self, name, domain=kwargs.get("domain"), path=kwargs.get("path")
215
+ )
216
+ return
217
+
218
+ if isinstance(value, Morsel):
219
+ c = morsel_to_cookie(value)
220
+ else:
221
+ c = create_cookie(name, value, **kwargs)
222
+ self.set_cookie(c)
223
+ return c
224
+
225
+ def iterkeys(self):
226
+ """Dict-like iterkeys() that returns an iterator of names of cookies
227
+ from the jar.
228
+
229
+ .. seealso:: itervalues() and iteritems().
230
+ """
231
+ for cookie in iter(self):
232
+ yield cookie.name
233
+
234
+ def keys(self):
235
+ """Dict-like keys() that returns a list of names of cookies from the
236
+ jar.
237
+
238
+ .. seealso:: values() and items().
239
+ """
240
+ return list(self.iterkeys())
241
+
242
+ def itervalues(self):
243
+ """Dict-like itervalues() that returns an iterator of values of cookies
244
+ from the jar.
245
+
246
+ .. seealso:: iterkeys() and iteritems().
247
+ """
248
+ for cookie in iter(self):
249
+ yield cookie.value
250
+
251
+ def values(self):
252
+ """Dict-like values() that returns a list of values of cookies from the
253
+ jar.
254
+
255
+ .. seealso:: keys() and items().
256
+ """
257
+ return list(self.itervalues())
258
+
259
+ def iteritems(self):
260
+ """Dict-like iteritems() that returns an iterator of name-value tuples
261
+ from the jar.
262
+
263
+ .. seealso:: iterkeys() and itervalues().
264
+ """
265
+ for cookie in iter(self):
266
+ yield cookie.name, cookie.value
267
+
268
+ def items(self):
269
+ """Dict-like items() that returns a list of name-value tuples from the
270
+ jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a
271
+ vanilla python dict of key value pairs.
272
+
273
+ .. seealso:: keys() and values().
274
+ """
275
+ return list(self.iteritems())
276
+
277
+ def list_domains(self):
278
+ """Utility method to list all the domains in the jar."""
279
+ domains = []
280
+ for cookie in iter(self):
281
+ if cookie.domain not in domains:
282
+ domains.append(cookie.domain)
283
+ return domains
284
+
285
+ def list_paths(self):
286
+ """Utility method to list all the paths in the jar."""
287
+ paths = []
288
+ for cookie in iter(self):
289
+ if cookie.path not in paths:
290
+ paths.append(cookie.path)
291
+ return paths
292
+
293
+ def multiple_domains(self):
294
+ """Returns True if there are multiple domains in the jar.
295
+ Returns False otherwise.
296
+
297
+ :rtype: bool
298
+ """
299
+ domains = []
300
+ for cookie in iter(self):
301
+ if cookie.domain is not None and cookie.domain in domains:
302
+ return True
303
+ domains.append(cookie.domain)
304
+ return False # there is only one domain in jar
305
+
306
+ def get_dict(self, domain=None, path=None):
307
+ """Takes as an argument an optional domain and path and returns a plain
308
+ old Python dict of name-value pairs of cookies that meet the
309
+ requirements.
310
+
311
+ :rtype: dict
312
+ """
313
+ dictionary = {}
314
+ for cookie in iter(self):
315
+ if (domain is None or cookie.domain == domain) and (
316
+ path is None or cookie.path == path
317
+ ):
318
+ dictionary[cookie.name] = cookie.value
319
+ return dictionary
320
+
321
+ def __contains__(self, name):
322
+ try:
323
+ return super().__contains__(name)
324
+ except CookieConflictError:
325
+ return True
326
+
327
+ def __getitem__(self, name):
328
+ """Dict-like __getitem__() for compatibility with client code. Throws
329
+ exception if there are more than one cookie with name. In that case,
330
+ use the more explicit get() method instead.
331
+
332
+ .. warning:: operation is O(n), not O(1).
333
+ """
334
+ return self._find_no_duplicates(name)
335
+
336
+ def __setitem__(self, name, value):
337
+ """Dict-like __setitem__ for compatibility with client code. Throws
338
+ exception if there is already a cookie of that name in the jar. In that
339
+ case, use the more explicit set() method instead.
340
+ """
341
+ self.set(name, value)
342
+
343
+ def __delitem__(self, name):
344
+ """Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s
345
+ ``remove_cookie_by_name()``.
346
+ """
347
+ remove_cookie_by_name(self, name)
348
+
349
+ def set_cookie(self, cookie, *args, **kwargs):
350
+ if (
351
+ hasattr(cookie.value, "startswith")
352
+ and cookie.value.startswith('"')
353
+ and cookie.value.endswith('"')
354
+ ):
355
+ cookie.value = cookie.value.replace('\\"', "")
356
+ return super().set_cookie(cookie, *args, **kwargs)
357
+
358
+ def update(self, other):
359
+ """Updates this jar with cookies from another CookieJar or dict-like"""
360
+ if isinstance(other, cookielib.CookieJar):
361
+ for cookie in other:
362
+ self.set_cookie(copy.copy(cookie))
363
+ else:
364
+ super().update(other)
365
+
366
+ def _find(self, name, domain=None, path=None):
367
+ """Requests uses this method internally to get cookie values.
368
+
369
+ If there are conflicting cookies, _find arbitrarily chooses one.
370
+ See _find_no_duplicates if you want an exception thrown if there are
371
+ conflicting cookies.
372
+
373
+ :param name: a string containing name of cookie
374
+ :param domain: (optional) string containing domain of cookie
375
+ :param path: (optional) string containing path of cookie
376
+ :return: cookie.value
377
+ """
378
+ for cookie in iter(self):
379
+ if cookie.name == name:
380
+ if domain is None or cookie.domain == domain:
381
+ if path is None or cookie.path == path:
382
+ return cookie.value
383
+
384
+ raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
385
+
386
+ def _find_no_duplicates(self, name, domain=None, path=None):
387
+ """Both ``__get_item__`` and ``get`` call this function: it's never
388
+ used elsewhere in Requests.
389
+
390
+ :param name: a string containing name of cookie
391
+ :param domain: (optional) string containing domain of cookie
392
+ :param path: (optional) string containing path of cookie
393
+ :raises KeyError: if cookie is not found
394
+ :raises CookieConflictError: if there are multiple cookies
395
+ that match name and optionally domain and path
396
+ :return: cookie.value
397
+ """
398
+ toReturn = None
399
+ for cookie in iter(self):
400
+ if cookie.name == name:
401
+ if domain is None or cookie.domain == domain:
402
+ if path is None or cookie.path == path:
403
+ if toReturn is not None:
404
+ # if there are multiple cookies that meet passed in criteria
405
+ raise CookieConflictError(
406
+ f"There are multiple cookies with name, {name!r}"
407
+ )
408
+ # we will eventually return this as long as no cookie conflict
409
+ toReturn = cookie.value
410
+
411
+ if toReturn:
412
+ return toReturn
413
+ raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
414
+
415
+ def __getstate__(self):
416
+ """Unlike a normal CookieJar, this class is pickleable."""
417
+ state = self.__dict__.copy()
418
+ # remove the unpickleable RLock object
419
+ state.pop("_cookies_lock")
420
+ return state
421
+
422
+ def __setstate__(self, state):
423
+ """Unlike a normal CookieJar, this class is pickleable."""
424
+ self.__dict__.update(state)
425
+ if "_cookies_lock" not in self.__dict__:
426
+ self._cookies_lock = threading.RLock()
427
+
428
+ def copy(self):
429
+ """Return a copy of this RequestsCookieJar."""
430
+ new_cj = RequestsCookieJar()
431
+ new_cj.set_policy(self.get_policy())
432
+ new_cj.update(self)
433
+ return new_cj
434
+
435
+ def get_policy(self):
436
+ """Return the CookiePolicy instance used."""
437
+ return self._policy
438
+
439
+
440
+ def _copy_cookie_jar(jar):
441
+ if jar is None:
442
+ return None
443
+
444
+ if hasattr(jar, "copy"):
445
+ # We're dealing with an instance of RequestsCookieJar
446
+ return jar.copy()
447
+ # We're dealing with a generic CookieJar instance
448
+ new_jar = copy.copy(jar)
449
+ new_jar.clear()
450
+ for cookie in jar:
451
+ new_jar.set_cookie(copy.copy(cookie))
452
+ return new_jar
453
+
454
+
455
+ def create_cookie(name, value, **kwargs):
456
+ """Make a cookie from underspecified parameters.
457
+
458
+ By default, the pair of `name` and `value` will be set for the domain ''
459
+ and sent on every request (this is sometimes called a "supercookie").
460
+ """
461
+ result = {
462
+ "version": 0,
463
+ "name": name,
464
+ "value": value,
465
+ "port": None,
466
+ "domain": "",
467
+ "path": "/",
468
+ "secure": False,
469
+ "expires": None,
470
+ "discard": True,
471
+ "comment": None,
472
+ "comment_url": None,
473
+ "rest": {"HttpOnly": None},
474
+ "rfc2109": False,
475
+ }
476
+
477
+ badargs = set(kwargs) - set(result)
478
+ if badargs:
479
+ raise TypeError(
480
+ f"create_cookie() got unexpected keyword arguments: {list(badargs)}"
481
+ )
482
+
483
+ result.update(kwargs)
484
+ result["port_specified"] = bool(result["port"])
485
+ result["domain_specified"] = bool(result["domain"])
486
+ result["domain_initial_dot"] = result["domain"].startswith(".")
487
+ result["path_specified"] = bool(result["path"])
488
+
489
+ return cookielib.Cookie(**result)
490
+
491
+
492
+ def morsel_to_cookie(morsel):
493
+ """Convert a Morsel object into a Cookie containing the one k/v pair."""
494
+
495
+ expires = None
496
+ if morsel["max-age"]:
497
+ try:
498
+ expires = int(time.time() + int(morsel["max-age"]))
499
+ except ValueError:
500
+ raise TypeError(f"max-age: {morsel['max-age']} must be integer")
501
+ elif morsel["expires"]:
502
+ time_template = "%a, %d-%b-%Y %H:%M:%S GMT"
503
+ expires = calendar.timegm(time.strptime(morsel["expires"], time_template))
504
+ return create_cookie(
505
+ comment=morsel["comment"],
506
+ comment_url=bool(morsel["comment"]),
507
+ discard=False,
508
+ domain=morsel["domain"],
509
+ expires=expires,
510
+ name=morsel.key,
511
+ path=morsel["path"],
512
+ port=None,
513
+ rest={"HttpOnly": morsel["httponly"]},
514
+ rfc2109=False,
515
+ secure=bool(morsel["secure"]),
516
+ value=morsel.value,
517
+ version=morsel["version"] or 0,
518
+ )
519
+
520
+
521
+ def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
522
+ """Returns a CookieJar from a key/value dictionary.
523
+
524
+ :param cookie_dict: Dict of key/values to insert into CookieJar.
525
+ :param cookiejar: (optional) A cookiejar to add the cookies to.
526
+ :param overwrite: (optional) If False, will not replace cookies
527
+ already in the jar with new ones.
528
+ :rtype: CookieJar
529
+ """
530
+ if cookiejar is None:
531
+ cookiejar = RequestsCookieJar()
532
+
533
+ if cookie_dict is not None:
534
+ names_from_jar = [cookie.name for cookie in cookiejar]
535
+ for name in cookie_dict:
536
+ if overwrite or (name not in names_from_jar):
537
+ cookiejar.set_cookie(create_cookie(name, cookie_dict[name]))
538
+
539
+ return cookiejar
540
+
541
+
542
+ def merge_cookies(cookiejar, cookies):
543
+ """Add cookies to cookiejar and returns a merged CookieJar.
544
+
545
+ :param cookiejar: CookieJar object to add the cookies to.
546
+ :param cookies: Dictionary or CookieJar object to be added.
547
+ :rtype: CookieJar
548
+ """
549
+ if not isinstance(cookiejar, cookielib.CookieJar):
550
+ raise ValueError("You can only merge into CookieJar")
551
+
552
+ if isinstance(cookies, dict):
553
+ cookiejar = cookiejar_from_dict(cookies, cookiejar=cookiejar, overwrite=False)
554
+ elif isinstance(cookies, cookielib.CookieJar):
555
+ try:
556
+ cookiejar.update(cookies)
557
+ except AttributeError:
558
+ for cookie_in_jar in cookies:
559
+ cookiejar.set_cookie(cookie_in_jar)
560
+
561
+ return cookiejar
.venv/Lib/site-packages/requests/exceptions.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ requests.exceptions
3
+ ~~~~~~~~~~~~~~~~~~~
4
+
5
+ This module contains the set of Requests' exceptions.
6
+ """
7
+ from urllib3.exceptions import HTTPError as BaseHTTPError
8
+
9
+ from .compat import JSONDecodeError as CompatJSONDecodeError
10
+
11
+
12
+ class RequestException(IOError):
13
+ """There was an ambiguous exception that occurred while handling your
14
+ request.
15
+ """
16
+
17
+ def __init__(self, *args, **kwargs):
18
+ """Initialize RequestException with `request` and `response` objects."""
19
+ response = kwargs.pop("response", None)
20
+ self.response = response
21
+ self.request = kwargs.pop("request", None)
22
+ if response is not None and not self.request and hasattr(response, "request"):
23
+ self.request = self.response.request
24
+ super().__init__(*args, **kwargs)
25
+
26
+
27
+ class InvalidJSONError(RequestException):
28
+ """A JSON error occurred."""
29
+
30
+
31
+ class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError):
32
+ """Couldn't decode the text into json"""
33
+
34
+ def __init__(self, *args, **kwargs):
35
+ """
36
+ Construct the JSONDecodeError instance first with all
37
+ args. Then use it's args to construct the IOError so that
38
+ the json specific args aren't used as IOError specific args
39
+ and the error message from JSONDecodeError is preserved.
40
+ """
41
+ CompatJSONDecodeError.__init__(self, *args)
42
+ InvalidJSONError.__init__(self, *self.args, **kwargs)
43
+
44
+ def __reduce__(self):
45
+ """
46
+ The __reduce__ method called when pickling the object must
47
+ be the one from the JSONDecodeError (be it json/simplejson)
48
+ as it expects all the arguments for instantiation, not just
49
+ one like the IOError, and the MRO would by default call the
50
+ __reduce__ method from the IOError due to the inheritance order.
51
+ """
52
+ return CompatJSONDecodeError.__reduce__(self)
53
+
54
+
55
+ class HTTPError(RequestException):
56
+ """An HTTP error occurred."""
57
+
58
+
59
+ class ConnectionError(RequestException):
60
+ """A Connection error occurred."""
61
+
62
+
63
+ class ProxyError(ConnectionError):
64
+ """A proxy error occurred."""
65
+
66
+
67
+ class SSLError(ConnectionError):
68
+ """An SSL error occurred."""
69
+
70
+
71
+ class Timeout(RequestException):
72
+ """The request timed out.
73
+
74
+ Catching this error will catch both
75
+ :exc:`~requests.exceptions.ConnectTimeout` and
76
+ :exc:`~requests.exceptions.ReadTimeout` errors.
77
+ """
78
+
79
+
80
+ class ConnectTimeout(ConnectionError, Timeout):
81
+ """The request timed out while trying to connect to the remote server.
82
+
83
+ Requests that produced this error are safe to retry.
84
+ """
85
+
86
+
87
+ class ReadTimeout(Timeout):
88
+ """The server did not send any data in the allotted amount of time."""
89
+
90
+
91
+ class URLRequired(RequestException):
92
+ """A valid URL is required to make a request."""
93
+
94
+
95
+ class TooManyRedirects(RequestException):
96
+ """Too many redirects."""
97
+
98
+
99
+ class MissingSchema(RequestException, ValueError):
100
+ """The URL scheme (e.g. http or https) is missing."""
101
+
102
+
103
+ class InvalidSchema(RequestException, ValueError):
104
+ """The URL scheme provided is either invalid or unsupported."""
105
+
106
+
107
+ class InvalidURL(RequestException, ValueError):
108
+ """The URL provided was somehow invalid."""
109
+
110
+
111
+ class InvalidHeader(RequestException, ValueError):
112
+ """The header value provided was somehow invalid."""
113
+
114
+
115
+ class InvalidProxyURL(InvalidURL):
116
+ """The proxy URL provided is invalid."""
117
+
118
+
119
+ class ChunkedEncodingError(RequestException):
120
+ """The server declared chunked encoding but sent an invalid chunk."""
121
+
122
+
123
+ class ContentDecodingError(RequestException, BaseHTTPError):
124
+ """Failed to decode response content."""
125
+
126
+
127
+ class StreamConsumedError(RequestException, TypeError):
128
+ """The content for this response was already consumed."""
129
+
130
+
131
+ class RetryError(RequestException):
132
+ """Custom retries logic failed"""
133
+
134
+
135
+ class UnrewindableBodyError(RequestException):
136
+ """Requests encountered an error when trying to rewind a body."""
137
+
138
+
139
+ # Warnings
140
+
141
+
142
+ class RequestsWarning(Warning):
143
+ """Base warning for Requests."""
144
+
145
+
146
+ class FileModeWarning(RequestsWarning, DeprecationWarning):
147
+ """A file was opened in text mode, but Requests determined its binary length."""
148
+
149
+
150
+ class RequestsDependencyWarning(RequestsWarning):
151
+ """An imported dependency doesn't match the expected version range."""
.venv/Lib/site-packages/requests/help.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module containing bug report helper(s)."""
2
+
3
+ import json
4
+ import platform
5
+ import ssl
6
+ import sys
7
+
8
+ import idna
9
+ import urllib3
10
+
11
+ from . import __version__ as requests_version
12
+
13
+ try:
14
+ import charset_normalizer
15
+ except ImportError:
16
+ charset_normalizer = None
17
+
18
+ try:
19
+ import chardet
20
+ except ImportError:
21
+ chardet = None
22
+
23
+ try:
24
+ from urllib3.contrib import pyopenssl
25
+ except ImportError:
26
+ pyopenssl = None
27
+ OpenSSL = None
28
+ cryptography = None
29
+ else:
30
+ import cryptography
31
+ import OpenSSL
32
+
33
+
34
+ def _implementation():
35
+ """Return a dict with the Python implementation and version.
36
+
37
+ Provide both the name and the version of the Python implementation
38
+ currently running. For example, on CPython 3.10.3 it will return
39
+ {'name': 'CPython', 'version': '3.10.3'}.
40
+
41
+ This function works best on CPython and PyPy: in particular, it probably
42
+ doesn't work for Jython or IronPython. Future investigation should be done
43
+ to work out the correct shape of the code for those platforms.
44
+ """
45
+ implementation = platform.python_implementation()
46
+
47
+ if implementation == "CPython":
48
+ implementation_version = platform.python_version()
49
+ elif implementation == "PyPy":
50
+ implementation_version = "{}.{}.{}".format(
51
+ sys.pypy_version_info.major,
52
+ sys.pypy_version_info.minor,
53
+ sys.pypy_version_info.micro,
54
+ )
55
+ if sys.pypy_version_info.releaselevel != "final":
56
+ implementation_version = "".join(
57
+ [implementation_version, sys.pypy_version_info.releaselevel]
58
+ )
59
+ elif implementation == "Jython":
60
+ implementation_version = platform.python_version() # Complete Guess
61
+ elif implementation == "IronPython":
62
+ implementation_version = platform.python_version() # Complete Guess
63
+ else:
64
+ implementation_version = "Unknown"
65
+
66
+ return {"name": implementation, "version": implementation_version}
67
+
68
+
69
+ def info():
70
+ """Generate information for a bug report."""
71
+ try:
72
+ platform_info = {
73
+ "system": platform.system(),
74
+ "release": platform.release(),
75
+ }
76
+ except OSError:
77
+ platform_info = {
78
+ "system": "Unknown",
79
+ "release": "Unknown",
80
+ }
81
+
82
+ implementation_info = _implementation()
83
+ urllib3_info = {"version": urllib3.__version__}
84
+ charset_normalizer_info = {"version": None}
85
+ chardet_info = {"version": None}
86
+ if charset_normalizer:
87
+ charset_normalizer_info = {"version": charset_normalizer.__version__}
88
+ if chardet:
89
+ chardet_info = {"version": chardet.__version__}
90
+
91
+ pyopenssl_info = {
92
+ "version": None,
93
+ "openssl_version": "",
94
+ }
95
+ if OpenSSL:
96
+ pyopenssl_info = {
97
+ "version": OpenSSL.__version__,
98
+ "openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}",
99
+ }
100
+ cryptography_info = {
101
+ "version": getattr(cryptography, "__version__", ""),
102
+ }
103
+ idna_info = {
104
+ "version": getattr(idna, "__version__", ""),
105
+ }
106
+
107
+ system_ssl = ssl.OPENSSL_VERSION_NUMBER
108
+ system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""}
109
+
110
+ return {
111
+ "platform": platform_info,
112
+ "implementation": implementation_info,
113
+ "system_ssl": system_ssl_info,
114
+ "using_pyopenssl": pyopenssl is not None,
115
+ "using_charset_normalizer": chardet is None,
116
+ "pyOpenSSL": pyopenssl_info,
117
+ "urllib3": urllib3_info,
118
+ "chardet": chardet_info,
119
+ "charset_normalizer": charset_normalizer_info,
120
+ "cryptography": cryptography_info,
121
+ "idna": idna_info,
122
+ "requests": {
123
+ "version": requests_version,
124
+ },
125
+ }
126
+
127
+
128
+ def main():
129
+ """Pretty-print the bug information as JSON."""
130
+ print(json.dumps(info(), sort_keys=True, indent=2))
131
+
132
+
133
+ if __name__ == "__main__":
134
+ main()