From a5a460e7652b3e4edb1ed19a0c6dc7d277f8956b Mon Sep 17 00:00:00 2001 From: Daniel Bedeleanu Date: Thu, 23 Apr 2026 13:58:33 +0300 Subject: [PATCH] feat(08): implement SSOT architecture for network configuration --- .standalone.pid | 3 + backend/config_loader.py | 33 +- backups/ainventory_2026-04-23_12-58-31.tar.gz | Bin 0 -> 29151 bytes config/Caddyfile.standalone | 41 + config/README.md | 7 +- config/backend.yaml.example | 13 - scripts/deploy.py | 13 + scripts/run_standalone.py | 341 +- venv/bin/activate | 4 +- venv/bin/activate.csh | 2 +- venv/bin/activate.fish | 2 +- venv/bin/coverage | 6 +- venv/bin/coverage-3.12 | 6 +- venv/bin/coverage3 | 6 +- venv/bin/distro | 6 +- venv/bin/dotenv | 6 +- venv/bin/f2py | 6 +- venv/bin/fastapi | 6 +- venv/bin/httpx | 6 +- venv/bin/normalizer | 6 +- venv/bin/numpy-config | 6 +- venv/bin/pip | 2 +- venv/bin/pip3 | 2 +- venv/bin/pip3.12 | 2 +- venv/bin/py.test | 6 +- venv/bin/pygmentize | 6 +- venv/bin/pyrsa-decrypt | 6 +- venv/bin/pyrsa-encrypt | 6 +- venv/bin/pyrsa-keygen | 6 +- venv/bin/pyrsa-priv2pub | 6 +- venv/bin/pyrsa-sign | 6 +- venv/bin/pyrsa-verify | 6 +- venv/bin/pytest | 6 +- venv/bin/uvicorn | 6 +- venv/bin/watchfiles | 6 +- venv/bin/websockets | 6 +- .../INSTALLER | 0 .../aiohappyeyeballs-2.6.1.dist-info/LICENSE | 279 + .../aiohappyeyeballs-2.6.1.dist-info/METADATA | 123 + .../aiohappyeyeballs-2.6.1.dist-info/RECORD | 16 + .../aiohappyeyeballs-2.6.1.dist-info/WHEEL | 4 + .../aiohappyeyeballs/__init__.py | 14 + .../aiohappyeyeballs/_staggered.py | 207 + .../site-packages/aiohappyeyeballs/impl.py | 259 + .../REQUESTED => aiohappyeyeballs/py.typed} | 0 .../site-packages/aiohappyeyeballs/types.py | 17 + .../site-packages/aiohappyeyeballs/utils.py | 97 + .../INSTALLER | 0 .../aiohttp-3.13.5.dist-info/METADATA | 262 + .../aiohttp-3.13.5.dist-info/RECORD | 139 + .../REQUESTED | 0 .../aiohttp-3.13.5.dist-info/WHEEL | 7 + .../licenses/LICENSE.txt | 13 + .../licenses/vendor/llhttp/LICENSE | 22 + .../aiohttp-3.13.5.dist-info/top_level.txt | 1 + .../aiohttp/.hash/_cparser.pxd.hash | 1 + .../aiohttp/.hash/_find_header.pxd.hash | 1 + .../aiohttp/.hash/_http_parser.pyx.hash | 1 + .../aiohttp/.hash/_http_writer.pyx.hash | 1 + .../site-packages/aiohttp/.hash/hdrs.py.hash | 1 + .../site-packages/aiohttp/__init__.py | 278 + .../site-packages/aiohttp/_cookie_helpers.py | 338 + .../site-packages/aiohttp/_cparser.pxd | 158 + .../site-packages/aiohttp/_find_header.pxd | 2 + .../site-packages/aiohttp/_headers.pxi | 83 + ...ttp_parser.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 2947720 bytes .../site-packages/aiohttp/_http_parser.pyx | 877 + ...ttp_writer.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 472496 bytes .../site-packages/aiohttp/_http_writer.pyx | 162 + .../aiohttp/_websocket/.hash/mask.pxd.hash | 1 + .../aiohttp/_websocket/.hash/mask.pyx.hash | 1 + .../_websocket/.hash/reader_c.pxd.hash | 1 + .../aiohttp/_websocket/__init__.py | 1 + .../aiohttp/_websocket/helpers.py | 147 + .../mask.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 263512 bytes .../site-packages/aiohttp/_websocket/mask.pxd | 3 + .../site-packages/aiohttp/_websocket/mask.pyx | 48 + .../aiohttp/_websocket/models.py | 84 + .../aiohttp/_websocket/reader.py | 31 + .../reader_c.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 1822128 bytes .../aiohttp/_websocket/reader_c.pxd | 110 + .../aiohttp/_websocket/reader_c.py | 478 + .../aiohttp/_websocket/reader_py.py | 478 + .../aiohttp/_websocket/writer.py | 262 + .../python3.12/site-packages/aiohttp/abc.py | 268 + .../site-packages/aiohttp/base_protocol.py | 100 + .../site-packages/aiohttp/client.py | 1646 ++ .../aiohttp/client_exceptions.py | 432 + .../aiohttp/client_middleware_digest_auth.py | 480 + .../aiohttp/client_middlewares.py | 55 + .../site-packages/aiohttp/client_proto.py | 361 + .../site-packages/aiohttp/client_reqrep.py | 1536 ++ .../site-packages/aiohttp/client_ws.py | 428 + .../aiohttp/compression_utils.py | 348 + .../site-packages/aiohttp/connector.py | 1854 ++ .../site-packages/aiohttp/cookiejar.py | 522 + .../site-packages/aiohttp/formdata.py | 184 + .../python3.12/site-packages/aiohttp/hdrs.py | 121 + .../site-packages/aiohttp/helpers.py | 986 + .../python3.12/site-packages/aiohttp/http.py | 72 + .../site-packages/aiohttp/http_exceptions.py | 117 + .../site-packages/aiohttp/http_parser.py | 1125 + .../site-packages/aiohttp/http_websocket.py | 36 + .../site-packages/aiohttp/http_writer.py | 379 + .../python3.12/site-packages/aiohttp/log.py | 8 + .../site-packages/aiohttp/multipart.py | 1213 ++ .../site-packages/aiohttp/payload.py | 1120 + .../site-packages/aiohttp/payload_streamer.py | 78 + .../python3.12/site-packages/aiohttp/py.typed | 1 + .../site-packages/aiohttp/pytest_plugin.py | 444 + .../site-packages/aiohttp/resolver.py | 274 + .../site-packages/aiohttp/streams.py | 762 + .../site-packages/aiohttp/tcp_helpers.py | 37 + .../site-packages/aiohttp/test_utils.py | 777 + .../site-packages/aiohttp/tracing.py | 455 + .../site-packages/aiohttp/typedefs.py | 69 + .../python3.12/site-packages/aiohttp/web.py | 592 + .../site-packages/aiohttp/web_app.py | 620 + .../site-packages/aiohttp/web_exceptions.py | 454 + .../site-packages/aiohttp/web_fileresponse.py | 418 + .../site-packages/aiohttp/web_log.py | 234 + .../site-packages/aiohttp/web_middlewares.py | 121 + .../site-packages/aiohttp/web_protocol.py | 799 + .../site-packages/aiohttp/web_request.py | 927 + .../site-packages/aiohttp/web_response.py | 856 + .../site-packages/aiohttp/web_routedef.py | 214 + .../site-packages/aiohttp/web_runner.py | 399 + .../site-packages/aiohttp/web_server.py | 84 + .../aiohttp/web_urldispatcher.py | 1309 ++ .../site-packages/aiohttp/web_ws.py | 631 + .../site-packages/aiohttp/worker.py | 262 + .../INSTALLER | 0 .../aiosignal-1.4.0.dist-info/METADATA | 112 + .../aiosignal-1.4.0.dist-info/RECORD | 9 + .../WHEEL | 2 +- .../licenses/LICENSE | 201 + .../aiosignal-1.4.0.dist-info/top_level.txt | 1 + .../site-packages/aiosignal/__init__.py | 59 + .../cli/__init__.py => aiosignal/py.typed} | 0 .../python3.12/site-packages/attr/__init__.py | 104 + .../site-packages/attr/__init__.pyi | 389 + .../lib/python3.12/site-packages/attr/_cmp.py | 160 + .../python3.12/site-packages/attr/_cmp.pyi | 13 + .../python3.12/site-packages/attr/_compat.py | 99 + .../python3.12/site-packages/attr/_config.py | 31 + .../python3.12/site-packages/attr/_funcs.py | 497 + .../python3.12/site-packages/attr/_make.py | 3406 +++ .../site-packages/attr/_next_gen.py | 674 + .../site-packages/attr/_typing_compat.pyi | 15 + .../site-packages/attr/_version_info.py | 89 + .../site-packages/attr/_version_info.pyi | 9 + .../site-packages/attr/converters.py | 162 + .../site-packages/attr/converters.pyi | 19 + .../site-packages/attr/exceptions.py | 95 + .../site-packages/attr/exceptions.pyi | 17 + .../python3.12/site-packages/attr/filters.py | 72 + .../python3.12/site-packages/attr/filters.pyi | 6 + .../metadata/__init__.py => attr/py.typed} | 0 .../python3.12/site-packages/attr/setters.py | 79 + .../python3.12/site-packages/attr/setters.pyi | 20 + .../site-packages/attr/validators.py | 750 + .../site-packages/attr/validators.pyi | 140 + .../INSTALLER | 0 .../attrs-26.1.0.dist-info/METADATA | 199 + .../attrs-26.1.0.dist-info/RECORD | 55 + .../WHEEL | 0 .../attrs-26.1.0.dist-info/licenses/LICENSE | 21 + .../site-packages/attrs/__init__.py | 72 + .../site-packages/attrs/__init__.pyi | 314 + .../site-packages/attrs/converters.py | 3 + .../site-packages/attrs/exceptions.py | 3 + .../python3.12/site-packages/attrs/filters.py | 3 + .../compat/__init__.py => attrs/py.typed} | 0 .../python3.12/site-packages/attrs/setters.py | 3 + .../site-packages/attrs/validators.py | 3 + .../INSTALLER | 0 .../beautifulsoup4-4.14.3.dist-info/METADATA | 123 + .../beautifulsoup4-4.14.3.dist-info/RECORD | 38 + .../REQUESTED | 0 .../beautifulsoup4-4.14.3.dist-info/WHEEL | 4 + .../licenses/AUTHORS | 49 + .../licenses/LICENSE | 31 + .../python3.12/site-packages/bs4/__init__.py | 1174 ++ .../site-packages/bs4/_deprecation.py | 80 + .../python3.12/site-packages/bs4/_typing.py | 205 + .../python3.12/site-packages/bs4/_warnings.py | 98 + .../site-packages/bs4/builder/__init__.py | 848 + .../site-packages/bs4/builder/_html5lib.py | 611 + .../site-packages/bs4/builder/_htmlparser.py | 462 + .../site-packages/bs4/builder/_lxml.py | 501 + venv/lib/python3.12/site-packages/bs4/css.py | 339 + .../python3.12/site-packages/bs4/dammit.py | 1516 ++ .../python3.12/site-packages/bs4/diagnose.py | 268 + .../python3.12/site-packages/bs4/element.py | 3211 +++ .../site-packages/bs4/exceptions.py | 28 + .../python3.12/site-packages/bs4/filter.py | 764 + .../python3.12/site-packages/bs4/formatter.py | 276 + .../lib/python3.12/site-packages/bs4/py.typed | 0 .../certifi-2026.2.25.dist-info/RECORD | 14 - .../INSTALLER} | 0 .../METADATA | 2 +- .../certifi-2026.4.22.dist-info/RECORD | 14 + .../WHEEL | 2 +- .../licenses/LICENSE | 0 .../top_level.txt | 0 .../site-packages/certifi/__init__.py | 2 +- .../charset_normalizer-3.4.7.dist-info/RECORD | 2 +- .../click-8.3.3.dist-info/INSTALLER | 1 + .../METADATA | 2 +- .../RECORD | 22 +- .../WHEEL | 0 .../licenses/LICENSE.txt | 0 .../site-packages/click/__init__.py | 1 + .../site-packages/click/_termui_impl.py | 44 +- .../python3.12/site-packages/click/core.py | 107 +- .../python3.12/site-packages/click/termui.py | 12 +- .../python3.12/site-packages/click/testing.py | 107 +- .../python3.12/site-packages/click/utils.py | 5 +- .../coverage-7.13.5.dist-info/RECORD | 6 +- .../distro-1.9.0.dist-info/RECORD | 2 +- .../et_xmlfile-2.0.0.dist-info/AUTHORS.txt | 5 + .../et_xmlfile-2.0.0.dist-info/INSTALLER | 1 + .../et_xmlfile-2.0.0.dist-info/LICENCE.python | 298 + .../et_xmlfile-2.0.0.dist-info/LICENCE.rst | 23 + .../et_xmlfile-2.0.0.dist-info/METADATA | 51 + .../et_xmlfile-2.0.0.dist-info/RECORD | 14 + .../et_xmlfile-2.0.0.dist-info/WHEEL | 5 + .../et_xmlfile-2.0.0.dist-info/top_level.txt | 1 + .../site-packages/et_xmlfile/__init__.py | 8 + .../et_xmlfile/incremental_tree.py | 917 + .../site-packages/et_xmlfile/xmlfile.py | 158 + .../fastapi-0.136.0.dist-info/RECORD | 2 +- .../frozenlist-1.8.0.dist-info/INSTALLER | 1 + .../frozenlist-1.8.0.dist-info/METADATA | 672 + .../frozenlist-1.8.0.dist-info/RECORD | 12 + .../frozenlist-1.8.0.dist-info/WHEEL | 7 + .../licenses/LICENSE | 201 + .../frozenlist-1.8.0.dist-info/top_level.txt | 1 + .../site-packages/frozenlist/__init__.py | 86 + .../site-packages/frozenlist/__init__.pyi | 47 + ...frozenlist.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 786872 bytes .../site-packages/frozenlist/_frozenlist.pyx | 148 + .../site-packages/frozenlist/py.typed | 1 + .../fuzzywuzzy-0.18.0.dist-info/INSTALLER | 1 + .../fuzzywuzzy-0.18.0.dist-info/LICENSE.txt | 340 + .../fuzzywuzzy-0.18.0.dist-info/METADATA | 163 + .../fuzzywuzzy-0.18.0.dist-info/RECORD | 20 + .../fuzzywuzzy-0.18.0.dist-info/REQUESTED | 0 .../fuzzywuzzy-0.18.0.dist-info/WHEEL | 6 + .../fuzzywuzzy-0.18.0.dist-info/top_level.txt | 1 + .../fuzzywuzzy-0.18.0.dist-info/zip-safe | 1 + .../site-packages/fuzzywuzzy/StringMatcher.py | 80 + .../site-packages/fuzzywuzzy/__init__.py | 2 + .../site-packages/fuzzywuzzy/fuzz.py | 306 + .../site-packages/fuzzywuzzy/process.py | 285 + .../fuzzywuzzy/string_processing.py | 30 + .../site-packages/fuzzywuzzy/utils.py | 105 + .../httpx-0.28.1.dist-info/RECORD | 2 +- .../idna-3.13.dist-info/INSTALLER | 1 + .../METADATA | 17 +- .../RECORD | 18 +- .../WHEEL | 0 .../idna-3.13.dist-info/licenses/LICENSE.md | 31 + .../lib/python3.12/site-packages/idna/core.py | 45 +- .../python3.12/site-packages/idna/idnadata.py | 6028 +++--- .../site-packages/idna/package_data.py | 2 +- .../site-packages/idna/uts46data.py | 17211 ++++++++-------- .../multidict-6.7.1.dist-info/INSTALLER | 1 + .../multidict-6.7.1.dist-info/METADATA | 149 + .../multidict-6.7.1.dist-info/RECORD | 16 + .../multidict-6.7.1.dist-info/WHEEL | 7 + .../licenses/LICENSE | 13 + .../multidict-6.7.1.dist-info/top_level.txt | 1 + .../site-packages/multidict/__init__.py | 60 + .../site-packages/multidict/_abc.py | 73 + .../site-packages/multidict/_compat.py | 15 + ..._multidict.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 847096 bytes .../site-packages/multidict/_multidict_py.py | 1242 ++ .../site-packages/multidict/py.typed | 1 + .../numpy-2.4.4.dist-info/RECORD | 4 +- .../openpyxl-3.1.5.dist-info/INSTALLER | 1 + .../openpyxl-3.1.5.dist-info/LICENCE.rst | 23 + .../openpyxl-3.1.5.dist-info/METADATA | 86 + .../openpyxl-3.1.5.dist-info/RECORD | 387 + .../openpyxl-3.1.5.dist-info/REQUESTED | 0 .../openpyxl-3.1.5.dist-info/WHEEL | 6 + .../openpyxl-3.1.5.dist-info/top_level.txt | 1 + .../site-packages/openpyxl/__init__.py | 19 + .../site-packages/openpyxl/_constants.py | 13 + .../site-packages/openpyxl/cell/__init__.py | 4 + .../site-packages/openpyxl/cell/_writer.py | 136 + .../site-packages/openpyxl/cell/cell.py | 332 + .../site-packages/openpyxl/cell/read_only.py | 136 + .../site-packages/openpyxl/cell/rich_text.py | 202 + .../site-packages/openpyxl/cell/text.py | 184 + .../site-packages/openpyxl/chart/_3d.py | 105 + .../site-packages/openpyxl/chart/__init__.py | 19 + .../site-packages/openpyxl/chart/_chart.py | 199 + .../openpyxl/chart/area_chart.py | 106 + .../site-packages/openpyxl/chart/axis.py | 401 + .../site-packages/openpyxl/chart/bar_chart.py | 144 + .../openpyxl/chart/bubble_chart.py | 67 + .../openpyxl/chart/chartspace.py | 195 + .../openpyxl/chart/data_source.py | 246 + .../openpyxl/chart/descriptors.py | 43 + .../site-packages/openpyxl/chart/error_bar.py | 62 + .../site-packages/openpyxl/chart/label.py | 127 + .../site-packages/openpyxl/chart/layout.py | 74 + .../site-packages/openpyxl/chart/legend.py | 75 + .../openpyxl/chart/line_chart.py | 129 + .../site-packages/openpyxl/chart/marker.py | 90 + .../site-packages/openpyxl/chart/picture.py | 35 + .../site-packages/openpyxl/chart/pie_chart.py | 177 + .../site-packages/openpyxl/chart/pivot.py | 65 + .../site-packages/openpyxl/chart/plotarea.py | 162 + .../openpyxl/chart/print_settings.py | 57 + .../openpyxl/chart/radar_chart.py | 55 + .../site-packages/openpyxl/chart/reader.py | 32 + .../site-packages/openpyxl/chart/reference.py | 124 + .../openpyxl/chart/scatter_chart.py | 53 + .../site-packages/openpyxl/chart/series.py | 197 + .../openpyxl/chart/series_factory.py | 41 + .../site-packages/openpyxl/chart/shapes.py | 89 + .../openpyxl/chart/stock_chart.py | 54 + .../openpyxl/chart/surface_chart.py | 119 + .../site-packages/openpyxl/chart/text.py | 78 + .../site-packages/openpyxl/chart/title.py | 76 + .../site-packages/openpyxl/chart/trendline.py | 98 + .../openpyxl/chart/updown_bars.py | 31 + .../openpyxl/chartsheet/__init__.py | 3 + .../openpyxl/chartsheet/chartsheet.py | 107 + .../openpyxl/chartsheet/custom.py | 61 + .../openpyxl/chartsheet/properties.py | 28 + .../openpyxl/chartsheet/protection.py | 41 + .../openpyxl/chartsheet/publish.py | 58 + .../openpyxl/chartsheet/relation.py | 97 + .../openpyxl/chartsheet/views.py | 51 + .../openpyxl/comments/__init__.py | 4 + .../site-packages/openpyxl/comments/author.py | 21 + .../openpyxl/comments/comment_sheet.py | 211 + .../openpyxl/comments/comments.py | 62 + .../openpyxl/comments/shape_writer.py | 112 + .../site-packages/openpyxl/compat/__init__.py | 54 + .../site-packages/openpyxl/compat/abc.py | 8 + .../site-packages/openpyxl/compat/numbers.py | 43 + .../site-packages/openpyxl/compat/product.py | 17 + .../openpyxl/compat/singleton.py | 40 + .../site-packages/openpyxl/compat/strings.py | 25 + .../openpyxl/descriptors/__init__.py | 58 + .../openpyxl/descriptors/base.py | 272 + .../openpyxl/descriptors/container.py | 41 + .../openpyxl/descriptors/excel.py | 112 + .../openpyxl/descriptors/namespace.py | 12 + .../openpyxl/descriptors/nested.py | 129 + .../openpyxl/descriptors/sequence.py | 136 + .../openpyxl/descriptors/serialisable.py | 240 + .../openpyxl/descriptors/slots.py | 18 + .../openpyxl/drawing/__init__.py | 4 + .../site-packages/openpyxl/drawing/colors.py | 435 + .../openpyxl/drawing/connector.py | 144 + .../site-packages/openpyxl/drawing/drawing.py | 92 + .../site-packages/openpyxl/drawing/effect.py | 407 + .../site-packages/openpyxl/drawing/fill.py | 425 + .../openpyxl/drawing/geometry.py | 584 + .../site-packages/openpyxl/drawing/graphic.py | 177 + .../site-packages/openpyxl/drawing/image.py | 65 + .../site-packages/openpyxl/drawing/line.py | 144 + .../site-packages/openpyxl/drawing/picture.py | 144 + .../openpyxl/drawing/properties.py | 174 + .../openpyxl/drawing/relation.py | 17 + .../openpyxl/drawing/spreadsheet_drawing.py | 382 + .../site-packages/openpyxl/drawing/text.py | 717 + .../site-packages/openpyxl/drawing/xdr.py | 33 + .../openpyxl/formatting/__init__.py | 3 + .../openpyxl/formatting/formatting.py | 114 + .../site-packages/openpyxl/formatting/rule.py | 291 + .../openpyxl/formula/__init__.py | 3 + .../openpyxl/formula/tokenizer.py | 446 + .../openpyxl/formula/translate.py | 166 + .../openpyxl/packaging/__init__.py | 3 + .../site-packages/openpyxl/packaging/core.py | 115 + .../openpyxl/packaging/custom.py | 289 + .../openpyxl/packaging/extended.py | 137 + .../openpyxl/packaging/interface.py | 56 + .../openpyxl/packaging/manifest.py | 194 + .../openpyxl/packaging/relationship.py | 158 + .../openpyxl/packaging/workbook.py | 185 + .../site-packages/openpyxl/pivot/__init__.py | 1 + .../site-packages/openpyxl/pivot/cache.py | 965 + .../site-packages/openpyxl/pivot/fields.py | 326 + .../site-packages/openpyxl/pivot/record.py | 111 + .../site-packages/openpyxl/pivot/table.py | 1261 ++ .../site-packages/openpyxl/reader/__init__.py | 1 + .../site-packages/openpyxl/reader/drawings.py | 71 + .../site-packages/openpyxl/reader/excel.py | 349 + .../site-packages/openpyxl/reader/strings.py | 44 + .../site-packages/openpyxl/reader/workbook.py | 133 + .../site-packages/openpyxl/styles/__init__.py | 11 + .../openpyxl/styles/alignment.py | 62 + .../site-packages/openpyxl/styles/borders.py | 103 + .../site-packages/openpyxl/styles/builtins.py | 1397 ++ .../openpyxl/styles/cell_style.py | 206 + .../site-packages/openpyxl/styles/colors.py | 172 + .../openpyxl/styles/differential.py | 95 + .../site-packages/openpyxl/styles/fills.py | 224 + .../site-packages/openpyxl/styles/fonts.py | 113 + .../openpyxl/styles/named_styles.py | 282 + .../site-packages/openpyxl/styles/numbers.py | 200 + .../openpyxl/styles/protection.py | 17 + .../site-packages/openpyxl/styles/proxy.py | 62 + .../openpyxl/styles/styleable.py | 151 + .../openpyxl/styles/stylesheet.py | 274 + .../site-packages/openpyxl/styles/table.py | 94 + .../site-packages/openpyxl/utils/__init__.py | 17 + .../openpyxl/utils/bound_dictionary.py | 26 + .../site-packages/openpyxl/utils/cell.py | 240 + .../site-packages/openpyxl/utils/dataframe.py | 87 + .../site-packages/openpyxl/utils/datetime.py | 140 + .../site-packages/openpyxl/utils/escape.py | 43 + .../openpyxl/utils/exceptions.py | 34 + .../site-packages/openpyxl/utils/formulas.py | 24 + .../openpyxl/utils/indexed_list.py | 49 + .../site-packages/openpyxl/utils/inference.py | 60 + .../openpyxl/utils/protection.py | 22 + .../site-packages/openpyxl/utils/units.py | 108 + .../openpyxl/workbook/__init__.py | 4 + .../openpyxl/workbook/_writer.py | 197 + .../site-packages/openpyxl/workbook/child.py | 166 + .../openpyxl/workbook/defined_name.py | 189 + .../workbook/external_link/__init__.py | 3 + .../workbook/external_link/external.py | 190 + .../openpyxl/workbook/external_reference.py | 18 + .../openpyxl/workbook/function_group.py | 36 + .../openpyxl/workbook/properties.py | 151 + .../openpyxl/workbook/protection.py | 163 + .../openpyxl/workbook/smart_tags.py | 56 + .../site-packages/openpyxl/workbook/views.py | 155 + .../site-packages/openpyxl/workbook/web.py | 98 + .../openpyxl/workbook/workbook.py | 438 + .../openpyxl/worksheet/__init__.py | 1 + .../openpyxl/worksheet/_read_only.py | 190 + .../openpyxl/worksheet/_reader.py | 472 + .../openpyxl/worksheet/_write_only.py | 160 + .../openpyxl/worksheet/_writer.py | 390 + .../openpyxl/worksheet/cell_range.py | 512 + .../openpyxl/worksheet/cell_watch.py | 34 + .../openpyxl/worksheet/controls.py | 107 + .../openpyxl/worksheet/copier.py | 70 + .../openpyxl/worksheet/custom.py | 35 + .../openpyxl/worksheet/datavalidation.py | 202 + .../openpyxl/worksheet/dimensions.py | 306 + .../openpyxl/worksheet/drawing.py | 14 + .../openpyxl/worksheet/errors.py | 93 + .../openpyxl/worksheet/filters.py | 486 + .../openpyxl/worksheet/formula.py | 51 + .../openpyxl/worksheet/header_footer.py | 270 + .../openpyxl/worksheet/hyperlink.py | 46 + .../site-packages/openpyxl/worksheet/merge.py | 141 + .../site-packages/openpyxl/worksheet/ole.py | 133 + .../site-packages/openpyxl/worksheet/page.py | 174 + .../openpyxl/worksheet/pagebreak.py | 94 + .../openpyxl/worksheet/picture.py | 8 + .../openpyxl/worksheet/print_settings.py | 184 + .../openpyxl/worksheet/properties.py | 97 + .../openpyxl/worksheet/protection.py | 120 + .../openpyxl/worksheet/related.py | 17 + .../openpyxl/worksheet/scenario.py | 105 + .../openpyxl/worksheet/smart_tag.py | 78 + .../site-packages/openpyxl/worksheet/table.py | 385 + .../site-packages/openpyxl/worksheet/views.py | 155 + .../openpyxl/worksheet/worksheet.py | 907 + .../site-packages/openpyxl/writer/__init__.py | 1 + .../site-packages/openpyxl/writer/excel.py | 295 + .../site-packages/openpyxl/writer/theme.py | 291 + .../site-packages/openpyxl/xml/__init__.py | 42 + .../site-packages/openpyxl/xml/constants.py | 129 + .../site-packages/openpyxl/xml/functions.py | 87 + .../site-packages/pip-24.0.dist-info/RECORD | 1005 - .../pip-24.0.dist-info/entry_points.txt | 4 - .../pip-26.0.1.dist-info/INSTALLER | 1 + .../METADATA | 57 +- .../site-packages/pip-26.0.1.dist-info/RECORD | 878 + .../pip-26.0.1.dist-info/REQUESTED | 0 .../site-packages/pip-26.0.1.dist-info/WHEEL | 4 + .../pip-26.0.1.dist-info/entry_points.txt | 4 + .../licenses}/AUTHORS.txt | 106 +- .../licenses}/LICENSE.txt | 0 .../src/pip/_vendor/cachecontrol/LICENSE.txt | 13 + .../licenses/src/pip/_vendor/certifi/LICENSE | 20 + .../pip/_vendor/dependency_groups/LICENSE.txt | 9 + .../src/pip/_vendor/distlib/LICENSE.txt | 284 + .../licenses/src/pip/_vendor/distro/LICENSE | 202 + .../licenses/src/pip/_vendor/idna}/LICENSE.md | 0 .../licenses/src/pip/_vendor/msgpack/COPYING | 14 + .../src/pip/_vendor/packaging/LICENSE | 3 + .../src/pip/_vendor/packaging/LICENSE.APACHE | 177 + .../src/pip/_vendor/packaging/LICENSE.BSD | 23 + .../src/pip/_vendor/pkg_resources/LICENSE | 17 + .../src/pip/_vendor/platformdirs/LICENSE | 21 + .../licenses/src/pip/_vendor/pygments/LICENSE | 25 + .../src/pip/_vendor/pyproject_hooks/LICENSE | 21 + .../licenses/src/pip/_vendor/requests/LICENSE | 175 + .../src/pip/_vendor/resolvelib/LICENSE | 13 + .../licenses/src/pip/_vendor/rich/LICENSE | 19 + .../licenses/src/pip/_vendor/tomli/LICENSE | 21 + .../licenses/src/pip/_vendor/tomli_w/LICENSE | 21 + .../src/pip/_vendor/truststore/LICENSE | 21 + .../src/pip/_vendor/urllib3/LICENSE.txt | 21 + .../python3.12/site-packages/pip/__init__.py | 6 +- .../site-packages/pip/__pip-runner__.py | 4 +- .../site-packages/pip/_internal/__init__.py | 4 +- .../site-packages/pip/_internal/build_env.py | 465 +- .../site-packages/pip/_internal/cache.py | 37 +- .../pip/_internal/cli/__init__.py | 3 +- .../pip/_internal/cli/autocompletion.py | 24 +- .../pip/_internal/cli/base_command.py | 157 +- .../pip/_internal/cli/cmdoptions.py | 369 +- .../pip/_internal/cli/command_context.py | 7 +- .../pip/_internal/cli/index_command.py | 195 + .../site-packages/pip/_internal/cli/main.py | 26 +- .../pip/_internal/cli/main_parser.py | 14 +- .../site-packages/pip/_internal/cli/parser.py | 152 +- .../pip/_internal/cli/progress_bars.py | 113 +- .../pip/_internal/cli/req_command.py | 368 +- .../pip/_internal/cli/spinners.py | 86 +- .../pip/_internal/commands/__init__.py | 13 +- .../pip/_internal/commands/cache.py | 62 +- .../pip/_internal/commands/check.py | 24 +- .../pip/_internal/commands/completion.py | 22 +- .../pip/_internal/commands/configuration.py | 44 +- .../pip/_internal/commands/debug.py | 18 +- .../pip/_internal/commands/download.py | 29 +- .../pip/_internal/commands/freeze.py | 8 +- .../pip/_internal/commands/hash.py | 3 +- .../pip/_internal/commands/help.py | 3 +- .../pip/_internal/commands/index.py | 83 +- .../pip/_internal/commands/inspect.py | 10 +- .../pip/_internal/commands/install.py | 196 +- .../pip/_internal/commands/list.py | 124 +- .../pip/_internal/commands/lock.py | 175 + .../pip/_internal/commands/search.py | 48 +- .../pip/_internal/commands/show.py | 80 +- .../pip/_internal/commands/uninstall.py | 6 +- .../pip/_internal/commands/wheel.py | 46 +- .../pip/_internal/configuration.py | 69 +- .../pip/_internal/distributions/base.py | 14 +- .../pip/_internal/distributions/installed.py | 12 +- .../pip/_internal/distributions/sdist.py | 64 +- .../pip/_internal/distributions/wheel.py | 12 +- .../site-packages/pip/_internal/exceptions.py | 309 +- .../pip/_internal/index/__init__.py | 3 +- .../pip/_internal/index/collector.py | 101 +- .../pip/_internal/index/package_finder.py | 374 +- .../pip/_internal/index/sources.py | 36 +- .../pip/_internal/locations/__init__.py | 63 +- .../pip/_internal/locations/_distutils.py | 25 +- .../pip/_internal/locations/_sysconfig.py | 19 +- .../pip/_internal/locations/base.py | 7 +- .../site-packages/pip/_internal/main.py | 4 +- .../pip/_internal/metadata/__init__.py | 77 +- .../pip/_internal/metadata/_json.py | 13 +- .../pip/_internal/metadata/base.py | 109 +- .../_internal/metadata/importlib/_compat.py | 44 +- .../_internal/metadata/importlib/_dists.py | 94 +- .../pip/_internal/metadata/importlib/_envs.py | 98 +- .../pip/_internal/metadata/pkg_resources.py | 56 +- .../pip/_internal/models/__init__.py | 3 +- .../pip/_internal/models/candidate.py | 29 +- .../pip/_internal/models/direct_url.py | 82 +- .../pip/_internal/models/format_control.py | 10 +- .../_internal/models/installation_report.py | 7 +- .../pip/_internal/models/link.py | 152 +- .../pip/_internal/models/release_control.py | 92 + .../pip/_internal/models/scheme.py | 20 +- .../pip/_internal/models/search_scope.py | 26 +- .../pip/_internal/models/selection_prefs.py | 17 +- .../pip/_internal/models/target_python.py | 20 +- .../pip/_internal/models/wheel.py | 48 +- .../pip/_internal/network/__init__.py | 3 +- .../pip/_internal/network/auth.py | 59 +- .../pip/_internal/network/cache.py | 40 +- .../pip/_internal/network/download.py | 301 +- .../pip/_internal/network/lazy_wheel.py | 27 +- .../pip/_internal/network/session.py | 80 +- .../pip/_internal/network/utils.py | 10 +- .../pip/_internal/network/xmlrpc.py | 7 +- .../pip/_internal/operations/check.py | 110 +- .../pip/_internal/operations/freeze.py | 56 +- .../_internal/operations/install/__init__.py | 3 +- .../operations/install/editable_legacy.py | 46 - .../pip/_internal/operations/install/wheel.py | 163 +- .../pip/_internal/operations/prepare.py | 91 +- .../site-packages/pip/_internal/pyproject.py | 78 +- .../pip/_internal/req/__init__.py | 43 +- .../pip/_internal/req/constructors.py | 176 +- .../site-packages/pip/_internal/req/pep723.py | 41 + .../pip/_internal/req/req_dependency_group.py | 75 + .../pip/_internal/req/req_file.py | 253 +- .../pip/_internal/req/req_install.py | 241 +- .../pip/_internal/req/req_set.py | 46 +- .../pip/_internal/req/req_uninstall.py | 76 +- .../pip/_internal/resolution/base.py | 6 +- .../_internal/resolution/legacy/resolver.py | 58 +- .../_internal/resolution/resolvelib/base.py | 43 +- .../resolution/resolvelib/candidates.py | 216 +- .../resolution/resolvelib/factory.py | 170 +- .../resolution/resolvelib/found_candidates.py | 73 +- .../resolution/resolvelib/provider.py | 186 +- .../resolution/resolvelib/reporter.py | 44 +- .../resolution/resolvelib/requirements.py | 91 +- .../resolution/resolvelib/resolver.py | 77 +- .../pip/_internal/self_outdated_check.py | 81 +- .../pip/_internal/utils/_jaraco_text.py | 2 +- .../pip/_internal/utils/appdirs.py | 6 +- .../pip/_internal/utils/compat.py | 24 +- .../pip/_internal/utils/compatibility_tags.py | 72 +- .../pip/_internal/utils/datetime.py | 21 +- .../pip/_internal/utils/deprecation.py | 36 +- .../pip/_internal/utils/direct_url_helpers.py | 8 +- .../pip/_internal/utils/egg_link.py | 11 +- .../pip/_internal/utils/encoding.py | 36 - .../pip/_internal/utils/entrypoints.py | 10 +- .../pip/_internal/utils/filesystem.py | 72 +- .../pip/_internal/utils/filetypes.py | 13 +- .../pip/_internal/utils/glibc.py | 28 +- .../pip/_internal/utils/hashes.py | 21 +- .../pip/_internal/utils/logging.py | 92 +- .../site-packages/pip/_internal/utils/misc.py | 216 +- .../pip/_internal/utils/models.py | 39 - .../pip/_internal/utils/packaging.py | 23 +- .../pip/_internal/utils/pylock.py | 116 + .../pip/_internal/utils/retry.py | 45 + .../pip/_internal/utils/setuptools_build.py | 146 - .../pip/_internal/utils/subprocess.py | 52 +- .../pip/_internal/utils/temp_dir.py | 24 +- .../pip/_internal/utils/unpacking.py | 219 +- .../site-packages/pip/_internal/utils/urls.py | 9 +- .../pip/_internal/utils/virtualenv.py | 5 +- .../pip/_internal/utils/wheel.py | 12 +- .../site-packages/pip/_internal/vcs/bazaar.py | 42 +- .../site-packages/pip/_internal/vcs/git.py | 93 +- .../pip/_internal/vcs/mercurial.py | 45 +- .../pip/_internal/vcs/subversion.py | 49 +- .../pip/_internal/vcs/versioncontrol.py | 154 +- .../pip/_internal/wheel_builder.py | 157 +- .../site-packages/pip/_vendor/README.rst | 180 + .../site-packages/pip/_vendor/__init__.py | 12 +- .../pip/_vendor/cachecontrol/LICENSE.txt | 13 + .../pip/_vendor/cachecontrol/__init__.py | 10 +- .../pip/_vendor/cachecontrol/adapter.py | 30 +- .../pip/_vendor/cachecontrol/cache.py | 1 + .../_vendor/cachecontrol/caches/file_cache.py | 62 +- .../pip/_vendor/cachecontrol/controller.py | 23 +- .../pip/_vendor/cachecontrol/filewrapper.py | 8 +- .../pip/_vendor/cachecontrol/heuristics.py | 5 +- .../pip/_vendor/cachecontrol/py.typed | 0 .../pip/_vendor/cachecontrol/serialize.py | 76 +- .../site-packages/pip/_vendor/certifi/LICENSE | 20 + .../pip/_vendor/certifi/__init__.py | 2 +- .../site-packages/pip/_vendor/certifi/core.py | 50 +- .../pip/_vendor/certifi/py.typed | 0 .../pip/_vendor/chardet/__init__.py | 115 - .../pip/_vendor/chardet/big5freq.py | 386 - .../pip/_vendor/chardet/big5prober.py | 47 - .../pip/_vendor/chardet/chardistribution.py | 261 - .../pip/_vendor/chardet/charsetgroupprober.py | 106 - .../pip/_vendor/chardet/charsetprober.py | 147 - .../pip/_vendor/chardet/cli/chardetect.py | 112 - .../pip/_vendor/chardet/codingstatemachine.py | 90 - .../_vendor/chardet/codingstatemachinedict.py | 19 - .../pip/_vendor/chardet/cp949prober.py | 49 - .../pip/_vendor/chardet/enums.py | 85 - .../pip/_vendor/chardet/escprober.py | 102 - .../pip/_vendor/chardet/escsm.py | 261 - .../pip/_vendor/chardet/eucjpprober.py | 102 - .../pip/_vendor/chardet/euckrfreq.py | 196 - .../pip/_vendor/chardet/euckrprober.py | 47 - .../pip/_vendor/chardet/euctwfreq.py | 388 - .../pip/_vendor/chardet/euctwprober.py | 47 - .../pip/_vendor/chardet/gb2312freq.py | 284 - .../pip/_vendor/chardet/gb2312prober.py | 47 - .../pip/_vendor/chardet/hebrewprober.py | 316 - .../pip/_vendor/chardet/jisfreq.py | 325 - .../pip/_vendor/chardet/johabfreq.py | 2382 --- .../pip/_vendor/chardet/johabprober.py | 47 - .../pip/_vendor/chardet/jpcntx.py | 238 - .../pip/_vendor/chardet/langbulgarianmodel.py | 4649 ----- .../pip/_vendor/chardet/langgreekmodel.py | 4397 ---- .../pip/_vendor/chardet/langhebrewmodel.py | 4380 ---- .../pip/_vendor/chardet/langhungarianmodel.py | 4649 ----- .../pip/_vendor/chardet/langrussianmodel.py | 5725 ----- .../pip/_vendor/chardet/langthaimodel.py | 4380 ---- .../pip/_vendor/chardet/langturkishmodel.py | 4380 ---- .../pip/_vendor/chardet/latin1prober.py | 147 - .../pip/_vendor/chardet/macromanprober.py | 162 - .../pip/_vendor/chardet/mbcharsetprober.py | 95 - .../pip/_vendor/chardet/mbcsgroupprober.py | 57 - .../pip/_vendor/chardet/mbcssm.py | 661 - .../pip/_vendor/chardet/metadata/languages.py | 352 - .../pip/_vendor/chardet/resultdict.py | 16 - .../pip/_vendor/chardet/sbcharsetprober.py | 162 - .../pip/_vendor/chardet/sbcsgroupprober.py | 88 - .../pip/_vendor/chardet/sjisprober.py | 105 - .../pip/_vendor/chardet/universaldetector.py | 362 - .../pip/_vendor/chardet/utf1632prober.py | 225 - .../pip/_vendor/chardet/utf8prober.py | 82 - .../pip/_vendor/chardet/version.py | 9 - .../pip/_vendor/colorama/__init__.py | 7 - .../pip/_vendor/colorama/ansi.py | 102 - .../pip/_vendor/colorama/ansitowin32.py | 277 - .../pip/_vendor/colorama/initialise.py | 121 - .../pip/_vendor/colorama/tests/__init__.py | 1 - .../pip/_vendor/colorama/tests/ansi_test.py | 76 - .../colorama/tests/ansitowin32_test.py | 294 - .../_vendor/colorama/tests/initialise_test.py | 189 - .../pip/_vendor/colorama/tests/isatty_test.py | 57 - .../pip/_vendor/colorama/tests/utils.py | 49 - .../_vendor/colorama/tests/winterm_test.py | 131 - .../pip/_vendor/colorama/win32.py | 180 - .../pip/_vendor/colorama/winterm.py | 195 - .../pip/_vendor/dependency_groups/LICENSE.txt | 9 + .../pip/_vendor/dependency_groups/__init__.py | 13 + .../pip/_vendor/dependency_groups/__main__.py | 65 + .../dependency_groups/_implementation.py | 209 + .../_lint_dependency_groups.py | 59 + .../_vendor/dependency_groups/_pip_wrapper.py | 62 + .../_vendor/dependency_groups/_toml_compat.py | 9 + .../pip/_vendor/dependency_groups/py.typed | 0 .../pip/_vendor/distlib/LICENSE.txt | 284 + .../pip/_vendor/distlib/__init__.py | 4 +- .../pip/_vendor/distlib/compat.py | 3 +- .../pip/_vendor/distlib/database.py | 1359 -- .../pip/_vendor/distlib/index.py | 508 - .../pip/_vendor/distlib/locators.py | 1303 -- .../pip/_vendor/distlib/manifest.py | 384 - .../pip/_vendor/distlib/markers.py | 167 - .../pip/_vendor/distlib/metadata.py | 1068 - .../pip/_vendor/distlib/scripts.py | 103 +- .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 97792 bytes .../pip/_vendor/distlib/t64-arm.exe | Bin 0 -> 182784 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 108032 bytes .../site-packages/pip/_vendor/distlib/util.py | 123 +- .../pip/_vendor/distlib/version.py | 751 - .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 91648 bytes .../pip/_vendor/distlib/w64-arm.exe | Bin 0 -> 168448 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 101888 bytes .../pip/_vendor/distlib/wheel.py | 1099 - .../site-packages/pip/_vendor/distro/LICENSE | 202 + .../pip/_vendor/distro/distro.py | 12 +- .../site-packages/pip/_vendor/distro/py.typed | 0 .../site-packages/pip/_vendor/idna/LICENSE.md | 31 + .../pip/_vendor/idna/__init__.py | 3 +- .../site-packages/pip/_vendor/idna/codec.py | 84 +- .../site-packages/pip/_vendor/idna/compat.py | 10 +- .../site-packages/pip/_vendor/idna/core.py | 295 +- .../pip/_vendor/idna/idnadata.py | 7145 +++---- .../pip/_vendor/idna/intranges.py | 11 +- .../pip/_vendor/idna/package_data.py | 3 +- .../site-packages/pip/_vendor/idna/py.typed | 0 .../pip/_vendor/idna/uts46data.py | 16601 +++++++-------- .../site-packages/pip/_vendor/msgpack/COPYING | 14 + .../pip/_vendor/msgpack/__init__.py | 22 +- .../site-packages/pip/_vendor/msgpack/ext.py | 59 +- .../pip/_vendor/msgpack/fallback.py | 225 +- .../pip/_vendor/packaging/LICENSE | 3 + .../pip/_vendor/packaging/LICENSE.APACHE | 177 + .../pip/_vendor/packaging/LICENSE.BSD | 23 + .../pip/_vendor/packaging/__about__.py | 26 - .../pip/_vendor/packaging/__init__.py | 30 +- .../pip/_vendor/packaging/_elffile.py | 108 + .../pip/_vendor/packaging/_manylinux.py | 263 +- .../pip/_vendor/packaging/_musllinux.py | 97 +- .../pip/_vendor/packaging/_parser.py | 365 + .../pip/_vendor/packaging/_structures.py | 8 + .../pip/_vendor/packaging/_tokenizer.py | 193 + .../_vendor/packaging/licenses/__init__.py | 147 + .../pip/_vendor/packaging/licenses/_spdx.py | 799 + .../pip/_vendor/packaging/markers.py | 392 +- .../pip/_vendor/packaging/metadata.py | 978 + .../pip/_vendor/packaging/py.typed | 0 .../pip/_vendor/packaging/pylock.py | 635 + .../pip/_vendor/packaging/requirements.py | 146 +- .../pip/_vendor/packaging/specifiers.py | 1214 +- .../pip/_vendor/packaging/tags.py | 346 +- .../pip/_vendor/packaging/utils.py | 132 +- .../pip/_vendor/packaging/version.py | 828 +- .../pip/_vendor/pkg_resources/LICENSE | 17 + .../pip/_vendor/pkg_resources/__init__.py | 1085 +- .../pip/_vendor/platformdirs/LICENSE | 21 + .../pip/_vendor/platformdirs/__init__.py | 199 +- .../pip/_vendor/platformdirs/__main__.py | 4 +- .../pip/_vendor/platformdirs/android.py | 83 +- .../pip/_vendor/platformdirs/api.py | 114 +- .../pip/_vendor/platformdirs/macos.py | 67 +- .../pip/_vendor/platformdirs/py.typed | 0 .../pip/_vendor/platformdirs/unix.py | 135 +- .../pip/_vendor/platformdirs/version.py | 36 +- .../pip/_vendor/platformdirs/windows.py | 57 +- .../pip/_vendor/pygments/LICENSE | 25 + .../pip/_vendor/pygments/__init__.py | 4 +- .../pip/_vendor/pygments/__main__.py | 2 +- .../pip/_vendor/pygments/cmdline.py | 668 - .../pip/_vendor/pygments/console.py | 10 +- .../pip/_vendor/pygments/filter.py | 5 +- .../pip/_vendor/pygments/filters/__init__.py | 8 +- .../pip/_vendor/pygments/formatter.py | 7 +- .../_vendor/pygments/formatters/__init__.py | 15 +- .../pip/_vendor/pygments/formatters/bbcode.py | 108 - .../pip/_vendor/pygments/formatters/groff.py | 170 - .../pip/_vendor/pygments/formatters/html.py | 989 - .../pip/_vendor/pygments/formatters/img.py | 645 - .../pip/_vendor/pygments/formatters/irc.py | 154 - .../pip/_vendor/pygments/formatters/latex.py | 521 - .../pip/_vendor/pygments/formatters/other.py | 161 - .../pygments/formatters/pangomarkup.py | 83 - .../pip/_vendor/pygments/formatters/rtf.py | 146 - .../pip/_vendor/pygments/formatters/svg.py | 188 - .../_vendor/pygments/formatters/terminal.py | 127 - .../pygments/formatters/terminal256.py | 338 - .../pip/_vendor/pygments/lexer.py | 88 +- .../pip/_vendor/pygments/lexers/__init__.py | 24 +- .../pip/_vendor/pygments/lexers/_mapping.py | 69 +- .../pip/_vendor/pygments/lexers/python.py | 95 +- .../pip/_vendor/pygments/modeline.py | 8 +- .../pip/_vendor/pygments/plugin.py | 22 +- .../pip/_vendor/pygments/regexopt.py | 2 +- .../pip/_vendor/pygments/scanner.py | 2 +- .../pip/_vendor/pygments/sphinxext.py | 46 +- .../pip/_vendor/pygments/style.py | 10 +- .../pip/_vendor/pygments/styles/__init__.py | 76 +- .../pip/_vendor/pygments/styles/_mapping.py | 54 + .../pip/_vendor/pygments/token.py | 3 +- .../pip/_vendor/pygments/unistring.py | 10 +- .../pip/_vendor/pygments/util.py | 32 +- .../pip/_vendor/pyparsing/__init__.py | 322 - .../pip/_vendor/pyparsing/actions.py | 217 - .../pip/_vendor/pyparsing/common.py | 432 - .../pip/_vendor/pyparsing/core.py | 6115 ------ .../pip/_vendor/pyparsing/diagram/__init__.py | 656 - .../pip/_vendor/pyparsing/exceptions.py | 299 - .../pip/_vendor/pyparsing/helpers.py | 1100 - .../pip/_vendor/pyparsing/results.py | 796 - .../pip/_vendor/pyparsing/testing.py | 331 - .../pip/_vendor/pyparsing/unicode.py | 361 - .../pip/_vendor/pyparsing/util.py | 284 - .../pip/_vendor/pyproject_hooks/LICENSE | 21 + .../pip/_vendor/pyproject_hooks/__init__.py | 26 +- .../pip/_vendor/pyproject_hooks/_compat.py | 8 - .../pip/_vendor/pyproject_hooks/_impl.py | 276 +- .../pyproject_hooks/_in_process/__init__.py | 7 +- .../_in_process/_in_process.py | 194 +- .../pip/_vendor/pyproject_hooks/py.typed | 0 .../pip/_vendor/requests/LICENSE | 175 + .../pip/_vendor/requests/__init__.py | 9 +- .../pip/_vendor/requests/__version__.py | 6 +- .../pip/_vendor/requests/adapters.py | 164 +- .../site-packages/pip/_vendor/requests/api.py | 2 +- .../pip/_vendor/requests/auth.py | 1 - .../pip/_vendor/requests/certs.py | 9 +- .../pip/_vendor/requests/compat.py | 27 +- .../pip/_vendor/requests/cookies.py | 16 +- .../pip/_vendor/requests/exceptions.py | 10 + .../pip/_vendor/requests/help.py | 6 +- .../pip/_vendor/requests/models.py | 17 +- .../pip/_vendor/requests/packages.py | 13 +- .../pip/_vendor/requests/sessions.py | 14 +- .../pip/_vendor/requests/status_codes.py | 10 +- .../pip/_vendor/requests/utils.py | 28 +- .../pip/_vendor/resolvelib/LICENSE | 13 + .../pip/_vendor/resolvelib/__init__.py | 9 +- .../resolvelib/compat/collections_abc.py | 6 - .../pip/_vendor/resolvelib/providers.py | 147 +- .../pip/_vendor/resolvelib/py.typed | 0 .../pip/_vendor/resolvelib/reporters.py | 32 +- .../_vendor/resolvelib/resolvers/__init__.py | 27 + .../_vendor/resolvelib/resolvers/abstract.py | 47 + .../_vendor/resolvelib/resolvers/criterion.py | 48 + .../resolvelib/resolvers/exceptions.py | 57 + .../{resolvers.py => resolvers/resolution.py} | 522 +- .../pip/_vendor/resolvelib/structs.py | 141 +- .../site-packages/pip/_vendor/rich/LICENSE | 19 + .../pip/_vendor/rich/__main__.py | 51 +- .../pip/_vendor/rich/_cell_widths.py | 367 +- .../pip/_vendor/rich/_export_format.py | 4 +- .../pip/_vendor/rich/_inspect.py | 4 +- .../pip/_vendor/rich/_null_file.py | 2 +- .../site-packages/pip/_vendor/rich/_ratio.py | 9 +- .../pip/_vendor/rich/_win32_console.py | 7 +- .../pip/_vendor/rich/_windows.py | 1 - .../site-packages/pip/_vendor/rich/_wrap.py | 73 +- .../site-packages/pip/_vendor/rich/align.py | 11 +- .../site-packages/pip/_vendor/rich/ansi.py | 1 + .../site-packages/pip/_vendor/rich/bar.py | 1 - .../site-packages/pip/_vendor/rich/box.py | 353 +- .../site-packages/pip/_vendor/rich/cells.py | 108 +- .../site-packages/pip/_vendor/rich/color.py | 5 +- .../site-packages/pip/_vendor/rich/console.py | 157 +- .../pip/_vendor/rich/containers.py | 8 +- .../site-packages/pip/_vendor/rich/control.py | 8 +- .../pip/_vendor/rich/default_styles.py | 5 +- .../pip/_vendor/rich/diagnose.py | 14 +- .../site-packages/pip/_vendor/rich/emoji.py | 7 +- .../pip/_vendor/rich/filesize.py | 3 +- .../pip/_vendor/rich/highlighter.py | 2 +- .../site-packages/pip/_vendor/rich/json.py | 1 - .../site-packages/pip/_vendor/rich/layout.py | 3 +- .../site-packages/pip/_vendor/rich/live.py | 43 +- .../pip/_vendor/rich/live_render.py | 9 +- .../site-packages/pip/_vendor/rich/logging.py | 8 + .../site-packages/pip/_vendor/rich/markup.py | 7 +- .../site-packages/pip/_vendor/rich/padding.py | 10 +- .../site-packages/pip/_vendor/rich/panel.py | 17 +- .../site-packages/pip/_vendor/rich/pretty.py | 86 +- .../pip/_vendor/rich/progress.py | 61 +- .../pip/_vendor/rich/progress_bar.py | 3 +- .../site-packages/pip/_vendor/rich/prompt.py | 34 +- .../site-packages/pip/_vendor/rich/py.typed | 0 .../site-packages/pip/_vendor/rich/repr.py | 2 +- .../site-packages/pip/_vendor/rich/segment.py | 51 +- .../site-packages/pip/_vendor/rich/spinner.py | 21 +- .../site-packages/pip/_vendor/rich/status.py | 1 - .../site-packages/pip/_vendor/rich/style.py | 22 +- .../site-packages/pip/_vendor/rich/syntax.py | 65 +- .../site-packages/pip/_vendor/rich/table.py | 28 +- .../site-packages/pip/_vendor/rich/text.py | 132 +- .../site-packages/pip/_vendor/rich/theme.py | 4 +- .../pip/_vendor/rich/traceback.py | 305 +- .../site-packages/pip/_vendor/rich/tree.py | 26 +- .../site-packages/pip/_vendor/six.py | 998 - .../pip/_vendor/tenacity/__init__.py | 608 - .../pip/_vendor/tenacity/_asyncio.py | 94 - .../pip/_vendor/tenacity/_utils.py | 76 - .../pip/_vendor/tenacity/after.py | 51 - .../pip/_vendor/tenacity/before.py | 46 - .../pip/_vendor/tenacity/before_sleep.py | 71 - .../site-packages/pip/_vendor/tenacity/nap.py | 43 - .../pip/_vendor/tenacity/retry.py | 272 - .../pip/_vendor/tenacity/stop.py | 103 - .../pip/_vendor/tenacity/tornadoweb.py | 59 - .../pip/_vendor/tenacity/wait.py | 228 - .../site-packages/pip/_vendor/tomli/LICENSE | 21 + .../pip/_vendor/tomli/__init__.py | 5 +- .../pip/_vendor/tomli/_parser.py | 274 +- .../site-packages/pip/_vendor/tomli/_re.py | 26 +- .../site-packages/pip/_vendor/tomli/py.typed | 1 + .../site-packages/pip/_vendor/tomli_w/LICENSE | 21 + .../pip/_vendor/tomli_w/__init__.py | 4 + .../pip/_vendor/tomli_w/_writer.py | 229 + .../pip/_vendor/tomli_w/py.typed | 1 + .../pip/_vendor/truststore/LICENSE | 21 + .../pip/_vendor/truststore/__init__.py | 25 +- .../pip/_vendor/truststore/_api.py | 77 +- .../pip/_vendor/truststore/_macos.py | 216 +- .../pip/_vendor/truststore/_openssl.py | 4 +- .../pip/_vendor/truststore/_windows.py | 37 +- .../pip/_vendor/truststore/py.typed | 0 .../pip/_vendor/typing_extensions.py | 3072 --- .../pip/_vendor/urllib3/LICENSE.txt | 21 + .../pip/_vendor/urllib3/_version.py | 2 +- .../pip/_vendor/urllib3/connection.py | 4 +- .../pip/_vendor/urllib3/connectionpool.py | 7 +- .../urllib3/contrib/securetransport.py | 3 +- .../pip/_vendor/urllib3/poolmanager.py | 18 +- .../pip/_vendor/urllib3/util/ssl_.py | 17 +- .../site-packages/pip/_vendor/vendor.txt | 43 +- .../pip/_vendor/webencodings/__init__.py | 342 - .../pip/_vendor/webencodings/labels.py | 231 - .../pip/_vendor/webencodings/mklabels.py | 59 - .../pip/_vendor/webencodings/tests.py | 153 - .../_vendor/webencodings/x_user_defined.py | 325 - .../propcache-0.4.1.dist-info/INSTALLER | 1 + .../propcache-0.4.1.dist-info/METADATA | 443 + .../propcache-0.4.1.dist-info/RECORD | 18 + .../propcache-0.4.1.dist-info/WHEEL | 7 + .../licenses/LICENSE | 202 + .../propcache-0.4.1.dist-info/licenses/NOTICE | 13 + .../propcache-0.4.1.dist-info/top_level.txt | 1 + .../site-packages/propcache/__init__.py | 32 + .../site-packages/propcache/_helpers.py | 39 + ..._helpers_c.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 745424 bytes .../site-packages/propcache/_helpers_c.pyx | 103 + .../site-packages/propcache/_helpers_py.py | 62 + .../python3.12/site-packages/propcache/api.py | 8 + .../site-packages/propcache/py.typed | 1 + .../pygments-2.20.0.dist-info/RECORD | 2 +- .../pytest-9.0.3.dist-info/RECORD | 4 +- .../python_dotenv-1.2.2.dist-info/RECORD | 3 +- .../pyyaml-6.0.3.dist-info/RECORD | 1 + .../pyyaml-6.0.3.dist-info/REQUESTED | 0 .../site-packages/rsa-4.9.1.dist-info/RECORD | 12 +- .../soupsieve-2.8.3.dist-info/INSTALLER | 1 + .../soupsieve-2.8.3.dist-info/METADATA | 115 + .../soupsieve-2.8.3.dist-info/RECORD | 20 + .../soupsieve-2.8.3.dist-info/WHEEL | 4 + .../licenses/LICENSE.md | 21 + .../site-packages/soupsieve/__init__.py | 168 + .../site-packages/soupsieve/__meta__.py | 197 + .../site-packages/soupsieve/css_match.py | 1654 ++ .../site-packages/soupsieve/css_parser.py | 1318 ++ .../site-packages/soupsieve/css_types.py | 407 + .../site-packages/soupsieve/pretty.py | 148 + .../site-packages/soupsieve/py.typed | 0 .../site-packages/soupsieve/util.py | 117 + .../uvicorn-0.46.0.dist-info/INSTALLER | 1 + .../METADATA | 2 +- .../RECORD | 32 +- .../uvicorn-0.46.0.dist-info/REQUESTED | 0 .../uvicorn-0.46.0.dist-info/WHEEL | 4 + .../entry_points.txt | 0 .../licenses/LICENSE.md | 0 .../site-packages/uvicorn/__init__.py | 2 +- .../site-packages/uvicorn/config.py | 18 +- .../python3.12/site-packages/uvicorn/main.py | 13 +- .../uvicorn/middleware/proxy_headers.py | 54 +- .../uvicorn/protocols/http/h11_impl.py | 29 +- .../uvicorn/protocols/http/httptools_impl.py | 40 +- .../websockets/websockets_sansio_impl.py | 12 +- .../protocols/websockets/wsproto_impl.py | 158 +- .../watchfiles-1.1.1.dist-info/RECORD | 2 +- .../websockets-16.0.dist-info/RECORD | 2 +- .../yarl-1.23.0.dist-info/INSTALLER | 1 + .../yarl-1.23.0.dist-info/METADATA | 2609 +++ .../yarl-1.23.0.dist-info/RECORD | 26 + .../site-packages/yarl-1.23.0.dist-info/WHEEL | 7 + .../yarl-1.23.0.dist-info/licenses/LICENSE | 202 + .../yarl-1.23.0.dist-info/licenses/NOTICE | 13 + .../yarl-1.23.0.dist-info/top_level.txt | 1 + .../python3.12/site-packages/yarl/__init__.py | 14 + .../python3.12/site-packages/yarl/_parse.py | 202 + .../python3.12/site-packages/yarl/_path.py | 41 + .../python3.12/site-packages/yarl/_query.py | 121 + .../python3.12/site-packages/yarl/_quoters.py | 32 + .../python3.12/site-packages/yarl/_quoting.py | 19 + ..._quoting_c.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 124904 bytes .../site-packages/yarl/_quoting_c.pyx | 451 + .../site-packages/yarl/_quoting_py.py | 213 + .../lib/python3.12/site-packages/yarl/_url.py | 1665 ++ .../python3.12/site-packages/yarl/py.typed | 1 + venv/pyvenv.cfg | 2 +- 1039 files changed, 138862 insertions(+), 105259 deletions(-) create mode 100644 .standalone.pid create mode 100644 backups/ainventory_2026-04-23_12-58-31.tar.gz create mode 100644 config/Caddyfile.standalone rename venv/lib/python3.12/site-packages/{certifi-2026.2.25.dist-info => aiohappyeyeballs-2.6.1.dist-info}/INSTALLER (100%) create mode 100644 venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE create mode 100644 venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/aiohappyeyeballs/__init__.py create mode 100644 venv/lib/python3.12/site-packages/aiohappyeyeballs/_staggered.py create mode 100644 venv/lib/python3.12/site-packages/aiohappyeyeballs/impl.py rename venv/lib/python3.12/site-packages/{pip-24.0.dist-info/REQUESTED => aiohappyeyeballs/py.typed} (100%) create mode 100644 venv/lib/python3.12/site-packages/aiohappyeyeballs/types.py create mode 100644 venv/lib/python3.12/site-packages/aiohappyeyeballs/utils.py rename venv/lib/python3.12/site-packages/{click-8.3.2.dist-info => aiohttp-3.13.5.dist-info}/INSTALLER (100%) create mode 100644 venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/RECORD rename venv/lib/python3.12/site-packages/{python_dotenv-1.2.2.dist-info => aiohttp-3.13.5.dist-info}/REQUESTED (100%) create mode 100644 venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/licenses/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/licenses/vendor/llhttp/LICENSE create mode 100644 venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/aiohttp/.hash/_cparser.pxd.hash create mode 100644 venv/lib/python3.12/site-packages/aiohttp/.hash/_find_header.pxd.hash create mode 100644 venv/lib/python3.12/site-packages/aiohttp/.hash/_http_parser.pyx.hash create mode 100644 venv/lib/python3.12/site-packages/aiohttp/.hash/_http_writer.pyx.hash create mode 100644 venv/lib/python3.12/site-packages/aiohttp/.hash/hdrs.py.hash create mode 100644 venv/lib/python3.12/site-packages/aiohttp/__init__.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_cookie_helpers.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_cparser.pxd create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_find_header.pxd create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_headers.pxi create mode 100755 venv/lib/python3.12/site-packages/aiohttp/_http_parser.cpython-312-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_http_parser.pyx create mode 100755 venv/lib/python3.12/site-packages/aiohttp/_http_writer.cpython-312-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_http_writer.pyx create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/mask.pxd.hash create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/mask.pyx.hash create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/reader_c.pxd.hash create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/__init__.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/helpers.py create mode 100755 venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.cpython-312-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.pxd create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.pyx create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/models.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/reader.py create mode 100755 venv/lib/python3.12/site-packages/aiohttp/_websocket/reader_c.cpython-312-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/reader_c.pxd create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/reader_c.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/reader_py.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/_websocket/writer.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/abc.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/base_protocol.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/client.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/client_exceptions.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/client_middleware_digest_auth.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/client_middlewares.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/client_proto.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/client_reqrep.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/client_ws.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/compression_utils.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/connector.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/cookiejar.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/formdata.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/hdrs.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/helpers.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/http.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/http_exceptions.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/http_parser.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/http_websocket.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/http_writer.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/log.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/multipart.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/payload.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/payload_streamer.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/py.typed create mode 100644 venv/lib/python3.12/site-packages/aiohttp/pytest_plugin.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/resolver.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/streams.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/tcp_helpers.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/test_utils.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/tracing.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/typedefs.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_app.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_exceptions.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_fileresponse.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_log.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_middlewares.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_protocol.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_request.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_response.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_routedef.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_runner.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_server.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_urldispatcher.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/web_ws.py create mode 100644 venv/lib/python3.12/site-packages/aiohttp/worker.py rename venv/lib/python3.12/site-packages/{idna-3.11.dist-info => aiosignal-1.4.0.dist-info}/INSTALLER (100%) create mode 100644 venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/RECORD rename venv/lib/python3.12/site-packages/{certifi-2026.2.25.dist-info => aiosignal-1.4.0.dist-info}/WHEEL (65%) create mode 100644 venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/aiosignal/__init__.py rename venv/lib/python3.12/site-packages/{pip/_vendor/chardet/cli/__init__.py => aiosignal/py.typed} (100%) create mode 100644 venv/lib/python3.12/site-packages/attr/__init__.py create mode 100644 venv/lib/python3.12/site-packages/attr/__init__.pyi create mode 100644 venv/lib/python3.12/site-packages/attr/_cmp.py create mode 100644 venv/lib/python3.12/site-packages/attr/_cmp.pyi create mode 100644 venv/lib/python3.12/site-packages/attr/_compat.py create mode 100644 venv/lib/python3.12/site-packages/attr/_config.py create mode 100644 venv/lib/python3.12/site-packages/attr/_funcs.py create mode 100644 venv/lib/python3.12/site-packages/attr/_make.py create mode 100644 venv/lib/python3.12/site-packages/attr/_next_gen.py create mode 100644 venv/lib/python3.12/site-packages/attr/_typing_compat.pyi create mode 100644 venv/lib/python3.12/site-packages/attr/_version_info.py create mode 100644 venv/lib/python3.12/site-packages/attr/_version_info.pyi create mode 100644 venv/lib/python3.12/site-packages/attr/converters.py create mode 100644 venv/lib/python3.12/site-packages/attr/converters.pyi create mode 100644 venv/lib/python3.12/site-packages/attr/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/attr/exceptions.pyi create mode 100644 venv/lib/python3.12/site-packages/attr/filters.py create mode 100644 venv/lib/python3.12/site-packages/attr/filters.pyi rename venv/lib/python3.12/site-packages/{pip/_vendor/chardet/metadata/__init__.py => attr/py.typed} (100%) create mode 100644 venv/lib/python3.12/site-packages/attr/setters.py create mode 100644 venv/lib/python3.12/site-packages/attr/setters.pyi create mode 100644 venv/lib/python3.12/site-packages/attr/validators.py create mode 100644 venv/lib/python3.12/site-packages/attr/validators.pyi rename venv/lib/python3.12/site-packages/{pip-24.0.dist-info => attrs-26.1.0.dist-info}/INSTALLER (100%) create mode 100644 venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD rename venv/lib/python3.12/site-packages/{uvicorn-0.44.0.dist-info => attrs-26.1.0.dist-info}/WHEEL (100%) create mode 100644 venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/attrs/__init__.py create mode 100644 venv/lib/python3.12/site-packages/attrs/__init__.pyi create mode 100644 venv/lib/python3.12/site-packages/attrs/converters.py create mode 100644 venv/lib/python3.12/site-packages/attrs/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/attrs/filters.py rename venv/lib/python3.12/site-packages/{pip/_vendor/resolvelib/compat/__init__.py => attrs/py.typed} (100%) create mode 100644 venv/lib/python3.12/site-packages/attrs/setters.py create mode 100644 venv/lib/python3.12/site-packages/attrs/validators.py rename venv/lib/python3.12/site-packages/{uvicorn-0.44.0.dist-info => beautifulsoup4-4.14.3.dist-info}/INSTALLER (100%) create mode 100644 venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD rename venv/lib/python3.12/site-packages/{uvicorn-0.44.0.dist-info => beautifulsoup4-4.14.3.dist-info}/REQUESTED (100%) create mode 100644 venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS create mode 100644 venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/bs4/__init__.py create mode 100644 venv/lib/python3.12/site-packages/bs4/_deprecation.py create mode 100644 venv/lib/python3.12/site-packages/bs4/_typing.py create mode 100644 venv/lib/python3.12/site-packages/bs4/_warnings.py create mode 100644 venv/lib/python3.12/site-packages/bs4/builder/__init__.py create mode 100644 venv/lib/python3.12/site-packages/bs4/builder/_html5lib.py create mode 100644 venv/lib/python3.12/site-packages/bs4/builder/_htmlparser.py create mode 100644 venv/lib/python3.12/site-packages/bs4/builder/_lxml.py create mode 100644 venv/lib/python3.12/site-packages/bs4/css.py create mode 100644 venv/lib/python3.12/site-packages/bs4/dammit.py create mode 100644 venv/lib/python3.12/site-packages/bs4/diagnose.py create mode 100644 venv/lib/python3.12/site-packages/bs4/element.py create mode 100644 venv/lib/python3.12/site-packages/bs4/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/bs4/filter.py create mode 100644 venv/lib/python3.12/site-packages/bs4/formatter.py create mode 100644 venv/lib/python3.12/site-packages/bs4/py.typed delete mode 100644 venv/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/RECORD rename venv/lib/python3.12/site-packages/{pip-24.0.dist-info/top_level.txt => certifi-2026.4.22.dist-info/INSTALLER} (100%) rename venv/lib/python3.12/site-packages/{certifi-2026.2.25.dist-info => certifi-2026.4.22.dist-info}/METADATA (99%) create mode 100644 venv/lib/python3.12/site-packages/certifi-2026.4.22.dist-info/RECORD rename venv/lib/python3.12/site-packages/{pip-24.0.dist-info => certifi-2026.4.22.dist-info}/WHEEL (65%) rename venv/lib/python3.12/site-packages/{certifi-2026.2.25.dist-info => certifi-2026.4.22.dist-info}/licenses/LICENSE (100%) rename venv/lib/python3.12/site-packages/{certifi-2026.2.25.dist-info => certifi-2026.4.22.dist-info}/top_level.txt (100%) create mode 100644 venv/lib/python3.12/site-packages/click-8.3.3.dist-info/INSTALLER rename venv/lib/python3.12/site-packages/{click-8.3.2.dist-info => click-8.3.3.dist-info}/METADATA (99%) rename venv/lib/python3.12/site-packages/{click-8.3.2.dist-info => click-8.3.3.dist-info}/RECORD (68%) rename venv/lib/python3.12/site-packages/{click-8.3.2.dist-info => click-8.3.3.dist-info}/WHEEL (100%) rename venv/lib/python3.12/site-packages/{click-8.3.2.dist-info => click-8.3.3.dist-info}/licenses/LICENSE.txt (100%) create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile-2.0.0.dist-info/AUTHORS.txt create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile-2.0.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile-2.0.0.dist-info/LICENCE.python create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile-2.0.0.dist-info/LICENCE.rst create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile-2.0.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile-2.0.0.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile-2.0.0.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile-2.0.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile/__init__.py create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile/incremental_tree.py create mode 100644 venv/lib/python3.12/site-packages/et_xmlfile/xmlfile.py create mode 100644 venv/lib/python3.12/site-packages/frozenlist-1.8.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/frozenlist-1.8.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/frozenlist-1.8.0.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/frozenlist-1.8.0.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/frozenlist-1.8.0.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/frozenlist-1.8.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/frozenlist/__init__.py create mode 100644 venv/lib/python3.12/site-packages/frozenlist/__init__.pyi create mode 100755 venv/lib/python3.12/site-packages/frozenlist/_frozenlist.cpython-312-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.12/site-packages/frozenlist/_frozenlist.pyx create mode 100644 venv/lib/python3.12/site-packages/frozenlist/py.typed create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy-0.18.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy-0.18.0.dist-info/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy-0.18.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy-0.18.0.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy-0.18.0.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy-0.18.0.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy-0.18.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy-0.18.0.dist-info/zip-safe create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy/StringMatcher.py create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy/__init__.py create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy/fuzz.py create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy/process.py create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy/string_processing.py create mode 100644 venv/lib/python3.12/site-packages/fuzzywuzzy/utils.py create mode 100644 venv/lib/python3.12/site-packages/idna-3.13.dist-info/INSTALLER rename venv/lib/python3.12/site-packages/{idna-3.11.dist-info => idna-3.13.dist-info}/METADATA (93%) rename venv/lib/python3.12/site-packages/{idna-3.11.dist-info => idna-3.13.dist-info}/RECORD (52%) rename venv/lib/python3.12/site-packages/{idna-3.11.dist-info => idna-3.13.dist-info}/WHEEL (100%) create mode 100644 venv/lib/python3.12/site-packages/idna-3.13.dist-info/licenses/LICENSE.md create mode 100644 venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/multidict/__init__.py create mode 100644 venv/lib/python3.12/site-packages/multidict/_abc.py create mode 100644 venv/lib/python3.12/site-packages/multidict/_compat.py create mode 100755 venv/lib/python3.12/site-packages/multidict/_multidict.cpython-312-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.12/site-packages/multidict/_multidict_py.py create mode 100644 venv/lib/python3.12/site-packages/multidict/py.typed create mode 100644 venv/lib/python3.12/site-packages/openpyxl-3.1.5.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/openpyxl-3.1.5.dist-info/LICENCE.rst create mode 100644 venv/lib/python3.12/site-packages/openpyxl-3.1.5.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/openpyxl-3.1.5.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/openpyxl-3.1.5.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/openpyxl-3.1.5.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/openpyxl-3.1.5.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/openpyxl/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/_constants.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/cell/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/cell/_writer.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/cell/cell.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/cell/read_only.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/cell/rich_text.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/cell/text.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/_3d.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/area_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/axis.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/bar_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/bubble_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/chartspace.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/data_source.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/descriptors.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/error_bar.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/label.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/layout.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/legend.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/line_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/marker.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/picture.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/pie_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/pivot.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/plotarea.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/print_settings.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/radar_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/reader.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/reference.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/scatter_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/series.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/series_factory.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/shapes.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/stock_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/surface_chart.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/text.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/title.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/trendline.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chart/updown_bars.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chartsheet/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chartsheet/chartsheet.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chartsheet/custom.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chartsheet/properties.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chartsheet/protection.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chartsheet/publish.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chartsheet/relation.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/chartsheet/views.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/comments/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/comments/author.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/comments/comment_sheet.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/comments/comments.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/comments/shape_writer.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/compat/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/compat/abc.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/compat/numbers.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/compat/product.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/compat/singleton.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/compat/strings.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/descriptors/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/descriptors/base.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/descriptors/container.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/descriptors/excel.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/descriptors/namespace.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/descriptors/nested.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/descriptors/sequence.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/descriptors/serialisable.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/descriptors/slots.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/colors.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/connector.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/drawing.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/effect.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/fill.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/geometry.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/graphic.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/image.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/line.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/picture.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/properties.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/relation.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/spreadsheet_drawing.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/text.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/drawing/xdr.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/formatting/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/formatting/formatting.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/formatting/rule.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/formula/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/formula/tokenizer.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/formula/translate.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/packaging/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/packaging/core.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/packaging/custom.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/packaging/extended.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/packaging/interface.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/packaging/workbook.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/pivot/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/pivot/cache.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/pivot/fields.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/pivot/record.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/pivot/table.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/reader/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/reader/drawings.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/reader/excel.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/reader/strings.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/reader/workbook.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/alignment.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/borders.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/builtins.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/cell_style.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/colors.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/differential.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/fills.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/fonts.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/numbers.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/protection.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/proxy.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/styleable.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/stylesheet.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/styles/table.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/bound_dictionary.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/cell.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/dataframe.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/datetime.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/escape.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/formulas.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/indexed_list.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/inference.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/protection.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/utils/units.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/_writer.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/child.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/defined_name.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/external_link/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/external_link/external.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/external_reference.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/function_group.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/properties.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/protection.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/smart_tags.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/views.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/web.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/workbook/workbook.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/_read_only.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/_reader.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/_write_only.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/_writer.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/cell_range.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/cell_watch.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/controls.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/copier.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/custom.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/datavalidation.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/dimensions.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/drawing.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/errors.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/filters.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/formula.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/header_footer.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/hyperlink.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/merge.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/ole.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/page.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/pagebreak.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/picture.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/print_settings.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/properties.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/protection.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/related.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/scenario.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/smart_tag.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/table.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/views.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/worksheet/worksheet.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/writer/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/writer/excel.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/writer/theme.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/xml/__init__.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/xml/constants.py create mode 100644 venv/lib/python3.12/site-packages/openpyxl/xml/functions.py delete mode 100644 venv/lib/python3.12/site-packages/pip-24.0.dist-info/RECORD delete mode 100644 venv/lib/python3.12/site-packages/pip-24.0.dist-info/entry_points.txt create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/INSTALLER rename venv/lib/python3.12/site-packages/{pip-24.0.dist-info => pip-26.0.1.dist-info}/METADATA (64%) create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/entry_points.txt rename venv/lib/python3.12/site-packages/{pip-24.0.dist-info => pip-26.0.1.dist-info/licenses}/AUTHORS.txt (88%) rename venv/lib/python3.12/site-packages/{pip-24.0.dist-info => pip-26.0.1.dist-info/licenses}/LICENSE.txt (100%) create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE rename venv/lib/python3.12/site-packages/{idna-3.11.dist-info/licenses => pip-26.0.1.dist-info/licenses/src/pip/_vendor/idna}/LICENSE.md (100%) create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt mode change 100644 => 100755 venv/lib/python3.12/site-packages/pip/_internal/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/index_command.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/lock.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/release_control.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/pep723.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/req_dependency_group.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/encoding.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/models.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/pylock.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/retry.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/README.rst create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/py.typed delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/__init__.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5freq.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5prober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/chardistribution.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetgroupprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/chardetect.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachine.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachinedict.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/cp949prober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/enums.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/escprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/escsm.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/eucjpprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrfreq.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwfreq.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312freq.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312prober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/hebrewprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/jisfreq.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabfreq.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/jpcntx.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/langbulgarianmodel.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/langgreekmodel.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhebrewmodel.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhungarianmodel.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/langrussianmodel.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/langthaimodel.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/langturkishmodel.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/latin1prober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/macromanprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcharsetprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcsgroupprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcssm.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/languages.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/resultdict.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcharsetprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcsgroupprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/sjisprober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/universaldetector.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf1632prober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf8prober.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/chardet/version.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/__init__.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/ansi.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/ansitowin32.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/initialise.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__init__.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansi_test.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/initialise_test.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/isatty_test.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/utils.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/winterm_test.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/win32.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__main__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_implementation.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_lint_dependency_groups.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_pip_wrapper.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_toml_compat.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/LICENSE.txt delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/database.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/index.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/locators.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/manifest.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/markers.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64-arm.exe create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64.exe delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/version.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64-arm.exe create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64.exe delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distro/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distro/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/LICENSE.md create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/msgpack/COPYING create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.APACHE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.BSD delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/_elffile.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/_parser.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/_tokenizer.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/_spdx.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/metadata.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/pylock.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/LICENSE delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/cmdline.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/bbcode.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/groff.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/html.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/img.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/irc.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/latex.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/other.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/rtf.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/svg.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/terminal.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/terminal256.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/_mapping.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__init__.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/actions.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/common.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/core.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/diagram/__init__.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/exceptions.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/helpers.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/results.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/testing.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/unicode.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/util.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_compat.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py rename venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/{resolvers.py => resolvers/resolution.py} (56%) create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/py.typed delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/six.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__init__.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_asyncio.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_utils.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/after.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before_sleep.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/nap.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/retry.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/stop.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/tornadoweb.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tenacity/wait.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/_writer.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/LICENSE create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/py.typed delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/typing_extensions.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/LICENSE.txt delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__init__.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/webencodings/labels.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/webencodings/mklabels.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/webencodings/tests.py delete mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/licenses/NOTICE create mode 100644 venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/propcache/__init__.py create mode 100644 venv/lib/python3.12/site-packages/propcache/_helpers.py create mode 100755 venv/lib/python3.12/site-packages/propcache/_helpers_c.cpython-312-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.12/site-packages/propcache/_helpers_c.pyx create mode 100644 venv/lib/python3.12/site-packages/propcache/_helpers_py.py create mode 100644 venv/lib/python3.12/site-packages/propcache/api.py create mode 100644 venv/lib/python3.12/site-packages/propcache/py.typed create mode 100644 venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/licenses/LICENSE.md create mode 100644 venv/lib/python3.12/site-packages/soupsieve/__init__.py create mode 100644 venv/lib/python3.12/site-packages/soupsieve/__meta__.py create mode 100644 venv/lib/python3.12/site-packages/soupsieve/css_match.py create mode 100644 venv/lib/python3.12/site-packages/soupsieve/css_parser.py create mode 100644 venv/lib/python3.12/site-packages/soupsieve/css_types.py create mode 100644 venv/lib/python3.12/site-packages/soupsieve/pretty.py create mode 100644 venv/lib/python3.12/site-packages/soupsieve/py.typed create mode 100644 venv/lib/python3.12/site-packages/soupsieve/util.py create mode 100644 venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/INSTALLER rename venv/lib/python3.12/site-packages/{uvicorn-0.44.0.dist-info => uvicorn-0.46.0.dist-info}/METADATA (99%) rename venv/lib/python3.12/site-packages/{uvicorn-0.44.0.dist-info => uvicorn-0.46.0.dist-info}/RECORD (79%) create mode 100644 venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/WHEEL rename venv/lib/python3.12/site-packages/{uvicorn-0.44.0.dist-info => uvicorn-0.46.0.dist-info}/entry_points.txt (100%) rename venv/lib/python3.12/site-packages/{uvicorn-0.44.0.dist-info => uvicorn-0.46.0.dist-info}/licenses/LICENSE.md (100%) create mode 100644 venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/licenses/NOTICE create mode 100644 venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/yarl/__init__.py create mode 100644 venv/lib/python3.12/site-packages/yarl/_parse.py create mode 100644 venv/lib/python3.12/site-packages/yarl/_path.py create mode 100644 venv/lib/python3.12/site-packages/yarl/_query.py create mode 100644 venv/lib/python3.12/site-packages/yarl/_quoters.py create mode 100644 venv/lib/python3.12/site-packages/yarl/_quoting.py create mode 100755 venv/lib/python3.12/site-packages/yarl/_quoting_c.cpython-312-x86_64-linux-gnu.so create mode 100644 venv/lib/python3.12/site-packages/yarl/_quoting_c.pyx create mode 100644 venv/lib/python3.12/site-packages/yarl/_quoting_py.py create mode 100644 venv/lib/python3.12/site-packages/yarl/_url.py create mode 100644 venv/lib/python3.12/site-packages/yarl/py.typed diff --git a/.standalone.pid b/.standalone.pid new file mode 100644 index 00000000..db3c36d1 --- /dev/null +++ b/.standalone.pid @@ -0,0 +1,3 @@ +825448 +825449 +825463 diff --git a/backend/config_loader.py b/backend/config_loader.py index 69459713..00af815a 100644 --- a/backend/config_loader.py +++ b/backend/config_loader.py @@ -201,7 +201,38 @@ def load_config() -> dict: except Exception as e: log.warning(f"⚠️ Failed to load {secrets_yaml_path}: {e}") - # 4. Apply Environment Overrides (D-06) + # 4. Load network.yaml (SSOT for infrastructure) + network_yaml_path = os.path.join(config_dir, "network.yaml") + if os.path.exists(network_yaml_path): + try: + with open(network_yaml_path, 'r') as f: + network_data = yaml.safe_load(f) or {} + # Map infrastructure settings to application domain + infra = network_data.get("application", {}) + if "server_ip" in infra: + config["application"]["server_ip"] = infra["server_ip"] + if "cors_origins" in infra: + # Append or override? Plan says establish as master. + # We merge with any existing ones. + existing = config["application"].get("cors_origins", "") + if existing: + config["application"]["cors_origins"] = f"{infra['cors_origins']},{existing}" + else: + config["application"]["cors_origins"] = infra["cors_origins"] + + # Map ports to application domain for CORS logic + ports = network_data.get("ports", {}) + for key, val in ports.items(): + config["application"][key] = val + + # Map SSL state + config["application"]["ssl_enabled"] = network_data.get("ssl", {}).get("ssl_enabled", False) + + log.info(f"✅ Loaded network topology from {network_yaml_path}") + except Exception as e: + log.warning(f"⚠️ Failed to load {network_yaml_path}: {e}") + + # 5. Apply Environment Overrides (D-06) _apply_env_overrides(config) _config = config diff --git a/backups/ainventory_2026-04-23_12-58-31.tar.gz b/backups/ainventory_2026-04-23_12-58-31.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ef6485ba72d086ddc2a441eab96c077816e7a761 GIT binary patch literal 29151 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 444) +https://:444 { + tls internal { + on_demand + } + reverse_proxy backend:8000 +} diff --git a/config/README.md b/config/README.md index afbb150d..a2ffdb9a 100644 --- a/config/README.md +++ b/config/README.md @@ -75,14 +75,17 @@ Controls the Next.js frontend application behavior and PWA settings. | `pwa.app_name` | Display name of the PWA | `TFM aInventory` | ### network.yaml -Defines host-side port mappings and SSL/CORS policies. +**SINGLE SOURCE OF TRUTH (SSOT):** This is the master file for your server's network topology. All other domains (Backend CORS, Frontend API URL) are dynamically derived from these settings. | Variable | Description | Default | |----------|-------------|---------| +| `application.server_ip` | **MASTER IP** of your server | `localhost` | +| `application.cors_origins`| Extra allowed origins (Subnets/IPs) | - | | `ports.backend_port` | Host port for backend | `8916` | | `ports.frontend_port` | Host port for frontend | `8917` | | `ssl.ssl_enabled` | Enable HTTPS via Caddy | `true` | -| `cors.allowed_origins` | List of allowed origins | `*` | + +> **Note:** You only need to change the IP address in **this file**. The system will automatically update the frontend's API URL and the backend's CORS policies upon restart. ### docker.yaml Resource limits and container orchestration settings. diff --git a/config/backend.yaml.example b/config/backend.yaml.example index 06a06c7a..b7ed82e4 100644 --- a/config/backend.yaml.example +++ b/config/backend.yaml.example @@ -35,21 +35,8 @@ ai: # Environment: BACKEND_AI_FALLBACK_PROVIDER fallback_provider: "claude" - # Gemini API Key (obtain from https://aistudio.google.com/) - # Environment: BACKEND_AI_GEMINI_API_KEY or GEMINI_API_KEY - gemini_api_key: "your-gemini-api-key" - - # Claude API Key (obtain from Anthropic Console) - # Environment: BACKEND_AI_CLAUDE_API_KEY or CLAUDE_API_KEY - claude_api_key: "your-claude-api-key" - # --- Authentication & LDAP --- auth: - # JWT Secret Key (min 32 chars) - # Generate with: openssl rand -hex 32 - # Environment: BACKEND_AUTH_JWT_SECRET_KEY or JWT_SECRET_KEY - jwt_secret_key: "change_me_in_production" - # Enable LDAP Authentication? # Default: false # Environment: BACKEND_AUTH_LDAP_ENABLED or LDAP_ENABLED diff --git a/scripts/deploy.py b/scripts/deploy.py index c572d80a..57ac170a 100755 --- a/scripts/deploy.py +++ b/scripts/deploy.py @@ -210,12 +210,25 @@ def main(): # Prepare environment variables for Docker Compose # We map YAML values to the environment variables expected by docker-compose.yml + server_ip = network_cfg.get("application", {}).get("server_ip", "localhost") + ssl_enabled = network_cfg.get("ssl", {}).get("ssl_enabled", False) + + # Dynamically construct API URL from master settings + if ssl_enabled: + api_url = f"https://{server_ip}:{ports_to_check['Backend HTTPS']}" + else: + api_url = f"http://{server_ip}:{ports_to_check['Backend HTTP']}" + + logger.info(f"Dynamically injecting API URL into Docker: {api_url}") + env = os.environ.copy() env["BACKEND_PORT"] = str(ports_to_check["Backend HTTP"]) env["FRONTEND_PORT"] = str(ports_to_check["Frontend HTTP"]) env["BACKEND_SSL_PORT"] = str(ports_to_check["Backend HTTPS"]) env["FRONTEND_SSL_PORT"] = str(ports_to_check["Frontend HTTPS"]) env["JWT_SECRET_KEY"] = jwt_secret or "change_me_in_production" + env["NEXT_PUBLIC_API_URL"] = api_url + env["SERVER_IP"] = server_ip # 8. Docker Compose Build logger.info("Step 8/12: Building/Pulling Docker images...") diff --git a/scripts/run_standalone.py b/scripts/run_standalone.py index 0b4714fc..d484ac75 100755 --- a/scripts/run_standalone.py +++ b/scripts/run_standalone.py @@ -64,6 +64,37 @@ def load_yaml(file_path: str) -> Dict[str, Any]: logger.error(f"Failed to parse {file_path}: {e}") return {} +# PID file for tracking processes +PID_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), ".standalone.pid") + +def get_running_pids() -> List[int]: + if not os.path.exists(PID_FILE): + return [] + try: + with open(PID_FILE, 'r') as f: + return [int(line.strip()) for line in f if line.strip()] + except: + return [] + +def save_pids(pids: List[int]): + with open(PID_FILE, 'w') as f: + for pid in pids: + f.write(f"{pid}\n") + +def clear_pids(): + if os.path.exists(PID_FILE): + try: + os.remove(PID_FILE) + except: + pass + +def is_pid_running(pid: int) -> bool: + try: + os.kill(pid, 0) + return True + except OSError: + return False + def is_port_in_use(port: int) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: return s.connect_ex(('localhost', port)) == 0 @@ -76,7 +107,7 @@ def log_stream(stream, prefix, color): print(f"{color}[{prefix}]{Colors.NC} {line.strip()}") stream.close() -def start_backend(backend_port: int, backend_cfg: Dict[str, Any]): +def start_backend(backend_port: int, backend_cfg: Dict[str, Any], background: bool = False): logger.info(f"Starting Backend on port {backend_port}...") app_cfg = backend_cfg.get("application", {}) @@ -96,73 +127,231 @@ def start_backend(backend_port: int, backend_cfg: Dict[str, Any]): "--port", str(backend_port) ] - # Add --reload if in dev mode (detectable via environment or flag, - # but the script is for 'run_standalone' which might be used for prod too) - # For now, let's keep it simple. - + log_file = None + if background: + log_path = os.path.join(env["LOGS_DIR"], "backend_standalone.log") + log_file = open(log_path, "a") + logger.info(f"Backend logs redirected to {log_path}") + try: p = subprocess.Popen( cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + stdout=log_file if background else subprocess.PIPE, + stderr=log_file if background else subprocess.PIPE, text=True, env=env, bufsize=1, - universal_newlines=True + universal_newlines=True, + start_new_session=background ) processes.append(p) - threading.Thread(target=log_stream, args=(p.stdout, "BACKEND", Colors.GREEN), daemon=True).start() - threading.Thread(target=log_stream, args=(p.stderr, "BACKEND", Colors.GREEN), daemon=True).start() + if not background: + threading.Thread(target=log_stream, args=(p.stdout, "BACKEND", Colors.GREEN), daemon=True).start() + threading.Thread(target=log_stream, args=(p.stderr, "BACKEND", Colors.GREEN), daemon=True).start() return p except FileNotFoundError: logger.error("uvicorn not found. Please install it with: pip install uvicorn") sys.exit(1) -def start_frontend(frontend_port: int, frontend_cfg: Dict[str, Any]): +def start_frontend(frontend_port: int, frontend_cfg: Dict[str, Any], network_cfg: Dict[str, Any], background: bool = False): logger.info(f"Starting Frontend on port {frontend_port}...") - api_cfg = frontend_cfg.get("api", {}) - env = os.environ.copy() - env["NEXT_PUBLIC_API_URL"] = api_cfg.get("backend_url", "http://localhost:8916") - env["PORT"] = str(frontend_port) + server_ip = network_cfg.get("application", {}).get("server_ip", "localhost") + ssl_enabled = network_cfg.get("ssl", {}).get("ssl_enabled", False) + ports = network_cfg.get("ports", {}) - # Check if we should use 'npm run dev' or 'node server.js' - # For standalone, we might prefer 'npm run start' if built, or 'npm run dev' - # Bash script used 'npm run start' + # Dynamically construct API URL from master settings + if ssl_enabled: + back_ssl_port = ports.get("backend_ssl_port", 8918) + api_url = f"https://{server_ip}:{back_ssl_port}" + else: + back_port = ports.get("backend_port", 8916) + api_url = f"http://{server_ip}:{back_port}" + + logger.info(f"Dynamically injecting API URL: {api_url}") + + env = os.environ.copy() + env["NEXT_PUBLIC_API_URL"] = api_url + env["PORT"] = str(frontend_port) cmd = ["npm", "run", "dev"] if os.path.exists("frontend/server.js"): - # If built for production cmd = ["node", "frontend/server.js"] + log_file = None + if background: + logs_dir = os.environ.get("LOGS_DIR", "./logs") + log_path = os.path.join(logs_dir, "frontend_standalone.log") + log_file = open(log_path, "a") + logger.info(f"Frontend logs redirected to {log_path}") + try: p = subprocess.Popen( cmd, cwd="frontend" if os.path.isdir("frontend") else None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + stdout=log_file if background else subprocess.PIPE, + stderr=log_file if background else subprocess.PIPE, text=True, env=env, bufsize=1, - universal_newlines=True + universal_newlines=True, + start_new_session=background ) processes.append(p) - threading.Thread(target=log_stream, args=(p.stdout, "FRONTEND", Colors.MAGENTA), daemon=True).start() - threading.Thread(target=log_stream, args=(p.stderr, "FRONTEND", Colors.MAGENTA), daemon=True).start() + if not background: + threading.Thread(target=log_stream, args=(p.stdout, "FRONTEND", Colors.MAGENTA), daemon=True).start() + threading.Thread(target=log_stream, args=(p.stderr, "FRONTEND", Colors.MAGENTA), daemon=True).start() return p except FileNotFoundError: logger.error("npm or node not found. Please install Node.js") sys.exit(1) +def start_proxy(network_cfg: Dict[str, Any], background: bool = False): + ssl_cfg = network_cfg.get("ssl", {}) + if not ssl_cfg.get("ssl_enabled", False): + return None + + caddyfile = "Caddyfile.standalone" + if not os.path.exists(caddyfile): + logger.warning(f"{caddyfile} not found. SSL proxy will not start.") + return None + + logger.info("Starting SSL Proxy (Caddy)...") + + # Check if caddy is installed + try: + subprocess.run(["caddy", "version"], capture_output=True, check=True) + except: + logger.error("Caddy not found. Please install Caddy to use SSL proxy.") + return None + + cmd = ["caddy", "run", "--config", caddyfile, "--adapter", "caddyfile"] + + log_file = None + if background: + logs_dir = os.environ.get("LOGS_DIR", "./logs") + log_path = os.path.join(logs_dir, "proxy_standalone.log") + log_file = open(log_path, "a") + logger.info(f"Proxy logs redirected to {log_path}") + + try: + p = subprocess.Popen( + cmd, + stdout=log_file if background else subprocess.PIPE, + stderr=log_file if background else subprocess.PIPE, + text=True, + bufsize=1, + universal_newlines=True, + start_new_session=background + ) + processes.append(p) + + if not background: + threading.Thread(target=log_stream, args=(p.stdout, "PROXY", Colors.CYAN), daemon=True).start() + threading.Thread(target=log_stream, args=(p.stderr, "PROXY", Colors.CYAN), daemon=True).start() + + return p + except Exception as e: + logger.error(f"Failed to start Caddy: {e}") + return None + +def action_status(backend_port: int, frontend_port: int, network_cfg: Dict[str, Any]): + logger.info("Checking status...") + backend_up = is_port_in_use(backend_port) + frontend_up = is_port_in_use(frontend_port) + + ssl_cfg = network_cfg.get("ssl", {}) + ssl_enabled = ssl_cfg.get("ssl_enabled", False) + proxy_up = False + if ssl_enabled: + proxy_up = is_port_in_use(network_cfg.get("ports", {}).get("frontend_ssl_port", 8919)) + + pids = get_running_pids() + running_pids = [pid for pid in pids if is_pid_running(pid)] + + if backend_up or frontend_up or running_pids: + print(f"{Colors.GREEN}● aInventory Standalone is RUNNING{Colors.NC}") + print(f" - Backend (Port {backend_port}): {'UP' if backend_up else 'DOWN'}") + print(f" - Frontend (Port {frontend_port}): {'UP' if frontend_up else 'DOWN'}") + if ssl_enabled: + print(f" - SSL Proxy: {'UP' if proxy_up else 'DOWN'}") + if running_pids: + print(f" - Active PIDs: {', '.join(map(str, running_pids))}") + return True + else: + print(f"{Colors.RED}○ aInventory Standalone is STOPPED{Colors.NC}") + return False + +def action_stop(backend_port: int, frontend_port: int, network_cfg: Dict[str, Any]): + pids = get_running_pids() + + if pids: + logger.info(f"Stopping processes with PIDs: {', '.join(map(str, pids))}...") + for pid in pids: + if is_pid_running(pid): + try: + # Kill the whole process group if possible + os.killpg(os.getpgid(pid), signal.SIGTERM) + except: + try: + os.kill(pid, signal.SIGTERM) + except OSError: + pass + + time.sleep(2) + + for pid in pids: + if is_pid_running(pid): + try: + os.killpg(os.getpgid(pid), signal.SIGKILL) + except: + try: + os.kill(pid, signal.SIGKILL) + except OSError: + pass + + clear_pids() + + # Port-based cleanup (handles orphaned children like npm -> node) + cleanup_ports = [backend_port, frontend_port] + ports_cfg = network_cfg.get("ports", {}) + if network_cfg.get("ssl", {}).get("ssl_enabled"): + cleanup_ports.append(ports_cfg.get("backend_ssl_port", 8918)) + cleanup_ports.append(ports_cfg.get("frontend_ssl_port", 8919)) + + for port in cleanup_ports: + if is_port_in_use(port): + logger.info(f"Cleaning up orphaned process on port {port}...") + try: + # Use fuser-like logic to find the PID on that port + # Note: This is Linux specific + cmd = ["lsof", "-t", f"-i:{port}"] + result = subprocess.run(cmd, capture_output=True, text=True) + orphans = [int(p) for p in result.stdout.splitlines() if p.strip()] + for o_pid in orphans: + if o_pid != os.getpid(): + logger.info(f" - Killing orphan PID {o_pid}") + try: + os.kill(o_pid, signal.SIGKILL) + except OSError: + pass + except Exception as e: + logger.warning(f"Could not perform port-based cleanup: {e}") + + logger.info("Stopped.") + def main(): parser = argparse.ArgumentParser(description="TFM aInventory Standalone Launcher") + parser.add_argument("action", nargs="?", choices=["start", "stop", "restart", "status", "run"], default="run", + help="Action to perform: run (foreground), start (background), stop, restart, status") + parser.add_argument("--no-proxy", action="store_true", help="Disable SSL proxy even if enabled in config") group = parser.add_mutually_exclusive_group() - group.add_argument("--backend-only", action="store_true", help="Start only the backend") - group.add_argument("--frontend-only", action="store_true", help="Start only the frontend") + group.add_argument("--backend-only", action="store_true", help="Operation on backend only") + group.add_argument("--frontend-only", action="store_true", help="Operation on frontend only") args = parser.parse_args() @@ -174,43 +363,71 @@ def main(): ports_cfg = network_cfg.get("ports", {}) backend_port = ports_cfg.get("backend_port", 8916) frontend_port = ports_cfg.get("frontend_port", 8917) - - if not args.frontend_only: - if is_port_in_use(backend_port): - logger.error(f"Port {backend_port} is already in use. Cannot start backend.") - if not args.backend_only: - # If we are supposed to start both, we fail. - sys.exit(1) - else: - start_backend(backend_port, backend_cfg) - - if not args.backend_only: - if is_port_in_use(frontend_port): - logger.error(f"Port {frontend_port} is already in use. Cannot start frontend.") - if not args.frontend_only: - sys.exit(1) - else: - start_frontend(frontend_port, frontend_cfg) - - if not processes: - logger.error("No processes started. Check configuration and port availability.") - sys.exit(1) + + if args.action == "status": + action_status(backend_port, frontend_port, network_cfg) + return + + if args.action == "stop": + action_stop(backend_port, frontend_port, network_cfg) + return + + if args.action == "restart": + action_stop(backend_port, frontend_port, network_cfg) + # Fall through to start/run logic + args.action = "start" if args.action == "restart" else args.action + + if args.action in ["start", "run"]: + background = (args.action == "start") - logger.info("All services started. Press Ctrl+C to stop.") - - # Monitor processes - try: - while not shutdown_event.is_set(): - time.sleep(1) - for p in processes: - if p.poll() is not None: - logger.error(f"Process {p.pid} exited with code {p.returncode}") - shutdown_event.set() - break - except KeyboardInterrupt: - pass - finally: - signal_handler(None, None) + if not args.frontend_only: + if is_port_in_use(backend_port): + logger.error(f"Port {backend_port} is already in use. Backend cannot start.") + if not args.backend_only and args.action == "run": + sys.exit(1) + else: + start_backend(backend_port, backend_cfg, background=background) + + if not args.backend_only: + if is_port_in_use(frontend_port): + logger.error(f"Port {frontend_port} is already in use. Frontend cannot start.") + if not args.frontend_only and args.action == "run": + sys.exit(1) + else: + start_frontend(frontend_port, frontend_cfg, network_cfg, background=background) + + # Start SSL Proxy if enabled + if not args.no_proxy and not args.backend_only and not args.frontend_only: + start_proxy(network_cfg, background=background) + + if not processes: + logger.error("No processes started. Check configuration and port availability.") + sys.exit(1) + + # Save PIDs + save_pids([p.pid for p in processes]) + + if background: + logger.info(f"All services started in BACKGROUND. PIDs: {', '.join(str(p.pid) for p in processes)}") + logger.info(f"Use 'python3 {sys.argv[0]} status' to check state.") + return + + logger.info("All services started in FOREGROUND. Press Ctrl+C to stop.") + + # Monitor processes + try: + while not shutdown_event.is_set(): + time.sleep(1) + for p in processes: + if p.poll() is not None: + logger.error(f"Process {p.pid} exited with code {p.returncode}") + shutdown_event.set() + break + except KeyboardInterrupt: + pass + finally: + signal_handler(None, None) + clear_pids() if __name__ == "__main__": main() diff --git a/venv/bin/activate b/venv/bin/activate index c80dcf0e..554ef367 100644 --- a/venv/bin/activate +++ b/venv/bin/activate @@ -39,10 +39,10 @@ deactivate nondestructive if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then # transform D:\path\to\venv to /d/path/to/venv on MSYS # and to /cygdrive/d/path/to/venv on Cygwin - export VIRTUAL_ENV=$(cygpath /tmp/image-system-phase1/venv) + export VIRTUAL_ENV=$(cygpath /data/programare_AI/tfm_ainventory/venv) else # use the path as-is - export VIRTUAL_ENV=/tmp/image-system-phase1/venv + export VIRTUAL_ENV=/data/programare_AI/tfm_ainventory/venv fi _OLD_VIRTUAL_PATH="$PATH" diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh index 879044d5..adc20ef2 100644 --- a/venv/bin/activate.csh +++ b/venv/bin/activate.csh @@ -9,7 +9,7 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA # Unset irrelevant variables. deactivate nondestructive -setenv VIRTUAL_ENV /tmp/image-system-phase1/venv +setenv VIRTUAL_ENV /data/programare_AI/tfm_ainventory/venv set _OLD_VIRTUAL_PATH="$PATH" setenv PATH "$VIRTUAL_ENV/"bin":$PATH" diff --git a/venv/bin/activate.fish b/venv/bin/activate.fish index aa286400..b41da9a0 100644 --- a/venv/bin/activate.fish +++ b/venv/bin/activate.fish @@ -33,7 +33,7 @@ end # Unset irrelevant variables. deactivate nondestructive -set -gx VIRTUAL_ENV /tmp/image-system-phase1/venv +set -gx VIRTUAL_ENV /data/programare_AI/tfm_ainventory/venv set -gx _OLD_VIRTUAL_PATH $PATH set -gx PATH "$VIRTUAL_ENV/"bin $PATH diff --git a/venv/bin/coverage b/venv/bin/coverage index a8b59369..0e5b3031 100755 --- a/venv/bin/coverage +++ b/venv/bin/coverage @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from coverage.cmdline import main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main()) diff --git a/venv/bin/coverage-3.12 b/venv/bin/coverage-3.12 index c0c3c82f..60b621b0 100755 --- a/venv/bin/coverage-3.12 +++ b/venv/bin/coverage-3.12 @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from coverage.cmdline import main_deprecated if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main_deprecated()) diff --git a/venv/bin/coverage3 b/venv/bin/coverage3 index c0c3c82f..60b621b0 100755 --- a/venv/bin/coverage3 +++ b/venv/bin/coverage3 @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from coverage.cmdline import main_deprecated if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main_deprecated()) diff --git a/venv/bin/distro b/venv/bin/distro index 2fd08905..e8f2b72f 100755 --- a/venv/bin/distro +++ b/venv/bin/distro @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from distro.distro import main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main()) diff --git a/venv/bin/dotenv b/venv/bin/dotenv index cbe9dab1..e6f0d4d3 100755 --- a/venv/bin/dotenv +++ b/venv/bin/dotenv @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from dotenv.__main__ import cli if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(cli()) diff --git a/venv/bin/f2py b/venv/bin/f2py index 04ceea44..1b8d1781 100755 --- a/venv/bin/f2py +++ b/venv/bin/f2py @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from numpy.f2py.f2py2e import main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main()) diff --git a/venv/bin/fastapi b/venv/bin/fastapi index c615b216..ff32f4ed 100755 --- a/venv/bin/fastapi +++ b/venv/bin/fastapi @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from fastapi.cli import main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main()) diff --git a/venv/bin/httpx b/venv/bin/httpx index 072b9f8e..6238dc77 100755 --- a/venv/bin/httpx +++ b/venv/bin/httpx @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from httpx import main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main()) diff --git a/venv/bin/normalizer b/venv/bin/normalizer index f6c7a2fe..ad164a67 100755 --- a/venv/bin/normalizer +++ b/venv/bin/normalizer @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from charset_normalizer.cli import cli_detect if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(cli_detect()) diff --git a/venv/bin/numpy-config b/venv/bin/numpy-config index 0f2ee944..fddfe0dd 100755 --- a/venv/bin/numpy-config +++ b/venv/bin/numpy-config @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from numpy._configtool import main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main()) diff --git a/venv/bin/pip b/venv/bin/pip index 775541e4..ceb6e792 100755 --- a/venv/bin/pip +++ b/venv/bin/pip @@ -1,4 +1,4 @@ -#!/tmp/image-system-phase1/venv/bin/python3 +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 # -*- coding: utf-8 -*- import re import sys diff --git a/venv/bin/pip3 b/venv/bin/pip3 index 775541e4..ceb6e792 100755 --- a/venv/bin/pip3 +++ b/venv/bin/pip3 @@ -1,4 +1,4 @@ -#!/tmp/image-system-phase1/venv/bin/python3 +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 # -*- coding: utf-8 -*- import re import sys diff --git a/venv/bin/pip3.12 b/venv/bin/pip3.12 index 775541e4..ceb6e792 100755 --- a/venv/bin/pip3.12 +++ b/venv/bin/pip3.12 @@ -1,4 +1,4 @@ -#!/tmp/image-system-phase1/venv/bin/python3 +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 # -*- coding: utf-8 -*- import re import sys diff --git a/venv/bin/py.test b/venv/bin/py.test index e3858445..b98c8529 100755 --- a/venv/bin/py.test +++ b/venv/bin/py.test @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from pytest import console_main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(console_main()) diff --git a/venv/bin/pygmentize b/venv/bin/pygmentize index 03fa6375..e247cf64 100755 --- a/venv/bin/pygmentize +++ b/venv/bin/pygmentize @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from pygments.cmdline import main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main()) diff --git a/venv/bin/pyrsa-decrypt b/venv/bin/pyrsa-decrypt index cd1d2f91..a88e4b30 100755 --- a/venv/bin/pyrsa-decrypt +++ b/venv/bin/pyrsa-decrypt @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from rsa.cli import decrypt if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(decrypt()) diff --git a/venv/bin/pyrsa-encrypt b/venv/bin/pyrsa-encrypt index 513f22a1..9f89632c 100755 --- a/venv/bin/pyrsa-encrypt +++ b/venv/bin/pyrsa-encrypt @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from rsa.cli import encrypt if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(encrypt()) diff --git a/venv/bin/pyrsa-keygen b/venv/bin/pyrsa-keygen index 7c6ec24c..3e11be61 100755 --- a/venv/bin/pyrsa-keygen +++ b/venv/bin/pyrsa-keygen @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from rsa.cli import keygen if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(keygen()) diff --git a/venv/bin/pyrsa-priv2pub b/venv/bin/pyrsa-priv2pub index ef73c995..8a4b8e6e 100755 --- a/venv/bin/pyrsa-priv2pub +++ b/venv/bin/pyrsa-priv2pub @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from rsa.util import private_to_public if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(private_to_public()) diff --git a/venv/bin/pyrsa-sign b/venv/bin/pyrsa-sign index bc01a9ac..d9ad0419 100755 --- a/venv/bin/pyrsa-sign +++ b/venv/bin/pyrsa-sign @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from rsa.cli import sign if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(sign()) diff --git a/venv/bin/pyrsa-verify b/venv/bin/pyrsa-verify index de711552..5bd87df2 100755 --- a/venv/bin/pyrsa-verify +++ b/venv/bin/pyrsa-verify @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from rsa.cli import verify if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(verify()) diff --git a/venv/bin/pytest b/venv/bin/pytest index e3858445..b98c8529 100755 --- a/venv/bin/pytest +++ b/venv/bin/pytest @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from pytest import console_main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(console_main()) diff --git a/venv/bin/uvicorn b/venv/bin/uvicorn index ade51830..14ebab7f 100755 --- a/venv/bin/uvicorn +++ b/venv/bin/uvicorn @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from uvicorn.main import main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main()) diff --git a/venv/bin/watchfiles b/venv/bin/watchfiles index 3da9930a..33e44a78 100755 --- a/venv/bin/watchfiles +++ b/venv/bin/watchfiles @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from watchfiles.cli import cli if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(cli()) diff --git a/venv/bin/websockets b/venv/bin/websockets index b773bb63..a4dc8c87 100755 --- a/venv/bin/websockets +++ b/venv/bin/websockets @@ -1,8 +1,6 @@ -#!/tmp/image-system-phase1/venv/bin/python3 -# -*- coding: utf-8 -*- -import re +#!/data/programare_AI/tfm_ainventory/venv/bin/python3 import sys from websockets.cli import main if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.argv[0] = sys.argv[0].removesuffix('.exe') sys.exit(main()) diff --git a/venv/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/INSTALLER similarity index 100% rename from venv/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/INSTALLER rename to venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/INSTALLER diff --git a/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE b/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE new file mode 100644 index 00000000..f26bcf4d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE @@ -0,0 +1,279 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see https://opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA new file mode 100644 index 00000000..c632040d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA @@ -0,0 +1,123 @@ +Metadata-Version: 2.3 +Name: aiohappyeyeballs +Version: 2.6.1 +Summary: Happy Eyeballs for asyncio +License: PSF-2.0 +Author: J. Nick Koston +Author-email: nick@koston.org +Requires-Python: >=3.9 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Topic :: Software Development :: Libraries +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: License :: OSI Approved :: Python Software Foundation License +Project-URL: Bug Tracker, https://github.com/aio-libs/aiohappyeyeballs/issues +Project-URL: Changelog, https://github.com/aio-libs/aiohappyeyeballs/blob/main/CHANGELOG.md +Project-URL: Documentation, https://aiohappyeyeballs.readthedocs.io +Project-URL: Repository, https://github.com/aio-libs/aiohappyeyeballs +Description-Content-Type: text/markdown + +# aiohappyeyeballs + +

+ + CI Status + + + Documentation Status + + + Test coverage percentage + +

+

+ + Poetry + + + Ruff + + + pre-commit + +

+

+ + PyPI Version + + Supported Python versions + License +

+ +--- + +**Documentation**: https://aiohappyeyeballs.readthedocs.io + +**Source Code**: https://github.com/aio-libs/aiohappyeyeballs + +--- + +[Happy Eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs) +([RFC 8305](https://www.rfc-editor.org/rfc/rfc8305.html)) + +## Use case + +This library exists to allow connecting with +[Happy Eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs) +([RFC 8305](https://www.rfc-editor.org/rfc/rfc8305.html)) +when you +already have a list of addrinfo and not a DNS name. + +The stdlib version of `loop.create_connection()` +will only work when you pass in an unresolved name which +is not a good fit when using DNS caching or resolving +names via another method such as `zeroconf`. + +## Installation + +Install this via pip (or your favourite package manager): + +`pip install aiohappyeyeballs` + +## License + +[aiohappyeyeballs is licensed under the same terms as cpython itself.](https://github.com/python/cpython/blob/main/LICENSE) + +## Example usage + +```python + +addr_infos = await loop.getaddrinfo("example.org", 80) + +socket = await start_connection(addr_infos) +socket = await start_connection(addr_infos, local_addr_infos=local_addr_infos, happy_eyeballs_delay=0.2) + +transport, protocol = await loop.create_connection( + MyProtocol, sock=socket, ...) + +# Remove the first address for each family from addr_info +pop_addr_infos_interleave(addr_info, 1) + +# Remove all matching address from addr_info +remove_addr_infos(addr_info, "dead::beef::") + +# Convert a local_addr to local_addr_infos +local_addr_infos = addr_to_addr_infos(("127.0.0.1",0)) +``` + +## Credits + +This package contains code from cpython and is licensed under the same terms as cpython itself. + +This package was created with +[Copier](https://copier.readthedocs.io/) and the +[browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template) +project template. + diff --git a/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/RECORD new file mode 100644 index 00000000..9e0e2023 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/RECORD @@ -0,0 +1,16 @@ +aiohappyeyeballs-2.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aiohappyeyeballs-2.6.1.dist-info/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936 +aiohappyeyeballs-2.6.1.dist-info/METADATA,sha256=NSXlhJwAfi380eEjAo7BQ4P_TVal9xi0qkyZWibMsVM,5915 +aiohappyeyeballs-2.6.1.dist-info/RECORD,, +aiohappyeyeballs-2.6.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88 +aiohappyeyeballs/__init__.py,sha256=x7kktHEtaD9quBcWDJPuLeKyjuVAI-Jj14S9B_5hcTs,361 +aiohappyeyeballs/__pycache__/__init__.cpython-312.pyc,, +aiohappyeyeballs/__pycache__/_staggered.cpython-312.pyc,, +aiohappyeyeballs/__pycache__/impl.cpython-312.pyc,, +aiohappyeyeballs/__pycache__/types.cpython-312.pyc,, +aiohappyeyeballs/__pycache__/utils.cpython-312.pyc,, +aiohappyeyeballs/_staggered.py,sha256=edfVowFx-P-ywJjIEF3MdPtEMVODujV6CeMYr65otac,6900 +aiohappyeyeballs/impl.py,sha256=Dlcm2mTJ28ucrGnxkb_fo9CZzLAkOOBizOt7dreBbXE,9681 +aiohappyeyeballs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +aiohappyeyeballs/types.py,sha256=YZJIAnyoV4Dz0WFtlaf_OyE4EW7Xus1z7aIfNI6tDDQ,425 +aiohappyeyeballs/utils.py,sha256=on9GxIR0LhEfZu8P6Twi9hepX9zDanuZM20MWsb3xlQ,3028 diff --git a/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/WHEEL new file mode 100644 index 00000000..0582547b --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: poetry-core 2.1.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/aiohappyeyeballs/__init__.py b/venv/lib/python3.12/site-packages/aiohappyeyeballs/__init__.py new file mode 100644 index 00000000..71c689cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohappyeyeballs/__init__.py @@ -0,0 +1,14 @@ +__version__ = "2.6.1" + +from .impl import start_connection +from .types import AddrInfoType, SocketFactoryType +from .utils import addr_to_addr_infos, pop_addr_infos_interleave, remove_addr_infos + +__all__ = ( + "AddrInfoType", + "SocketFactoryType", + "addr_to_addr_infos", + "pop_addr_infos_interleave", + "remove_addr_infos", + "start_connection", +) diff --git a/venv/lib/python3.12/site-packages/aiohappyeyeballs/_staggered.py b/venv/lib/python3.12/site-packages/aiohappyeyeballs/_staggered.py new file mode 100644 index 00000000..9a4ba720 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohappyeyeballs/_staggered.py @@ -0,0 +1,207 @@ +import asyncio +import contextlib + +# PY3.9: Import Callable from typing until we drop Python 3.9 support +# https://github.com/python/cpython/issues/87131 +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Iterable, + List, + Optional, + Set, + Tuple, + TypeVar, + Union, +) + +_T = TypeVar("_T") + +RE_RAISE_EXCEPTIONS = (SystemExit, KeyboardInterrupt) + + +def _set_result(wait_next: "asyncio.Future[None]") -> None: + """Set the result of a future if it is not already done.""" + if not wait_next.done(): + wait_next.set_result(None) + + +async def _wait_one( + futures: "Iterable[asyncio.Future[Any]]", + loop: asyncio.AbstractEventLoop, +) -> _T: + """Wait for the first future to complete.""" + wait_next = loop.create_future() + + def _on_completion(fut: "asyncio.Future[Any]") -> None: + if not wait_next.done(): + wait_next.set_result(fut) + + for f in futures: + f.add_done_callback(_on_completion) + + try: + return await wait_next + finally: + for f in futures: + f.remove_done_callback(_on_completion) + + +async def staggered_race( + coro_fns: Iterable[Callable[[], Awaitable[_T]]], + delay: Optional[float], + *, + loop: Optional[asyncio.AbstractEventLoop] = None, +) -> Tuple[Optional[_T], Optional[int], List[Optional[BaseException]]]: + """ + Run coroutines with staggered start times and take the first to finish. + + This method takes an iterable of coroutine functions. The first one is + started immediately. From then on, whenever the immediately preceding one + fails (raises an exception), or when *delay* seconds has passed, the next + coroutine is started. This continues until one of the coroutines complete + successfully, in which case all others are cancelled, or until all + coroutines fail. + + The coroutines provided should be well-behaved in the following way: + + * They should only ``return`` if completed successfully. + + * They should always raise an exception if they did not complete + successfully. In particular, if they handle cancellation, they should + probably reraise, like this:: + + try: + # do work + except asyncio.CancelledError: + # undo partially completed work + raise + + Args: + ---- + coro_fns: an iterable of coroutine functions, i.e. callables that + return a coroutine object when called. Use ``functools.partial`` or + lambdas to pass arguments. + + delay: amount of time, in seconds, between starting coroutines. If + ``None``, the coroutines will run sequentially. + + loop: the event loop to use. If ``None``, the running loop is used. + + Returns: + ------- + tuple *(winner_result, winner_index, exceptions)* where + + - *winner_result*: the result of the winning coroutine, or ``None`` + if no coroutines won. + + - *winner_index*: the index of the winning coroutine in + ``coro_fns``, or ``None`` if no coroutines won. If the winning + coroutine may return None on success, *winner_index* can be used + to definitively determine whether any coroutine won. + + - *exceptions*: list of exceptions returned by the coroutines. + ``len(exceptions)`` is equal to the number of coroutines actually + started, and the order is the same as in ``coro_fns``. The winning + coroutine's entry is ``None``. + + """ + loop = loop or asyncio.get_running_loop() + exceptions: List[Optional[BaseException]] = [] + tasks: Set[asyncio.Task[Optional[Tuple[_T, int]]]] = set() + + async def run_one_coro( + coro_fn: Callable[[], Awaitable[_T]], + this_index: int, + start_next: "asyncio.Future[None]", + ) -> Optional[Tuple[_T, int]]: + """ + Run a single coroutine. + + If the coroutine fails, set the exception in the exceptions list and + start the next coroutine by setting the result of the start_next. + + If the coroutine succeeds, return the result and the index of the + coroutine in the coro_fns list. + + If SystemExit or KeyboardInterrupt is raised, re-raise it. + """ + try: + result = await coro_fn() + except RE_RAISE_EXCEPTIONS: + raise + except BaseException as e: + exceptions[this_index] = e + _set_result(start_next) # Kickstart the next coroutine + return None + + return result, this_index + + start_next_timer: Optional[asyncio.TimerHandle] = None + start_next: Optional[asyncio.Future[None]] + task: asyncio.Task[Optional[Tuple[_T, int]]] + done: Union[asyncio.Future[None], asyncio.Task[Optional[Tuple[_T, int]]]] + coro_iter = iter(coro_fns) + this_index = -1 + try: + while True: + if coro_fn := next(coro_iter, None): + this_index += 1 + exceptions.append(None) + start_next = loop.create_future() + task = loop.create_task(run_one_coro(coro_fn, this_index, start_next)) + tasks.add(task) + start_next_timer = ( + loop.call_later(delay, _set_result, start_next) if delay else None + ) + elif not tasks: + # We exhausted the coro_fns list and no tasks are running + # so we have no winner and all coroutines failed. + break + + while tasks or start_next: + done = await _wait_one( + (*tasks, start_next) if start_next else tasks, loop + ) + if done is start_next: + # The current task has failed or the timer has expired + # so we need to start the next task. + start_next = None + if start_next_timer: + start_next_timer.cancel() + start_next_timer = None + + # Break out of the task waiting loop to start the next + # task. + break + + if TYPE_CHECKING: + assert isinstance(done, asyncio.Task) + + tasks.remove(done) + if winner := done.result(): + return *winner, exceptions + finally: + # We either have: + # - a winner + # - all tasks failed + # - a KeyboardInterrupt or SystemExit. + + # + # If the timer is still running, cancel it. + # + if start_next_timer: + start_next_timer.cancel() + + # + # If there are any tasks left, cancel them and than + # wait them so they fill the exceptions list. + # + for task in tasks: + task.cancel() + with contextlib.suppress(asyncio.CancelledError): + await task + + return None, None, exceptions diff --git a/venv/lib/python3.12/site-packages/aiohappyeyeballs/impl.py b/venv/lib/python3.12/site-packages/aiohappyeyeballs/impl.py new file mode 100644 index 00000000..8f3919a0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohappyeyeballs/impl.py @@ -0,0 +1,259 @@ +"""Base implementation.""" + +import asyncio +import collections +import contextlib +import functools +import itertools +import socket +from typing import List, Optional, Sequence, Set, Union + +from . import _staggered +from .types import AddrInfoType, SocketFactoryType + + +async def start_connection( + addr_infos: Sequence[AddrInfoType], + *, + local_addr_infos: Optional[Sequence[AddrInfoType]] = None, + happy_eyeballs_delay: Optional[float] = None, + interleave: Optional[int] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + socket_factory: Optional[SocketFactoryType] = None, +) -> socket.socket: + """ + Connect to a TCP server. + + Create a socket connection to a specified destination. The + destination is specified as a list of AddrInfoType tuples as + returned from getaddrinfo(). + + The arguments are, in order: + + * ``family``: the address family, e.g. ``socket.AF_INET`` or + ``socket.AF_INET6``. + * ``type``: the socket type, e.g. ``socket.SOCK_STREAM`` or + ``socket.SOCK_DGRAM``. + * ``proto``: the protocol, e.g. ``socket.IPPROTO_TCP`` or + ``socket.IPPROTO_UDP``. + * ``canonname``: the canonical name of the address, e.g. + ``"www.python.org"``. + * ``sockaddr``: the socket address + + This method is a coroutine which will try to establish the connection + in the background. When successful, the coroutine returns a + socket. + + The expected use case is to use this method in conjunction with + loop.create_connection() to establish a connection to a server:: + + socket = await start_connection(addr_infos) + transport, protocol = await loop.create_connection( + MyProtocol, sock=socket, ...) + """ + if not (current_loop := loop): + current_loop = asyncio.get_running_loop() + + single_addr_info = len(addr_infos) == 1 + + if happy_eyeballs_delay is not None and interleave is None: + # If using happy eyeballs, default to interleave addresses by family + interleave = 1 + + if interleave and not single_addr_info: + addr_infos = _interleave_addrinfos(addr_infos, interleave) + + sock: Optional[socket.socket] = None + # uvloop can raise RuntimeError instead of OSError + exceptions: List[List[Union[OSError, RuntimeError]]] = [] + if happy_eyeballs_delay is None or single_addr_info: + # not using happy eyeballs + for addrinfo in addr_infos: + try: + sock = await _connect_sock( + current_loop, + exceptions, + addrinfo, + local_addr_infos, + None, + socket_factory, + ) + break + except (RuntimeError, OSError): + continue + else: # using happy eyeballs + open_sockets: Set[socket.socket] = set() + try: + sock, _, _ = await _staggered.staggered_race( + ( + functools.partial( + _connect_sock, + current_loop, + exceptions, + addrinfo, + local_addr_infos, + open_sockets, + socket_factory, + ) + for addrinfo in addr_infos + ), + happy_eyeballs_delay, + ) + finally: + # If we have a winner, staggered_race will + # cancel the other tasks, however there is a + # small race window where any of the other tasks + # can be done before they are cancelled which + # will leave the socket open. To avoid this problem + # we pass a set to _connect_sock to keep track of + # the open sockets and close them here if there + # are any "runner up" sockets. + for s in open_sockets: + if s is not sock: + with contextlib.suppress(OSError): + s.close() + open_sockets = None # type: ignore[assignment] + + if sock is None: + all_exceptions = [exc for sub in exceptions for exc in sub] + try: + first_exception = all_exceptions[0] + if len(all_exceptions) == 1: + raise first_exception + else: + # If they all have the same str(), raise one. + model = str(first_exception) + if all(str(exc) == model for exc in all_exceptions): + raise first_exception + # Raise a combined exception so the user can see all + # the various error messages. + msg = "Multiple exceptions: {}".format( + ", ".join(str(exc) for exc in all_exceptions) + ) + # If the errno is the same for all exceptions, raise + # an OSError with that errno. + if isinstance(first_exception, OSError): + first_errno = first_exception.errno + if all( + isinstance(exc, OSError) and exc.errno == first_errno + for exc in all_exceptions + ): + raise OSError(first_errno, msg) + elif isinstance(first_exception, RuntimeError) and all( + isinstance(exc, RuntimeError) for exc in all_exceptions + ): + raise RuntimeError(msg) + # We have a mix of OSError and RuntimeError + # so we have to pick which one to raise. + # and we raise OSError for compatibility + raise OSError(msg) + finally: + all_exceptions = None # type: ignore[assignment] + exceptions = None # type: ignore[assignment] + + return sock + + +async def _connect_sock( + loop: asyncio.AbstractEventLoop, + exceptions: List[List[Union[OSError, RuntimeError]]], + addr_info: AddrInfoType, + local_addr_infos: Optional[Sequence[AddrInfoType]] = None, + open_sockets: Optional[Set[socket.socket]] = None, + socket_factory: Optional[SocketFactoryType] = None, +) -> socket.socket: + """ + Create, bind and connect one socket. + + If open_sockets is passed, add the socket to the set of open sockets. + Any failure caught here will remove the socket from the set and close it. + + Callers can use this set to close any sockets that are not the winner + of all staggered tasks in the result there are runner up sockets aka + multiple winners. + """ + my_exceptions: List[Union[OSError, RuntimeError]] = [] + exceptions.append(my_exceptions) + family, type_, proto, _, address = addr_info + sock = None + try: + if socket_factory is not None: + sock = socket_factory(addr_info) + else: + sock = socket.socket(family=family, type=type_, proto=proto) + if open_sockets is not None: + open_sockets.add(sock) + sock.setblocking(False) + if local_addr_infos is not None: + for lfamily, _, _, _, laddr in local_addr_infos: + # skip local addresses of different family + if lfamily != family: + continue + try: + sock.bind(laddr) + break + except OSError as exc: + msg = ( + f"error while attempting to bind on " + f"address {laddr!r}: " + f"{(exc.strerror or '').lower()}" + ) + exc = OSError(exc.errno, msg) + my_exceptions.append(exc) + else: # all bind attempts failed + if my_exceptions: + raise my_exceptions.pop() + else: + raise OSError(f"no matching local address with {family=} found") + await loop.sock_connect(sock, address) + return sock + except (RuntimeError, OSError) as exc: + my_exceptions.append(exc) + if sock is not None: + if open_sockets is not None: + open_sockets.remove(sock) + try: + sock.close() + except OSError as e: + my_exceptions.append(e) + raise + raise + except: + if sock is not None: + if open_sockets is not None: + open_sockets.remove(sock) + try: + sock.close() + except OSError as e: + my_exceptions.append(e) + raise + raise + finally: + exceptions = my_exceptions = None # type: ignore[assignment] + + +def _interleave_addrinfos( + addrinfos: Sequence[AddrInfoType], first_address_family_count: int = 1 +) -> List[AddrInfoType]: + """Interleave list of addrinfo tuples by family.""" + # Group addresses by family + addrinfos_by_family: collections.OrderedDict[int, List[AddrInfoType]] = ( + collections.OrderedDict() + ) + for addr in addrinfos: + family = addr[0] + if family not in addrinfos_by_family: + addrinfos_by_family[family] = [] + addrinfos_by_family[family].append(addr) + addrinfos_lists = list(addrinfos_by_family.values()) + + reordered: List[AddrInfoType] = [] + if first_address_family_count > 1: + reordered.extend(addrinfos_lists[0][: first_address_family_count - 1]) + del addrinfos_lists[0][: first_address_family_count - 1] + reordered.extend( + a + for a in itertools.chain.from_iterable(itertools.zip_longest(*addrinfos_lists)) + if a is not None + ) + return reordered diff --git a/venv/lib/python3.12/site-packages/pip-24.0.dist-info/REQUESTED b/venv/lib/python3.12/site-packages/aiohappyeyeballs/py.typed similarity index 100% rename from venv/lib/python3.12/site-packages/pip-24.0.dist-info/REQUESTED rename to venv/lib/python3.12/site-packages/aiohappyeyeballs/py.typed diff --git a/venv/lib/python3.12/site-packages/aiohappyeyeballs/types.py b/venv/lib/python3.12/site-packages/aiohappyeyeballs/types.py new file mode 100644 index 00000000..e8c75074 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohappyeyeballs/types.py @@ -0,0 +1,17 @@ +"""Types for aiohappyeyeballs.""" + +import socket + +# PY3.9: Import Callable from typing until we drop Python 3.9 support +# https://github.com/python/cpython/issues/87131 +from typing import Callable, Tuple, Union + +AddrInfoType = Tuple[ + Union[int, socket.AddressFamily], + Union[int, socket.SocketKind], + int, + str, + Tuple, # type: ignore[type-arg] +] + +SocketFactoryType = Callable[[AddrInfoType], socket.socket] diff --git a/venv/lib/python3.12/site-packages/aiohappyeyeballs/utils.py b/venv/lib/python3.12/site-packages/aiohappyeyeballs/utils.py new file mode 100644 index 00000000..ea29adb9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohappyeyeballs/utils.py @@ -0,0 +1,97 @@ +"""Utility functions for aiohappyeyeballs.""" + +import ipaddress +import socket +from typing import Dict, List, Optional, Tuple, Union + +from .types import AddrInfoType + + +def addr_to_addr_infos( + addr: Optional[ + Union[Tuple[str, int, int, int], Tuple[str, int, int], Tuple[str, int]] + ], +) -> Optional[List[AddrInfoType]]: + """Convert an address tuple to a list of addr_info tuples.""" + if addr is None: + return None + host = addr[0] + port = addr[1] + is_ipv6 = ":" in host + if is_ipv6: + flowinfo = 0 + scopeid = 0 + addr_len = len(addr) + if addr_len >= 4: + scopeid = addr[3] # type: ignore[misc] + if addr_len >= 3: + flowinfo = addr[2] # type: ignore[misc] + addr = (host, port, flowinfo, scopeid) + family = socket.AF_INET6 + else: + addr = (host, port) + family = socket.AF_INET + return [(family, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", addr)] + + +def pop_addr_infos_interleave( + addr_infos: List[AddrInfoType], interleave: Optional[int] = None +) -> None: + """ + Pop addr_info from the list of addr_infos by family up to interleave times. + + The interleave parameter is used to know how many addr_infos for + each family should be popped of the top of the list. + """ + seen: Dict[int, int] = {} + if interleave is None: + interleave = 1 + to_remove: List[AddrInfoType] = [] + for addr_info in addr_infos: + family = addr_info[0] + if family not in seen: + seen[family] = 0 + if seen[family] < interleave: + to_remove.append(addr_info) + seen[family] += 1 + for addr_info in to_remove: + addr_infos.remove(addr_info) + + +def _addr_tuple_to_ip_address( + addr: Union[Tuple[str, int], Tuple[str, int, int, int]], +) -> Union[ + Tuple[ipaddress.IPv4Address, int], Tuple[ipaddress.IPv6Address, int, int, int] +]: + """Convert an address tuple to an IPv4Address.""" + return (ipaddress.ip_address(addr[0]), *addr[1:]) + + +def remove_addr_infos( + addr_infos: List[AddrInfoType], + addr: Union[Tuple[str, int], Tuple[str, int, int, int]], +) -> None: + """ + Remove an address from the list of addr_infos. + + The addr value is typically the return value of + sock.getpeername(). + """ + bad_addrs_infos: List[AddrInfoType] = [] + for addr_info in addr_infos: + if addr_info[-1] == addr: + bad_addrs_infos.append(addr_info) + if bad_addrs_infos: + for bad_addr_info in bad_addrs_infos: + addr_infos.remove(bad_addr_info) + return + # Slow path in case addr is formatted differently + match_addr = _addr_tuple_to_ip_address(addr) + for addr_info in addr_infos: + if match_addr == _addr_tuple_to_ip_address(addr_info[-1]): + bad_addrs_infos.append(addr_info) + if bad_addrs_infos: + for bad_addr_info in bad_addrs_infos: + addr_infos.remove(bad_addr_info) + return + raise ValueError(f"Address {addr} not found in addr_infos") diff --git a/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/INSTALLER similarity index 100% rename from venv/lib/python3.12/site-packages/click-8.3.2.dist-info/INSTALLER rename to venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/INSTALLER diff --git a/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/METADATA b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/METADATA new file mode 100644 index 00000000..3977fcdf --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/METADATA @@ -0,0 +1,262 @@ +Metadata-Version: 2.4 +Name: aiohttp +Version: 3.13.5 +Summary: Async http client/server framework (asyncio) +Maintainer-email: aiohttp team +License: Apache-2.0 AND MIT +Project-URL: Homepage, https://github.com/aio-libs/aiohttp +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: GitHub Actions, https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/aiohttp +Project-URL: Docs: Changelog, https://docs.aiohttp.org/en/stable/changes.html +Project-URL: Docs: RTD, https://docs.aiohttp.org +Project-URL: GitHub: issues, https://github.com/aio-libs/aiohttp/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/aiohttp +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: AsyncIO +Classifier: Intended Audience :: Developers +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt +License-File: vendor/llhttp/LICENSE +Requires-Dist: aiohappyeyeballs>=2.5.0 +Requires-Dist: aiosignal>=1.4.0 +Requires-Dist: async-timeout<6.0,>=4.0; python_version < "3.11" +Requires-Dist: attrs>=17.3.0 +Requires-Dist: frozenlist>=1.1.1 +Requires-Dist: multidict<7.0,>=4.5 +Requires-Dist: propcache>=0.2.0 +Requires-Dist: yarl<2.0,>=1.17.0 +Provides-Extra: speedups +Requires-Dist: aiodns>=3.3.0; extra == "speedups" +Requires-Dist: Brotli>=1.2; platform_python_implementation == "CPython" and extra == "speedups" +Requires-Dist: brotlicffi>=1.2; platform_python_implementation != "CPython" and extra == "speedups" +Requires-Dist: backports.zstd; (platform_python_implementation == "CPython" and python_version < "3.14") and extra == "speedups" +Dynamic: license-file + +================================== +Async http client/server framework +================================== + +.. image:: https://raw.githubusercontent.com/aio-libs/aiohttp/master/docs/aiohttp-plain.svg + :height: 64px + :width: 64px + :alt: aiohttp logo + +| + +.. image:: https://github.com/aio-libs/aiohttp/workflows/CI/badge.svg + :target: https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI + :alt: GitHub Actions status for master branch + +.. image:: https://codecov.io/gh/aio-libs/aiohttp/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/aiohttp + :alt: codecov.io status for master branch + +.. image:: https://badge.fury.io/py/aiohttp.svg + :target: https://pypi.org/project/aiohttp + :alt: Latest PyPI package version + +.. image:: https://img.shields.io/pypi/dm/aiohttp + :target: https://pypistats.org/packages/aiohttp + :alt: Downloads count + +.. image:: https://readthedocs.org/projects/aiohttp/badge/?version=latest + :target: https://docs.aiohttp.org/ + :alt: Latest Read The Docs + +.. image:: https://img.shields.io/endpoint?url=https://codspeed.io/badge.json + :target: https://codspeed.io/aio-libs/aiohttp + :alt: Codspeed.io status for aiohttp + + +Key Features +============ + +- Supports both client and server side of HTTP protocol. +- Supports both client and server Web-Sockets out-of-the-box and avoids + Callback Hell. +- Provides Web-server with middleware and pluggable routing. + + +Getting started +=============== + +Client +------ + +To get something from the web: + +.. code-block:: python + + import aiohttp + import asyncio + + async def main(): + + async with aiohttp.ClientSession() as session: + async with session.get('http://python.org') as response: + + print("Status:", response.status) + print("Content-type:", response.headers['content-type']) + + html = await response.text() + print("Body:", html[:15], "...") + + asyncio.run(main()) + +This prints: + +.. code-block:: + + Status: 200 + Content-type: text/html; charset=utf-8 + Body: ... + +Coming from `requests `_ ? Read `why we need so many lines `_. + +Server +------ + +An example using a simple server: + +.. code-block:: python + + # examples/server_simple.py + from aiohttp import web + + async def handle(request): + name = request.match_info.get('name', "Anonymous") + text = "Hello, " + name + return web.Response(text=text) + + async def wshandle(request): + ws = web.WebSocketResponse() + await ws.prepare(request) + + async for msg in ws: + if msg.type == web.WSMsgType.text: + await ws.send_str("Hello, {}".format(msg.data)) + elif msg.type == web.WSMsgType.binary: + await ws.send_bytes(msg.data) + elif msg.type == web.WSMsgType.close: + break + + return ws + + + app = web.Application() + app.add_routes([web.get('/', handle), + web.get('/echo', wshandle), + web.get('/{name}', handle)]) + + if __name__ == '__main__': + web.run_app(app) + + +Documentation +============= + +https://aiohttp.readthedocs.io/ + + +Demos +===== + +https://github.com/aio-libs/aiohttp-demos + + +External links +============== + +* `Third party libraries + `_ +* `Built with aiohttp + `_ +* `Powered by aiohttp + `_ + +Feel free to make a Pull Request for adding your link to these pages! + + +Communication channels +====================== + +*aio-libs Discussions*: https://github.com/aio-libs/aiohttp/discussions + +*Matrix*: `#aio-libs:matrix.org `_ + +We support `Stack Overflow +`_. +Please add *aiohttp* tag to your question there. + +Requirements +============ + +- attrs_ +- multidict_ +- yarl_ +- frozenlist_ + +Optionally you may install the aiodns_ library (highly recommended for sake of speed). + +.. _aiodns: https://pypi.python.org/pypi/aiodns +.. _attrs: https://github.com/python-attrs/attrs +.. _multidict: https://pypi.python.org/pypi/multidict +.. _frozenlist: https://pypi.org/project/frozenlist/ +.. _yarl: https://pypi.python.org/pypi/yarl +.. _async-timeout: https://pypi.python.org/pypi/async_timeout + +License +======= + +``aiohttp`` is offered under the Apache 2 license. + + +Keepsafe +======== + +The aiohttp community would like to thank Keepsafe +(https://www.getkeepsafe.com) for its support in the early days of +the project. + + +Source code +=========== + +The latest developer version is available in a GitHub repository: +https://github.com/aio-libs/aiohttp + +Benchmarks +========== + +If you are interested in efficiency, the AsyncIO community maintains a +list of benchmarks on the official wiki: +https://github.com/python/asyncio/wiki/Benchmarks + +-------- + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + +.. image:: https://insights.linuxfoundation.org/api/badge/health-score?project=aiohttp + :target: https://insights.linuxfoundation.org/project/aiohttp + :alt: LFX Health Score diff --git a/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/RECORD b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/RECORD new file mode 100644 index 00000000..4cc4f8d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/RECORD @@ -0,0 +1,139 @@ +aiohttp-3.13.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aiohttp-3.13.5.dist-info/METADATA,sha256=yVHd0XDIuuar5cgTh5EZI2mDXCREeUCWCZ-VA6gWvUQ,8145 +aiohttp-3.13.5.dist-info/RECORD,, +aiohttp-3.13.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +aiohttp-3.13.5.dist-info/WHEEL,sha256=Tc3fF66yn9Kh-hkUUsdKQyuB9Lw0CDoeANnEbSVc3f4,190 +aiohttp-3.13.5.dist-info/licenses/LICENSE.txt,sha256=n4DQ2311WpQdtFchcsJw7L2PCCuiFd3QlZhZQu2Uqes,588 +aiohttp-3.13.5.dist-info/licenses/vendor/llhttp/LICENSE,sha256=68qFTgE0zSVtZzYnwgSZ9CV363S6zwi58ltianPJEnc,1105 +aiohttp-3.13.5.dist-info/top_level.txt,sha256=iv-JIaacmTl-hSho3QmphcKnbRRYx1st47yjz_178Ro,8 +aiohttp/.hash/_cparser.pxd.hash,sha256=pjs-sEXNw_eijXGAedwG-BHnlFp8B7sOCgUagIWaU2A,121 +aiohttp/.hash/_find_header.pxd.hash,sha256=_mbpD6vM-CVCKq3ulUvsOAz5Wdo88wrDzfpOsMQaMNA,125 +aiohttp/.hash/_http_parser.pyx.hash,sha256=T47Ily1qaTY3xOHgBgJZE5A_bKruDtCG_droC3m7BD8,125 +aiohttp/.hash/_http_writer.pyx.hash,sha256=DO9V6lTsDngY8e4y5O0ghHRc-RNGq-pVt_dLNHJy7xA,125 +aiohttp/.hash/hdrs.py.hash,sha256=v6IaKbsxjsdQxBzhb5AjP0x_9G3rUe84D7avf7AI4cs,116 +aiohttp/__init__.py,sha256=r1a3wmBz41XCNY0dqSnCcgfqrDYSDYD-_vKBN3Bfy3Y,8302 +aiohttp/__pycache__/__init__.cpython-312.pyc,, +aiohttp/__pycache__/_cookie_helpers.cpython-312.pyc,, +aiohttp/__pycache__/abc.cpython-312.pyc,, +aiohttp/__pycache__/base_protocol.cpython-312.pyc,, +aiohttp/__pycache__/client.cpython-312.pyc,, +aiohttp/__pycache__/client_exceptions.cpython-312.pyc,, +aiohttp/__pycache__/client_middleware_digest_auth.cpython-312.pyc,, +aiohttp/__pycache__/client_middlewares.cpython-312.pyc,, +aiohttp/__pycache__/client_proto.cpython-312.pyc,, +aiohttp/__pycache__/client_reqrep.cpython-312.pyc,, +aiohttp/__pycache__/client_ws.cpython-312.pyc,, +aiohttp/__pycache__/compression_utils.cpython-312.pyc,, +aiohttp/__pycache__/connector.cpython-312.pyc,, +aiohttp/__pycache__/cookiejar.cpython-312.pyc,, +aiohttp/__pycache__/formdata.cpython-312.pyc,, +aiohttp/__pycache__/hdrs.cpython-312.pyc,, +aiohttp/__pycache__/helpers.cpython-312.pyc,, +aiohttp/__pycache__/http.cpython-312.pyc,, +aiohttp/__pycache__/http_exceptions.cpython-312.pyc,, +aiohttp/__pycache__/http_parser.cpython-312.pyc,, +aiohttp/__pycache__/http_websocket.cpython-312.pyc,, +aiohttp/__pycache__/http_writer.cpython-312.pyc,, +aiohttp/__pycache__/log.cpython-312.pyc,, +aiohttp/__pycache__/multipart.cpython-312.pyc,, +aiohttp/__pycache__/payload.cpython-312.pyc,, +aiohttp/__pycache__/payload_streamer.cpython-312.pyc,, +aiohttp/__pycache__/pytest_plugin.cpython-312.pyc,, +aiohttp/__pycache__/resolver.cpython-312.pyc,, +aiohttp/__pycache__/streams.cpython-312.pyc,, +aiohttp/__pycache__/tcp_helpers.cpython-312.pyc,, +aiohttp/__pycache__/test_utils.cpython-312.pyc,, +aiohttp/__pycache__/tracing.cpython-312.pyc,, +aiohttp/__pycache__/typedefs.cpython-312.pyc,, +aiohttp/__pycache__/web.cpython-312.pyc,, +aiohttp/__pycache__/web_app.cpython-312.pyc,, +aiohttp/__pycache__/web_exceptions.cpython-312.pyc,, +aiohttp/__pycache__/web_fileresponse.cpython-312.pyc,, +aiohttp/__pycache__/web_log.cpython-312.pyc,, +aiohttp/__pycache__/web_middlewares.cpython-312.pyc,, +aiohttp/__pycache__/web_protocol.cpython-312.pyc,, +aiohttp/__pycache__/web_request.cpython-312.pyc,, +aiohttp/__pycache__/web_response.cpython-312.pyc,, +aiohttp/__pycache__/web_routedef.cpython-312.pyc,, +aiohttp/__pycache__/web_runner.cpython-312.pyc,, +aiohttp/__pycache__/web_server.cpython-312.pyc,, +aiohttp/__pycache__/web_urldispatcher.cpython-312.pyc,, +aiohttp/__pycache__/web_ws.cpython-312.pyc,, +aiohttp/__pycache__/worker.cpython-312.pyc,, +aiohttp/_cookie_helpers.py,sha256=_p7y-B8OCAk7FLjByiuwFIpDLGuNoJn3_vixzymAFnE,13659 +aiohttp/_cparser.pxd,sha256=UnbUYCHg4NdXfgyRVYAMv2KTLWClB4P-xCrvtj_r7ew,4295 +aiohttp/_find_header.pxd,sha256=0GfwFCPN2zxEKTO1_MA5sYq2UfzsG8kcV3aTqvwlz3g,68 +aiohttp/_headers.pxi,sha256=n701k28dVPjwRnx5j6LpJhLTfj7dqu2vJt7f0O60Oyg,2007 +aiohttp/_http_parser.cpython-312-x86_64-linux-gnu.so,sha256=8_yExteNcPiEn2dGe3fYFBx_b_qsQwVwhrN_0j6dMQQ,2947720 +aiohttp/_http_parser.pyx,sha256=veuwNKb6YzBJoO3nbvrkOCaTzbcROS5MVksUJhDHNgg,30036 +aiohttp/_http_writer.cpython-312-x86_64-linux-gnu.so,sha256=2dzfCN0-Sz-bY-MqfHits-FvPnvcxBYgqQrAQSxeIvo,472496 +aiohttp/_http_writer.pyx,sha256=TXNOC90txk4wnPakxex_ygSbr3xq1UxaCi2T4gv2teY,4690 +aiohttp/_websocket/.hash/mask.pxd.hash,sha256=Y0zBddk_ck3pi9-BFzMcpkcvCKvwvZ4GTtZFb9u1nxQ,128 +aiohttp/_websocket/.hash/mask.pyx.hash,sha256=90owpXYM8_kIma4KUcOxhWSk-Uv4NVMBoCYeFM1B3d0,128 +aiohttp/_websocket/.hash/reader_c.pxd.hash,sha256=5xf3oobk6vx4xbJm-xtZ1_QufB8fYFtLQV2MNdqUc1w,132 +aiohttp/_websocket/__init__.py,sha256=Mar3R9_vBN_Ea4lsW7iTAVXD7OKswKPGqF5xgSyt77k,44 +aiohttp/_websocket/__pycache__/__init__.cpython-312.pyc,, +aiohttp/_websocket/__pycache__/helpers.cpython-312.pyc,, +aiohttp/_websocket/__pycache__/models.cpython-312.pyc,, +aiohttp/_websocket/__pycache__/reader.cpython-312.pyc,, +aiohttp/_websocket/__pycache__/reader_c.cpython-312.pyc,, +aiohttp/_websocket/__pycache__/reader_py.cpython-312.pyc,, +aiohttp/_websocket/__pycache__/writer.cpython-312.pyc,, +aiohttp/_websocket/helpers.py,sha256=P-XLv8IUaihKzDenVUqfKU5DJbWE5HvG8uhvUZK8Ic4,5038 +aiohttp/_websocket/mask.cpython-312-x86_64-linux-gnu.so,sha256=EpRwPJm1K1yavMCd9llAWdT4AsqKx_QEN0rb0eJH_Kc,263512 +aiohttp/_websocket/mask.pxd,sha256=sBmZ1Amym9kW4Ge8lj1fLZ7mPPya4LzLdpkQExQXv5M,112 +aiohttp/_websocket/mask.pyx,sha256=BHjOtV0O0w7xp9p0LNADRJvGmgfPn9sGeJvSs0fL__4,1397 +aiohttp/_websocket/models.py,sha256=XAzjs_8JYszWXIgZ6R3ZRrF-tX9Q_6LiD49WRYojopM,2121 +aiohttp/_websocket/reader.py,sha256=eC4qS0c5sOeQ2ebAHLaBpIaTVFaSKX79pY2xvh3Pqyw,1030 +aiohttp/_websocket/reader_c.cpython-312-x86_64-linux-gnu.so,sha256=GYd-y-IkGkOIp3vmma5BmZgmG9Py9fLTybcmMWyHNf0,1822128 +aiohttp/_websocket/reader_c.pxd,sha256=nl_njtDrzlQU0rjgGGjZDB-swguE0tX_bCPobkShVa4,2625 +aiohttp/_websocket/reader_c.py,sha256=V5YtZ2gj2BjE2Q-W9sR_MdAl1VAm1pB7ZjozVJcOpbg,18868 +aiohttp/_websocket/reader_py.py,sha256=V5YtZ2gj2BjE2Q-W9sR_MdAl1VAm1pB7ZjozVJcOpbg,18868 +aiohttp/_websocket/writer.py,sha256=2OvSktPmNh_g20h1cXJt2Xu8u6IvswnPjdur7OwBbJk,11261 +aiohttp/abc.py,sha256=M66F4S6m00bIEn7y4ha_XLTMDmVQ9dPihfOVB0pGfOo,7149 +aiohttp/base_protocol.py,sha256=Tp8cxUPQvv9kUPk3w6lAzk6d2MAzV3scwI_3Go3C47c,3025 +aiohttp/client.py,sha256=JkHGrrJq1pSI7QEJw0jZKx5TLLzM87of8Yx-62m4x6g,58795 +aiohttp/client_exceptions.py,sha256=t6vDg0ZhHDvij6E89Nv473zLpNu3CyMPAp0LqLHOc9I,11744 +aiohttp/client_middleware_digest_auth.py,sha256=rInzAPEmFsaF42oVORmYSx56og1B7jZUSmmIdFD0MQ8,17641 +aiohttp/client_middlewares.py,sha256=kP5N9CMzQPMGPIEydeVUiLUTLsw8Vl8Gr4qAWYdu3vM,1918 +aiohttp/client_proto.py,sha256=eimrsOVNMt_y62gmXZ4JoBI5VZ0kGaW6WnqMkB1cnNQ,12179 +aiohttp/client_reqrep.py,sha256=eEREDrZ0M8ZFTt1wjHduR-P8_sm40K65gNz-iMGYask,53391 +aiohttp/client_ws.py,sha256=1CIjIXwyzOMIYw6AjUES4-qUwbyVHW1seJKQfg_Rta8,15109 +aiohttp/compression_utils.py,sha256=hJ2LXhN2OWukFHm5b78TJFGKcAiL2kthi9Sf5PRYO-U,11738 +aiohttp/connector.py,sha256=0AFVMAx7NwkIbovQ0qecQ3JO2bpEZpxMG_OnGZ3tP3E,69451 +aiohttp/cookiejar.py,sha256=e28ZMQwJ5P0vbPX1OX4Se7-k3zeGvocFEqzGhwpG53k,18922 +aiohttp/formdata.py,sha256=0cxe-naBSFp_NHB1l-TUSbhesU4dpNvwJMzhsLYoSns,6625 +aiohttp/hdrs.py,sha256=2rj5MyA-6yRdYPhW5UKkW4iNWhEAlGIOSBH5D4FmKNE,5111 +aiohttp/helpers.py,sha256=Q1307PCEnWz4RP8crUw8dk58c0YF2Ei3JywkKfRxz5E,30629 +aiohttp/http.py,sha256=8o8j8xH70OWjnfTWA9V44NR785QPxEPrUtzMXiAVpwc,1842 +aiohttp/http_exceptions.py,sha256=Z4IiKYxyfn1Pe947OfqW6NjXoVd12bDI4eKBZKC3Htw,3085 +aiohttp/http_parser.py,sha256=TIvopmjIH3TSTrVTbbqxpBWBpkEuOaNjkzES0-fm4-4,40472 +aiohttp/http_websocket.py,sha256=8VXFKw6KQUEmPg48GtRMB37v0gTK7A0inoxXuDxMZEc,842 +aiohttp/http_writer.py,sha256=4sbiFD_RkE0x_xlAPXwGrL0iLS53L72Qw13hBPaQLls,12508 +aiohttp/log.py,sha256=BbNKx9e3VMIm0xYjZI0IcBBoS7wjdeIeSaiJE7-qK2g,325 +aiohttp/multipart.py,sha256=qMDEHLf64qQcPQLZv4RdwKUDB2L1CC_vN-5Z-P8wDzw,42805 +aiohttp/payload.py,sha256=O6nsYNULL7AeM2cyJ6TYX73ncVnL5xJwt5AegxwMKqw,40874 +aiohttp/payload_streamer.py,sha256=ZzEYyfzcjGWkVkK3XR2pBthSCSIykYvY3Wr5cGQ2eTc,2211 +aiohttp/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 +aiohttp/pytest_plugin.py,sha256=z4XwqmsKdyJCKxbGiA5kFf90zcedvomqk4RqjZbhKNk,12901 +aiohttp/resolver.py,sha256=gsrfUpFf8iHlcHfJvY-1fiBHW3PRvRVNb5lNZBg3zlY,10031 +aiohttp/streams.py,sha256=i32xpXEjGL2c6ifepnBxBto5y9wXobzzYAIsi8kAkKI,23920 +aiohttp/tcp_helpers.py,sha256=BSadqVWaBpMFDRWnhaaR941N9MiDZ7bdTrxgCb0CW-M,961 +aiohttp/test_utils.py,sha256=7hMcJnOUSkZ54Axgdvveeth5y6R_B2T-0H2xX7mKWYc,23130 +aiohttp/tracing.py,sha256=-6aaW6l0J9uJD45LzR4cijYH0j62pt0U_nn_aVzFku4,14558 +aiohttp/typedefs.py,sha256=wUlqwe9Mw9W8jT3HsYJcYk00qP3EMPz3nTkYXmeNN48,1657 +aiohttp/web.py,sha256=JzSNmejg5G6YeFAnkIgZfytqbU86sNu844yYKmoUpqs,17852 +aiohttp/web_app.py,sha256=lGU_aAMN-h3wy-LTTHi6SeKH8ydt1G51BXcCspgD5ZA,19452 +aiohttp/web_exceptions.py,sha256=aXW8MStpRS7JOFxYDPgPlWojMJP9RhxyAqvq2g0jOCE,10495 +aiohttp/web_fileresponse.py,sha256=arimioSXf0rZQ46H9ejCip4hVN6ri_faFWfuP_XL_us,16374 +aiohttp/web_log.py,sha256=1hhC05PJizS2POVfNxncorNM_fjYHMx2TbcpP6isxkQ,8624 +aiohttp/web_middlewares.py,sha256=sFI0AgeNjdyAjuz92QtMIpngmJSOxrqe2Jfbs4BNUu0,4165 +aiohttp/web_protocol.py,sha256=ZEwjomG6Y2olq2b967UXDezwYnuLJJHDNl48SgHpKJs,27210 +aiohttp/web_request.py,sha256=xNCrsUQSqTB64G1_9QiC9xfQmdgkJwYkGfPBwnfIYQo,30406 +aiohttp/web_response.py,sha256=DofOPOU4be_upiqouWjxfhMLyubtRFvOGnJZbIamH6s,29366 +aiohttp/web_routedef.py,sha256=VT1GAx6BrawoDh5RwBwBu5wSABSqgWwAe74AUCyZAEo,6110 +aiohttp/web_runner.py,sha256=v1G1nKiOOQgFnTSR4IMc6I9ReEFDMaHtMLvO_roDM-A,11786 +aiohttp/web_server.py,sha256=-9WDKUAiR9ll-rSdwXSqG6YjaoW79d1R4y0BGSqgUMA,2888 +aiohttp/web_urldispatcher.py,sha256=9pJ6b3LuujtZuAURNYh0tdhgCJn2ZFrakeEySNwNqhU,44512 +aiohttp/web_ws.py,sha256=lItgmyatkXh0M6EY7JoZnSZkUl6R0wv8B88X4ILqQbU,22739 +aiohttp/worker.py,sha256=o6BL8dbCGYysB76i_yGGvYagc9gyy2PyW1UisiOznjA,8345 diff --git a/venv/lib/python3.12/site-packages/python_dotenv-1.2.2.dist-info/REQUESTED b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/REQUESTED similarity index 100% rename from venv/lib/python3.12/site-packages/python_dotenv-1.2.2.dist-info/REQUESTED rename to venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/REQUESTED diff --git a/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/WHEEL b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/WHEEL new file mode 100644 index 00000000..0493eaf8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/WHEEL @@ -0,0 +1,7 @@ +Wheel-Version: 1.0 +Generator: setuptools (82.0.1) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/licenses/LICENSE.txt b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/licenses/LICENSE.txt new file mode 100644 index 00000000..e497a322 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/licenses/LICENSE.txt @@ -0,0 +1,13 @@ + Copyright aio-libs contributors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/licenses/vendor/llhttp/LICENSE b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/licenses/vendor/llhttp/LICENSE new file mode 100644 index 00000000..6c1512dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/licenses/vendor/llhttp/LICENSE @@ -0,0 +1,22 @@ +This software is licensed under the MIT License. + +Copyright Fedor Indutny, 2018. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/top_level.txt new file mode 100644 index 00000000..ee4ba4f3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp-3.13.5.dist-info/top_level.txt @@ -0,0 +1 @@ +aiohttp diff --git a/venv/lib/python3.12/site-packages/aiohttp/.hash/_cparser.pxd.hash b/venv/lib/python3.12/site-packages/aiohttp/.hash/_cparser.pxd.hash new file mode 100644 index 00000000..3f5cd0e6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/.hash/_cparser.pxd.hash @@ -0,0 +1 @@ +5276d46021e0e0d7577e0c9155800cbf62932d60a50783fec42aefb63febedec /home/runner/work/aiohttp/aiohttp/aiohttp/_cparser.pxd diff --git a/venv/lib/python3.12/site-packages/aiohttp/.hash/_find_header.pxd.hash b/venv/lib/python3.12/site-packages/aiohttp/.hash/_find_header.pxd.hash new file mode 100644 index 00000000..f006c2de --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/.hash/_find_header.pxd.hash @@ -0,0 +1 @@ +d067f01423cddb3c442933b5fcc039b18ab651fcec1bc91c577693aafc25cf78 /home/runner/work/aiohttp/aiohttp/aiohttp/_find_header.pxd diff --git a/venv/lib/python3.12/site-packages/aiohttp/.hash/_http_parser.pyx.hash b/venv/lib/python3.12/site-packages/aiohttp/.hash/_http_parser.pyx.hash new file mode 100644 index 00000000..af02f94d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/.hash/_http_parser.pyx.hash @@ -0,0 +1 @@ +bdebb034a6fa633049a0ede76efae4382693cdb711392e4c564b142610c73608 /home/runner/work/aiohttp/aiohttp/aiohttp/_http_parser.pyx diff --git a/venv/lib/python3.12/site-packages/aiohttp/.hash/_http_writer.pyx.hash b/venv/lib/python3.12/site-packages/aiohttp/.hash/_http_writer.pyx.hash new file mode 100644 index 00000000..43c1ab17 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/.hash/_http_writer.pyx.hash @@ -0,0 +1 @@ +4d734e0bdd2dc64e309cf6a4c5ec7fca049baf7c6ad54c5a0a2d93e20bf6b5e6 /home/runner/work/aiohttp/aiohttp/aiohttp/_http_writer.pyx diff --git a/venv/lib/python3.12/site-packages/aiohttp/.hash/hdrs.py.hash b/venv/lib/python3.12/site-packages/aiohttp/.hash/hdrs.py.hash new file mode 100644 index 00000000..c8d55240 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/.hash/hdrs.py.hash @@ -0,0 +1 @@ +dab8f933203eeb245d60f856e542a45b888d5a110094620e4811f90f816628d1 /home/runner/work/aiohttp/aiohttp/aiohttp/hdrs.py diff --git a/venv/lib/python3.12/site-packages/aiohttp/__init__.py b/venv/lib/python3.12/site-packages/aiohttp/__init__.py new file mode 100644 index 00000000..404f9267 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/__init__.py @@ -0,0 +1,278 @@ +__version__ = "3.13.5" + +from typing import TYPE_CHECKING, Tuple + +from . import hdrs as hdrs +from .client import ( + BaseConnector, + ClientConnectionError, + ClientConnectionResetError, + ClientConnectorCertificateError, + ClientConnectorDNSError, + ClientConnectorError, + ClientConnectorSSLError, + ClientError, + ClientHttpProxyError, + ClientOSError, + ClientPayloadError, + ClientProxyConnectionError, + ClientRequest, + ClientResponse, + ClientResponseError, + ClientSession, + ClientSSLError, + ClientTimeout, + ClientWebSocketResponse, + ClientWSTimeout, + ConnectionTimeoutError, + ContentTypeError, + Fingerprint, + InvalidURL, + InvalidUrlClientError, + InvalidUrlRedirectClientError, + NamedPipeConnector, + NonHttpUrlClientError, + NonHttpUrlRedirectClientError, + RedirectClientError, + RequestInfo, + ServerConnectionError, + ServerDisconnectedError, + ServerFingerprintMismatch, + ServerTimeoutError, + SocketTimeoutError, + TCPConnector, + TooManyRedirects, + UnixConnector, + WSMessageTypeError, + WSServerHandshakeError, + request, +) +from .client_middleware_digest_auth import DigestAuthMiddleware +from .client_middlewares import ClientHandlerType, ClientMiddlewareType +from .compression_utils import set_zlib_backend +from .connector import ( + AddrInfoType as AddrInfoType, + SocketFactoryType as SocketFactoryType, +) +from .cookiejar import CookieJar as CookieJar, DummyCookieJar as DummyCookieJar +from .formdata import FormData as FormData +from .helpers import BasicAuth, ChainMapProxy, ETag +from .http import ( + HttpVersion as HttpVersion, + HttpVersion10 as HttpVersion10, + HttpVersion11 as HttpVersion11, + WebSocketError as WebSocketError, + WSCloseCode as WSCloseCode, + WSMessage as WSMessage, + WSMsgType as WSMsgType, +) +from .multipart import ( + BadContentDispositionHeader as BadContentDispositionHeader, + BadContentDispositionParam as BadContentDispositionParam, + BodyPartReader as BodyPartReader, + MultipartReader as MultipartReader, + MultipartWriter as MultipartWriter, + content_disposition_filename as content_disposition_filename, + parse_content_disposition as parse_content_disposition, +) +from .payload import ( + PAYLOAD_REGISTRY as PAYLOAD_REGISTRY, + AsyncIterablePayload as AsyncIterablePayload, + BufferedReaderPayload as BufferedReaderPayload, + BytesIOPayload as BytesIOPayload, + BytesPayload as BytesPayload, + IOBasePayload as IOBasePayload, + JsonPayload as JsonPayload, + Payload as Payload, + StringIOPayload as StringIOPayload, + StringPayload as StringPayload, + TextIOPayload as TextIOPayload, + get_payload as get_payload, + payload_type as payload_type, +) +from .payload_streamer import streamer as streamer +from .resolver import ( + AsyncResolver as AsyncResolver, + DefaultResolver as DefaultResolver, + ThreadedResolver as ThreadedResolver, +) +from .streams import ( + EMPTY_PAYLOAD as EMPTY_PAYLOAD, + DataQueue as DataQueue, + EofStream as EofStream, + FlowControlDataQueue as FlowControlDataQueue, + StreamReader as StreamReader, +) +from .tracing import ( + TraceConfig as TraceConfig, + TraceConnectionCreateEndParams as TraceConnectionCreateEndParams, + TraceConnectionCreateStartParams as TraceConnectionCreateStartParams, + TraceConnectionQueuedEndParams as TraceConnectionQueuedEndParams, + TraceConnectionQueuedStartParams as TraceConnectionQueuedStartParams, + TraceConnectionReuseconnParams as TraceConnectionReuseconnParams, + TraceDnsCacheHitParams as TraceDnsCacheHitParams, + TraceDnsCacheMissParams as TraceDnsCacheMissParams, + TraceDnsResolveHostEndParams as TraceDnsResolveHostEndParams, + TraceDnsResolveHostStartParams as TraceDnsResolveHostStartParams, + TraceRequestChunkSentParams as TraceRequestChunkSentParams, + TraceRequestEndParams as TraceRequestEndParams, + TraceRequestExceptionParams as TraceRequestExceptionParams, + TraceRequestHeadersSentParams as TraceRequestHeadersSentParams, + TraceRequestRedirectParams as TraceRequestRedirectParams, + TraceRequestStartParams as TraceRequestStartParams, + TraceResponseChunkReceivedParams as TraceResponseChunkReceivedParams, +) + +if TYPE_CHECKING: + # At runtime these are lazy-loaded at the bottom of the file. + from .worker import ( + GunicornUVLoopWebWorker as GunicornUVLoopWebWorker, + GunicornWebWorker as GunicornWebWorker, + ) + +__all__: Tuple[str, ...] = ( + "hdrs", + # client + "AddrInfoType", + "BaseConnector", + "ClientConnectionError", + "ClientConnectionResetError", + "ClientConnectorCertificateError", + "ClientConnectorDNSError", + "ClientConnectorError", + "ClientConnectorSSLError", + "ClientError", + "ClientHttpProxyError", + "ClientOSError", + "ClientPayloadError", + "ClientProxyConnectionError", + "ClientResponse", + "ClientRequest", + "ClientResponseError", + "ClientSSLError", + "ClientSession", + "ClientTimeout", + "ClientWebSocketResponse", + "ClientWSTimeout", + "ConnectionTimeoutError", + "ContentTypeError", + "Fingerprint", + "FlowControlDataQueue", + "InvalidURL", + "InvalidUrlClientError", + "InvalidUrlRedirectClientError", + "NonHttpUrlClientError", + "NonHttpUrlRedirectClientError", + "RedirectClientError", + "RequestInfo", + "ServerConnectionError", + "ServerDisconnectedError", + "ServerFingerprintMismatch", + "ServerTimeoutError", + "SocketFactoryType", + "SocketTimeoutError", + "TCPConnector", + "TooManyRedirects", + "UnixConnector", + "NamedPipeConnector", + "WSServerHandshakeError", + "request", + # client_middleware + "ClientMiddlewareType", + "ClientHandlerType", + # cookiejar + "CookieJar", + "DummyCookieJar", + # formdata + "FormData", + # helpers + "BasicAuth", + "ChainMapProxy", + "DigestAuthMiddleware", + "ETag", + "set_zlib_backend", + # http + "HttpVersion", + "HttpVersion10", + "HttpVersion11", + "WSMsgType", + "WSCloseCode", + "WSMessage", + "WebSocketError", + # multipart + "BadContentDispositionHeader", + "BadContentDispositionParam", + "BodyPartReader", + "MultipartReader", + "MultipartWriter", + "content_disposition_filename", + "parse_content_disposition", + # payload + "AsyncIterablePayload", + "BufferedReaderPayload", + "BytesIOPayload", + "BytesPayload", + "IOBasePayload", + "JsonPayload", + "PAYLOAD_REGISTRY", + "Payload", + "StringIOPayload", + "StringPayload", + "TextIOPayload", + "get_payload", + "payload_type", + # payload_streamer + "streamer", + # resolver + "AsyncResolver", + "DefaultResolver", + "ThreadedResolver", + # streams + "DataQueue", + "EMPTY_PAYLOAD", + "EofStream", + "StreamReader", + # tracing + "TraceConfig", + "TraceConnectionCreateEndParams", + "TraceConnectionCreateStartParams", + "TraceConnectionQueuedEndParams", + "TraceConnectionQueuedStartParams", + "TraceConnectionReuseconnParams", + "TraceDnsCacheHitParams", + "TraceDnsCacheMissParams", + "TraceDnsResolveHostEndParams", + "TraceDnsResolveHostStartParams", + "TraceRequestChunkSentParams", + "TraceRequestEndParams", + "TraceRequestExceptionParams", + "TraceRequestHeadersSentParams", + "TraceRequestRedirectParams", + "TraceRequestStartParams", + "TraceResponseChunkReceivedParams", + # workers (imported lazily with __getattr__) + "GunicornUVLoopWebWorker", + "GunicornWebWorker", + "WSMessageTypeError", +) + + +def __dir__() -> Tuple[str, ...]: + return __all__ + ("__doc__",) + + +def __getattr__(name: str) -> object: + global GunicornUVLoopWebWorker, GunicornWebWorker + + # Importing gunicorn takes a long time (>100ms), so only import if actually needed. + if name in ("GunicornUVLoopWebWorker", "GunicornWebWorker"): + try: + from .worker import GunicornUVLoopWebWorker as guv, GunicornWebWorker as gw + except ImportError: + return None + + GunicornUVLoopWebWorker = guv # type: ignore[misc] + GunicornWebWorker = gw # type: ignore[misc] + return guv if name == "GunicornUVLoopWebWorker" else gw + + raise AttributeError(f"module {__name__} has no attribute {name}") diff --git a/venv/lib/python3.12/site-packages/aiohttp/_cookie_helpers.py b/venv/lib/python3.12/site-packages/aiohttp/_cookie_helpers.py new file mode 100644 index 00000000..10e2e0ef --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_cookie_helpers.py @@ -0,0 +1,338 @@ +""" +Internal cookie handling helpers. + +This module contains internal utilities for cookie parsing and manipulation. +These are not part of the public API and may change without notice. +""" + +import re +from http.cookies import Morsel +from typing import List, Optional, Sequence, Tuple, cast + +from .log import internal_logger + +__all__ = ( + "parse_set_cookie_headers", + "parse_cookie_header", + "preserve_morsel_with_coded_value", +) + +# Cookie parsing constants +# Allow more characters in cookie names to handle real-world cookies +# that don't strictly follow RFC standards (fixes #2683) +# RFC 6265 defines cookie-name token as per RFC 2616 Section 2.2, +# but many servers send cookies with characters like {} [] () etc. +# This makes the cookie parser more tolerant of real-world cookies +# while still providing some validation to catch obviously malformed names. +_COOKIE_NAME_RE = re.compile(r"^[!#$%&\'()*+\-./0-9:<=>?@A-Z\[\]^_`a-z{|}~]+$") +_COOKIE_KNOWN_ATTRS = frozenset( # AKA Morsel._reserved + ( + "path", + "domain", + "max-age", + "expires", + "secure", + "httponly", + "samesite", + "partitioned", + "version", + "comment", + ) +) +_COOKIE_BOOL_ATTRS = frozenset( # AKA Morsel._flags + ("secure", "httponly", "partitioned") +) + +# SimpleCookie's pattern for parsing cookies with relaxed validation +# Based on http.cookies pattern but extended to allow more characters in cookie names +# to handle real-world cookies (fixes #2683) +_COOKIE_PATTERN = re.compile( + r""" + \s* # Optional whitespace at start of cookie + (?P # Start of group 'key' + # aiohttp has extended to include [] for compatibility with real-world cookies + [\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\[\]]+ # Any word of at least one letter + ) # End of group 'key' + ( # Optional group: there may not be a value. + \s*=\s* # Equal Sign + (?P # Start of group 'val' + "(?:[^\\"]|\\.)*" # Any double-quoted string (properly closed) + | # or + "[^";]* # Unmatched opening quote (differs from SimpleCookie - issue #7993) + | # or + # Special case for "expires" attr - RFC 822, RFC 850, RFC 1036, RFC 1123 + (\w{3,6}day|\w{3}),\s # Day of the week or abbreviated day (with comma) + [\w\d\s-]{9,11}\s[\d:]{8}\s # Date and time in specific format + (GMT|[+-]\d{4}) # Timezone: GMT or RFC 2822 offset like -0000, +0100 + # NOTE: RFC 2822 timezone support is an aiohttp extension + # for issue #4493 - SimpleCookie does NOT support this + | # or + # ANSI C asctime() format: "Wed Jun 9 10:18:14 2021" + # NOTE: This is an aiohttp extension for issue #4327 - SimpleCookie does NOT support this format + \w{3}\s+\w{3}\s+[\s\d]\d\s+\d{2}:\d{2}:\d{2}\s+\d{4} + | # or + [\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=\[\]]* # Any word or empty string + ) # End of group 'val' + )? # End of optional value group + \s* # Any number of spaces. + (\s+|;|$) # Ending either at space, semicolon, or EOS. + """, + re.VERBOSE | re.ASCII, +) + + +def preserve_morsel_with_coded_value(cookie: Morsel[str]) -> Morsel[str]: + """ + Preserve a Morsel's coded_value exactly as received from the server. + + This function ensures that cookie encoding is preserved exactly as sent by + the server, which is critical for compatibility with old servers that have + strict requirements about cookie formats. + + This addresses the issue described in https://github.com/aio-libs/aiohttp/pull/1453 + where Python's SimpleCookie would re-encode cookies, breaking authentication + with certain servers. + + Args: + cookie: A Morsel object from SimpleCookie + + Returns: + A Morsel object with preserved coded_value + + """ + mrsl_val = cast("Morsel[str]", cookie.get(cookie.key, Morsel())) + # We use __setstate__ instead of the public set() API because it allows us to + # bypass validation and set already validated state. This is more stable than + # setting protected attributes directly and unlikely to change since it would + # break pickling. + mrsl_val.__setstate__( # type: ignore[attr-defined] + {"key": cookie.key, "value": cookie.value, "coded_value": cookie.coded_value} + ) + return mrsl_val + + +_unquote_sub = re.compile(r"\\(?:([0-3][0-7][0-7])|(.))").sub + + +def _unquote_replace(m: re.Match[str]) -> str: + """ + Replace function for _unquote_sub regex substitution. + + Handles escaped characters in cookie values: + - Octal sequences are converted to their character representation + - Other escaped characters are unescaped by removing the backslash + """ + if m[1]: + return chr(int(m[1], 8)) + return m[2] + + +def _unquote(value: str) -> str: + """ + Unquote a cookie value. + + Vendored from http.cookies._unquote to ensure compatibility. + + Note: The original implementation checked for None, but we've removed + that check since all callers already ensure the value is not None. + """ + # If there aren't any doublequotes, + # then there can't be any special characters. See RFC 2109. + if len(value) < 2: + return value + if value[0] != '"' or value[-1] != '"': + return value + + # We have to assume that we must decode this string. + # Down to work. + + # Remove the "s + value = value[1:-1] + + # Check for special sequences. Examples: + # \012 --> \n + # \" --> " + # + return _unquote_sub(_unquote_replace, value) + + +def parse_cookie_header(header: str) -> List[Tuple[str, Morsel[str]]]: + """ + Parse a Cookie header according to RFC 6265 Section 5.4. + + Cookie headers contain only name-value pairs separated by semicolons. + There are no attributes in Cookie headers - even names that match + attribute names (like 'path' or 'secure') should be treated as cookies. + + This parser uses the same regex-based approach as parse_set_cookie_headers + to properly handle quoted values that may contain semicolons. When the + regex fails to match a malformed cookie, it falls back to simple parsing + to ensure subsequent cookies are not lost + https://github.com/aio-libs/aiohttp/issues/11632 + + Args: + header: The Cookie header value to parse + + Returns: + List of (name, Morsel) tuples for compatibility with SimpleCookie.update() + """ + if not header: + return [] + + cookies: List[Tuple[str, Morsel[str]]] = [] + morsel: Morsel[str] + i = 0 + n = len(header) + + invalid_names = [] + while i < n: + # Use the same pattern as parse_set_cookie_headers to find cookies + match = _COOKIE_PATTERN.match(header, i) + if not match: + # Fallback for malformed cookies https://github.com/aio-libs/aiohttp/issues/11632 + # Find next semicolon to skip or attempt simple key=value parsing + next_semi = header.find(";", i) + eq_pos = header.find("=", i) + + # Try to extract key=value if '=' comes before ';' + if eq_pos != -1 and (next_semi == -1 or eq_pos < next_semi): + end_pos = next_semi if next_semi != -1 else n + key = header[i:eq_pos].strip() + value = header[eq_pos + 1 : end_pos].strip() + + # Validate the name (same as regex path) + if not _COOKIE_NAME_RE.match(key): + invalid_names.append(key) + else: + morsel = Morsel() + morsel.__setstate__( # type: ignore[attr-defined] + {"key": key, "value": _unquote(value), "coded_value": value} + ) + cookies.append((key, morsel)) + + # Move to next cookie or end + i = next_semi + 1 if next_semi != -1 else n + continue + + key = match.group("key") + value = match.group("val") or "" + i = match.end(0) + + # Validate the name + if not key or not _COOKIE_NAME_RE.match(key): + invalid_names.append(key) + continue + + # Create new morsel + morsel = Morsel() + # Preserve the original value as coded_value (with quotes if present) + # We use __setstate__ instead of the public set() API because it allows us to + # bypass validation and set already validated state. This is more stable than + # setting protected attributes directly and unlikely to change since it would + # break pickling. + morsel.__setstate__( # type: ignore[attr-defined] + {"key": key, "value": _unquote(value), "coded_value": value} + ) + + cookies.append((key, morsel)) + + if invalid_names: + internal_logger.debug( + "Cannot load cookie. Illegal cookie names: %r", invalid_names + ) + + return cookies + + +def parse_set_cookie_headers(headers: Sequence[str]) -> List[Tuple[str, Morsel[str]]]: + """ + Parse cookie headers using a vendored version of SimpleCookie parsing. + + This implementation is based on SimpleCookie.__parse_string to ensure + compatibility with how SimpleCookie parses cookies, including handling + of malformed cookies with missing semicolons. + + This function is used for both Cookie and Set-Cookie headers in order to be + forgiving. Ideally we would have followed RFC 6265 Section 5.2 (for Cookie + headers) and RFC 6265 Section 4.2.1 (for Set-Cookie headers), but the + real world data makes it impossible since we need to be a bit more forgiving. + + NOTE: This implementation differs from SimpleCookie in handling unmatched quotes. + SimpleCookie will stop parsing when it encounters a cookie value with an unmatched + quote (e.g., 'cookie="value'), causing subsequent cookies to be silently dropped. + This implementation handles unmatched quotes more gracefully to prevent cookie loss. + See https://github.com/aio-libs/aiohttp/issues/7993 + """ + parsed_cookies: List[Tuple[str, Morsel[str]]] = [] + + for header in headers: + if not header: + continue + + # Parse cookie string using SimpleCookie's algorithm + i = 0 + n = len(header) + current_morsel: Optional[Morsel[str]] = None + morsel_seen = False + + while 0 <= i < n: + # Start looking for a cookie + match = _COOKIE_PATTERN.match(header, i) + if not match: + # No more cookies + break + + key, value = match.group("key"), match.group("val") + i = match.end(0) + lower_key = key.lower() + + if key[0] == "$": + if not morsel_seen: + # We ignore attributes which pertain to the cookie + # mechanism as a whole, such as "$Version". + continue + # Process as attribute + if current_morsel is not None: + attr_lower_key = lower_key[1:] + if attr_lower_key in _COOKIE_KNOWN_ATTRS: + current_morsel[attr_lower_key] = value or "" + elif lower_key in _COOKIE_KNOWN_ATTRS: + if not morsel_seen: + # Invalid cookie string - attribute before cookie + break + if lower_key in _COOKIE_BOOL_ATTRS: + # Boolean attribute with any value should be True + if current_morsel is not None and current_morsel.isReservedKey(key): + current_morsel[lower_key] = True + elif value is None: + # Invalid cookie string - non-boolean attribute without value + break + elif current_morsel is not None: + # Regular attribute with value + current_morsel[lower_key] = _unquote(value) + elif value is not None: + # This is a cookie name=value pair + # Validate the name + if key in _COOKIE_KNOWN_ATTRS or not _COOKIE_NAME_RE.match(key): + internal_logger.warning( + "Can not load cookies: Illegal cookie name %r", key + ) + current_morsel = None + else: + # Create new morsel + current_morsel = Morsel() + # Preserve the original value as coded_value (with quotes if present) + # We use __setstate__ instead of the public set() API because it allows us to + # bypass validation and set already validated state. This is more stable than + # setting protected attributes directly and unlikely to change since it would + # break pickling. + current_morsel.__setstate__( # type: ignore[attr-defined] + {"key": key, "value": _unquote(value), "coded_value": value} + ) + parsed_cookies.append((key, current_morsel)) + morsel_seen = True + else: + # Invalid cookie string - no value for non-attribute + break + + return parsed_cookies diff --git a/venv/lib/python3.12/site-packages/aiohttp/_cparser.pxd b/venv/lib/python3.12/site-packages/aiohttp/_cparser.pxd new file mode 100644 index 00000000..1b3be6d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_cparser.pxd @@ -0,0 +1,158 @@ +from libc.stdint cimport int32_t, uint8_t, uint16_t, uint64_t + + +cdef extern from "llhttp.h": + + struct llhttp__internal_s: + int32_t _index + void* _span_pos0 + void* _span_cb0 + int32_t error + const char* reason + const char* error_pos + void* data + void* _current + uint64_t content_length + uint8_t type + uint8_t method + uint8_t http_major + uint8_t http_minor + uint8_t header_state + uint8_t lenient_flags + uint8_t upgrade + uint8_t finish + uint16_t flags + uint16_t status_code + void* settings + + ctypedef llhttp__internal_s llhttp__internal_t + ctypedef llhttp__internal_t llhttp_t + + ctypedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length) except -1 + ctypedef int (*llhttp_cb)(llhttp_t*) except -1 + + struct llhttp_settings_s: + llhttp_cb on_message_begin + llhttp_data_cb on_url + llhttp_data_cb on_status + llhttp_data_cb on_header_field + llhttp_data_cb on_header_value + llhttp_cb on_headers_complete + llhttp_data_cb on_body + llhttp_cb on_message_complete + llhttp_cb on_chunk_header + llhttp_cb on_chunk_complete + + llhttp_cb on_url_complete + llhttp_cb on_status_complete + llhttp_cb on_header_field_complete + llhttp_cb on_header_value_complete + + ctypedef llhttp_settings_s llhttp_settings_t + + enum llhttp_errno: + HPE_OK, + HPE_INTERNAL, + HPE_STRICT, + HPE_LF_EXPECTED, + HPE_UNEXPECTED_CONTENT_LENGTH, + HPE_CLOSED_CONNECTION, + HPE_INVALID_METHOD, + HPE_INVALID_URL, + HPE_INVALID_CONSTANT, + HPE_INVALID_VERSION, + HPE_INVALID_HEADER_TOKEN, + HPE_INVALID_CONTENT_LENGTH, + HPE_INVALID_CHUNK_SIZE, + HPE_INVALID_STATUS, + HPE_INVALID_EOF_STATE, + HPE_INVALID_TRANSFER_ENCODING, + HPE_CB_MESSAGE_BEGIN, + HPE_CB_HEADERS_COMPLETE, + HPE_CB_MESSAGE_COMPLETE, + HPE_CB_CHUNK_HEADER, + HPE_CB_CHUNK_COMPLETE, + HPE_PAUSED, + HPE_PAUSED_UPGRADE, + HPE_USER + + ctypedef llhttp_errno llhttp_errno_t + + enum llhttp_flags: + F_CHUNKED, + F_CONTENT_LENGTH + + enum llhttp_type: + HTTP_REQUEST, + HTTP_RESPONSE, + HTTP_BOTH + + enum llhttp_method: + HTTP_DELETE, + HTTP_GET, + HTTP_HEAD, + HTTP_POST, + HTTP_PUT, + HTTP_CONNECT, + HTTP_OPTIONS, + HTTP_TRACE, + HTTP_COPY, + HTTP_LOCK, + HTTP_MKCOL, + HTTP_MOVE, + HTTP_PROPFIND, + HTTP_PROPPATCH, + HTTP_SEARCH, + HTTP_UNLOCK, + HTTP_BIND, + HTTP_REBIND, + HTTP_UNBIND, + HTTP_ACL, + HTTP_REPORT, + HTTP_MKACTIVITY, + HTTP_CHECKOUT, + HTTP_MERGE, + HTTP_MSEARCH, + HTTP_NOTIFY, + HTTP_SUBSCRIBE, + HTTP_UNSUBSCRIBE, + HTTP_PATCH, + HTTP_PURGE, + HTTP_MKCALENDAR, + HTTP_LINK, + HTTP_UNLINK, + HTTP_SOURCE, + HTTP_PRI, + HTTP_DESCRIBE, + HTTP_ANNOUNCE, + HTTP_SETUP, + HTTP_PLAY, + HTTP_PAUSE, + HTTP_TEARDOWN, + HTTP_GET_PARAMETER, + HTTP_SET_PARAMETER, + HTTP_REDIRECT, + HTTP_RECORD, + HTTP_FLUSH + + ctypedef llhttp_method llhttp_method_t; + + void llhttp_settings_init(llhttp_settings_t* settings) + void llhttp_init(llhttp_t* parser, llhttp_type type, + const llhttp_settings_t* settings) + + llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) + + int llhttp_should_keep_alive(const llhttp_t* parser) + + void llhttp_resume_after_upgrade(llhttp_t* parser) + + llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) + const char* llhttp_get_error_reason(const llhttp_t* parser) + const char* llhttp_get_error_pos(const llhttp_t* parser) + + const char* llhttp_method_name(llhttp_method_t method) + + void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) + void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) + void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) diff --git a/venv/lib/python3.12/site-packages/aiohttp/_find_header.pxd b/venv/lib/python3.12/site-packages/aiohttp/_find_header.pxd new file mode 100644 index 00000000..37a6c372 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_find_header.pxd @@ -0,0 +1,2 @@ +cdef extern from "_find_header.h": + int find_header(char *, int) diff --git a/venv/lib/python3.12/site-packages/aiohttp/_headers.pxi b/venv/lib/python3.12/site-packages/aiohttp/_headers.pxi new file mode 100644 index 00000000..3744721d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_headers.pxi @@ -0,0 +1,83 @@ +# The file is autogenerated from aiohttp/hdrs.py +# Run ./tools/gen.py to update it after the origin changing. + +from . import hdrs +cdef tuple headers = ( + hdrs.ACCEPT, + hdrs.ACCEPT_CHARSET, + hdrs.ACCEPT_ENCODING, + hdrs.ACCEPT_LANGUAGE, + hdrs.ACCEPT_RANGES, + hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS, + hdrs.ACCESS_CONTROL_ALLOW_HEADERS, + hdrs.ACCESS_CONTROL_ALLOW_METHODS, + hdrs.ACCESS_CONTROL_ALLOW_ORIGIN, + hdrs.ACCESS_CONTROL_EXPOSE_HEADERS, + hdrs.ACCESS_CONTROL_MAX_AGE, + hdrs.ACCESS_CONTROL_REQUEST_HEADERS, + hdrs.ACCESS_CONTROL_REQUEST_METHOD, + hdrs.AGE, + hdrs.ALLOW, + hdrs.AUTHORIZATION, + hdrs.CACHE_CONTROL, + hdrs.CONNECTION, + hdrs.CONTENT_DISPOSITION, + hdrs.CONTENT_ENCODING, + hdrs.CONTENT_LANGUAGE, + hdrs.CONTENT_LENGTH, + hdrs.CONTENT_LOCATION, + hdrs.CONTENT_MD5, + hdrs.CONTENT_RANGE, + hdrs.CONTENT_TRANSFER_ENCODING, + hdrs.CONTENT_TYPE, + hdrs.COOKIE, + hdrs.DATE, + hdrs.DESTINATION, + hdrs.DIGEST, + hdrs.ETAG, + hdrs.EXPECT, + hdrs.EXPIRES, + hdrs.FORWARDED, + hdrs.FROM, + hdrs.HOST, + hdrs.IF_MATCH, + hdrs.IF_MODIFIED_SINCE, + hdrs.IF_NONE_MATCH, + hdrs.IF_RANGE, + hdrs.IF_UNMODIFIED_SINCE, + hdrs.KEEP_ALIVE, + hdrs.LAST_EVENT_ID, + hdrs.LAST_MODIFIED, + hdrs.LINK, + hdrs.LOCATION, + hdrs.MAX_FORWARDS, + hdrs.ORIGIN, + hdrs.PRAGMA, + hdrs.PROXY_AUTHENTICATE, + hdrs.PROXY_AUTHORIZATION, + hdrs.RANGE, + hdrs.REFERER, + hdrs.RETRY_AFTER, + hdrs.SEC_WEBSOCKET_ACCEPT, + hdrs.SEC_WEBSOCKET_EXTENSIONS, + hdrs.SEC_WEBSOCKET_KEY, + hdrs.SEC_WEBSOCKET_KEY1, + hdrs.SEC_WEBSOCKET_PROTOCOL, + hdrs.SEC_WEBSOCKET_VERSION, + hdrs.SERVER, + hdrs.SET_COOKIE, + hdrs.TE, + hdrs.TRAILER, + hdrs.TRANSFER_ENCODING, + hdrs.URI, + hdrs.UPGRADE, + hdrs.USER_AGENT, + hdrs.VARY, + hdrs.VIA, + hdrs.WWW_AUTHENTICATE, + hdrs.WANT_DIGEST, + hdrs.WARNING, + hdrs.X_FORWARDED_FOR, + hdrs.X_FORWARDED_HOST, + hdrs.X_FORWARDED_PROTO, +) diff --git a/venv/lib/python3.12/site-packages/aiohttp/_http_parser.cpython-312-x86_64-linux-gnu.so b/venv/lib/python3.12/site-packages/aiohttp/_http_parser.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..11ff2c1cfd930b626b767590eb9796743a96d736 GIT binary patch literal 2947720 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

dd9>z{tK`2x!L*`h>Ov4J<;shTE8=9hWd0+Hs*| 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È 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

h@AjQOHgM2Ml!(|oTT}cP(E3i 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%0DV*keJvDXkO{U&d2~VO)f0 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%0D<>`-p+o6_+AQfJ8RoK<@N%8nf;f@2cx3+&vi7A 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�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 X-M_GYYF(`STVFQi+d+ZgvGD%^$zMI} literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/aiohttp/_http_parser.pyx b/venv/lib/python3.12/site-packages/aiohttp/_http_parser.pyx new file mode 100644 index 00000000..5da835bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_http_parser.pyx @@ -0,0 +1,877 @@ +# Based on https://github.com/MagicStack/httptools +# + +from cpython cimport ( + Py_buffer, + PyBUF_SIMPLE, + PyBuffer_Release, + PyBytes_AsString, + PyBytes_AsStringAndSize, + PyObject_GetBuffer, +) +from cpython.mem cimport PyMem_Free, PyMem_Malloc +from libc.limits cimport ULLONG_MAX +from libc.string cimport memcpy + +from multidict import CIMultiDict as _CIMultiDict, CIMultiDictProxy as _CIMultiDictProxy +from yarl import URL as _URL + +from aiohttp import hdrs +from aiohttp.helpers import DEBUG, set_exception + +from .http_exceptions import ( + BadHttpMessage, + BadHttpMethod, + BadStatusLine, + ContentLengthError, + InvalidHeader, + InvalidURLError, + LineTooLong, + PayloadEncodingError, + TransferEncodingError, +) +from .http_parser import DeflateBuffer as _DeflateBuffer +from .http_writer import ( + HttpVersion as _HttpVersion, + HttpVersion10 as _HttpVersion10, + HttpVersion11 as _HttpVersion11, +) +from .streams import EMPTY_PAYLOAD as _EMPTY_PAYLOAD, StreamReader as _StreamReader + +cimport cython + +from aiohttp cimport _cparser as cparser + +include "_headers.pxi" + +from aiohttp cimport _find_header + +ALLOWED_UPGRADES = frozenset({"websocket"}) +DEF DEFAULT_FREELIST_SIZE = 250 + +cdef extern from "Python.h": + int PyByteArray_Resize(object, Py_ssize_t) except -1 + Py_ssize_t PyByteArray_Size(object) except -1 + char* PyByteArray_AsString(object) + +__all__ = ('HttpRequestParser', 'HttpResponseParser', + 'RawRequestMessage', 'RawResponseMessage') + +cdef object URL = _URL +cdef object URL_build = URL.build +cdef object CIMultiDict = _CIMultiDict +cdef object CIMultiDictProxy = _CIMultiDictProxy +cdef object HttpVersion = _HttpVersion +cdef object HttpVersion10 = _HttpVersion10 +cdef object HttpVersion11 = _HttpVersion11 +cdef object SEC_WEBSOCKET_KEY1 = hdrs.SEC_WEBSOCKET_KEY1 +cdef object CONTENT_ENCODING = hdrs.CONTENT_ENCODING +cdef object EMPTY_PAYLOAD = _EMPTY_PAYLOAD +cdef object StreamReader = _StreamReader +cdef object DeflateBuffer = _DeflateBuffer +cdef bytes EMPTY_BYTES = b"" + +# RFC 9110 singleton headers — duplicates are rejected in strict mode. +# In lax mode (response parser default), the check is skipped entirely +# since real-world servers (e.g. Google APIs, Werkzeug) commonly send +# duplicate headers like Content-Type or Server. +cdef frozenset SINGLETON_HEADERS = frozenset({ + hdrs.CONTENT_LENGTH, + hdrs.CONTENT_LOCATION, + hdrs.CONTENT_RANGE, + hdrs.CONTENT_TYPE, + hdrs.ETAG, + hdrs.HOST, + hdrs.MAX_FORWARDS, + hdrs.SERVER, + hdrs.TRANSFER_ENCODING, + hdrs.USER_AGENT, +}) + +cdef inline object extend(object buf, const char* at, size_t length): + cdef Py_ssize_t s + cdef char* ptr + s = PyByteArray_Size(buf) + PyByteArray_Resize(buf, s + length) + ptr = PyByteArray_AsString(buf) + memcpy(ptr + s, at, length) + + +DEF METHODS_COUNT = 46; + +cdef list _http_method = [] + +for i in range(METHODS_COUNT): + _http_method.append( + cparser.llhttp_method_name( i).decode('ascii')) + + +cdef inline str http_method_str(int i): + if i < METHODS_COUNT: + return _http_method[i] + else: + return "" + +cdef inline object find_header(bytes raw_header): + cdef Py_ssize_t size + cdef char *buf + cdef int idx + PyBytes_AsStringAndSize(raw_header, &buf, &size) + idx = _find_header.find_header(buf, size) + if idx == -1: + return raw_header.decode('utf-8', 'surrogateescape') + return headers[idx] + + +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class RawRequestMessage: + cdef readonly str method + cdef readonly str path + cdef readonly object version # HttpVersion + cdef readonly object headers # CIMultiDict + cdef readonly object raw_headers # tuple + cdef readonly object should_close + cdef readonly object compression + cdef readonly object upgrade + cdef readonly object chunked + cdef readonly object url # yarl.URL + + def __init__(self, method, path, version, headers, raw_headers, + should_close, compression, upgrade, chunked, url): + self.method = method + self.path = path + self.version = version + self.headers = headers + self.raw_headers = raw_headers + self.should_close = should_close + self.compression = compression + self.upgrade = upgrade + self.chunked = chunked + self.url = url + + def __repr__(self): + info = [] + info.append(("method", self.method)) + info.append(("path", self.path)) + info.append(("version", self.version)) + info.append(("headers", self.headers)) + info.append(("raw_headers", self.raw_headers)) + info.append(("should_close", self.should_close)) + info.append(("compression", self.compression)) + info.append(("upgrade", self.upgrade)) + info.append(("chunked", self.chunked)) + info.append(("url", self.url)) + sinfo = ', '.join(name + '=' + repr(val) for name, val in info) + return '' + + def _replace(self, **dct): + cdef RawRequestMessage ret + ret = _new_request_message(self.method, + self.path, + self.version, + self.headers, + self.raw_headers, + self.should_close, + self.compression, + self.upgrade, + self.chunked, + self.url) + if "method" in dct: + ret.method = dct["method"] + if "path" in dct: + ret.path = dct["path"] + if "version" in dct: + ret.version = dct["version"] + if "headers" in dct: + ret.headers = dct["headers"] + if "raw_headers" in dct: + ret.raw_headers = dct["raw_headers"] + if "should_close" in dct: + ret.should_close = dct["should_close"] + if "compression" in dct: + ret.compression = dct["compression"] + if "upgrade" in dct: + ret.upgrade = dct["upgrade"] + if "chunked" in dct: + ret.chunked = dct["chunked"] + if "url" in dct: + ret.url = dct["url"] + return ret + +cdef _new_request_message(str method, + str path, + object version, + object headers, + object raw_headers, + bint should_close, + object compression, + bint upgrade, + bint chunked, + object url): + cdef RawRequestMessage ret + ret = RawRequestMessage.__new__(RawRequestMessage) + ret.method = method + ret.path = path + ret.version = version + ret.headers = headers + ret.raw_headers = raw_headers + ret.should_close = should_close + ret.compression = compression + ret.upgrade = upgrade + ret.chunked = chunked + ret.url = url + return ret + + +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class RawResponseMessage: + cdef readonly object version # HttpVersion + cdef readonly int code + cdef readonly str reason + cdef readonly object headers # CIMultiDict + cdef readonly object raw_headers # tuple + cdef readonly object should_close + cdef readonly object compression + cdef readonly object upgrade + cdef readonly object chunked + + def __init__(self, version, code, reason, headers, raw_headers, + should_close, compression, upgrade, chunked): + self.version = version + self.code = code + self.reason = reason + self.headers = headers + self.raw_headers = raw_headers + self.should_close = should_close + self.compression = compression + self.upgrade = upgrade + self.chunked = chunked + + def __repr__(self): + info = [] + info.append(("version", self.version)) + info.append(("code", self.code)) + info.append(("reason", self.reason)) + info.append(("headers", self.headers)) + info.append(("raw_headers", self.raw_headers)) + info.append(("should_close", self.should_close)) + info.append(("compression", self.compression)) + info.append(("upgrade", self.upgrade)) + info.append(("chunked", self.chunked)) + sinfo = ', '.join(name + '=' + repr(val) for name, val in info) + return '' + + +cdef _new_response_message(object version, + int code, + str reason, + object headers, + object raw_headers, + bint should_close, + object compression, + bint upgrade, + bint chunked): + cdef RawResponseMessage ret + ret = RawResponseMessage.__new__(RawResponseMessage) + ret.version = version + ret.code = code + ret.reason = reason + ret.headers = headers + ret.raw_headers = raw_headers + ret.should_close = should_close + ret.compression = compression + ret.upgrade = upgrade + ret.chunked = chunked + return ret + + +@cython.internal +cdef class HttpParser: + + cdef: + cparser.llhttp_t* _cparser + cparser.llhttp_settings_t* _csettings + + bytes _raw_name + object _name + bytes _raw_value + bint _has_value + int _header_name_size + + object _protocol + object _loop + object _timer + + size_t _max_line_size + size_t _max_field_size + size_t _max_headers + bint _response_with_body + bint _read_until_eof + bint _lax + + bint _started + object _url + bytearray _buf + str _path + str _reason + list _headers + set _seen_singletons + list _raw_headers + bint _upgraded + list _messages + object _payload + bint _payload_error + object _payload_exception + object _last_error + bint _auto_decompress + int _limit + + str _content_encoding + + Py_buffer py_buf + + def __cinit__(self): + self._cparser = \ + PyMem_Malloc(sizeof(cparser.llhttp_t)) + if self._cparser is NULL: + raise MemoryError() + + self._csettings = \ + PyMem_Malloc(sizeof(cparser.llhttp_settings_t)) + if self._csettings is NULL: + raise MemoryError() + + def __dealloc__(self): + PyMem_Free(self._cparser) + PyMem_Free(self._csettings) + + cdef _init( + self, cparser.llhttp_type mode, + object protocol, object loop, int limit, + object timer=None, + size_t max_line_size=8190, size_t max_headers=128, + size_t max_field_size=8190, payload_exception=None, + bint response_with_body=True, bint read_until_eof=False, + bint auto_decompress=True, + ): + cparser.llhttp_settings_init(self._csettings) + cparser.llhttp_init(self._cparser, mode, self._csettings) + self._cparser.data = self + self._cparser.content_length = 0 + + self._protocol = protocol + self._loop = loop + self._timer = timer + + self._buf = bytearray() + self._payload = None + self._payload_error = 0 + self._payload_exception = payload_exception + self._messages = [] + + self._raw_name = EMPTY_BYTES + self._raw_value = EMPTY_BYTES + self._has_value = False + self._header_name_size = 0 + + self._max_line_size = max_line_size + self._max_headers = max_headers + self._max_field_size = max_field_size + self._response_with_body = response_with_body + self._read_until_eof = read_until_eof + self._upgraded = False + self._auto_decompress = auto_decompress + self._content_encoding = None + self._lax = False + self._seen_singletons = set() + + self._csettings.on_url = cb_on_url + self._csettings.on_status = cb_on_status + self._csettings.on_header_field = cb_on_header_field + self._csettings.on_header_value = cb_on_header_value + self._csettings.on_headers_complete = cb_on_headers_complete + self._csettings.on_body = cb_on_body + self._csettings.on_message_begin = cb_on_message_begin + self._csettings.on_message_complete = cb_on_message_complete + self._csettings.on_chunk_header = cb_on_chunk_header + self._csettings.on_chunk_complete = cb_on_chunk_complete + + self._last_error = None + self._limit = limit + + cdef _process_header(self): + cdef str value + if self._raw_name is not EMPTY_BYTES: + name = find_header(self._raw_name) + value = self._raw_value.decode('utf-8', 'surrogateescape') + + # reject null bytes in header values - matches the Python parser + # check at http_parser.py. llhttp in lenient mode doesn't reject + # these itself, so we need to catch them here. + # ref: RFC 9110 section 5.5 (CTL chars forbidden in field values) + if "\x00" in value: + raise InvalidHeader(self._raw_value) + + if not self._lax and name in SINGLETON_HEADERS: + if name in self._seen_singletons: + raise BadHttpMessage(f"Duplicate '{name}' header found.") + self._seen_singletons.add(name) + self._headers.append((name, value)) + if len(self._headers) > self._max_headers: + raise BadHttpMessage("Too many headers received") + + if name is CONTENT_ENCODING: + self._content_encoding = value + + self._has_value = False + self._header_name_size = 0 + self._raw_headers.append((self._raw_name, self._raw_value)) + self._raw_name = EMPTY_BYTES + self._raw_value = EMPTY_BYTES + + cdef _on_header_field(self, char* at, size_t length): + if self._has_value: + self._process_header() + + if self._raw_name is EMPTY_BYTES: + self._raw_name = at[:length] + else: + self._raw_name += at[:length] + + cdef _on_header_value(self, char* at, size_t length): + if self._raw_value is EMPTY_BYTES: + self._raw_value = at[:length] + else: + self._raw_value += at[:length] + self._has_value = True + + cdef _on_headers_complete(self): + self._process_header() + + should_close = not cparser.llhttp_should_keep_alive(self._cparser) + upgrade = self._cparser.upgrade + chunked = self._cparser.flags & cparser.F_CHUNKED + + raw_headers = tuple(self._raw_headers) + headers = CIMultiDictProxy(CIMultiDict(self._headers)) + + if self._cparser.type == cparser.HTTP_REQUEST: + h_upg = headers.get("upgrade", "") + allowed = upgrade and h_upg.isascii() and h_upg.lower() in ALLOWED_UPGRADES + if allowed or self._cparser.method == cparser.HTTP_CONNECT: + self._upgraded = True + else: + if upgrade and self._cparser.status_code == 101: + self._upgraded = True + + # do not support old websocket spec + if SEC_WEBSOCKET_KEY1 in headers: + raise InvalidHeader(SEC_WEBSOCKET_KEY1) + + encoding = None + enc = self._content_encoding + if enc is not None: + self._content_encoding = None + if enc.isascii() and enc.lower() in {"gzip", "deflate", "br", "zstd"}: + encoding = enc + + if self._cparser.type == cparser.HTTP_REQUEST: + method = http_method_str(self._cparser.method) + msg = _new_request_message( + method, self._path, + self.http_version(), headers, raw_headers, + should_close, encoding, upgrade, chunked, self._url) + else: + msg = _new_response_message( + self.http_version(), self._cparser.status_code, self._reason, + headers, raw_headers, should_close, encoding, + upgrade, chunked) + + if ( + ULLONG_MAX > self._cparser.content_length > 0 or chunked or + self._cparser.method == cparser.HTTP_CONNECT or + (self._cparser.status_code >= 199 and + self._cparser.content_length == 0 and + self._read_until_eof) + ): + payload = StreamReader( + self._protocol, timer=self._timer, loop=self._loop, + limit=self._limit) + else: + payload = EMPTY_PAYLOAD + + self._payload = payload + if encoding is not None and self._auto_decompress: + self._payload = DeflateBuffer(payload, encoding) + + if not self._response_with_body: + payload = EMPTY_PAYLOAD + + self._messages.append((msg, payload)) + + cdef _on_message_complete(self): + self._payload.feed_eof() + self._payload = None + + cdef _on_chunk_header(self): + self._payload.begin_http_chunk_receiving() + + cdef _on_chunk_complete(self): + self._payload.end_http_chunk_receiving() + + cdef object _on_status_complete(self): + pass + + cdef inline http_version(self): + cdef cparser.llhttp_t* parser = self._cparser + + if parser.http_major == 1: + if parser.http_minor == 0: + return HttpVersion10 + elif parser.http_minor == 1: + return HttpVersion11 + + return HttpVersion(parser.http_major, parser.http_minor) + + ### Public API ### + + def feed_eof(self): + cdef bytes desc + + if self._payload is not None: + if self._cparser.flags & cparser.F_CHUNKED: + raise TransferEncodingError( + "Not enough data to satisfy transfer length header.") + elif self._cparser.flags & cparser.F_CONTENT_LENGTH: + raise ContentLengthError( + "Not enough data to satisfy content length header.") + elif cparser.llhttp_get_errno(self._cparser) != cparser.HPE_OK: + desc = cparser.llhttp_get_error_reason(self._cparser) + raise PayloadEncodingError(desc.decode('latin-1')) + else: + self._payload.feed_eof() + elif self._started: + self._on_headers_complete() + if self._messages: + return self._messages[-1][0] + + def feed_data(self, data): + cdef: + size_t data_len + size_t nb + char* base + cdef cparser.llhttp_errno_t errno + + PyObject_GetBuffer(data, &self.py_buf, PyBUF_SIMPLE) + # Cache buffer pointer before PyBuffer_Release to avoid use-after-release. + base = self.py_buf.buf + data_len = self.py_buf.len + + errno = cparser.llhttp_execute( + self._cparser, + base, + data_len) + + if errno is cparser.HPE_PAUSED_UPGRADE: + cparser.llhttp_resume_after_upgrade(self._cparser) + + nb = cparser.llhttp_get_error_pos(self._cparser) - base + + PyBuffer_Release(&self.py_buf) + + if errno not in (cparser.HPE_OK, cparser.HPE_PAUSED_UPGRADE): + if self._payload_error == 0: + if self._last_error is not None: + ex = self._last_error + self._last_error = None + else: + after = cparser.llhttp_get_error_pos(self._cparser) + before = data[:after - base] + after_b = after.split(b"\r\n", 1)[0] + before = before.rsplit(b"\r\n", 1)[-1] + data = before + after_b + pointer = " " * (len(repr(before))-1) + "^" + ex = parser_error_from_errno(self._cparser, data, pointer) + self._payload = None + raise ex + + if self._messages: + messages = self._messages + self._messages = [] + else: + messages = () + + if self._upgraded: + return messages, True, data[nb:] + else: + return messages, False, b"" + + def set_upgraded(self, val): + self._upgraded = val + + +cdef class HttpRequestParser(HttpParser): + + def __init__( + self, protocol, loop, int limit, timer=None, + size_t max_line_size=8190, size_t max_headers=128, + size_t max_field_size=8190, payload_exception=None, + bint response_with_body=True, bint read_until_eof=False, + bint auto_decompress=True, + ): + self._init(cparser.HTTP_REQUEST, protocol, loop, limit, timer, + max_line_size, max_headers, max_field_size, + payload_exception, response_with_body, read_until_eof, + auto_decompress) + + cdef object _on_status_complete(self): + cdef int idx1, idx2 + if not self._buf: + return + self._path = self._buf.decode('utf-8', 'surrogateescape') + try: + idx3 = len(self._path) + if self._cparser.method == cparser.HTTP_CONNECT: + # authority-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3 + self._url = URL.build(authority=self._path, encoded=True) + elif idx3 > 1 and self._path[0] == '/': + # origin-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1 + idx1 = self._path.find("?") + if idx1 == -1: + query = "" + idx2 = self._path.find("#") + if idx2 == -1: + path = self._path + fragment = "" + else: + path = self._path[0: idx2] + fragment = self._path[idx2+1:] + + else: + path = self._path[0:idx1] + idx1 += 1 + idx2 = self._path.find("#", idx1+1) + if idx2 == -1: + query = self._path[idx1:] + fragment = "" + else: + query = self._path[idx1: idx2] + fragment = self._path[idx2+1:] + + self._url = URL.build( + path=path, + query_string=query, + fragment=fragment, + encoded=True, + ) + else: + # absolute-form for proxy maybe, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.2 + self._url = URL(self._path, encoded=True) + finally: + PyByteArray_Resize(self._buf, 0) + + +cdef class HttpResponseParser(HttpParser): + + def __init__( + self, protocol, loop, int limit, timer=None, + size_t max_line_size=8190, size_t max_headers=128, + size_t max_field_size=8190, payload_exception=None, + bint response_with_body=True, bint read_until_eof=False, + bint auto_decompress=True + ): + self._init(cparser.HTTP_RESPONSE, protocol, loop, limit, timer, + max_line_size, max_headers, max_field_size, + payload_exception, response_with_body, read_until_eof, + auto_decompress) + # Use strict parsing on dev mode, so users are warned about broken servers. + if not DEBUG: + cparser.llhttp_set_lenient_headers(self._cparser, 1) + cparser.llhttp_set_lenient_optional_cr_before_lf(self._cparser, 1) + cparser.llhttp_set_lenient_spaces_after_chunk_size(self._cparser, 1) + self._lax = True + + cdef object _on_status_complete(self): + if self._buf: + self._reason = self._buf.decode('utf-8', 'surrogateescape') + PyByteArray_Resize(self._buf, 0) + else: + self._reason = self._reason or '' + +cdef int cb_on_message_begin(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + + pyparser._started = True + pyparser._headers = [] + pyparser._seen_singletons = set() + pyparser._raw_headers = [] + PyByteArray_Resize(pyparser._buf, 0) + pyparser._path = None + pyparser._reason = None + return 0 + + +cdef int cb_on_url(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + try: + if length > pyparser._max_line_size: + status = pyparser._buf + at[:length] + raise LineTooLong(status[:100] + b"...", pyparser._max_line_size) + extend(pyparser._buf, at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_status(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + try: + if length > pyparser._max_line_size: + reason = pyparser._buf + at[:length] + raise LineTooLong(reason[:100] + b"...", pyparser._max_line_size) + extend(pyparser._buf, at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_header_field(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef Py_ssize_t size + try: + pyparser._on_status_complete() + size = len(pyparser._raw_name) + length + if size > pyparser._max_field_size: + name = pyparser._raw_name + at[:length] + raise LineTooLong(name[:100] + b"...", pyparser._max_field_size) + pyparser._header_name_size = size + pyparser._on_header_field(at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_header_value(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef Py_ssize_t size + try: + size = len(pyparser._raw_value) + length + if pyparser._header_name_size + size > pyparser._max_field_size: + value = pyparser._raw_value + at[:length] + raise LineTooLong(value[:100] + b"...", pyparser._max_field_size) + pyparser._on_header_value(at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_headers_complete(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._on_status_complete() + pyparser._on_headers_complete() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + if pyparser._upgraded or pyparser._cparser.method == cparser.HTTP_CONNECT: + return 2 + else: + return 0 + + +cdef int cb_on_body(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef bytes body = at[:length] + try: + pyparser._payload.feed_data(body, length) + except BaseException as underlying_exc: + reraised_exc = underlying_exc + if pyparser._payload_exception is not None: + reraised_exc = pyparser._payload_exception(str(underlying_exc)) + + set_exception(pyparser._payload, reraised_exc, underlying_exc) + + pyparser._payload_error = 1 + return -1 + else: + return 0 + + +cdef int cb_on_message_complete(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._started = False + pyparser._on_message_complete() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + return 0 + + +cdef int cb_on_chunk_header(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._on_chunk_header() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + return 0 + + +cdef int cb_on_chunk_complete(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._on_chunk_complete() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + return 0 + + +cdef parser_error_from_errno(cparser.llhttp_t* parser, data, pointer): + cdef cparser.llhttp_errno_t errno = cparser.llhttp_get_errno(parser) + cdef bytes desc = cparser.llhttp_get_error_reason(parser) + + err_msg = "{}:\n\n {!r}\n {}".format(desc.decode("latin-1"), data, pointer) + + if errno in {cparser.HPE_CB_MESSAGE_BEGIN, + cparser.HPE_CB_HEADERS_COMPLETE, + cparser.HPE_CB_MESSAGE_COMPLETE, + cparser.HPE_CB_CHUNK_HEADER, + cparser.HPE_CB_CHUNK_COMPLETE, + cparser.HPE_INVALID_CONSTANT, + cparser.HPE_INVALID_HEADER_TOKEN, + cparser.HPE_INVALID_CONTENT_LENGTH, + cparser.HPE_INVALID_CHUNK_SIZE, + cparser.HPE_INVALID_EOF_STATE, + cparser.HPE_INVALID_TRANSFER_ENCODING}: + return BadHttpMessage(err_msg) + elif errno == cparser.HPE_INVALID_METHOD: + return BadHttpMethod(error=err_msg) + elif errno in {cparser.HPE_INVALID_STATUS, + cparser.HPE_INVALID_VERSION}: + return BadStatusLine(error=err_msg) + elif errno == cparser.HPE_INVALID_URL: + return InvalidURLError(err_msg) + + return BadHttpMessage(err_msg) diff --git a/venv/lib/python3.12/site-packages/aiohttp/_http_writer.cpython-312-x86_64-linux-gnu.so b/venv/lib/python3.12/site-packages/aiohttp/_http_writer.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..9e76164242e0f9e1314e0d650884c26521b79698 GIT binary patch literal 472496 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 b(Y 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/aiohttp/_http_writer.pyx b/venv/lib/python3.12/site-packages/aiohttp/_http_writer.pyx new file mode 100644 index 00000000..ee8dcd7d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_http_writer.pyx @@ -0,0 +1,162 @@ +from cpython.bytes cimport PyBytes_FromStringAndSize +from cpython.exc cimport PyErr_NoMemory +from cpython.mem cimport PyMem_Free, PyMem_Malloc, PyMem_Realloc +from cpython.object cimport PyObject_Str +from libc.stdint cimport uint8_t, uint64_t +from libc.string cimport memcpy + +from multidict import istr + +DEF BUF_SIZE = 16 * 1024 # 16KiB + +cdef object _istr = istr + + +# ----------------- writer --------------------------- + +cdef struct Writer: + char *buf + Py_ssize_t size + Py_ssize_t pos + bint heap_allocated + +cdef inline void _init_writer(Writer* writer, char *buf): + writer.buf = buf + writer.size = BUF_SIZE + writer.pos = 0 + writer.heap_allocated = 0 + + +cdef inline void _release_writer(Writer* writer): + if writer.heap_allocated: + PyMem_Free(writer.buf) + + +cdef inline int _write_byte(Writer* writer, uint8_t ch): + cdef char * buf + cdef Py_ssize_t size + + if writer.pos == writer.size: + # reallocate + size = writer.size + BUF_SIZE + if not writer.heap_allocated: + buf = PyMem_Malloc(size) + if buf == NULL: + PyErr_NoMemory() + return -1 + memcpy(buf, writer.buf, writer.size) + else: + buf = PyMem_Realloc(writer.buf, size) + if buf == NULL: + PyErr_NoMemory() + return -1 + writer.buf = buf + writer.size = size + writer.heap_allocated = 1 + writer.buf[writer.pos] = ch + writer.pos += 1 + return 0 + + +cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): + cdef uint64_t utf = symbol + + if utf < 0x80: + return _write_byte(writer, utf) + elif utf < 0x800: + if _write_byte(writer, (0xc0 | (utf >> 6))) < 0: + return -1 + return _write_byte(writer, (0x80 | (utf & 0x3f))) + elif 0xD800 <= utf <= 0xDFFF: + # surogate pair, ignored + return 0 + elif utf < 0x10000: + if _write_byte(writer, (0xe0 | (utf >> 12))) < 0: + return -1 + if _write_byte(writer, (0x80 | ((utf >> 6) & 0x3f))) < 0: + return -1 + return _write_byte(writer, (0x80 | (utf & 0x3f))) + elif utf > 0x10FFFF: + # symbol is too large + return 0 + else: + if _write_byte(writer, (0xf0 | (utf >> 18))) < 0: + return -1 + if _write_byte(writer, + (0x80 | ((utf >> 12) & 0x3f))) < 0: + return -1 + if _write_byte(writer, + (0x80 | ((utf >> 6) & 0x3f))) < 0: + return -1 + return _write_byte(writer, (0x80 | (utf & 0x3f))) + + +cdef inline int _write_str(Writer* writer, str s): + cdef Py_UCS4 ch + for ch in s: + if _write_utf8(writer, ch) < 0: + return -1 + + +cdef inline int _write_str_raise_on_nlcr(Writer* writer, object s): + cdef Py_UCS4 ch + cdef str out_str + if type(s) is str: + out_str = s + elif type(s) is _istr: + out_str = PyObject_Str(s) + elif not isinstance(s, str): + raise TypeError("Cannot serialize non-str key {!r}".format(s)) + else: + out_str = str(s) + + for ch in out_str: + if ch in {0x0D, 0x0A, 0x00}: + raise ValueError( + "Newline, carriage return, or null byte detected in headers. " + "Potential header injection attack." + ) + if _write_utf8(writer, ch) < 0: + return -1 + + +# --------------- _serialize_headers ---------------------- + +def _serialize_headers(str status_line, headers): + cdef Writer writer + cdef object key + cdef object val + cdef char buf[BUF_SIZE] + + _init_writer(&writer, buf) + + try: + if _write_str_raise_on_nlcr(&writer, status_line) < 0: + raise + if _write_byte(&writer, b'\r') < 0: + raise + if _write_byte(&writer, b'\n') < 0: + raise + + for key, val in headers.items(): + if _write_str_raise_on_nlcr(&writer, key) < 0: + raise + if _write_byte(&writer, b':') < 0: + raise + if _write_byte(&writer, b' ') < 0: + raise + if _write_str_raise_on_nlcr(&writer, val) < 0: + raise + if _write_byte(&writer, b'\r') < 0: + raise + if _write_byte(&writer, b'\n') < 0: + raise + + if _write_byte(&writer, b'\r') < 0: + raise + if _write_byte(&writer, b'\n') < 0: + raise + + return PyBytes_FromStringAndSize(writer.buf, writer.pos) + finally: + _release_writer(&writer) diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/mask.pxd.hash b/venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/mask.pxd.hash new file mode 100644 index 00000000..eadfed3d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/mask.pxd.hash @@ -0,0 +1 @@ +b01999d409b29bd916e067bc963d5f2d9ee63cfc9ae0bccb769910131417bf93 /home/runner/work/aiohttp/aiohttp/aiohttp/_websocket/mask.pxd diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/mask.pyx.hash b/venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/mask.pyx.hash new file mode 100644 index 00000000..5cd7ae67 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/mask.pyx.hash @@ -0,0 +1 @@ +0478ceb55d0ed30ef1a7da742cd003449bc69a07cf9fdb06789bd2b347cbfffe /home/runner/work/aiohttp/aiohttp/aiohttp/_websocket/mask.pyx diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/reader_c.pxd.hash b/venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/reader_c.pxd.hash new file mode 100644 index 00000000..f2feb6aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/.hash/reader_c.pxd.hash @@ -0,0 +1 @@ +9e5fe78ed0ebce5414d2b8e01868d90c1facc20b84d2d5ff6c23e86e44a155ae /home/runner/work/aiohttp/aiohttp/aiohttp/_websocket/reader_c.pxd diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/__init__.py b/venv/lib/python3.12/site-packages/aiohttp/_websocket/__init__.py new file mode 100644 index 00000000..836257cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/__init__.py @@ -0,0 +1 @@ +"""WebSocket protocol versions 13 and 8.""" diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/helpers.py b/venv/lib/python3.12/site-packages/aiohttp/_websocket/helpers.py new file mode 100644 index 00000000..0bb58df9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/helpers.py @@ -0,0 +1,147 @@ +"""Helpers for WebSocket protocol versions 13 and 8.""" + +import functools +import re +from struct import Struct +from typing import TYPE_CHECKING, Final, List, Optional, Pattern, Tuple + +from ..helpers import NO_EXTENSIONS +from .models import WSHandshakeError + +UNPACK_LEN3 = Struct("!Q").unpack_from +UNPACK_CLOSE_CODE = Struct("!H").unpack +PACK_LEN1 = Struct("!BB").pack +PACK_LEN2 = Struct("!BBH").pack +PACK_LEN3 = Struct("!BBQ").pack +PACK_CLOSE_CODE = Struct("!H").pack +PACK_RANDBITS = Struct("!L").pack +MSG_SIZE: Final[int] = 2**14 +MASK_LEN: Final[int] = 4 + +WS_KEY: Final[bytes] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + + +# Used by _websocket_mask_python +@functools.lru_cache +def _xor_table() -> List[bytes]: + return [bytes(a ^ b for a in range(256)) for b in range(256)] + + +def _websocket_mask_python(mask: bytes, data: bytearray) -> None: + """Websocket masking function. + + `mask` is a `bytes` object of length 4; `data` is a `bytearray` + object of any length. The contents of `data` are masked with `mask`, + as specified in section 5.3 of RFC 6455. + + Note that this function mutates the `data` argument. + + This pure-python implementation may be replaced by an optimized + version when available. + + """ + assert isinstance(data, bytearray), data + assert len(mask) == 4, mask + + if data: + _XOR_TABLE = _xor_table() + a, b, c, d = (_XOR_TABLE[n] for n in mask) + data[::4] = data[::4].translate(a) + data[1::4] = data[1::4].translate(b) + data[2::4] = data[2::4].translate(c) + data[3::4] = data[3::4].translate(d) + + +if TYPE_CHECKING or NO_EXTENSIONS: # pragma: no cover + websocket_mask = _websocket_mask_python +else: + try: + from .mask import _websocket_mask_cython # type: ignore[import-not-found] + + websocket_mask = _websocket_mask_cython + except ImportError: # pragma: no cover + websocket_mask = _websocket_mask_python + + +_WS_EXT_RE: Final[Pattern[str]] = re.compile( + r"^(?:;\s*(?:" + r"(server_no_context_takeover)|" + r"(client_no_context_takeover)|" + r"(server_max_window_bits(?:=(\d+))?)|" + r"(client_max_window_bits(?:=(\d+))?)))*$" +) + +_WS_EXT_RE_SPLIT: Final[Pattern[str]] = re.compile(r"permessage-deflate([^,]+)?") + + +def ws_ext_parse(extstr: Optional[str], isserver: bool = False) -> Tuple[int, bool]: + if not extstr: + return 0, False + + compress = 0 + notakeover = False + for ext in _WS_EXT_RE_SPLIT.finditer(extstr): + defext = ext.group(1) + # Return compress = 15 when get `permessage-deflate` + if not defext: + compress = 15 + break + match = _WS_EXT_RE.match(defext) + if match: + compress = 15 + if isserver: + # Server never fail to detect compress handshake. + # Server does not need to send max wbit to client + if match.group(4): + compress = int(match.group(4)) + # Group3 must match if group4 matches + # Compress wbit 8 does not support in zlib + # If compress level not support, + # CONTINUE to next extension + if compress > 15 or compress < 9: + compress = 0 + continue + if match.group(1): + notakeover = True + # Ignore regex group 5 & 6 for client_max_window_bits + break + else: + if match.group(6): + compress = int(match.group(6)) + # Group5 must match if group6 matches + # Compress wbit 8 does not support in zlib + # If compress level not support, + # FAIL the parse progress + if compress > 15 or compress < 9: + raise WSHandshakeError("Invalid window size") + if match.group(2): + notakeover = True + # Ignore regex group 5 & 6 for client_max_window_bits + break + # Return Fail if client side and not match + elif not isserver: + raise WSHandshakeError("Extension for deflate not supported" + ext.group(1)) + + return compress, notakeover + + +def ws_ext_gen( + compress: int = 15, isserver: bool = False, server_notakeover: bool = False +) -> str: + # client_notakeover=False not used for server + # compress wbit 8 does not support in zlib + if compress < 9 or compress > 15: + raise ValueError( + "Compress wbits must between 9 and 15, zlib does not support wbits=8" + ) + enabledext = ["permessage-deflate"] + if not isserver: + enabledext.append("client_max_window_bits") + + if compress < 15: + enabledext.append("server_max_window_bits=" + str(compress)) + if server_notakeover: + enabledext.append("server_no_context_takeover") + # if client_notakeover: + # enabledext.append('client_no_context_takeover') + return "; ".join(enabledext) diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.cpython-312-x86_64-linux-gnu.so b/venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..3fdd1844a119c3fc75bbb4c063bed9643520b286 GIT binary patch literal 263512 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

CI?z#rRJ0cevv8q?vj*=>+}}hP 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 W;wQ9kx>NE$`MssC3Iu{?_J0BWCeQ-_ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.pxd b/venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.pxd new file mode 100644 index 00000000..90983de9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.pxd @@ -0,0 +1,3 @@ +"""Cython declarations for websocket masking.""" + +cpdef void _websocket_mask_cython(bytes mask, bytearray data) diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.pyx b/venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.pyx new file mode 100644 index 00000000..2d956c88 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/mask.pyx @@ -0,0 +1,48 @@ +from cpython cimport PyBytes_AsString + + +#from cpython cimport PyByteArray_AsString # cython still not exports that +cdef extern from "Python.h": + char* PyByteArray_AsString(bytearray ba) except NULL + +from libc.stdint cimport uint32_t, uint64_t, uintmax_t + + +cpdef void _websocket_mask_cython(bytes mask, bytearray data): + """Note, this function mutates its `data` argument + """ + cdef: + Py_ssize_t data_len, i + # bit operations on signed integers are implementation-specific + unsigned char * in_buf + const unsigned char * mask_buf + uint32_t uint32_msk + uint64_t uint64_msk + + assert len(mask) == 4 + + data_len = len(data) + in_buf = PyByteArray_AsString(data) + mask_buf = PyBytes_AsString(mask) + uint32_msk = (mask_buf)[0] + + # TODO: align in_data ptr to achieve even faster speeds + # does it need in python ?! malloc() always aligns to sizeof(long) bytes + + if sizeof(size_t) >= 8: + uint64_msk = uint32_msk + uint64_msk = (uint64_msk << 32) | uint32_msk + + while data_len >= 8: + (in_buf)[0] ^= uint64_msk + in_buf += 8 + data_len -= 8 + + + while data_len >= 4: + (in_buf)[0] ^= uint32_msk + in_buf += 4 + data_len -= 4 + + for i in range(0, data_len): + in_buf[i] ^= mask_buf[i] diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/models.py b/venv/lib/python3.12/site-packages/aiohttp/_websocket/models.py new file mode 100644 index 00000000..7e89b965 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/models.py @@ -0,0 +1,84 @@ +"""Models for WebSocket protocol versions 13 and 8.""" + +import json +from enum import IntEnum +from typing import Any, Callable, Final, NamedTuple, Optional, cast + +WS_DEFLATE_TRAILING: Final[bytes] = bytes([0x00, 0x00, 0xFF, 0xFF]) + + +class WSCloseCode(IntEnum): + OK = 1000 + GOING_AWAY = 1001 + PROTOCOL_ERROR = 1002 + UNSUPPORTED_DATA = 1003 + ABNORMAL_CLOSURE = 1006 + INVALID_TEXT = 1007 + POLICY_VIOLATION = 1008 + MESSAGE_TOO_BIG = 1009 + MANDATORY_EXTENSION = 1010 + INTERNAL_ERROR = 1011 + SERVICE_RESTART = 1012 + TRY_AGAIN_LATER = 1013 + BAD_GATEWAY = 1014 + + +class WSMsgType(IntEnum): + # websocket spec types + CONTINUATION = 0x0 + TEXT = 0x1 + BINARY = 0x2 + PING = 0x9 + PONG = 0xA + CLOSE = 0x8 + + # aiohttp specific types + CLOSING = 0x100 + CLOSED = 0x101 + ERROR = 0x102 + + text = TEXT + binary = BINARY + ping = PING + pong = PONG + close = CLOSE + closing = CLOSING + closed = CLOSED + error = ERROR + + +class WSMessage(NamedTuple): + type: WSMsgType + # To type correctly, this would need some kind of tagged union for each type. + data: Any + extra: Optional[str] + + def json(self, *, loads: Callable[[Any], Any] = json.loads) -> Any: + """Return parsed JSON data. + + .. versionadded:: 0.22 + """ + return loads(self.data) + + +# Constructing the tuple directly to avoid the overhead of +# the lambda and arg processing since NamedTuples are constructed +# with a run time built lambda +# https://github.com/python/cpython/blob/d83fcf8371f2f33c7797bc8f5423a8bca8c46e5c/Lib/collections/__init__.py#L441 +WS_CLOSED_MESSAGE = tuple.__new__(WSMessage, (WSMsgType.CLOSED, None, None)) +WS_CLOSING_MESSAGE = tuple.__new__(WSMessage, (WSMsgType.CLOSING, None, None)) + + +class WebSocketError(Exception): + """WebSocket protocol parser error.""" + + def __init__(self, code: int, message: str) -> None: + self.code = code + super().__init__(code, message) + + def __str__(self) -> str: + return cast(str, self.args[1]) + + +class WSHandshakeError(Exception): + """WebSocket protocol handshake error.""" diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/reader.py b/venv/lib/python3.12/site-packages/aiohttp/_websocket/reader.py new file mode 100644 index 00000000..23f32265 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/reader.py @@ -0,0 +1,31 @@ +"""Reader for WebSocket protocol versions 13 and 8.""" + +from typing import TYPE_CHECKING + +from ..helpers import NO_EXTENSIONS + +if TYPE_CHECKING or NO_EXTENSIONS: # pragma: no cover + from .reader_py import ( + WebSocketDataQueue as WebSocketDataQueuePython, + WebSocketReader as WebSocketReaderPython, + ) + + WebSocketReader = WebSocketReaderPython + WebSocketDataQueue = WebSocketDataQueuePython +else: + try: + from .reader_c import ( # type: ignore[import-not-found] + WebSocketDataQueue as WebSocketDataQueueCython, + WebSocketReader as WebSocketReaderCython, + ) + + WebSocketReader = WebSocketReaderCython + WebSocketDataQueue = WebSocketDataQueueCython + except ImportError: # pragma: no cover + from .reader_py import ( + WebSocketDataQueue as WebSocketDataQueuePython, + WebSocketReader as WebSocketReaderPython, + ) + + WebSocketReader = WebSocketReaderPython + WebSocketDataQueue = WebSocketDataQueuePython diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/reader_c.cpython-312-x86_64-linux-gnu.so b/venv/lib/python3.12/site-packages/aiohttp/_websocket/reader_c.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..e12a92da4ee7893203012fdf39f1c493f1d7658a GIT binary patch literal 1822128 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%0DF5vZ3 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

H>xy)@p?i(K5 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 None: + self._size = 0 + self._protocol = protocol + self._limit = limit * 2 + self._loop = loop + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._exception: Union[BaseException, None] = None + self._buffer: Deque[Tuple[WSMessage, int]] = deque() + self._get_buffer = self._buffer.popleft + self._put_buffer = self._buffer.append + + def is_eof(self) -> bool: + return self._eof + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: builtins.BaseException = _EXC_SENTINEL, + ) -> None: + self._eof = True + self._exception = exc + if (waiter := self._waiter) is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + def _release_waiter(self) -> None: + if (waiter := self._waiter) is None: + return + self._waiter = None + if not waiter.done(): + waiter.set_result(None) + + def feed_eof(self) -> None: + self._eof = True + self._release_waiter() + self._exception = None # Break cyclic references + + def feed_data(self, data: "WSMessage", size: "cython_int") -> None: + self._size += size + self._put_buffer((data, size)) + self._release_waiter() + if self._size > self._limit and not self._protocol._reading_paused: + self._protocol.pause_reading() + + async def read(self) -> WSMessage: + if not self._buffer and not self._eof: + assert not self._waiter + self._waiter = self._loop.create_future() + try: + await self._waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._waiter = None + raise + return self._read_from_buffer() + + def _read_from_buffer(self) -> WSMessage: + if self._buffer: + data, size = self._get_buffer() + self._size -= size + if self._size < self._limit and self._protocol._reading_paused: + self._protocol.resume_reading() + return data + if self._exception is not None: + raise self._exception + raise EofStream + + +class WebSocketReader: + def __init__( + self, queue: WebSocketDataQueue, max_msg_size: int, compress: bool = True + ) -> None: + self.queue = queue + self._max_msg_size = max_msg_size + + self._exc: Optional[Exception] = None + self._partial = bytearray() + self._state = READ_HEADER + + self._opcode: int = OP_CODE_NOT_SET + self._frame_fin = False + self._frame_opcode: int = OP_CODE_NOT_SET + self._payload_fragments: list[bytes] = [] + self._frame_payload_len = 0 + + self._tail: bytes = b"" + self._has_mask = False + self._frame_mask: Optional[bytes] = None + self._payload_bytes_to_read = 0 + self._payload_len_flag = 0 + self._compressed: int = COMPRESSED_NOT_SET + self._decompressobj: Optional[ZLibDecompressor] = None + self._compress = compress + + def feed_eof(self) -> None: + self.queue.feed_eof() + + # data can be bytearray on Windows because proactor event loop uses bytearray + # and asyncio types this to Union[bytes, bytearray, memoryview] so we need + # coerce data to bytes if it is not + def feed_data( + self, data: Union[bytes, bytearray, memoryview] + ) -> Tuple[bool, bytes]: + if type(data) is not bytes: + data = bytes(data) + + if self._exc is not None: + return True, data + + try: + self._feed_data(data) + except Exception as exc: + self._exc = exc + set_exception(self.queue, exc) + return EMPTY_FRAME_ERROR + + return EMPTY_FRAME + + def _handle_frame( + self, + fin: bool, + opcode: Union[int, cython_int], # Union intended: Cython pxd uses C int + payload: Union[bytes, bytearray], + compressed: Union[int, cython_int], # Union intended: Cython pxd uses C int + ) -> None: + msg: WSMessage + if opcode in {OP_CODE_TEXT, OP_CODE_BINARY, OP_CODE_CONTINUATION}: + # Validate continuation frames before processing + if opcode == OP_CODE_CONTINUATION and self._opcode == OP_CODE_NOT_SET: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Continuation frame for non started message", + ) + + # load text/binary + if not fin: + # got partial frame payload + if opcode != OP_CODE_CONTINUATION: + self._opcode = opcode + self._partial += payload + if self._max_msg_size and len(self._partial) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Message size {len(self._partial)} " + f"exceeds limit {self._max_msg_size}", + ) + return + + has_partial = bool(self._partial) + if opcode == OP_CODE_CONTINUATION: + opcode = self._opcode + self._opcode = OP_CODE_NOT_SET + # previous frame was non finished + # we should get continuation opcode + elif has_partial: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "The opcode in non-fin frame is expected " + f"to be zero, got {opcode!r}", + ) + + assembled_payload: Union[bytes, bytearray] + if has_partial: + assembled_payload = self._partial + payload + self._partial.clear() + else: + assembled_payload = payload + + if self._max_msg_size and len(assembled_payload) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Message size {len(assembled_payload)} " + f"exceeds limit {self._max_msg_size}", + ) + + # Decompress process must to be done after all packets + # received. + if compressed: + if not self._decompressobj: + self._decompressobj = ZLibDecompressor(suppress_deflate_header=True) + # XXX: It's possible that the zlib backend (isal is known to + # do this, maybe others too?) will return max_length bytes, + # but internally buffer more data such that the payload is + # >max_length, so we return one extra byte and if we're able + # to do that, then the message is too big. + payload_merged = self._decompressobj.decompress_sync( + assembled_payload + WS_DEFLATE_TRAILING, + ( + self._max_msg_size + 1 + if self._max_msg_size + else self._max_msg_size + ), + ) + if self._max_msg_size and len(payload_merged) > self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Decompressed message exceeds size limit {self._max_msg_size}", + ) + elif type(assembled_payload) is bytes: + payload_merged = assembled_payload + else: + payload_merged = bytes(assembled_payload) + + if opcode == OP_CODE_TEXT: + try: + text = payload_merged.decode("utf-8") + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + + # XXX: The Text and Binary messages here can be a performance + # bottleneck, so we use tuple.__new__ to improve performance. + # This is not type safe, but many tests should fail in + # test_client_ws_functional.py if this is wrong. + self.queue.feed_data( + TUPLE_NEW(WSMessage, (WS_MSG_TYPE_TEXT, text, "")), + len(payload_merged), + ) + else: + self.queue.feed_data( + TUPLE_NEW(WSMessage, (WS_MSG_TYPE_BINARY, payload_merged, "")), + len(payload_merged), + ) + elif opcode == OP_CODE_CLOSE: + if len(payload) >= 2: + close_code = UNPACK_CLOSE_CODE(payload[:2])[0] + if close_code < 3000 and close_code not in ALLOWED_CLOSE_CODES: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close code: {close_code}", + ) + try: + close_message = payload[2:].decode("utf-8") + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + msg = TUPLE_NEW(WSMessage, (WSMsgType.CLOSE, close_code, close_message)) + elif payload: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close frame: {fin} {opcode} {payload!r}", + ) + else: + msg = TUPLE_NEW(WSMessage, (WSMsgType.CLOSE, 0, "")) + + self.queue.feed_data(msg, 0) + elif opcode == OP_CODE_PING: + msg = TUPLE_NEW(WSMessage, (WSMsgType.PING, payload, "")) + self.queue.feed_data(msg, len(payload)) + elif opcode == OP_CODE_PONG: + msg = TUPLE_NEW(WSMessage, (WSMsgType.PONG, payload, "")) + self.queue.feed_data(msg, len(payload)) + else: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, f"Unexpected opcode={opcode!r}" + ) + + def _feed_data(self, data: bytes) -> None: + """Return the next frame from the socket.""" + if self._tail: + data, self._tail = self._tail + data, b"" + + start_pos: int = 0 + data_len = len(data) + data_cstr = data + + while True: + # read header + if self._state == READ_HEADER: + if data_len - start_pos < 2: + break + first_byte = data_cstr[start_pos] + second_byte = data_cstr[start_pos + 1] + start_pos += 2 + + fin = (first_byte >> 7) & 1 + rsv1 = (first_byte >> 6) & 1 + rsv2 = (first_byte >> 5) & 1 + rsv3 = (first_byte >> 4) & 1 + opcode = first_byte & 0xF + + # frame-fin = %x0 ; more frames of this message follow + # / %x1 ; final frame of this message + # frame-rsv1 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv2 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv3 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # + # Remove rsv1 from this test for deflate development + if rsv2 or rsv3 or (rsv1 and not self._compress): + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + if opcode > 0x7 and fin == 0: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received fragmented control frame", + ) + + has_mask = (second_byte >> 7) & 1 + length = second_byte & 0x7F + + # Control frames MUST have a payload + # length of 125 bytes or less + if opcode > 0x7 and length > 125: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Control frame payload cannot be larger than 125 bytes", + ) + + # Set compress status if last package is FIN + # OR set compress status if this is first fragment + # Raise error if not first fragment with rsv1 = 0x1 + if self._frame_fin or self._compressed == COMPRESSED_NOT_SET: + self._compressed = COMPRESSED_TRUE if rsv1 else COMPRESSED_FALSE + elif rsv1: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + self._frame_fin = bool(fin) + self._frame_opcode = opcode + self._has_mask = bool(has_mask) + self._payload_len_flag = length + self._state = READ_PAYLOAD_LENGTH + + # read payload length + if self._state == READ_PAYLOAD_LENGTH: + len_flag = self._payload_len_flag + if len_flag == 126: + if data_len - start_pos < 2: + break + first_byte = data_cstr[start_pos] + second_byte = data_cstr[start_pos + 1] + start_pos += 2 + self._payload_bytes_to_read = first_byte << 8 | second_byte + elif len_flag > 126: + if data_len - start_pos < 8: + break + self._payload_bytes_to_read = UNPACK_LEN3(data, start_pos)[0] + start_pos += 8 + else: + self._payload_bytes_to_read = len_flag + + self._state = READ_PAYLOAD_MASK if self._has_mask else READ_PAYLOAD + + # read payload mask + if self._state == READ_PAYLOAD_MASK: + if data_len - start_pos < 4: + break + self._frame_mask = data_cstr[start_pos : start_pos + 4] + start_pos += 4 + self._state = READ_PAYLOAD + + if self._state == READ_PAYLOAD: + chunk_len = data_len - start_pos + if self._payload_bytes_to_read >= chunk_len: + f_end_pos = data_len + self._payload_bytes_to_read -= chunk_len + else: + f_end_pos = start_pos + self._payload_bytes_to_read + self._payload_bytes_to_read = 0 + + had_fragments = self._frame_payload_len + self._frame_payload_len += f_end_pos - start_pos + f_start_pos = start_pos + start_pos = f_end_pos + + if self._payload_bytes_to_read != 0: + # If we don't have a complete frame, we need to save the + # data for the next call to feed_data. + self._payload_fragments.append(data_cstr[f_start_pos:f_end_pos]) + break + + payload: Union[bytes, bytearray] + if had_fragments: + # We have to join the payload fragments get the payload + self._payload_fragments.append(data_cstr[f_start_pos:f_end_pos]) + if self._has_mask: + assert self._frame_mask is not None + payload_bytearray = bytearray(b"".join(self._payload_fragments)) + websocket_mask(self._frame_mask, payload_bytearray) + payload = payload_bytearray + else: + payload = b"".join(self._payload_fragments) + self._payload_fragments.clear() + elif self._has_mask: + assert self._frame_mask is not None + payload_bytearray = data_cstr[f_start_pos:f_end_pos] # type: ignore[assignment] + if type(payload_bytearray) is not bytearray: # pragma: no branch + # Cython will do the conversion for us + # but we need to do it for Python and we + # will always get here in Python + payload_bytearray = bytearray(payload_bytearray) + websocket_mask(self._frame_mask, payload_bytearray) + payload = payload_bytearray + else: + payload = data_cstr[f_start_pos:f_end_pos] + + self._handle_frame( + self._frame_fin, self._frame_opcode, payload, self._compressed + ) + self._frame_payload_len = 0 + self._state = READ_HEADER + + # XXX: Cython needs slices to be bounded, so we can't omit the slice end here. + self._tail = data_cstr[start_pos:data_len] if start_pos < data_len else b"" diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/reader_py.py b/venv/lib/python3.12/site-packages/aiohttp/_websocket/reader_py.py new file mode 100644 index 00000000..5166d7ec --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/reader_py.py @@ -0,0 +1,478 @@ +"""Reader for WebSocket protocol versions 13 and 8.""" + +import asyncio +import builtins +from collections import deque +from typing import Deque, Final, Optional, Set, Tuple, Union + +from ..base_protocol import BaseProtocol +from ..compression_utils import ZLibDecompressor +from ..helpers import _EXC_SENTINEL, set_exception +from ..streams import EofStream +from .helpers import UNPACK_CLOSE_CODE, UNPACK_LEN3, websocket_mask +from .models import ( + WS_DEFLATE_TRAILING, + WebSocketError, + WSCloseCode, + WSMessage, + WSMsgType, +) + +ALLOWED_CLOSE_CODES: Final[Set[int]] = {int(i) for i in WSCloseCode} + +# States for the reader, used to parse the WebSocket frame +# integer values are used so they can be cythonized +READ_HEADER = 1 +READ_PAYLOAD_LENGTH = 2 +READ_PAYLOAD_MASK = 3 +READ_PAYLOAD = 4 + +WS_MSG_TYPE_BINARY = WSMsgType.BINARY +WS_MSG_TYPE_TEXT = WSMsgType.TEXT + +# WSMsgType values unpacked so they can by cythonized to ints +OP_CODE_NOT_SET = -1 +OP_CODE_CONTINUATION = WSMsgType.CONTINUATION.value +OP_CODE_TEXT = WSMsgType.TEXT.value +OP_CODE_BINARY = WSMsgType.BINARY.value +OP_CODE_CLOSE = WSMsgType.CLOSE.value +OP_CODE_PING = WSMsgType.PING.value +OP_CODE_PONG = WSMsgType.PONG.value + +EMPTY_FRAME_ERROR = (True, b"") +EMPTY_FRAME = (False, b"") + +COMPRESSED_NOT_SET = -1 +COMPRESSED_FALSE = 0 +COMPRESSED_TRUE = 1 + +TUPLE_NEW = tuple.__new__ + +cython_int = int # Typed to int in Python, but cython with use a signed int in the pxd + + +class WebSocketDataQueue: + """WebSocketDataQueue resumes and pauses an underlying stream. + + It is a destination for WebSocket data. + """ + + def __init__( + self, protocol: BaseProtocol, limit: int, *, loop: asyncio.AbstractEventLoop + ) -> None: + self._size = 0 + self._protocol = protocol + self._limit = limit * 2 + self._loop = loop + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._exception: Union[BaseException, None] = None + self._buffer: Deque[Tuple[WSMessage, int]] = deque() + self._get_buffer = self._buffer.popleft + self._put_buffer = self._buffer.append + + def is_eof(self) -> bool: + return self._eof + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: builtins.BaseException = _EXC_SENTINEL, + ) -> None: + self._eof = True + self._exception = exc + if (waiter := self._waiter) is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + def _release_waiter(self) -> None: + if (waiter := self._waiter) is None: + return + self._waiter = None + if not waiter.done(): + waiter.set_result(None) + + def feed_eof(self) -> None: + self._eof = True + self._release_waiter() + self._exception = None # Break cyclic references + + def feed_data(self, data: "WSMessage", size: "cython_int") -> None: + self._size += size + self._put_buffer((data, size)) + self._release_waiter() + if self._size > self._limit and not self._protocol._reading_paused: + self._protocol.pause_reading() + + async def read(self) -> WSMessage: + if not self._buffer and not self._eof: + assert not self._waiter + self._waiter = self._loop.create_future() + try: + await self._waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._waiter = None + raise + return self._read_from_buffer() + + def _read_from_buffer(self) -> WSMessage: + if self._buffer: + data, size = self._get_buffer() + self._size -= size + if self._size < self._limit and self._protocol._reading_paused: + self._protocol.resume_reading() + return data + if self._exception is not None: + raise self._exception + raise EofStream + + +class WebSocketReader: + def __init__( + self, queue: WebSocketDataQueue, max_msg_size: int, compress: bool = True + ) -> None: + self.queue = queue + self._max_msg_size = max_msg_size + + self._exc: Optional[Exception] = None + self._partial = bytearray() + self._state = READ_HEADER + + self._opcode: int = OP_CODE_NOT_SET + self._frame_fin = False + self._frame_opcode: int = OP_CODE_NOT_SET + self._payload_fragments: list[bytes] = [] + self._frame_payload_len = 0 + + self._tail: bytes = b"" + self._has_mask = False + self._frame_mask: Optional[bytes] = None + self._payload_bytes_to_read = 0 + self._payload_len_flag = 0 + self._compressed: int = COMPRESSED_NOT_SET + self._decompressobj: Optional[ZLibDecompressor] = None + self._compress = compress + + def feed_eof(self) -> None: + self.queue.feed_eof() + + # data can be bytearray on Windows because proactor event loop uses bytearray + # and asyncio types this to Union[bytes, bytearray, memoryview] so we need + # coerce data to bytes if it is not + def feed_data( + self, data: Union[bytes, bytearray, memoryview] + ) -> Tuple[bool, bytes]: + if type(data) is not bytes: + data = bytes(data) + + if self._exc is not None: + return True, data + + try: + self._feed_data(data) + except Exception as exc: + self._exc = exc + set_exception(self.queue, exc) + return EMPTY_FRAME_ERROR + + return EMPTY_FRAME + + def _handle_frame( + self, + fin: bool, + opcode: Union[int, cython_int], # Union intended: Cython pxd uses C int + payload: Union[bytes, bytearray], + compressed: Union[int, cython_int], # Union intended: Cython pxd uses C int + ) -> None: + msg: WSMessage + if opcode in {OP_CODE_TEXT, OP_CODE_BINARY, OP_CODE_CONTINUATION}: + # Validate continuation frames before processing + if opcode == OP_CODE_CONTINUATION and self._opcode == OP_CODE_NOT_SET: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Continuation frame for non started message", + ) + + # load text/binary + if not fin: + # got partial frame payload + if opcode != OP_CODE_CONTINUATION: + self._opcode = opcode + self._partial += payload + if self._max_msg_size and len(self._partial) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Message size {len(self._partial)} " + f"exceeds limit {self._max_msg_size}", + ) + return + + has_partial = bool(self._partial) + if opcode == OP_CODE_CONTINUATION: + opcode = self._opcode + self._opcode = OP_CODE_NOT_SET + # previous frame was non finished + # we should get continuation opcode + elif has_partial: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "The opcode in non-fin frame is expected " + f"to be zero, got {opcode!r}", + ) + + assembled_payload: Union[bytes, bytearray] + if has_partial: + assembled_payload = self._partial + payload + self._partial.clear() + else: + assembled_payload = payload + + if self._max_msg_size and len(assembled_payload) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Message size {len(assembled_payload)} " + f"exceeds limit {self._max_msg_size}", + ) + + # Decompress process must to be done after all packets + # received. + if compressed: + if not self._decompressobj: + self._decompressobj = ZLibDecompressor(suppress_deflate_header=True) + # XXX: It's possible that the zlib backend (isal is known to + # do this, maybe others too?) will return max_length bytes, + # but internally buffer more data such that the payload is + # >max_length, so we return one extra byte and if we're able + # to do that, then the message is too big. + payload_merged = self._decompressobj.decompress_sync( + assembled_payload + WS_DEFLATE_TRAILING, + ( + self._max_msg_size + 1 + if self._max_msg_size + else self._max_msg_size + ), + ) + if self._max_msg_size and len(payload_merged) > self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + f"Decompressed message exceeds size limit {self._max_msg_size}", + ) + elif type(assembled_payload) is bytes: + payload_merged = assembled_payload + else: + payload_merged = bytes(assembled_payload) + + if opcode == OP_CODE_TEXT: + try: + text = payload_merged.decode("utf-8") + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + + # XXX: The Text and Binary messages here can be a performance + # bottleneck, so we use tuple.__new__ to improve performance. + # This is not type safe, but many tests should fail in + # test_client_ws_functional.py if this is wrong. + self.queue.feed_data( + TUPLE_NEW(WSMessage, (WS_MSG_TYPE_TEXT, text, "")), + len(payload_merged), + ) + else: + self.queue.feed_data( + TUPLE_NEW(WSMessage, (WS_MSG_TYPE_BINARY, payload_merged, "")), + len(payload_merged), + ) + elif opcode == OP_CODE_CLOSE: + if len(payload) >= 2: + close_code = UNPACK_CLOSE_CODE(payload[:2])[0] + if close_code < 3000 and close_code not in ALLOWED_CLOSE_CODES: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close code: {close_code}", + ) + try: + close_message = payload[2:].decode("utf-8") + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + msg = TUPLE_NEW(WSMessage, (WSMsgType.CLOSE, close_code, close_message)) + elif payload: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close frame: {fin} {opcode} {payload!r}", + ) + else: + msg = TUPLE_NEW(WSMessage, (WSMsgType.CLOSE, 0, "")) + + self.queue.feed_data(msg, 0) + elif opcode == OP_CODE_PING: + msg = TUPLE_NEW(WSMessage, (WSMsgType.PING, payload, "")) + self.queue.feed_data(msg, len(payload)) + elif opcode == OP_CODE_PONG: + msg = TUPLE_NEW(WSMessage, (WSMsgType.PONG, payload, "")) + self.queue.feed_data(msg, len(payload)) + else: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, f"Unexpected opcode={opcode!r}" + ) + + def _feed_data(self, data: bytes) -> None: + """Return the next frame from the socket.""" + if self._tail: + data, self._tail = self._tail + data, b"" + + start_pos: int = 0 + data_len = len(data) + data_cstr = data + + while True: + # read header + if self._state == READ_HEADER: + if data_len - start_pos < 2: + break + first_byte = data_cstr[start_pos] + second_byte = data_cstr[start_pos + 1] + start_pos += 2 + + fin = (first_byte >> 7) & 1 + rsv1 = (first_byte >> 6) & 1 + rsv2 = (first_byte >> 5) & 1 + rsv3 = (first_byte >> 4) & 1 + opcode = first_byte & 0xF + + # frame-fin = %x0 ; more frames of this message follow + # / %x1 ; final frame of this message + # frame-rsv1 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv2 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv3 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # + # Remove rsv1 from this test for deflate development + if rsv2 or rsv3 or (rsv1 and not self._compress): + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + if opcode > 0x7 and fin == 0: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received fragmented control frame", + ) + + has_mask = (second_byte >> 7) & 1 + length = second_byte & 0x7F + + # Control frames MUST have a payload + # length of 125 bytes or less + if opcode > 0x7 and length > 125: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Control frame payload cannot be larger than 125 bytes", + ) + + # Set compress status if last package is FIN + # OR set compress status if this is first fragment + # Raise error if not first fragment with rsv1 = 0x1 + if self._frame_fin or self._compressed == COMPRESSED_NOT_SET: + self._compressed = COMPRESSED_TRUE if rsv1 else COMPRESSED_FALSE + elif rsv1: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + self._frame_fin = bool(fin) + self._frame_opcode = opcode + self._has_mask = bool(has_mask) + self._payload_len_flag = length + self._state = READ_PAYLOAD_LENGTH + + # read payload length + if self._state == READ_PAYLOAD_LENGTH: + len_flag = self._payload_len_flag + if len_flag == 126: + if data_len - start_pos < 2: + break + first_byte = data_cstr[start_pos] + second_byte = data_cstr[start_pos + 1] + start_pos += 2 + self._payload_bytes_to_read = first_byte << 8 | second_byte + elif len_flag > 126: + if data_len - start_pos < 8: + break + self._payload_bytes_to_read = UNPACK_LEN3(data, start_pos)[0] + start_pos += 8 + else: + self._payload_bytes_to_read = len_flag + + self._state = READ_PAYLOAD_MASK if self._has_mask else READ_PAYLOAD + + # read payload mask + if self._state == READ_PAYLOAD_MASK: + if data_len - start_pos < 4: + break + self._frame_mask = data_cstr[start_pos : start_pos + 4] + start_pos += 4 + self._state = READ_PAYLOAD + + if self._state == READ_PAYLOAD: + chunk_len = data_len - start_pos + if self._payload_bytes_to_read >= chunk_len: + f_end_pos = data_len + self._payload_bytes_to_read -= chunk_len + else: + f_end_pos = start_pos + self._payload_bytes_to_read + self._payload_bytes_to_read = 0 + + had_fragments = self._frame_payload_len + self._frame_payload_len += f_end_pos - start_pos + f_start_pos = start_pos + start_pos = f_end_pos + + if self._payload_bytes_to_read != 0: + # If we don't have a complete frame, we need to save the + # data for the next call to feed_data. + self._payload_fragments.append(data_cstr[f_start_pos:f_end_pos]) + break + + payload: Union[bytes, bytearray] + if had_fragments: + # We have to join the payload fragments get the payload + self._payload_fragments.append(data_cstr[f_start_pos:f_end_pos]) + if self._has_mask: + assert self._frame_mask is not None + payload_bytearray = bytearray(b"".join(self._payload_fragments)) + websocket_mask(self._frame_mask, payload_bytearray) + payload = payload_bytearray + else: + payload = b"".join(self._payload_fragments) + self._payload_fragments.clear() + elif self._has_mask: + assert self._frame_mask is not None + payload_bytearray = data_cstr[f_start_pos:f_end_pos] # type: ignore[assignment] + if type(payload_bytearray) is not bytearray: # pragma: no branch + # Cython will do the conversion for us + # but we need to do it for Python and we + # will always get here in Python + payload_bytearray = bytearray(payload_bytearray) + websocket_mask(self._frame_mask, payload_bytearray) + payload = payload_bytearray + else: + payload = data_cstr[f_start_pos:f_end_pos] + + self._handle_frame( + self._frame_fin, self._frame_opcode, payload, self._compressed + ) + self._frame_payload_len = 0 + self._state = READ_HEADER + + # XXX: Cython needs slices to be bounded, so we can't omit the slice end here. + self._tail = data_cstr[start_pos:data_len] if start_pos < data_len else b"" diff --git a/venv/lib/python3.12/site-packages/aiohttp/_websocket/writer.py b/venv/lib/python3.12/site-packages/aiohttp/_websocket/writer.py new file mode 100644 index 00000000..96042023 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/_websocket/writer.py @@ -0,0 +1,262 @@ +"""WebSocket protocol versions 13 and 8.""" + +import asyncio +import random +import sys +from functools import partial +from typing import Final, Optional, Set, Union + +from ..base_protocol import BaseProtocol +from ..client_exceptions import ClientConnectionResetError +from ..compression_utils import ZLibBackend, ZLibCompressor +from .helpers import ( + MASK_LEN, + MSG_SIZE, + PACK_CLOSE_CODE, + PACK_LEN1, + PACK_LEN2, + PACK_LEN3, + PACK_RANDBITS, + websocket_mask, +) +from .models import WS_DEFLATE_TRAILING, WSMsgType + +DEFAULT_LIMIT: Final[int] = 2**16 + +# WebSocket opcode boundary: opcodes 0-7 are data frames, 8-15 are control frames +# Control frames (ping, pong, close) are never compressed +WS_CONTROL_FRAME_OPCODE: Final[int] = 8 + +# For websockets, keeping latency low is extremely important as implementations +# generally expect to be able to send and receive messages quickly. We use a +# larger chunk size to reduce the number of executor calls and avoid task +# creation overhead, since both are significant sources of latency when chunks +# are small. A size of 16KiB was chosen as a balance between avoiding task +# overhead and not blocking the event loop too long with synchronous compression. + +WEBSOCKET_MAX_SYNC_CHUNK_SIZE = 16 * 1024 + + +class WebSocketWriter: + """WebSocket writer. + + The writer is responsible for sending messages to the client. It is + created by the protocol when a connection is established. The writer + should avoid implementing any application logic and should only be + concerned with the low-level details of the WebSocket protocol. + """ + + def __init__( + self, + protocol: BaseProtocol, + transport: asyncio.Transport, + *, + use_mask: bool = False, + limit: int = DEFAULT_LIMIT, + random: random.Random = random.Random(), + compress: int = 0, + notakeover: bool = False, + ) -> None: + """Initialize a WebSocket writer.""" + self.protocol = protocol + self.transport = transport + self.use_mask = use_mask + self.get_random_bits = partial(random.getrandbits, 32) + self.compress = compress + self.notakeover = notakeover + self._closing = False + self._limit = limit + self._output_size = 0 + self._compressobj: Optional[ZLibCompressor] = None + self._send_lock = asyncio.Lock() + self._background_tasks: Set[asyncio.Task[None]] = set() + + async def send_frame( + self, message: bytes, opcode: int, compress: Optional[int] = None + ) -> None: + """Send a frame over the websocket with message as its payload.""" + if self._closing and not (opcode & WSMsgType.CLOSE): + raise ClientConnectionResetError("Cannot write to closing transport") + + if not (compress or self.compress) or opcode >= WS_CONTROL_FRAME_OPCODE: + # Non-compressed frames don't need lock or shield + self._write_websocket_frame(message, opcode, 0) + elif len(message) <= WEBSOCKET_MAX_SYNC_CHUNK_SIZE: + # Small compressed payloads - compress synchronously in event loop + # We need the lock even though sync compression has no await points. + # This prevents small frames from interleaving with large frames that + # compress in the executor, avoiding compressor state corruption. + async with self._send_lock: + self._send_compressed_frame_sync(message, opcode, compress) + else: + # Large compressed frames need shield to prevent corruption + # For large compressed frames, the entire compress+send + # operation must be atomic. If cancelled after compression but + # before send, the compressor state would be advanced but data + # not sent, corrupting subsequent frames. + # Create a task to shield from cancellation + # The lock is acquired inside the shielded task so the entire + # operation (lock + compress + send) completes atomically. + # Use eager_start on Python 3.12+ to avoid scheduling overhead + loop = asyncio.get_running_loop() + coro = self._send_compressed_frame_async_locked(message, opcode, compress) + if sys.version_info >= (3, 12): + send_task = asyncio.Task(coro, loop=loop, eager_start=True) + else: + send_task = loop.create_task(coro) + # Keep a strong reference to prevent garbage collection + self._background_tasks.add(send_task) + send_task.add_done_callback(self._background_tasks.discard) + await asyncio.shield(send_task) + + # It is safe to return control to the event loop when using compression + # after this point as we have already sent or buffered all the data. + # Once we have written output_size up to the limit, we call the + # drain helper which waits for the transport to be ready to accept + # more data. This is a flow control mechanism to prevent the buffer + # from growing too large. The drain helper will return right away + # if the writer is not paused. + if self._output_size > self._limit: + self._output_size = 0 + if self.protocol._paused: + await self.protocol._drain_helper() + + def _write_websocket_frame(self, message: bytes, opcode: int, rsv: int) -> None: + """ + Write a websocket frame to the transport. + + This method handles frame header construction, masking, and writing to transport. + It does not handle compression or flow control - those are the responsibility + of the caller. + """ + msg_length = len(message) + + use_mask = self.use_mask + mask_bit = 0x80 if use_mask else 0 + + # Depending on the message length, the header is assembled differently. + # The first byte is reserved for the opcode and the RSV bits. + first_byte = 0x80 | rsv | opcode + if msg_length < 126: + header = PACK_LEN1(first_byte, msg_length | mask_bit) + header_len = 2 + elif msg_length < 65536: + header = PACK_LEN2(first_byte, 126 | mask_bit, msg_length) + header_len = 4 + else: + header = PACK_LEN3(first_byte, 127 | mask_bit, msg_length) + header_len = 10 + + if self.transport.is_closing(): + raise ClientConnectionResetError("Cannot write to closing transport") + + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.3 + # If we are using a mask, we need to generate it randomly + # and apply it to the message before sending it. A mask is + # a 32-bit value that is applied to the message using a + # bitwise XOR operation. It is used to prevent certain types + # of attacks on the websocket protocol. The mask is only used + # when aiohttp is acting as a client. Servers do not use a mask. + if use_mask: + mask = PACK_RANDBITS(self.get_random_bits()) + message = bytearray(message) + websocket_mask(mask, message) + self.transport.write(header + mask + message) + self._output_size += MASK_LEN + elif msg_length > MSG_SIZE: + self.transport.write(header) + self.transport.write(message) + else: + self.transport.write(header + message) + + self._output_size += header_len + msg_length + + def _get_compressor(self, compress: Optional[int]) -> ZLibCompressor: + """Get or create a compressor object for the given compression level.""" + if compress: + # Do not set self._compress if compressing is for this frame + return ZLibCompressor( + level=ZLibBackend.Z_BEST_SPEED, + wbits=-compress, + max_sync_chunk_size=WEBSOCKET_MAX_SYNC_CHUNK_SIZE, + ) + if not self._compressobj: + self._compressobj = ZLibCompressor( + level=ZLibBackend.Z_BEST_SPEED, + wbits=-self.compress, + max_sync_chunk_size=WEBSOCKET_MAX_SYNC_CHUNK_SIZE, + ) + return self._compressobj + + def _send_compressed_frame_sync( + self, message: bytes, opcode: int, compress: Optional[int] + ) -> None: + """ + Synchronous send for small compressed frames. + + This is used for small compressed payloads that compress synchronously in the event loop. + Since there are no await points, this is inherently cancellation-safe. + """ + # RSV are the reserved bits in the frame header. They are used to + # indicate that the frame is using an extension. + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 + compressobj = self._get_compressor(compress) + # (0x40) RSV1 is set for compressed frames + # https://datatracker.ietf.org/doc/html/rfc7692#section-7.2.3.1 + self._write_websocket_frame( + ( + compressobj.compress_sync(message) + + compressobj.flush( + ZLibBackend.Z_FULL_FLUSH + if self.notakeover + else ZLibBackend.Z_SYNC_FLUSH + ) + ).removesuffix(WS_DEFLATE_TRAILING), + opcode, + 0x40, + ) + + async def _send_compressed_frame_async_locked( + self, message: bytes, opcode: int, compress: Optional[int] + ) -> None: + """ + Async send for large compressed frames with lock. + + Acquires the lock and compresses large payloads asynchronously in + the executor. The lock is held for the entire operation to ensure + the compressor state is not corrupted by concurrent sends. + + MUST be run shielded from cancellation. If cancelled after + compression but before sending, the compressor state would be + advanced but data not sent, corrupting subsequent frames. + """ + async with self._send_lock: + # RSV are the reserved bits in the frame header. They are used to + # indicate that the frame is using an extension. + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 + compressobj = self._get_compressor(compress) + # (0x40) RSV1 is set for compressed frames + # https://datatracker.ietf.org/doc/html/rfc7692#section-7.2.3.1 + self._write_websocket_frame( + ( + await compressobj.compress(message) + + compressobj.flush( + ZLibBackend.Z_FULL_FLUSH + if self.notakeover + else ZLibBackend.Z_SYNC_FLUSH + ) + ).removesuffix(WS_DEFLATE_TRAILING), + opcode, + 0x40, + ) + + async def close(self, code: int = 1000, message: Union[bytes, str] = b"") -> None: + """Close the websocket, sending the specified code and message.""" + if isinstance(message, str): + message = message.encode("utf-8") + try: + await self.send_frame( + PACK_CLOSE_CODE(code) + message, opcode=WSMsgType.CLOSE + ) + finally: + self._closing = True diff --git a/venv/lib/python3.12/site-packages/aiohttp/abc.py b/venv/lib/python3.12/site-packages/aiohttp/abc.py new file mode 100644 index 00000000..faf09575 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/abc.py @@ -0,0 +1,268 @@ +import asyncio +import logging +import socket +from abc import ABC, abstractmethod +from collections.abc import Sized +from http.cookies import BaseCookie, Morsel +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Dict, + Generator, + Iterable, + List, + Optional, + Sequence, + Tuple, + TypedDict, + Union, +) + +from multidict import CIMultiDict +from yarl import URL + +from ._cookie_helpers import parse_set_cookie_headers +from .typedefs import LooseCookies + +if TYPE_CHECKING: + from .web_app import Application + from .web_exceptions import HTTPException + from .web_request import BaseRequest, Request + from .web_response import StreamResponse +else: + BaseRequest = Request = Application = StreamResponse = None + HTTPException = None + + +class AbstractRouter(ABC): + def __init__(self) -> None: + self._frozen = False + + def post_init(self, app: Application) -> None: + """Post init stage. + + Not an abstract method for sake of backward compatibility, + but if the router wants to be aware of the application + it can override this. + """ + + @property + def frozen(self) -> bool: + return self._frozen + + def freeze(self) -> None: + """Freeze router.""" + self._frozen = True + + @abstractmethod + async def resolve(self, request: Request) -> "AbstractMatchInfo": + """Return MATCH_INFO for given request""" + + +class AbstractMatchInfo(ABC): + + __slots__ = () + + @property # pragma: no branch + @abstractmethod + def handler(self) -> Callable[[Request], Awaitable[StreamResponse]]: + """Execute matched request handler""" + + @property + @abstractmethod + def expect_handler( + self, + ) -> Callable[[Request], Awaitable[Optional[StreamResponse]]]: + """Expect handler for 100-continue processing""" + + @property # pragma: no branch + @abstractmethod + def http_exception(self) -> Optional[HTTPException]: + """HTTPException instance raised on router's resolving, or None""" + + @abstractmethod # pragma: no branch + def get_info(self) -> Dict[str, Any]: + """Return a dict with additional info useful for introspection""" + + @property # pragma: no branch + @abstractmethod + def apps(self) -> Tuple[Application, ...]: + """Stack of nested applications. + + Top level application is left-most element. + + """ + + @abstractmethod + def add_app(self, app: Application) -> None: + """Add application to the nested apps stack.""" + + @abstractmethod + def freeze(self) -> None: + """Freeze the match info. + + The method is called after route resolution. + + After the call .add_app() is forbidden. + + """ + + +class AbstractView(ABC): + """Abstract class based view.""" + + def __init__(self, request: Request) -> None: + self._request = request + + @property + def request(self) -> Request: + """Request instance.""" + return self._request + + @abstractmethod + def __await__(self) -> Generator[None, None, StreamResponse]: + """Execute the view handler.""" + + +class ResolveResult(TypedDict): + """Resolve result. + + This is the result returned from an AbstractResolver's + resolve method. + + :param hostname: The hostname that was provided. + :param host: The IP address that was resolved. + :param port: The port that was resolved. + :param family: The address family that was resolved. + :param proto: The protocol that was resolved. + :param flags: The flags that were resolved. + """ + + hostname: str + host: str + port: int + family: int + proto: int + flags: int + + +class AbstractResolver(ABC): + """Abstract DNS resolver.""" + + @abstractmethod + async def resolve( + self, host: str, port: int = 0, family: socket.AddressFamily = socket.AF_INET + ) -> List[ResolveResult]: + """Return IP address for given hostname""" + + @abstractmethod + async def close(self) -> None: + """Release resolver""" + + +if TYPE_CHECKING: + IterableBase = Iterable[Morsel[str]] +else: + IterableBase = Iterable + + +ClearCookiePredicate = Callable[["Morsel[str]"], bool] + + +class AbstractCookieJar(Sized, IterableBase): + """Abstract Cookie Jar.""" + + def __init__(self, *, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + self._loop = loop or asyncio.get_running_loop() + + @property + @abstractmethod + def quote_cookie(self) -> bool: + """Return True if cookies should be quoted.""" + + @abstractmethod + def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: + """Clear all cookies if no predicate is passed.""" + + @abstractmethod + def clear_domain(self, domain: str) -> None: + """Clear all cookies for domain and all subdomains.""" + + @abstractmethod + def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: + """Update cookies.""" + + def update_cookies_from_headers( + self, headers: Sequence[str], response_url: URL + ) -> None: + """Update cookies from raw Set-Cookie headers.""" + if headers and (cookies_to_update := parse_set_cookie_headers(headers)): + self.update_cookies(cookies_to_update, response_url) + + @abstractmethod + def filter_cookies(self, request_url: URL) -> "BaseCookie[str]": + """Return the jar's cookies filtered by their attributes.""" + + +class AbstractStreamWriter(ABC): + """Abstract stream writer.""" + + buffer_size: int = 0 + output_size: int = 0 + length: Optional[int] = 0 + + @abstractmethod + async def write(self, chunk: Union[bytes, bytearray, memoryview]) -> None: + """Write chunk into stream.""" + + @abstractmethod + async def write_eof(self, chunk: bytes = b"") -> None: + """Write last chunk.""" + + @abstractmethod + async def drain(self) -> None: + """Flush the write buffer.""" + + @abstractmethod + def enable_compression( + self, encoding: str = "deflate", strategy: Optional[int] = None + ) -> None: + """Enable HTTP body compression""" + + @abstractmethod + def enable_chunking(self) -> None: + """Enable HTTP chunked mode""" + + @abstractmethod + async def write_headers( + self, status_line: str, headers: "CIMultiDict[str]" + ) -> None: + """Write HTTP headers""" + + def send_headers(self) -> None: + """Force sending buffered headers if not already sent. + + Required only if write_headers() buffers headers instead of sending immediately. + For backwards compatibility, this method does nothing by default. + """ + + +class AbstractAccessLogger(ABC): + """Abstract writer to access log.""" + + __slots__ = ("logger", "log_format") + + def __init__(self, logger: logging.Logger, log_format: str) -> None: + self.logger = logger + self.log_format = log_format + + @abstractmethod + def log(self, request: BaseRequest, response: StreamResponse, time: float) -> None: + """Emit log to logger.""" + + @property + def enabled(self) -> bool: + """Check if logger is enabled.""" + return True diff --git a/venv/lib/python3.12/site-packages/aiohttp/base_protocol.py b/venv/lib/python3.12/site-packages/aiohttp/base_protocol.py new file mode 100644 index 00000000..b0a67ed6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/base_protocol.py @@ -0,0 +1,100 @@ +import asyncio +from typing import Optional, cast + +from .client_exceptions import ClientConnectionResetError +from .helpers import set_exception +from .tcp_helpers import tcp_nodelay + + +class BaseProtocol(asyncio.Protocol): + __slots__ = ( + "_loop", + "_paused", + "_drain_waiter", + "_connection_lost", + "_reading_paused", + "transport", + ) + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop: asyncio.AbstractEventLoop = loop + self._paused = False + self._drain_waiter: Optional[asyncio.Future[None]] = None + self._reading_paused = False + + self.transport: Optional[asyncio.Transport] = None + + @property + def connected(self) -> bool: + """Return True if the connection is open.""" + return self.transport is not None + + @property + def writing_paused(self) -> bool: + return self._paused + + def pause_writing(self) -> None: + assert not self._paused + self._paused = True + + def resume_writing(self) -> None: + assert self._paused + self._paused = False + + waiter = self._drain_waiter + if waiter is not None: + self._drain_waiter = None + if not waiter.done(): + waiter.set_result(None) + + def pause_reading(self) -> None: + if not self._reading_paused and self.transport is not None: + try: + self.transport.pause_reading() + except (AttributeError, NotImplementedError, RuntimeError): + pass + self._reading_paused = True + + def resume_reading(self) -> None: + if self._reading_paused and self.transport is not None: + try: + self.transport.resume_reading() + except (AttributeError, NotImplementedError, RuntimeError): + pass + self._reading_paused = False + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + tr = cast(asyncio.Transport, transport) + tcp_nodelay(tr, True) + self.transport = tr + + def connection_lost(self, exc: Optional[BaseException]) -> None: + # Wake up the writer if currently paused. + self.transport = None + if not self._paused: + return + waiter = self._drain_waiter + if waiter is None: + return + self._drain_waiter = None + if waiter.done(): + return + if exc is None: + waiter.set_result(None) + else: + set_exception( + waiter, + ConnectionError("Connection lost"), + exc, + ) + + async def _drain_helper(self) -> None: + if self.transport is None: + raise ClientConnectionResetError("Connection lost") + if not self._paused: + return + waiter = self._drain_waiter + if waiter is None: + waiter = self._loop.create_future() + self._drain_waiter = waiter + await asyncio.shield(waiter) diff --git a/venv/lib/python3.12/site-packages/aiohttp/client.py b/venv/lib/python3.12/site-packages/aiohttp/client.py new file mode 100644 index 00000000..5d10d3e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/client.py @@ -0,0 +1,1646 @@ +"""HTTP Client for asyncio.""" + +import asyncio +import base64 +import hashlib +import json +import os +import sys +import traceback +import warnings +from contextlib import suppress +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Coroutine, + Final, + FrozenSet, + Generator, + Generic, + Iterable, + List, + Mapping, + Optional, + Sequence, + Set, + Tuple, + Type, + TypedDict, + TypeVar, + Union, +) + +import attr +from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr +from yarl import URL + +from . import hdrs, http, payload +from ._websocket.reader import WebSocketDataQueue +from .abc import AbstractCookieJar +from .client_exceptions import ( + ClientConnectionError, + ClientConnectionResetError, + ClientConnectorCertificateError, + ClientConnectorDNSError, + ClientConnectorError, + ClientConnectorSSLError, + ClientError, + ClientHttpProxyError, + ClientOSError, + ClientPayloadError, + ClientProxyConnectionError, + ClientResponseError, + ClientSSLError, + ConnectionTimeoutError, + ContentTypeError, + InvalidURL, + InvalidUrlClientError, + InvalidUrlRedirectClientError, + NonHttpUrlClientError, + NonHttpUrlRedirectClientError, + RedirectClientError, + ServerConnectionError, + ServerDisconnectedError, + ServerFingerprintMismatch, + ServerTimeoutError, + SocketTimeoutError, + TooManyRedirects, + WSMessageTypeError, + WSServerHandshakeError, +) +from .client_middlewares import ClientMiddlewareType, build_client_middlewares +from .client_reqrep import ( + ClientRequest as ClientRequest, + ClientResponse as ClientResponse, + Fingerprint as Fingerprint, + RequestInfo as RequestInfo, + _merge_ssl_params, +) +from .client_ws import ( + DEFAULT_WS_CLIENT_TIMEOUT, + ClientWebSocketResponse as ClientWebSocketResponse, + ClientWSTimeout as ClientWSTimeout, +) +from .connector import ( + HTTP_AND_EMPTY_SCHEMA_SET, + BaseConnector as BaseConnector, + NamedPipeConnector as NamedPipeConnector, + TCPConnector as TCPConnector, + UnixConnector as UnixConnector, +) +from .cookiejar import CookieJar +from .helpers import ( + _SENTINEL, + DEBUG, + EMPTY_BODY_METHODS, + BasicAuth, + TimeoutHandle, + basicauth_from_netrc, + get_env_proxy_for_url, + netrc_from_env, + sentinel, + strip_auth_from_url, +) +from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter +from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse +from .tracing import Trace, TraceConfig +from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, Query, StrOrURL + +__all__ = ( + # client_exceptions + "ClientConnectionError", + "ClientConnectionResetError", + "ClientConnectorCertificateError", + "ClientConnectorDNSError", + "ClientConnectorError", + "ClientConnectorSSLError", + "ClientError", + "ClientHttpProxyError", + "ClientOSError", + "ClientPayloadError", + "ClientProxyConnectionError", + "ClientResponseError", + "ClientSSLError", + "ConnectionTimeoutError", + "ContentTypeError", + "InvalidURL", + "InvalidUrlClientError", + "RedirectClientError", + "NonHttpUrlClientError", + "InvalidUrlRedirectClientError", + "NonHttpUrlRedirectClientError", + "ServerConnectionError", + "ServerDisconnectedError", + "ServerFingerprintMismatch", + "ServerTimeoutError", + "SocketTimeoutError", + "TooManyRedirects", + "WSServerHandshakeError", + # client_reqrep + "ClientRequest", + "ClientResponse", + "Fingerprint", + "RequestInfo", + # connector + "BaseConnector", + "TCPConnector", + "UnixConnector", + "NamedPipeConnector", + # client_ws + "ClientWebSocketResponse", + # client + "ClientSession", + "ClientTimeout", + "ClientWSTimeout", + "request", + "WSMessageTypeError", +) + + +if TYPE_CHECKING: + from ssl import SSLContext +else: + SSLContext = None + +if sys.version_info >= (3, 11) and TYPE_CHECKING: + from typing import Unpack + + +class _RequestOptions(TypedDict, total=False): + params: Query + data: Any + json: Any + cookies: Union[LooseCookies, None] + headers: Union[LooseHeaders, None] + skip_auto_headers: Union[Iterable[str], None] + auth: Union[BasicAuth, None] + allow_redirects: bool + max_redirects: int + compress: Union[str, bool, None] + chunked: Union[bool, None] + expect100: bool + raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]] + read_until_eof: bool + proxy: Union[StrOrURL, None] + proxy_auth: Union[BasicAuth, None] + timeout: "Union[ClientTimeout, _SENTINEL, None]" + ssl: Union[SSLContext, bool, Fingerprint] + server_hostname: Union[str, None] + proxy_headers: Union[LooseHeaders, None] + trace_request_ctx: Union[Mapping[str, Any], None] + read_bufsize: Union[int, None] + auto_decompress: Union[bool, None] + max_line_size: Union[int, None] + max_field_size: Union[int, None] + max_headers: Union[int, None] + middlewares: Optional[Sequence[ClientMiddlewareType]] + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ClientTimeout: + total: Optional[float] = None + connect: Optional[float] = None + sock_read: Optional[float] = None + sock_connect: Optional[float] = None + ceil_threshold: float = 5 + + # pool_queue_timeout: Optional[float] = None + # dns_resolution_timeout: Optional[float] = None + # socket_connect_timeout: Optional[float] = None + # connection_acquiring_timeout: Optional[float] = None + # new_connection_timeout: Optional[float] = None + # http_header_timeout: Optional[float] = None + # response_body_timeout: Optional[float] = None + + # to create a timeout specific for a single request, either + # - create a completely new one to overwrite the default + # - or use http://www.attrs.org/en/stable/api.html#attr.evolve + # to overwrite the defaults + + +# 5 Minute default read timeout +DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60, sock_connect=30) + +# https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2 +IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"}) + +_RetType = TypeVar("_RetType", ClientResponse, ClientWebSocketResponse) +_CharsetResolver = Callable[[ClientResponse, bytes], str] + + +class ClientSession: + """First-class interface for making HTTP requests.""" + + ATTRS = frozenset( + [ + "_base_url", + "_base_url_origin", + "_source_traceback", + "_connector", + "_loop", + "_cookie_jar", + "_connector_owner", + "_default_auth", + "_version", + "_json_serialize", + "_requote_redirect_url", + "_timeout", + "_raise_for_status", + "_auto_decompress", + "_trust_env", + "_default_headers", + "_skip_auto_headers", + "_request_class", + "_response_class", + "_ws_response_class", + "_trace_configs", + "_read_bufsize", + "_max_line_size", + "_max_field_size", + "_max_headers", + "_resolve_charset", + "_default_proxy", + "_default_proxy_auth", + "_retry_connection", + "_middlewares", + "requote_redirect_url", + ] + ) + + _source_traceback: Optional[traceback.StackSummary] = None + _connector: Optional[BaseConnector] = None + + def __init__( + self, + base_url: Optional[StrOrURL] = None, + *, + connector: Optional[BaseConnector] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + cookies: Optional[LooseCookies] = None, + headers: Optional[LooseHeaders] = None, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + skip_auto_headers: Optional[Iterable[str]] = None, + auth: Optional[BasicAuth] = None, + json_serialize: JSONEncoder = json.dumps, + request_class: Type[ClientRequest] = ClientRequest, + response_class: Type[ClientResponse] = ClientResponse, + ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse, + version: HttpVersion = http.HttpVersion11, + cookie_jar: Optional[AbstractCookieJar] = None, + connector_owner: bool = True, + raise_for_status: Union[ + bool, Callable[[ClientResponse], Awaitable[None]] + ] = False, + read_timeout: Union[float, _SENTINEL] = sentinel, + conn_timeout: Optional[float] = None, + timeout: Union[object, ClientTimeout] = sentinel, + auto_decompress: bool = True, + trust_env: bool = False, + requote_redirect_url: bool = True, + trace_configs: Optional[List[TraceConfig]] = None, + read_bufsize: int = 2**16, + max_line_size: int = 8190, + max_field_size: int = 8190, + max_headers: int = 128, + fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8", + middlewares: Sequence[ClientMiddlewareType] = (), + ssl_shutdown_timeout: Union[_SENTINEL, None, float] = sentinel, + ) -> None: + # We initialise _connector to None immediately, as it's referenced in __del__() + # and could cause issues if an exception occurs during initialisation. + self._connector: Optional[BaseConnector] = None + + if loop is None: + if connector is not None: + loop = connector._loop + + loop = loop or asyncio.get_running_loop() + + if base_url is None or isinstance(base_url, URL): + self._base_url: Optional[URL] = base_url + self._base_url_origin = None if base_url is None else base_url.origin() + else: + self._base_url = URL(base_url) + self._base_url_origin = self._base_url.origin() + assert self._base_url.absolute, "Only absolute URLs are supported" + if self._base_url is not None and not self._base_url.path.endswith("/"): + raise ValueError("base_url must have a trailing '/'") + + if timeout is sentinel or timeout is None: + self._timeout = DEFAULT_TIMEOUT + if read_timeout is not sentinel: + warnings.warn( + "read_timeout is deprecated, use timeout argument instead", + DeprecationWarning, + stacklevel=2, + ) + self._timeout = attr.evolve(self._timeout, total=read_timeout) + if conn_timeout is not None: + self._timeout = attr.evolve(self._timeout, connect=conn_timeout) + warnings.warn( + "conn_timeout is deprecated, use timeout argument instead", + DeprecationWarning, + stacklevel=2, + ) + else: + if not isinstance(timeout, ClientTimeout): + raise ValueError( + f"timeout parameter cannot be of {type(timeout)} type, " + "please use 'timeout=ClientTimeout(...)'", + ) + self._timeout = timeout + if read_timeout is not sentinel: + raise ValueError( + "read_timeout and timeout parameters " + "conflict, please setup " + "timeout.read" + ) + if conn_timeout is not None: + raise ValueError( + "conn_timeout and timeout parameters " + "conflict, please setup " + "timeout.connect" + ) + + if ssl_shutdown_timeout is not sentinel: + warnings.warn( + "The ssl_shutdown_timeout parameter is deprecated and will be removed in aiohttp 4.0", + DeprecationWarning, + stacklevel=2, + ) + + if connector is None: + connector = TCPConnector( + loop=loop, ssl_shutdown_timeout=ssl_shutdown_timeout + ) + + if connector._loop is not loop: + raise RuntimeError("Session and connector has to use same event loop") + + self._loop = loop + + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + if cookie_jar is None: + cookie_jar = CookieJar(loop=loop) + self._cookie_jar = cookie_jar + + if cookies: + self._cookie_jar.update_cookies(cookies) + + self._connector = connector + self._connector_owner = connector_owner + self._default_auth = auth + self._version = version + self._json_serialize = json_serialize + self._raise_for_status = raise_for_status + self._auto_decompress = auto_decompress + self._trust_env = trust_env + self._requote_redirect_url = requote_redirect_url + self._read_bufsize = read_bufsize + self._max_line_size = max_line_size + self._max_field_size = max_field_size + self._max_headers = max_headers + + # Convert to list of tuples + if headers: + real_headers: CIMultiDict[str] = CIMultiDict(headers) + else: + real_headers = CIMultiDict() + self._default_headers: CIMultiDict[str] = real_headers + if skip_auto_headers is not None: + self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers) + else: + self._skip_auto_headers = frozenset() + + self._request_class = request_class + self._response_class = response_class + self._ws_response_class = ws_response_class + + self._trace_configs = trace_configs or [] + for trace_config in self._trace_configs: + trace_config.freeze() + + self._resolve_charset = fallback_charset_resolver + + self._default_proxy = proxy + self._default_proxy_auth = proxy_auth + self._retry_connection: bool = True + self._middlewares = middlewares + + def __init_subclass__(cls: Type["ClientSession"]) -> None: + warnings.warn( + "Inheritance class {} from ClientSession " + "is discouraged".format(cls.__name__), + DeprecationWarning, + stacklevel=2, + ) + + if DEBUG: + + def __setattr__(self, name: str, val: Any) -> None: + if name not in self.ATTRS: + warnings.warn( + "Setting custom ClientSession.{} attribute " + "is discouraged".format(name), + DeprecationWarning, + stacklevel=2, + ) + super().__setattr__(name, val) + + def __del__(self, _warnings: Any = warnings) -> None: + if not self.closed: + kwargs = {"source": self} + _warnings.warn( + f"Unclosed client session {self!r}", ResourceWarning, **kwargs + ) + context = {"client_session": self, "message": "Unclosed client session"} + if self._source_traceback is not None: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + if sys.version_info >= (3, 11) and TYPE_CHECKING: + + def request( + self, + method: str, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + else: + + def request( + self, method: str, url: StrOrURL, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP request.""" + return _RequestContextManager(self._request(method, url, **kwargs)) + + def _build_url(self, str_or_url: StrOrURL) -> URL: + url = URL(str_or_url) + if self._base_url and not url.absolute: + return self._base_url.join(url) + return url + + async def _request( + self, + method: str, + str_or_url: StrOrURL, + *, + params: Query = None, + data: Any = None, + json: Any = None, + cookies: Optional[LooseCookies] = None, + headers: Optional[LooseHeaders] = None, + skip_auto_headers: Optional[Iterable[str]] = None, + auth: Optional[BasicAuth] = None, + allow_redirects: bool = True, + max_redirects: int = 10, + compress: Union[str, bool, None] = None, + chunked: Optional[bool] = None, + expect100: bool = False, + raise_for_status: Union[ + None, bool, Callable[[ClientResponse], Awaitable[None]] + ] = None, + read_until_eof: bool = True, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + timeout: Union[ClientTimeout, _SENTINEL] = sentinel, + verify_ssl: Optional[bool] = None, + fingerprint: Optional[bytes] = None, + ssl_context: Optional[SSLContext] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + server_hostname: Optional[str] = None, + proxy_headers: Optional[LooseHeaders] = None, + trace_request_ctx: Optional[Mapping[str, Any]] = None, + read_bufsize: Optional[int] = None, + auto_decompress: Optional[bool] = None, + max_line_size: Optional[int] = None, + max_field_size: Optional[int] = None, + max_headers: Optional[int] = None, + middlewares: Optional[Sequence[ClientMiddlewareType]] = None, + ) -> ClientResponse: + + # NOTE: timeout clamps existing connect and read timeouts. We cannot + # set the default to None because we need to detect if the user wants + # to use the existing timeouts by setting timeout to None. + + if self.closed: + raise RuntimeError("Session is closed") + + ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) + + if data is not None and json is not None: + raise ValueError( + "data and json parameters can not be used at the same time" + ) + elif json is not None: + data = payload.JsonPayload(json, dumps=self._json_serialize) + + if not isinstance(chunked, bool) and chunked is not None: + warnings.warn("Chunk size is deprecated #1615", DeprecationWarning) + + redirects = 0 + history: List[ClientResponse] = [] + version = self._version + params = params or {} + + # Merge with default headers and transform to CIMultiDict + headers = self._prepare_headers(headers) + + try: + url = self._build_url(str_or_url) + except ValueError as e: + raise InvalidUrlClientError(str_or_url) from e + + assert self._connector is not None + if url.scheme not in self._connector.allowed_protocol_schema_set: + raise NonHttpUrlClientError(url) + + skip_headers: Optional[Iterable[istr]] + if skip_auto_headers is not None: + skip_headers = { + istr(i) for i in skip_auto_headers + } | self._skip_auto_headers + elif self._skip_auto_headers: + skip_headers = self._skip_auto_headers + else: + skip_headers = None + + if proxy is None: + proxy = self._default_proxy + if proxy_auth is None: + proxy_auth = self._default_proxy_auth + + if proxy is None: + proxy_headers = None + else: + proxy_headers = self._prepare_headers(proxy_headers) + try: + proxy = URL(proxy) + except ValueError as e: + raise InvalidURL(proxy) from e + + if timeout is sentinel: + real_timeout: ClientTimeout = self._timeout + else: + if not isinstance(timeout, ClientTimeout): + real_timeout = ClientTimeout(total=timeout) + else: + real_timeout = timeout + # timeout is cumulative for all request operations + # (request, redirects, responses, data consuming) + tm = TimeoutHandle( + self._loop, real_timeout.total, ceil_threshold=real_timeout.ceil_threshold + ) + handle = tm.start() + + if read_bufsize is None: + read_bufsize = self._read_bufsize + + if auto_decompress is None: + auto_decompress = self._auto_decompress + + if max_line_size is None: + max_line_size = self._max_line_size + + if max_field_size is None: + max_field_size = self._max_field_size + + if max_headers is None: + max_headers = self._max_headers + + traces = [ + Trace( + self, + trace_config, + trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), + ) + for trace_config in self._trace_configs + ] + + for trace in traces: + await trace.send_request_start(method, url.update_query(params), headers) + + timer = tm.timer() + try: + with timer: + # https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests + retry_persistent_connection = ( + self._retry_connection and method in IDEMPOTENT_METHODS + ) + while True: + url, auth_from_url = strip_auth_from_url(url) + if not url.raw_host: + # NOTE: Bail early, otherwise, causes `InvalidURL` through + # NOTE: `self._request_class()` below. + err_exc_cls = ( + InvalidUrlRedirectClientError + if redirects + else InvalidUrlClientError + ) + raise err_exc_cls(url) + # If `auth` was passed for an already authenticated URL, + # disallow only if this is the initial URL; this is to avoid issues + # with sketchy redirects that are not the caller's responsibility + if not history and (auth and auth_from_url): + raise ValueError( + "Cannot combine AUTH argument with " + "credentials encoded in URL" + ) + + # Override the auth with the one from the URL only if we + # have no auth, or if we got an auth from a redirect URL + if auth is None or (history and auth_from_url is not None): + auth = auth_from_url + + if ( + auth is None + and self._default_auth + and ( + not self._base_url or self._base_url_origin == url.origin() + ) + ): + auth = self._default_auth + + # Try netrc if auth is still None and trust_env is enabled. + if auth is None and self._trust_env and url.host is not None: + auth = await self._loop.run_in_executor( + None, self._get_netrc_auth, url.host + ) + + # It would be confusing if we support explicit + # Authorization header with auth argument + if ( + headers is not None + and auth is not None + and hdrs.AUTHORIZATION in headers + ): + raise ValueError( + "Cannot combine AUTHORIZATION header " + "with AUTH argument or credentials " + "encoded in URL" + ) + + all_cookies = self._cookie_jar.filter_cookies(url) + + if cookies is not None: + tmp_cookie_jar = CookieJar( + quote_cookie=self._cookie_jar.quote_cookie + ) + tmp_cookie_jar.update_cookies(cookies) + req_cookies = tmp_cookie_jar.filter_cookies(url) + if req_cookies: + all_cookies.load(req_cookies) + + proxy_: Optional[URL] = None + if proxy is not None: + proxy_ = URL(proxy) + elif self._trust_env: + with suppress(LookupError): + proxy_, proxy_auth = await asyncio.to_thread( + get_env_proxy_for_url, url + ) + + req = self._request_class( + method, + url, + params=params, + headers=headers, + skip_auto_headers=skip_headers, + data=data, + cookies=all_cookies, + auth=auth, + version=version, + compress=compress, + chunked=chunked, + expect100=expect100, + loop=self._loop, + response_class=self._response_class, + proxy=proxy_, + proxy_auth=proxy_auth, + timer=timer, + session=self, + ssl=ssl if ssl is not None else True, + server_hostname=server_hostname, + proxy_headers=proxy_headers, + traces=traces, + trust_env=self.trust_env, + ) + + async def _connect_and_send_request( + req: ClientRequest, + ) -> ClientResponse: + # connection timeout + assert self._connector is not None + try: + conn = await self._connector.connect( + req, traces=traces, timeout=real_timeout + ) + except asyncio.TimeoutError as exc: + raise ConnectionTimeoutError( + f"Connection timeout to host {req.url}" + ) from exc + + assert conn.protocol is not None + conn.protocol.set_response_params( + timer=timer, + skip_payload=req.method in EMPTY_BODY_METHODS, + read_until_eof=read_until_eof, + auto_decompress=auto_decompress, + read_timeout=real_timeout.sock_read, + read_bufsize=read_bufsize, + timeout_ceil_threshold=self._connector._timeout_ceil_threshold, + max_line_size=max_line_size, + max_field_size=max_field_size, + max_headers=max_headers, + ) + try: + resp = await req.send(conn) + try: + await resp.start(conn) + except BaseException: + resp.close() + raise + except BaseException: + conn.close() + raise + return resp + + # Apply middleware (if any) - per-request middleware overrides session middleware + effective_middlewares = ( + self._middlewares if middlewares is None else middlewares + ) + + if effective_middlewares: + handler = build_client_middlewares( + _connect_and_send_request, effective_middlewares + ) + else: + handler = _connect_and_send_request + + try: + resp = await handler(req) + # Client connector errors should not be retried + except ( + ConnectionTimeoutError, + ClientConnectorError, + ClientConnectorCertificateError, + ClientConnectorSSLError, + ): + raise + except (ClientOSError, ServerDisconnectedError): + if retry_persistent_connection: + retry_persistent_connection = False + continue + raise + except ClientError: + raise + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise ClientOSError(*exc.args) from exc + + # Update cookies from raw headers to preserve duplicates + if resp._raw_cookie_headers: + self._cookie_jar.update_cookies_from_headers( + resp._raw_cookie_headers, resp.url + ) + + # redirects + if resp.status in (301, 302, 303, 307, 308) and allow_redirects: + + for trace in traces: + await trace.send_request_redirect( + method, url.update_query(params), headers, resp + ) + + redirects += 1 + history.append(resp) + if max_redirects and redirects >= max_redirects: + if req._body is not None: + await req._body.close() + resp.close() + raise TooManyRedirects( + history[0].request_info, tuple(history) + ) + + # For 301 and 302, mimic IE, now changed in RFC + # https://github.com/kennethreitz/requests/pull/269 + if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or ( + resp.status in (301, 302) and resp.method == hdrs.METH_POST + ): + method = hdrs.METH_GET + data = None + if headers.get(hdrs.CONTENT_LENGTH): + headers.pop(hdrs.CONTENT_LENGTH) + else: + # For 307/308, always preserve the request body + # For 301/302 with non-POST methods, preserve the request body + # https://www.rfc-editor.org/rfc/rfc9110#section-15.4.3-3.1 + # Use the existing payload to avoid recreating it from a potentially consumed file + data = req._body + + r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get( + hdrs.URI + ) + if r_url is None: + # see github.com/aio-libs/aiohttp/issues/2022 + break + else: + # reading from correct redirection + # response is forbidden + resp.release() + + try: + parsed_redirect_url = URL( + r_url, encoded=not self._requote_redirect_url + ) + except ValueError as e: + if req._body is not None: + await req._body.close() + resp.close() + raise InvalidUrlRedirectClientError( + r_url, + "Server attempted redirecting to a location that does not look like a URL", + ) from e + + scheme = parsed_redirect_url.scheme + if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: + if req._body is not None: + await req._body.close() + resp.close() + raise NonHttpUrlRedirectClientError(r_url) + elif not scheme: + parsed_redirect_url = url.join(parsed_redirect_url) + + try: + redirect_origin = parsed_redirect_url.origin() + except ValueError as origin_val_err: + if req._body is not None: + await req._body.close() + resp.close() + raise InvalidUrlRedirectClientError( + parsed_redirect_url, + "Invalid redirect URL origin", + ) from origin_val_err + + if url.origin() != redirect_origin: + auth = None + headers.pop(hdrs.AUTHORIZATION, None) + headers.pop(hdrs.COOKIE, None) + headers.pop(hdrs.PROXY_AUTHORIZATION, None) + + url = parsed_redirect_url + params = {} + resp.release() + continue + + break + + if req._body is not None: + await req._body.close() + # check response status + if raise_for_status is None: + raise_for_status = self._raise_for_status + + if raise_for_status is None: + pass + elif callable(raise_for_status): + await raise_for_status(resp) + elif raise_for_status: + resp.raise_for_status() + + # register connection + if handle is not None: + if resp.connection is not None: + resp.connection.add_callback(handle.cancel) + else: + handle.cancel() + + resp._history = tuple(history) + + for trace in traces: + await trace.send_request_end( + method, url.update_query(params), headers, resp + ) + return resp + + except BaseException as e: + # cleanup timer + tm.close() + if handle: + handle.cancel() + handle = None + + for trace in traces: + await trace.send_request_exception( + method, url.update_query(params), headers, e + ) + raise + + def ws_connect( + self, + url: StrOrURL, + *, + method: str = hdrs.METH_GET, + protocols: Iterable[str] = (), + timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, + receive_timeout: Optional[float] = None, + autoclose: bool = True, + autoping: bool = True, + heartbeat: Optional[float] = None, + auth: Optional[BasicAuth] = None, + origin: Optional[str] = None, + params: Query = None, + headers: Optional[LooseHeaders] = None, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + verify_ssl: Optional[bool] = None, + fingerprint: Optional[bytes] = None, + ssl_context: Optional[SSLContext] = None, + server_hostname: Optional[str] = None, + proxy_headers: Optional[LooseHeaders] = None, + compress: int = 0, + max_msg_size: int = 4 * 1024 * 1024, + ) -> "_WSRequestContextManager": + """Initiate websocket connection.""" + return _WSRequestContextManager( + self._ws_connect( + url, + method=method, + protocols=protocols, + timeout=timeout, + receive_timeout=receive_timeout, + autoclose=autoclose, + autoping=autoping, + heartbeat=heartbeat, + auth=auth, + origin=origin, + params=params, + headers=headers, + proxy=proxy, + proxy_auth=proxy_auth, + ssl=ssl, + verify_ssl=verify_ssl, + fingerprint=fingerprint, + ssl_context=ssl_context, + server_hostname=server_hostname, + proxy_headers=proxy_headers, + compress=compress, + max_msg_size=max_msg_size, + ) + ) + + async def _ws_connect( + self, + url: StrOrURL, + *, + method: str = hdrs.METH_GET, + protocols: Iterable[str] = (), + timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, + receive_timeout: Optional[float] = None, + autoclose: bool = True, + autoping: bool = True, + heartbeat: Optional[float] = None, + auth: Optional[BasicAuth] = None, + origin: Optional[str] = None, + params: Query = None, + headers: Optional[LooseHeaders] = None, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + verify_ssl: Optional[bool] = None, + fingerprint: Optional[bytes] = None, + ssl_context: Optional[SSLContext] = None, + server_hostname: Optional[str] = None, + proxy_headers: Optional[LooseHeaders] = None, + compress: int = 0, + max_msg_size: int = 4 * 1024 * 1024, + ) -> ClientWebSocketResponse: + if timeout is not sentinel: + if isinstance(timeout, ClientWSTimeout): + ws_timeout = timeout + else: + warnings.warn( + "parameter 'timeout' of type 'float' " + "is deprecated, please use " + "'timeout=ClientWSTimeout(ws_close=...)'", + DeprecationWarning, + stacklevel=2, + ) + ws_timeout = ClientWSTimeout(ws_close=timeout) + else: + ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT + if receive_timeout is not None: + warnings.warn( + "float parameter 'receive_timeout' " + "is deprecated, please use parameter " + "'timeout=ClientWSTimeout(ws_receive=...)'", + DeprecationWarning, + stacklevel=2, + ) + ws_timeout = attr.evolve(ws_timeout, ws_receive=receive_timeout) + + if headers is None: + real_headers: CIMultiDict[str] = CIMultiDict() + else: + real_headers = CIMultiDict(headers) + + default_headers = { + hdrs.UPGRADE: "websocket", + hdrs.CONNECTION: "Upgrade", + hdrs.SEC_WEBSOCKET_VERSION: "13", + } + + for key, value in default_headers.items(): + real_headers.setdefault(key, value) + + sec_key = base64.b64encode(os.urandom(16)) + real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode() + + if protocols: + real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols) + if origin is not None: + real_headers[hdrs.ORIGIN] = origin + if compress: + extstr = ws_ext_gen(compress=compress) + real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr + + # For the sake of backward compatibility, if user passes in None, convert it to True + if ssl is None: + warnings.warn( + "ssl=None is deprecated, please use ssl=True", + DeprecationWarning, + stacklevel=2, + ) + ssl = True + ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) + + # send request + resp = await self.request( + method, + url, + params=params, + headers=real_headers, + read_until_eof=False, + auth=auth, + proxy=proxy, + proxy_auth=proxy_auth, + ssl=ssl, + server_hostname=server_hostname, + proxy_headers=proxy_headers, + ) + + try: + # check handshake + if resp.status != 101: + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid response status", + status=resp.status, + headers=resp.headers, + ) + + if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket": + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid upgrade header", + status=resp.status, + headers=resp.headers, + ) + + if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade": + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid connection header", + status=resp.status, + headers=resp.headers, + ) + + # key calculation + r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "") + match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode() + if r_key != match: + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid challenge response", + status=resp.status, + headers=resp.headers, + ) + + # websocket protocol + protocol = None + if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers: + resp_protocols = [ + proto.strip() + for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",") + ] + + for proto in resp_protocols: + if proto in protocols: + protocol = proto + break + + # websocket compress + notakeover = False + if compress: + compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS) + if compress_hdrs: + try: + compress, notakeover = ws_ext_parse(compress_hdrs) + except WSHandshakeError as exc: + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message=exc.args[0], + status=resp.status, + headers=resp.headers, + ) from exc + else: + compress = 0 + notakeover = False + + conn = resp.connection + assert conn is not None + conn_proto = conn.protocol + assert conn_proto is not None + + # For WS connection the read_timeout must be either receive_timeout or greater + # None == no timeout, i.e. infinite timeout, so None is the max timeout possible + if ws_timeout.ws_receive is None: + # Reset regardless + conn_proto.read_timeout = None + elif conn_proto.read_timeout is not None: + conn_proto.read_timeout = max( + ws_timeout.ws_receive, conn_proto.read_timeout + ) + + transport = conn.transport + assert transport is not None + reader = WebSocketDataQueue(conn_proto, 2**16, loop=self._loop) + conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader) + writer = WebSocketWriter( + conn_proto, + transport, + use_mask=True, + compress=compress, + notakeover=notakeover, + ) + except BaseException: + resp.close() + raise + else: + return self._ws_response_class( + reader, + writer, + protocol, + resp, + ws_timeout, + autoclose, + autoping, + self._loop, + heartbeat=heartbeat, + compress=compress, + client_notakeover=notakeover, + ) + + def _prepare_headers(self, headers: Optional[LooseHeaders]) -> "CIMultiDict[str]": + """Add default headers and transform it to CIMultiDict""" + # Convert headers to MultiDict + result = CIMultiDict(self._default_headers) + if headers: + if not isinstance(headers, (MultiDictProxy, MultiDict)): + headers = CIMultiDict(headers) + added_names: Set[str] = set() + for key, value in headers.items(): + if key in added_names: + result.add(key, value) + else: + result[key] = value + added_names.add(key) + return result + + def _get_netrc_auth(self, host: str) -> Optional[BasicAuth]: + """ + Get auth from netrc for the given host. + + This method is designed to be called in an executor to avoid + blocking I/O in the event loop. + """ + netrc_obj = netrc_from_env() + try: + return basicauth_from_netrc(netrc_obj, host) + except LookupError: + return None + + if sys.version_info >= (3, 11) and TYPE_CHECKING: + + def get( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def options( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def head( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def post( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def put( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def patch( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + def delete( + self, + url: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> "_RequestContextManager": ... + + else: + + def get( + self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP GET request.""" + return _RequestContextManager( + self._request( + hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs + ) + ) + + def options( + self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP OPTIONS request.""" + return _RequestContextManager( + self._request( + hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs + ) + ) + + def head( + self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP HEAD request.""" + return _RequestContextManager( + self._request( + hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs + ) + ) + + def post( + self, url: StrOrURL, *, data: Any = None, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP POST request.""" + return _RequestContextManager( + self._request(hdrs.METH_POST, url, data=data, **kwargs) + ) + + def put( + self, url: StrOrURL, *, data: Any = None, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP PUT request.""" + return _RequestContextManager( + self._request(hdrs.METH_PUT, url, data=data, **kwargs) + ) + + def patch( + self, url: StrOrURL, *, data: Any = None, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP PATCH request.""" + return _RequestContextManager( + self._request(hdrs.METH_PATCH, url, data=data, **kwargs) + ) + + def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager": + """Perform HTTP DELETE request.""" + return _RequestContextManager( + self._request(hdrs.METH_DELETE, url, **kwargs) + ) + + async def close(self) -> None: + """Close underlying connector. + + Release all acquired resources. + """ + if not self.closed: + if self._connector is not None and self._connector_owner: + await self._connector.close() + self._connector = None + + @property + def closed(self) -> bool: + """Is client session closed. + + A readonly property. + """ + return self._connector is None or self._connector.closed + + @property + def connector(self) -> Optional[BaseConnector]: + """Connector instance used for the session.""" + return self._connector + + @property + def cookie_jar(self) -> AbstractCookieJar: + """The session cookies.""" + return self._cookie_jar + + @property + def version(self) -> Tuple[int, int]: + """The session HTTP protocol version.""" + return self._version + + @property + def requote_redirect_url(self) -> bool: + """Do URL requoting on redirection handling.""" + return self._requote_redirect_url + + @requote_redirect_url.setter + def requote_redirect_url(self, val: bool) -> None: + """Do URL requoting on redirection handling.""" + warnings.warn( + "session.requote_redirect_url modification is deprecated #2778", + DeprecationWarning, + stacklevel=2, + ) + self._requote_redirect_url = val + + @property + def loop(self) -> asyncio.AbstractEventLoop: + """Session's loop.""" + warnings.warn( + "client.loop property is deprecated", DeprecationWarning, stacklevel=2 + ) + return self._loop + + @property + def timeout(self) -> ClientTimeout: + """Timeout for the session.""" + return self._timeout + + @property + def headers(self) -> "CIMultiDict[str]": + """The default headers of the client session.""" + return self._default_headers + + @property + def skip_auto_headers(self) -> FrozenSet[istr]: + """Headers for which autogeneration should be skipped""" + return self._skip_auto_headers + + @property + def auth(self) -> Optional[BasicAuth]: + """An object that represents HTTP Basic Authorization""" + return self._default_auth + + @property + def json_serialize(self) -> JSONEncoder: + """Json serializer callable""" + return self._json_serialize + + @property + def connector_owner(self) -> bool: + """Should connector be closed on session closing""" + return self._connector_owner + + @property + def raise_for_status( + self, + ) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]: + """Should `ClientResponse.raise_for_status()` be called for each response.""" + return self._raise_for_status + + @property + def auto_decompress(self) -> bool: + """Should the body response be automatically decompressed.""" + return self._auto_decompress + + @property + def trust_env(self) -> bool: + """ + Should proxies information from environment or netrc be trusted. + + Information is from HTTP_PROXY / HTTPS_PROXY environment variables + or ~/.netrc file if present. + """ + return self._trust_env + + @property + def trace_configs(self) -> List[TraceConfig]: + """A list of TraceConfig instances used for client tracing""" + return self._trace_configs + + def detach(self) -> None: + """Detach connector from session without closing the former. + + Session is switched to closed state anyway. + """ + self._connector = None + + def __enter__(self) -> None: + raise TypeError("Use async with instead") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + # __exit__ should exist in pair with __enter__ but never executed + pass # pragma: no cover + + async def __aenter__(self) -> "ClientSession": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + await self.close() + + +class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType]): + + __slots__ = ("_coro", "_resp") + + def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None: + self._coro: Coroutine["asyncio.Future[Any]", None, _RetType] = coro + + def send(self, arg: None) -> "asyncio.Future[Any]": + return self._coro.send(arg) + + def throw(self, *args: Any, **kwargs: Any) -> "asyncio.Future[Any]": + return self._coro.throw(*args, **kwargs) + + def close(self) -> None: + return self._coro.close() + + def __await__(self) -> Generator[Any, None, _RetType]: + ret = self._coro.__await__() + return ret + + def __iter__(self) -> Generator[Any, None, _RetType]: + return self.__await__() + + async def __aenter__(self) -> _RetType: + self._resp: _RetType = await self._coro + return await self._resp.__aenter__() + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + await self._resp.__aexit__(exc_type, exc, tb) + + +_RequestContextManager = _BaseRequestContextManager[ClientResponse] +_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse] + + +class _SessionRequestContextManager: + + __slots__ = ("_coro", "_resp", "_session") + + def __init__( + self, + coro: Coroutine["asyncio.Future[Any]", None, ClientResponse], + session: ClientSession, + ) -> None: + self._coro = coro + self._resp: Optional[ClientResponse] = None + self._session = session + + async def __aenter__(self) -> ClientResponse: + try: + self._resp = await self._coro + except BaseException: + await self._session.close() + raise + else: + return self._resp + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + assert self._resp is not None + self._resp.close() + await self._session.close() + + +if sys.version_info >= (3, 11) and TYPE_CHECKING: + + def request( + method: str, + url: StrOrURL, + *, + version: HttpVersion = http.HttpVersion11, + connector: Optional[BaseConnector] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Unpack[_RequestOptions], + ) -> _SessionRequestContextManager: ... + +else: + + def request( + method: str, + url: StrOrURL, + *, + version: HttpVersion = http.HttpVersion11, + connector: Optional[BaseConnector] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Any, + ) -> _SessionRequestContextManager: + """Constructs and sends a request. + + Returns response object. + method - HTTP method + url - request url + params - (optional) Dictionary or bytes to be sent in the query + string of the new request + data - (optional) Dictionary, bytes, or file-like object to + send in the body of the request + json - (optional) Any json compatible python object + headers - (optional) Dictionary of HTTP Headers to send with + the request + cookies - (optional) Dict object to send with the request + auth - (optional) BasicAuth named tuple represent HTTP Basic Auth + auth - aiohttp.helpers.BasicAuth + allow_redirects - (optional) If set to False, do not follow + redirects + version - Request HTTP version. + compress - Set to True if request has to be compressed + with deflate encoding. + chunked - Set to chunk size for chunked transfer encoding. + expect100 - Expect 100-continue response from server. + connector - BaseConnector sub-class instance to support + connection pooling. + read_until_eof - Read response until eof if response + does not have Content-Length header. + loop - Optional event loop. + timeout - Optional ClientTimeout settings structure, 5min + total timeout by default. + Usage:: + >>> import aiohttp + >>> async with aiohttp.request('GET', 'http://python.org/') as resp: + ... print(resp) + ... data = await resp.read() + + """ + connector_owner = False + if connector is None: + connector_owner = True + connector = TCPConnector(loop=loop, force_close=True) + + session = ClientSession( + loop=loop, + cookies=kwargs.pop("cookies", None), + version=version, + timeout=kwargs.pop("timeout", sentinel), + connector=connector, + connector_owner=connector_owner, + ) + + return _SessionRequestContextManager( + session._request(method, url, **kwargs), + session, + ) diff --git a/venv/lib/python3.12/site-packages/aiohttp/client_exceptions.py b/venv/lib/python3.12/site-packages/aiohttp/client_exceptions.py new file mode 100644 index 00000000..4702da8d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/client_exceptions.py @@ -0,0 +1,432 @@ +"""HTTP related errors.""" + +import asyncio +import warnings +from typing import TYPE_CHECKING, Optional, Tuple, Union + +from multidict import MultiMapping + +from .typedefs import StrOrURL + +if TYPE_CHECKING: + import ssl + + SSLContext = ssl.SSLContext +else: + try: + import ssl + + SSLContext = ssl.SSLContext + except ImportError: # pragma: no cover + ssl = SSLContext = None # type: ignore[assignment] + +if TYPE_CHECKING: + from .client_reqrep import ClientResponse, ConnectionKey, Fingerprint, RequestInfo + from .http_parser import RawResponseMessage +else: + RequestInfo = ClientResponse = ConnectionKey = RawResponseMessage = None + +__all__ = ( + "ClientError", + "ClientConnectionError", + "ClientConnectionResetError", + "ClientOSError", + "ClientConnectorError", + "ClientProxyConnectionError", + "ClientSSLError", + "ClientConnectorDNSError", + "ClientConnectorSSLError", + "ClientConnectorCertificateError", + "ConnectionTimeoutError", + "SocketTimeoutError", + "ServerConnectionError", + "ServerTimeoutError", + "ServerDisconnectedError", + "ServerFingerprintMismatch", + "ClientResponseError", + "ClientHttpProxyError", + "WSServerHandshakeError", + "ContentTypeError", + "ClientPayloadError", + "InvalidURL", + "InvalidUrlClientError", + "RedirectClientError", + "NonHttpUrlClientError", + "InvalidUrlRedirectClientError", + "NonHttpUrlRedirectClientError", + "WSMessageTypeError", +) + + +class ClientError(Exception): + """Base class for client connection errors.""" + + +class ClientResponseError(ClientError): + """Base class for exceptions that occur after getting a response. + + request_info: An instance of RequestInfo. + history: A sequence of responses, if redirects occurred. + status: HTTP status code. + message: Error message. + headers: Response headers. + """ + + def __init__( + self, + request_info: RequestInfo, + history: Tuple[ClientResponse, ...], + *, + code: Optional[int] = None, + status: Optional[int] = None, + message: str = "", + headers: Optional[MultiMapping[str]] = None, + ) -> None: + self.request_info = request_info + if code is not None: + if status is not None: + raise ValueError( + "Both code and status arguments are provided; " + "code is deprecated, use status instead" + ) + warnings.warn( + "code argument is deprecated, use status instead", + DeprecationWarning, + stacklevel=2, + ) + if status is not None: + self.status = status + elif code is not None: + self.status = code + else: + self.status = 0 + self.message = message + self.headers = headers + self.history = history + self.args = (request_info, history) + + def __str__(self) -> str: + return "{}, message={!r}, url={!r}".format( + self.status, + self.message, + str(self.request_info.real_url), + ) + + def __repr__(self) -> str: + args = f"{self.request_info!r}, {self.history!r}" + if self.status != 0: + args += f", status={self.status!r}" + if self.message != "": + args += f", message={self.message!r}" + if self.headers is not None: + args += f", headers={self.headers!r}" + return f"{type(self).__name__}({args})" + + @property + def code(self) -> int: + warnings.warn( + "code property is deprecated, use status instead", + DeprecationWarning, + stacklevel=2, + ) + return self.status + + @code.setter + def code(self, value: int) -> None: + warnings.warn( + "code property is deprecated, use status instead", + DeprecationWarning, + stacklevel=2, + ) + self.status = value + + +class ContentTypeError(ClientResponseError): + """ContentType found is not valid.""" + + +class WSServerHandshakeError(ClientResponseError): + """websocket server handshake error.""" + + +class ClientHttpProxyError(ClientResponseError): + """HTTP proxy error. + + Raised in :class:`aiohttp.connector.TCPConnector` if + proxy responds with status other than ``200 OK`` + on ``CONNECT`` request. + """ + + +class TooManyRedirects(ClientResponseError): + """Client was redirected too many times.""" + + +class ClientConnectionError(ClientError): + """Base class for client socket errors.""" + + +class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): + """ConnectionResetError""" + + +class ClientOSError(ClientConnectionError, OSError): + """OSError error.""" + + +class ClientConnectorError(ClientOSError): + """Client connector error. + + Raised in :class:`aiohttp.connector.TCPConnector` if + a connection can not be established. + """ + + def __init__(self, connection_key: ConnectionKey, os_error: OSError) -> None: + self._conn_key = connection_key + self._os_error = os_error + super().__init__(os_error.errno, os_error.strerror) + self.args = (connection_key, os_error) + + @property + def os_error(self) -> OSError: + return self._os_error + + @property + def host(self) -> str: + return self._conn_key.host + + @property + def port(self) -> Optional[int]: + return self._conn_key.port + + @property + def ssl(self) -> Union[SSLContext, bool, "Fingerprint"]: + return self._conn_key.ssl + + def __str__(self) -> str: + return "Cannot connect to host {0.host}:{0.port} ssl:{1} [{2}]".format( + self, "default" if self.ssl is True else self.ssl, self.strerror + ) + + # OSError.__reduce__ does too much black magick + __reduce__ = BaseException.__reduce__ + + +class ClientConnectorDNSError(ClientConnectorError): + """DNS resolution failed during client connection. + + Raised in :class:`aiohttp.connector.TCPConnector` if + DNS resolution fails. + """ + + +class ClientProxyConnectionError(ClientConnectorError): + """Proxy connection error. + + Raised in :class:`aiohttp.connector.TCPConnector` if + connection to proxy can not be established. + """ + + +class UnixClientConnectorError(ClientConnectorError): + """Unix connector error. + + Raised in :py:class:`aiohttp.connector.UnixConnector` + if connection to unix socket can not be established. + """ + + def __init__( + self, path: str, connection_key: ConnectionKey, os_error: OSError + ) -> None: + self._path = path + super().__init__(connection_key, os_error) + + @property + def path(self) -> str: + return self._path + + def __str__(self) -> str: + return "Cannot connect to unix socket {0.path} ssl:{1} [{2}]".format( + self, "default" if self.ssl is True else self.ssl, self.strerror + ) + + +class ServerConnectionError(ClientConnectionError): + """Server connection errors.""" + + +class ServerDisconnectedError(ServerConnectionError): + """Server disconnected.""" + + def __init__(self, message: Union[RawResponseMessage, str, None] = None) -> None: + if message is None: + message = "Server disconnected" + + self.args = (message,) + self.message = message + + +class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): + """Server timeout error.""" + + +class ConnectionTimeoutError(ServerTimeoutError): + """Connection timeout error.""" + + +class SocketTimeoutError(ServerTimeoutError): + """Socket timeout error.""" + + +class ServerFingerprintMismatch(ServerConnectionError): + """SSL certificate does not match expected fingerprint.""" + + def __init__(self, expected: bytes, got: bytes, host: str, port: int) -> None: + self.expected = expected + self.got = got + self.host = host + self.port = port + self.args = (expected, got, host, port) + + def __repr__(self) -> str: + return "<{} expected={!r} got={!r} host={!r} port={!r}>".format( + self.__class__.__name__, self.expected, self.got, self.host, self.port + ) + + +class ClientPayloadError(ClientError): + """Response payload error.""" + + +class InvalidURL(ClientError, ValueError): + """Invalid URL. + + URL used for fetching is malformed, e.g. it doesn't contains host + part. + """ + + # Derive from ValueError for backward compatibility + + def __init__(self, url: StrOrURL, description: Union[str, None] = None) -> None: + # The type of url is not yarl.URL because the exception can be raised + # on URL(url) call + self._url = url + self._description = description + + if description: + super().__init__(url, description) + else: + super().__init__(url) + + @property + def url(self) -> StrOrURL: + return self._url + + @property + def description(self) -> "str | None": + return self._description + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self}>" + + def __str__(self) -> str: + if self._description: + return f"{self._url} - {self._description}" + return str(self._url) + + +class InvalidUrlClientError(InvalidURL): + """Invalid URL client error.""" + + +class RedirectClientError(ClientError): + """Client redirect error.""" + + +class NonHttpUrlClientError(ClientError): + """Non http URL client error.""" + + +class InvalidUrlRedirectClientError(InvalidUrlClientError, RedirectClientError): + """Invalid URL redirect client error.""" + + +class NonHttpUrlRedirectClientError(NonHttpUrlClientError, RedirectClientError): + """Non http URL redirect client error.""" + + +class ClientSSLError(ClientConnectorError): + """Base error for ssl.*Errors.""" + + +if ssl is not None: + cert_errors = (ssl.CertificateError,) + cert_errors_bases = ( + ClientSSLError, + ssl.CertificateError, + ) + + ssl_errors = (ssl.SSLError,) + ssl_error_bases = (ClientSSLError, ssl.SSLError) +else: # pragma: no cover + cert_errors = tuple() + cert_errors_bases = ( + ClientSSLError, + ValueError, + ) + + ssl_errors = tuple() + ssl_error_bases = (ClientSSLError,) + + +class ClientConnectorSSLError(*ssl_error_bases): # type: ignore[misc] + """Response ssl error.""" + + +class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore[misc] + """Response certificate error.""" + + _conn_key: ConnectionKey + + def __init__( + # TODO: If we require ssl in future, this can become ssl.CertificateError + self, + connection_key: ConnectionKey, + certificate_error: Exception, + ) -> None: + if isinstance(certificate_error, cert_errors + (OSError,)): + # ssl.CertificateError has errno and strerror, so we should be fine + os_error = certificate_error + else: + os_error = OSError() + + super().__init__(connection_key, os_error) + self._certificate_error = certificate_error + self.args = (connection_key, certificate_error) + + @property + def certificate_error(self) -> Exception: + return self._certificate_error + + @property + def host(self) -> str: + return self._conn_key.host + + @property + def port(self) -> Optional[int]: + return self._conn_key.port + + @property + def ssl(self) -> bool: + return self._conn_key.is_ssl + + def __str__(self) -> str: + return ( + "Cannot connect to host {0.host}:{0.port} ssl:{0.ssl} " + "[{0.certificate_error.__class__.__name__}: " + "{0.certificate_error.args}]".format(self) + ) + + +class WSMessageTypeError(TypeError): + """WebSocket message type is not valid.""" diff --git a/venv/lib/python3.12/site-packages/aiohttp/client_middleware_digest_auth.py b/venv/lib/python3.12/site-packages/aiohttp/client_middleware_digest_auth.py new file mode 100644 index 00000000..d7f2f1eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/client_middleware_digest_auth.py @@ -0,0 +1,480 @@ +""" +Digest authentication middleware for aiohttp client. + +This middleware implements HTTP Digest Authentication according to RFC 7616, +providing a more secure alternative to Basic Authentication. It supports all +standard hash algorithms including MD5, SHA, SHA-256, SHA-512 and their session +variants, as well as both 'auth' and 'auth-int' quality of protection (qop) options. +""" + +import hashlib +import os +import re +import sys +import time +from typing import ( + Callable, + Dict, + Final, + FrozenSet, + List, + Literal, + Tuple, + TypedDict, + Union, +) + +from yarl import URL + +from . import hdrs +from .client_exceptions import ClientError +from .client_middlewares import ClientHandlerType +from .client_reqrep import ClientRequest, ClientResponse +from .payload import Payload + + +class DigestAuthChallenge(TypedDict, total=False): + realm: str + nonce: str + qop: str + algorithm: str + opaque: str + domain: str + stale: str + + +DigestFunctions: Dict[str, Callable[[bytes], "hashlib._Hash"]] = { + "MD5": hashlib.md5, + "MD5-SESS": hashlib.md5, + "SHA": hashlib.sha1, + "SHA-SESS": hashlib.sha1, + "SHA256": hashlib.sha256, + "SHA256-SESS": hashlib.sha256, + "SHA-256": hashlib.sha256, + "SHA-256-SESS": hashlib.sha256, + "SHA512": hashlib.sha512, + "SHA512-SESS": hashlib.sha512, + "SHA-512": hashlib.sha512, + "SHA-512-SESS": hashlib.sha512, +} + + +# Compile the regex pattern once at module level for performance +_HEADER_PAIRS_PATTERN = re.compile( + r'(?:^|\s|,\s*)(\w+)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+))' + if sys.version_info < (3, 11) + else r'(?:^|\s|,\s*)((?>\w+))\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+))' + # +------------|--------|--|-|-|--|----|------|----|--||-----|-> Match valid start/sep + # +--------|--|-|-|--|----|------|----|--||-----|-> alphanumeric key (atomic + # | | | | | | | | || | group reduces backtracking) + # +--|-|-|--|----|------|----|--||-----|-> maybe whitespace + # | | | | | | | || | + # +-|-|--|----|------|----|--||-----|-> = (delimiter) + # +-|--|----|------|----|--||-----|-> maybe whitespace + # | | | | | || | + # +--|----|------|----|--||-----|-> group quoted or unquoted + # | | | | || | + # +----|------|----|--||-----|-> if quoted... + # +------|----|--||-----|-> anything but " or \ + # +----|--||-----|-> escaped characters allowed + # +--||-----|-> or can be empty string + # || | + # +|-----|-> if unquoted... + # +-----|-> anything but , or + # +-> at least one char req'd +) + + +# RFC 7616: Challenge parameters to extract +CHALLENGE_FIELDS: Final[ + Tuple[ + Literal["realm", "nonce", "qop", "algorithm", "opaque", "domain", "stale"], ... + ] +] = ( + "realm", + "nonce", + "qop", + "algorithm", + "opaque", + "domain", + "stale", +) + +# Supported digest authentication algorithms +# Use a tuple of sorted keys for predictable documentation and error messages +SUPPORTED_ALGORITHMS: Final[Tuple[str, ...]] = tuple(sorted(DigestFunctions.keys())) + +# RFC 7616: Fields that require quoting in the Digest auth header +# These fields must be enclosed in double quotes in the Authorization header. +# Algorithm, qop, and nc are never quoted per RFC specifications. +# This frozen set is used by the template-based header construction to +# automatically determine which fields need quotes. +QUOTED_AUTH_FIELDS: Final[FrozenSet[str]] = frozenset( + {"username", "realm", "nonce", "uri", "response", "opaque", "cnonce"} +) + + +def escape_quotes(value: str) -> str: + """Escape double quotes for HTTP header values.""" + return value.replace('"', '\\"') + + +def unescape_quotes(value: str) -> str: + """Unescape double quotes in HTTP header values.""" + return value.replace('\\"', '"') + + +def parse_header_pairs(header: str) -> Dict[str, str]: + """ + Parse key-value pairs from WWW-Authenticate or similar HTTP headers. + + This function handles the complex format of WWW-Authenticate header values, + supporting both quoted and unquoted values, proper handling of commas in + quoted values, and whitespace variations per RFC 7616. + + Examples of supported formats: + - key1="value1", key2=value2 + - key1 = "value1" , key2="value, with, commas" + - key1=value1,key2="value2" + - realm="example.com", nonce="12345", qop="auth" + + Args: + header: The header value string to parse + + Returns: + Dictionary mapping parameter names to their values + """ + return { + stripped_key: unescape_quotes(quoted_val) if quoted_val else unquoted_val + for key, quoted_val, unquoted_val in _HEADER_PAIRS_PATTERN.findall(header) + if (stripped_key := key.strip()) + } + + +class DigestAuthMiddleware: + """ + HTTP digest authentication middleware for aiohttp client. + + This middleware intercepts 401 Unauthorized responses containing a Digest + authentication challenge, calculates the appropriate digest credentials, + and automatically retries the request with the proper Authorization header. + + Features: + - Handles all aspects of Digest authentication handshake automatically + - Supports all standard hash algorithms: + - MD5, MD5-SESS + - SHA, SHA-SESS + - SHA256, SHA256-SESS, SHA-256, SHA-256-SESS + - SHA512, SHA512-SESS, SHA-512, SHA-512-SESS + - Supports 'auth' and 'auth-int' quality of protection modes + - Properly handles quoted strings and parameter parsing + - Includes replay attack protection with client nonce count tracking + - Supports preemptive authentication per RFC 7616 Section 3.6 + + Standards compliance: + - RFC 7616: HTTP Digest Access Authentication (primary reference) + - RFC 2617: HTTP Authentication (deprecated by RFC 7616) + - RFC 1945: Section 11.1 (username restrictions) + + Implementation notes: + The core digest calculation is inspired by the implementation in + https://github.com/requests/requests/blob/v2.18.4/requests/auth.py + with added support for modern digest auth features and error handling. + """ + + def __init__( + self, + login: str, + password: str, + preemptive: bool = True, + ) -> None: + if login is None: + raise ValueError("None is not allowed as login value") + + if password is None: + raise ValueError("None is not allowed as password value") + + if ":" in login: + raise ValueError('A ":" is not allowed in username (RFC 1945#section-11.1)') + + self._login_str: Final[str] = login + self._login_bytes: Final[bytes] = login.encode("utf-8") + self._password_bytes: Final[bytes] = password.encode("utf-8") + + self._last_nonce_bytes = b"" + self._nonce_count = 0 + self._challenge: DigestAuthChallenge = {} + self._preemptive: bool = preemptive + # Set of URLs defining the protection space + self._protection_space: List[str] = [] + + async def _encode( + self, method: str, url: URL, body: Union[Payload, Literal[b""]] + ) -> str: + """ + Build digest authorization header for the current challenge. + + Args: + method: The HTTP method (GET, POST, etc.) + url: The request URL + body: The request body (used for qop=auth-int) + + Returns: + A fully formatted Digest authorization header string + + Raises: + ClientError: If the challenge is missing required parameters or + contains unsupported values + + """ + challenge = self._challenge + if "realm" not in challenge: + raise ClientError( + "Malformed Digest auth challenge: Missing 'realm' parameter" + ) + + if "nonce" not in challenge: + raise ClientError( + "Malformed Digest auth challenge: Missing 'nonce' parameter" + ) + + # Empty realm values are allowed per RFC 7616 (SHOULD, not MUST, contain host name) + realm = challenge["realm"] + nonce = challenge["nonce"] + + # Empty nonce values are not allowed as they are security-critical for replay protection + if not nonce: + raise ClientError( + "Security issue: Digest auth challenge contains empty 'nonce' value" + ) + + qop_raw = challenge.get("qop", "") + # Preserve original algorithm case for response while using uppercase for processing + algorithm_original = challenge.get("algorithm", "MD5") + algorithm = algorithm_original.upper() + opaque = challenge.get("opaque", "") + + # Convert string values to bytes once + nonce_bytes = nonce.encode("utf-8") + realm_bytes = realm.encode("utf-8") + path = URL(url).path_qs + + # Process QoP + qop = "" + qop_bytes = b"" + if qop_raw: + valid_qops = {"auth", "auth-int"}.intersection( + {q.strip() for q in qop_raw.split(",") if q.strip()} + ) + if not valid_qops: + raise ClientError( + f"Digest auth error: Unsupported Quality of Protection (qop) value(s): {qop_raw}" + ) + + qop = "auth-int" if "auth-int" in valid_qops else "auth" + qop_bytes = qop.encode("utf-8") + + if algorithm not in DigestFunctions: + raise ClientError( + f"Digest auth error: Unsupported hash algorithm: {algorithm}. " + f"Supported algorithms: {', '.join(SUPPORTED_ALGORITHMS)}" + ) + hash_fn: Final = DigestFunctions[algorithm] + + def H(x: bytes) -> bytes: + """RFC 7616 Section 3: Hash function H(data) = hex(hash(data)).""" + return hash_fn(x).hexdigest().encode() + + def KD(s: bytes, d: bytes) -> bytes: + """RFC 7616 Section 3: KD(secret, data) = H(concat(secret, ":", data)).""" + return H(b":".join((s, d))) + + # Calculate A1 and A2 + A1 = b":".join((self._login_bytes, realm_bytes, self._password_bytes)) + A2 = f"{method.upper()}:{path}".encode() + if qop == "auth-int": + if isinstance(body, Payload): # will always be empty bytes unless Payload + entity_bytes = await body.as_bytes() # Get bytes from Payload + else: + entity_bytes = body + entity_hash = H(entity_bytes) + A2 = b":".join((A2, entity_hash)) + + HA1 = H(A1) + HA2 = H(A2) + + # Nonce count handling + if nonce_bytes == self._last_nonce_bytes: + self._nonce_count += 1 + else: + self._nonce_count = 1 + + self._last_nonce_bytes = nonce_bytes + ncvalue = f"{self._nonce_count:08x}" + ncvalue_bytes = ncvalue.encode("utf-8") + + # Generate client nonce + cnonce = hashlib.sha1( + b"".join( + [ + str(self._nonce_count).encode("utf-8"), + nonce_bytes, + time.ctime().encode("utf-8"), + os.urandom(8), + ] + ) + ).hexdigest()[:16] + cnonce_bytes = cnonce.encode("utf-8") + + # Special handling for session-based algorithms + if algorithm.upper().endswith("-SESS"): + HA1 = H(b":".join((HA1, nonce_bytes, cnonce_bytes))) + + # Calculate the response digest + if qop: + noncebit = b":".join( + (nonce_bytes, ncvalue_bytes, cnonce_bytes, qop_bytes, HA2) + ) + response_digest = KD(HA1, noncebit) + else: + response_digest = KD(HA1, b":".join((nonce_bytes, HA2))) + + # Define a dict mapping of header fields to their values + # Group fields into always-present, optional, and qop-dependent + header_fields = { + # Always present fields + "username": escape_quotes(self._login_str), + "realm": escape_quotes(realm), + "nonce": escape_quotes(nonce), + "uri": path, + "response": response_digest.decode(), + "algorithm": algorithm_original, + } + + # Optional fields + if opaque: + header_fields["opaque"] = escape_quotes(opaque) + + # QoP-dependent fields + if qop: + header_fields["qop"] = qop + header_fields["nc"] = ncvalue + header_fields["cnonce"] = cnonce + + # Build header using templates for each field type + pairs: List[str] = [] + for field, value in header_fields.items(): + if field in QUOTED_AUTH_FIELDS: + pairs.append(f'{field}="{value}"') + else: + pairs.append(f"{field}={value}") + + return f"Digest {', '.join(pairs)}" + + def _in_protection_space(self, url: URL) -> bool: + """ + Check if the given URL is within the current protection space. + + According to RFC 7616, a URI is in the protection space if any URI + in the protection space is a prefix of it (after both have been made absolute). + """ + request_str = str(url) + for space_str in self._protection_space: + # Check if request starts with space URL + if not request_str.startswith(space_str): + continue + # Exact match or space ends with / (proper directory prefix) + if len(request_str) == len(space_str) or space_str[-1] == "/": + return True + # Check next char is / to ensure proper path boundary + if request_str[len(space_str)] == "/": + return True + return False + + def _authenticate(self, response: ClientResponse) -> bool: + """ + Takes the given response and tries digest-auth, if needed. + + Returns true if the original request must be resent. + """ + if response.status != 401: + return False + + auth_header = response.headers.get("www-authenticate", "") + if not auth_header: + return False # No authentication header present + + method, sep, headers = auth_header.partition(" ") + if not sep: + # No space found in www-authenticate header + return False # Malformed auth header, missing scheme separator + + if method.lower() != "digest": + # Not a digest auth challenge (could be Basic, Bearer, etc.) + return False + + if not headers: + # We have a digest scheme but no parameters + return False # Malformed digest header, missing parameters + + # We have a digest auth header with content + if not (header_pairs := parse_header_pairs(headers)): + # Failed to parse any key-value pairs + return False # Malformed digest header, no valid parameters + + # Extract challenge parameters + self._challenge = {} + for field in CHALLENGE_FIELDS: + if (value := header_pairs.get(field)) is not None: + self._challenge[field] = value + + # Update protection space based on domain parameter or default to origin + origin = response.url.origin() + + if domain := self._challenge.get("domain"): + # Parse space-separated list of URIs + self._protection_space = [] + for uri in domain.split(): + # Remove quotes if present + uri = uri.strip('"') + if uri.startswith("/"): + # Path-absolute, relative to origin + self._protection_space.append(str(origin.join(URL(uri)))) + else: + # Absolute URI + self._protection_space.append(str(URL(uri))) + else: + # No domain specified, protection space is entire origin + self._protection_space = [str(origin)] + + # Return True only if we found at least one challenge parameter + return bool(self._challenge) + + async def __call__( + self, request: ClientRequest, handler: ClientHandlerType + ) -> ClientResponse: + """Run the digest auth middleware.""" + response = None + for retry_count in range(2): + # Apply authorization header if: + # 1. This is a retry after 401 (retry_count > 0), OR + # 2. Preemptive auth is enabled AND we have a challenge AND the URL is in protection space + if retry_count > 0 or ( + self._preemptive + and self._challenge + and self._in_protection_space(request.url) + ): + request.headers[hdrs.AUTHORIZATION] = await self._encode( + request.method, request.url, request.body + ) + + # Send the request + response = await handler(request) + + # Check if we need to authenticate + if not self._authenticate(response): + break + + # At this point, response is guaranteed to be defined + assert response is not None + return response diff --git a/venv/lib/python3.12/site-packages/aiohttp/client_middlewares.py b/venv/lib/python3.12/site-packages/aiohttp/client_middlewares.py new file mode 100644 index 00000000..3ca2cb20 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/client_middlewares.py @@ -0,0 +1,55 @@ +"""Client middleware support.""" + +from collections.abc import Awaitable, Callable, Sequence + +from .client_reqrep import ClientRequest, ClientResponse + +__all__ = ("ClientMiddlewareType", "ClientHandlerType", "build_client_middlewares") + +# Type alias for client request handlers - functions that process requests and return responses +ClientHandlerType = Callable[[ClientRequest], Awaitable[ClientResponse]] + +# Type for client middleware - similar to server but uses ClientRequest/ClientResponse +ClientMiddlewareType = Callable[ + [ClientRequest, ClientHandlerType], Awaitable[ClientResponse] +] + + +def build_client_middlewares( + handler: ClientHandlerType, + middlewares: Sequence[ClientMiddlewareType], +) -> ClientHandlerType: + """ + Apply middlewares to request handler. + + The middlewares are applied in reverse order, so the first middleware + in the list wraps all subsequent middlewares and the handler. + + This implementation avoids using partial/update_wrapper to minimize overhead + and doesn't cache to avoid holding references to stateful middleware. + """ + # Optimize for single middleware case + if len(middlewares) == 1: + middleware = middlewares[0] + + async def single_middleware_handler(req: ClientRequest) -> ClientResponse: + return await middleware(req, handler) + + return single_middleware_handler + + # Build the chain for multiple middlewares + current_handler = handler + + for middleware in reversed(middlewares): + # Create a new closure that captures the current state + def make_wrapper( + mw: ClientMiddlewareType, next_h: ClientHandlerType + ) -> ClientHandlerType: + async def wrapped(req: ClientRequest) -> ClientResponse: + return await mw(req, next_h) + + return wrapped + + current_handler = make_wrapper(middleware, current_handler) + + return current_handler diff --git a/venv/lib/python3.12/site-packages/aiohttp/client_proto.py b/venv/lib/python3.12/site-packages/aiohttp/client_proto.py new file mode 100644 index 00000000..aea31917 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/client_proto.py @@ -0,0 +1,361 @@ +import asyncio +from contextlib import suppress +from typing import Any, Optional, Tuple, Union + +from .base_protocol import BaseProtocol +from .client_exceptions import ( + ClientConnectionError, + ClientOSError, + ClientPayloadError, + ServerDisconnectedError, + SocketTimeoutError, +) +from .helpers import ( + _EXC_SENTINEL, + EMPTY_BODY_STATUS_CODES, + BaseTimerContext, + set_exception, + set_result, +) +from .http import HttpResponseParser, RawResponseMessage +from .http_exceptions import HttpProcessingError +from .streams import EMPTY_PAYLOAD, DataQueue, StreamReader + + +class ResponseHandler(BaseProtocol, DataQueue[Tuple[RawResponseMessage, StreamReader]]): + """Helper class to adapt between Protocol and StreamReader.""" + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + BaseProtocol.__init__(self, loop=loop) + DataQueue.__init__(self, loop) + + self._should_close = False + + self._payload: Optional[StreamReader] = None + self._skip_payload = False + self._payload_parser = None + + self._timer = None + + self._tail = b"" + self._upgraded = False + self._parser: Optional[HttpResponseParser] = None + + self._read_timeout: Optional[float] = None + self._read_timeout_handle: Optional[asyncio.TimerHandle] = None + + self._timeout_ceil_threshold: Optional[float] = 5 + + self._closed: Union[None, asyncio.Future[None]] = None + self._connection_lost_called = False + + @property + def closed(self) -> Union[None, asyncio.Future[None]]: + """Future that is set when the connection is closed. + + This property returns a Future that will be completed when the connection + is closed. The Future is created lazily on first access to avoid creating + futures that will never be awaited. + + Returns: + - A Future[None] if the connection is still open or was closed after + this property was accessed + - None if connection_lost() was already called before this property + was ever accessed (indicating no one is waiting for the closure) + """ + if self._closed is None and not self._connection_lost_called: + self._closed = self._loop.create_future() + return self._closed + + @property + def upgraded(self) -> bool: + return self._upgraded + + @property + def should_close(self) -> bool: + return bool( + self._should_close + or (self._payload is not None and not self._payload.is_eof()) + or self._upgraded + or self._exception is not None + or self._payload_parser is not None + or self._buffer + or self._tail + ) + + def force_close(self) -> None: + self._should_close = True + + def close(self) -> None: + self._exception = None # Break cyclic references + transport = self.transport + if transport is not None: + transport.close() + self.transport = None + self._payload = None + self._drop_timeout() + + def abort(self) -> None: + self._exception = None # Break cyclic references + transport = self.transport + if transport is not None: + transport.abort() + self.transport = None + self._payload = None + self._drop_timeout() + + def is_connected(self) -> bool: + return self.transport is not None and not self.transport.is_closing() + + def connection_lost(self, exc: Optional[BaseException]) -> None: + self._connection_lost_called = True + self._drop_timeout() + + original_connection_error = exc + reraised_exc = original_connection_error + + connection_closed_cleanly = original_connection_error is None + + if self._closed is not None: + # If someone is waiting for the closed future, + # we should set it to None or an exception. If + # self._closed is None, it means that + # connection_lost() was called already + # or nobody is waiting for it. + if connection_closed_cleanly: + set_result(self._closed, None) + else: + assert original_connection_error is not None + set_exception( + self._closed, + ClientConnectionError( + f"Connection lost: {original_connection_error !s}", + ), + original_connection_error, + ) + + if self._payload_parser is not None: + with suppress(Exception): # FIXME: log this somehow? + self._payload_parser.feed_eof() + + uncompleted = None + if self._parser is not None: + try: + uncompleted = self._parser.feed_eof() + except Exception as underlying_exc: + if self._payload is not None: + client_payload_exc_msg = ( + f"Response payload is not completed: {underlying_exc !r}" + ) + if not connection_closed_cleanly: + client_payload_exc_msg = ( + f"{client_payload_exc_msg !s}. " + f"{original_connection_error !r}" + ) + set_exception( + self._payload, + ClientPayloadError(client_payload_exc_msg), + underlying_exc, + ) + + if not self.is_eof(): + if isinstance(original_connection_error, OSError): + reraised_exc = ClientOSError(*original_connection_error.args) + if connection_closed_cleanly: + reraised_exc = ServerDisconnectedError(uncompleted) + # assigns self._should_close to True as side effect, + # we do it anyway below + underlying_non_eof_exc = ( + _EXC_SENTINEL + if connection_closed_cleanly + else original_connection_error + ) + assert underlying_non_eof_exc is not None + assert reraised_exc is not None + self.set_exception(reraised_exc, underlying_non_eof_exc) + + self._should_close = True + self._parser = None + self._payload = None + self._payload_parser = None + self._reading_paused = False + + super().connection_lost(reraised_exc) + + def eof_received(self) -> None: + # should call parser.feed_eof() most likely + self._drop_timeout() + + def pause_reading(self) -> None: + super().pause_reading() + self._drop_timeout() + + def resume_reading(self) -> None: + super().resume_reading() + self._reschedule_timeout() + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + self._should_close = True + self._drop_timeout() + super().set_exception(exc, exc_cause) + + def set_parser(self, parser: Any, payload: Any) -> None: + # TODO: actual types are: + # parser: WebSocketReader + # payload: WebSocketDataQueue + # but they are not generi enough + # Need an ABC for both types + self._payload = payload + self._payload_parser = parser + + self._drop_timeout() + + if self._tail: + data, self._tail = self._tail, b"" + self.data_received(data) + + def set_response_params( + self, + *, + timer: Optional[BaseTimerContext] = None, + skip_payload: bool = False, + read_until_eof: bool = False, + auto_decompress: bool = True, + read_timeout: Optional[float] = None, + read_bufsize: int = 2**16, + timeout_ceil_threshold: float = 5, + max_line_size: int = 8190, + max_field_size: int = 8190, + max_headers: int = 128, + ) -> None: + self._skip_payload = skip_payload + + self._read_timeout = read_timeout + + self._timeout_ceil_threshold = timeout_ceil_threshold + + self._parser = HttpResponseParser( + self, + self._loop, + read_bufsize, + timer=timer, + payload_exception=ClientPayloadError, + response_with_body=not skip_payload, + read_until_eof=read_until_eof, + auto_decompress=auto_decompress, + max_line_size=max_line_size, + max_field_size=max_field_size, + max_headers=max_headers, + ) + + if self._tail: + data, self._tail = self._tail, b"" + self.data_received(data) + + def _drop_timeout(self) -> None: + if self._read_timeout_handle is not None: + self._read_timeout_handle.cancel() + self._read_timeout_handle = None + + def _reschedule_timeout(self) -> None: + timeout = self._read_timeout + if self._read_timeout_handle is not None: + self._read_timeout_handle.cancel() + + if timeout: + self._read_timeout_handle = self._loop.call_later( + timeout, self._on_read_timeout + ) + else: + self._read_timeout_handle = None + + def start_timeout(self) -> None: + self._reschedule_timeout() + + @property + def read_timeout(self) -> Optional[float]: + return self._read_timeout + + @read_timeout.setter + def read_timeout(self, read_timeout: Optional[float]) -> None: + self._read_timeout = read_timeout + + def _on_read_timeout(self) -> None: + exc = SocketTimeoutError("Timeout on reading data from socket") + self.set_exception(exc) + if self._payload is not None: + set_exception(self._payload, exc) + + def data_received(self, data: bytes) -> None: + self._reschedule_timeout() + + if not data: + return + + # custom payload parser - currently always WebSocketReader + if self._payload_parser is not None: + eof, tail = self._payload_parser.feed_data(data) + if eof: + self._payload = None + self._payload_parser = None + + if tail: + self.data_received(tail) + return + + if self._upgraded or self._parser is None: + # i.e. websocket connection, websocket parser is not set yet + self._tail += data + return + + # parse http messages + try: + messages, upgraded, tail = self._parser.feed_data(data) + except BaseException as underlying_exc: + if self.transport is not None: + # connection.release() could be called BEFORE + # data_received(), the transport is already + # closed in this case + self.transport.close() + # should_close is True after the call + if isinstance(underlying_exc, HttpProcessingError): + exc = HttpProcessingError( + code=underlying_exc.code, + message=underlying_exc.message, + headers=underlying_exc.headers, + ) + else: + exc = HttpProcessingError() + self.set_exception(exc, underlying_exc) + return + + self._upgraded = upgraded + + payload: Optional[StreamReader] = None + for message, payload in messages: + if message.should_close: + self._should_close = True + + self._payload = payload + + if self._skip_payload or message.code in EMPTY_BODY_STATUS_CODES: + self.feed_data((message, EMPTY_PAYLOAD), 0) + else: + self.feed_data((message, payload), 0) + + if payload is not None: + # new message(s) was processed + # register timeout handler unsubscribing + # either on end-of-stream or immediately for + # EMPTY_PAYLOAD + if payload is not EMPTY_PAYLOAD: + payload.on_eof(self._drop_timeout) + else: + self._drop_timeout() + + if upgraded and tail: + self.data_received(tail) diff --git a/venv/lib/python3.12/site-packages/aiohttp/client_reqrep.py b/venv/lib/python3.12/site-packages/aiohttp/client_reqrep.py new file mode 100644 index 00000000..a9e07958 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/client_reqrep.py @@ -0,0 +1,1536 @@ +import asyncio +import codecs +import contextlib +import functools +import io +import re +import sys +import traceback +import warnings +from collections.abc import Mapping +from hashlib import md5, sha1, sha256 +from http.cookies import Morsel, SimpleCookie +from types import MappingProxyType, TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterable, + List, + Literal, + NamedTuple, + Optional, + Tuple, + Type, + Union, +) + +import attr +from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy +from yarl import URL + +from . import hdrs, helpers, http, multipart, payload +from ._cookie_helpers import ( + parse_cookie_header, + parse_set_cookie_headers, + preserve_morsel_with_coded_value, +) +from .abc import AbstractStreamWriter +from .client_exceptions import ( + ClientConnectionError, + ClientOSError, + ClientResponseError, + ContentTypeError, + InvalidURL, + ServerFingerprintMismatch, +) +from .compression_utils import HAS_BROTLI, HAS_ZSTD +from .formdata import FormData +from .helpers import ( + _SENTINEL, + BaseTimerContext, + BasicAuth, + HeadersMixin, + TimerNoop, + noop, + reify, + sentinel, + set_exception, + set_result, +) +from .http import ( + SERVER_SOFTWARE, + HttpVersion, + HttpVersion10, + HttpVersion11, + StreamWriter, +) +from .streams import StreamReader +from .typedefs import ( + DEFAULT_JSON_DECODER, + JSONDecoder, + LooseCookies, + LooseHeaders, + Query, + RawHeaders, +) + +if TYPE_CHECKING: + import ssl + from ssl import SSLContext +else: + try: + import ssl + from ssl import SSLContext + except ImportError: # pragma: no cover + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] + + +__all__ = ("ClientRequest", "ClientResponse", "RequestInfo", "Fingerprint") + + +if TYPE_CHECKING: + from .client import ClientSession + from .connector import Connection + from .tracing import Trace + + +_CONNECTION_CLOSED_EXCEPTION = ClientConnectionError("Connection closed") +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") +json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json") + + +def _gen_default_accept_encoding() -> str: + encodings = [ + "gzip", + "deflate", + ] + if HAS_BROTLI: + encodings.append("br") + if HAS_ZSTD: + encodings.append("zstd") + return ", ".join(encodings) + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ContentDisposition: + type: Optional[str] + parameters: "MappingProxyType[str, str]" + filename: Optional[str] + + +class _RequestInfo(NamedTuple): + url: URL + method: str + headers: "CIMultiDictProxy[str]" + real_url: URL + + +class RequestInfo(_RequestInfo): + + def __new__( + cls, + url: URL, + method: str, + headers: "CIMultiDictProxy[str]", + real_url: Union[URL, _SENTINEL] = sentinel, + ) -> "RequestInfo": + """Create a new RequestInfo instance. + + For backwards compatibility, the real_url parameter is optional. + """ + return tuple.__new__( + cls, (url, method, headers, url if real_url is sentinel else real_url) + ) + + +class Fingerprint: + HASHFUNC_BY_DIGESTLEN = { + 16: md5, + 20: sha1, + 32: sha256, + } + + def __init__(self, fingerprint: bytes) -> None: + digestlen = len(fingerprint) + hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen) + if not hashfunc: + raise ValueError("fingerprint has invalid length") + elif hashfunc is md5 or hashfunc is sha1: + raise ValueError("md5 and sha1 are insecure and not supported. Use sha256.") + self._hashfunc = hashfunc + self._fingerprint = fingerprint + + @property + def fingerprint(self) -> bytes: + return self._fingerprint + + def check(self, transport: asyncio.Transport) -> None: + if not transport.get_extra_info("sslcontext"): + return + sslobj = transport.get_extra_info("ssl_object") + cert = sslobj.getpeercert(binary_form=True) + got = self._hashfunc(cert).digest() + if got != self._fingerprint: + host, port, *_ = transport.get_extra_info("peername") + raise ServerFingerprintMismatch(self._fingerprint, got, host, port) + + +if ssl is not None: + SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None)) +else: # pragma: no cover + SSL_ALLOWED_TYPES = (bool, type(None)) + + +def _merge_ssl_params( + ssl: Union["SSLContext", bool, Fingerprint], + verify_ssl: Optional[bool], + ssl_context: Optional["SSLContext"], + fingerprint: Optional[bytes], +) -> Union["SSLContext", bool, Fingerprint]: + if ssl is None: + ssl = True # Double check for backwards compatibility + if verify_ssl is not None and not verify_ssl: + warnings.warn( + "verify_ssl is deprecated, use ssl=False instead", + DeprecationWarning, + stacklevel=3, + ) + if ssl is not True: + raise ValueError( + "verify_ssl, ssl_context, fingerprint and ssl " + "parameters are mutually exclusive" + ) + else: + ssl = False + if ssl_context is not None: + warnings.warn( + "ssl_context is deprecated, use ssl=context instead", + DeprecationWarning, + stacklevel=3, + ) + if ssl is not True: + raise ValueError( + "verify_ssl, ssl_context, fingerprint and ssl " + "parameters are mutually exclusive" + ) + else: + ssl = ssl_context + if fingerprint is not None: + warnings.warn( + "fingerprint is deprecated, use ssl=Fingerprint(fingerprint) instead", + DeprecationWarning, + stacklevel=3, + ) + if ssl is not True: + raise ValueError( + "verify_ssl, ssl_context, fingerprint and ssl " + "parameters are mutually exclusive" + ) + else: + ssl = Fingerprint(fingerprint) + if not isinstance(ssl, SSL_ALLOWED_TYPES): + raise TypeError( + "ssl should be SSLContext, bool, Fingerprint or None, " + "got {!r} instead.".format(ssl) + ) + return ssl + + +_SSL_SCHEMES = frozenset(("https", "wss")) + + +# ConnectionKey is a NamedTuple because it is used as a key in a dict +# and a set in the connector. Since a NamedTuple is a tuple it uses +# the fast native tuple __hash__ and __eq__ implementation in CPython. +class ConnectionKey(NamedTuple): + # the key should contain an information about used proxy / TLS + # to prevent reusing wrong connections from a pool + host: str + port: Optional[int] + is_ssl: bool + ssl: Union[SSLContext, bool, Fingerprint] + proxy: Optional[URL] + proxy_auth: Optional[BasicAuth] + proxy_headers_hash: Optional[int] # hash(CIMultiDict) + + +def _is_expected_content_type( + response_content_type: str, expected_content_type: str +) -> bool: + if expected_content_type == "application/json": + return json_re.match(response_content_type) is not None + return expected_content_type in response_content_type + + +def _warn_if_unclosed_payload(payload: payload.Payload, stacklevel: int = 2) -> None: + """Warn if the payload is not closed. + + Callers must check that the body is a Payload before calling this method. + + Args: + payload: The payload to check + stacklevel: Stack level for the warning (default 2 for direct callers) + """ + if not payload.autoclose and not payload.consumed: + warnings.warn( + "The previous request body contains unclosed resources. " + "Use await request.update_body() instead of setting request.body " + "directly to properly close resources and avoid leaks.", + ResourceWarning, + stacklevel=stacklevel, + ) + + +class ClientResponse(HeadersMixin): + + # Some of these attributes are None when created, + # but will be set by the start() method. + # As the end user will likely never see the None values, we cheat the types below. + # from the Status-Line of the response + version: Optional[HttpVersion] = None # HTTP-Version + status: int = None # type: ignore[assignment] # Status-Code + reason: Optional[str] = None # Reason-Phrase + + content: StreamReader = None # type: ignore[assignment] # Payload stream + _body: Optional[bytes] = None + _headers: CIMultiDictProxy[str] = None # type: ignore[assignment] + _history: Tuple["ClientResponse", ...] = () + _raw_headers: RawHeaders = None # type: ignore[assignment] + + _connection: Optional["Connection"] = None # current connection + _cookies: Optional[SimpleCookie] = None + _raw_cookie_headers: Optional[Tuple[str, ...]] = None + _continue: Optional["asyncio.Future[bool]"] = None + _source_traceback: Optional[traceback.StackSummary] = None + _session: Optional["ClientSession"] = None + # set up by ClientRequest after ClientResponse object creation + # post-init stage allows to not change ctor signature + _closed = True # to allow __del__ for non-initialized properly response + _released = False + _in_context = False + + _resolve_charset: Callable[["ClientResponse", bytes], str] = lambda *_: "utf-8" + + __writer: Optional["asyncio.Task[None]"] = None + + def __init__( + self, + method: str, + url: URL, + *, + writer: "Optional[asyncio.Task[None]]", + continue100: Optional["asyncio.Future[bool]"], + timer: BaseTimerContext, + request_info: RequestInfo, + traces: List["Trace"], + loop: asyncio.AbstractEventLoop, + session: "ClientSession", + ) -> None: + # URL forbids subclasses, so a simple type check is enough. + assert type(url) is URL + + self.method = method + + self._real_url = url + self._url = url.with_fragment(None) if url.raw_fragment else url + if writer is not None: + self._writer = writer + if continue100 is not None: + self._continue = continue100 + self._request_info = request_info + self._timer = timer if timer is not None else TimerNoop() + self._cache: Dict[str, Any] = {} + self._traces = traces + self._loop = loop + # Save reference to _resolve_charset, so that get_encoding() will still + # work after the response has finished reading the body. + # TODO: Fix session=None in tests (see ClientRequest.__init__). + if session is not None: + # store a reference to session #1985 + self._session = session + self._resolve_charset = session._resolve_charset + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + def __reset_writer(self, _: object = None) -> None: + self.__writer = None + + @property + def _writer(self) -> Optional["asyncio.Task[None]"]: + """The writer task for streaming data. + + _writer is only provided for backwards compatibility + for subclasses that may need to access it. + """ + return self.__writer + + @_writer.setter + def _writer(self, writer: Optional["asyncio.Task[None]"]) -> None: + """Set the writer task for streaming data.""" + if self.__writer is not None: + self.__writer.remove_done_callback(self.__reset_writer) + self.__writer = writer + if writer is None: + return + if writer.done(): + # The writer is already done, so we can clear it immediately. + self.__writer = None + else: + writer.add_done_callback(self.__reset_writer) + + @property + def cookies(self) -> SimpleCookie: + if self._cookies is None: + if self._raw_cookie_headers is not None: + # Parse cookies for response.cookies (SimpleCookie for backward compatibility) + cookies = SimpleCookie() + # Use parse_set_cookie_headers for more lenient parsing that handles + # malformed cookies better than SimpleCookie.load + cookies.update(parse_set_cookie_headers(self._raw_cookie_headers)) + self._cookies = cookies + else: + self._cookies = SimpleCookie() + return self._cookies + + @cookies.setter + def cookies(self, cookies: SimpleCookie) -> None: + self._cookies = cookies + # Generate raw cookie headers from the SimpleCookie + if cookies: + self._raw_cookie_headers = tuple( + morsel.OutputString() for morsel in cookies.values() + ) + else: + self._raw_cookie_headers = None + + @reify + def url(self) -> URL: + return self._url + + @reify + def url_obj(self) -> URL: + warnings.warn("Deprecated, use .url #1654", DeprecationWarning, stacklevel=2) + return self._url + + @reify + def real_url(self) -> URL: + return self._real_url + + @reify + def host(self) -> str: + assert self._url.host is not None + return self._url.host + + @reify + def headers(self) -> "CIMultiDictProxy[str]": + return self._headers + + @reify + def raw_headers(self) -> RawHeaders: + return self._raw_headers + + @reify + def request_info(self) -> RequestInfo: + return self._request_info + + @reify + def content_disposition(self) -> Optional[ContentDisposition]: + raw = self._headers.get(hdrs.CONTENT_DISPOSITION) + if raw is None: + return None + disposition_type, params_dct = multipart.parse_content_disposition(raw) + params = MappingProxyType(params_dct) + filename = multipart.content_disposition_filename(params) + return ContentDisposition(disposition_type, params, filename) + + def __del__(self, _warnings: Any = warnings) -> None: + if self._closed: + return + + if self._connection is not None: + self._connection.release() + self._cleanup_writer() + + if self._loop.get_debug(): + kwargs = {"source": self} + _warnings.warn(f"Unclosed response {self!r}", ResourceWarning, **kwargs) + context = {"client_response": self, "message": "Unclosed response"} + if self._source_traceback: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def __repr__(self) -> str: + out = io.StringIO() + ascii_encodable_url = str(self.url) + if self.reason: + ascii_encodable_reason = self.reason.encode( + "ascii", "backslashreplace" + ).decode("ascii") + else: + ascii_encodable_reason = "None" + print( + "".format( + ascii_encodable_url, self.status, ascii_encodable_reason + ), + file=out, + ) + print(self.headers, file=out) + return out.getvalue() + + @property + def connection(self) -> Optional["Connection"]: + return self._connection + + @reify + def history(self) -> Tuple["ClientResponse", ...]: + """A sequence of of responses, if redirects occurred.""" + return self._history + + @reify + def links(self) -> "MultiDictProxy[MultiDictProxy[Union[str, URL]]]": + links_str = ", ".join(self.headers.getall("link", [])) + + if not links_str: + return MultiDictProxy(MultiDict()) + + links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict() + + for val in re.split(r",(?=\s*<)", links_str): + match = re.match(r"\s*<(.*)>(.*)", val) + if match is None: # pragma: no cover + # the check exists to suppress mypy error + continue + url, params_str = match.groups() + params = params_str.split(";")[1:] + + link: MultiDict[Union[str, URL]] = MultiDict() + + for param in params: + match = re.match(r"^\s*(\S*)\s*=\s*(['\"]?)(.*?)(\2)\s*$", param, re.M) + if match is None: # pragma: no cover + # the check exists to suppress mypy error + continue + key, _, value, _ = match.groups() + + link.add(key, value) + + key = link.get("rel", url) + + link.add("url", self.url.join(URL(url))) + + links.add(str(key), MultiDictProxy(link)) + + return MultiDictProxy(links) + + async def start(self, connection: "Connection") -> "ClientResponse": + """Start response processing.""" + self._closed = False + self._protocol = connection.protocol + self._connection = connection + + with self._timer: + while True: + # read response + try: + protocol = self._protocol + message, payload = await protocol.read() # type: ignore[union-attr] + except http.HttpProcessingError as exc: + raise ClientResponseError( + self.request_info, + self.history, + status=exc.code, + message=exc.message, + headers=exc.headers, + ) from exc + + if message.code < 100 or message.code > 199 or message.code == 101: + break + + if self._continue is not None: + set_result(self._continue, True) + self._continue = None + + # payload eof handler + payload.on_eof(self._response_eof) + + # response status + self.version = message.version + self.status = message.code + self.reason = message.reason + + # headers + self._headers = message.headers # type is CIMultiDictProxy + self._raw_headers = message.raw_headers # type is Tuple[bytes, bytes] + + # payload + self.content = payload + + # cookies + if cookie_hdrs := self.headers.getall(hdrs.SET_COOKIE, ()): + # Store raw cookie headers for CookieJar + self._raw_cookie_headers = tuple(cookie_hdrs) + return self + + def _response_eof(self) -> None: + if self._closed: + return + + # protocol could be None because connection could be detached + protocol = self._connection and self._connection.protocol + if protocol is not None and protocol.upgraded: + return + + self._closed = True + self._cleanup_writer() + self._release_connection() + + @property + def closed(self) -> bool: + return self._closed + + def close(self) -> None: + if not self._released: + self._notify_content() + + self._closed = True + if self._loop is None or self._loop.is_closed(): + return + + self._cleanup_writer() + if self._connection is not None: + self._connection.close() + self._connection = None + + def release(self) -> Any: + if not self._released: + self._notify_content() + + self._closed = True + + self._cleanup_writer() + self._release_connection() + return noop() + + @property + def ok(self) -> bool: + """Returns ``True`` if ``status`` is less than ``400``, ``False`` if not. + + This is **not** a check for ``200 OK`` but a check that the response + status is under 400. + """ + return 400 > self.status + + def raise_for_status(self) -> None: + if not self.ok: + # reason should always be not None for a started response + assert self.reason is not None + + # If we're in a context we can rely on __aexit__() to release as the + # exception propagates. + if not self._in_context: + self.release() + + raise ClientResponseError( + self.request_info, + self.history, + status=self.status, + message=self.reason, + headers=self.headers, + ) + + def _release_connection(self) -> None: + if self._connection is not None: + if self.__writer is None: + self._connection.release() + self._connection = None + else: + self.__writer.add_done_callback(lambda f: self._release_connection()) + + async def _wait_released(self) -> None: + if self.__writer is not None: + try: + await self.__writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + self._release_connection() + + def _cleanup_writer(self) -> None: + if self.__writer is not None: + self.__writer.cancel() + self._session = None + + def _notify_content(self) -> None: + content = self.content + if content and content.exception() is None: + set_exception(content, _CONNECTION_CLOSED_EXCEPTION) + self._released = True + + async def wait_for_close(self) -> None: + if self.__writer is not None: + try: + await self.__writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + self.release() + + async def read(self) -> bytes: + """Read response payload.""" + if self._body is None: + try: + self._body = await self.content.read() + for trace in self._traces: + await trace.send_response_chunk_received( + self.method, self.url, self._body + ) + except BaseException: + self.close() + raise + elif self._released: # Response explicitly released + raise ClientConnectionError("Connection closed") + + protocol = self._connection and self._connection.protocol + if protocol is None or not protocol.upgraded: + await self._wait_released() # Underlying connection released + return self._body + + def get_encoding(self) -> str: + ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() + mimetype = helpers.parse_mimetype(ctype) + + encoding = mimetype.parameters.get("charset") + if encoding: + with contextlib.suppress(LookupError, ValueError): + return codecs.lookup(encoding).name + + if mimetype.type == "application" and ( + mimetype.subtype == "json" or mimetype.subtype == "rdap" + ): + # RFC 7159 states that the default encoding is UTF-8. + # RFC 7483 defines application/rdap+json + return "utf-8" + + if self._body is None: + raise RuntimeError( + "Cannot compute fallback encoding of a not yet read body" + ) + + return self._resolve_charset(self, self._body) + + async def text(self, encoding: Optional[str] = None, errors: str = "strict") -> str: + """Read response payload and decode.""" + if self._body is None: + await self.read() + + if encoding is None: + encoding = self.get_encoding() + + return self._body.decode(encoding, errors=errors) # type: ignore[union-attr] + + async def json( + self, + *, + encoding: Optional[str] = None, + loads: JSONDecoder = DEFAULT_JSON_DECODER, + content_type: Optional[str] = "application/json", + ) -> Any: + """Read and decodes JSON response.""" + if self._body is None: + await self.read() + + if content_type: + ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() + if not _is_expected_content_type(ctype, content_type): + raise ContentTypeError( + self.request_info, + self.history, + status=self.status, + message=( + "Attempt to decode JSON with unexpected mimetype: %s" % ctype + ), + headers=self.headers, + ) + + stripped = self._body.strip() # type: ignore[union-attr] + if not stripped: + return None + + if encoding is None: + encoding = self.get_encoding() + + return loads(stripped.decode(encoding)) + + async def __aenter__(self) -> "ClientResponse": + self._in_context = True + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + self._in_context = False + # similar to _RequestContextManager, we do not need to check + # for exceptions, response object can close connection + # if state is broken + self.release() + await self.wait_for_close() + + +class ClientRequest: + GET_METHODS = { + hdrs.METH_GET, + hdrs.METH_HEAD, + hdrs.METH_OPTIONS, + hdrs.METH_TRACE, + } + POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT} + ALL_METHODS = GET_METHODS.union(POST_METHODS).union({hdrs.METH_DELETE}) + + DEFAULT_HEADERS = { + hdrs.ACCEPT: "*/*", + hdrs.ACCEPT_ENCODING: _gen_default_accept_encoding(), + } + + # Type of body depends on PAYLOAD_REGISTRY, which is dynamic. + _body: Union[None, payload.Payload] = None + auth = None + response = None + + __writer: Optional["asyncio.Task[None]"] = None # async task for streaming data + + # These class defaults help create_autospec() work correctly. + # If autospec is improved in future, maybe these can be removed. + url = URL() + method = "GET" + + _continue = None # waiter future for '100 Continue' response + + _skip_auto_headers: Optional["CIMultiDict[None]"] = None + + # N.B. + # Adding __del__ method with self._writer closing doesn't make sense + # because _writer is instance method, thus it keeps a reference to self. + # Until writer has finished finalizer will not be called. + + def __init__( + self, + method: str, + url: URL, + *, + params: Query = None, + headers: Optional[LooseHeaders] = None, + skip_auto_headers: Optional[Iterable[str]] = None, + data: Any = None, + cookies: Optional[LooseCookies] = None, + auth: Optional[BasicAuth] = None, + version: http.HttpVersion = http.HttpVersion11, + compress: Union[str, bool, None] = None, + chunked: Optional[bool] = None, + expect100: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + response_class: Optional[Type["ClientResponse"]] = None, + proxy: Optional[URL] = None, + proxy_auth: Optional[BasicAuth] = None, + timer: Optional[BaseTimerContext] = None, + session: Optional["ClientSession"] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + proxy_headers: Optional[LooseHeaders] = None, + traces: Optional[List["Trace"]] = None, + trust_env: bool = False, + server_hostname: Optional[str] = None, + ): + if loop is None: + loop = asyncio.get_event_loop() + if match := _CONTAINS_CONTROL_CHAR_RE.search(method): + raise ValueError( + f"Method cannot contain non-token characters {method!r} " + f"(found at least {match.group()!r})" + ) + # URL forbids subclasses, so a simple type check is enough. + assert type(url) is URL, url + if proxy is not None: + assert type(proxy) is URL, proxy + # FIXME: session is None in tests only, need to fix tests + # assert session is not None + if TYPE_CHECKING: + assert session is not None + self._session = session + if params: + url = url.extend_query(params) + self.original_url = url + self.url = url.with_fragment(None) if url.raw_fragment else url + self.method = method.upper() + self.chunked = chunked + self.compress = compress + self.loop = loop + self.length = None + if response_class is None: + real_response_class = ClientResponse + else: + real_response_class = response_class + self.response_class: Type[ClientResponse] = real_response_class + self._timer = timer if timer is not None else TimerNoop() + self._ssl = ssl if ssl is not None else True + self.server_hostname = server_hostname + + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + self.update_version(version) + self.update_host(url) + self.update_headers(headers) + self.update_auto_headers(skip_auto_headers) + self.update_cookies(cookies) + self.update_content_encoding(data) + self.update_auth(auth, trust_env) + self.update_proxy(proxy, proxy_auth, proxy_headers) + + self.update_body_from_data(data) + if data is not None or self.method not in self.GET_METHODS: + self.update_transfer_encoding() + self.update_expect_continue(expect100) + self._traces = [] if traces is None else traces + + def __reset_writer(self, _: object = None) -> None: + self.__writer = None + + def _get_content_length(self) -> Optional[int]: + """Extract and validate Content-Length header value. + + Returns parsed Content-Length value or None if not set. + Raises ValueError if header exists but cannot be parsed as an integer. + """ + if hdrs.CONTENT_LENGTH not in self.headers: + return None + + content_length_hdr = self.headers[hdrs.CONTENT_LENGTH] + try: + return int(content_length_hdr) + except ValueError: + raise ValueError( + f"Invalid Content-Length header: {content_length_hdr}" + ) from None + + @property + def skip_auto_headers(self) -> CIMultiDict[None]: + return self._skip_auto_headers or CIMultiDict() + + @property + def _writer(self) -> Optional["asyncio.Task[None]"]: + return self.__writer + + @_writer.setter + def _writer(self, writer: "asyncio.Task[None]") -> None: + if self.__writer is not None: + self.__writer.remove_done_callback(self.__reset_writer) + self.__writer = writer + writer.add_done_callback(self.__reset_writer) + + def is_ssl(self) -> bool: + return self.url.scheme in _SSL_SCHEMES + + @property + def ssl(self) -> Union["SSLContext", bool, Fingerprint]: + return self._ssl + + @property + def connection_key(self) -> ConnectionKey: + if proxy_headers := self.proxy_headers: + h: Optional[int] = hash(tuple(proxy_headers.items())) + else: + h = None + url = self.url + return tuple.__new__( + ConnectionKey, + ( + url.raw_host or "", + url.port, + url.scheme in _SSL_SCHEMES, + self._ssl, + self.proxy, + self.proxy_auth, + h, + ), + ) + + @property + def host(self) -> str: + ret = self.url.raw_host + assert ret is not None + return ret + + @property + def port(self) -> Optional[int]: + return self.url.port + + @property + def body(self) -> Union[payload.Payload, Literal[b""]]: + """Request body.""" + # empty body is represented as bytes for backwards compatibility + return self._body or b"" + + @body.setter + def body(self, value: Any) -> None: + """Set request body with warning for non-autoclose payloads. + + WARNING: This setter must be called from within an event loop and is not + thread-safe. Setting body outside of an event loop may raise RuntimeError + when closing file-based payloads. + + DEPRECATED: Direct assignment to body is deprecated and will be removed + in a future version. Use await update_body() instead for proper resource + management. + """ + # Close existing payload if present + if self._body is not None: + # Warn if the payload needs manual closing + # stacklevel=3: user code -> body setter -> _warn_if_unclosed_payload + _warn_if_unclosed_payload(self._body, stacklevel=3) + # NOTE: In the future, when we remove sync close support, + # this setter will need to be removed and only the async + # update_body() method will be available. For now, we call + # _close() for backwards compatibility. + self._body._close() + self._update_body(value) + + @property + def request_info(self) -> RequestInfo: + headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers) + # These are created on every request, so we use a NamedTuple + # for performance reasons. We don't use the RequestInfo.__new__ + # method because it has a different signature which is provided + # for backwards compatibility only. + return tuple.__new__( + RequestInfo, (self.url, self.method, headers, self.original_url) + ) + + @property + def session(self) -> "ClientSession": + """Return the ClientSession instance. + + This property provides access to the ClientSession that initiated + this request, allowing middleware to make additional requests + using the same session. + """ + return self._session + + def update_host(self, url: URL) -> None: + """Update destination host, port and connection type (ssl).""" + # get host/port + if not url.raw_host: + raise InvalidURL(url) + + # basic auth info + if url.raw_user or url.raw_password: + self.auth = helpers.BasicAuth(url.user or "", url.password or "") + + def update_version(self, version: Union[http.HttpVersion, str]) -> None: + """Convert request version to two elements tuple. + + parser HTTP version '1.1' => (1, 1) + """ + if isinstance(version, str): + v = [part.strip() for part in version.split(".", 1)] + try: + version = http.HttpVersion(int(v[0]), int(v[1])) + except ValueError: + raise ValueError( + f"Can not parse http version number: {version}" + ) from None + self.version = version + + def update_headers(self, headers: Optional[LooseHeaders]) -> None: + """Update request headers.""" + self.headers: CIMultiDict[str] = CIMultiDict() + + # Build the host header + host = self.url.host_port_subcomponent + + # host_port_subcomponent is None when the URL is a relative URL. + # but we know we do not have a relative URL here. + assert host is not None + self.headers[hdrs.HOST] = host + + if not headers: + return + + if isinstance(headers, (dict, MultiDictProxy, MultiDict)): + headers = headers.items() + + for key, value in headers: # type: ignore[misc] + # A special case for Host header + if key in hdrs.HOST_ALL: + self.headers[key] = value + else: + self.headers.add(key, value) + + def update_auto_headers(self, skip_auto_headers: Optional[Iterable[str]]) -> None: + if skip_auto_headers is not None: + self._skip_auto_headers = CIMultiDict( + (hdr, None) for hdr in sorted(skip_auto_headers) + ) + used_headers = self.headers.copy() + used_headers.extend(self._skip_auto_headers) # type: ignore[arg-type] + else: + # Fast path when there are no headers to skip + # which is the most common case. + used_headers = self.headers + + for hdr, val in self.DEFAULT_HEADERS.items(): + if hdr not in used_headers: + self.headers[hdr] = val + + if hdrs.USER_AGENT not in used_headers: + self.headers[hdrs.USER_AGENT] = SERVER_SOFTWARE + + def update_cookies(self, cookies: Optional[LooseCookies]) -> None: + """Update request cookies header.""" + if not cookies: + return + + c = SimpleCookie() + if hdrs.COOKIE in self.headers: + # parse_cookie_header for RFC 6265 compliant Cookie header parsing + c.update(parse_cookie_header(self.headers.get(hdrs.COOKIE, ""))) + del self.headers[hdrs.COOKIE] + + if isinstance(cookies, Mapping): + iter_cookies = cookies.items() + else: + iter_cookies = cookies # type: ignore[assignment] + for name, value in iter_cookies: + if isinstance(value, Morsel): + # Use helper to preserve coded_value exactly as sent by server + c[name] = preserve_morsel_with_coded_value(value) + else: + c[name] = value # type: ignore[assignment] + + self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip() + + def update_content_encoding(self, data: Any) -> None: + """Set request content encoding.""" + if not data: + # Don't compress an empty body. + self.compress = None + return + + if self.headers.get(hdrs.CONTENT_ENCODING): + if self.compress: + raise ValueError( + "compress can not be set if Content-Encoding header is set" + ) + elif self.compress: + if not isinstance(self.compress, str): + self.compress = "deflate" + self.headers[hdrs.CONTENT_ENCODING] = self.compress + self.chunked = True # enable chunked, no need to deal with length + + def update_transfer_encoding(self) -> None: + """Analyze transfer-encoding header.""" + te = self.headers.get(hdrs.TRANSFER_ENCODING, "").lower() + + if "chunked" in te: + if self.chunked: + raise ValueError( + "chunked can not be set " + 'if "Transfer-Encoding: chunked" header is set' + ) + + elif self.chunked: + if hdrs.CONTENT_LENGTH in self.headers: + raise ValueError( + "chunked can not be set if Content-Length header is set" + ) + + self.headers[hdrs.TRANSFER_ENCODING] = "chunked" + + def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> None: + """Set basic auth.""" + if auth is None: + auth = self.auth + if auth is None: + return + + if not isinstance(auth, helpers.BasicAuth): + raise TypeError("BasicAuth() tuple is required instead") + + self.headers[hdrs.AUTHORIZATION] = auth.encode() + + def update_body_from_data(self, body: Any, _stacklevel: int = 3) -> None: + """Update request body from data.""" + if self._body is not None: + _warn_if_unclosed_payload(self._body, stacklevel=_stacklevel) + + if body is None: + self._body = None + # Set Content-Length to 0 when body is None for methods that expect a body + if ( + self.method not in self.GET_METHODS + and not self.chunked + and hdrs.CONTENT_LENGTH not in self.headers + ): + self.headers[hdrs.CONTENT_LENGTH] = "0" + return + + # FormData + maybe_payload = body() if isinstance(body, FormData) else body + + try: + body_payload = payload.PAYLOAD_REGISTRY.get(maybe_payload, disposition=None) + except payload.LookupError: + body_payload = FormData(maybe_payload)() # type: ignore[arg-type] + + self._body = body_payload + # enable chunked encoding if needed + if not self.chunked and hdrs.CONTENT_LENGTH not in self.headers: + if (size := body_payload.size) is not None: + self.headers[hdrs.CONTENT_LENGTH] = str(size) + else: + self.chunked = True + + # copy payload headers + assert body_payload.headers + headers = self.headers + skip_headers = self._skip_auto_headers + for key, value in body_payload.headers.items(): + if key in headers or (skip_headers is not None and key in skip_headers): + continue + headers[key] = value + + def _update_body(self, body: Any) -> None: + """Update request body after its already been set.""" + # Remove existing Content-Length header since body is changing + if hdrs.CONTENT_LENGTH in self.headers: + del self.headers[hdrs.CONTENT_LENGTH] + + # Remove existing Transfer-Encoding header to avoid conflicts + if self.chunked and hdrs.TRANSFER_ENCODING in self.headers: + del self.headers[hdrs.TRANSFER_ENCODING] + + # Now update the body using the existing method + # Called from _update_body, add 1 to stacklevel from caller + self.update_body_from_data(body, _stacklevel=4) + + # Update transfer encoding headers if needed (same logic as __init__) + if body is not None or self.method not in self.GET_METHODS: + self.update_transfer_encoding() + + async def update_body(self, body: Any) -> None: + """ + Update request body and close previous payload if needed. + + This method safely updates the request body by first closing any existing + payload to prevent resource leaks, then setting the new body. + + IMPORTANT: Always use this method instead of setting request.body directly. + Direct assignment to request.body will leak resources if the previous body + contains file handles, streams, or other resources that need cleanup. + + Args: + body: The new body content. Can be: + - bytes/bytearray: Raw binary data + - str: Text data (will be encoded using charset from Content-Type) + - FormData: Form data that will be encoded as multipart/form-data + - Payload: A pre-configured payload object + - AsyncIterable: An async iterable of bytes chunks + - File-like object: Will be read and sent as binary data + - None: Clears the body + + Usage: + # CORRECT: Use update_body + await request.update_body(b"new request data") + + # WRONG: Don't set body directly + # request.body = b"new request data" # This will leak resources! + + # Update with form data + form_data = FormData() + form_data.add_field('field', 'value') + await request.update_body(form_data) + + # Clear body + await request.update_body(None) + + Note: + This method is async because it may need to close file handles or + other resources associated with the previous payload. Always await + this method to ensure proper cleanup. + + Warning: + Setting request.body directly is highly discouraged and can lead to: + - Resource leaks (unclosed file handles, streams) + - Memory leaks (unreleased buffers) + - Unexpected behavior with streaming payloads + + It is not recommended to change the payload type in middleware. If the + body was already set (e.g., as bytes), it's best to keep the same type + rather than converting it (e.g., to str) as this may result in unexpected + behavior. + + See Also: + - update_body_from_data: Synchronous body update without cleanup + - body property: Direct body access (STRONGLY DISCOURAGED) + + """ + # Close existing payload if it exists and needs closing + if self._body is not None: + await self._body.close() + self._update_body(body) + + def update_expect_continue(self, expect: bool = False) -> None: + if expect: + self.headers[hdrs.EXPECT] = "100-continue" + elif ( + hdrs.EXPECT in self.headers + and self.headers[hdrs.EXPECT].lower() == "100-continue" + ): + expect = True + + if expect: + self._continue = self.loop.create_future() + + def update_proxy( + self, + proxy: Optional[URL], + proxy_auth: Optional[BasicAuth], + proxy_headers: Optional[LooseHeaders], + ) -> None: + self.proxy = proxy + if proxy is None: + self.proxy_auth = None + self.proxy_headers = None + return + + if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth): + raise ValueError("proxy_auth must be None or BasicAuth() tuple") + self.proxy_auth = proxy_auth + + if proxy_headers is not None and not isinstance( + proxy_headers, (MultiDict, MultiDictProxy) + ): + proxy_headers = CIMultiDict(proxy_headers) + self.proxy_headers = proxy_headers + + async def write_bytes( + self, + writer: AbstractStreamWriter, + conn: "Connection", + content_length: Optional[int] = None, + ) -> None: + """ + Write the request body to the connection stream. + + This method handles writing different types of request bodies: + 1. Payload objects (using their specialized write_with_length method) + 2. Bytes/bytearray objects + 3. Iterable body content + + Args: + writer: The stream writer to write the body to + conn: The connection being used for this request + content_length: Optional maximum number of bytes to write from the body + (None means write the entire body) + + The method properly handles: + - Waiting for 100-Continue responses if required + - Content length constraints for chunked encoding + - Error handling for network issues, cancellation, and other exceptions + - Signaling EOF and timeout management + + Raises: + ClientOSError: When there's an OS-level error writing the body + ClientConnectionError: When there's a general connection error + asyncio.CancelledError: When the operation is cancelled + + """ + # 100 response + if self._continue is not None: + # Force headers to be sent before waiting for 100-continue + writer.send_headers() + await writer.drain() + await self._continue + + protocol = conn.protocol + assert protocol is not None + try: + # This should be a rare case but the + # self._body can be set to None while + # the task is being started or we wait above + # for the 100-continue response. + # The more likely case is we have an empty + # payload, but 100-continue is still expected. + if self._body is not None: + await self._body.write_with_length(writer, content_length) + except OSError as underlying_exc: + reraised_exc = underlying_exc + + # Distinguish between timeout and other OS errors for better error reporting + exc_is_not_timeout = underlying_exc.errno is not None or not isinstance( + underlying_exc, asyncio.TimeoutError + ) + if exc_is_not_timeout: + reraised_exc = ClientOSError( + underlying_exc.errno, + f"Can not write request body for {self.url !s}", + ) + + set_exception(protocol, reraised_exc, underlying_exc) + except asyncio.CancelledError: + # Body hasn't been fully sent, so connection can't be reused + conn.close() + raise + except Exception as underlying_exc: + set_exception( + protocol, + ClientConnectionError( + "Failed to send bytes into the underlying connection " + f"{conn !s}: {underlying_exc!r}", + ), + underlying_exc, + ) + else: + # Successfully wrote the body, signal EOF and start response timeout + await writer.write_eof() + protocol.start_timeout() + + async def send(self, conn: "Connection") -> "ClientResponse": + # Specify request target: + # - CONNECT request must send authority form URI + # - not CONNECT proxy must send absolute form URI + # - most common is origin form URI + if self.method == hdrs.METH_CONNECT: + connect_host = self.url.host_subcomponent + assert connect_host is not None + path = f"{connect_host}:{self.url.port}" + elif self.proxy and not self.is_ssl(): + path = str(self.url) + else: + path = self.url.raw_path_qs + + protocol = conn.protocol + assert protocol is not None + writer = StreamWriter( + protocol, + self.loop, + on_chunk_sent=( + functools.partial(self._on_chunk_request_sent, self.method, self.url) + if self._traces + else None + ), + on_headers_sent=( + functools.partial(self._on_headers_request_sent, self.method, self.url) + if self._traces + else None + ), + ) + + if self.compress: + writer.enable_compression(self.compress) # type: ignore[arg-type] + + if self.chunked is not None: + writer.enable_chunking() + + # set default content-type + if ( + self.method in self.POST_METHODS + and ( + self._skip_auto_headers is None + or hdrs.CONTENT_TYPE not in self._skip_auto_headers + ) + and hdrs.CONTENT_TYPE not in self.headers + ): + self.headers[hdrs.CONTENT_TYPE] = "application/octet-stream" + + v = self.version + if hdrs.CONNECTION not in self.headers: + if conn._connector.force_close: + if v == HttpVersion11: + self.headers[hdrs.CONNECTION] = "close" + elif v == HttpVersion10: + self.headers[hdrs.CONNECTION] = "keep-alive" + + # status + headers + status_line = f"{self.method} {path} HTTP/{v.major}.{v.minor}" + + # Buffer headers for potential coalescing with body + await writer.write_headers(status_line, self.headers) + + task: Optional["asyncio.Task[None]"] + if self._body or self._continue is not None or protocol.writing_paused: + coro = self.write_bytes(writer, conn, self._get_content_length()) + if sys.version_info >= (3, 12): + # Optimization for Python 3.12, try to write + # bytes immediately to avoid having to schedule + # the task on the event loop. + task = asyncio.Task(coro, loop=self.loop, eager_start=True) + else: + task = self.loop.create_task(coro) + if task.done(): + task = None + else: + self._writer = task + else: + # We have nothing to write because + # - there is no body + # - the protocol does not have writing paused + # - we are not waiting for a 100-continue response + protocol.start_timeout() + writer.set_eof() + task = None + response_class = self.response_class + assert response_class is not None + self.response = response_class( + self.method, + self.original_url, + writer=task, + continue100=self._continue, + timer=self._timer, + request_info=self.request_info, + traces=self._traces, + loop=self.loop, + session=self._session, + ) + return self.response + + async def close(self) -> None: + if self.__writer is not None: + try: + await self.__writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + + def terminate(self) -> None: + if self.__writer is not None: + if not self.loop.is_closed(): + self.__writer.cancel() + self.__writer.remove_done_callback(self.__reset_writer) + self.__writer = None + + async def _on_chunk_request_sent(self, method: str, url: URL, chunk: bytes) -> None: + for trace in self._traces: + await trace.send_request_chunk_sent(method, url, chunk) + + async def _on_headers_request_sent( + self, method: str, url: URL, headers: "CIMultiDict[str]" + ) -> None: + for trace in self._traces: + await trace.send_request_headers(method, url, headers) diff --git a/venv/lib/python3.12/site-packages/aiohttp/client_ws.py b/venv/lib/python3.12/site-packages/aiohttp/client_ws.py new file mode 100644 index 00000000..daa57d19 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/client_ws.py @@ -0,0 +1,428 @@ +"""WebSocket client for asyncio.""" + +import asyncio +import sys +from types import TracebackType +from typing import Any, Optional, Type, cast + +import attr + +from ._websocket.reader import WebSocketDataQueue +from .client_exceptions import ClientError, ServerTimeoutError, WSMessageTypeError +from .client_reqrep import ClientResponse +from .helpers import calculate_timeout_when, set_result +from .http import ( + WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE, + WebSocketError, + WSCloseCode, + WSMessage, + WSMsgType, +) +from .http_websocket import _INTERNAL_RECEIVE_TYPES, WebSocketWriter +from .streams import EofStream +from .typedefs import ( + DEFAULT_JSON_DECODER, + DEFAULT_JSON_ENCODER, + JSONDecoder, + JSONEncoder, +) + +if sys.version_info >= (3, 11): + import asyncio as async_timeout +else: + import async_timeout + + +@attr.s(frozen=True, slots=True) +class ClientWSTimeout: + ws_receive = attr.ib(type=Optional[float], default=None) + ws_close = attr.ib(type=Optional[float], default=None) + + +DEFAULT_WS_CLIENT_TIMEOUT = ClientWSTimeout(ws_receive=None, ws_close=10.0) + + +class ClientWebSocketResponse: + def __init__( + self, + reader: WebSocketDataQueue, + writer: WebSocketWriter, + protocol: Optional[str], + response: ClientResponse, + timeout: ClientWSTimeout, + autoclose: bool, + autoping: bool, + loop: asyncio.AbstractEventLoop, + *, + heartbeat: Optional[float] = None, + compress: int = 0, + client_notakeover: bool = False, + ) -> None: + self._response = response + self._conn = response.connection + + self._writer = writer + self._reader = reader + self._protocol = protocol + self._closed = False + self._closing = False + self._close_code: Optional[int] = None + self._timeout = timeout + self._autoclose = autoclose + self._autoping = autoping + self._heartbeat = heartbeat + self._heartbeat_cb: Optional[asyncio.TimerHandle] = None + self._heartbeat_when: float = 0.0 + if heartbeat is not None: + self._pong_heartbeat = heartbeat / 2.0 + self._pong_response_cb: Optional[asyncio.TimerHandle] = None + self._loop = loop + self._waiting: bool = False + self._close_wait: Optional[asyncio.Future[None]] = None + self._exception: Optional[BaseException] = None + self._compress = compress + self._client_notakeover = client_notakeover + self._ping_task: Optional[asyncio.Task[None]] = None + + self._reset_heartbeat() + + def _cancel_heartbeat(self) -> None: + self._cancel_pong_response_cb() + if self._heartbeat_cb is not None: + self._heartbeat_cb.cancel() + self._heartbeat_cb = None + if self._ping_task is not None: + self._ping_task.cancel() + self._ping_task = None + + def _cancel_pong_response_cb(self) -> None: + if self._pong_response_cb is not None: + self._pong_response_cb.cancel() + self._pong_response_cb = None + + def _reset_heartbeat(self) -> None: + if self._heartbeat is None: + return + self._cancel_pong_response_cb() + loop = self._loop + assert loop is not None + conn = self._conn + timeout_ceil_threshold = ( + conn._connector._timeout_ceil_threshold if conn is not None else 5 + ) + now = loop.time() + when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold) + self._heartbeat_when = when + if self._heartbeat_cb is None: + # We do not cancel the previous heartbeat_cb here because + # it generates a significant amount of TimerHandle churn + # which causes asyncio to rebuild the heap frequently. + # Instead _send_heartbeat() will reschedule the next + # heartbeat if it fires too early. + self._heartbeat_cb = loop.call_at(when, self._send_heartbeat) + + def _send_heartbeat(self) -> None: + self._heartbeat_cb = None + loop = self._loop + now = loop.time() + if now < self._heartbeat_when: + # Heartbeat fired too early, reschedule + self._heartbeat_cb = loop.call_at( + self._heartbeat_when, self._send_heartbeat + ) + return + + conn = self._conn + timeout_ceil_threshold = ( + conn._connector._timeout_ceil_threshold if conn is not None else 5 + ) + when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold) + self._cancel_pong_response_cb() + self._pong_response_cb = loop.call_at(when, self._pong_not_received) + + coro = self._writer.send_frame(b"", WSMsgType.PING) + if sys.version_info >= (3, 12): + # Optimization for Python 3.12, try to send the ping + # immediately to avoid having to schedule + # the task on the event loop. + ping_task = asyncio.Task(coro, loop=loop, eager_start=True) + else: + ping_task = loop.create_task(coro) + + if not ping_task.done(): + self._ping_task = ping_task + ping_task.add_done_callback(self._ping_task_done) + else: + self._ping_task_done(ping_task) + + def _ping_task_done(self, task: "asyncio.Task[None]") -> None: + """Callback for when the ping task completes.""" + if not task.cancelled() and (exc := task.exception()): + self._handle_ping_pong_exception(exc) + self._ping_task = None + + def _pong_not_received(self) -> None: + self._handle_ping_pong_exception( + ServerTimeoutError(f"No PONG received after {self._pong_heartbeat} seconds") + ) + + def _handle_ping_pong_exception(self, exc: BaseException) -> None: + """Handle exceptions raised during ping/pong processing.""" + if self._closed: + return + self._set_closed() + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._exception = exc + self._response.close() + if self._waiting and not self._closing: + self._reader.feed_data(WSMessage(WSMsgType.ERROR, exc, None), 0) + + def _set_closed(self) -> None: + """Set the connection to closed. + + Cancel any heartbeat timers and set the closed flag. + """ + self._closed = True + self._cancel_heartbeat() + + def _set_closing(self) -> None: + """Set the connection to closing. + + Cancel any heartbeat timers and set the closing flag. + """ + self._closing = True + self._cancel_heartbeat() + + @property + def closed(self) -> bool: + return self._closed + + @property + def close_code(self) -> Optional[int]: + return self._close_code + + @property + def protocol(self) -> Optional[str]: + return self._protocol + + @property + def compress(self) -> int: + return self._compress + + @property + def client_notakeover(self) -> bool: + return self._client_notakeover + + def get_extra_info(self, name: str, default: Any = None) -> Any: + """extra info from connection transport""" + conn = self._response.connection + if conn is None: + return default + transport = conn.transport + if transport is None: + return default + return transport.get_extra_info(name, default) + + def exception(self) -> Optional[BaseException]: + return self._exception + + async def ping(self, message: bytes = b"") -> None: + await self._writer.send_frame(message, WSMsgType.PING) + + async def pong(self, message: bytes = b"") -> None: + await self._writer.send_frame(message, WSMsgType.PONG) + + async def send_frame( + self, message: bytes, opcode: WSMsgType, compress: Optional[int] = None + ) -> None: + """Send a frame over the websocket.""" + await self._writer.send_frame(message, opcode, compress) + + async def send_str(self, data: str, compress: Optional[int] = None) -> None: + if not isinstance(data, str): + raise TypeError("data argument must be str (%r)" % type(data)) + await self._writer.send_frame( + data.encode("utf-8"), WSMsgType.TEXT, compress=compress + ) + + async def send_bytes(self, data: bytes, compress: Optional[int] = None) -> None: + if not isinstance(data, (bytes, bytearray, memoryview)): + raise TypeError("data argument must be byte-ish (%r)" % type(data)) + await self._writer.send_frame(data, WSMsgType.BINARY, compress=compress) + + async def send_json( + self, + data: Any, + compress: Optional[int] = None, + *, + dumps: JSONEncoder = DEFAULT_JSON_ENCODER, + ) -> None: + await self.send_str(dumps(data), compress=compress) + + async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bool: + # we need to break `receive()` cycle first, + # `close()` may be called from different task + if self._waiting and not self._closing: + assert self._loop is not None + self._close_wait = self._loop.create_future() + self._set_closing() + self._reader.feed_data(WS_CLOSING_MESSAGE, 0) + await self._close_wait + + if self._closed: + return False + + self._set_closed() + try: + await self._writer.close(code, message) + except asyncio.CancelledError: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._response.close() + raise + except Exception as exc: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._exception = exc + self._response.close() + return True + + if self._close_code: + self._response.close() + return True + + while True: + try: + async with async_timeout.timeout(self._timeout.ws_close): + msg = await self._reader.read() + except asyncio.CancelledError: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._response.close() + raise + except Exception as exc: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._exception = exc + self._response.close() + return True + + if msg.type is WSMsgType.CLOSE: + self._close_code = msg.data + self._response.close() + return True + + async def receive(self, timeout: Optional[float] = None) -> WSMessage: + receive_timeout = timeout or self._timeout.ws_receive + + while True: + if self._waiting: + raise RuntimeError("Concurrent call to receive() is not allowed") + + if self._closed: + return WS_CLOSED_MESSAGE + elif self._closing: + await self.close() + return WS_CLOSED_MESSAGE + + try: + self._waiting = True + try: + if receive_timeout: + # Entering the context manager and creating + # Timeout() object can take almost 50% of the + # run time in this loop so we avoid it if + # there is no read timeout. + async with async_timeout.timeout(receive_timeout): + msg = await self._reader.read() + else: + msg = await self._reader.read() + self._reset_heartbeat() + finally: + self._waiting = False + if self._close_wait: + set_result(self._close_wait, None) + except (asyncio.CancelledError, asyncio.TimeoutError): + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + raise + except EofStream: + self._close_code = WSCloseCode.OK + await self.close() + return WSMessage(WSMsgType.CLOSED, None, None) + except ClientError: + # Likely ServerDisconnectedError when connection is lost + self._set_closed() + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + return WS_CLOSED_MESSAGE + except WebSocketError as exc: + self._close_code = exc.code + await self.close(code=exc.code) + return WSMessage(WSMsgType.ERROR, exc, None) + except Exception as exc: + self._exception = exc + self._set_closing() + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + await self.close() + return WSMessage(WSMsgType.ERROR, exc, None) + + if msg.type not in _INTERNAL_RECEIVE_TYPES: + # If its not a close/closing/ping/pong message + # we can return it immediately + return msg + + if msg.type is WSMsgType.CLOSE: + self._set_closing() + self._close_code = msg.data + if not self._closed and self._autoclose: + await self.close() + elif msg.type is WSMsgType.CLOSING: + self._set_closing() + elif msg.type is WSMsgType.PING and self._autoping: + await self.pong(msg.data) + continue + elif msg.type is WSMsgType.PONG and self._autoping: + continue + + return msg + + async def receive_str(self, *, timeout: Optional[float] = None) -> str: + msg = await self.receive(timeout) + if msg.type is not WSMsgType.TEXT: + raise WSMessageTypeError( + f"Received message {msg.type}:{msg.data!r} is not WSMsgType.TEXT" + ) + return cast(str, msg.data) + + async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes: + msg = await self.receive(timeout) + if msg.type is not WSMsgType.BINARY: + raise WSMessageTypeError( + f"Received message {msg.type}:{msg.data!r} is not WSMsgType.BINARY" + ) + return cast(bytes, msg.data) + + async def receive_json( + self, + *, + loads: JSONDecoder = DEFAULT_JSON_DECODER, + timeout: Optional[float] = None, + ) -> Any: + data = await self.receive_str(timeout=timeout) + return loads(data) + + def __aiter__(self) -> "ClientWebSocketResponse": + return self + + async def __anext__(self) -> WSMessage: + msg = await self.receive() + if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED): + raise StopAsyncIteration + return msg + + async def __aenter__(self) -> "ClientWebSocketResponse": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + await self.close() diff --git a/venv/lib/python3.12/site-packages/aiohttp/compression_utils.py b/venv/lib/python3.12/site-packages/aiohttp/compression_utils.py new file mode 100644 index 00000000..e478d24c --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/compression_utils.py @@ -0,0 +1,348 @@ +import asyncio +import sys +import zlib +from abc import ABC, abstractmethod +from concurrent.futures import Executor +from typing import Any, Final, Optional, Protocol, TypedDict, cast + +if sys.version_info >= (3, 12): + from collections.abc import Buffer +else: + from typing import Union + + Buffer = Union[bytes, bytearray, "memoryview[int]", "memoryview[bytes]"] + +try: + try: + import brotlicffi as brotli + except ImportError: + import brotli + + HAS_BROTLI = True +except ImportError: # pragma: no cover + HAS_BROTLI = False + +try: + if sys.version_info >= (3, 14): + from compression.zstd import ZstdDecompressor # noqa: I900 + else: # TODO(PY314): Remove mentions of backports.zstd across codebase + from backports.zstd import ZstdDecompressor + + HAS_ZSTD = True +except ImportError: + HAS_ZSTD = False + + +MAX_SYNC_CHUNK_SIZE = 4096 +DEFAULT_MAX_DECOMPRESS_SIZE = 2**25 # 32MiB + +# Unlimited decompression constants - different libraries use different conventions +ZLIB_MAX_LENGTH_UNLIMITED = 0 # zlib uses 0 to mean unlimited +ZSTD_MAX_LENGTH_UNLIMITED = -1 # zstd uses -1 to mean unlimited + + +class ZLibCompressObjProtocol(Protocol): + def compress(self, data: Buffer) -> bytes: ... + def flush(self, mode: int = ..., /) -> bytes: ... + + +class ZLibDecompressObjProtocol(Protocol): + def decompress(self, data: Buffer, max_length: int = ...) -> bytes: ... + def flush(self, length: int = ..., /) -> bytes: ... + + @property + def eof(self) -> bool: ... + + +class ZLibBackendProtocol(Protocol): + MAX_WBITS: int + Z_FULL_FLUSH: int + Z_SYNC_FLUSH: int + Z_BEST_SPEED: int + Z_FINISH: int + + def compressobj( + self, + level: int = ..., + method: int = ..., + wbits: int = ..., + memLevel: int = ..., + strategy: int = ..., + zdict: Optional[Buffer] = ..., + ) -> ZLibCompressObjProtocol: ... + def decompressobj( + self, wbits: int = ..., zdict: Buffer = ... + ) -> ZLibDecompressObjProtocol: ... + + def compress( + self, data: Buffer, /, level: int = ..., wbits: int = ... + ) -> bytes: ... + def decompress( + self, data: Buffer, /, wbits: int = ..., bufsize: int = ... + ) -> bytes: ... + + +class CompressObjArgs(TypedDict, total=False): + wbits: int + strategy: int + level: int + + +class ZLibBackendWrapper: + def __init__(self, _zlib_backend: ZLibBackendProtocol): + self._zlib_backend: ZLibBackendProtocol = _zlib_backend + + @property + def name(self) -> str: + return getattr(self._zlib_backend, "__name__", "undefined") + + @property + def MAX_WBITS(self) -> int: + return self._zlib_backend.MAX_WBITS + + @property + def Z_FULL_FLUSH(self) -> int: + return self._zlib_backend.Z_FULL_FLUSH + + @property + def Z_SYNC_FLUSH(self) -> int: + return self._zlib_backend.Z_SYNC_FLUSH + + @property + def Z_BEST_SPEED(self) -> int: + return self._zlib_backend.Z_BEST_SPEED + + @property + def Z_FINISH(self) -> int: + return self._zlib_backend.Z_FINISH + + def compressobj(self, *args: Any, **kwargs: Any) -> ZLibCompressObjProtocol: + return self._zlib_backend.compressobj(*args, **kwargs) + + def decompressobj(self, *args: Any, **kwargs: Any) -> ZLibDecompressObjProtocol: + return self._zlib_backend.decompressobj(*args, **kwargs) + + def compress(self, data: Buffer, *args: Any, **kwargs: Any) -> bytes: + return self._zlib_backend.compress(data, *args, **kwargs) + + def decompress(self, data: Buffer, *args: Any, **kwargs: Any) -> bytes: + return self._zlib_backend.decompress(data, *args, **kwargs) + + # Everything not explicitly listed in the Protocol we just pass through + def __getattr__(self, attrname: str) -> Any: + return getattr(self._zlib_backend, attrname) + + +ZLibBackend: ZLibBackendWrapper = ZLibBackendWrapper(zlib) + + +def set_zlib_backend(new_zlib_backend: ZLibBackendProtocol) -> None: + ZLibBackend._zlib_backend = new_zlib_backend + + +def encoding_to_mode( + encoding: Optional[str] = None, + suppress_deflate_header: bool = False, +) -> int: + if encoding == "gzip": + return 16 + ZLibBackend.MAX_WBITS + + return -ZLibBackend.MAX_WBITS if suppress_deflate_header else ZLibBackend.MAX_WBITS + + +class DecompressionBaseHandler(ABC): + def __init__( + self, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ): + """Base class for decompression handlers.""" + self._executor = executor + self._max_sync_chunk_size = max_sync_chunk_size + + @abstractmethod + def decompress_sync( + self, data: bytes, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + """Decompress the given data.""" + + async def decompress( + self, data: bytes, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + """Decompress the given data.""" + if ( + self._max_sync_chunk_size is not None + and len(data) > self._max_sync_chunk_size + ): + return await asyncio.get_event_loop().run_in_executor( + self._executor, self.decompress_sync, data, max_length + ) + return self.decompress_sync(data, max_length) + + +class ZLibCompressor: + def __init__( + self, + encoding: Optional[str] = None, + suppress_deflate_header: bool = False, + level: Optional[int] = None, + wbits: Optional[int] = None, + strategy: Optional[int] = None, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ): + self._executor = executor + self._max_sync_chunk_size = max_sync_chunk_size + self._mode = ( + encoding_to_mode(encoding, suppress_deflate_header) + if wbits is None + else wbits + ) + self._zlib_backend: Final = ZLibBackendWrapper(ZLibBackend._zlib_backend) + + kwargs: CompressObjArgs = {} + kwargs["wbits"] = self._mode + if strategy is not None: + kwargs["strategy"] = strategy + if level is not None: + kwargs["level"] = level + self._compressor = self._zlib_backend.compressobj(**kwargs) + + def compress_sync(self, data: bytes) -> bytes: + return self._compressor.compress(data) + + async def compress(self, data: bytes) -> bytes: + """Compress the data and returned the compressed bytes. + + Note that flush() must be called after the last call to compress() + + If the data size is large than the max_sync_chunk_size, the compression + will be done in the executor. Otherwise, the compression will be done + in the event loop. + + **WARNING: This method is NOT cancellation-safe when used with flush().** + If this operation is cancelled, the compressor state may be corrupted. + The connection MUST be closed after cancellation to avoid data corruption + in subsequent compress operations. + + For cancellation-safe compression (e.g., WebSocket), the caller MUST wrap + compress() + flush() + send operations in a shield and lock to ensure atomicity. + """ + # For large payloads, offload compression to executor to avoid blocking event loop + should_use_executor = ( + self._max_sync_chunk_size is not None + and len(data) > self._max_sync_chunk_size + ) + if should_use_executor: + return await asyncio.get_running_loop().run_in_executor( + self._executor, self._compressor.compress, data + ) + return self.compress_sync(data) + + def flush(self, mode: Optional[int] = None) -> bytes: + """Flush the compressor synchronously. + + **WARNING: This method is NOT cancellation-safe when called after compress().** + The flush() operation accesses shared compressor state. If compress() was + cancelled, calling flush() may result in corrupted data. The connection MUST + be closed after compress() cancellation. + + For cancellation-safe compression (e.g., WebSocket), the caller MUST wrap + compress() + flush() + send operations in a shield and lock to ensure atomicity. + """ + return self._compressor.flush( + mode if mode is not None else self._zlib_backend.Z_FINISH + ) + + +class ZLibDecompressor(DecompressionBaseHandler): + def __init__( + self, + encoding: Optional[str] = None, + suppress_deflate_header: bool = False, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ): + super().__init__(executor=executor, max_sync_chunk_size=max_sync_chunk_size) + self._mode = encoding_to_mode(encoding, suppress_deflate_header) + self._zlib_backend: Final = ZLibBackendWrapper(ZLibBackend._zlib_backend) + self._decompressor = self._zlib_backend.decompressobj(wbits=self._mode) + + def decompress_sync( + self, data: Buffer, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + return self._decompressor.decompress(data, max_length) + + def flush(self, length: int = 0) -> bytes: + return ( + self._decompressor.flush(length) + if length > 0 + else self._decompressor.flush() + ) + + @property + def eof(self) -> bool: + return self._decompressor.eof + + +class BrotliDecompressor(DecompressionBaseHandler): + # Supports both 'brotlipy' and 'Brotli' packages + # since they share an import name. The top branches + # are for 'brotlipy' and bottom branches for 'Brotli' + def __init__( + self, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ) -> None: + """Decompress data using the Brotli library.""" + if not HAS_BROTLI: + raise RuntimeError( + "The brotli decompression is not available. " + "Please install `Brotli` module" + ) + self._obj = brotli.Decompressor() + super().__init__(executor=executor, max_sync_chunk_size=max_sync_chunk_size) + + def decompress_sync( + self, data: Buffer, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + """Decompress the given data.""" + if hasattr(self._obj, "decompress"): + return cast(bytes, self._obj.decompress(data, max_length)) + return cast(bytes, self._obj.process(data, max_length)) + + def flush(self) -> bytes: + """Flush the decompressor.""" + if hasattr(self._obj, "flush"): + return cast(bytes, self._obj.flush()) + return b"" + + +class ZSTDDecompressor(DecompressionBaseHandler): + def __init__( + self, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ) -> None: + if not HAS_ZSTD: + raise RuntimeError( + "The zstd decompression is not available. " + "Please install `backports.zstd` module" + ) + self._obj = ZstdDecompressor() + super().__init__(executor=executor, max_sync_chunk_size=max_sync_chunk_size) + + def decompress_sync( + self, data: bytes, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED + ) -> bytes: + # zstd uses -1 for unlimited, while zlib uses 0 for unlimited + # Convert the zlib convention (0=unlimited) to zstd convention (-1=unlimited) + zstd_max_length = ( + ZSTD_MAX_LENGTH_UNLIMITED + if max_length == ZLIB_MAX_LENGTH_UNLIMITED + else max_length + ) + return self._obj.decompress(data, zstd_max_length) + + def flush(self) -> bytes: + return b"" diff --git a/venv/lib/python3.12/site-packages/aiohttp/connector.py b/venv/lib/python3.12/site-packages/aiohttp/connector.py new file mode 100644 index 00000000..ce28ce03 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/connector.py @@ -0,0 +1,1854 @@ +import asyncio +import functools +import random +import socket +import sys +import traceback +import warnings +from collections import OrderedDict, defaultdict, deque +from contextlib import suppress +from http import HTTPStatus +from itertools import chain, cycle, islice +from time import monotonic +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + DefaultDict, + Deque, + Dict, + Iterator, + List, + Literal, + Optional, + Sequence, + Set, + Tuple, + Type, + Union, + cast, +) + +import aiohappyeyeballs +from aiohappyeyeballs import AddrInfoType, SocketFactoryType + +from . import hdrs, helpers +from .abc import AbstractResolver, ResolveResult +from .client_exceptions import ( + ClientConnectionError, + ClientConnectorCertificateError, + ClientConnectorDNSError, + ClientConnectorError, + ClientConnectorSSLError, + ClientHttpProxyError, + ClientProxyConnectionError, + ServerFingerprintMismatch, + UnixClientConnectorError, + cert_errors, + ssl_errors, +) +from .client_proto import ResponseHandler +from .client_reqrep import ClientRequest, Fingerprint, _merge_ssl_params +from .helpers import ( + _SENTINEL, + ceil_timeout, + is_ip_address, + noop, + sentinel, + set_exception, + set_result, +) +from .log import client_logger +from .resolver import DefaultResolver + +if sys.version_info >= (3, 12): + from collections.abc import Buffer +else: + Buffer = Union[bytes, bytearray, "memoryview[int]", "memoryview[bytes]"] + +if TYPE_CHECKING: + import ssl + + SSLContext = ssl.SSLContext +else: + try: + import ssl + + SSLContext = ssl.SSLContext + except ImportError: # pragma: no cover + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] + +EMPTY_SCHEMA_SET = frozenset({""}) +HTTP_SCHEMA_SET = frozenset({"http", "https"}) +WS_SCHEMA_SET = frozenset({"ws", "wss"}) + +HTTP_AND_EMPTY_SCHEMA_SET = HTTP_SCHEMA_SET | EMPTY_SCHEMA_SET +HIGH_LEVEL_SCHEMA_SET = HTTP_AND_EMPTY_SCHEMA_SET | WS_SCHEMA_SET + +NEEDS_CLEANUP_CLOSED = (3, 13, 0) <= sys.version_info < ( + 3, + 13, + 1, +) or sys.version_info < (3, 12, 8) +# Cleanup closed is no longer needed after https://github.com/python/cpython/pull/118960 +# which first appeared in Python 3.12.8 and 3.13.1 + + +__all__ = ( + "BaseConnector", + "TCPConnector", + "UnixConnector", + "NamedPipeConnector", + "AddrInfoType", + "SocketFactoryType", +) + + +if TYPE_CHECKING: + from .client import ClientTimeout + from .client_reqrep import ConnectionKey + from .tracing import Trace + + +class _DeprecationWaiter: + __slots__ = ("_awaitable", "_awaited") + + def __init__(self, awaitable: Awaitable[Any]) -> None: + self._awaitable = awaitable + self._awaited = False + + def __await__(self) -> Any: + self._awaited = True + return self._awaitable.__await__() + + def __del__(self) -> None: + if not self._awaited: + warnings.warn( + "Connector.close() is a coroutine, " + "please use await connector.close()", + DeprecationWarning, + ) + + +async def _wait_for_close(waiters: List[Awaitable[object]]) -> None: + """Wait for all waiters to finish closing.""" + results = await asyncio.gather(*waiters, return_exceptions=True) + for res in results: + if isinstance(res, Exception): + client_logger.debug("Error while closing connector: %r", res) + + +class Connection: + + _source_traceback = None + + def __init__( + self, + connector: "BaseConnector", + key: "ConnectionKey", + protocol: ResponseHandler, + loop: asyncio.AbstractEventLoop, + ) -> None: + self._key = key + self._connector = connector + self._loop = loop + self._protocol: Optional[ResponseHandler] = protocol + self._callbacks: List[Callable[[], None]] = [] + + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + def __repr__(self) -> str: + return f"Connection<{self._key}>" + + def __del__(self, _warnings: Any = warnings) -> None: + if self._protocol is not None: + kwargs = {"source": self} + _warnings.warn(f"Unclosed connection {self!r}", ResourceWarning, **kwargs) + if self._loop.is_closed(): + return + + self._connector._release(self._key, self._protocol, should_close=True) + + context = {"client_connection": self, "message": "Unclosed connection"} + if self._source_traceback is not None: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def __bool__(self) -> Literal[True]: + """Force subclasses to not be falsy, to make checks simpler.""" + return True + + @property + def loop(self) -> asyncio.AbstractEventLoop: + warnings.warn( + "connector.loop property is deprecated", DeprecationWarning, stacklevel=2 + ) + return self._loop + + @property + def transport(self) -> Optional[asyncio.Transport]: + if self._protocol is None: + return None + return self._protocol.transport + + @property + def protocol(self) -> Optional[ResponseHandler]: + return self._protocol + + def add_callback(self, callback: Callable[[], None]) -> None: + if callback is not None: + self._callbacks.append(callback) + + def _notify_release(self) -> None: + callbacks, self._callbacks = self._callbacks[:], [] + + for cb in callbacks: + with suppress(Exception): + cb() + + def close(self) -> None: + self._notify_release() + + if self._protocol is not None: + self._connector._release(self._key, self._protocol, should_close=True) + self._protocol = None + + def release(self) -> None: + self._notify_release() + + if self._protocol is not None: + self._connector._release(self._key, self._protocol) + self._protocol = None + + @property + def closed(self) -> bool: + return self._protocol is None or not self._protocol.is_connected() + + +class _ConnectTunnelConnection(Connection): + """Special connection wrapper for CONNECT tunnels that must never be pooled. + + This connection wraps the proxy connection that will be upgraded with TLS. + It must never be released to the pool because: + 1. Its 'closed' future will never complete, causing session.close() to hang + 2. It represents an intermediate state, not a reusable connection + 3. The real connection (with TLS) will be created separately + """ + + def release(self) -> None: + """Do nothing - don't pool or close the connection. + + These connections are an intermediate state during the CONNECT tunnel + setup and will be cleaned up naturally after the TLS upgrade. If they + were to be pooled, they would never be properly closed, causing + session.close() to wait forever for their 'closed' future. + """ + + +class _TransportPlaceholder: + """placeholder for BaseConnector.connect function""" + + __slots__ = ("closed", "transport") + + def __init__(self, closed_future: asyncio.Future[Optional[Exception]]) -> None: + """Initialize a placeholder for a transport.""" + self.closed = closed_future + self.transport = None + + def close(self) -> None: + """Close the placeholder.""" + + def abort(self) -> None: + """Abort the placeholder (does nothing).""" + + +class BaseConnector: + """Base connector class. + + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + enable_cleanup_closed - Enables clean-up closed ssl transports. + Disabled by default. + timeout_ceil_threshold - Trigger ceiling of timeout values when + it's above timeout_ceil_threshold. + loop - Optional event loop. + """ + + _closed = True # prevent AttributeError in __del__ if ctor was failed + _source_traceback = None + + # abort transport after 2 seconds (cleanup broken connections) + _cleanup_closed_period = 2.0 + + allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET + + def __init__( + self, + *, + keepalive_timeout: Union[object, None, float] = sentinel, + force_close: bool = False, + limit: int = 100, + limit_per_host: int = 0, + enable_cleanup_closed: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + timeout_ceil_threshold: float = 5, + ) -> None: + + if force_close: + if keepalive_timeout is not None and keepalive_timeout is not sentinel: + raise ValueError( + "keepalive_timeout cannot be set if force_close is True" + ) + else: + if keepalive_timeout is sentinel: + keepalive_timeout = 15.0 + + loop = loop or asyncio.get_running_loop() + self._timeout_ceil_threshold = timeout_ceil_threshold + + self._closed = False + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + # Connection pool of reusable connections. + # We use a deque to store connections because it has O(1) popleft() + # and O(1) append() operations to implement a FIFO queue. + self._conns: DefaultDict[ + ConnectionKey, Deque[Tuple[ResponseHandler, float]] + ] = defaultdict(deque) + self._limit = limit + self._limit_per_host = limit_per_host + self._acquired: Set[ResponseHandler] = set() + self._acquired_per_host: DefaultDict[ConnectionKey, Set[ResponseHandler]] = ( + defaultdict(set) + ) + self._keepalive_timeout = cast(float, keepalive_timeout) + self._force_close = force_close + + # {host_key: FIFO list of waiters} + # The FIFO is implemented with an OrderedDict with None keys because + # python does not have an ordered set. + self._waiters: DefaultDict[ + ConnectionKey, OrderedDict[asyncio.Future[None], None] + ] = defaultdict(OrderedDict) + + self._loop = loop + self._factory = functools.partial(ResponseHandler, loop=loop) + + # start keep-alive connection cleanup task + self._cleanup_handle: Optional[asyncio.TimerHandle] = None + + # start cleanup closed transports task + self._cleanup_closed_handle: Optional[asyncio.TimerHandle] = None + + if enable_cleanup_closed and not NEEDS_CLEANUP_CLOSED: + warnings.warn( + "enable_cleanup_closed ignored because " + "https://github.com/python/cpython/pull/118960 is fixed " + f"in Python version {sys.version_info}", + DeprecationWarning, + stacklevel=2, + ) + enable_cleanup_closed = False + + self._cleanup_closed_disabled = not enable_cleanup_closed + self._cleanup_closed_transports: List[Optional[asyncio.Transport]] = [] + self._placeholder_future: asyncio.Future[Optional[Exception]] = ( + loop.create_future() + ) + self._placeholder_future.set_result(None) + self._cleanup_closed() + + def __del__(self, _warnings: Any = warnings) -> None: + if self._closed: + return + if not self._conns: + return + + conns = [repr(c) for c in self._conns.values()] + + self._close() + + kwargs = {"source": self} + _warnings.warn(f"Unclosed connector {self!r}", ResourceWarning, **kwargs) + context = { + "connector": self, + "connections": conns, + "message": "Unclosed connector", + } + if self._source_traceback is not None: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def __enter__(self) -> "BaseConnector": + warnings.warn( + '"with Connector():" is deprecated, ' + 'use "async with Connector():" instead', + DeprecationWarning, + ) + return self + + def __exit__(self, *exc: Any) -> None: + self._close() + + async def __aenter__(self) -> "BaseConnector": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + exc_traceback: Optional[TracebackType] = None, + ) -> None: + await self.close() + + @property + def force_close(self) -> bool: + """Ultimately close connection on releasing if True.""" + return self._force_close + + @property + def limit(self) -> int: + """The total number for simultaneous connections. + + If limit is 0 the connector has no limit. + The default limit size is 100. + """ + return self._limit + + @property + def limit_per_host(self) -> int: + """The limit for simultaneous connections to the same endpoint. + + Endpoints are the same if they are have equal + (host, port, is_ssl) triple. + """ + return self._limit_per_host + + def _cleanup(self) -> None: + """Cleanup unused transports.""" + if self._cleanup_handle: + self._cleanup_handle.cancel() + # _cleanup_handle should be unset, otherwise _release() will not + # recreate it ever! + self._cleanup_handle = None + + now = monotonic() + timeout = self._keepalive_timeout + + if self._conns: + connections = defaultdict(deque) + deadline = now - timeout + for key, conns in self._conns.items(): + alive: Deque[Tuple[ResponseHandler, float]] = deque() + for proto, use_time in conns: + if proto.is_connected() and use_time - deadline >= 0: + alive.append((proto, use_time)) + continue + transport = proto.transport + proto.close() + if not self._cleanup_closed_disabled and key.is_ssl: + self._cleanup_closed_transports.append(transport) + + if alive: + connections[key] = alive + + self._conns = connections + + if self._conns: + self._cleanup_handle = helpers.weakref_handle( + self, + "_cleanup", + timeout, + self._loop, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + + def _cleanup_closed(self) -> None: + """Double confirmation for transport close. + + Some broken ssl servers may leave socket open without proper close. + """ + if self._cleanup_closed_handle: + self._cleanup_closed_handle.cancel() + + for transport in self._cleanup_closed_transports: + if transport is not None: + transport.abort() + + self._cleanup_closed_transports = [] + + if not self._cleanup_closed_disabled: + self._cleanup_closed_handle = helpers.weakref_handle( + self, + "_cleanup_closed", + self._cleanup_closed_period, + self._loop, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + + def close(self, *, abort_ssl: bool = False) -> Awaitable[None]: + """Close all opened transports. + + :param abort_ssl: If True, SSL connections will be aborted immediately + without performing the shutdown handshake. This provides + faster cleanup at the cost of less graceful disconnection. + """ + if not (waiters := self._close(abort_ssl=abort_ssl)): + # If there are no connections to close, we can return a noop + # awaitable to avoid scheduling a task on the event loop. + return _DeprecationWaiter(noop()) + coro = _wait_for_close(waiters) + if sys.version_info >= (3, 12): + # Optimization for Python 3.12, try to close connections + # immediately to avoid having to schedule the task on the event loop. + task = asyncio.Task(coro, loop=self._loop, eager_start=True) + else: + task = self._loop.create_task(coro) + return _DeprecationWaiter(task) + + def _close(self, *, abort_ssl: bool = False) -> List[Awaitable[object]]: + waiters: List[Awaitable[object]] = [] + + if self._closed: + return waiters + + self._closed = True + + try: + if self._loop.is_closed(): + return waiters + + # cancel cleanup task + if self._cleanup_handle: + self._cleanup_handle.cancel() + + # cancel cleanup close task + if self._cleanup_closed_handle: + self._cleanup_closed_handle.cancel() + + for data in self._conns.values(): + for proto, _ in data: + if ( + abort_ssl + and proto.transport + and proto.transport.get_extra_info("sslcontext") is not None + ): + proto.abort() + else: + proto.close() + if closed := proto.closed: + waiters.append(closed) + + for proto in self._acquired: + if ( + abort_ssl + and proto.transport + and proto.transport.get_extra_info("sslcontext") is not None + ): + proto.abort() + else: + proto.close() + if closed := proto.closed: + waiters.append(closed) + + for transport in self._cleanup_closed_transports: + if transport is not None: + transport.abort() + + return waiters + + finally: + self._conns.clear() + self._acquired.clear() + for keyed_waiters in self._waiters.values(): + for keyed_waiter in keyed_waiters: + keyed_waiter.cancel() + self._waiters.clear() + self._cleanup_handle = None + self._cleanup_closed_transports.clear() + self._cleanup_closed_handle = None + + @property + def closed(self) -> bool: + """Is connector closed. + + A readonly property. + """ + return self._closed + + def _available_connections(self, key: "ConnectionKey") -> int: + """ + Return number of available connections. + + The limit, limit_per_host and the connection key are taken into account. + + If it returns less than 1 means that there are no connections + available. + """ + # check total available connections + # If there are no limits, this will always return 1 + total_remain = 1 + + if self._limit and (total_remain := self._limit - len(self._acquired)) <= 0: + return total_remain + + # check limit per host + if host_remain := self._limit_per_host: + if acquired := self._acquired_per_host.get(key): + host_remain -= len(acquired) + if total_remain > host_remain: + return host_remain + + return total_remain + + def _update_proxy_auth_header_and_build_proxy_req( + self, req: ClientRequest + ) -> ClientRequest: + """Set Proxy-Authorization header for non-SSL proxy requests and builds the proxy request for SSL proxy requests.""" + url = req.proxy + assert url is not None + headers: Dict[str, str] = {} + if req.proxy_headers is not None: + headers = req.proxy_headers # type: ignore[assignment] + headers[hdrs.HOST] = req.headers[hdrs.HOST] + proxy_req = ClientRequest( + hdrs.METH_GET, + url, + headers=headers, + auth=req.proxy_auth, + loop=self._loop, + ssl=req.ssl, + ) + auth = proxy_req.headers.pop(hdrs.AUTHORIZATION, None) + if auth is not None: + if not req.is_ssl(): + req.headers[hdrs.PROXY_AUTHORIZATION] = auth + else: + proxy_req.headers[hdrs.PROXY_AUTHORIZATION] = auth + return proxy_req + + async def connect( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> Connection: + """Get from pool or create new connection.""" + key = req.connection_key + if (conn := await self._get(key, traces)) is not None: + # If we do not have to wait and we can get a connection from the pool + # we can avoid the timeout ceil logic and directly return the connection + if req.proxy: + self._update_proxy_auth_header_and_build_proxy_req(req) + return conn + + async with ceil_timeout(timeout.connect, timeout.ceil_threshold): + if self._available_connections(key) <= 0: + await self._wait_for_available_connection(key, traces) + if (conn := await self._get(key, traces)) is not None: + if req.proxy: + self._update_proxy_auth_header_and_build_proxy_req(req) + return conn + + placeholder = cast( + ResponseHandler, _TransportPlaceholder(self._placeholder_future) + ) + self._acquired.add(placeholder) + if self._limit_per_host: + self._acquired_per_host[key].add(placeholder) + + try: + # Traces are done inside the try block to ensure that the + # that the placeholder is still cleaned up if an exception + # is raised. + if traces: + for trace in traces: + await trace.send_connection_create_start() + proto = await self._create_connection(req, traces, timeout) + if traces: + for trace in traces: + await trace.send_connection_create_end() + except BaseException: + self._release_acquired(key, placeholder) + raise + else: + if self._closed: + proto.close() + raise ClientConnectionError("Connector is closed.") + + # The connection was successfully created, drop the placeholder + # and add the real connection to the acquired set. There should + # be no awaits after the proto is added to the acquired set + # to ensure that the connection is not left in the acquired set + # on cancellation. + self._acquired.remove(placeholder) + self._acquired.add(proto) + if self._limit_per_host: + acquired_per_host = self._acquired_per_host[key] + acquired_per_host.remove(placeholder) + acquired_per_host.add(proto) + return Connection(self, key, proto, self._loop) + + async def _wait_for_available_connection( + self, key: "ConnectionKey", traces: List["Trace"] + ) -> None: + """Wait for an available connection slot.""" + # We loop here because there is a race between + # the connection limit check and the connection + # being acquired. If the connection is acquired + # between the check and the await statement, we + # need to loop again to check if the connection + # slot is still available. + attempts = 0 + while True: + fut: asyncio.Future[None] = self._loop.create_future() + keyed_waiters = self._waiters[key] + keyed_waiters[fut] = None + if attempts: + # If we have waited before, we need to move the waiter + # to the front of the queue as otherwise we might get + # starved and hit the timeout. + keyed_waiters.move_to_end(fut, last=False) + + try: + # Traces happen in the try block to ensure that the + # the waiter is still cleaned up if an exception is raised. + if traces: + for trace in traces: + await trace.send_connection_queued_start() + await fut + if traces: + for trace in traces: + await trace.send_connection_queued_end() + finally: + # pop the waiter from the queue if its still + # there and not already removed by _release_waiter + keyed_waiters.pop(fut, None) + if not self._waiters.get(key, True): + del self._waiters[key] + + if self._available_connections(key) > 0: + break + attempts += 1 + + async def _get( + self, key: "ConnectionKey", traces: List["Trace"] + ) -> Optional[Connection]: + """Get next reusable connection for the key or None. + + The connection will be marked as acquired. + """ + if (conns := self._conns.get(key)) is None: + return None + + t1 = monotonic() + while conns: + proto, t0 = conns.popleft() + # We will we reuse the connection if its connected and + # the keepalive timeout has not been exceeded + if proto.is_connected() and t1 - t0 <= self._keepalive_timeout: + if not conns: + # The very last connection was reclaimed: drop the key + del self._conns[key] + self._acquired.add(proto) + if self._limit_per_host: + self._acquired_per_host[key].add(proto) + if traces: + for trace in traces: + try: + await trace.send_connection_reuseconn() + except BaseException: + self._release_acquired(key, proto) + raise + return Connection(self, key, proto, self._loop) + + # Connection cannot be reused, close it + transport = proto.transport + proto.close() + # only for SSL transports + if not self._cleanup_closed_disabled and key.is_ssl: + self._cleanup_closed_transports.append(transport) + + # No more connections: drop the key + del self._conns[key] + return None + + def _release_waiter(self) -> None: + """ + Iterates over all waiters until one to be released is found. + + The one to be released is not finished and + belongs to a host that has available connections. + """ + if not self._waiters: + return + + # Having the dict keys ordered this avoids to iterate + # at the same order at each call. + queues = list(self._waiters) + random.shuffle(queues) + + for key in queues: + if self._available_connections(key) < 1: + continue + + waiters = self._waiters[key] + while waiters: + waiter, _ = waiters.popitem(last=False) + if not waiter.done(): + waiter.set_result(None) + return + + def _release_acquired(self, key: "ConnectionKey", proto: ResponseHandler) -> None: + """Release acquired connection.""" + if self._closed: + # acquired connection is already released on connector closing + return + + self._acquired.discard(proto) + if self._limit_per_host and (conns := self._acquired_per_host.get(key)): + conns.discard(proto) + if not conns: + del self._acquired_per_host[key] + self._release_waiter() + + def _release( + self, + key: "ConnectionKey", + protocol: ResponseHandler, + *, + should_close: bool = False, + ) -> None: + if self._closed: + # acquired connection is already released on connector closing + return + + self._release_acquired(key, protocol) + + if self._force_close or should_close or protocol.should_close: + transport = protocol.transport + protocol.close() + + if key.is_ssl and not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transport) + return + + self._conns[key].append((protocol, monotonic())) + + if self._cleanup_handle is None: + self._cleanup_handle = helpers.weakref_handle( + self, + "_cleanup", + self._keepalive_timeout, + self._loop, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + raise NotImplementedError() + + +class _DNSCacheTable: + def __init__(self, ttl: Optional[float] = None, max_size: int = 1000) -> None: + self._addrs_rr: OrderedDict[ + Tuple[str, int], Tuple[Iterator[ResolveResult], int] + ] = OrderedDict() + self._timestamps: Dict[Tuple[str, int], float] = {} + self._ttl = ttl + self._max_size = max_size + + def __contains__(self, host: object) -> bool: + return host in self._addrs_rr + + def add(self, key: Tuple[str, int], addrs: List[ResolveResult]) -> None: + if key in self._addrs_rr: + self._addrs_rr.move_to_end(key) + + self._addrs_rr[key] = (cycle(addrs), len(addrs)) + + if self._ttl is not None: + self._timestamps[key] = monotonic() + + if len(self._addrs_rr) > self._max_size: + oldest_key, _ = self._addrs_rr.popitem(last=False) + self._timestamps.pop(oldest_key, None) + + def remove(self, key: Tuple[str, int]) -> None: + self._addrs_rr.pop(key, None) + self._timestamps.pop(key, None) + + def clear(self) -> None: + self._addrs_rr.clear() + self._timestamps.clear() + + def next_addrs(self, key: Tuple[str, int]) -> List[ResolveResult]: + loop, length = self._addrs_rr[key] + addrs = list(islice(loop, length)) + # Consume one more element to shift internal state of `cycle` + next(loop) + self._addrs_rr.move_to_end(key) + return addrs + + def expired(self, key: Tuple[str, int]) -> bool: + if self._ttl is None: + return False + + return self._timestamps[key] + self._ttl < monotonic() + + +def _make_ssl_context(verified: bool) -> SSLContext: + """Create SSL context. + + This method is not async-friendly and should be called from a thread + because it will load certificates from disk and do other blocking I/O. + """ + if ssl is None: + # No ssl support + return None + if verified: + sslcontext = ssl.create_default_context() + else: + sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + sslcontext.options |= ssl.OP_NO_SSLv2 + sslcontext.options |= ssl.OP_NO_SSLv3 + sslcontext.check_hostname = False + sslcontext.verify_mode = ssl.CERT_NONE + sslcontext.options |= ssl.OP_NO_COMPRESSION + sslcontext.set_default_verify_paths() + sslcontext.set_alpn_protocols(("http/1.1",)) + return sslcontext + + +# The default SSLContext objects are created at import time +# since they do blocking I/O to load certificates from disk, +# and imports should always be done before the event loop starts +# or in a thread. +_SSL_CONTEXT_VERIFIED = _make_ssl_context(True) +_SSL_CONTEXT_UNVERIFIED = _make_ssl_context(False) + + +class TCPConnector(BaseConnector): + """TCP connector. + + verify_ssl - Set to True to check ssl certifications. + fingerprint - Pass the binary sha256 + digest of the expected certificate in DER format to verify + that the certificate the server presents matches. See also + https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning + resolver - Enable DNS lookups and use this + resolver + use_dns_cache - Use memory cache for DNS lookups. + ttl_dns_cache - Max seconds having cached a DNS entry, None forever. + family - socket address family + local_addr - local tuple of (host, port) to bind socket to + + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + enable_cleanup_closed - Enables clean-up closed ssl transports. + Disabled by default. + happy_eyeballs_delay - This is the “Connection Attempt Delay” + as defined in RFC 8305. To disable + the happy eyeballs algorithm, set to None. + interleave - “First Address Family Count” as defined in RFC 8305 + loop - Optional event loop. + socket_factory - A SocketFactoryType function that, if supplied, + will be used to create sockets given an + AddrInfoType. + ssl_shutdown_timeout - DEPRECATED. Will be removed in aiohttp 4.0. + Grace period for SSL shutdown handshake on TLS + connections. Default is 0 seconds (immediate abort). + This parameter allowed for a clean SSL shutdown by + notifying the remote peer of connection closure, + while avoiding excessive delays during connector cleanup. + Note: Only takes effect on Python 3.11+. + """ + + allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET | frozenset({"tcp"}) + + def __init__( + self, + *, + verify_ssl: bool = True, + fingerprint: Optional[bytes] = None, + use_dns_cache: bool = True, + ttl_dns_cache: Optional[int] = 10, + dns_cache_max_size: int = 1000, + family: socket.AddressFamily = socket.AddressFamily.AF_UNSPEC, + ssl_context: Optional[SSLContext] = None, + ssl: Union[bool, Fingerprint, SSLContext] = True, + local_addr: Optional[Tuple[str, int]] = None, + resolver: Optional[AbstractResolver] = None, + keepalive_timeout: Union[None, float, object] = sentinel, + force_close: bool = False, + limit: int = 100, + limit_per_host: int = 0, + enable_cleanup_closed: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + timeout_ceil_threshold: float = 5, + happy_eyeballs_delay: Optional[float] = 0.25, + interleave: Optional[int] = None, + socket_factory: Optional[SocketFactoryType] = None, + ssl_shutdown_timeout: Union[_SENTINEL, None, float] = sentinel, + ): + super().__init__( + keepalive_timeout=keepalive_timeout, + force_close=force_close, + limit=limit, + limit_per_host=limit_per_host, + enable_cleanup_closed=enable_cleanup_closed, + loop=loop, + timeout_ceil_threshold=timeout_ceil_threshold, + ) + + self._ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) + + self._resolver: AbstractResolver + if resolver is None: + self._resolver = DefaultResolver(loop=self._loop) + self._resolver_owner = True + else: + self._resolver = resolver + self._resolver_owner = False + + self._use_dns_cache = use_dns_cache + self._cached_hosts = _DNSCacheTable( + ttl=ttl_dns_cache, max_size=dns_cache_max_size + ) + self._throttle_dns_futures: Dict[ + Tuple[str, int], Set["asyncio.Future[None]"] + ] = {} + self._family = family + self._local_addr_infos = aiohappyeyeballs.addr_to_addr_infos(local_addr) + self._happy_eyeballs_delay = happy_eyeballs_delay + self._interleave = interleave + self._resolve_host_tasks: Set["asyncio.Task[List[ResolveResult]]"] = set() + self._socket_factory = socket_factory + self._ssl_shutdown_timeout: Optional[float] + # Handle ssl_shutdown_timeout with warning for Python < 3.11 + if ssl_shutdown_timeout is sentinel: + self._ssl_shutdown_timeout = 0 + else: + # Deprecation warning for ssl_shutdown_timeout parameter + warnings.warn( + "The ssl_shutdown_timeout parameter is deprecated and will be removed in aiohttp 4.0", + DeprecationWarning, + stacklevel=2, + ) + if ( + sys.version_info < (3, 11) + and ssl_shutdown_timeout is not None + and ssl_shutdown_timeout != 0 + ): + warnings.warn( + f"ssl_shutdown_timeout={ssl_shutdown_timeout} is ignored on Python < 3.11; " + "only ssl_shutdown_timeout=0 is supported. The timeout will be ignored.", + RuntimeWarning, + stacklevel=2, + ) + self._ssl_shutdown_timeout = ssl_shutdown_timeout + + def _close(self, *, abort_ssl: bool = False) -> List[Awaitable[object]]: + """Close all ongoing DNS calls.""" + for fut in chain.from_iterable(self._throttle_dns_futures.values()): + fut.cancel() + + waiters = super()._close(abort_ssl=abort_ssl) + + for t in self._resolve_host_tasks: + t.cancel() + waiters.append(t) + + return waiters + + async def close(self, *, abort_ssl: bool = False) -> None: + """ + Close all opened transports. + + :param abort_ssl: If True, SSL connections will be aborted immediately + without performing the shutdown handshake. If False (default), + the behavior is determined by ssl_shutdown_timeout: + - If ssl_shutdown_timeout=0: connections are aborted + - If ssl_shutdown_timeout>0: graceful shutdown is performed + """ + if self._resolver_owner: + await self._resolver.close() + # Use abort_ssl param if explicitly set, otherwise use ssl_shutdown_timeout default + await super().close(abort_ssl=abort_ssl or self._ssl_shutdown_timeout == 0) + + @property + def family(self) -> int: + """Socket family like AF_INET.""" + return self._family + + @property + def use_dns_cache(self) -> bool: + """True if local DNS caching is enabled.""" + return self._use_dns_cache + + def clear_dns_cache( + self, host: Optional[str] = None, port: Optional[int] = None + ) -> None: + """Remove specified host/port or clear all dns local cache.""" + if host is not None and port is not None: + self._cached_hosts.remove((host, port)) + elif host is not None or port is not None: + raise ValueError("either both host and port or none of them are allowed") + else: + self._cached_hosts.clear() + + async def _resolve_host( + self, host: str, port: int, traces: Optional[Sequence["Trace"]] = None + ) -> List[ResolveResult]: + """Resolve host and return list of addresses.""" + if is_ip_address(host): + return [ + { + "hostname": host, + "host": host, + "port": port, + "family": self._family, + "proto": 0, + "flags": 0, + } + ] + + if not self._use_dns_cache: + + if traces: + for trace in traces: + await trace.send_dns_resolvehost_start(host) + + res = await self._resolver.resolve(host, port, family=self._family) + + if traces: + for trace in traces: + await trace.send_dns_resolvehost_end(host) + + return res + + key = (host, port) + if key in self._cached_hosts and not self._cached_hosts.expired(key): + # get result early, before any await (#4014) + result = self._cached_hosts.next_addrs(key) + + if traces: + for trace in traces: + await trace.send_dns_cache_hit(host) + return result + + futures: Set["asyncio.Future[None]"] + # + # If multiple connectors are resolving the same host, we wait + # for the first one to resolve and then use the result for all of them. + # We use a throttle to ensure that we only resolve the host once + # and then use the result for all the waiters. + # + if key in self._throttle_dns_futures: + # get futures early, before any await (#4014) + futures = self._throttle_dns_futures[key] + future: asyncio.Future[None] = self._loop.create_future() + futures.add(future) + if traces: + for trace in traces: + await trace.send_dns_cache_hit(host) + try: + await future + finally: + futures.discard(future) + return self._cached_hosts.next_addrs(key) + + # update dict early, before any await (#4014) + self._throttle_dns_futures[key] = futures = set() + # In this case we need to create a task to ensure that we can shield + # the task from cancellation as cancelling this lookup should not cancel + # the underlying lookup or else the cancel event will get broadcast to + # all the waiters across all connections. + # + coro = self._resolve_host_with_throttle(key, host, port, futures, traces) + loop = asyncio.get_running_loop() + if sys.version_info >= (3, 12): + # Optimization for Python 3.12, try to send immediately + resolved_host_task = asyncio.Task(coro, loop=loop, eager_start=True) + else: + resolved_host_task = loop.create_task(coro) + + if not resolved_host_task.done(): + self._resolve_host_tasks.add(resolved_host_task) + resolved_host_task.add_done_callback(self._resolve_host_tasks.discard) + + try: + return await asyncio.shield(resolved_host_task) + except asyncio.CancelledError: + + def drop_exception(fut: "asyncio.Future[List[ResolveResult]]") -> None: + with suppress(Exception, asyncio.CancelledError): + fut.result() + + resolved_host_task.add_done_callback(drop_exception) + raise + + async def _resolve_host_with_throttle( + self, + key: Tuple[str, int], + host: str, + port: int, + futures: Set["asyncio.Future[None]"], + traces: Optional[Sequence["Trace"]], + ) -> List[ResolveResult]: + """Resolve host and set result for all waiters. + + This method must be run in a task and shielded from cancellation + to avoid cancelling the underlying lookup. + """ + try: + if traces: + for trace in traces: + await trace.send_dns_cache_miss(host) + + for trace in traces: + await trace.send_dns_resolvehost_start(host) + + addrs = await self._resolver.resolve(host, port, family=self._family) + if traces: + for trace in traces: + await trace.send_dns_resolvehost_end(host) + + self._cached_hosts.add(key, addrs) + for fut in futures: + set_result(fut, None) + except BaseException as e: + # any DNS exception is set for the waiters to raise the same exception. + # This coro is always run in task that is shielded from cancellation so + # we should never be propagating cancellation here. + for fut in futures: + set_exception(fut, e) + raise + finally: + self._throttle_dns_futures.pop(key) + + return self._cached_hosts.next_addrs(key) + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + """Create connection. + + Has same keyword arguments as BaseEventLoop.create_connection. + """ + if req.proxy: + _, proto = await self._create_proxy_connection(req, traces, timeout) + else: + _, proto = await self._create_direct_connection(req, traces, timeout) + + return proto + + def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]: + """Logic to get the correct SSL context + + 0. if req.ssl is false, return None + + 1. if ssl_context is specified in req, use it + 2. if _ssl_context is specified in self, use it + 3. otherwise: + 1. if verify_ssl is not specified in req, use self.ssl_context + (will generate a default context according to self.verify_ssl) + 2. if verify_ssl is True in req, generate a default SSL context + 3. if verify_ssl is False in req, generate a SSL context that + won't verify + """ + if not req.is_ssl(): + return None + + if ssl is None: # pragma: no cover + raise RuntimeError("SSL is not supported.") + sslcontext = req.ssl + if isinstance(sslcontext, ssl.SSLContext): + return sslcontext + if sslcontext is not True: + # not verified or fingerprinted + return _SSL_CONTEXT_UNVERIFIED + sslcontext = self._ssl + if isinstance(sslcontext, ssl.SSLContext): + return sslcontext + if sslcontext is not True: + # not verified or fingerprinted + return _SSL_CONTEXT_UNVERIFIED + return _SSL_CONTEXT_VERIFIED + + def _get_fingerprint(self, req: ClientRequest) -> Optional["Fingerprint"]: + ret = req.ssl + if isinstance(ret, Fingerprint): + return ret + ret = self._ssl + if isinstance(ret, Fingerprint): + return ret + return None + + async def _wrap_create_connection( + self, + *args: Any, + addr_infos: List[AddrInfoType], + req: ClientRequest, + timeout: "ClientTimeout", + client_error: Type[Exception] = ClientConnectorError, + **kwargs: Any, + ) -> Tuple[asyncio.Transport, ResponseHandler]: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + sock = await aiohappyeyeballs.start_connection( + addr_infos=addr_infos, + local_addr_infos=self._local_addr_infos, + happy_eyeballs_delay=self._happy_eyeballs_delay, + interleave=self._interleave, + loop=self._loop, + socket_factory=self._socket_factory, + ) + # Add ssl_shutdown_timeout for Python 3.11+ when SSL is used + if ( + kwargs.get("ssl") + and self._ssl_shutdown_timeout + and sys.version_info >= (3, 11) + ): + kwargs["ssl_shutdown_timeout"] = self._ssl_shutdown_timeout + return await self._loop.create_connection(*args, **kwargs, sock=sock) + except cert_errors as exc: + raise ClientConnectorCertificateError(req.connection_key, exc) from exc + except ssl_errors as exc: + raise ClientConnectorSSLError(req.connection_key, exc) from exc + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise client_error(req.connection_key, exc) from exc + + async def _wrap_existing_connection( + self, + *args: Any, + req: ClientRequest, + timeout: "ClientTimeout", + client_error: Type[Exception] = ClientConnectorError, + **kwargs: Any, + ) -> Tuple[asyncio.Transport, ResponseHandler]: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + return await self._loop.create_connection(*args, **kwargs) + except cert_errors as exc: + raise ClientConnectorCertificateError(req.connection_key, exc) from exc + except ssl_errors as exc: + raise ClientConnectorSSLError(req.connection_key, exc) from exc + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise client_error(req.connection_key, exc) from exc + + def _fail_on_no_start_tls(self, req: "ClientRequest") -> None: + """Raise a :py:exc:`RuntimeError` on missing ``start_tls()``. + + It is necessary for TLS-in-TLS so that it is possible to + send HTTPS queries through HTTPS proxies. + + This doesn't affect regular HTTP requests, though. + """ + if not req.is_ssl(): + return + + proxy_url = req.proxy + assert proxy_url is not None + if proxy_url.scheme != "https": + return + + self._check_loop_for_start_tls() + + def _check_loop_for_start_tls(self) -> None: + try: + self._loop.start_tls + except AttributeError as attr_exc: + raise RuntimeError( + "An HTTPS request is being sent through an HTTPS proxy. " + "This needs support for TLS in TLS but it is not implemented " + "in your runtime for the stdlib asyncio.\n\n" + "Please upgrade to Python 3.11 or higher. For more details, " + "please see:\n" + "* https://bugs.python.org/issue37179\n" + "* https://github.com/python/cpython/pull/28073\n" + "* https://docs.aiohttp.org/en/stable/" + "client_advanced.html#proxy-support\n" + "* https://github.com/aio-libs/aiohttp/discussions/6044\n", + ) from attr_exc + + def _loop_supports_start_tls(self) -> bool: + try: + self._check_loop_for_start_tls() + except RuntimeError: + return False + else: + return True + + def _warn_about_tls_in_tls( + self, + underlying_transport: asyncio.Transport, + req: ClientRequest, + ) -> None: + """Issue a warning if the requested URL has HTTPS scheme.""" + if req.request_info.url.scheme != "https": + return + + # Check if uvloop is being used, which supports TLS in TLS, + # otherwise assume that asyncio's native transport is being used. + if type(underlying_transport).__module__.startswith("uvloop"): + return + + # Support in asyncio was added in Python 3.11 (bpo-44011) + asyncio_supports_tls_in_tls = sys.version_info >= (3, 11) or getattr( + underlying_transport, + "_start_tls_compatible", + False, + ) + + if asyncio_supports_tls_in_tls: + return + + warnings.warn( + "An HTTPS request is being sent through an HTTPS proxy. " + "This support for TLS in TLS is known to be disabled " + "in the stdlib asyncio (Python <3.11). This is why you'll probably see " + "an error in the log below.\n\n" + "It is possible to enable it via monkeypatching. " + "For more details, see:\n" + "* https://bugs.python.org/issue37179\n" + "* https://github.com/python/cpython/pull/28073\n\n" + "You can temporarily patch this as follows:\n" + "* https://docs.aiohttp.org/en/stable/client_advanced.html#proxy-support\n" + "* https://github.com/aio-libs/aiohttp/discussions/6044\n", + RuntimeWarning, + source=self, + # Why `4`? At least 3 of the calls in the stack originate + # from the methods in this class. + stacklevel=3, + ) + + async def _start_tls_connection( + self, + underlying_transport: asyncio.Transport, + req: ClientRequest, + timeout: "ClientTimeout", + client_error: Type[Exception] = ClientConnectorError, + ) -> Tuple[asyncio.BaseTransport, ResponseHandler]: + """Wrap the raw TCP transport with TLS.""" + tls_proto = self._factory() # Create a brand new proto for TLS + sslcontext = self._get_ssl_context(req) + if TYPE_CHECKING: + # _start_tls_connection is unreachable in the current code path + # if sslcontext is None. + assert sslcontext is not None + + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + try: + # ssl_shutdown_timeout is only available in Python 3.11+ + if sys.version_info >= (3, 11) and self._ssl_shutdown_timeout: + tls_transport = await self._loop.start_tls( + underlying_transport, + tls_proto, + sslcontext, + server_hostname=req.server_hostname or req.host, + ssl_handshake_timeout=timeout.total or None, + ssl_shutdown_timeout=self._ssl_shutdown_timeout, + ) + else: + tls_transport = await self._loop.start_tls( + underlying_transport, + tls_proto, + sslcontext, + server_hostname=req.server_hostname or req.host, + ssl_handshake_timeout=timeout.total or None, + ) + except BaseException: + # We need to close the underlying transport since + # `start_tls()` probably failed before it had a + # chance to do this: + if self._ssl_shutdown_timeout == 0: + underlying_transport.abort() + else: + underlying_transport.close() + raise + if isinstance(tls_transport, asyncio.Transport): + fingerprint = self._get_fingerprint(req) + if fingerprint: + try: + fingerprint.check(tls_transport) + except ServerFingerprintMismatch: + tls_transport.close() + if not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(tls_transport) + raise + except cert_errors as exc: + raise ClientConnectorCertificateError(req.connection_key, exc) from exc + except ssl_errors as exc: + raise ClientConnectorSSLError(req.connection_key, exc) from exc + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise client_error(req.connection_key, exc) from exc + except TypeError as type_err: + # Example cause looks like this: + # TypeError: transport is not supported by start_tls() + + raise ClientConnectionError( + "Cannot initialize a TLS-in-TLS connection to host " + f"{req.host!s}:{req.port:d} through an underlying connection " + f"to an HTTPS proxy {req.proxy!s} ssl:{req.ssl or 'default'} " + f"[{type_err!s}]" + ) from type_err + else: + if tls_transport is None: + msg = "Failed to start TLS (possibly caused by closing transport)" + raise client_error(req.connection_key, OSError(msg)) + tls_proto.connection_made( + tls_transport + ) # Kick the state machine of the new TLS protocol + + return tls_transport, tls_proto + + def _convert_hosts_to_addr_infos( + self, hosts: List[ResolveResult] + ) -> List[AddrInfoType]: + """Converts the list of hosts to a list of addr_infos. + + The list of hosts is the result of a DNS lookup. The list of + addr_infos is the result of a call to `socket.getaddrinfo()`. + """ + addr_infos: List[AddrInfoType] = [] + for hinfo in hosts: + host = hinfo["host"] + is_ipv6 = ":" in host + family = socket.AF_INET6 if is_ipv6 else socket.AF_INET + if self._family and self._family != family: + continue + addr = (host, hinfo["port"], 0, 0) if is_ipv6 else (host, hinfo["port"]) + addr_infos.append( + (family, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", addr) + ) + return addr_infos + + async def _create_direct_connection( + self, + req: ClientRequest, + traces: List["Trace"], + timeout: "ClientTimeout", + *, + client_error: Type[Exception] = ClientConnectorError, + ) -> Tuple[asyncio.Transport, ResponseHandler]: + sslcontext = self._get_ssl_context(req) + fingerprint = self._get_fingerprint(req) + + host = req.url.raw_host + assert host is not None + # Replace multiple trailing dots with a single one. + # A trailing dot is only present for fully-qualified domain names. + # See https://github.com/aio-libs/aiohttp/pull/7364. + if host.endswith(".."): + host = host.rstrip(".") + "." + port = req.port + assert port is not None + try: + # Cancelling this lookup should not cancel the underlying lookup + # or else the cancel event will get broadcast to all the waiters + # across all connections. + hosts = await self._resolve_host(host, port, traces=traces) + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + # in case of proxy it is not ClientProxyConnectionError + # it is problem of resolving proxy ip itself + raise ClientConnectorDNSError(req.connection_key, exc) from exc + + last_exc: Optional[Exception] = None + addr_infos = self._convert_hosts_to_addr_infos(hosts) + while addr_infos: + # Strip trailing dots, certificates contain FQDN without dots. + # See https://github.com/aio-libs/aiohttp/issues/3636 + server_hostname = ( + (req.server_hostname or host).rstrip(".") if sslcontext else None + ) + + try: + transp, proto = await self._wrap_create_connection( + self._factory, + timeout=timeout, + ssl=sslcontext, + addr_infos=addr_infos, + server_hostname=server_hostname, + req=req, + client_error=client_error, + ) + except (ClientConnectorError, asyncio.TimeoutError) as exc: + last_exc = exc + aiohappyeyeballs.pop_addr_infos_interleave(addr_infos, self._interleave) + continue + + if req.is_ssl() and fingerprint: + try: + fingerprint.check(transp) + except ServerFingerprintMismatch as exc: + transp.close() + if not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transp) + last_exc = exc + # Remove the bad peer from the list of addr_infos + sock: socket.socket = transp.get_extra_info("socket") + bad_peer = sock.getpeername() + aiohappyeyeballs.remove_addr_infos(addr_infos, bad_peer) + continue + + return transp, proto + else: + assert last_exc is not None + raise last_exc + + async def _create_proxy_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> Tuple[asyncio.BaseTransport, ResponseHandler]: + self._fail_on_no_start_tls(req) + runtime_has_start_tls = self._loop_supports_start_tls() + proxy_req = self._update_proxy_auth_header_and_build_proxy_req(req) + + # create connection to proxy server + transport, proto = await self._create_direct_connection( + proxy_req, [], timeout, client_error=ClientProxyConnectionError + ) + + if req.is_ssl(): + if runtime_has_start_tls: + self._warn_about_tls_in_tls(transport, req) + + # For HTTPS requests over HTTP proxy + # we must notify proxy to tunnel connection + # so we send CONNECT command: + # CONNECT www.python.org:443 HTTP/1.1 + # Host: www.python.org + # + # next we must do TLS handshake and so on + # to do this we must wrap raw socket into secure one + # asyncio handles this perfectly + proxy_req.method = hdrs.METH_CONNECT + proxy_req.url = req.url + key = req.connection_key._replace( + proxy=None, proxy_auth=None, proxy_headers_hash=None + ) + conn = _ConnectTunnelConnection(self, key, proto, self._loop) + proxy_resp = await proxy_req.send(conn) + try: + protocol = conn._protocol + assert protocol is not None + + # read_until_eof=True will ensure the connection isn't closed + # once the response is received and processed allowing + # START_TLS to work on the connection below. + protocol.set_response_params( + read_until_eof=runtime_has_start_tls, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + resp = await proxy_resp.start(conn) + except BaseException: + proxy_resp.close() + conn.close() + raise + else: + conn._protocol = None + try: + if resp.status != 200: + message = resp.reason + if message is None: + message = HTTPStatus(resp.status).phrase + raise ClientHttpProxyError( + proxy_resp.request_info, + resp.history, + status=resp.status, + message=message, + headers=resp.headers, + ) + if not runtime_has_start_tls: + rawsock = transport.get_extra_info("socket", default=None) + if rawsock is None: + raise RuntimeError( + "Transport does not expose socket instance" + ) + # Duplicate the socket, so now we can close proxy transport + rawsock = rawsock.dup() + except BaseException: + # It shouldn't be closed in `finally` because it's fed to + # `loop.start_tls()` and the docs say not to touch it after + # passing there. + transport.close() + raise + finally: + if not runtime_has_start_tls: + transport.close() + + if not runtime_has_start_tls: + # HTTP proxy with support for upgrade to HTTPS + sslcontext = self._get_ssl_context(req) + return await self._wrap_existing_connection( + self._factory, + timeout=timeout, + ssl=sslcontext, + sock=rawsock, + server_hostname=req.host, + req=req, + ) + + return await self._start_tls_connection( + # Access the old transport for the last time before it's + # closed and forgotten forever: + transport, + req=req, + timeout=timeout, + ) + finally: + proxy_resp.close() + + return transport, proto + + +class UnixConnector(BaseConnector): + """Unix socket connector. + + path - Unix socket path. + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + loop - Optional event loop. + """ + + allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET | frozenset({"unix"}) + + def __init__( + self, + path: str, + force_close: bool = False, + keepalive_timeout: Union[object, float, None] = sentinel, + limit: int = 100, + limit_per_host: int = 0, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + super().__init__( + force_close=force_close, + keepalive_timeout=keepalive_timeout, + limit=limit, + limit_per_host=limit_per_host, + loop=loop, + ) + self._path = path + + @property + def path(self) -> str: + """Path to unix socket.""" + return self._path + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + _, proto = await self._loop.create_unix_connection( + self._factory, self._path + ) + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise UnixClientConnectorError(self.path, req.connection_key, exc) from exc + + return proto + + +class NamedPipeConnector(BaseConnector): + """Named pipe connector. + + Only supported by the proactor event loop. + See also: https://docs.python.org/3/library/asyncio-eventloop.html + + path - Windows named pipe path. + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + loop - Optional event loop. + """ + + allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET | frozenset({"npipe"}) + + def __init__( + self, + path: str, + force_close: bool = False, + keepalive_timeout: Union[object, float, None] = sentinel, + limit: int = 100, + limit_per_host: int = 0, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + super().__init__( + force_close=force_close, + keepalive_timeout=keepalive_timeout, + limit=limit, + limit_per_host=limit_per_host, + loop=loop, + ) + if not isinstance( + self._loop, + asyncio.ProactorEventLoop, # type: ignore[attr-defined] + ): + raise RuntimeError( + "Named Pipes only available in proactor loop under windows" + ) + self._path = path + + @property + def path(self) -> str: + """Path to the named pipe.""" + return self._path + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + _, proto = await self._loop.create_pipe_connection( # type: ignore[attr-defined] + self._factory, self._path + ) + # the drain is required so that the connection_made is called + # and transport is set otherwise it is not set before the + # `assert conn.transport is not None` + # in client.py's _request method + await asyncio.sleep(0) + # other option is to manually set transport like + # `proto.transport = trans` + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise ClientConnectorError(req.connection_key, exc) from exc + + return cast(ResponseHandler, proto) diff --git a/venv/lib/python3.12/site-packages/aiohttp/cookiejar.py b/venv/lib/python3.12/site-packages/aiohttp/cookiejar.py new file mode 100644 index 00000000..193648d4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/cookiejar.py @@ -0,0 +1,522 @@ +import asyncio +import calendar +import contextlib +import datetime +import heapq +import itertools +import os # noqa +import pathlib +import pickle +import re +import time +import warnings +from collections import defaultdict +from collections.abc import Mapping +from http.cookies import BaseCookie, Morsel, SimpleCookie +from typing import ( + DefaultDict, + Dict, + Iterable, + Iterator, + List, + Optional, + Set, + Tuple, + Union, +) + +from yarl import URL + +from ._cookie_helpers import preserve_morsel_with_coded_value +from .abc import AbstractCookieJar, ClearCookiePredicate +from .helpers import is_ip_address +from .typedefs import LooseCookies, PathLike, StrOrURL + +__all__ = ("CookieJar", "DummyCookieJar") + + +CookieItem = Union[str, "Morsel[str]"] + +# We cache these string methods here as their use is in performance critical code. +_FORMAT_PATH = "{}/{}".format +_FORMAT_DOMAIN_REVERSED = "{1}.{0}".format + +# The minimum number of scheduled cookie expirations before we start cleaning up +# the expiration heap. This is a performance optimization to avoid cleaning up the +# heap too often when there are only a few scheduled expirations. +_MIN_SCHEDULED_COOKIE_EXPIRATION = 100 +_SIMPLE_COOKIE = SimpleCookie() + + +class CookieJar(AbstractCookieJar): + """Implements cookie storage adhering to RFC 6265.""" + + DATE_TOKENS_RE = re.compile( + r"[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]*" + r"(?P[\x00-\x08\x0A-\x1F\d:a-zA-Z\x7F-\xFF]+)" + ) + + DATE_HMS_TIME_RE = re.compile(r"(\d{1,2}):(\d{1,2}):(\d{1,2})") + + DATE_DAY_OF_MONTH_RE = re.compile(r"(\d{1,2})") + + DATE_MONTH_RE = re.compile( + "(jan)|(feb)|(mar)|(apr)|(may)|(jun)|(jul)|(aug)|(sep)|(oct)|(nov)|(dec)", + re.I, + ) + + DATE_YEAR_RE = re.compile(r"(\d{2,4})") + + # calendar.timegm() fails for timestamps after datetime.datetime.max + # Minus one as a loss of precision occurs when timestamp() is called. + MAX_TIME = ( + int(datetime.datetime.max.replace(tzinfo=datetime.timezone.utc).timestamp()) - 1 + ) + try: + calendar.timegm(time.gmtime(MAX_TIME)) + except (OSError, ValueError): + # Hit the maximum representable time on Windows + # https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-localtime32-localtime64 + # Throws ValueError on PyPy 3.9, OSError elsewhere + MAX_TIME = calendar.timegm((3000, 12, 31, 23, 59, 59, -1, -1, -1)) + except OverflowError: + # #4515: datetime.max may not be representable on 32-bit platforms + MAX_TIME = 2**31 - 1 + # Avoid minuses in the future, 3x faster + SUB_MAX_TIME = MAX_TIME - 1 + + def __init__( + self, + *, + unsafe: bool = False, + quote_cookie: bool = True, + treat_as_secure_origin: Union[StrOrURL, List[StrOrURL], None] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + super().__init__(loop=loop) + self._cookies: DefaultDict[Tuple[str, str], SimpleCookie] = defaultdict( + SimpleCookie + ) + self._morsel_cache: DefaultDict[Tuple[str, str], Dict[str, Morsel[str]]] = ( + defaultdict(dict) + ) + self._host_only_cookies: Set[Tuple[str, str]] = set() + self._unsafe = unsafe + self._quote_cookie = quote_cookie + if treat_as_secure_origin is None: + treat_as_secure_origin = [] + elif isinstance(treat_as_secure_origin, URL): + treat_as_secure_origin = [treat_as_secure_origin.origin()] + elif isinstance(treat_as_secure_origin, str): + treat_as_secure_origin = [URL(treat_as_secure_origin).origin()] + else: + treat_as_secure_origin = [ + URL(url).origin() if isinstance(url, str) else url.origin() + for url in treat_as_secure_origin + ] + self._treat_as_secure_origin = treat_as_secure_origin + self._expire_heap: List[Tuple[float, Tuple[str, str, str]]] = [] + self._expirations: Dict[Tuple[str, str, str], float] = {} + + @property + def quote_cookie(self) -> bool: + return self._quote_cookie + + def save(self, file_path: PathLike) -> None: + file_path = pathlib.Path(file_path) + with file_path.open(mode="wb") as f: + pickle.dump(self._cookies, f, pickle.HIGHEST_PROTOCOL) + + def load(self, file_path: PathLike) -> None: + file_path = pathlib.Path(file_path) + with file_path.open(mode="rb") as f: + self._cookies = pickle.load(f) + + def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: + if predicate is None: + self._expire_heap.clear() + self._cookies.clear() + self._morsel_cache.clear() + self._host_only_cookies.clear() + self._expirations.clear() + return + + now = time.time() + to_del = [ + key + for (domain, path), cookie in self._cookies.items() + for name, morsel in cookie.items() + if ( + (key := (domain, path, name)) in self._expirations + and self._expirations[key] <= now + ) + or predicate(morsel) + ] + if to_del: + self._delete_cookies(to_del) + + def clear_domain(self, domain: str) -> None: + self.clear(lambda x: self._is_domain_match(domain, x["domain"])) + + def __iter__(self) -> "Iterator[Morsel[str]]": + self._do_expiration() + for val in self._cookies.values(): + yield from val.values() + + def __len__(self) -> int: + """Return number of cookies. + + This function does not iterate self to avoid unnecessary expiration + checks. + """ + return sum(len(cookie.values()) for cookie in self._cookies.values()) + + def _do_expiration(self) -> None: + """Remove expired cookies.""" + if not (expire_heap_len := len(self._expire_heap)): + return + + # If the expiration heap grows larger than the number expirations + # times two, we clean it up to avoid keeping expired entries in + # the heap and consuming memory. We guard this with a minimum + # threshold to avoid cleaning up the heap too often when there are + # only a few scheduled expirations. + if ( + expire_heap_len > _MIN_SCHEDULED_COOKIE_EXPIRATION + and expire_heap_len > len(self._expirations) * 2 + ): + # Remove any expired entries from the expiration heap + # that do not match the expiration time in the expirations + # as it means the cookie has been re-added to the heap + # with a different expiration time. + self._expire_heap = [ + entry + for entry in self._expire_heap + if self._expirations.get(entry[1]) == entry[0] + ] + heapq.heapify(self._expire_heap) + + now = time.time() + to_del: List[Tuple[str, str, str]] = [] + # Find any expired cookies and add them to the to-delete list + while self._expire_heap: + when, cookie_key = self._expire_heap[0] + if when > now: + break + heapq.heappop(self._expire_heap) + # Check if the cookie hasn't been re-added to the heap + # with a different expiration time as it will be removed + # later when it reaches the top of the heap and its + # expiration time is met. + if self._expirations.get(cookie_key) == when: + to_del.append(cookie_key) + + if to_del: + self._delete_cookies(to_del) + + def _delete_cookies(self, to_del: List[Tuple[str, str, str]]) -> None: + for domain, path, name in to_del: + self._host_only_cookies.discard((domain, name)) + self._cookies[(domain, path)].pop(name, None) + self._morsel_cache[(domain, path)].pop(name, None) + self._expirations.pop((domain, path, name), None) + + def _expire_cookie(self, when: float, domain: str, path: str, name: str) -> None: + cookie_key = (domain, path, name) + if self._expirations.get(cookie_key) == when: + # Avoid adding duplicates to the heap + return + heapq.heappush(self._expire_heap, (when, cookie_key)) + self._expirations[cookie_key] = when + + def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: + """Update cookies.""" + hostname = response_url.raw_host + + if not self._unsafe and is_ip_address(hostname): + # Don't accept cookies from IPs + return + + if isinstance(cookies, Mapping): + cookies = cookies.items() + + for name, cookie in cookies: + if not isinstance(cookie, Morsel): + tmp = SimpleCookie() + tmp[name] = cookie # type: ignore[assignment] + cookie = tmp[name] + + domain = cookie["domain"] + + # ignore domains with trailing dots + if domain and domain[-1] == ".": + domain = "" + del cookie["domain"] + + if not domain and hostname is not None: + # Set the cookie's domain to the response hostname + # and set its host-only-flag + self._host_only_cookies.add((hostname, name)) + domain = cookie["domain"] = hostname + + if domain and domain[0] == ".": + # Remove leading dot + domain = domain[1:] + cookie["domain"] = domain + + if hostname and not self._is_domain_match(domain, hostname): + # Setting cookies for different domains is not allowed + continue + + path = cookie["path"] + if not path or path[0] != "/": + # Set the cookie's path to the response path + path = response_url.path + if not path.startswith("/"): + path = "/" + else: + # Cut everything from the last slash to the end + path = "/" + path[1 : path.rfind("/")] + cookie["path"] = path + path = path.rstrip("/") + + if max_age := cookie["max-age"]: + try: + delta_seconds = int(max_age) + max_age_expiration = min(time.time() + delta_seconds, self.MAX_TIME) + self._expire_cookie(max_age_expiration, domain, path, name) + except ValueError: + cookie["max-age"] = "" + + elif expires := cookie["expires"]: + if expire_time := self._parse_date(expires): + self._expire_cookie(expire_time, domain, path, name) + else: + cookie["expires"] = "" + + key = (domain, path) + if self._cookies[key].get(name) != cookie: + # Don't blow away the cache if the same + # cookie gets set again + self._cookies[key][name] = cookie + self._morsel_cache[key].pop(name, None) + + self._do_expiration() + + def filter_cookies(self, request_url: URL = URL()) -> "BaseCookie[str]": + """Returns this jar's cookies filtered by their attributes.""" + # We always use BaseCookie now since all + # cookies set on on filtered are fully constructed + # Morsels, not just names and values. + filtered: BaseCookie[str] = BaseCookie() + if not self._cookies: + # Skip do_expiration() if there are no cookies. + return filtered + self._do_expiration() + if not self._cookies: + # Skip rest of function if no non-expired cookies. + return filtered + if type(request_url) is not URL: + warnings.warn( + "filter_cookies expects yarl.URL instances only," + f"and will stop working in 4.x, got {type(request_url)}", + DeprecationWarning, + stacklevel=2, + ) + request_url = URL(request_url) + hostname = request_url.raw_host or "" + + is_not_secure = request_url.scheme not in ("https", "wss") + if is_not_secure and self._treat_as_secure_origin: + request_origin = URL() + with contextlib.suppress(ValueError): + request_origin = request_url.origin() + is_not_secure = request_origin not in self._treat_as_secure_origin + + # Send shared cookie + key = ("", "") + for c in self._cookies[key].values(): + # Check cache first + if c.key in self._morsel_cache[key]: + filtered[c.key] = self._morsel_cache[key][c.key] + continue + + # Build and cache the morsel + mrsl_val = self._build_morsel(c) + self._morsel_cache[key][c.key] = mrsl_val + filtered[c.key] = mrsl_val + + if is_ip_address(hostname): + if not self._unsafe: + return filtered + domains: Iterable[str] = (hostname,) + else: + # Get all the subdomains that might match a cookie (e.g. "foo.bar.com", "bar.com", "com") + domains = itertools.accumulate( + reversed(hostname.split(".")), _FORMAT_DOMAIN_REVERSED + ) + + # Get all the path prefixes that might match a cookie (e.g. "", "/foo", "/foo/bar") + paths = itertools.accumulate(request_url.path.split("/"), _FORMAT_PATH) + # Create every combination of (domain, path) pairs. + pairs = itertools.product(domains, paths) + + path_len = len(request_url.path) + # Point 2: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.4 + for p in pairs: + if p not in self._cookies: + continue + for name, cookie in self._cookies[p].items(): + domain = cookie["domain"] + + if (domain, name) in self._host_only_cookies and domain != hostname: + continue + + # Skip edge case when the cookie has a trailing slash but request doesn't. + if len(cookie["path"]) > path_len: + continue + + if is_not_secure and cookie["secure"]: + continue + + # We already built the Morsel so reuse it here + if name in self._morsel_cache[p]: + filtered[name] = self._morsel_cache[p][name] + continue + + # Build and cache the morsel + mrsl_val = self._build_morsel(cookie) + self._morsel_cache[p][name] = mrsl_val + filtered[name] = mrsl_val + + return filtered + + def _build_morsel(self, cookie: Morsel[str]) -> Morsel[str]: + """Build a morsel for sending, respecting quote_cookie setting.""" + if self._quote_cookie and cookie.coded_value and cookie.coded_value[0] == '"': + return preserve_morsel_with_coded_value(cookie) + morsel: Morsel[str] = Morsel() + if self._quote_cookie: + value, coded_value = _SIMPLE_COOKIE.value_encode(cookie.value) + else: + coded_value = value = cookie.value + # We use __setstate__ instead of the public set() API because it allows us to + # bypass validation and set already validated state. This is more stable than + # setting protected attributes directly and unlikely to change since it would + # break pickling. + morsel.__setstate__({"key": cookie.key, "value": value, "coded_value": coded_value}) # type: ignore[attr-defined] + return morsel + + @staticmethod + def _is_domain_match(domain: str, hostname: str) -> bool: + """Implements domain matching adhering to RFC 6265.""" + if hostname == domain: + return True + + if not hostname.endswith(domain): + return False + + non_matching = hostname[: -len(domain)] + + if not non_matching.endswith("."): + return False + + return not is_ip_address(hostname) + + @classmethod + def _parse_date(cls, date_str: str) -> Optional[int]: + """Implements date string parsing adhering to RFC 6265.""" + if not date_str: + return None + + found_time = False + found_day = False + found_month = False + found_year = False + + hour = minute = second = 0 + day = 0 + month = 0 + year = 0 + + for token_match in cls.DATE_TOKENS_RE.finditer(date_str): + + token = token_match.group("token") + + if not found_time: + time_match = cls.DATE_HMS_TIME_RE.match(token) + if time_match: + found_time = True + hour, minute, second = (int(s) for s in time_match.groups()) + continue + + if not found_day: + day_match = cls.DATE_DAY_OF_MONTH_RE.match(token) + if day_match: + found_day = True + day = int(day_match.group()) + continue + + if not found_month: + month_match = cls.DATE_MONTH_RE.match(token) + if month_match: + found_month = True + assert month_match.lastindex is not None + month = month_match.lastindex + continue + + if not found_year: + year_match = cls.DATE_YEAR_RE.match(token) + if year_match: + found_year = True + year = int(year_match.group()) + + if 70 <= year <= 99: + year += 1900 + elif 0 <= year <= 69: + year += 2000 + + if False in (found_day, found_month, found_year, found_time): + return None + + if not 1 <= day <= 31: + return None + + if year < 1601 or hour > 23 or minute > 59 or second > 59: + return None + + return calendar.timegm((year, month, day, hour, minute, second, -1, -1, -1)) + + +class DummyCookieJar(AbstractCookieJar): + """Implements a dummy cookie storage. + + It can be used with the ClientSession when no cookie processing is needed. + + """ + + def __init__(self, *, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + super().__init__(loop=loop) + + def __iter__(self) -> "Iterator[Morsel[str]]": + while False: + yield None + + def __len__(self) -> int: + return 0 + + @property + def quote_cookie(self) -> bool: + return True + + def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: + pass + + def clear_domain(self, domain: str) -> None: + pass + + def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: + pass + + def filter_cookies(self, request_url: URL) -> "BaseCookie[str]": + return SimpleCookie() diff --git a/venv/lib/python3.12/site-packages/aiohttp/formdata.py b/venv/lib/python3.12/site-packages/aiohttp/formdata.py new file mode 100644 index 00000000..17d219a3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/formdata.py @@ -0,0 +1,184 @@ +import io +import warnings +from typing import Any, Iterable, List, Optional +from urllib.parse import urlencode + +from multidict import MultiDict, MultiDictProxy + +from . import hdrs, multipart, payload +from .helpers import guess_filename +from .payload import Payload + +__all__ = ("FormData",) + + +class FormData: + """Helper class for form body generation. + + Supports multipart/form-data and application/x-www-form-urlencoded. + """ + + def __init__( + self, + fields: Iterable[Any] = (), + quote_fields: bool = True, + charset: Optional[str] = None, + *, + default_to_multipart: bool = False, + ) -> None: + self._writer = multipart.MultipartWriter("form-data") + self._fields: List[Any] = [] + self._is_multipart = default_to_multipart + self._quote_fields = quote_fields + self._charset = charset + + if isinstance(fields, dict): + fields = list(fields.items()) + elif not isinstance(fields, (list, tuple)): + fields = (fields,) + self.add_fields(*fields) + + @property + def is_multipart(self) -> bool: + return self._is_multipart + + def add_field( + self, + name: str, + value: Any, + *, + content_type: Optional[str] = None, + filename: Optional[str] = None, + content_transfer_encoding: Optional[str] = None, + ) -> None: + + if isinstance(value, io.IOBase): + self._is_multipart = True + elif isinstance(value, (bytes, bytearray, memoryview)): + msg = ( + "In v4, passing bytes will no longer create a file field. " + "Please explicitly use the filename parameter or pass a BytesIO object." + ) + if filename is None and content_transfer_encoding is None: + warnings.warn(msg, DeprecationWarning) + filename = name + + type_options: MultiDict[str] = MultiDict({"name": name}) + if filename is not None and not isinstance(filename, str): + raise TypeError("filename must be an instance of str. Got: %s" % filename) + if filename is None and isinstance(value, io.IOBase): + filename = guess_filename(value, name) + if filename is not None: + type_options["filename"] = filename + self._is_multipart = True + + headers = {} + if content_type is not None: + if not isinstance(content_type, str): + raise TypeError( + "content_type must be an instance of str. Got: %s" % content_type + ) + if "\r" in content_type or "\n" in content_type: + raise ValueError( + "Newline or carriage return detected in headers. " + "Potential header injection attack." + ) + headers[hdrs.CONTENT_TYPE] = content_type + self._is_multipart = True + if content_transfer_encoding is not None: + if not isinstance(content_transfer_encoding, str): + raise TypeError( + "content_transfer_encoding must be an instance" + " of str. Got: %s" % content_transfer_encoding + ) + msg = ( + "content_transfer_encoding is deprecated. " + "To maintain compatibility with v4 please pass a BytesPayload." + ) + warnings.warn(msg, DeprecationWarning) + self._is_multipart = True + + self._fields.append((type_options, headers, value)) + + def add_fields(self, *fields: Any) -> None: + to_add = list(fields) + + while to_add: + rec = to_add.pop(0) + + if isinstance(rec, io.IOBase): + k = guess_filename(rec, "unknown") + self.add_field(k, rec) # type: ignore[arg-type] + + elif isinstance(rec, (MultiDictProxy, MultiDict)): + to_add.extend(rec.items()) + + elif isinstance(rec, (list, tuple)) and len(rec) == 2: + k, fp = rec + self.add_field(k, fp) + + else: + raise TypeError( + "Only io.IOBase, multidict and (name, file) " + "pairs allowed, use .add_field() for passing " + "more complex parameters, got {!r}".format(rec) + ) + + def _gen_form_urlencoded(self) -> payload.BytesPayload: + # form data (x-www-form-urlencoded) + data = [] + for type_options, _, value in self._fields: + data.append((type_options["name"], value)) + + charset = self._charset if self._charset is not None else "utf-8" + + if charset == "utf-8": + content_type = "application/x-www-form-urlencoded" + else: + content_type = "application/x-www-form-urlencoded; charset=%s" % charset + + return payload.BytesPayload( + urlencode(data, doseq=True, encoding=charset).encode(), + content_type=content_type, + ) + + def _gen_form_data(self) -> multipart.MultipartWriter: + """Encode a list of fields using the multipart/form-data MIME format""" + for dispparams, headers, value in self._fields: + try: + if hdrs.CONTENT_TYPE in headers: + part = payload.get_payload( + value, + content_type=headers[hdrs.CONTENT_TYPE], + headers=headers, + encoding=self._charset, + ) + else: + part = payload.get_payload( + value, headers=headers, encoding=self._charset + ) + except Exception as exc: + raise TypeError( + "Can not serialize value type: %r\n " + "headers: %r\n value: %r" % (type(value), headers, value) + ) from exc + + if dispparams: + part.set_content_disposition( + "form-data", quote_fields=self._quote_fields, **dispparams + ) + # FIXME cgi.FieldStorage doesn't likes body parts with + # Content-Length which were sent via chunked transfer encoding + assert part.headers is not None + part.headers.popall(hdrs.CONTENT_LENGTH, None) + + self._writer.append_payload(part) + + self._fields.clear() + return self._writer + + def __call__(self) -> Payload: + if self._is_multipart: + return self._gen_form_data() + else: + return self._gen_form_urlencoded() diff --git a/venv/lib/python3.12/site-packages/aiohttp/hdrs.py b/venv/lib/python3.12/site-packages/aiohttp/hdrs.py new file mode 100644 index 00000000..c8d6b35f --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/hdrs.py @@ -0,0 +1,121 @@ +"""HTTP Headers constants.""" + +# After changing the file content call ./tools/gen.py +# to regenerate the headers parser +import itertools +from typing import Final, Set + +from multidict import istr + +METH_ANY: Final[str] = "*" +METH_CONNECT: Final[str] = "CONNECT" +METH_HEAD: Final[str] = "HEAD" +METH_GET: Final[str] = "GET" +METH_DELETE: Final[str] = "DELETE" +METH_OPTIONS: Final[str] = "OPTIONS" +METH_PATCH: Final[str] = "PATCH" +METH_POST: Final[str] = "POST" +METH_PUT: Final[str] = "PUT" +METH_TRACE: Final[str] = "TRACE" + +METH_ALL: Final[Set[str]] = { + METH_CONNECT, + METH_HEAD, + METH_GET, + METH_DELETE, + METH_OPTIONS, + METH_PATCH, + METH_POST, + METH_PUT, + METH_TRACE, +} + +ACCEPT: Final[istr] = istr("Accept") +ACCEPT_CHARSET: Final[istr] = istr("Accept-Charset") +ACCEPT_ENCODING: Final[istr] = istr("Accept-Encoding") +ACCEPT_LANGUAGE: Final[istr] = istr("Accept-Language") +ACCEPT_RANGES: Final[istr] = istr("Accept-Ranges") +ACCESS_CONTROL_MAX_AGE: Final[istr] = istr("Access-Control-Max-Age") +ACCESS_CONTROL_ALLOW_CREDENTIALS: Final[istr] = istr("Access-Control-Allow-Credentials") +ACCESS_CONTROL_ALLOW_HEADERS: Final[istr] = istr("Access-Control-Allow-Headers") +ACCESS_CONTROL_ALLOW_METHODS: Final[istr] = istr("Access-Control-Allow-Methods") +ACCESS_CONTROL_ALLOW_ORIGIN: Final[istr] = istr("Access-Control-Allow-Origin") +ACCESS_CONTROL_EXPOSE_HEADERS: Final[istr] = istr("Access-Control-Expose-Headers") +ACCESS_CONTROL_REQUEST_HEADERS: Final[istr] = istr("Access-Control-Request-Headers") +ACCESS_CONTROL_REQUEST_METHOD: Final[istr] = istr("Access-Control-Request-Method") +AGE: Final[istr] = istr("Age") +ALLOW: Final[istr] = istr("Allow") +AUTHORIZATION: Final[istr] = istr("Authorization") +CACHE_CONTROL: Final[istr] = istr("Cache-Control") +CONNECTION: Final[istr] = istr("Connection") +CONTENT_DISPOSITION: Final[istr] = istr("Content-Disposition") +CONTENT_ENCODING: Final[istr] = istr("Content-Encoding") +CONTENT_LANGUAGE: Final[istr] = istr("Content-Language") +CONTENT_LENGTH: Final[istr] = istr("Content-Length") +CONTENT_LOCATION: Final[istr] = istr("Content-Location") +CONTENT_MD5: Final[istr] = istr("Content-MD5") +CONTENT_RANGE: Final[istr] = istr("Content-Range") +CONTENT_TRANSFER_ENCODING: Final[istr] = istr("Content-Transfer-Encoding") +CONTENT_TYPE: Final[istr] = istr("Content-Type") +COOKIE: Final[istr] = istr("Cookie") +DATE: Final[istr] = istr("Date") +DESTINATION: Final[istr] = istr("Destination") +DIGEST: Final[istr] = istr("Digest") +ETAG: Final[istr] = istr("Etag") +EXPECT: Final[istr] = istr("Expect") +EXPIRES: Final[istr] = istr("Expires") +FORWARDED: Final[istr] = istr("Forwarded") +FROM: Final[istr] = istr("From") +HOST: Final[istr] = istr("Host") +IF_MATCH: Final[istr] = istr("If-Match") +IF_MODIFIED_SINCE: Final[istr] = istr("If-Modified-Since") +IF_NONE_MATCH: Final[istr] = istr("If-None-Match") +IF_RANGE: Final[istr] = istr("If-Range") +IF_UNMODIFIED_SINCE: Final[istr] = istr("If-Unmodified-Since") +KEEP_ALIVE: Final[istr] = istr("Keep-Alive") +LAST_EVENT_ID: Final[istr] = istr("Last-Event-ID") +LAST_MODIFIED: Final[istr] = istr("Last-Modified") +LINK: Final[istr] = istr("Link") +LOCATION: Final[istr] = istr("Location") +MAX_FORWARDS: Final[istr] = istr("Max-Forwards") +ORIGIN: Final[istr] = istr("Origin") +PRAGMA: Final[istr] = istr("Pragma") +PROXY_AUTHENTICATE: Final[istr] = istr("Proxy-Authenticate") +PROXY_AUTHORIZATION: Final[istr] = istr("Proxy-Authorization") +RANGE: Final[istr] = istr("Range") +REFERER: Final[istr] = istr("Referer") +RETRY_AFTER: Final[istr] = istr("Retry-After") +SEC_WEBSOCKET_ACCEPT: Final[istr] = istr("Sec-WebSocket-Accept") +SEC_WEBSOCKET_VERSION: Final[istr] = istr("Sec-WebSocket-Version") +SEC_WEBSOCKET_PROTOCOL: Final[istr] = istr("Sec-WebSocket-Protocol") +SEC_WEBSOCKET_EXTENSIONS: Final[istr] = istr("Sec-WebSocket-Extensions") +SEC_WEBSOCKET_KEY: Final[istr] = istr("Sec-WebSocket-Key") +SEC_WEBSOCKET_KEY1: Final[istr] = istr("Sec-WebSocket-Key1") +SERVER: Final[istr] = istr("Server") +SET_COOKIE: Final[istr] = istr("Set-Cookie") +TE: Final[istr] = istr("TE") +TRAILER: Final[istr] = istr("Trailer") +TRANSFER_ENCODING: Final[istr] = istr("Transfer-Encoding") +UPGRADE: Final[istr] = istr("Upgrade") +URI: Final[istr] = istr("URI") +USER_AGENT: Final[istr] = istr("User-Agent") +VARY: Final[istr] = istr("Vary") +VIA: Final[istr] = istr("Via") +WANT_DIGEST: Final[istr] = istr("Want-Digest") +WARNING: Final[istr] = istr("Warning") +WWW_AUTHENTICATE: Final[istr] = istr("WWW-Authenticate") +X_FORWARDED_FOR: Final[istr] = istr("X-Forwarded-For") +X_FORWARDED_HOST: Final[istr] = istr("X-Forwarded-Host") +X_FORWARDED_PROTO: Final[istr] = istr("X-Forwarded-Proto") + +# These are the upper/lower case variants of the headers/methods +# Example: {'hOst', 'host', 'HoST', 'HOSt', 'hOsT', 'HosT', 'hoSt', ...} +METH_HEAD_ALL: Final = frozenset( + map("".join, itertools.product(*zip(METH_HEAD.upper(), METH_HEAD.lower()))) +) +METH_CONNECT_ALL: Final = frozenset( + map("".join, itertools.product(*zip(METH_CONNECT.upper(), METH_CONNECT.lower()))) +) +HOST_ALL: Final = frozenset( + map("".join, itertools.product(*zip(HOST.upper(), HOST.lower()))) +) diff --git a/venv/lib/python3.12/site-packages/aiohttp/helpers.py b/venv/lib/python3.12/site-packages/aiohttp/helpers.py new file mode 100644 index 00000000..dfab9877 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/helpers.py @@ -0,0 +1,986 @@ +"""Various helper functions""" + +import asyncio +import base64 +import binascii +import contextlib +import datetime +import enum +import functools +import inspect +import netrc +import os +import platform +import re +import sys +import time +import weakref +from collections import namedtuple +from contextlib import suppress +from email.message import EmailMessage +from email.parser import HeaderParser +from email.policy import HTTP +from email.utils import parsedate +from math import ceil +from pathlib import Path +from types import MappingProxyType, TracebackType +from typing import ( + Any, + Callable, + ContextManager, + Dict, + Generator, + Generic, + Iterable, + Iterator, + List, + Mapping, + Optional, + Protocol, + Tuple, + Type, + TypeVar, + Union, + get_args, + overload, +) +from urllib.parse import quote +from urllib.request import getproxies, proxy_bypass + +import attr +from multidict import MultiDict, MultiDictProxy, MultiMapping +from propcache.api import under_cached_property as reify +from yarl import URL + +from . import hdrs +from .log import client_logger + +if sys.version_info >= (3, 11): + import asyncio as async_timeout +else: + import async_timeout + +__all__ = ("BasicAuth", "ChainMapProxy", "ETag", "reify") + +IS_MACOS = platform.system() == "Darwin" +IS_WINDOWS = platform.system() == "Windows" + +PY_310 = sys.version_info >= (3, 10) +PY_311 = sys.version_info >= (3, 11) + + +_T = TypeVar("_T") +_S = TypeVar("_S") + +_SENTINEL = enum.Enum("_SENTINEL", "sentinel") +sentinel = _SENTINEL.sentinel + +NO_EXTENSIONS = bool(os.environ.get("AIOHTTP_NO_EXTENSIONS")) + +# https://datatracker.ietf.org/doc/html/rfc9112#section-6.3-2.1 +EMPTY_BODY_STATUS_CODES = frozenset((204, 304, *range(100, 200))) +# https://datatracker.ietf.org/doc/html/rfc9112#section-6.3-2.1 +# https://datatracker.ietf.org/doc/html/rfc9112#section-6.3-2.2 +EMPTY_BODY_METHODS = hdrs.METH_HEAD_ALL + +DEBUG = sys.flags.dev_mode or ( + not sys.flags.ignore_environment and bool(os.environ.get("PYTHONASYNCIODEBUG")) +) + + +CHAR = {chr(i) for i in range(0, 128)} +CTL = {chr(i) for i in range(0, 32)} | { + chr(127), +} +SEPARATORS = { + "(", + ")", + "<", + ">", + "@", + ",", + ";", + ":", + "\\", + '"', + "/", + "[", + "]", + "?", + "=", + "{", + "}", + " ", + chr(9), +} +TOKEN = CHAR ^ CTL ^ SEPARATORS + + +class noop: + def __await__(self) -> Generator[None, None, None]: + yield + + +class BasicAuth(namedtuple("BasicAuth", ["login", "password", "encoding"])): + """Http basic authentication helper.""" + + def __new__( + cls, login: str, password: str = "", encoding: str = "latin1" + ) -> "BasicAuth": + if login is None: + raise ValueError("None is not allowed as login value") + + if password is None: + raise ValueError("None is not allowed as password value") + + if ":" in login: + raise ValueError('A ":" is not allowed in login (RFC 1945#section-11.1)') + + return super().__new__(cls, login, password, encoding) + + @classmethod + def decode(cls, auth_header: str, encoding: str = "latin1") -> "BasicAuth": + """Create a BasicAuth object from an Authorization HTTP header.""" + try: + auth_type, encoded_credentials = auth_header.split(" ", 1) + except ValueError: + raise ValueError("Could not parse authorization header.") + + if auth_type.lower() != "basic": + raise ValueError("Unknown authorization method %s" % auth_type) + + try: + decoded = base64.b64decode( + encoded_credentials.encode("ascii"), validate=True + ).decode(encoding) + except binascii.Error: + raise ValueError("Invalid base64 encoding.") + + try: + # RFC 2617 HTTP Authentication + # https://www.ietf.org/rfc/rfc2617.txt + # the colon must be present, but the username and password may be + # otherwise blank. + username, password = decoded.split(":", 1) + except ValueError: + raise ValueError("Invalid credentials.") + + return cls(username, password, encoding=encoding) + + @classmethod + def from_url(cls, url: URL, *, encoding: str = "latin1") -> Optional["BasicAuth"]: + """Create BasicAuth from url.""" + if not isinstance(url, URL): + raise TypeError("url should be yarl.URL instance") + # Check raw_user and raw_password first as yarl is likely + # to already have these values parsed from the netloc in the cache. + if url.raw_user is None and url.raw_password is None: + return None + return cls(url.user or "", url.password or "", encoding=encoding) + + def encode(self) -> str: + """Encode credentials.""" + creds = (f"{self.login}:{self.password}").encode(self.encoding) + return "Basic %s" % base64.b64encode(creds).decode(self.encoding) + + +def strip_auth_from_url(url: URL) -> Tuple[URL, Optional[BasicAuth]]: + """Remove user and password from URL if present and return BasicAuth object.""" + # Check raw_user and raw_password first as yarl is likely + # to already have these values parsed from the netloc in the cache. + if url.raw_user is None and url.raw_password is None: + return url, None + return url.with_user(None), BasicAuth(url.user or "", url.password or "") + + +def netrc_from_env() -> Optional[netrc.netrc]: + """Load netrc from file. + + Attempt to load it from the path specified by the env-var + NETRC or in the default location in the user's home directory. + + Returns None if it couldn't be found or fails to parse. + """ + netrc_env = os.environ.get("NETRC") + + if netrc_env is not None: + netrc_path = Path(netrc_env) + else: + try: + home_dir = Path.home() + except RuntimeError as e: # pragma: no cover + # if pathlib can't resolve home, it may raise a RuntimeError + client_logger.debug( + "Could not resolve home directory when " + "trying to look for .netrc file: %s", + e, + ) + return None + + netrc_path = home_dir / ("_netrc" if IS_WINDOWS else ".netrc") + + try: + return netrc.netrc(str(netrc_path)) + except netrc.NetrcParseError as e: + client_logger.warning("Could not parse .netrc file: %s", e) + except OSError as e: + netrc_exists = False + with contextlib.suppress(OSError): + netrc_exists = netrc_path.is_file() + # we couldn't read the file (doesn't exist, permissions, etc.) + if netrc_env or netrc_exists: + # only warn if the environment wanted us to load it, + # or it appears like the default file does actually exist + client_logger.warning("Could not read .netrc file: %s", e) + + return None + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ProxyInfo: + proxy: URL + proxy_auth: Optional[BasicAuth] + + +def basicauth_from_netrc(netrc_obj: Optional[netrc.netrc], host: str) -> BasicAuth: + """ + Return :py:class:`~aiohttp.BasicAuth` credentials for ``host`` from ``netrc_obj``. + + :raises LookupError: if ``netrc_obj`` is :py:data:`None` or if no + entry is found for the ``host``. + """ + if netrc_obj is None: + raise LookupError("No .netrc file found") + auth_from_netrc = netrc_obj.authenticators(host) + + if auth_from_netrc is None: + raise LookupError(f"No entry for {host!s} found in the `.netrc` file.") + login, account, password = auth_from_netrc + + # TODO(PY311): username = login or account + # Up to python 3.10, account could be None if not specified, + # and login will be empty string if not specified. From 3.11, + # login and account will be empty string if not specified. + username = login if (login or account is None) else account + + # TODO(PY311): Remove this, as password will be empty string + # if not specified + if password is None: + password = "" + + return BasicAuth(username, password) + + +def proxies_from_env() -> Dict[str, ProxyInfo]: + proxy_urls = { + k: URL(v) + for k, v in getproxies().items() + if k in ("http", "https", "ws", "wss") + } + netrc_obj = netrc_from_env() + stripped = {k: strip_auth_from_url(v) for k, v in proxy_urls.items()} + ret = {} + for proto, val in stripped.items(): + proxy, auth = val + if proxy.scheme in ("https", "wss"): + client_logger.warning( + "%s proxies %s are not supported, ignoring", proxy.scheme.upper(), proxy + ) + continue + if netrc_obj and auth is None: + if proxy.host is not None: + try: + auth = basicauth_from_netrc(netrc_obj, proxy.host) + except LookupError: + auth = None + ret[proto] = ProxyInfo(proxy, auth) + return ret + + +def get_env_proxy_for_url(url: URL) -> Tuple[URL, Optional[BasicAuth]]: + """Get a permitted proxy for the given URL from the env.""" + if url.host is not None and proxy_bypass(url.host): + raise LookupError(f"Proxying is disallowed for `{url.host!r}`") + + proxies_in_env = proxies_from_env() + try: + proxy_info = proxies_in_env[url.scheme] + except KeyError: + raise LookupError(f"No proxies found for `{url!s}` in the env") + else: + return proxy_info.proxy, proxy_info.proxy_auth + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class MimeType: + type: str + subtype: str + suffix: str + parameters: "MultiDictProxy[str]" + + +@functools.lru_cache(maxsize=56) +def parse_mimetype(mimetype: str) -> MimeType: + """Parses a MIME type into its components. + + mimetype is a MIME type string. + + Returns a MimeType object. + + Example: + + >>> parse_mimetype('text/html; charset=utf-8') + MimeType(type='text', subtype='html', suffix='', + parameters={'charset': 'utf-8'}) + + """ + if not mimetype: + return MimeType( + type="", subtype="", suffix="", parameters=MultiDictProxy(MultiDict()) + ) + + parts = mimetype.split(";") + params: MultiDict[str] = MultiDict() + for item in parts[1:]: + if not item: + continue + key, _, value = item.partition("=") + params.add(key.lower().strip(), value.strip(' "')) + + fulltype = parts[0].strip().lower() + if fulltype == "*": + fulltype = "*/*" + + mtype, _, stype = fulltype.partition("/") + stype, _, suffix = stype.partition("+") + + return MimeType( + type=mtype, subtype=stype, suffix=suffix, parameters=MultiDictProxy(params) + ) + + +class EnsureOctetStream(EmailMessage): + def __init__(self) -> None: + super().__init__() + # https://www.rfc-editor.org/rfc/rfc9110#section-8.3-5 + self.set_default_type("application/octet-stream") + + def get_content_type(self) -> str: + """Re-implementation from Message + + Returns application/octet-stream in place of plain/text when + value is wrong. + + The way this class is used guarantees that content-type will + be present so simplify the checks wrt to the base implementation. + """ + value = self.get("content-type", "").lower() + + # Based on the implementation of _splitparam in the standard library + ctype, _, _ = value.partition(";") + ctype = ctype.strip() + if ctype.count("/") != 1: + return self.get_default_type() + return ctype + + +@functools.lru_cache(maxsize=56) +def parse_content_type(raw: str) -> Tuple[str, MappingProxyType[str, str]]: + """Parse Content-Type header. + + Returns a tuple of the parsed content type and a + MappingProxyType of parameters. The default returned value + is `application/octet-stream` + """ + msg = HeaderParser(EnsureOctetStream, policy=HTTP).parsestr(f"Content-Type: {raw}") + content_type = msg.get_content_type() + params = msg.get_params(()) + content_dict = dict(params[1:]) # First element is content type again + return content_type, MappingProxyType(content_dict) + + +def guess_filename(obj: Any, default: Optional[str] = None) -> Optional[str]: + name = getattr(obj, "name", None) + if name and isinstance(name, str) and name[0] != "<" and name[-1] != ">": + return Path(name).name + return default + + +not_qtext_re = re.compile(r"[^\041\043-\133\135-\176]") +QCONTENT = {chr(i) for i in range(0x20, 0x7F)} | {"\t"} + + +def quoted_string(content: str) -> str: + """Return 7-bit content as quoted-string. + + Format content into a quoted-string as defined in RFC5322 for + Internet Message Format. Notice that this is not the 8-bit HTTP + format, but the 7-bit email format. Content must be in usascii or + a ValueError is raised. + """ + if not (QCONTENT > set(content)): + raise ValueError(f"bad content for quoted-string {content!r}") + return not_qtext_re.sub(lambda x: "\\" + x.group(0), content) + + +def content_disposition_header( + disptype: str, quote_fields: bool = True, _charset: str = "utf-8", **params: str +) -> str: + """Sets ``Content-Disposition`` header for MIME. + + This is the MIME payload Content-Disposition header from RFC 2183 + and RFC 7579 section 4.2, not the HTTP Content-Disposition from + RFC 6266. + + disptype is a disposition type: inline, attachment, form-data. + Should be valid extension token (see RFC 2183) + + quote_fields performs value quoting to 7-bit MIME headers + according to RFC 7578. Set to quote_fields to False if recipient + can take 8-bit file names and field values. + + _charset specifies the charset to use when quote_fields is True. + + params is a dict with disposition params. + """ + if not disptype or not (TOKEN > set(disptype)): + raise ValueError(f"bad content disposition type {disptype!r}") + + value = disptype + if params: + lparams = [] + for key, val in params.items(): + if not key or not (TOKEN > set(key)): + raise ValueError(f"bad content disposition parameter {key!r}={val!r}") + if quote_fields: + if key.lower() == "filename": + qval = quote(val, "", encoding=_charset) + lparams.append((key, '"%s"' % qval)) + else: + try: + qval = quoted_string(val) + except ValueError: + qval = "".join( + (_charset, "''", quote(val, "", encoding=_charset)) + ) + lparams.append((key + "*", qval)) + else: + lparams.append((key, '"%s"' % qval)) + else: + qval = val.replace("\\", "\\\\").replace('"', '\\"') + lparams.append((key, '"%s"' % qval)) + sparams = "; ".join("=".join(pair) for pair in lparams) + value = "; ".join((value, sparams)) + return value + + +def is_ip_address(host: Optional[str]) -> bool: + """Check if host looks like an IP Address. + + This check is only meant as a heuristic to ensure that + a host is not a domain name. + """ + if not host: + return False + # For a host to be an ipv4 address, it must be all numeric. + # The host must contain a colon to be an IPv6 address. + return ":" in host or host.replace(".", "").isdigit() + + +_cached_current_datetime: Optional[int] = None +_cached_formatted_datetime = "" + + +def rfc822_formatted_time() -> str: + global _cached_current_datetime + global _cached_formatted_datetime + + now = int(time.time()) + if now != _cached_current_datetime: + # Weekday and month names for HTTP date/time formatting; + # always English! + # Tuples are constants stored in codeobject! + _weekdayname = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") + _monthname = ( + "", # Dummy so we can use 1-based month numbers + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ) + + year, month, day, hh, mm, ss, wd, *tail = time.gmtime(now) + _cached_formatted_datetime = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( + _weekdayname[wd], + day, + _monthname[month], + year, + hh, + mm, + ss, + ) + _cached_current_datetime = now + return _cached_formatted_datetime + + +def _weakref_handle(info: "Tuple[weakref.ref[object], str]") -> None: + ref, name = info + ob = ref() + if ob is not None: + with suppress(Exception): + getattr(ob, name)() + + +def weakref_handle( + ob: object, + name: str, + timeout: float, + loop: asyncio.AbstractEventLoop, + timeout_ceil_threshold: float = 5, +) -> Optional[asyncio.TimerHandle]: + if timeout is not None and timeout > 0: + when = loop.time() + timeout + if timeout >= timeout_ceil_threshold: + when = ceil(when) + + return loop.call_at(when, _weakref_handle, (weakref.ref(ob), name)) + return None + + +def call_later( + cb: Callable[[], Any], + timeout: float, + loop: asyncio.AbstractEventLoop, + timeout_ceil_threshold: float = 5, +) -> Optional[asyncio.TimerHandle]: + if timeout is None or timeout <= 0: + return None + now = loop.time() + when = calculate_timeout_when(now, timeout, timeout_ceil_threshold) + return loop.call_at(when, cb) + + +def calculate_timeout_when( + loop_time: float, + timeout: float, + timeout_ceiling_threshold: float, +) -> float: + """Calculate when to execute a timeout.""" + when = loop_time + timeout + if timeout > timeout_ceiling_threshold: + return ceil(when) + return when + + +class TimeoutHandle: + """Timeout handle""" + + __slots__ = ("_timeout", "_loop", "_ceil_threshold", "_callbacks") + + def __init__( + self, + loop: asyncio.AbstractEventLoop, + timeout: Optional[float], + ceil_threshold: float = 5, + ) -> None: + self._timeout = timeout + self._loop = loop + self._ceil_threshold = ceil_threshold + self._callbacks: List[ + Tuple[Callable[..., None], Tuple[Any, ...], Dict[str, Any]] + ] = [] + + def register( + self, callback: Callable[..., None], *args: Any, **kwargs: Any + ) -> None: + self._callbacks.append((callback, args, kwargs)) + + def close(self) -> None: + self._callbacks.clear() + + def start(self) -> Optional[asyncio.TimerHandle]: + timeout = self._timeout + if timeout is not None and timeout > 0: + when = self._loop.time() + timeout + if timeout >= self._ceil_threshold: + when = ceil(when) + return self._loop.call_at(when, self.__call__) + else: + return None + + def timer(self) -> "BaseTimerContext": + if self._timeout is not None and self._timeout > 0: + timer = TimerContext(self._loop) + self.register(timer.timeout) + return timer + else: + return TimerNoop() + + def __call__(self) -> None: + for cb, args, kwargs in self._callbacks: + with suppress(Exception): + cb(*args, **kwargs) + + self._callbacks.clear() + + +class BaseTimerContext(ContextManager["BaseTimerContext"]): + + __slots__ = () + + def assert_timeout(self) -> None: + """Raise TimeoutError if timeout has been exceeded.""" + + +class TimerNoop(BaseTimerContext): + + __slots__ = () + + def __enter__(self) -> BaseTimerContext: + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + return + + +class TimerContext(BaseTimerContext): + """Low resolution timeout context manager""" + + __slots__ = ("_loop", "_tasks", "_cancelled", "_cancelling") + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop = loop + self._tasks: List[asyncio.Task[Any]] = [] + self._cancelled = False + self._cancelling = 0 + + def assert_timeout(self) -> None: + """Raise TimeoutError if timer has already been cancelled.""" + if self._cancelled: + raise asyncio.TimeoutError from None + + def __enter__(self) -> BaseTimerContext: + task = asyncio.current_task(loop=self._loop) + if task is None: + raise RuntimeError("Timeout context manager should be used inside a task") + + if sys.version_info >= (3, 11): + # Remember if the task was already cancelling + # so when we __exit__ we can decide if we should + # raise asyncio.TimeoutError or let the cancellation propagate + self._cancelling = task.cancelling() + + if self._cancelled: + raise asyncio.TimeoutError from None + + self._tasks.append(task) + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: + enter_task: Optional[asyncio.Task[Any]] = None + if self._tasks: + enter_task = self._tasks.pop() + + if exc_type is asyncio.CancelledError and self._cancelled: + assert enter_task is not None + # The timeout was hit, and the task was cancelled + # so we need to uncancel the last task that entered the context manager + # since the cancellation should not leak out of the context manager + if sys.version_info >= (3, 11): + # If the task was already cancelling don't raise + # asyncio.TimeoutError and instead return None + # to allow the cancellation to propagate + if enter_task.uncancel() > self._cancelling: + return None + raise asyncio.TimeoutError from exc_val + return None + + def timeout(self) -> None: + if not self._cancelled: + for task in set(self._tasks): + task.cancel() + + self._cancelled = True + + +def ceil_timeout( + delay: Optional[float], ceil_threshold: float = 5 +) -> async_timeout.Timeout: + if delay is None or delay <= 0: + return async_timeout.timeout(None) + + loop = asyncio.get_running_loop() + now = loop.time() + when = now + delay + if delay > ceil_threshold: + when = ceil(when) + return async_timeout.timeout_at(when) + + +class HeadersMixin: + """Mixin for handling headers.""" + + ATTRS = frozenset(["_content_type", "_content_dict", "_stored_content_type"]) + + _headers: MultiMapping[str] + _content_type: Optional[str] = None + _content_dict: Optional[Dict[str, str]] = None + _stored_content_type: Union[str, None, _SENTINEL] = sentinel + + def _parse_content_type(self, raw: Optional[str]) -> None: + self._stored_content_type = raw + if raw is None: + # default value according to RFC 2616 + self._content_type = "application/octet-stream" + self._content_dict = {} + else: + content_type, content_mapping_proxy = parse_content_type(raw) + self._content_type = content_type + # _content_dict needs to be mutable so we can update it + self._content_dict = content_mapping_proxy.copy() + + @property + def content_type(self) -> str: + """The value of content part for Content-Type HTTP header.""" + raw = self._headers.get(hdrs.CONTENT_TYPE) + if self._stored_content_type != raw: + self._parse_content_type(raw) + assert self._content_type is not None + return self._content_type + + @property + def charset(self) -> Optional[str]: + """The value of charset part for Content-Type HTTP header.""" + raw = self._headers.get(hdrs.CONTENT_TYPE) + if self._stored_content_type != raw: + self._parse_content_type(raw) + assert self._content_dict is not None + return self._content_dict.get("charset") + + @property + def content_length(self) -> Optional[int]: + """The value of Content-Length HTTP header.""" + content_length = self._headers.get(hdrs.CONTENT_LENGTH) + return None if content_length is None else int(content_length) + + +def set_result(fut: "asyncio.Future[_T]", result: _T) -> None: + if not fut.done(): + fut.set_result(result) + + +_EXC_SENTINEL = BaseException() + + +class ErrorableProtocol(Protocol): + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = ..., + ) -> None: ... # pragma: no cover + + +def set_exception( + fut: "asyncio.Future[_T] | ErrorableProtocol", + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, +) -> None: + """Set future exception. + + If the future is marked as complete, this function is a no-op. + + :param exc_cause: An exception that is a direct cause of ``exc``. + Only set if provided. + """ + if asyncio.isfuture(fut) and fut.done(): + return + + exc_is_sentinel = exc_cause is _EXC_SENTINEL + exc_causes_itself = exc is exc_cause + if not exc_is_sentinel and not exc_causes_itself: + exc.__cause__ = exc_cause + + fut.set_exception(exc) + + +@functools.total_ordering +class AppKey(Generic[_T]): + """Keys for static typing support in Application.""" + + __slots__ = ("_name", "_t", "__orig_class__") + + # This may be set by Python when instantiating with a generic type. We need to + # support this, in order to support types that are not concrete classes, + # like Iterable, which can't be passed as the second parameter to __init__. + __orig_class__: Type[object] + + def __init__(self, name: str, t: Optional[Type[_T]] = None): + # Prefix with module name to help deduplicate key names. + frame = inspect.currentframe() + while frame: + if frame.f_code.co_name == "": + module: str = frame.f_globals["__name__"] + break + frame = frame.f_back + + self._name = module + "." + name + self._t = t + + def __lt__(self, other: object) -> bool: + if isinstance(other, AppKey): + return self._name < other._name + return True # Order AppKey above other types. + + def __repr__(self) -> str: + t = self._t + if t is None: + with suppress(AttributeError): + # Set to type arg. + t = get_args(self.__orig_class__)[0] + + if t is None: + t_repr = "<>" + elif isinstance(t, type): + if t.__module__ == "builtins": + t_repr = t.__qualname__ + else: + t_repr = f"{t.__module__}.{t.__qualname__}" + else: + t_repr = repr(t) + return f"" + + +class ChainMapProxy(Mapping[Union[str, AppKey[Any]], Any]): + __slots__ = ("_maps",) + + def __init__(self, maps: Iterable[Mapping[Union[str, AppKey[Any]], Any]]) -> None: + self._maps = tuple(maps) + + def __init_subclass__(cls) -> None: + raise TypeError( + "Inheritance class {} from ChainMapProxy " + "is forbidden".format(cls.__name__) + ) + + @overload # type: ignore[override] + def __getitem__(self, key: AppKey[_T]) -> _T: ... + + @overload + def __getitem__(self, key: str) -> Any: ... + + def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any: + for mapping in self._maps: + try: + return mapping[key] + except KeyError: + pass + raise KeyError(key) + + @overload # type: ignore[override] + def get(self, key: AppKey[_T], default: _S) -> Union[_T, _S]: ... + + @overload + def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ... + + @overload + def get(self, key: str, default: Any = ...) -> Any: ... + + def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any: + try: + return self[key] + except KeyError: + return default + + def __len__(self) -> int: + # reuses stored hash values if possible + return len(set().union(*self._maps)) + + def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]: + d: Dict[Union[str, AppKey[Any]], Any] = {} + for mapping in reversed(self._maps): + # reuses stored hash values if possible + d.update(mapping) + return iter(d) + + def __contains__(self, key: object) -> bool: + return any(key in m for m in self._maps) + + def __bool__(self) -> bool: + return any(self._maps) + + def __repr__(self) -> str: + content = ", ".join(map(repr, self._maps)) + return f"ChainMapProxy({content})" + + +# https://tools.ietf.org/html/rfc7232#section-2.3 +_ETAGC = r"[!\x23-\x7E\x80-\xff]+" +_ETAGC_RE = re.compile(_ETAGC) +_QUOTED_ETAG = rf'(W/)?"({_ETAGC})"' +QUOTED_ETAG_RE = re.compile(_QUOTED_ETAG) +LIST_QUOTED_ETAG_RE = re.compile(rf"({_QUOTED_ETAG})(?:\s*,\s*|$)|(.)") + +ETAG_ANY = "*" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ETag: + value: str + is_weak: bool = False + + +def validate_etag_value(value: str) -> None: + if value != ETAG_ANY and not _ETAGC_RE.fullmatch(value): + raise ValueError( + f"Value {value!r} is not a valid etag. Maybe it contains '\"'?" + ) + + +def parse_http_date(date_str: Optional[str]) -> Optional[datetime.datetime]: + """Process a date string, return a datetime object""" + if date_str is not None: + timetuple = parsedate(date_str) + if timetuple is not None: + with suppress(ValueError): + return datetime.datetime(*timetuple[:6], tzinfo=datetime.timezone.utc) + return None + + +@functools.lru_cache +def must_be_empty_body(method: str, code: int) -> bool: + """Check if a request must return an empty body.""" + return ( + code in EMPTY_BODY_STATUS_CODES + or method in EMPTY_BODY_METHODS + or (200 <= code < 300 and method in hdrs.METH_CONNECT_ALL) + ) + + +def should_remove_content_length(method: str, code: int) -> bool: + """Check if a Content-Length header should be removed. + + This should always be a subset of must_be_empty_body + """ + # https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6-8 + # https://www.rfc-editor.org/rfc/rfc9110.html#section-15.4.5-4 + return code in EMPTY_BODY_STATUS_CODES or ( + 200 <= code < 300 and method in hdrs.METH_CONNECT_ALL + ) diff --git a/venv/lib/python3.12/site-packages/aiohttp/http.py b/venv/lib/python3.12/site-packages/aiohttp/http.py new file mode 100644 index 00000000..a1feae2d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/http.py @@ -0,0 +1,72 @@ +import sys +from http import HTTPStatus +from typing import Mapping, Tuple + +from . import __version__ +from .http_exceptions import HttpProcessingError as HttpProcessingError +from .http_parser import ( + HeadersParser as HeadersParser, + HttpParser as HttpParser, + HttpRequestParser as HttpRequestParser, + HttpResponseParser as HttpResponseParser, + RawRequestMessage as RawRequestMessage, + RawResponseMessage as RawResponseMessage, +) +from .http_websocket import ( + WS_CLOSED_MESSAGE as WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE as WS_CLOSING_MESSAGE, + WS_KEY as WS_KEY, + WebSocketError as WebSocketError, + WebSocketReader as WebSocketReader, + WebSocketWriter as WebSocketWriter, + WSCloseCode as WSCloseCode, + WSMessage as WSMessage, + WSMsgType as WSMsgType, + ws_ext_gen as ws_ext_gen, + ws_ext_parse as ws_ext_parse, +) +from .http_writer import ( + HttpVersion as HttpVersion, + HttpVersion10 as HttpVersion10, + HttpVersion11 as HttpVersion11, + StreamWriter as StreamWriter, +) + +__all__ = ( + "HttpProcessingError", + "RESPONSES", + "SERVER_SOFTWARE", + # .http_writer + "StreamWriter", + "HttpVersion", + "HttpVersion10", + "HttpVersion11", + # .http_parser + "HeadersParser", + "HttpParser", + "HttpRequestParser", + "HttpResponseParser", + "RawRequestMessage", + "RawResponseMessage", + # .http_websocket + "WS_CLOSED_MESSAGE", + "WS_CLOSING_MESSAGE", + "WS_KEY", + "WebSocketReader", + "WebSocketWriter", + "ws_ext_gen", + "ws_ext_parse", + "WSMessage", + "WebSocketError", + "WSMsgType", + "WSCloseCode", +) + + +SERVER_SOFTWARE: str = "Python/{0[0]}.{0[1]} aiohttp/{1}".format( + sys.version_info, __version__ +) + +RESPONSES: Mapping[int, Tuple[str, str]] = { + v: (v.phrase, v.description) for v in HTTPStatus.__members__.values() +} diff --git a/venv/lib/python3.12/site-packages/aiohttp/http_exceptions.py b/venv/lib/python3.12/site-packages/aiohttp/http_exceptions.py new file mode 100644 index 00000000..a389e94c --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/http_exceptions.py @@ -0,0 +1,117 @@ +"""Low-level http related exceptions.""" + +from textwrap import indent +from typing import Optional, Union + +from .typedefs import _CIMultiDict + +__all__ = ("HttpProcessingError",) + + +class HttpProcessingError(Exception): + """HTTP error. + + Shortcut for raising HTTP errors with custom code, message and headers. + + code: HTTP Error code. + message: (optional) Error message. + headers: (optional) Headers to be sent in response, a list of pairs + """ + + code = 0 + message = "" + headers = None + + def __init__( + self, + *, + code: Optional[int] = None, + message: str = "", + headers: Optional[_CIMultiDict] = None, + ) -> None: + if code is not None: + self.code = code + self.headers = headers + self.message = message + + def __str__(self) -> str: + msg = indent(self.message, " ") + return f"{self.code}, message:\n{msg}" + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}: {self.code}, message={self.message!r}>" + + +class BadHttpMessage(HttpProcessingError): + + code = 400 + message = "Bad Request" + + def __init__(self, message: str, *, headers: Optional[_CIMultiDict] = None) -> None: + super().__init__(message=message, headers=headers) + self.args = (message,) + + +class HttpBadRequest(BadHttpMessage): + + code = 400 + message = "Bad Request" + + +class PayloadEncodingError(BadHttpMessage): + """Base class for payload errors""" + + +class ContentEncodingError(PayloadEncodingError): + """Content encoding error.""" + + +class TransferEncodingError(PayloadEncodingError): + """transfer encoding error.""" + + +class ContentLengthError(PayloadEncodingError): + """Not enough data to satisfy content length header.""" + + +class DecompressSizeError(PayloadEncodingError): + """Decompressed size exceeds the configured limit.""" + + +class LineTooLong(BadHttpMessage): + def __init__( + self, + line: Union[str, bytes], + limit: Union[str, int] = "Unknown", + actual_size: str = "Unknown", + ) -> None: + super().__init__(f"Got more than {limit} bytes when reading: {line!r}.") + self.args = (line, limit, actual_size) + + +class InvalidHeader(BadHttpMessage): + def __init__(self, hdr: Union[bytes, str]) -> None: + hdr_s = hdr.decode(errors="backslashreplace") if isinstance(hdr, bytes) else hdr + super().__init__(f"Invalid HTTP header: {hdr!r}") + self.hdr = hdr_s + self.args = (hdr,) + + +class BadStatusLine(BadHttpMessage): + def __init__(self, line: str = "", error: Optional[str] = None) -> None: + if not isinstance(line, str): + line = repr(line) + super().__init__(error or f"Bad status line {line!r}") + self.args = (line,) + self.line = line + + +class BadHttpMethod(BadStatusLine): + """Invalid HTTP method in status line.""" + + def __init__(self, line: str = "", error: Optional[str] = None) -> None: + super().__init__(line, error or f"Bad HTTP method in status line {line!r}") + + +class InvalidURLError(BadHttpMessage): + pass diff --git a/venv/lib/python3.12/site-packages/aiohttp/http_parser.py b/venv/lib/python3.12/site-packages/aiohttp/http_parser.py new file mode 100644 index 00000000..6a471cd7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/http_parser.py @@ -0,0 +1,1125 @@ +import abc +import asyncio +import re +import string +from contextlib import suppress +from enum import IntEnum +from typing import ( + Any, + ClassVar, + Final, + Generic, + List, + Literal, + NamedTuple, + Optional, + Pattern, + Set, + Tuple, + Type, + TypeVar, + Union, +) + +from multidict import CIMultiDict, CIMultiDictProxy, istr +from yarl import URL + +from . import hdrs +from .base_protocol import BaseProtocol +from .compression_utils import ( + DEFAULT_MAX_DECOMPRESS_SIZE, + HAS_BROTLI, + HAS_ZSTD, + BrotliDecompressor, + ZLibDecompressor, + ZSTDDecompressor, +) +from .helpers import ( + _EXC_SENTINEL, + DEBUG, + EMPTY_BODY_METHODS, + EMPTY_BODY_STATUS_CODES, + NO_EXTENSIONS, + BaseTimerContext, + set_exception, +) +from .http_exceptions import ( + BadHttpMessage, + BadHttpMethod, + BadStatusLine, + ContentEncodingError, + ContentLengthError, + DecompressSizeError, + InvalidHeader, + InvalidURLError, + LineTooLong, + TransferEncodingError, +) +from .http_writer import HttpVersion, HttpVersion10 +from .streams import EMPTY_PAYLOAD, StreamReader +from .typedefs import RawHeaders + +__all__ = ( + "HeadersParser", + "HttpParser", + "HttpRequestParser", + "HttpResponseParser", + "RawRequestMessage", + "RawResponseMessage", +) + +_SEP = Literal[b"\r\n", b"\n"] + +ASCIISET: Final[Set[str]] = set(string.printable) + +# See https://www.rfc-editor.org/rfc/rfc9110.html#name-overview +# and https://www.rfc-editor.org/rfc/rfc9110.html#name-tokens +# +# method = token +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +# "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +# token = 1*tchar +_TCHAR_SPECIALS: Final[str] = re.escape("!#$%&'*+-.^_`|~") +TOKENRE: Final[Pattern[str]] = re.compile(f"[0-9A-Za-z{_TCHAR_SPECIALS}]+") +# https://www.rfc-editor.org/rfc/rfc9110#section-5.5-5 +_FIELD_VALUE_FORBIDDEN_CTL_RE: Final[Pattern[str]] = re.compile( + r"[\x00-\x08\x0a-\x1f\x7f]" +) +VERSRE: Final[Pattern[str]] = re.compile(r"HTTP/(\d)\.(\d)", re.ASCII) +DIGITS: Final[Pattern[str]] = re.compile(r"\d+", re.ASCII) +HEXDIGITS: Final[Pattern[bytes]] = re.compile(rb"[0-9a-fA-F]+") + +# RFC 9110 singleton headers — duplicates are rejected in strict mode. +# In lax mode (response parser default), the check is skipped entirely +# since real-world servers (e.g. Google APIs, Werkzeug) commonly send +# duplicate headers like Content-Type or Server. +# Lowercased for case-insensitive matching against wire names. +SINGLETON_HEADERS: Final[frozenset[str]] = frozenset( + { + "content-length", + "content-location", + "content-range", + "content-type", + "etag", + "host", + "max-forwards", + "server", + "transfer-encoding", + "user-agent", + } +) + + +class RawRequestMessage(NamedTuple): + method: str + path: str + version: HttpVersion + headers: "CIMultiDictProxy[str]" + raw_headers: RawHeaders + should_close: bool + compression: Optional[str] + upgrade: bool + chunked: bool + url: URL + + +class RawResponseMessage(NamedTuple): + version: HttpVersion + code: int + reason: str + headers: CIMultiDictProxy[str] + raw_headers: RawHeaders + should_close: bool + compression: Optional[str] + upgrade: bool + chunked: bool + + +_MsgT = TypeVar("_MsgT", RawRequestMessage, RawResponseMessage) + + +class ParseState(IntEnum): + + PARSE_NONE = 0 + PARSE_LENGTH = 1 + PARSE_CHUNKED = 2 + PARSE_UNTIL_EOF = 3 + + +class ChunkState(IntEnum): + PARSE_CHUNKED_SIZE = 0 + PARSE_CHUNKED_CHUNK = 1 + PARSE_CHUNKED_CHUNK_EOF = 2 + PARSE_MAYBE_TRAILERS = 3 + PARSE_TRAILERS = 4 + + +class HeadersParser: + def __init__( + self, + max_line_size: int = 8190, + max_headers: int = 32768, + max_field_size: int = 8190, + lax: bool = False, + ) -> None: + self.max_line_size = max_line_size + self.max_headers = max_headers + self.max_field_size = max_field_size + self._lax = lax + + def parse_headers( + self, lines: List[bytes] + ) -> Tuple["CIMultiDictProxy[str]", RawHeaders]: + headers: CIMultiDict[str] = CIMultiDict() + # note: "raw" does not mean inclusion of OWS before/after the field value + raw_headers = [] + + lines_idx = 0 + line = lines[lines_idx] + line_count = len(lines) + + while line: + # Parse initial header name : value pair. + try: + bname, bvalue = line.split(b":", 1) + except ValueError: + raise InvalidHeader(line) from None + + if len(bname) == 0: + raise InvalidHeader(bname) + + # https://www.rfc-editor.org/rfc/rfc9112.html#section-5.1-2 + if {bname[0], bname[-1]} & {32, 9}: # {" ", "\t"} + raise InvalidHeader(line) + + bvalue = bvalue.lstrip(b" \t") + name = bname.decode("utf-8", "surrogateescape") + if not TOKENRE.fullmatch(name): + raise InvalidHeader(bname) + + # next line + lines_idx += 1 + line = lines[lines_idx] + + # consume continuation lines + continuation = self._lax and line and line[0] in (32, 9) # (' ', '\t') + + # Deprecated: https://www.rfc-editor.org/rfc/rfc9112.html#name-obsolete-line-folding + if continuation: + header_length = len(bvalue) + bvalue_lst = [bvalue] + while continuation: + header_length += len(line) + if header_length > self.max_field_size: + header_line = bname + b": " + b"".join(bvalue_lst) + raise LineTooLong( + header_line[:100] + b"...", self.max_field_size + ) + bvalue_lst.append(line) + + # next line + lines_idx += 1 + if lines_idx < line_count: + line = lines[lines_idx] + if line: + continuation = line[0] in (32, 9) # (' ', '\t') + else: + line = b"" + break + bvalue = b"".join(bvalue_lst) + + bvalue = bvalue.strip(b" \t") + value = bvalue.decode("utf-8", "surrogateescape") + + # https://www.rfc-editor.org/rfc/rfc9110.html#section-5.5-5 + if self._lax: + if "\n" in value or "\r" in value or "\x00" in value: + raise InvalidHeader(bvalue) + elif _FIELD_VALUE_FORBIDDEN_CTL_RE.search(value): + raise InvalidHeader(bvalue) + + if not self._lax and name in headers and name.lower() in SINGLETON_HEADERS: + raise BadHttpMessage(f"Duplicate '{name}' header found.") + headers.add(name, value) + raw_headers.append((bname, bvalue)) + + return (CIMultiDictProxy(headers), tuple(raw_headers)) + + +def _is_supported_upgrade(headers: CIMultiDictProxy[str]) -> bool: + """Check if the upgrade header is supported.""" + u = headers.get(hdrs.UPGRADE, "") + # .lower() can transform non-ascii characters. + return u.isascii() and u.lower() in {"tcp", "websocket"} + + +class HttpParser(abc.ABC, Generic[_MsgT]): + lax: ClassVar[bool] = False + + def __init__( + self, + protocol: Optional[BaseProtocol] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + limit: int = 2**16, + max_line_size: int = 8190, + max_headers: int = 128, + max_field_size: int = 8190, + timer: Optional[BaseTimerContext] = None, + code: Optional[int] = None, + method: Optional[str] = None, + payload_exception: Optional[Type[BaseException]] = None, + response_with_body: bool = True, + read_until_eof: bool = False, + auto_decompress: bool = True, + ) -> None: + self.protocol = protocol + self.loop = loop + self.max_line_size = max_line_size + self.max_headers = max_headers + self.max_field_size = max_field_size + self.max_headers = max_headers + self.timer = timer + self.code = code + self.method = method + self.payload_exception = payload_exception + self.response_with_body = response_with_body + self.read_until_eof = read_until_eof + + self._lines: List[bytes] = [] + self._tail = b"" + self._upgraded = False + self._payload = None + self._payload_parser: Optional[HttpPayloadParser] = None + self._auto_decompress = auto_decompress + self._limit = limit + self._headers_parser = HeadersParser( + max_line_size, max_headers, max_field_size, self.lax + ) + + @abc.abstractmethod + def parse_message(self, lines: List[bytes]) -> _MsgT: ... + + @abc.abstractmethod + def _is_chunked_te(self, te: str) -> bool: ... + + def feed_eof(self) -> Optional[_MsgT]: + if self._payload_parser is not None: + self._payload_parser.feed_eof() + self._payload_parser = None + else: + # try to extract partial message + if self._tail: + self._lines.append(self._tail) + + if self._lines: + if self._lines[-1] != "\r\n": + self._lines.append(b"") + with suppress(Exception): + return self.parse_message(self._lines) + return None + + def feed_data( + self, + data: bytes, + SEP: _SEP = b"\r\n", + EMPTY: bytes = b"", + CONTENT_LENGTH: istr = hdrs.CONTENT_LENGTH, + METH_CONNECT: str = hdrs.METH_CONNECT, + SEC_WEBSOCKET_KEY1: istr = hdrs.SEC_WEBSOCKET_KEY1, + ) -> Tuple[List[Tuple[_MsgT, StreamReader]], bool, bytes]: + + messages = [] + + if self._tail: + data, self._tail = self._tail + data, b"" + + data_len = len(data) + start_pos = 0 + loop = self.loop + max_line_length = self.max_line_size + + should_close = False + while start_pos < data_len: + + # read HTTP message (request/response line + headers), \r\n\r\n + # and split by lines + if self._payload_parser is None and not self._upgraded: + pos = data.find(SEP, start_pos) + # consume \r\n + if pos == start_pos and not self._lines: + start_pos = pos + len(SEP) + continue + + if pos >= start_pos: + if should_close: + raise BadHttpMessage("Data after `Connection: close`") + + # line found + line = data[start_pos:pos] + if SEP == b"\n": # For lax response parsing + line = line.rstrip(b"\r") + if len(line) > max_line_length: + raise LineTooLong(line[:100] + b"...", max_line_length) + + self._lines.append(line) + # After processing the status/request line, everything is a header. + max_line_length = self.max_field_size + + if len(self._lines) > self.max_headers: + raise BadHttpMessage("Too many headers received") + + start_pos = pos + len(SEP) + + # \r\n\r\n found + if self._lines[-1] == EMPTY: + max_trailers = self.max_headers - len(self._lines) + try: + msg: _MsgT = self.parse_message(self._lines) + finally: + self._lines.clear() + + def get_content_length() -> Optional[int]: + # payload length + length_hdr = msg.headers.get(CONTENT_LENGTH) + if length_hdr is None: + return None + + # Shouldn't allow +/- or other number formats. + # https://www.rfc-editor.org/rfc/rfc9110#section-8.6-2 + # msg.headers is already stripped of leading/trailing wsp + if not DIGITS.fullmatch(length_hdr): + raise InvalidHeader(CONTENT_LENGTH) + + return int(length_hdr) + + length = get_content_length() + # do not support old websocket spec + if SEC_WEBSOCKET_KEY1 in msg.headers: + raise InvalidHeader(SEC_WEBSOCKET_KEY1) + + self._upgraded = msg.upgrade and _is_supported_upgrade( + msg.headers + ) + + method = getattr(msg, "method", self.method) + # code is only present on responses + code = getattr(msg, "code", 0) + + assert self.protocol is not None + # calculate payload + empty_body = code in EMPTY_BODY_STATUS_CODES or bool( + method and method in EMPTY_BODY_METHODS + ) + if not empty_body and ( + ((length is not None and length > 0) or msg.chunked) + and not self._upgraded + ): + payload = StreamReader( + self.protocol, + timer=self.timer, + loop=loop, + limit=self._limit, + ) + payload_parser = HttpPayloadParser( + payload, + length=length, + chunked=msg.chunked, + method=method, + compression=msg.compression, + code=self.code, + response_with_body=self.response_with_body, + auto_decompress=self._auto_decompress, + lax=self.lax, + headers_parser=self._headers_parser, + max_line_size=self.max_line_size, + max_field_size=self.max_field_size, + max_trailers=max_trailers, + ) + if not payload_parser.done: + self._payload_parser = payload_parser + elif method == METH_CONNECT: + assert isinstance(msg, RawRequestMessage) + payload = StreamReader( + self.protocol, + timer=self.timer, + loop=loop, + limit=self._limit, + ) + self._upgraded = True + self._payload_parser = HttpPayloadParser( + payload, + method=msg.method, + compression=msg.compression, + auto_decompress=self._auto_decompress, + lax=self.lax, + headers_parser=self._headers_parser, + max_line_size=self.max_line_size, + max_field_size=self.max_field_size, + max_trailers=max_trailers, + ) + elif not empty_body and length is None and self.read_until_eof: + payload = StreamReader( + self.protocol, + timer=self.timer, + loop=loop, + limit=self._limit, + ) + payload_parser = HttpPayloadParser( + payload, + length=length, + chunked=msg.chunked, + method=method, + compression=msg.compression, + code=self.code, + response_with_body=self.response_with_body, + auto_decompress=self._auto_decompress, + lax=self.lax, + headers_parser=self._headers_parser, + max_line_size=self.max_line_size, + max_field_size=self.max_field_size, + max_trailers=max_trailers, + ) + if not payload_parser.done: + self._payload_parser = payload_parser + else: + payload = EMPTY_PAYLOAD + + messages.append((msg, payload)) + should_close = msg.should_close + else: + self._tail = data[start_pos:] + if len(self._tail) > self.max_line_size: + raise LineTooLong(self._tail[:100] + b"...", self.max_line_size) + data = EMPTY + break + + # no parser, just store + elif self._payload_parser is None and self._upgraded: + assert not self._lines + break + + # feed payload + elif data and start_pos < data_len: + assert not self._lines + assert self._payload_parser is not None + try: + eof, data = self._payload_parser.feed_data(data[start_pos:], SEP) + except BaseException as underlying_exc: + reraised_exc = underlying_exc + if self.payload_exception is not None: + reraised_exc = self.payload_exception(str(underlying_exc)) + + set_exception( + self._payload_parser.payload, + reraised_exc, + underlying_exc, + ) + + eof = True + data = b"" + if isinstance( + underlying_exc, (InvalidHeader, TransferEncodingError) + ): + raise + + if eof: + start_pos = 0 + data_len = len(data) + self._payload_parser = None + continue + else: + break + + if data and start_pos < data_len: + data = data[start_pos:] + else: + data = EMPTY + + return messages, self._upgraded, data + + def parse_headers( + self, lines: List[bytes] + ) -> Tuple[ + "CIMultiDictProxy[str]", RawHeaders, Optional[bool], Optional[str], bool, bool + ]: + """Parses RFC 5322 headers from a stream. + + Line continuations are supported. Returns list of header name + and value pairs. Header name is in upper case. + """ + headers, raw_headers = self._headers_parser.parse_headers(lines) + close_conn = None + encoding = None + upgrade = False + chunked = False + + # keep-alive and protocol switching + # RFC 9110 section 7.6.1 defines Connection as a comma-separated list. + conn_values = headers.getall(hdrs.CONNECTION, ()) + if conn_values: + conn_tokens = { + token.lower() + for conn_value in conn_values + for token in (part.strip(" \t") for part in conn_value.split(",")) + if token and token.isascii() + } + + if "close" in conn_tokens: + close_conn = True + elif "keep-alive" in conn_tokens: + close_conn = False + + # https://www.rfc-editor.org/rfc/rfc9110.html#name-101-switching-protocols + if "upgrade" in conn_tokens and headers.get(hdrs.UPGRADE): + upgrade = True + + # encoding + enc = headers.get(hdrs.CONTENT_ENCODING, "") + if enc.isascii() and enc.lower() in {"gzip", "deflate", "br", "zstd"}: + encoding = enc + + # chunking + te = headers.get(hdrs.TRANSFER_ENCODING) + if te is not None: + if self._is_chunked_te(te): + chunked = True + + if hdrs.CONTENT_LENGTH in headers: + raise BadHttpMessage( + "Transfer-Encoding can't be present with Content-Length", + ) + + return (headers, raw_headers, close_conn, encoding, upgrade, chunked) + + def set_upgraded(self, val: bool) -> None: + """Set connection upgraded (to websocket) mode. + + :param bool val: new state. + """ + self._upgraded = val + + +class HttpRequestParser(HttpParser[RawRequestMessage]): + """Read request status line. + + Exception .http_exceptions.BadStatusLine + could be raised in case of any errors in status line. + Returns RawRequestMessage. + """ + + def parse_message(self, lines: List[bytes]) -> RawRequestMessage: + # request line + line = lines[0].decode("utf-8", "surrogateescape") + try: + method, path, version = line.split(" ", maxsplit=2) + except ValueError: + raise BadHttpMethod(line) from None + + # method + if not TOKENRE.fullmatch(method): + raise BadHttpMethod(method) + + # version + match = VERSRE.fullmatch(version) + if match is None: + raise BadStatusLine(line) + version_o = HttpVersion(int(match.group(1)), int(match.group(2))) + + if method == "CONNECT": + # authority-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3 + url = URL.build(authority=path, encoded=True) + elif path.startswith("/"): + # origin-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1 + path_part, _hash_separator, url_fragment = path.partition("#") + path_part, _question_mark_separator, qs_part = path_part.partition("?") + + # NOTE: `yarl.URL.build()` is used to mimic what the Cython-based + # NOTE: parser does, otherwise it results into the same + # NOTE: HTTP Request-Line input producing different + # NOTE: `yarl.URL()` objects + url = URL.build( + path=path_part, + query_string=qs_part, + fragment=url_fragment, + encoded=True, + ) + elif path == "*" and method == "OPTIONS": + # asterisk-form, + url = URL(path, encoded=True) + else: + # absolute-form for proxy maybe, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.2 + url = URL(path, encoded=True) + if url.scheme == "": + # not absolute-form + raise InvalidURLError( + path.encode(errors="surrogateescape").decode("latin1") + ) + + # read headers + ( + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + ) = self.parse_headers(lines[1:]) + + if close is None: # then the headers weren't set in the request + if version_o <= HttpVersion10: # HTTP 1.0 must asks to not close + close = True + else: # HTTP 1.1 must ask to close. + close = False + + return RawRequestMessage( + method, + path, + version_o, + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + url, + ) + + def _is_chunked_te(self, te: str) -> bool: + te = te.rsplit(",", maxsplit=1)[-1].strip(" \t") + # .lower() transforms some non-ascii chars, so must check first. + if te.isascii() and te.lower() == "chunked": + return True + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3 + raise BadHttpMessage("Request has invalid `Transfer-Encoding`") + + +class HttpResponseParser(HttpParser[RawResponseMessage]): + """Read response status line and headers. + + BadStatusLine could be raised in case of any errors in status line. + Returns RawResponseMessage. + """ + + # Lax mode should only be enabled on response parser. + lax = not DEBUG + + def feed_data( + self, + data: bytes, + SEP: Optional[_SEP] = None, + *args: Any, + **kwargs: Any, + ) -> Tuple[List[Tuple[RawResponseMessage, StreamReader]], bool, bytes]: + if SEP is None: + SEP = b"\r\n" if DEBUG else b"\n" + return super().feed_data(data, SEP, *args, **kwargs) + + def parse_message(self, lines: List[bytes]) -> RawResponseMessage: + line = lines[0].decode("utf-8", "surrogateescape") + try: + version, status = line.split(maxsplit=1) + except ValueError: + raise BadStatusLine(line) from None + + try: + status, reason = status.split(maxsplit=1) + except ValueError: + status = status.strip() + reason = "" + + # version + match = VERSRE.fullmatch(version) + if match is None: + raise BadStatusLine(line) + version_o = HttpVersion(int(match.group(1)), int(match.group(2))) + + # The status code is a three-digit ASCII number, no padding + if len(status) != 3 or not DIGITS.fullmatch(status): + raise BadStatusLine(line) + status_i = int(status) + + # read headers + ( + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + ) = self.parse_headers(lines[1:]) + + if close is None: + if version_o <= HttpVersion10: + close = True + # https://www.rfc-editor.org/rfc/rfc9112.html#name-message-body-length + elif 100 <= status_i < 200 or status_i in {204, 304}: + close = False + elif hdrs.CONTENT_LENGTH in headers or hdrs.TRANSFER_ENCODING in headers: + close = False + else: + # https://www.rfc-editor.org/rfc/rfc9112.html#section-6.3-2.8 + close = True + + return RawResponseMessage( + version_o, + status_i, + reason.strip(), + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + ) + + def _is_chunked_te(self, te: str) -> bool: + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.2 + return te.rsplit(",", maxsplit=1)[-1].strip(" \t").lower() == "chunked" + + +class HttpPayloadParser: + def __init__( + self, + payload: StreamReader, + length: Optional[int] = None, + chunked: bool = False, + compression: Optional[str] = None, + code: Optional[int] = None, + method: Optional[str] = None, + response_with_body: bool = True, + auto_decompress: bool = True, + lax: bool = False, + *, + headers_parser: HeadersParser, + max_line_size: int = 8190, + max_field_size: int = 8190, + max_trailers: int = 128, + ) -> None: + self._length = 0 + self._type = ParseState.PARSE_UNTIL_EOF + self._chunk = ChunkState.PARSE_CHUNKED_SIZE + self._chunk_size = 0 + self._chunk_tail = b"" + self._auto_decompress = auto_decompress + self._lax = lax + self._headers_parser = headers_parser + self._max_line_size = max_line_size + self._max_field_size = max_field_size + self._max_trailers = max_trailers + self._trailer_lines: list[bytes] = [] + self.done = False + + # payload decompression wrapper + if response_with_body and compression and self._auto_decompress: + real_payload: Union[StreamReader, DeflateBuffer] = DeflateBuffer( + payload, compression + ) + else: + real_payload = payload + + # payload parser + if not response_with_body: + # don't parse payload if it's not expected to be received + self._type = ParseState.PARSE_NONE + real_payload.feed_eof() + self.done = True + elif chunked: + self._type = ParseState.PARSE_CHUNKED + elif length is not None: + self._type = ParseState.PARSE_LENGTH + self._length = length + if self._length == 0: + real_payload.feed_eof() + self.done = True + + self.payload = real_payload + + def feed_eof(self) -> None: + if self._type == ParseState.PARSE_UNTIL_EOF: + self.payload.feed_eof() + elif self._type == ParseState.PARSE_LENGTH: + raise ContentLengthError( + "Not enough data to satisfy content length header." + ) + elif self._type == ParseState.PARSE_CHUNKED: + raise TransferEncodingError( + "Not enough data to satisfy transfer length header." + ) + + def feed_data( + self, chunk: bytes, SEP: _SEP = b"\r\n", CHUNK_EXT: bytes = b";" + ) -> Tuple[bool, bytes]: + # Read specified amount of bytes + if self._type == ParseState.PARSE_LENGTH: + required = self._length + chunk_len = len(chunk) + + if required >= chunk_len: + self._length = required - chunk_len + self.payload.feed_data(chunk, chunk_len) + if self._length == 0: + self.payload.feed_eof() + return True, b"" + else: + self._length = 0 + self.payload.feed_data(chunk[:required], required) + self.payload.feed_eof() + return True, chunk[required:] + + # Chunked transfer encoding parser + elif self._type == ParseState.PARSE_CHUNKED: + if self._chunk_tail: + # We should never have a tail if we're inside the payload body. + assert self._chunk != ChunkState.PARSE_CHUNKED_CHUNK + # We should check the length is sane. + max_line_length = self._max_line_size + if self._chunk == ChunkState.PARSE_TRAILERS: + max_line_length = self._max_field_size + if len(self._chunk_tail) > max_line_length: + raise LineTooLong(self._chunk_tail[:100] + b"...", max_line_length) + + chunk = self._chunk_tail + chunk + self._chunk_tail = b"" + + while chunk: + + # read next chunk size + if self._chunk == ChunkState.PARSE_CHUNKED_SIZE: + pos = chunk.find(SEP) + if pos >= 0: + i = chunk.find(CHUNK_EXT, 0, pos) + if i >= 0: + size_b = chunk[:i] # strip chunk-extensions + # Verify no LF in the chunk-extension + if b"\n" in (ext := chunk[i:pos]): + exc = TransferEncodingError( + f"Unexpected LF in chunk-extension: {ext!r}" + ) + set_exception(self.payload, exc) + raise exc + else: + size_b = chunk[:pos] + + if self._lax: # Allow whitespace in lax mode. + size_b = size_b.strip() + + if not re.fullmatch(HEXDIGITS, size_b): + exc = TransferEncodingError( + chunk[:pos].decode("ascii", "surrogateescape") + ) + set_exception(self.payload, exc) + raise exc + size = int(bytes(size_b), 16) + + chunk = chunk[pos + len(SEP) :] + if size == 0: # eof marker + self._chunk = ChunkState.PARSE_TRAILERS + if self._lax and chunk.startswith(b"\r"): + chunk = chunk[1:] + else: + self._chunk = ChunkState.PARSE_CHUNKED_CHUNK + self._chunk_size = size + self.payload.begin_http_chunk_receiving() + else: + self._chunk_tail = chunk + return False, b"" + + # read chunk and feed buffer + if self._chunk == ChunkState.PARSE_CHUNKED_CHUNK: + required = self._chunk_size + chunk_len = len(chunk) + + if required > chunk_len: + self._chunk_size = required - chunk_len + self.payload.feed_data(chunk, chunk_len) + return False, b"" + else: + self._chunk_size = 0 + self.payload.feed_data(chunk[:required], required) + chunk = chunk[required:] + self._chunk = ChunkState.PARSE_CHUNKED_CHUNK_EOF + self.payload.end_http_chunk_receiving() + + # toss the CRLF at the end of the chunk + if self._chunk == ChunkState.PARSE_CHUNKED_CHUNK_EOF: + if self._lax and chunk.startswith(b"\r"): + chunk = chunk[1:] + if chunk[: len(SEP)] == SEP: + chunk = chunk[len(SEP) :] + self._chunk = ChunkState.PARSE_CHUNKED_SIZE + elif len(chunk) >= len(SEP) or chunk != SEP[: len(chunk)]: + exc = TransferEncodingError( + "Chunk size mismatch: expected CRLF after chunk data" + ) + set_exception(self.payload, exc) + raise exc + else: + self._chunk_tail = chunk + return False, b"" + + if self._chunk == ChunkState.PARSE_TRAILERS: + pos = chunk.find(SEP) + if pos < 0: # No line found + self._chunk_tail = chunk + return False, b"" + + line = chunk[:pos] + chunk = chunk[pos + len(SEP) :] + if SEP == b"\n": # For lax response parsing + line = line.rstrip(b"\r") + + if len(line) > self._max_field_size: + raise LineTooLong(line[:100] + b"...", self._max_field_size) + + self._trailer_lines.append(line) + + if len(self._trailer_lines) > self._max_trailers: + raise BadHttpMessage("Too many trailers received") + + # \r\n\r\n found, end of stream + if self._trailer_lines[-1] == b"": + # Headers and trailers are defined the same way, + # so we reuse the HeadersParser here. + try: + trailers, raw_trailers = self._headers_parser.parse_headers( + self._trailer_lines + ) + finally: + self._trailer_lines.clear() + self.payload.feed_eof() + return True, chunk + + # Read all bytes until eof + elif self._type == ParseState.PARSE_UNTIL_EOF: + self.payload.feed_data(chunk, len(chunk)) + + return False, b"" + + +class DeflateBuffer: + """DeflateStream decompress stream and feed data into specified stream.""" + + decompressor: Any + + def __init__( + self, + out: StreamReader, + encoding: Optional[str], + max_decompress_size: int = DEFAULT_MAX_DECOMPRESS_SIZE, + ) -> None: + self.out = out + self.size = 0 + out.total_compressed_bytes = self.size + self.encoding = encoding + self._started_decoding = False + + self.decompressor: Union[BrotliDecompressor, ZLibDecompressor, ZSTDDecompressor] + if encoding == "br": + if not HAS_BROTLI: # pragma: no cover + raise ContentEncodingError( + "Can not decode content-encoding: brotli (br). " + "Please install `Brotli`" + ) + self.decompressor = BrotliDecompressor() + elif encoding == "zstd": + if not HAS_ZSTD: + raise ContentEncodingError( + "Can not decode content-encoding: zstandard (zstd). " + "Please install `backports.zstd`" + ) + self.decompressor = ZSTDDecompressor() + else: + self.decompressor = ZLibDecompressor(encoding=encoding) + + self._max_decompress_size = max_decompress_size + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + set_exception(self.out, exc, exc_cause) + + def feed_data(self, chunk: bytes, size: int) -> None: + if not size: + return + + self.size += size + self.out.total_compressed_bytes = self.size + + # RFC1950 + # bits 0..3 = CM = 0b1000 = 8 = "deflate" + # bits 4..7 = CINFO = 1..7 = windows size. + if ( + not self._started_decoding + and self.encoding == "deflate" + and chunk[0] & 0xF != 8 + ): + # Change the decoder to decompress incorrectly compressed data + # Actually we should issue a warning about non-RFC-compliant data. + self.decompressor = ZLibDecompressor( + encoding=self.encoding, suppress_deflate_header=True + ) + + try: + # Decompress with limit + 1 so we can detect if output exceeds limit + chunk = self.decompressor.decompress_sync( + chunk, max_length=self._max_decompress_size + 1 + ) + except Exception: + raise ContentEncodingError( + "Can not decode content-encoding: %s" % self.encoding + ) + + self._started_decoding = True + + # Check if decompression limit was exceeded + if len(chunk) > self._max_decompress_size: + raise DecompressSizeError( + "Decompressed data exceeds the configured limit of %d bytes" + % self._max_decompress_size + ) + + if chunk: + self.out.feed_data(chunk, len(chunk)) + + def feed_eof(self) -> None: + chunk = self.decompressor.flush() + + if chunk or self.size > 0: + self.out.feed_data(chunk, len(chunk)) + if self.encoding == "deflate" and not self.decompressor.eof: + raise ContentEncodingError("deflate") + + self.out.feed_eof() + + def begin_http_chunk_receiving(self) -> None: + self.out.begin_http_chunk_receiving() + + def end_http_chunk_receiving(self) -> None: + self.out.end_http_chunk_receiving() + + +HttpRequestParserPy = HttpRequestParser +HttpResponseParserPy = HttpResponseParser +RawRequestMessagePy = RawRequestMessage +RawResponseMessagePy = RawResponseMessage + +try: + if not NO_EXTENSIONS: + from ._http_parser import ( # type: ignore[import-not-found,no-redef] + HttpRequestParser, + HttpResponseParser, + RawRequestMessage, + RawResponseMessage, + ) + + HttpRequestParserC = HttpRequestParser + HttpResponseParserC = HttpResponseParser + RawRequestMessageC = RawRequestMessage + RawResponseMessageC = RawResponseMessage +except ImportError: # pragma: no cover + pass diff --git a/venv/lib/python3.12/site-packages/aiohttp/http_websocket.py b/venv/lib/python3.12/site-packages/aiohttp/http_websocket.py new file mode 100644 index 00000000..6b4b30e0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/http_websocket.py @@ -0,0 +1,36 @@ +"""WebSocket protocol versions 13 and 8.""" + +from ._websocket.helpers import WS_KEY, ws_ext_gen, ws_ext_parse +from ._websocket.models import ( + WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE, + WebSocketError, + WSCloseCode, + WSHandshakeError, + WSMessage, + WSMsgType, +) +from ._websocket.reader import WebSocketReader +from ._websocket.writer import WebSocketWriter + +# Messages that the WebSocketResponse.receive needs to handle internally +_INTERNAL_RECEIVE_TYPES = frozenset( + (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.PING, WSMsgType.PONG) +) + + +__all__ = ( + "WS_CLOSED_MESSAGE", + "WS_CLOSING_MESSAGE", + "WS_KEY", + "WebSocketReader", + "WebSocketWriter", + "WSMessage", + "WebSocketError", + "WSMsgType", + "WSCloseCode", + "ws_ext_gen", + "ws_ext_parse", + "WSHandshakeError", + "WSMessage", +) diff --git a/venv/lib/python3.12/site-packages/aiohttp/http_writer.py b/venv/lib/python3.12/site-packages/aiohttp/http_writer.py new file mode 100644 index 00000000..8c1c910d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/http_writer.py @@ -0,0 +1,379 @@ +"""Http related parsers and protocol.""" + +import asyncio +import sys +from typing import ( # noqa + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Iterable, + List, + NamedTuple, + Optional, + Union, +) + +from multidict import CIMultiDict + +from .abc import AbstractStreamWriter +from .base_protocol import BaseProtocol +from .client_exceptions import ClientConnectionResetError +from .compression_utils import ZLibCompressor +from .helpers import NO_EXTENSIONS + +__all__ = ("StreamWriter", "HttpVersion", "HttpVersion10", "HttpVersion11") + + +MIN_PAYLOAD_FOR_WRITELINES = 2048 +IS_PY313_BEFORE_313_2 = (3, 13, 0) <= sys.version_info < (3, 13, 2) +IS_PY_BEFORE_312_9 = sys.version_info < (3, 12, 9) +SKIP_WRITELINES = IS_PY313_BEFORE_313_2 or IS_PY_BEFORE_312_9 +# writelines is not safe for use +# on Python 3.12+ until 3.12.9 +# on Python 3.13+ until 3.13.2 +# and on older versions it not any faster than write +# CVE-2024-12254: https://github.com/python/cpython/pull/127656 + + +class HttpVersion(NamedTuple): + major: int + minor: int + + +HttpVersion10 = HttpVersion(1, 0) +HttpVersion11 = HttpVersion(1, 1) + + +_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]] +_T_OnHeadersSent = Optional[Callable[["CIMultiDict[str]"], Awaitable[None]]] + + +class StreamWriter(AbstractStreamWriter): + + length: Optional[int] = None + chunked: bool = False + _eof: bool = False + _compress: Optional[ZLibCompressor] = None + + def __init__( + self, + protocol: BaseProtocol, + loop: asyncio.AbstractEventLoop, + on_chunk_sent: _T_OnChunkSent = None, + on_headers_sent: _T_OnHeadersSent = None, + ) -> None: + self._protocol = protocol + self.loop = loop + self._on_chunk_sent: _T_OnChunkSent = on_chunk_sent + self._on_headers_sent: _T_OnHeadersSent = on_headers_sent + self._headers_buf: Optional[bytes] = None + self._headers_written: bool = False + + @property + def transport(self) -> Optional[asyncio.Transport]: + return self._protocol.transport + + @property + def protocol(self) -> BaseProtocol: + return self._protocol + + def enable_chunking(self) -> None: + self.chunked = True + + def enable_compression( + self, encoding: str = "deflate", strategy: Optional[int] = None + ) -> None: + self._compress = ZLibCompressor(encoding=encoding, strategy=strategy) + + def _write(self, chunk: Union[bytes, bytearray, memoryview]) -> None: + size = len(chunk) + self.buffer_size += size + self.output_size += size + transport = self._protocol.transport + if transport is None or transport.is_closing(): + raise ClientConnectionResetError("Cannot write to closing transport") + transport.write(chunk) + + def _writelines(self, chunks: Iterable[bytes]) -> None: + size = 0 + for chunk in chunks: + size += len(chunk) + self.buffer_size += size + self.output_size += size + transport = self._protocol.transport + if transport is None or transport.is_closing(): + raise ClientConnectionResetError("Cannot write to closing transport") + if SKIP_WRITELINES or size < MIN_PAYLOAD_FOR_WRITELINES: + transport.write(b"".join(chunks)) + else: + transport.writelines(chunks) + + def _write_chunked_payload( + self, chunk: Union[bytes, bytearray, "memoryview[int]", "memoryview[bytes]"] + ) -> None: + """Write a chunk with proper chunked encoding.""" + chunk_len_pre = f"{len(chunk):x}\r\n".encode("ascii") + self._writelines((chunk_len_pre, chunk, b"\r\n")) + + def _send_headers_with_payload( + self, + chunk: Union[bytes, bytearray, "memoryview[int]", "memoryview[bytes]"], + is_eof: bool, + ) -> None: + """Send buffered headers with payload, coalescing into single write.""" + # Mark headers as written + self._headers_written = True + headers_buf = self._headers_buf + self._headers_buf = None + + if TYPE_CHECKING: + # Safe because callers (write() and write_eof()) only invoke this method + # after checking that self._headers_buf is truthy + assert headers_buf is not None + + if not self.chunked: + # Non-chunked: coalesce headers with body + if chunk: + self._writelines((headers_buf, chunk)) + else: + self._write(headers_buf) + return + + # Coalesce headers with chunked data + if chunk: + chunk_len_pre = f"{len(chunk):x}\r\n".encode("ascii") + if is_eof: + self._writelines((headers_buf, chunk_len_pre, chunk, b"\r\n0\r\n\r\n")) + else: + self._writelines((headers_buf, chunk_len_pre, chunk, b"\r\n")) + elif is_eof: + self._writelines((headers_buf, b"0\r\n\r\n")) + else: + self._write(headers_buf) + + async def write( + self, + chunk: Union[bytes, bytearray, memoryview], + *, + drain: bool = True, + LIMIT: int = 0x10000, + ) -> None: + """ + Writes chunk of data to a stream. + + write_eof() indicates end of stream. + writer can't be used after write_eof() method being called. + write() return drain future. + """ + if self._on_chunk_sent is not None: + await self._on_chunk_sent(chunk) + + if isinstance(chunk, memoryview): + if chunk.nbytes != len(chunk): + # just reshape it + chunk = chunk.cast("c") + + if self._compress is not None: + chunk = await self._compress.compress(chunk) + if not chunk: + return + + if self.length is not None: + chunk_len = len(chunk) + if self.length >= chunk_len: + self.length = self.length - chunk_len + else: + chunk = chunk[: self.length] + self.length = 0 + if not chunk: + return + + # Handle buffered headers for small payload optimization + if self._headers_buf and not self._headers_written: + self._send_headers_with_payload(chunk, False) + if drain and self.buffer_size > LIMIT: + self.buffer_size = 0 + await self.drain() + return + + if chunk: + if self.chunked: + self._write_chunked_payload(chunk) + else: + self._write(chunk) + + if drain and self.buffer_size > LIMIT: + self.buffer_size = 0 + await self.drain() + + async def write_headers( + self, status_line: str, headers: "CIMultiDict[str]" + ) -> None: + """Write headers to the stream.""" + if self._on_headers_sent is not None: + await self._on_headers_sent(headers) + # status + headers + buf = _serialize_headers(status_line, headers) + self._headers_written = False + self._headers_buf = buf + + def send_headers(self) -> None: + """Force sending buffered headers if not already sent.""" + if not self._headers_buf or self._headers_written: + return + + self._headers_written = True + headers_buf = self._headers_buf + self._headers_buf = None + + if TYPE_CHECKING: + # Safe because we only enter this block when self._headers_buf is truthy + assert headers_buf is not None + + self._write(headers_buf) + + def set_eof(self) -> None: + """Indicate that the message is complete.""" + if self._eof: + return + + # If headers haven't been sent yet, send them now + # This handles the case where there's no body at all + if self._headers_buf and not self._headers_written: + self._headers_written = True + headers_buf = self._headers_buf + self._headers_buf = None + + if TYPE_CHECKING: + # Safe because we only enter this block when self._headers_buf is truthy + assert headers_buf is not None + + # Combine headers and chunked EOF marker in a single write + if self.chunked: + self._writelines((headers_buf, b"0\r\n\r\n")) + else: + self._write(headers_buf) + elif self.chunked and self._headers_written: + # Headers already sent, just send the final chunk marker + self._write(b"0\r\n\r\n") + + self._eof = True + + async def write_eof(self, chunk: bytes = b"") -> None: + if self._eof: + return + + if chunk and self._on_chunk_sent is not None: + await self._on_chunk_sent(chunk) + + # Handle body/compression + if self._compress: + chunks: List[bytes] = [] + chunks_len = 0 + if chunk and (compressed_chunk := await self._compress.compress(chunk)): + chunks_len = len(compressed_chunk) + chunks.append(compressed_chunk) + + flush_chunk = self._compress.flush() + chunks_len += len(flush_chunk) + chunks.append(flush_chunk) + assert chunks_len + + # Send buffered headers with compressed data if not yet sent + if self._headers_buf and not self._headers_written: + self._headers_written = True + headers_buf = self._headers_buf + self._headers_buf = None + + if self.chunked: + # Coalesce headers with compressed chunked data + chunk_len_pre = f"{chunks_len:x}\r\n".encode("ascii") + self._writelines( + (headers_buf, chunk_len_pre, *chunks, b"\r\n0\r\n\r\n") + ) + else: + # Coalesce headers with compressed data + self._writelines((headers_buf, *chunks)) + await self.drain() + self._eof = True + return + + # Headers already sent, just write compressed data + if self.chunked: + chunk_len_pre = f"{chunks_len:x}\r\n".encode("ascii") + self._writelines((chunk_len_pre, *chunks, b"\r\n0\r\n\r\n")) + elif len(chunks) > 1: + self._writelines(chunks) + else: + self._write(chunks[0]) + await self.drain() + self._eof = True + return + + # No compression - send buffered headers if not yet sent + if self._headers_buf and not self._headers_written: + # Use helper to send headers with payload + self._send_headers_with_payload(chunk, True) + await self.drain() + self._eof = True + return + + # Handle remaining body + if self.chunked: + if chunk: + # Write final chunk with EOF marker + self._writelines( + (f"{len(chunk):x}\r\n".encode("ascii"), chunk, b"\r\n0\r\n\r\n") + ) + else: + self._write(b"0\r\n\r\n") + await self.drain() + self._eof = True + return + + if chunk: + self._write(chunk) + await self.drain() + + self._eof = True + + async def drain(self) -> None: + """Flush the write buffer. + + The intended use is to write + + await w.write(data) + await w.drain() + """ + protocol = self._protocol + if protocol.transport is not None and protocol._paused: + await protocol._drain_helper() + + +def _safe_header(string: str) -> str: + if "\r" in string or "\n" in string or "\x00" in string: + raise ValueError( + "Newline, carriage return, or null byte detected in headers. " + "Potential header injection attack." + ) + return string + + +def _py_serialize_headers(status_line: str, headers: "CIMultiDict[str]") -> bytes: + _safe_header(status_line) + headers_gen = (_safe_header(k) + ": " + _safe_header(v) for k, v in headers.items()) + line = status_line + "\r\n" + "\r\n".join(headers_gen) + "\r\n\r\n" + return line.encode("utf-8") + + +_serialize_headers = _py_serialize_headers + +try: + import aiohttp._http_writer as _http_writer # type: ignore[import-not-found] + + _c_serialize_headers = _http_writer._serialize_headers + if not NO_EXTENSIONS: + _serialize_headers = _c_serialize_headers +except ImportError: + pass diff --git a/venv/lib/python3.12/site-packages/aiohttp/log.py b/venv/lib/python3.12/site-packages/aiohttp/log.py new file mode 100644 index 00000000..3cecea2b --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/log.py @@ -0,0 +1,8 @@ +import logging + +access_logger = logging.getLogger("aiohttp.access") +client_logger = logging.getLogger("aiohttp.client") +internal_logger = logging.getLogger("aiohttp.internal") +server_logger = logging.getLogger("aiohttp.server") +web_logger = logging.getLogger("aiohttp.web") +ws_logger = logging.getLogger("aiohttp.websocket") diff --git a/venv/lib/python3.12/site-packages/aiohttp/multipart.py b/venv/lib/python3.12/site-packages/aiohttp/multipart.py new file mode 100644 index 00000000..ec8b82b7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/multipart.py @@ -0,0 +1,1213 @@ +import base64 +import binascii +import json +import re +import sys +import uuid +import warnings +from collections import deque +from collections.abc import Mapping, Sequence +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + AsyncIterator, + Deque, + Dict, + Iterator, + List, + Optional, + Tuple, + Type, + Union, + cast, +) +from urllib.parse import parse_qsl, unquote, urlencode + +from multidict import CIMultiDict, CIMultiDictProxy + +from .abc import AbstractStreamWriter +from .compression_utils import ( + DEFAULT_MAX_DECOMPRESS_SIZE, + ZLibCompressor, + ZLibDecompressor, +) +from .hdrs import ( + CONTENT_DISPOSITION, + CONTENT_ENCODING, + CONTENT_LENGTH, + CONTENT_TRANSFER_ENCODING, + CONTENT_TYPE, +) +from .helpers import CHAR, TOKEN, parse_mimetype, reify +from .http import HeadersParser +from .http_exceptions import BadHttpMessage +from .log import internal_logger +from .payload import ( + JsonPayload, + LookupError, + Order, + Payload, + StringPayload, + get_payload, + payload_type, +) +from .streams import StreamReader + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing import TypeVar + + Self = TypeVar("Self", bound="BodyPartReader") + +__all__ = ( + "MultipartReader", + "MultipartWriter", + "BodyPartReader", + "BadContentDispositionHeader", + "BadContentDispositionParam", + "parse_content_disposition", + "content_disposition_filename", +) + + +if TYPE_CHECKING: + from .client_reqrep import ClientResponse + + +class BadContentDispositionHeader(RuntimeWarning): + pass + + +class BadContentDispositionParam(RuntimeWarning): + pass + + +def parse_content_disposition( + header: Optional[str], +) -> Tuple[Optional[str], Dict[str, str]]: + def is_token(string: str) -> bool: + return bool(string) and TOKEN >= set(string) + + def is_quoted(string: str) -> bool: + return string[0] == string[-1] == '"' + + def is_rfc5987(string: str) -> bool: + return is_token(string) and string.count("'") == 2 + + def is_extended_param(string: str) -> bool: + return string.endswith("*") + + def is_continuous_param(string: str) -> bool: + pos = string.find("*") + 1 + if not pos: + return False + substring = string[pos:-1] if string.endswith("*") else string[pos:] + return substring.isdigit() + + def unescape(text: str, *, chars: str = "".join(map(re.escape, CHAR))) -> str: + return re.sub(f"\\\\([{chars}])", "\\1", text) + + if not header: + return None, {} + + disptype, *parts = header.split(";") + if not is_token(disptype): + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + params: Dict[str, str] = {} + while parts: + item = parts.pop(0) + + if not item: # To handle trailing semicolons + warnings.warn(BadContentDispositionHeader(header)) + continue + + if "=" not in item: + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + key, value = item.split("=", 1) + key = key.lower().strip() + value = value.lstrip() + + if key in params: + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + if not is_token(key): + warnings.warn(BadContentDispositionParam(item)) + continue + + elif is_continuous_param(key): + if is_quoted(value): + value = unescape(value[1:-1]) + elif not is_token(value): + warnings.warn(BadContentDispositionParam(item)) + continue + + elif is_extended_param(key): + if is_rfc5987(value): + encoding, _, value = value.split("'", 2) + encoding = encoding or "utf-8" + else: + warnings.warn(BadContentDispositionParam(item)) + continue + + try: + value = unquote(value, encoding, "strict") + except UnicodeDecodeError: # pragma: nocover + warnings.warn(BadContentDispositionParam(item)) + continue + + else: + failed = True + if is_quoted(value): + failed = False + value = unescape(value[1:-1].lstrip("\\/")) + elif is_token(value): + failed = False + elif parts: + # maybe just ; in filename, in any case this is just + # one case fix, for proper fix we need to redesign parser + _value = f"{value};{parts[0]}" + if is_quoted(_value): + parts.pop(0) + value = unescape(_value[1:-1].lstrip("\\/")) + failed = False + + if failed: + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + params[key] = value + + return disptype.lower(), params + + +def content_disposition_filename( + params: Mapping[str, str], name: str = "filename" +) -> Optional[str]: + name_suf = "%s*" % name + if not params: + return None + elif name_suf in params: + return params[name_suf] + elif name in params: + return params[name] + else: + parts = [] + fnparams = sorted( + (key, value) for key, value in params.items() if key.startswith(name_suf) + ) + for num, (key, value) in enumerate(fnparams): + _, tail = key.split("*", 1) + if tail.endswith("*"): + tail = tail[:-1] + if tail == str(num): + parts.append(value) + else: + break + if not parts: + return None + value = "".join(parts) + if "'" in value: + encoding, _, value = value.split("'", 2) + encoding = encoding or "utf-8" + return unquote(value, encoding, "strict") + return value + + +class MultipartResponseWrapper: + """Wrapper around the MultipartReader. + + It takes care about + underlying connection and close it when it needs in. + """ + + def __init__( + self, + resp: "ClientResponse", + stream: "MultipartReader", + ) -> None: + self.resp = resp + self.stream = stream + + def __aiter__(self) -> "MultipartResponseWrapper": + return self + + async def __anext__( + self, + ) -> Union["MultipartReader", "BodyPartReader"]: + part = await self.next() + if part is None: + raise StopAsyncIteration + return part + + def at_eof(self) -> bool: + """Returns True when all response data had been read.""" + return self.resp.content.at_eof() + + async def next( + self, + ) -> Optional[Union["MultipartReader", "BodyPartReader"]]: + """Emits next multipart reader object.""" + item = await self.stream.next() + if self.stream.at_eof(): + await self.release() + return item + + async def release(self) -> None: + """Release the connection gracefully. + + All remaining content is read to the void. + """ + await self.resp.release() + + +class BodyPartReader: + """Multipart reader for single body part.""" + + chunk_size = 8192 + + def __init__( + self, + boundary: bytes, + headers: "CIMultiDictProxy[str]", + content: StreamReader, + *, + subtype: str = "mixed", + default_charset: Optional[str] = None, + max_decompress_size: int = DEFAULT_MAX_DECOMPRESS_SIZE, + ) -> None: + self.headers = headers + self._boundary = boundary + self._boundary_len = len(boundary) + 2 # Boundary + \r\n + self._content = content + self._default_charset = default_charset + self._at_eof = False + self._is_form_data = subtype == "form-data" + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8 + length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None) + self._length = int(length) if length is not None else None + self._read_bytes = 0 + self._unread: Deque[bytes] = deque() + self._prev_chunk: Optional[bytes] = None + self._content_eof = 0 + self._cache: Dict[str, Any] = {} + self._max_decompress_size = max_decompress_size + + def __aiter__(self: Self) -> Self: + return self + + async def __anext__(self) -> bytes: + part = await self.next() + if part is None: + raise StopAsyncIteration + return part + + async def next(self) -> Optional[bytes]: + item = await self.read() + if not item: + return None + return item + + async def read(self, *, decode: bool = False) -> bytes: + """Reads body part data. + + decode: Decodes data following by encoding + method from Content-Encoding header. If it missed + data remains untouched + """ + if self._at_eof: + return b"" + data = bytearray() + while not self._at_eof: + data.extend(await self.read_chunk(self.chunk_size)) + if decode: + decoded_data = bytearray() + async for d in self.decode_iter(data): + decoded_data.extend(d) + return decoded_data + return data + + async def read_chunk(self, size: int = chunk_size) -> bytes: + """Reads body part content chunk of the specified size. + + size: chunk size + """ + if self._at_eof: + return b"" + if self._length: + chunk = await self._read_chunk_from_length(size) + else: + chunk = await self._read_chunk_from_stream(size) + + # For the case of base64 data, we must read a fragment of size with a + # remainder of 0 by dividing by 4 for string without symbols \n or \r + encoding = self.headers.get(CONTENT_TRANSFER_ENCODING) + if encoding and encoding.lower() == "base64": + stripped_chunk = b"".join(chunk.split()) + remainder = len(stripped_chunk) % 4 + + while remainder != 0 and not self.at_eof(): + over_chunk_size = 4 - remainder + over_chunk = b"" + + if self._prev_chunk: + over_chunk = self._prev_chunk[:over_chunk_size] + self._prev_chunk = self._prev_chunk[len(over_chunk) :] + + if len(over_chunk) != over_chunk_size: + over_chunk += await self._content.read(4 - len(over_chunk)) + + if not over_chunk: + self._at_eof = True + + stripped_chunk += b"".join(over_chunk.split()) + chunk += over_chunk + remainder = len(stripped_chunk) % 4 + + self._read_bytes += len(chunk) + if self._read_bytes == self._length: + self._at_eof = True + if self._at_eof and await self._content.readline() != b"\r\n": + raise ValueError("Reader did not read all the data or it is malformed") + return chunk + + async def _read_chunk_from_length(self, size: int) -> bytes: + # Reads body part content chunk of the specified size. + # The body part must has Content-Length header with proper value. + assert self._length is not None, "Content-Length required for chunked read" + chunk_size = min(size, self._length - self._read_bytes) + chunk = await self._content.read(chunk_size) + if self._content.at_eof(): + self._at_eof = True + return chunk + + async def _read_chunk_from_stream(self, size: int) -> bytes: + # Reads content chunk of body part with unknown length. + # The Content-Length header for body part is not necessary. + assert ( + size >= self._boundary_len + ), "Chunk size must be greater or equal than boundary length + 2" + first_chunk = self._prev_chunk is None + if first_chunk: + # We need to re-add the CRLF that got removed from headers parsing. + self._prev_chunk = b"\r\n" + await self._content.read(size) + + chunk = b"" + # content.read() may return less than size, so we need to loop to ensure + # we have enough data to detect the boundary. + while len(chunk) < self._boundary_len: + chunk += await self._content.read(size) + self._content_eof += int(self._content.at_eof()) + if self._content_eof > 2: + raise ValueError("Reading after EOF") + if self._content_eof: + break + if len(chunk) > size: + self._content.unread_data(chunk[size:]) + chunk = chunk[:size] + + assert self._prev_chunk is not None + window = self._prev_chunk + chunk + sub = b"\r\n" + self._boundary + if first_chunk: + idx = window.find(sub) + else: + idx = window.find(sub, max(0, len(self._prev_chunk) - len(sub))) + if idx >= 0: + # pushing boundary back to content + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + self._content.unread_data(window[idx:]) + self._prev_chunk = self._prev_chunk[:idx] + chunk = window[len(self._prev_chunk) : idx] + if not chunk: + self._at_eof = True + result = self._prev_chunk[2 if first_chunk else 0 :] # Strip initial CRLF + self._prev_chunk = chunk + return result + + async def readline(self) -> bytes: + """Reads body part by line by line.""" + if self._at_eof: + return b"" + + if self._unread: + line = self._unread.popleft() + else: + line = await self._content.readline() + + if line.startswith(self._boundary): + # the very last boundary may not come with \r\n, + # so set single rules for everyone + sline = line.rstrip(b"\r\n") + boundary = self._boundary + last_boundary = self._boundary + b"--" + # ensure that we read exactly the boundary, not something alike + if sline == boundary or sline == last_boundary: + self._at_eof = True + self._unread.append(line) + return b"" + else: + next_line = await self._content.readline() + if next_line.startswith(self._boundary): + line = line[:-2] # strip CRLF but only once + self._unread.append(next_line) + + return line + + async def release(self) -> None: + """Like read(), but reads all the data to the void.""" + if self._at_eof: + return + while not self._at_eof: + await self.read_chunk(self.chunk_size) + + async def text(self, *, encoding: Optional[str] = None) -> str: + """Like read(), but assumes that body part contains text data.""" + data = await self.read(decode=True) + # see https://www.w3.org/TR/html5/forms.html#multipart/form-data-encoding-algorithm + # and https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-send + encoding = encoding or self.get_charset(default="utf-8") + return data.decode(encoding) + + async def json(self, *, encoding: Optional[str] = None) -> Optional[Dict[str, Any]]: + """Like read(), but assumes that body parts contains JSON data.""" + data = await self.read(decode=True) + if not data: + return None + encoding = encoding or self.get_charset(default="utf-8") + return cast(Dict[str, Any], json.loads(data.decode(encoding))) + + async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]: + """Like read(), but assumes that body parts contain form urlencoded data.""" + data = await self.read(decode=True) + if not data: + return [] + if encoding is not None: + real_encoding = encoding + else: + real_encoding = self.get_charset(default="utf-8") + try: + decoded_data = data.rstrip().decode(real_encoding) + except UnicodeDecodeError: + raise ValueError("data cannot be decoded with %s encoding" % real_encoding) + + return parse_qsl( + decoded_data, + keep_blank_values=True, + encoding=real_encoding, + ) + + def at_eof(self) -> bool: + """Returns True if the boundary was reached or False otherwise.""" + return self._at_eof + + def _apply_content_transfer_decoding(self, data: bytes) -> bytes: + """Apply Content-Transfer-Encoding decoding if header is present.""" + if CONTENT_TRANSFER_ENCODING in self.headers: + return self._decode_content_transfer(data) + return data + + def _needs_content_decoding(self) -> bool: + """Check if Content-Encoding decoding should be applied.""" + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8 + return not self._is_form_data and CONTENT_ENCODING in self.headers + + def decode(self, data: bytes) -> bytes: + """Decodes data synchronously. + + Decodes data according the specified Content-Encoding + or Content-Transfer-Encoding headers value. + + Note: For large payloads, consider using decode_iter() instead + to avoid blocking the event loop during decompression. + """ + data = self._apply_content_transfer_decoding(data) + if self._needs_content_decoding(): + return self._decode_content(data) + return data + + async def decode_iter(self, data: bytes) -> AsyncIterator[bytes]: + """Async generator that yields decoded data chunks. + + Decodes data according the specified Content-Encoding + or Content-Transfer-Encoding headers value. + + This method offloads decompression to an executor for large payloads + to avoid blocking the event loop. + """ + data = self._apply_content_transfer_decoding(data) + if self._needs_content_decoding(): + async for d in self._decode_content_async(data): + yield d + else: + yield data + + def _decode_content(self, data: bytes) -> bytes: + encoding = self.headers.get(CONTENT_ENCODING, "").lower() + if encoding == "identity": + return data + if encoding in {"deflate", "gzip"}: + return ZLibDecompressor( + encoding=encoding, + suppress_deflate_header=True, + ).decompress_sync(data, max_length=self._max_decompress_size) + + raise RuntimeError(f"unknown content encoding: {encoding}") + + async def _decode_content_async(self, data: bytes) -> AsyncIterator[bytes]: + encoding = self.headers.get(CONTENT_ENCODING, "").lower() + if encoding == "identity": + yield data + elif encoding in {"deflate", "gzip"}: + d = ZLibDecompressor( + encoding=encoding, + suppress_deflate_header=True, + ) + yield await d.decompress(data, max_length=self._max_decompress_size) + else: + raise RuntimeError(f"unknown content encoding: {encoding}") + + def _decode_content_transfer(self, data: bytes) -> bytes: + encoding = self.headers.get(CONTENT_TRANSFER_ENCODING, "").lower() + + if encoding == "base64": + return base64.b64decode(data) + elif encoding == "quoted-printable": + return binascii.a2b_qp(data) + elif encoding in ("binary", "8bit", "7bit"): + return data + else: + raise RuntimeError(f"unknown content transfer encoding: {encoding}") + + def get_charset(self, default: str) -> str: + """Returns charset parameter from Content-Type header or default.""" + ctype = self.headers.get(CONTENT_TYPE, "") + mimetype = parse_mimetype(ctype) + return mimetype.parameters.get("charset", self._default_charset or default) + + @reify + def name(self) -> Optional[str]: + """Returns name specified in Content-Disposition header. + + If the header is missing or malformed, returns None. + """ + _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION)) + return content_disposition_filename(params, "name") + + @reify + def filename(self) -> Optional[str]: + """Returns filename specified in Content-Disposition header. + + Returns None if the header is missing or malformed. + """ + _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION)) + return content_disposition_filename(params, "filename") + + +@payload_type(BodyPartReader, order=Order.try_first) +class BodyPartReaderPayload(Payload): + _value: BodyPartReader + # _autoclose = False (inherited) - Streaming reader that may have resources + + def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None: + super().__init__(value, *args, **kwargs) + + params: Dict[str, str] = {} + if value.name is not None: + params["name"] = value.name + if value.filename is not None: + params["filename"] = value.filename + + if params: + self.set_content_disposition("attachment", True, **params) + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """Raises TypeError as body parts should be consumed via write(). + + This is intentional: BodyPartReader payloads are designed for streaming + large data (potentially gigabytes) and must be consumed only once via + the write() method to avoid memory exhaustion. They cannot be buffered + in memory for reuse. + """ + raise TypeError("Unable to read body part as bytes. Use write() to consume.") + + async def write(self, writer: AbstractStreamWriter) -> None: + field = self._value + while chunk := await field.read_chunk(size=2**18): + async for d in field.decode_iter(chunk): + await writer.write(d) + + +class MultipartReader: + """Multipart body reader.""" + + #: Response wrapper, used when multipart readers constructs from response. + response_wrapper_cls = MultipartResponseWrapper + #: Multipart reader class, used to handle multipart/* body parts. + #: None points to type(self) + multipart_reader_cls: Optional[Type["MultipartReader"]] = None + #: Body part reader class for non multipart/* content types. + part_reader_cls = BodyPartReader + + def __init__( + self, + headers: Mapping[str, str], + content: StreamReader, + *, + max_field_size: int = 8190, + max_headers: int = 128, + ) -> None: + self._mimetype = parse_mimetype(headers[CONTENT_TYPE]) + assert self._mimetype.type == "multipart", "multipart/* content type expected" + if "boundary" not in self._mimetype.parameters: + raise ValueError( + "boundary missed for Content-Type: %s" % headers[CONTENT_TYPE] + ) + + self.headers = headers + self._boundary = ("--" + self._get_boundary()).encode() + self._content = content + self._default_charset: str | None = None + self._last_part: MultipartReader | BodyPartReader | None = None + self._max_field_size = max_field_size + self._max_headers = max_headers + self._at_eof = False + self._at_bof = True + self._unread: List[bytes] = [] + + def __aiter__(self: Self) -> Self: + return self + + async def __anext__( + self, + ) -> Optional[Union["MultipartReader", BodyPartReader]]: + part = await self.next() + if part is None: + raise StopAsyncIteration + return part + + @classmethod + def from_response( + cls, + response: "ClientResponse", + ) -> MultipartResponseWrapper: + """Constructs reader instance from HTTP response. + + :param response: :class:`~aiohttp.client.ClientResponse` instance + """ + obj = cls.response_wrapper_cls( + response, cls(response.headers, response.content) + ) + return obj + + def at_eof(self) -> bool: + """Returns True if the final boundary was reached, false otherwise.""" + return self._at_eof + + async def next( + self, + ) -> Optional[Union["MultipartReader", BodyPartReader]]: + """Emits the next multipart body part.""" + # So, if we're at BOF, we need to skip till the boundary. + if self._at_eof: + return None + await self._maybe_release_last_part() + if self._at_bof: + await self._read_until_first_boundary() + self._at_bof = False + else: + await self._read_boundary() + if self._at_eof: # we just read the last boundary, nothing to do there + return None + + part = await self.fetch_next_part() + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.6 + if ( + self._last_part is None + and self._mimetype.subtype == "form-data" + and isinstance(part, BodyPartReader) + ): + _, params = parse_content_disposition(part.headers.get(CONTENT_DISPOSITION)) + if params.get("name") == "_charset_": + # Longest encoding in https://encoding.spec.whatwg.org/encodings.json + # is 19 characters, so 32 should be more than enough for any valid encoding. + charset = await part.read_chunk(32) + if len(charset) > 31: + raise RuntimeError("Invalid default charset") + self._default_charset = charset.strip().decode() + part = await self.fetch_next_part() + self._last_part = part + return self._last_part + + async def release(self) -> None: + """Reads all the body parts to the void till the final boundary.""" + while not self._at_eof: + item = await self.next() + if item is None: + break + await item.release() + + async def fetch_next_part( + self, + ) -> Union["MultipartReader", BodyPartReader]: + """Returns the next body part reader.""" + headers = await self._read_headers() + return self._get_part_reader(headers) + + def _get_part_reader( + self, + headers: "CIMultiDictProxy[str]", + ) -> Union["MultipartReader", BodyPartReader]: + """Dispatches the response by the `Content-Type` header. + + Returns a suitable reader instance. + + :param dict headers: Response headers + """ + ctype = headers.get(CONTENT_TYPE, "") + mimetype = parse_mimetype(ctype) + + if mimetype.type == "multipart": + if self.multipart_reader_cls is None: + return type(self)(headers, self._content) + return self.multipart_reader_cls( + headers, + self._content, + max_field_size=self._max_field_size, + max_headers=self._max_headers, + ) + else: + return self.part_reader_cls( + self._boundary, + headers, + self._content, + subtype=self._mimetype.subtype, + default_charset=self._default_charset, + ) + + def _get_boundary(self) -> str: + boundary = self._mimetype.parameters["boundary"] + if len(boundary) > 70: + raise ValueError("boundary %r is too long (70 chars max)" % boundary) + + return boundary + + async def _readline(self) -> bytes: + if self._unread: + return self._unread.pop() + return await self._content.readline() + + async def _read_until_first_boundary(self) -> None: + while True: + chunk = await self._readline() + if chunk == b"": + raise ValueError( + "Could not find starting boundary %r" % (self._boundary) + ) + chunk = chunk.rstrip() + if chunk == self._boundary: + return + elif chunk == self._boundary + b"--": + self._at_eof = True + return + + async def _read_boundary(self) -> None: + chunk = (await self._readline()).rstrip() + if chunk == self._boundary: + pass + elif chunk == self._boundary + b"--": + self._at_eof = True + epilogue = await self._readline() + next_line = await self._readline() + + # the epilogue is expected and then either the end of input or the + # parent multipart boundary, if the parent boundary is found then + # it should be marked as unread and handed to the parent for + # processing + if next_line[:2] == b"--": + self._unread.append(next_line) + # otherwise the request is likely missing an epilogue and both + # lines should be passed to the parent for processing + # (this handles the old behavior gracefully) + else: + self._unread.extend([next_line, epilogue]) + else: + raise ValueError(f"Invalid boundary {chunk!r}, expected {self._boundary!r}") + + async def _read_headers(self) -> "CIMultiDictProxy[str]": + lines = [] + while True: + chunk = await self._content.readline(max_line_length=self._max_field_size) + chunk = chunk.rstrip(b"\r\n") + lines.append(chunk) + if not chunk: + break + if len(lines) > self._max_headers: + raise BadHttpMessage("Too many headers received") + parser = HeadersParser(max_field_size=self._max_field_size) + headers, raw_headers = parser.parse_headers(lines) + return headers + + async def _maybe_release_last_part(self) -> None: + """Ensures that the last read body part is read completely.""" + if self._last_part is not None: + if not self._last_part.at_eof(): + await self._last_part.release() + self._unread.extend(self._last_part._unread) + self._last_part = None + + +_Part = Tuple[Payload, str, str] + + +class MultipartWriter(Payload): + """Multipart body writer.""" + + _value: None + # _consumed = False (inherited) - Can be encoded multiple times + _autoclose = True # No file handles, just collects parts in memory + + def __init__(self, subtype: str = "mixed", boundary: Optional[str] = None) -> None: + boundary = boundary if boundary is not None else uuid.uuid4().hex + # The underlying Payload API demands a str (utf-8), not bytes, + # so we need to ensure we don't lose anything during conversion. + # As a result, require the boundary to be ASCII only. + # In both situations. + + try: + self._boundary = boundary.encode("ascii") + except UnicodeEncodeError: + raise ValueError("boundary should contain ASCII only chars") from None + ctype = f"multipart/{subtype}; boundary={self._boundary_value}" + + super().__init__(None, content_type=ctype) + + self._parts: List[_Part] = [] + self._is_form_data = subtype == "form-data" + + def __enter__(self) -> "MultipartWriter": + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + pass + + def __iter__(self) -> Iterator[_Part]: + return iter(self._parts) + + def __len__(self) -> int: + return len(self._parts) + + def __bool__(self) -> bool: + return True + + _valid_tchar_regex = re.compile(rb"\A[!#$%&'*+\-.^_`|~\w]+\Z") + _invalid_qdtext_char_regex = re.compile(rb"[\x00-\x08\x0A-\x1F\x7F]") + + @property + def _boundary_value(self) -> str: + """Wrap boundary parameter value in quotes, if necessary. + + Reads self.boundary and returns a unicode string. + """ + # Refer to RFCs 7231, 7230, 5234. + # + # parameter = token "=" ( token / quoted-string ) + # token = 1*tchar + # quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + # qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text + # obs-text = %x80-FF + # quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" + # / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" + # / DIGIT / ALPHA + # ; any VCHAR, except delimiters + # VCHAR = %x21-7E + value = self._boundary + if re.match(self._valid_tchar_regex, value): + return value.decode("ascii") # cannot fail + + if re.search(self._invalid_qdtext_char_regex, value): + raise ValueError("boundary value contains invalid characters") + + # escape %x5C and %x22 + quoted_value_content = value.replace(b"\\", b"\\\\") + quoted_value_content = quoted_value_content.replace(b'"', b'\\"') + + return '"' + quoted_value_content.decode("ascii") + '"' + + @property + def boundary(self) -> str: + return self._boundary.decode("ascii") + + def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload: + if headers is None: + headers = CIMultiDict() + + if isinstance(obj, Payload): + obj.headers.update(headers) + return self.append_payload(obj) + else: + try: + payload = get_payload(obj, headers=headers) + except LookupError: + raise TypeError("Cannot create payload from %r" % obj) + else: + return self.append_payload(payload) + + def append_payload(self, payload: Payload) -> Payload: + """Adds a new body part to multipart writer.""" + encoding: Optional[str] = None + te_encoding: Optional[str] = None + if self._is_form_data: + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7 + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8 + assert ( + not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING} + & payload.headers.keys() + ) + # Set default Content-Disposition in case user doesn't create one + if CONTENT_DISPOSITION not in payload.headers: + name = f"section-{len(self._parts)}" + payload.set_content_disposition("form-data", name=name) + else: + # compression + encoding = payload.headers.get(CONTENT_ENCODING, "").lower() + if encoding and encoding not in ("deflate", "gzip", "identity"): + raise RuntimeError(f"unknown content encoding: {encoding}") + if encoding == "identity": + encoding = None + + # te encoding + te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, "").lower() + if te_encoding not in ("", "base64", "quoted-printable", "binary"): + raise RuntimeError(f"unknown content transfer encoding: {te_encoding}") + if te_encoding == "binary": + te_encoding = None + + # size + size = payload.size + if size is not None and not (encoding or te_encoding): + payload.headers[CONTENT_LENGTH] = str(size) + + self._parts.append((payload, encoding, te_encoding)) # type: ignore[arg-type] + return payload + + def append_json( + self, obj: Any, headers: Optional[Mapping[str, str]] = None + ) -> Payload: + """Helper to append JSON part.""" + if headers is None: + headers = CIMultiDict() + + return self.append_payload(JsonPayload(obj, headers=headers)) + + def append_form( + self, + obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]], + headers: Optional[Mapping[str, str]] = None, + ) -> Payload: + """Helper to append form urlencoded part.""" + assert isinstance(obj, (Sequence, Mapping)) + + if headers is None: + headers = CIMultiDict() + + if isinstance(obj, Mapping): + obj = list(obj.items()) + data = urlencode(obj, doseq=True) + + return self.append_payload( + StringPayload( + data, headers=headers, content_type="application/x-www-form-urlencoded" + ) + ) + + @property + def size(self) -> Optional[int]: + """Size of the payload.""" + total = 0 + for part, encoding, te_encoding in self._parts: + part_size = part.size + if encoding or te_encoding or part_size is None: + return None + + total += int( + 2 + + len(self._boundary) + + 2 + + part_size # b'--'+self._boundary+b'\r\n' + + len(part._binary_headers) + + 2 # b'\r\n' + ) + + total += 2 + len(self._boundary) + 4 # b'--'+self._boundary+b'--\r\n' + return total + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """Return string representation of the multipart data. + + WARNING: This method may do blocking I/O if parts contain file payloads. + It should not be called in the event loop. Use as_bytes().decode() instead. + """ + return "".join( + "--" + + self.boundary + + "\r\n" + + part._binary_headers.decode(encoding, errors) + + part.decode() + for part, _e, _te in self._parts + ) + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """Return bytes representation of the multipart data. + + This method is async-safe and calls as_bytes on underlying payloads. + """ + parts: List[bytes] = [] + + # Process each part + for part, _e, _te in self._parts: + # Add boundary + parts.append(b"--" + self._boundary + b"\r\n") + + # Add headers + parts.append(part._binary_headers) + + # Add payload content using as_bytes for async safety + part_bytes = await part.as_bytes(encoding, errors) + parts.append(part_bytes) + + # Add trailing CRLF + parts.append(b"\r\n") + + # Add closing boundary + parts.append(b"--" + self._boundary + b"--\r\n") + + return b"".join(parts) + + async def write( + self, writer: AbstractStreamWriter, close_boundary: bool = True + ) -> None: + """Write body.""" + for part, encoding, te_encoding in self._parts: + if self._is_form_data: + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.2 + assert CONTENT_DISPOSITION in part.headers + assert "name=" in part.headers[CONTENT_DISPOSITION] + + await writer.write(b"--" + self._boundary + b"\r\n") + await writer.write(part._binary_headers) + + if encoding or te_encoding: + w = MultipartPayloadWriter(writer) + if encoding: + w.enable_compression(encoding) + if te_encoding: + w.enable_encoding(te_encoding) + await part.write(w) # type: ignore[arg-type] + await w.write_eof() + else: + await part.write(writer) + + await writer.write(b"\r\n") + + if close_boundary: + await writer.write(b"--" + self._boundary + b"--\r\n") + + async def close(self) -> None: + """ + Close all part payloads that need explicit closing. + + IMPORTANT: This method must not await anything that might not finish + immediately, as it may be called during cleanup/cancellation. Schedule + any long-running operations without awaiting them. + """ + if self._consumed: + return + self._consumed = True + + # Close all parts that need explicit closing + # We catch and log exceptions to ensure all parts get a chance to close + # we do not use asyncio.gather() here because we are not allowed + # to suspend given we may be called during cleanup + for idx, (part, _, _) in enumerate(self._parts): + if not part.autoclose and not part.consumed: + try: + await part.close() + except Exception as exc: + internal_logger.error( + "Failed to close multipart part %d: %s", idx, exc, exc_info=True + ) + + +class MultipartPayloadWriter: + def __init__(self, writer: AbstractStreamWriter) -> None: + self._writer = writer + self._encoding: Optional[str] = None + self._compress: Optional[ZLibCompressor] = None + self._encoding_buffer: Optional[bytearray] = None + + def enable_encoding(self, encoding: str) -> None: + if encoding == "base64": + self._encoding = encoding + self._encoding_buffer = bytearray() + elif encoding == "quoted-printable": + self._encoding = "quoted-printable" + + def enable_compression( + self, encoding: str = "deflate", strategy: Optional[int] = None + ) -> None: + self._compress = ZLibCompressor( + encoding=encoding, + suppress_deflate_header=True, + strategy=strategy, + ) + + async def write_eof(self) -> None: + if self._compress is not None: + chunk = self._compress.flush() + if chunk: + self._compress = None + await self.write(chunk) + + if self._encoding == "base64": + if self._encoding_buffer: + await self._writer.write(base64.b64encode(self._encoding_buffer)) + + async def write(self, chunk: bytes) -> None: + if self._compress is not None: + if chunk: + chunk = await self._compress.compress(chunk) + if not chunk: + return + + if self._encoding == "base64": + buf = self._encoding_buffer + assert buf is not None + buf.extend(chunk) + + if buf: + div, mod = divmod(len(buf), 3) + enc_chunk, self._encoding_buffer = (buf[: div * 3], buf[div * 3 :]) + if enc_chunk: + b64chunk = base64.b64encode(enc_chunk) + await self._writer.write(b64chunk) + elif self._encoding == "quoted-printable": + await self._writer.write(binascii.b2a_qp(chunk)) + else: + await self._writer.write(chunk) diff --git a/venv/lib/python3.12/site-packages/aiohttp/payload.py b/venv/lib/python3.12/site-packages/aiohttp/payload.py new file mode 100644 index 00000000..5b88fa09 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/payload.py @@ -0,0 +1,1120 @@ +import asyncio +import enum +import io +import json +import mimetypes +import os +import sys +import warnings +from abc import ABC, abstractmethod +from collections.abc import Iterable +from itertools import chain +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + Final, + List, + Optional, + Set, + TextIO, + Tuple, + Type, + Union, +) + +from multidict import CIMultiDict + +from . import hdrs +from .abc import AbstractStreamWriter +from .helpers import ( + _SENTINEL, + content_disposition_header, + guess_filename, + parse_mimetype, + sentinel, +) +from .streams import StreamReader +from .typedefs import JSONEncoder, _CIMultiDict + +__all__ = ( + "PAYLOAD_REGISTRY", + "get_payload", + "payload_type", + "Payload", + "BytesPayload", + "StringPayload", + "IOBasePayload", + "BytesIOPayload", + "BufferedReaderPayload", + "TextIOPayload", + "StringIOPayload", + "JsonPayload", + "AsyncIterablePayload", +) + +TOO_LARGE_BYTES_BODY: Final[int] = 2**20 # 1 MB +READ_SIZE: Final[int] = 2**16 # 64 KB +_CLOSE_FUTURES: Set[asyncio.Future[None]] = set() + + +class LookupError(Exception): + """Raised when no payload factory is found for the given data type.""" + + +class Order(str, enum.Enum): + normal = "normal" + try_first = "try_first" + try_last = "try_last" + + +def get_payload(data: Any, *args: Any, **kwargs: Any) -> "Payload": + return PAYLOAD_REGISTRY.get(data, *args, **kwargs) + + +def register_payload( + factory: Type["Payload"], type: Any, *, order: Order = Order.normal +) -> None: + PAYLOAD_REGISTRY.register(factory, type, order=order) + + +class payload_type: + def __init__(self, type: Any, *, order: Order = Order.normal) -> None: + self.type = type + self.order = order + + def __call__(self, factory: Type["Payload"]) -> Type["Payload"]: + register_payload(factory, self.type, order=self.order) + return factory + + +PayloadType = Type["Payload"] +_PayloadRegistryItem = Tuple[PayloadType, Any] + + +class PayloadRegistry: + """Payload registry. + + note: we need zope.interface for more efficient adapter search + """ + + __slots__ = ("_first", "_normal", "_last", "_normal_lookup") + + def __init__(self) -> None: + self._first: List[_PayloadRegistryItem] = [] + self._normal: List[_PayloadRegistryItem] = [] + self._last: List[_PayloadRegistryItem] = [] + self._normal_lookup: Dict[Any, PayloadType] = {} + + def get( + self, + data: Any, + *args: Any, + _CHAIN: "Type[chain[_PayloadRegistryItem]]" = chain, + **kwargs: Any, + ) -> "Payload": + if self._first: + for factory, type_ in self._first: + if isinstance(data, type_): + return factory(data, *args, **kwargs) + # Try the fast lookup first + if lookup_factory := self._normal_lookup.get(type(data)): + return lookup_factory(data, *args, **kwargs) + # Bail early if its already a Payload + if isinstance(data, Payload): + return data + # Fallback to the slower linear search + for factory, type_ in _CHAIN(self._normal, self._last): + if isinstance(data, type_): + return factory(data, *args, **kwargs) + raise LookupError() + + def register( + self, factory: PayloadType, type: Any, *, order: Order = Order.normal + ) -> None: + if order is Order.try_first: + self._first.append((factory, type)) + elif order is Order.normal: + self._normal.append((factory, type)) + if isinstance(type, Iterable): + for t in type: + self._normal_lookup[t] = factory + else: + self._normal_lookup[type] = factory + elif order is Order.try_last: + self._last.append((factory, type)) + else: + raise ValueError(f"Unsupported order {order!r}") + + +class Payload(ABC): + + _default_content_type: str = "application/octet-stream" + _size: Optional[int] = None + _consumed: bool = False # Default: payload has not been consumed yet + _autoclose: bool = False # Default: assume resource needs explicit closing + + def __init__( + self, + value: Any, + headers: Optional[ + Union[_CIMultiDict, Dict[str, str], Iterable[Tuple[str, str]]] + ] = None, + content_type: Union[str, None, _SENTINEL] = sentinel, + filename: Optional[str] = None, + encoding: Optional[str] = None, + **kwargs: Any, + ) -> None: + self._encoding = encoding + self._filename = filename + self._headers: _CIMultiDict = CIMultiDict() + self._value = value + if content_type is not sentinel and content_type is not None: + self._headers[hdrs.CONTENT_TYPE] = content_type + elif self._filename is not None: + if sys.version_info >= (3, 13): + guesser = mimetypes.guess_file_type + else: + guesser = mimetypes.guess_type + content_type = guesser(self._filename)[0] + if content_type is None: + content_type = self._default_content_type + self._headers[hdrs.CONTENT_TYPE] = content_type + else: + self._headers[hdrs.CONTENT_TYPE] = self._default_content_type + if headers: + self._headers.update(headers) + + @property + def size(self) -> Optional[int]: + """Size of the payload in bytes. + + Returns the number of bytes that will be transmitted when the payload + is written. For string payloads, this is the size after encoding to bytes, + not the length of the string. + """ + return self._size + + @property + def filename(self) -> Optional[str]: + """Filename of the payload.""" + return self._filename + + @property + def headers(self) -> _CIMultiDict: + """Custom item headers""" + return self._headers + + @property + def _binary_headers(self) -> bytes: + return ( + "".join([k + ": " + v + "\r\n" for k, v in self.headers.items()]).encode( + "utf-8" + ) + + b"\r\n" + ) + + @property + def encoding(self) -> Optional[str]: + """Payload encoding""" + return self._encoding + + @property + def content_type(self) -> str: + """Content type""" + return self._headers[hdrs.CONTENT_TYPE] + + @property + def consumed(self) -> bool: + """Whether the payload has been consumed and cannot be reused.""" + return self._consumed + + @property + def autoclose(self) -> bool: + """ + Whether the payload can close itself automatically. + + Returns True if the payload has no file handles or resources that need + explicit closing. If False, callers must await close() to release resources. + """ + return self._autoclose + + def set_content_disposition( + self, + disptype: str, + quote_fields: bool = True, + _charset: str = "utf-8", + **params: Any, + ) -> None: + """Sets ``Content-Disposition`` header.""" + self._headers[hdrs.CONTENT_DISPOSITION] = content_disposition_header( + disptype, quote_fields=quote_fields, _charset=_charset, **params + ) + + @abstractmethod + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """ + Return string representation of the value. + + This is named decode() to allow compatibility with bytes objects. + """ + + @abstractmethod + async def write(self, writer: AbstractStreamWriter) -> None: + """ + Write payload to the writer stream. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + + This is a legacy method that writes the entire payload without length constraints. + + Important: + For new implementations, use write_with_length() instead of this method. + This method is maintained for backwards compatibility and will eventually + delegate to write_with_length(writer, None) in all implementations. + + All payload subclasses must override this method for backwards compatibility, + but new code should use write_with_length for more flexibility and control. + + """ + + # write_with_length is new in aiohttp 3.12 + # it should be overridden by subclasses + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This method allows writing payload content with a specific length constraint, + which is particularly useful for HTTP responses with Content-Length header. + + Note: + This is the base implementation that provides backwards compatibility + for subclasses that don't override this method. Specific payload types + should override this method to implement proper length-constrained writing. + + """ + # Backwards compatibility for subclasses that don't override this method + # and for the default implementation + await self.write(writer) + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This is a convenience method that calls decode() and encodes the result + to bytes using the specified encoding. + """ + # Use instance encoding if available, otherwise use parameter + actual_encoding = self._encoding or encoding + return self.decode(actual_encoding, errors).encode(actual_encoding) + + def _close(self) -> None: + """ + Async safe synchronous close operations for backwards compatibility. + + This method exists only for backwards compatibility with code that + needs to clean up payloads synchronously. In the future, we will + drop this method and only support the async close() method. + + WARNING: This method must be safe to call from within the event loop + without blocking. Subclasses should not perform any blocking I/O here. + + WARNING: This method must be called from within an event loop for + certain payload types (e.g., IOBasePayload). Calling it outside an + event loop may raise RuntimeError. + """ + # This is a no-op by default, but subclasses can override it + # for non-blocking cleanup operations. + + async def close(self) -> None: + """ + Close the payload if it holds any resources. + + IMPORTANT: This method must not await anything that might not finish + immediately, as it may be called during cleanup/cancellation. Schedule + any long-running operations without awaiting them. + + In the future, this will be the only close method supported. + """ + self._close() + + +class BytesPayload(Payload): + _value: bytes + # _consumed = False (inherited) - Bytes are immutable and can be reused + _autoclose = True # No file handle, just bytes in memory + + def __init__( + self, value: Union[bytes, bytearray, memoryview], *args: Any, **kwargs: Any + ) -> None: + if "content_type" not in kwargs: + kwargs["content_type"] = "application/octet-stream" + + super().__init__(value, *args, **kwargs) + + if isinstance(value, memoryview): + self._size = value.nbytes + elif isinstance(value, (bytes, bytearray)): + self._size = len(value) + else: + raise TypeError(f"value argument must be byte-ish, not {type(value)!r}") + + if self._size > TOO_LARGE_BYTES_BODY: + kwargs = {"source": self} + warnings.warn( + "Sending a large body directly with raw bytes might" + " lock the event loop. You should probably pass an " + "io.BytesIO object instead", + ResourceWarning, + **kwargs, + ) + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.decode(encoding, errors) + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method returns the raw bytes content of the payload. + It is equivalent to accessing the _value attribute directly. + """ + return self._value + + async def write(self, writer: AbstractStreamWriter) -> None: + """ + Write the entire bytes payload to the writer stream. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + + This method writes the entire bytes content without any length constraint. + + Note: + For new implementations that need length control, use write_with_length(). + This method is maintained for backwards compatibility and is equivalent + to write_with_length(writer, None). + + """ + await writer.write(self._value) + + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write bytes payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This method writes either the entire byte sequence or a slice of it + up to the specified content_length. For BytesPayload, this operation + is performed efficiently using array slicing. + + """ + if content_length is not None: + await writer.write(self._value[:content_length]) + else: + await writer.write(self._value) + + +class StringPayload(BytesPayload): + def __init__( + self, + value: str, + *args: Any, + encoding: Optional[str] = None, + content_type: Optional[str] = None, + **kwargs: Any, + ) -> None: + + if encoding is None: + if content_type is None: + real_encoding = "utf-8" + content_type = "text/plain; charset=utf-8" + else: + mimetype = parse_mimetype(content_type) + real_encoding = mimetype.parameters.get("charset", "utf-8") + else: + if content_type is None: + content_type = "text/plain; charset=%s" % encoding + real_encoding = encoding + + super().__init__( + value.encode(real_encoding), + encoding=real_encoding, + content_type=content_type, + *args, + **kwargs, + ) + + +class StringIOPayload(StringPayload): + def __init__(self, value: IO[str], *args: Any, **kwargs: Any) -> None: + super().__init__(value.read(), *args, **kwargs) + + +class IOBasePayload(Payload): + _value: io.IOBase + # _consumed = False (inherited) - File can be re-read from the same position + _start_position: Optional[int] = None + # _autoclose = False (inherited) - Has file handle that needs explicit closing + + def __init__( + self, value: IO[Any], disposition: str = "attachment", *args: Any, **kwargs: Any + ) -> None: + if "filename" not in kwargs: + kwargs["filename"] = guess_filename(value) + + super().__init__(value, *args, **kwargs) + + if self._filename is not None and disposition is not None: + if hdrs.CONTENT_DISPOSITION not in self.headers: + self.set_content_disposition(disposition, filename=self._filename) + + def _set_or_restore_start_position(self) -> None: + """Set or restore the start position of the file-like object.""" + if self._start_position is None: + try: + self._start_position = self._value.tell() + except (OSError, AttributeError): + self._consumed = True # Cannot seek, mark as consumed + return + try: + self._value.seek(self._start_position) + except (OSError, AttributeError): + # Failed to seek back - mark as consumed since we've already read + self._consumed = True + + def _read_and_available_len( + self, remaining_content_len: Optional[int] + ) -> Tuple[Optional[int], bytes]: + """ + Read the file-like object and return both its total size and the first chunk. + + Args: + remaining_content_len: Optional limit on how many bytes to read in this operation. + If None, READ_SIZE will be used as the default chunk size. + + Returns: + A tuple containing: + - The total size of the remaining unread content (None if size cannot be determined) + - The first chunk of bytes read from the file object + + This method is optimized to perform both size calculation and initial read + in a single operation, which is executed in a single executor job to minimize + context switches and file operations when streaming content. + + """ + self._set_or_restore_start_position() + size = self.size # Call size only once since it does I/O + return size, self._value.read( + min(READ_SIZE, size or READ_SIZE, remaining_content_len or READ_SIZE) + ) + + def _read(self, remaining_content_len: Optional[int]) -> bytes: + """ + Read a chunk of data from the file-like object. + + Args: + remaining_content_len: Optional maximum number of bytes to read. + If None, READ_SIZE will be used as the default chunk size. + + Returns: + A chunk of bytes read from the file object, respecting the + remaining_content_len limit if specified. + + This method is used for subsequent reads during streaming after + the initial _read_and_available_len call has been made. + + """ + return self._value.read(remaining_content_len or READ_SIZE) # type: ignore[no-any-return] + + @property + def size(self) -> Optional[int]: + """ + Size of the payload in bytes. + + Returns the total size of the payload content from the initial position. + This ensures consistent Content-Length for requests, including 307/308 redirects + where the same payload instance is reused. + + Returns None if the size cannot be determined (e.g., for unseekable streams). + """ + try: + # Store the start position on first access. + # This is critical when the same payload instance is reused (e.g., 307/308 + # redirects). Without storing the initial position, after the payload is + # read once, the file position would be at EOF, which would cause the + # size calculation to return 0 (file_size - EOF position). + # By storing the start position, we ensure the size calculation always + # returns the correct total size for any subsequent use. + if self._start_position is None: + self._start_position = self._value.tell() + + # Return the total size from the start position + # This ensures Content-Length is correct even after reading + return os.fstat(self._value.fileno()).st_size - self._start_position + except (AttributeError, OSError): + return None + + async def write(self, writer: AbstractStreamWriter) -> None: + """ + Write the entire file-like payload to the writer stream. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + + This method writes the entire file content without any length constraint. + It delegates to write_with_length() with no length limit for implementation + consistency. + + Note: + For new implementations that need length control, use write_with_length() directly. + This method is maintained for backwards compatibility with existing code. + + """ + await self.write_with_length(writer, None) + + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write file-like payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This method implements optimized streaming of file content with length constraints: + + 1. File reading is performed in a thread pool to avoid blocking the event loop + 2. Content is read and written in chunks to maintain memory efficiency + 3. Writing stops when either: + - All available file content has been written (when size is known) + - The specified content_length has been reached + 4. File resources are properly closed even if the operation is cancelled + + The implementation carefully handles both known-size and unknown-size payloads, + as well as constrained and unconstrained content lengths. + + """ + loop = asyncio.get_running_loop() + total_written_len = 0 + remaining_content_len = content_length + + # Get initial data and available length + available_len, chunk = await loop.run_in_executor( + None, self._read_and_available_len, remaining_content_len + ) + # Process data chunks until done + while chunk: + chunk_len = len(chunk) + + # Write data with or without length constraint + if remaining_content_len is None: + await writer.write(chunk) + else: + await writer.write(chunk[:remaining_content_len]) + remaining_content_len -= chunk_len + + total_written_len += chunk_len + + # Check if we're done writing + if self._should_stop_writing( + available_len, total_written_len, remaining_content_len + ): + return + + # Read next chunk + chunk = await loop.run_in_executor( + None, + self._read, + ( + min(READ_SIZE, remaining_content_len) + if remaining_content_len is not None + else READ_SIZE + ), + ) + + def _should_stop_writing( + self, + available_len: Optional[int], + total_written_len: int, + remaining_content_len: Optional[int], + ) -> bool: + """ + Determine if we should stop writing data. + + Args: + available_len: Known size of the payload if available (None if unknown) + total_written_len: Number of bytes already written + remaining_content_len: Remaining bytes to be written for content-length limited responses + + Returns: + True if we should stop writing data, based on either: + - Having written all available data (when size is known) + - Having written all requested content (when content-length is specified) + + """ + return (available_len is not None and total_written_len >= available_len) or ( + remaining_content_len is not None and remaining_content_len <= 0 + ) + + def _close(self) -> None: + """ + Async safe synchronous close operations for backwards compatibility. + + This method exists only for backwards + compatibility. Use the async close() method instead. + + WARNING: This method MUST be called from within an event loop. + Calling it outside an event loop will raise RuntimeError. + """ + # Skip if already consumed + if self._consumed: + return + self._consumed = True # Mark as consumed to prevent further writes + # Schedule file closing without awaiting to prevent cancellation issues + loop = asyncio.get_running_loop() + close_future = loop.run_in_executor(None, self._value.close) + # Hold a strong reference to the future to prevent it from being + # garbage collected before it completes. + _CLOSE_FUTURES.add(close_future) + close_future.add_done_callback(_CLOSE_FUTURES.remove) + + async def close(self) -> None: + """ + Close the payload if it holds any resources. + + IMPORTANT: This method must not await anything that might not finish + immediately, as it may be called during cleanup/cancellation. Schedule + any long-running operations without awaiting them. + """ + self._close() + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """ + Return string representation of the value. + + WARNING: This method does blocking I/O and should not be called in the event loop. + """ + return self._read_all().decode(encoding, errors) + + def _read_all(self) -> bytes: + """Read the entire file-like object and return its content as bytes.""" + self._set_or_restore_start_position() + # Use readlines() to ensure we get all content + return b"".join(self._value.readlines()) + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method reads the entire file content and returns it as bytes. + It is equivalent to reading the file-like object directly. + The file reading is performed in an executor to avoid blocking the event loop. + """ + loop = asyncio.get_running_loop() + return await loop.run_in_executor(None, self._read_all) + + +class TextIOPayload(IOBasePayload): + _value: io.TextIOBase + # _autoclose = False (inherited) - Has text file handle that needs explicit closing + + def __init__( + self, + value: TextIO, + *args: Any, + encoding: Optional[str] = None, + content_type: Optional[str] = None, + **kwargs: Any, + ) -> None: + + if encoding is None: + if content_type is None: + encoding = "utf-8" + content_type = "text/plain; charset=utf-8" + else: + mimetype = parse_mimetype(content_type) + encoding = mimetype.parameters.get("charset", "utf-8") + else: + if content_type is None: + content_type = "text/plain; charset=%s" % encoding + + super().__init__( + value, + content_type=content_type, + encoding=encoding, + *args, + **kwargs, + ) + + def _read_and_available_len( + self, remaining_content_len: Optional[int] + ) -> Tuple[Optional[int], bytes]: + """ + Read the text file-like object and return both its total size and the first chunk. + + Args: + remaining_content_len: Optional limit on how many bytes to read in this operation. + If None, READ_SIZE will be used as the default chunk size. + + Returns: + A tuple containing: + - The total size of the remaining unread content (None if size cannot be determined) + - The first chunk of bytes read from the file object, encoded using the payload's encoding + + This method is optimized to perform both size calculation and initial read + in a single operation, which is executed in a single executor job to minimize + context switches and file operations when streaming content. + + Note: + TextIOPayload handles encoding of the text content before writing it + to the stream. If no encoding is specified, UTF-8 is used as the default. + + """ + self._set_or_restore_start_position() + size = self.size + chunk = self._value.read( + min(READ_SIZE, size or READ_SIZE, remaining_content_len or READ_SIZE) + ) + return size, chunk.encode(self._encoding) if self._encoding else chunk.encode() + + def _read(self, remaining_content_len: Optional[int]) -> bytes: + """ + Read a chunk of data from the text file-like object. + + Args: + remaining_content_len: Optional maximum number of bytes to read. + If None, READ_SIZE will be used as the default chunk size. + + Returns: + A chunk of bytes read from the file object and encoded using the payload's + encoding. The data is automatically converted from text to bytes. + + This method is used for subsequent reads during streaming after + the initial _read_and_available_len call has been made. It properly + handles text encoding, converting the text content to bytes using + the specified encoding (or UTF-8 if none was provided). + + """ + chunk = self._value.read(remaining_content_len or READ_SIZE) + return chunk.encode(self._encoding) if self._encoding else chunk.encode() + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """ + Return string representation of the value. + + WARNING: This method does blocking I/O and should not be called in the event loop. + """ + self._set_or_restore_start_position() + return self._value.read() + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method reads the entire text file content and returns it as bytes. + It encodes the text content using the specified encoding. + The file reading is performed in an executor to avoid blocking the event loop. + """ + loop = asyncio.get_running_loop() + + # Use instance encoding if available, otherwise use parameter + actual_encoding = self._encoding or encoding + + def _read_and_encode() -> bytes: + self._set_or_restore_start_position() + # TextIO read() always returns the full content + return self._value.read().encode(actual_encoding, errors) + + return await loop.run_in_executor(None, _read_and_encode) + + +class BytesIOPayload(IOBasePayload): + _value: io.BytesIO + _size: int # Always initialized in __init__ + _autoclose = True # BytesIO is in-memory, safe to auto-close + + def __init__(self, value: io.BytesIO, *args: Any, **kwargs: Any) -> None: + super().__init__(value, *args, **kwargs) + # Calculate size once during initialization + self._size = len(self._value.getbuffer()) - self._value.tell() + + @property + def size(self) -> int: + """Size of the payload in bytes. + + Returns the number of bytes in the BytesIO buffer that will be transmitted. + This is calculated once during initialization for efficiency. + """ + return self._size + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + self._set_or_restore_start_position() + return self._value.read().decode(encoding, errors) + + async def write(self, writer: AbstractStreamWriter) -> None: + return await self.write_with_length(writer, None) + + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write BytesIO payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This implementation is specifically optimized for BytesIO objects: + + 1. Reads content in chunks to maintain memory efficiency + 2. Yields control back to the event loop periodically to prevent blocking + when dealing with large BytesIO objects + 3. Respects content_length constraints when specified + 4. Properly cleans up by closing the BytesIO object when done or on error + + The periodic yielding to the event loop is important for maintaining + responsiveness when processing large in-memory buffers. + + """ + self._set_or_restore_start_position() + loop_count = 0 + remaining_bytes = content_length + while chunk := self._value.read(READ_SIZE): + if loop_count > 0: + # Avoid blocking the event loop + # if they pass a large BytesIO object + # and we are not in the first iteration + # of the loop + await asyncio.sleep(0) + if remaining_bytes is None: + await writer.write(chunk) + else: + await writer.write(chunk[:remaining_bytes]) + remaining_bytes -= len(chunk) + if remaining_bytes <= 0: + return + loop_count += 1 + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method reads the entire BytesIO content and returns it as bytes. + It is equivalent to accessing the _value attribute directly. + """ + self._set_or_restore_start_position() + return self._value.read() + + async def close(self) -> None: + """ + Close the BytesIO payload. + + This does nothing since BytesIO is in-memory and does not require explicit closing. + """ + + +class BufferedReaderPayload(IOBasePayload): + _value: io.BufferedIOBase + # _autoclose = False (inherited) - Has buffered file handle that needs explicit closing + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + self._set_or_restore_start_position() + return self._value.read().decode(encoding, errors) + + +class JsonPayload(BytesPayload): + def __init__( + self, + value: Any, + encoding: str = "utf-8", + content_type: str = "application/json", + dumps: JSONEncoder = json.dumps, + *args: Any, + **kwargs: Any, + ) -> None: + + super().__init__( + dumps(value).encode(encoding), + content_type=content_type, + encoding=encoding, + *args, + **kwargs, + ) + + +if TYPE_CHECKING: + from typing import AsyncIterable, AsyncIterator + + _AsyncIterator = AsyncIterator[bytes] + _AsyncIterable = AsyncIterable[bytes] +else: + from collections.abc import AsyncIterable, AsyncIterator + + _AsyncIterator = AsyncIterator + _AsyncIterable = AsyncIterable + + +class AsyncIterablePayload(Payload): + + _iter: Optional[_AsyncIterator] = None + _value: _AsyncIterable + _cached_chunks: Optional[List[bytes]] = None + # _consumed stays False to allow reuse with cached content + _autoclose = True # Iterator doesn't need explicit closing + + def __init__(self, value: _AsyncIterable, *args: Any, **kwargs: Any) -> None: + if not isinstance(value, AsyncIterable): + raise TypeError( + "value argument must support " + "collections.abc.AsyncIterable interface, " + "got {!r}".format(type(value)) + ) + + if "content_type" not in kwargs: + kwargs["content_type"] = "application/octet-stream" + + super().__init__(value, *args, **kwargs) + + self._iter = value.__aiter__() + + async def write(self, writer: AbstractStreamWriter) -> None: + """ + Write the entire async iterable payload to the writer stream. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + + This method iterates through the async iterable and writes each chunk + to the writer without any length constraint. + + Note: + For new implementations that need length control, use write_with_length() directly. + This method is maintained for backwards compatibility with existing code. + + """ + await self.write_with_length(writer, None) + + async def write_with_length( + self, writer: AbstractStreamWriter, content_length: Optional[int] + ) -> None: + """ + Write async iterable payload with a specific content length constraint. + + Args: + writer: An AbstractStreamWriter instance that handles the actual writing + content_length: Maximum number of bytes to write (None for unlimited) + + This implementation handles streaming of async iterable content with length constraints: + + 1. If cached chunks are available, writes from them + 2. Otherwise iterates through the async iterable one chunk at a time + 3. Respects content_length constraints when specified + 4. Does NOT generate cache - that's done by as_bytes() + + """ + # If we have cached chunks, use them + if self._cached_chunks is not None: + remaining_bytes = content_length + for chunk in self._cached_chunks: + if remaining_bytes is None: + await writer.write(chunk) + elif remaining_bytes > 0: + await writer.write(chunk[:remaining_bytes]) + remaining_bytes -= len(chunk) + else: + break + return + + # If iterator is exhausted and we don't have cached chunks, nothing to write + if self._iter is None: + return + + # Stream from the iterator + remaining_bytes = content_length + + try: + while True: + if sys.version_info >= (3, 10): + chunk = await anext(self._iter) + else: + chunk = await self._iter.__anext__() + if remaining_bytes is None: + await writer.write(chunk) + # If we have a content length limit + elif remaining_bytes > 0: + await writer.write(chunk[:remaining_bytes]) + remaining_bytes -= len(chunk) + # We still want to exhaust the iterator even + # if we have reached the content length limit + # since the file handle may not get closed by + # the iterator if we don't do this + except StopAsyncIteration: + # Iterator is exhausted + self._iter = None + self._consumed = True # Mark as consumed when streamed without caching + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """Decode the payload content as a string if cached chunks are available.""" + if self._cached_chunks is not None: + return b"".join(self._cached_chunks).decode(encoding, errors) + raise TypeError("Unable to decode - content not cached. Call as_bytes() first.") + + async def as_bytes(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: + """ + Return bytes representation of the value. + + This method reads the entire async iterable content and returns it as bytes. + It generates and caches the chunks for future reuse. + """ + # If we have cached chunks, return them joined + if self._cached_chunks is not None: + return b"".join(self._cached_chunks) + + # If iterator is exhausted and no cache, return empty + if self._iter is None: + return b"" + + # Read all chunks and cache them + chunks: List[bytes] = [] + async for chunk in self._iter: + chunks.append(chunk) + + # Iterator is exhausted, cache the chunks + self._iter = None + self._cached_chunks = chunks + # Keep _consumed as False to allow reuse with cached chunks + + return b"".join(chunks) + + +class StreamReaderPayload(AsyncIterablePayload): + def __init__(self, value: StreamReader, *args: Any, **kwargs: Any) -> None: + super().__init__(value.iter_any(), *args, **kwargs) + + +PAYLOAD_REGISTRY = PayloadRegistry() +PAYLOAD_REGISTRY.register(BytesPayload, (bytes, bytearray, memoryview)) +PAYLOAD_REGISTRY.register(StringPayload, str) +PAYLOAD_REGISTRY.register(StringIOPayload, io.StringIO) +PAYLOAD_REGISTRY.register(TextIOPayload, io.TextIOBase) +PAYLOAD_REGISTRY.register(BytesIOPayload, io.BytesIO) +PAYLOAD_REGISTRY.register(BufferedReaderPayload, (io.BufferedReader, io.BufferedRandom)) +PAYLOAD_REGISTRY.register(IOBasePayload, io.IOBase) +PAYLOAD_REGISTRY.register(StreamReaderPayload, StreamReader) +# try_last for giving a chance to more specialized async interables like +# multipart.BodyPartReaderPayload override the default +PAYLOAD_REGISTRY.register(AsyncIterablePayload, AsyncIterable, order=Order.try_last) diff --git a/venv/lib/python3.12/site-packages/aiohttp/payload_streamer.py b/venv/lib/python3.12/site-packages/aiohttp/payload_streamer.py new file mode 100644 index 00000000..831fdc0a --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/payload_streamer.py @@ -0,0 +1,78 @@ +""" +Payload implementation for coroutines as data provider. + +As a simple case, you can upload data from file:: + + @aiohttp.streamer + async def file_sender(writer, file_name=None): + with open(file_name, 'rb') as f: + chunk = f.read(2**16) + while chunk: + await writer.write(chunk) + + chunk = f.read(2**16) + +Then you can use `file_sender` like this: + + async with session.post('http://httpbin.org/post', + data=file_sender(file_name='huge_file')) as resp: + print(await resp.text()) + +..note:: Coroutine must accept `writer` as first argument + +""" + +import types +import warnings +from typing import Any, Awaitable, Callable, Dict, Tuple + +from .abc import AbstractStreamWriter +from .payload import Payload, payload_type + +__all__ = ("streamer",) + + +class _stream_wrapper: + def __init__( + self, + coro: Callable[..., Awaitable[None]], + args: Tuple[Any, ...], + kwargs: Dict[str, Any], + ) -> None: + self.coro = types.coroutine(coro) + self.args = args + self.kwargs = kwargs + + async def __call__(self, writer: AbstractStreamWriter) -> None: + await self.coro(writer, *self.args, **self.kwargs) + + +class streamer: + def __init__(self, coro: Callable[..., Awaitable[None]]) -> None: + warnings.warn( + "@streamer is deprecated, use async generators instead", + DeprecationWarning, + stacklevel=2, + ) + self.coro = coro + + def __call__(self, *args: Any, **kwargs: Any) -> _stream_wrapper: + return _stream_wrapper(self.coro, args, kwargs) + + +@payload_type(_stream_wrapper) +class StreamWrapperPayload(Payload): + async def write(self, writer: AbstractStreamWriter) -> None: + await self._value(writer) + + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + + +@payload_type(streamer) +class StreamPayload(StreamWrapperPayload): + def __init__(self, value: Any, *args: Any, **kwargs: Any) -> None: + super().__init__(value(), *args, **kwargs) + + async def write(self, writer: AbstractStreamWriter) -> None: + await self._value(writer) diff --git a/venv/lib/python3.12/site-packages/aiohttp/py.typed b/venv/lib/python3.12/site-packages/aiohttp/py.typed new file mode 100644 index 00000000..f5642f79 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/py.typed @@ -0,0 +1 @@ +Marker diff --git a/venv/lib/python3.12/site-packages/aiohttp/pytest_plugin.py b/venv/lib/python3.12/site-packages/aiohttp/pytest_plugin.py new file mode 100644 index 00000000..7d59fe82 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/pytest_plugin.py @@ -0,0 +1,444 @@ +import asyncio +import contextlib +import inspect +import warnings +from typing import ( + Any, + Awaitable, + Callable, + Dict, + Iterator, + Optional, + Protocol, + Union, + overload, +) + +import pytest + +from .test_utils import ( + BaseTestServer, + RawTestServer, + TestClient, + TestServer, + loop_context, + setup_test_loop, + teardown_test_loop, + unused_port as _unused_port, +) +from .web import Application, BaseRequest, Request +from .web_protocol import _RequestHandler + +try: + import uvloop +except ImportError: # pragma: no cover + uvloop = None # type: ignore[assignment] + + +class AiohttpClient(Protocol): + @overload + async def __call__( + self, + __param: Application, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[Request, Application]: ... + @overload + async def __call__( + self, + __param: BaseTestServer, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[BaseRequest, None]: ... + + +class AiohttpServer(Protocol): + def __call__( + self, app: Application, *, port: Optional[int] = None, **kwargs: Any + ) -> Awaitable[TestServer]: ... + + +class AiohttpRawServer(Protocol): + def __call__( + self, handler: _RequestHandler, *, port: Optional[int] = None, **kwargs: Any + ) -> Awaitable[RawTestServer]: ... + + +def pytest_addoption(parser): # type: ignore[no-untyped-def] + parser.addoption( + "--aiohttp-fast", + action="store_true", + default=False, + help="run tests faster by disabling extra checks", + ) + parser.addoption( + "--aiohttp-loop", + action="store", + default="pyloop", + help="run tests with specific loop: pyloop, uvloop or all", + ) + parser.addoption( + "--aiohttp-enable-loop-debug", + action="store_true", + default=False, + help="enable event loop debug mode", + ) + + +def pytest_fixture_setup(fixturedef): # type: ignore[no-untyped-def] + """Set up pytest fixture. + + Allow fixtures to be coroutines. Run coroutine fixtures in an event loop. + """ + func = fixturedef.func + + if inspect.isasyncgenfunction(func): + # async generator fixture + is_async_gen = True + elif inspect.iscoroutinefunction(func): + # regular async fixture + is_async_gen = False + else: + # not an async fixture, nothing to do + return + + strip_request = False + if "request" not in fixturedef.argnames: + fixturedef.argnames += ("request",) + strip_request = True + + def wrapper(*args, **kwargs): # type: ignore[no-untyped-def] + request = kwargs["request"] + if strip_request: + del kwargs["request"] + + # if neither the fixture nor the test use the 'loop' fixture, + # 'getfixturevalue' will fail because the test is not parameterized + # (this can be removed someday if 'loop' is no longer parameterized) + if "loop" not in request.fixturenames: + raise Exception( + "Asynchronous fixtures must depend on the 'loop' fixture or " + "be used in tests depending from it." + ) + + _loop = request.getfixturevalue("loop") + + if is_async_gen: + # for async generators, we need to advance the generator once, + # then advance it again in a finalizer + gen = func(*args, **kwargs) + + def finalizer(): # type: ignore[no-untyped-def] + try: + return _loop.run_until_complete(gen.__anext__()) + except StopAsyncIteration: + pass + + request.addfinalizer(finalizer) + return _loop.run_until_complete(gen.__anext__()) + else: + return _loop.run_until_complete(func(*args, **kwargs)) + + fixturedef.func = wrapper + + +@pytest.fixture +def fast(request): # type: ignore[no-untyped-def] + """--fast config option""" + return request.config.getoption("--aiohttp-fast") + + +@pytest.fixture +def loop_debug(request): # type: ignore[no-untyped-def] + """--enable-loop-debug config option""" + return request.config.getoption("--aiohttp-enable-loop-debug") + + +@contextlib.contextmanager +def _runtime_warning_context(): # type: ignore[no-untyped-def] + """Context manager which checks for RuntimeWarnings. + + This exists specifically to + avoid "coroutine 'X' was never awaited" warnings being missed. + + If RuntimeWarnings occur in the context a RuntimeError is raised. + """ + with warnings.catch_warnings(record=True) as _warnings: + yield + rw = [ + "{w.filename}:{w.lineno}:{w.message}".format(w=w) + for w in _warnings + if w.category == RuntimeWarning + ] + if rw: + raise RuntimeError( + "{} Runtime Warning{},\n{}".format( + len(rw), "" if len(rw) == 1 else "s", "\n".join(rw) + ) + ) + + +@contextlib.contextmanager +def _passthrough_loop_context(loop, fast=False): # type: ignore[no-untyped-def] + """Passthrough loop context. + + Sets up and tears down a loop unless one is passed in via the loop + argument when it's passed straight through. + """ + if loop: + # loop already exists, pass it straight through + yield loop + else: + # this shadows loop_context's standard behavior + loop = setup_test_loop() + yield loop + teardown_test_loop(loop, fast=fast) + + +def pytest_pycollect_makeitem(collector, name, obj): # type: ignore[no-untyped-def] + """Fix pytest collecting for coroutines.""" + if collector.funcnamefilter(name) and inspect.iscoroutinefunction(obj): + return list(collector._genfunctions(name, obj)) + + +def pytest_pyfunc_call(pyfuncitem): # type: ignore[no-untyped-def] + """Run coroutines in an event loop instead of a normal function call.""" + fast = pyfuncitem.config.getoption("--aiohttp-fast") + if inspect.iscoroutinefunction(pyfuncitem.function): + existing_loop = ( + pyfuncitem.funcargs.get("proactor_loop") + or pyfuncitem.funcargs.get("selector_loop") + or pyfuncitem.funcargs.get("uvloop_loop") + or pyfuncitem.funcargs.get("loop", None) + ) + + with _runtime_warning_context(): + with _passthrough_loop_context(existing_loop, fast=fast) as _loop: + testargs = { + arg: pyfuncitem.funcargs[arg] + for arg in pyfuncitem._fixtureinfo.argnames + } + _loop.run_until_complete(pyfuncitem.obj(**testargs)) + + return True + + +def pytest_generate_tests(metafunc): # type: ignore[no-untyped-def] + if "loop_factory" not in metafunc.fixturenames: + return + + loops = metafunc.config.option.aiohttp_loop + avail_factories: dict[str, Callable[[], asyncio.AbstractEventLoop]] + avail_factories = {"pyloop": asyncio.new_event_loop} + + if uvloop is not None: # pragma: no cover + avail_factories["uvloop"] = uvloop.new_event_loop + + if loops == "all": + loops = "pyloop,uvloop?" + + factories = {} # type: ignore[var-annotated] + for name in loops.split(","): + required = not name.endswith("?") + name = name.strip(" ?") + if name not in avail_factories: # pragma: no cover + if required: + raise ValueError( + "Unknown loop '%s', available loops: %s" + % (name, list(factories.keys())) + ) + else: + continue + factories[name] = avail_factories[name] + metafunc.parametrize( + "loop_factory", list(factories.values()), ids=list(factories.keys()) + ) + + +@pytest.fixture +def loop( + loop_factory: Callable[[], asyncio.AbstractEventLoop], + fast: bool, + loop_debug: bool, +) -> Iterator[asyncio.AbstractEventLoop]: + """Return an instance of the event loop.""" + with loop_context(loop_factory, fast=fast) as _loop: + if loop_debug: + _loop.set_debug(True) # pragma: no cover + asyncio.set_event_loop(_loop) + yield _loop + + +@pytest.fixture +def proactor_loop() -> Iterator[asyncio.AbstractEventLoop]: + factory = asyncio.ProactorEventLoop # type: ignore[attr-defined] + + with loop_context(factory) as _loop: + asyncio.set_event_loop(_loop) + yield _loop + + +@pytest.fixture +def unused_port(aiohttp_unused_port: Callable[[], int]) -> Callable[[], int]: + warnings.warn( + "Deprecated, use aiohttp_unused_port fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_unused_port + + +@pytest.fixture +def aiohttp_unused_port() -> Callable[[], int]: + """Return a port that is unused on the current host.""" + return _unused_port + + +@pytest.fixture +def aiohttp_server(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpServer]: + """Factory to create a TestServer instance, given an app. + + aiohttp_server(app, **kwargs) + """ + servers = [] + + async def go( + app: Application, + *, + host: str = "127.0.0.1", + port: Optional[int] = None, + **kwargs: Any, + ) -> TestServer: + server = TestServer(app, host=host, port=port) + await server.start_server(loop=loop, **kwargs) + servers.append(server) + return server + + yield go + + async def finalize() -> None: + while servers: + await servers.pop().close() + + loop.run_until_complete(finalize()) + + +@pytest.fixture +def test_server(aiohttp_server): # type: ignore[no-untyped-def] # pragma: no cover + warnings.warn( + "Deprecated, use aiohttp_server fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_server + + +@pytest.fixture +def aiohttp_raw_server(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpRawServer]: + """Factory to create a RawTestServer instance, given a web handler. + + aiohttp_raw_server(handler, **kwargs) + """ + servers = [] + + async def go( + handler: _RequestHandler, *, port: Optional[int] = None, **kwargs: Any + ) -> RawTestServer: + server = RawTestServer(handler, port=port) + await server.start_server(loop=loop, **kwargs) + servers.append(server) + return server + + yield go + + async def finalize() -> None: + while servers: + await servers.pop().close() + + loop.run_until_complete(finalize()) + + +@pytest.fixture +def raw_test_server( # type: ignore[no-untyped-def] # pragma: no cover + aiohttp_raw_server, +): + warnings.warn( + "Deprecated, use aiohttp_raw_server fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_raw_server + + +@pytest.fixture +def aiohttp_client(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpClient]: + """Factory to create a TestClient instance. + + aiohttp_client(app, **kwargs) + aiohttp_client(server, **kwargs) + aiohttp_client(raw_server, **kwargs) + """ + clients = [] + + @overload + async def go( + __param: Application, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[Request, Application]: ... + + @overload + async def go( + __param: BaseTestServer, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[BaseRequest, None]: ... + + async def go( + __param: Union[Application, BaseTestServer], + *args: Any, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> TestClient[Any, Any]: + if isinstance(__param, Callable) and not isinstance( # type: ignore[arg-type] + __param, (Application, BaseTestServer) + ): + __param = __param(loop, *args, **kwargs) + kwargs = {} + else: + assert not args, "args should be empty" + + if isinstance(__param, Application): + server_kwargs = server_kwargs or {} + server = TestServer(__param, loop=loop, **server_kwargs) + client = TestClient(server, loop=loop, **kwargs) + elif isinstance(__param, BaseTestServer): + client = TestClient(__param, loop=loop, **kwargs) + else: + raise ValueError("Unknown argument type: %r" % type(__param)) + + await client.start_server() + clients.append(client) + return client + + yield go + + async def finalize() -> None: + while clients: + await clients.pop().close() + + loop.run_until_complete(finalize()) + + +@pytest.fixture +def test_client(aiohttp_client): # type: ignore[no-untyped-def] # pragma: no cover + warnings.warn( + "Deprecated, use aiohttp_client fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_client diff --git a/venv/lib/python3.12/site-packages/aiohttp/resolver.py b/venv/lib/python3.12/site-packages/aiohttp/resolver.py new file mode 100644 index 00000000..b20e5672 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/resolver.py @@ -0,0 +1,274 @@ +import asyncio +import socket +import weakref +from typing import Any, Dict, Final, List, Optional, Tuple, Type, Union + +from .abc import AbstractResolver, ResolveResult + +__all__ = ("ThreadedResolver", "AsyncResolver", "DefaultResolver") + + +try: + import aiodns + + aiodns_default = hasattr(aiodns.DNSResolver, "getaddrinfo") +except ImportError: # pragma: no cover + aiodns = None # type: ignore[assignment] + aiodns_default = False + + +_NUMERIC_SOCKET_FLAGS = socket.AI_NUMERICHOST | socket.AI_NUMERICSERV +_NAME_SOCKET_FLAGS = socket.NI_NUMERICHOST | socket.NI_NUMERICSERV +_AI_ADDRCONFIG = socket.AI_ADDRCONFIG +if hasattr(socket, "AI_MASK"): + _AI_ADDRCONFIG &= socket.AI_MASK + + +class ThreadedResolver(AbstractResolver): + """Threaded resolver. + + Uses an Executor for synchronous getaddrinfo() calls. + concurrent.futures.ThreadPoolExecutor is used by default. + """ + + def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + self._loop = loop or asyncio.get_running_loop() + + async def resolve( + self, host: str, port: int = 0, family: socket.AddressFamily = socket.AF_INET + ) -> List[ResolveResult]: + infos = await self._loop.getaddrinfo( + host, + port, + type=socket.SOCK_STREAM, + family=family, + flags=_AI_ADDRCONFIG, + ) + + hosts: List[ResolveResult] = [] + for family, _, proto, _, address in infos: + if family == socket.AF_INET6: + if len(address) < 3: + # IPv6 is not supported by Python build, + # or IPv6 is not enabled in the host + continue + if address[3]: + # This is essential for link-local IPv6 addresses. + # LL IPv6 is a VERY rare case. Strictly speaking, we should use + # getnameinfo() unconditionally, but performance makes sense. + resolved_host, _port = await self._loop.getnameinfo( + address, _NAME_SOCKET_FLAGS + ) + port = int(_port) + else: + resolved_host, port = address[:2] + else: # IPv4 + assert family == socket.AF_INET + resolved_host, port = address # type: ignore[misc] + hosts.append( + ResolveResult( + hostname=host, + host=resolved_host, + port=port, + family=family, + proto=proto, + flags=_NUMERIC_SOCKET_FLAGS, + ) + ) + + return hosts + + async def close(self) -> None: + pass + + +class AsyncResolver(AbstractResolver): + """Use the `aiodns` package to make asynchronous DNS lookups""" + + def __init__( + self, + loop: Optional[asyncio.AbstractEventLoop] = None, + *args: Any, + **kwargs: Any, + ) -> None: + if aiodns is None: + raise RuntimeError("Resolver requires aiodns library") + + self._loop = loop or asyncio.get_running_loop() + self._manager: Optional[_DNSResolverManager] = None + # If custom args are provided, create a dedicated resolver instance + # This means each AsyncResolver with custom args gets its own + # aiodns.DNSResolver instance + if args or kwargs: + self._resolver = aiodns.DNSResolver(*args, **kwargs) + return + # Use the shared resolver from the manager for default arguments + self._manager = _DNSResolverManager() + self._resolver = self._manager.get_resolver(self, self._loop) + + if not hasattr(self._resolver, "gethostbyname"): + # aiodns 1.1 is not available, fallback to DNSResolver.query + self.resolve = self._resolve_with_query # type: ignore + + async def resolve( + self, host: str, port: int = 0, family: socket.AddressFamily = socket.AF_INET + ) -> List[ResolveResult]: + try: + resp = await self._resolver.getaddrinfo( + host, + port=port, + type=socket.SOCK_STREAM, + family=family, + flags=_AI_ADDRCONFIG, + ) + except aiodns.error.DNSError as exc: + msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" + raise OSError(None, msg) from exc + hosts: List[ResolveResult] = [] + for node in resp.nodes: + address: Union[Tuple[bytes, int], Tuple[bytes, int, int, int]] = node.addr + family = node.family + if family == socket.AF_INET6: + if len(address) > 3 and address[3]: + # This is essential for link-local IPv6 addresses. + # LL IPv6 is a VERY rare case. Strictly speaking, we should use + # getnameinfo() unconditionally, but performance makes sense. + result = await self._resolver.getnameinfo( + (address[0].decode("ascii"), *address[1:]), + _NAME_SOCKET_FLAGS, + ) + resolved_host = result.node + else: + resolved_host = address[0].decode("ascii") + port = address[1] + else: # IPv4 + assert family == socket.AF_INET + resolved_host = address[0].decode("ascii") + port = address[1] + hosts.append( + ResolveResult( + hostname=host, + host=resolved_host, + port=port, + family=family, + proto=0, + flags=_NUMERIC_SOCKET_FLAGS, + ) + ) + + if not hosts: + raise OSError(None, "DNS lookup failed") + + return hosts + + async def _resolve_with_query( + self, host: str, port: int = 0, family: int = socket.AF_INET + ) -> List[Dict[str, Any]]: + qtype: Final = "AAAA" if family == socket.AF_INET6 else "A" + + try: + resp = await self._resolver.query(host, qtype) + except aiodns.error.DNSError as exc: + msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" + raise OSError(None, msg) from exc + + hosts = [] + for rr in resp: + hosts.append( + { + "hostname": host, + "host": rr.host, + "port": port, + "family": family, + "proto": 0, + "flags": socket.AI_NUMERICHOST, + } + ) + + if not hosts: + raise OSError(None, "DNS lookup failed") + + return hosts + + async def close(self) -> None: + if self._manager: + # Release the resolver from the manager if using the shared resolver + self._manager.release_resolver(self, self._loop) + self._manager = None # Clear reference to manager + self._resolver = None # type: ignore[assignment] # Clear reference to resolver + return + # Otherwise cancel our dedicated resolver + if self._resolver is not None: + self._resolver.cancel() + self._resolver = None # type: ignore[assignment] # Clear reference + + +class _DNSResolverManager: + """Manager for aiodns.DNSResolver objects. + + This class manages shared aiodns.DNSResolver instances + with no custom arguments across different event loops. + """ + + _instance: Optional["_DNSResolverManager"] = None + + def __new__(cls) -> "_DNSResolverManager": + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._instance._init() + return cls._instance + + def _init(self) -> None: + # Use WeakKeyDictionary to allow event loops to be garbage collected + self._loop_data: weakref.WeakKeyDictionary[ + asyncio.AbstractEventLoop, + tuple["aiodns.DNSResolver", weakref.WeakSet["AsyncResolver"]], + ] = weakref.WeakKeyDictionary() + + def get_resolver( + self, client: "AsyncResolver", loop: asyncio.AbstractEventLoop + ) -> "aiodns.DNSResolver": + """Get or create the shared aiodns.DNSResolver instance for a specific event loop. + + Args: + client: The AsyncResolver instance requesting the resolver. + This is required to track resolver usage. + loop: The event loop to use for the resolver. + """ + # Create a new resolver and client set for this loop if it doesn't exist + if loop not in self._loop_data: + resolver = aiodns.DNSResolver(loop=loop) + client_set: weakref.WeakSet["AsyncResolver"] = weakref.WeakSet() + self._loop_data[loop] = (resolver, client_set) + else: + # Get the existing resolver and client set + resolver, client_set = self._loop_data[loop] + + # Register this client with the loop + client_set.add(client) + return resolver + + def release_resolver( + self, client: "AsyncResolver", loop: asyncio.AbstractEventLoop + ) -> None: + """Release the resolver for an AsyncResolver client when it's closed. + + Args: + client: The AsyncResolver instance to release. + loop: The event loop the resolver was using. + """ + # Remove client from its loop's tracking + current_loop_data = self._loop_data.get(loop) + if current_loop_data is None: + return + resolver, client_set = current_loop_data + client_set.discard(client) + # If no more clients for this loop, cancel and remove its resolver + if not client_set: + if resolver is not None: + resolver.cancel() + del self._loop_data[loop] + + +_DefaultType = Type[Union[AsyncResolver, ThreadedResolver]] +DefaultResolver: _DefaultType = AsyncResolver if aiodns_default else ThreadedResolver diff --git a/venv/lib/python3.12/site-packages/aiohttp/streams.py b/venv/lib/python3.12/site-packages/aiohttp/streams.py new file mode 100644 index 00000000..921827eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/streams.py @@ -0,0 +1,762 @@ +import asyncio +import collections +import warnings +from typing import ( + Awaitable, + Callable, + Deque, + Final, + Generic, + List, + Optional, + Tuple, + TypeVar, +) + +from .base_protocol import BaseProtocol +from .helpers import ( + _EXC_SENTINEL, + BaseTimerContext, + TimerNoop, + set_exception, + set_result, +) +from .http_exceptions import LineTooLong +from .log import internal_logger + +__all__ = ( + "EMPTY_PAYLOAD", + "EofStream", + "StreamReader", + "DataQueue", +) + +_T = TypeVar("_T") + + +class EofStream(Exception): + """eof stream indication.""" + + +class AsyncStreamIterator(Generic[_T]): + + __slots__ = ("read_func",) + + def __init__(self, read_func: Callable[[], Awaitable[_T]]) -> None: + self.read_func = read_func + + def __aiter__(self) -> "AsyncStreamIterator[_T]": + return self + + async def __anext__(self) -> _T: + try: + rv = await self.read_func() + except EofStream: + raise StopAsyncIteration + if rv == b"": + raise StopAsyncIteration + return rv + + +class ChunkTupleAsyncStreamIterator: + + __slots__ = ("_stream",) + + def __init__(self, stream: "StreamReader") -> None: + self._stream = stream + + def __aiter__(self) -> "ChunkTupleAsyncStreamIterator": + return self + + async def __anext__(self) -> Tuple[bytes, bool]: + rv = await self._stream.readchunk() + if rv == (b"", False): + raise StopAsyncIteration + return rv + + +class AsyncStreamReaderMixin: + + __slots__ = () + + def __aiter__(self) -> AsyncStreamIterator[bytes]: + return AsyncStreamIterator(self.readline) # type: ignore[attr-defined] + + def iter_chunked(self, n: int) -> AsyncStreamIterator[bytes]: + """Returns an asynchronous iterator that yields chunks of size n.""" + return AsyncStreamIterator(lambda: self.read(n)) # type: ignore[attr-defined] + + def iter_any(self) -> AsyncStreamIterator[bytes]: + """Yield all available data as soon as it is received.""" + return AsyncStreamIterator(self.readany) # type: ignore[attr-defined] + + def iter_chunks(self) -> ChunkTupleAsyncStreamIterator: + """Yield chunks of data as they are received by the server. + + The yielded objects are tuples + of (bytes, bool) as returned by the StreamReader.readchunk method. + """ + return ChunkTupleAsyncStreamIterator(self) # type: ignore[arg-type] + + +class StreamReader(AsyncStreamReaderMixin): + """An enhancement of asyncio.StreamReader. + + Supports asynchronous iteration by line, chunk or as available:: + + async for line in reader: + ... + async for chunk in reader.iter_chunked(1024): + ... + async for slice in reader.iter_any(): + ... + + """ + + __slots__ = ( + "_protocol", + "_low_water", + "_high_water", + "_low_water_chunks", + "_high_water_chunks", + "_loop", + "_size", + "_cursor", + "_http_chunk_splits", + "_buffer", + "_buffer_offset", + "_eof", + "_waiter", + "_eof_waiter", + "_exception", + "_timer", + "_eof_callbacks", + "_eof_counter", + "total_bytes", + "total_compressed_bytes", + ) + + def __init__( + self, + protocol: BaseProtocol, + limit: int, + *, + timer: Optional[BaseTimerContext] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + self._protocol = protocol + self._low_water = limit + self._high_water = limit * 2 + if loop is None: + loop = asyncio.get_event_loop() + # Ensure high_water_chunks >= 3 so it's always > low_water_chunks. + self._high_water_chunks = max(3, limit // 4) + # Use max(2, ...) because there's always at least 1 chunk split remaining + # (the current position), so we need low_water >= 2 to allow resume. + self._low_water_chunks = max(2, self._high_water_chunks // 2) + self._loop = loop + self._size = 0 + self._cursor = 0 + self._http_chunk_splits: Optional[Deque[int]] = None + self._buffer: Deque[bytes] = collections.deque() + self._buffer_offset = 0 + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._eof_waiter: Optional[asyncio.Future[None]] = None + self._exception: Optional[BaseException] = None + self._timer = TimerNoop() if timer is None else timer + self._eof_callbacks: List[Callable[[], None]] = [] + self._eof_counter = 0 + self.total_bytes = 0 + self.total_compressed_bytes: Optional[int] = None + + def __repr__(self) -> str: + info = [self.__class__.__name__] + if self._size: + info.append("%d bytes" % self._size) + if self._eof: + info.append("eof") + if self._low_water != 2**16: # default limit + info.append("low=%d high=%d" % (self._low_water, self._high_water)) + if self._waiter: + info.append("w=%r" % self._waiter) + if self._exception: + info.append("e=%r" % self._exception) + return "<%s>" % " ".join(info) + + def get_read_buffer_limits(self) -> Tuple[int, int]: + return (self._low_water, self._high_water) + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + self._exception = exc + self._eof_callbacks.clear() + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + waiter = self._eof_waiter + if waiter is not None: + self._eof_waiter = None + set_exception(waiter, exc, exc_cause) + + def on_eof(self, callback: Callable[[], None]) -> None: + if self._eof: + try: + callback() + except Exception: + internal_logger.exception("Exception in eof callback") + else: + self._eof_callbacks.append(callback) + + def feed_eof(self) -> None: + self._eof = True + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + waiter = self._eof_waiter + if waiter is not None: + self._eof_waiter = None + set_result(waiter, None) + + if self._protocol._reading_paused: + self._protocol.resume_reading() + + for cb in self._eof_callbacks: + try: + cb() + except Exception: + internal_logger.exception("Exception in eof callback") + + self._eof_callbacks.clear() + + def is_eof(self) -> bool: + """Return True if 'feed_eof' was called.""" + return self._eof + + def at_eof(self) -> bool: + """Return True if the buffer is empty and 'feed_eof' was called.""" + return self._eof and not self._buffer + + async def wait_eof(self) -> None: + if self._eof: + return + + assert self._eof_waiter is None + self._eof_waiter = self._loop.create_future() + try: + await self._eof_waiter + finally: + self._eof_waiter = None + + @property + def total_raw_bytes(self) -> int: + if self.total_compressed_bytes is None: + return self.total_bytes + return self.total_compressed_bytes + + def unread_data(self, data: bytes) -> None: + """rollback reading some data from stream, inserting it to buffer head.""" + warnings.warn( + "unread_data() is deprecated " + "and will be removed in future releases (#3260)", + DeprecationWarning, + stacklevel=2, + ) + if not data: + return + + if self._buffer_offset: + self._buffer[0] = self._buffer[0][self._buffer_offset :] + self._buffer_offset = 0 + self._size += len(data) + self._cursor -= len(data) + self._buffer.appendleft(data) + self._eof_counter = 0 + + # TODO: size is ignored, remove the param later + def feed_data(self, data: bytes, size: int = 0) -> None: + assert not self._eof, "feed_data after feed_eof" + + if not data: + return + + data_len = len(data) + self._size += data_len + self._buffer.append(data) + self.total_bytes += data_len + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + if self._size > self._high_water and not self._protocol._reading_paused: + self._protocol.pause_reading() + + def begin_http_chunk_receiving(self) -> None: + if self._http_chunk_splits is None: + if self.total_bytes: + raise RuntimeError( + "Called begin_http_chunk_receiving when some data was already fed" + ) + self._http_chunk_splits = collections.deque() + + def end_http_chunk_receiving(self) -> None: + if self._http_chunk_splits is None: + raise RuntimeError( + "Called end_chunk_receiving without calling " + "begin_chunk_receiving first" + ) + + # self._http_chunk_splits contains logical byte offsets from start of + # the body transfer. Each offset is the offset of the end of a chunk. + # "Logical" means bytes, accessible for a user. + # If no chunks containing logical data were received, current position + # is difinitely zero. + pos = self._http_chunk_splits[-1] if self._http_chunk_splits else 0 + + if self.total_bytes == pos: + # We should not add empty chunks here. So we check for that. + # Note, when chunked + gzip is used, we can receive a chunk + # of compressed data, but that data may not be enough for gzip FSM + # to yield any uncompressed data. That's why current position may + # not change after receiving a chunk. + return + + self._http_chunk_splits.append(self.total_bytes) + + # If we get too many small chunks before self._high_water is reached, then any + # .read() call becomes computationally expensive, and could block the event loop + # for too long, hence an additional self._high_water_chunks here. + if ( + len(self._http_chunk_splits) > self._high_water_chunks + and not self._protocol._reading_paused + ): + self._protocol.pause_reading() + + # wake up readchunk when end of http chunk received + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + async def _wait(self, func_name: str) -> None: + if not self._protocol.connected: + raise RuntimeError("Connection closed.") + + # StreamReader uses a future to link the protocol feed_data() method + # to a read coroutine. Running two read coroutines at the same time + # would have an unexpected behaviour. It would not possible to know + # which coroutine would get the next data. + if self._waiter is not None: + raise RuntimeError( + "%s() called while another coroutine is " + "already waiting for incoming data" % func_name + ) + + waiter = self._waiter = self._loop.create_future() + try: + with self._timer: + await waiter + finally: + self._waiter = None + + async def readline(self, *, max_line_length: Optional[int] = None) -> bytes: + return await self.readuntil(max_size=max_line_length) + + async def readuntil( + self, separator: bytes = b"\n", *, max_size: Optional[int] = None + ) -> bytes: + seplen = len(separator) + if seplen == 0: + raise ValueError("Separator should be at least one-byte string") + + if self._exception is not None: + raise self._exception + + chunk = b"" + chunk_size = 0 + not_enough = True + max_size = max_size or self._high_water + + while not_enough: + while self._buffer and not_enough: + offset = self._buffer_offset + ichar = self._buffer[0].find(separator, offset) + 1 + # Read from current offset to found separator or to the end. + data = self._read_nowait_chunk( + ichar - offset + seplen - 1 if ichar else -1 + ) + chunk += data + chunk_size += len(data) + if ichar: + not_enough = False + + if chunk_size > max_size: + raise LineTooLong(chunk[:100] + b"...", max_size) + + if self._eof: + break + + if not_enough: + await self._wait("readuntil") + + return chunk + + async def read(self, n: int = -1) -> bytes: + if self._exception is not None: + raise self._exception + + # migration problem; with DataQueue you have to catch + # EofStream exception, so common way is to run payload.read() inside + # infinite loop. what can cause real infinite loop with StreamReader + # lets keep this code one major release. + if __debug__: + if self._eof and not self._buffer: + self._eof_counter = getattr(self, "_eof_counter", 0) + 1 + if self._eof_counter > 5: + internal_logger.warning( + "Multiple access to StreamReader in eof state, " + "might be infinite loop.", + stack_info=True, + ) + + if not n: + return b"" + + if n < 0: + # This used to just loop creating a new waiter hoping to + # collect everything in self._buffer, but that would + # deadlock if the subprocess sends more than self.limit + # bytes. So just call self.readany() until EOF. + blocks = [] + while True: + block = await self.readany() + if not block: + break + blocks.append(block) + return b"".join(blocks) + + # TODO: should be `if` instead of `while` + # because waiter maybe triggered on chunk end, + # without feeding any data + while not self._buffer and not self._eof: + await self._wait("read") + + return self._read_nowait(n) + + async def readany(self) -> bytes: + if self._exception is not None: + raise self._exception + + # TODO: should be `if` instead of `while` + # because waiter maybe triggered on chunk end, + # without feeding any data + while not self._buffer and not self._eof: + await self._wait("readany") + + return self._read_nowait(-1) + + async def readchunk(self) -> Tuple[bytes, bool]: + """Returns a tuple of (data, end_of_http_chunk). + + When chunked transfer + encoding is used, end_of_http_chunk is a boolean indicating if the end + of the data corresponds to the end of a HTTP chunk , otherwise it is + always False. + """ + while True: + if self._exception is not None: + raise self._exception + + while self._http_chunk_splits: + pos = self._http_chunk_splits.popleft() + if pos == self._cursor: + return (b"", True) + if pos > self._cursor: + return (self._read_nowait(pos - self._cursor), True) + internal_logger.warning( + "Skipping HTTP chunk end due to data " + "consumption beyond chunk boundary" + ) + + if self._buffer: + return (self._read_nowait_chunk(-1), False) + # return (self._read_nowait(-1), False) + + if self._eof: + # Special case for signifying EOF. + # (b'', True) is not a final return value actually. + return (b"", False) + + await self._wait("readchunk") + + async def readexactly(self, n: int) -> bytes: + if self._exception is not None: + raise self._exception + + blocks: List[bytes] = [] + while n > 0: + block = await self.read(n) + if not block: + partial = b"".join(blocks) + raise asyncio.IncompleteReadError(partial, len(partial) + n) + blocks.append(block) + n -= len(block) + + return b"".join(blocks) + + def read_nowait(self, n: int = -1) -> bytes: + # default was changed to be consistent with .read(-1) + # + # I believe the most users don't know about the method and + # they are not affected. + if self._exception is not None: + raise self._exception + + if self._waiter and not self._waiter.done(): + raise RuntimeError( + "Called while some coroutine is waiting for incoming data." + ) + + return self._read_nowait(n) + + def _read_nowait_chunk(self, n: int) -> bytes: + first_buffer = self._buffer[0] + offset = self._buffer_offset + if n != -1 and len(first_buffer) - offset > n: + data = first_buffer[offset : offset + n] + self._buffer_offset += n + + elif offset: + self._buffer.popleft() + data = first_buffer[offset:] + self._buffer_offset = 0 + + else: + data = self._buffer.popleft() + + data_len = len(data) + self._size -= data_len + self._cursor += data_len + + chunk_splits = self._http_chunk_splits + # Prevent memory leak: drop useless chunk splits + while chunk_splits and chunk_splits[0] < self._cursor: + chunk_splits.popleft() + + if ( + self._protocol._reading_paused + and self._size < self._low_water + and ( + self._http_chunk_splits is None + or len(self._http_chunk_splits) < self._low_water_chunks + ) + ): + self._protocol.resume_reading() + return data + + def _read_nowait(self, n: int) -> bytes: + """Read not more than n bytes, or whole buffer if n == -1""" + self._timer.assert_timeout() + + chunks = [] + while self._buffer: + chunk = self._read_nowait_chunk(n) + chunks.append(chunk) + if n != -1: + n -= len(chunk) + if n == 0: + break + + return b"".join(chunks) if chunks else b"" + + +class EmptyStreamReader(StreamReader): # lgtm [py/missing-call-to-init] + + __slots__ = ("_read_eof_chunk",) + + def __init__(self) -> None: + self._read_eof_chunk = False + self.total_bytes = 0 + + def __repr__(self) -> str: + return "<%s>" % self.__class__.__name__ + + def exception(self) -> Optional[BaseException]: + return None + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + pass + + def on_eof(self, callback: Callable[[], None]) -> None: + try: + callback() + except Exception: + internal_logger.exception("Exception in eof callback") + + def feed_eof(self) -> None: + pass + + def is_eof(self) -> bool: + return True + + def at_eof(self) -> bool: + return True + + async def wait_eof(self) -> None: + return + + def feed_data(self, data: bytes, n: int = 0) -> None: + pass + + async def readline(self, *, max_line_length: Optional[int] = None) -> bytes: + return b"" + + async def read(self, n: int = -1) -> bytes: + return b"" + + # TODO add async def readuntil + + async def readany(self) -> bytes: + return b"" + + async def readchunk(self) -> Tuple[bytes, bool]: + if not self._read_eof_chunk: + self._read_eof_chunk = True + return (b"", False) + + return (b"", True) + + async def readexactly(self, n: int) -> bytes: + raise asyncio.IncompleteReadError(b"", n) + + def read_nowait(self, n: int = -1) -> bytes: + return b"" + + +EMPTY_PAYLOAD: Final[StreamReader] = EmptyStreamReader() + + +class DataQueue(Generic[_T]): + """DataQueue is a general-purpose blocking queue with one reader.""" + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop = loop + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._exception: Optional[BaseException] = None + self._buffer: Deque[Tuple[_T, int]] = collections.deque() + + def __len__(self) -> int: + return len(self._buffer) + + def is_eof(self) -> bool: + return self._eof + + def at_eof(self) -> bool: + return self._eof and not self._buffer + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + self._eof = True + self._exception = exc + if (waiter := self._waiter) is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + def feed_data(self, data: _T, size: int = 0) -> None: + self._buffer.append((data, size)) + if (waiter := self._waiter) is not None: + self._waiter = None + set_result(waiter, None) + + def feed_eof(self) -> None: + self._eof = True + if (waiter := self._waiter) is not None: + self._waiter = None + set_result(waiter, None) + + async def read(self) -> _T: + if not self._buffer and not self._eof: + assert not self._waiter + self._waiter = self._loop.create_future() + try: + await self._waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._waiter = None + raise + if self._buffer: + data, _ = self._buffer.popleft() + return data + if self._exception is not None: + raise self._exception + raise EofStream + + def __aiter__(self) -> AsyncStreamIterator[_T]: + return AsyncStreamIterator(self.read) + + +class FlowControlDataQueue(DataQueue[_T]): + """FlowControlDataQueue resumes and pauses an underlying stream. + + It is a destination for parsed data. + + This class is deprecated and will be removed in version 4.0. + """ + + def __init__( + self, protocol: BaseProtocol, limit: int, *, loop: asyncio.AbstractEventLoop + ) -> None: + super().__init__(loop=loop) + self._size = 0 + self._protocol = protocol + self._limit = limit * 2 + + def feed_data(self, data: _T, size: int = 0) -> None: + super().feed_data(data, size) + self._size += size + + if self._size > self._limit and not self._protocol._reading_paused: + self._protocol.pause_reading() + + async def read(self) -> _T: + if not self._buffer and not self._eof: + assert not self._waiter + self._waiter = self._loop.create_future() + try: + await self._waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._waiter = None + raise + if self._buffer: + data, size = self._buffer.popleft() + self._size -= size + if self._size < self._limit and self._protocol._reading_paused: + self._protocol.resume_reading() + return data + if self._exception is not None: + raise self._exception + raise EofStream diff --git a/venv/lib/python3.12/site-packages/aiohttp/tcp_helpers.py b/venv/lib/python3.12/site-packages/aiohttp/tcp_helpers.py new file mode 100644 index 00000000..88b24422 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/tcp_helpers.py @@ -0,0 +1,37 @@ +"""Helper methods to tune a TCP connection""" + +import asyncio +import socket +from contextlib import suppress +from typing import Optional # noqa + +__all__ = ("tcp_keepalive", "tcp_nodelay") + + +if hasattr(socket, "SO_KEEPALIVE"): + + def tcp_keepalive(transport: asyncio.Transport) -> None: + sock = transport.get_extra_info("socket") + if sock is not None: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + +else: + + def tcp_keepalive(transport: asyncio.Transport) -> None: # pragma: no cover + pass + + +def tcp_nodelay(transport: asyncio.Transport, value: bool) -> None: + sock = transport.get_extra_info("socket") + + if sock is None: + return + + if sock.family not in (socket.AF_INET, socket.AF_INET6): + return + + value = bool(value) + + # socket may be closed already, on windows OSError get raised + with suppress(OSError): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, value) diff --git a/venv/lib/python3.12/site-packages/aiohttp/test_utils.py b/venv/lib/python3.12/site-packages/aiohttp/test_utils.py new file mode 100644 index 00000000..34b77d47 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/test_utils.py @@ -0,0 +1,777 @@ +"""Utilities shared by tests.""" + +import asyncio +import contextlib +import gc +import inspect +import ipaddress +import os +import socket +import sys +import warnings +from abc import ABC, abstractmethod +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Generic, + Iterator, + List, + Optional, + Type, + TypeVar, + cast, + overload, +) +from unittest import IsolatedAsyncioTestCase, mock + +from aiosignal import Signal +from multidict import CIMultiDict, CIMultiDictProxy +from yarl import URL + +import aiohttp +from aiohttp.client import ( + _RequestContextManager, + _RequestOptions, + _WSRequestContextManager, +) + +from . import ClientSession, hdrs +from .abc import AbstractCookieJar +from .client_reqrep import ClientResponse +from .client_ws import ClientWebSocketResponse +from .helpers import sentinel +from .http import HttpVersion, RawRequestMessage +from .streams import EMPTY_PAYLOAD, StreamReader +from .typedefs import StrOrURL +from .web import ( + Application, + AppRunner, + BaseRequest, + BaseRunner, + Request, + Server, + ServerRunner, + SockSite, + UrlMappingMatchInfo, +) +from .web_protocol import _RequestHandler + +if TYPE_CHECKING: + from ssl import SSLContext +else: + SSLContext = None + +if sys.version_info >= (3, 11) and TYPE_CHECKING: + from typing import Unpack + +if sys.version_info >= (3, 11): + from typing import Self +else: + Self = Any + +_ApplicationNone = TypeVar("_ApplicationNone", Application, None) +_Request = TypeVar("_Request", bound=BaseRequest) + +REUSE_ADDRESS = os.name == "posix" and sys.platform != "cygwin" + + +def get_unused_port_socket( + host: str, family: socket.AddressFamily = socket.AF_INET +) -> socket.socket: + return get_port_socket(host, 0, family) + + +def get_port_socket( + host: str, port: int, family: socket.AddressFamily +) -> socket.socket: + s = socket.socket(family, socket.SOCK_STREAM) + if REUSE_ADDRESS: + # Windows has different semantics for SO_REUSEADDR, + # so don't set it. Ref: + # https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind((host, port)) + return s + + +def unused_port() -> int: + """Return a port that is unused on the current host.""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(("127.0.0.1", 0)) + return cast(int, s.getsockname()[1]) + + +class BaseTestServer(ABC): + __test__ = False + + def __init__( + self, + *, + scheme: str = "", + loop: Optional[asyncio.AbstractEventLoop] = None, + host: str = "127.0.0.1", + port: Optional[int] = None, + skip_url_asserts: bool = False, + socket_factory: Callable[ + [str, int, socket.AddressFamily], socket.socket + ] = get_port_socket, + **kwargs: Any, + ) -> None: + self._loop = loop + self.runner: Optional[BaseRunner] = None + self._root: Optional[URL] = None + self.host = host + self.port = port + self._closed = False + self.scheme = scheme + self.skip_url_asserts = skip_url_asserts + self.socket_factory = socket_factory + + async def start_server( + self, loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs: Any + ) -> None: + if self.runner: + return + self._loop = loop + self._ssl = kwargs.pop("ssl", None) + self.runner = await self._make_runner(handler_cancellation=True, **kwargs) + await self.runner.setup() + if not self.port: + self.port = 0 + absolute_host = self.host + try: + version = ipaddress.ip_address(self.host).version + except ValueError: + version = 4 + if version == 6: + absolute_host = f"[{self.host}]" + family = socket.AF_INET6 if version == 6 else socket.AF_INET + _sock = self.socket_factory(self.host, self.port, family) + self.host, self.port = _sock.getsockname()[:2] + site = SockSite(self.runner, sock=_sock, ssl_context=self._ssl) + await site.start() + server = site._server + assert server is not None + sockets = server.sockets # type: ignore[attr-defined] + assert sockets is not None + self.port = sockets[0].getsockname()[1] + if not self.scheme: + self.scheme = "https" if self._ssl else "http" + self._root = URL(f"{self.scheme}://{absolute_host}:{self.port}") + + @abstractmethod # pragma: no cover + async def _make_runner(self, **kwargs: Any) -> BaseRunner: + pass + + def make_url(self, path: StrOrURL) -> URL: + assert self._root is not None + url = URL(path) + if not self.skip_url_asserts: + assert not url.absolute + return self._root.join(url) + else: + return URL(str(self._root) + str(path)) + + @property + def started(self) -> bool: + return self.runner is not None + + @property + def closed(self) -> bool: + return self._closed + + @property + def handler(self) -> Server: + # for backward compatibility + # web.Server instance + runner = self.runner + assert runner is not None + assert runner.server is not None + return runner.server + + async def close(self) -> None: + """Close all fixtures created by the test client. + + After that point, the TestClient is no longer usable. + + This is an idempotent function: running close multiple times + will not have any additional effects. + + close is also run when the object is garbage collected, and on + exit when used as a context manager. + + """ + if self.started and not self.closed: + assert self.runner is not None + await self.runner.cleanup() + self._root = None + self.port = None + self._closed = True + + def __enter__(self) -> None: + raise TypeError("Use async with instead") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> None: + # __exit__ should exist in pair with __enter__ but never executed + pass # pragma: no cover + + async def __aenter__(self) -> "BaseTestServer": + await self.start_server(loop=self._loop) + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> None: + await self.close() + + +class TestServer(BaseTestServer): + def __init__( + self, + app: Application, + *, + scheme: str = "", + host: str = "127.0.0.1", + port: Optional[int] = None, + **kwargs: Any, + ): + self.app = app + super().__init__(scheme=scheme, host=host, port=port, **kwargs) + + async def _make_runner(self, **kwargs: Any) -> BaseRunner: + return AppRunner(self.app, **kwargs) + + +class RawTestServer(BaseTestServer): + def __init__( + self, + handler: _RequestHandler, + *, + scheme: str = "", + host: str = "127.0.0.1", + port: Optional[int] = None, + **kwargs: Any, + ) -> None: + self._handler = handler + super().__init__(scheme=scheme, host=host, port=port, **kwargs) + + async def _make_runner(self, debug: bool = True, **kwargs: Any) -> ServerRunner: + srv = Server(self._handler, loop=self._loop, debug=debug, **kwargs) + return ServerRunner(srv, debug=debug, **kwargs) + + +class TestClient(Generic[_Request, _ApplicationNone]): + """ + A test client implementation. + + To write functional tests for aiohttp based servers. + + """ + + __test__ = False + + @overload + def __init__( + self: "TestClient[Request, Application]", + server: TestServer, + *, + cookie_jar: Optional[AbstractCookieJar] = None, + **kwargs: Any, + ) -> None: ... + @overload + def __init__( + self: "TestClient[_Request, None]", + server: BaseTestServer, + *, + cookie_jar: Optional[AbstractCookieJar] = None, + **kwargs: Any, + ) -> None: ... + def __init__( + self, + server: BaseTestServer, + *, + cookie_jar: Optional[AbstractCookieJar] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Any, + ) -> None: + if not isinstance(server, BaseTestServer): + raise TypeError( + "server must be TestServer instance, found type: %r" % type(server) + ) + self._server = server + self._loop = loop + if cookie_jar is None: + cookie_jar = aiohttp.CookieJar(unsafe=True, loop=loop) + self._session = ClientSession(loop=loop, cookie_jar=cookie_jar, **kwargs) + self._session._retry_connection = False + self._closed = False + self._responses: List[ClientResponse] = [] + self._websockets: List[ClientWebSocketResponse] = [] + + async def start_server(self) -> None: + await self._server.start_server(loop=self._loop) + + @property + def host(self) -> str: + return self._server.host + + @property + def port(self) -> Optional[int]: + return self._server.port + + @property + def server(self) -> BaseTestServer: + return self._server + + @property + def app(self) -> _ApplicationNone: + return getattr(self._server, "app", None) # type: ignore[return-value] + + @property + def session(self) -> ClientSession: + """An internal aiohttp.ClientSession. + + Unlike the methods on the TestClient, client session requests + do not automatically include the host in the url queried, and + will require an absolute path to the resource. + + """ + return self._session + + def make_url(self, path: StrOrURL) -> URL: + return self._server.make_url(path) + + async def _request( + self, method: str, path: StrOrURL, **kwargs: Any + ) -> ClientResponse: + resp = await self._session.request(method, self.make_url(path), **kwargs) + # save it to close later + self._responses.append(resp) + return resp + + if sys.version_info >= (3, 11) and TYPE_CHECKING: + + def request( + self, method: str, path: StrOrURL, **kwargs: Unpack[_RequestOptions] + ) -> _RequestContextManager: ... + + def get( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def options( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def head( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def post( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def put( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def patch( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + def delete( + self, + path: StrOrURL, + **kwargs: Unpack[_RequestOptions], + ) -> _RequestContextManager: ... + + else: + + def request( + self, method: str, path: StrOrURL, **kwargs: Any + ) -> _RequestContextManager: + """Routes a request to tested http server. + + The interface is identical to aiohttp.ClientSession.request, + except the loop kwarg is overridden by the instance used by the + test server. + + """ + return _RequestContextManager(self._request(method, path, **kwargs)) + + def get(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP GET request.""" + return _RequestContextManager(self._request(hdrs.METH_GET, path, **kwargs)) + + def post(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP POST request.""" + return _RequestContextManager(self._request(hdrs.METH_POST, path, **kwargs)) + + def options(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP OPTIONS request.""" + return _RequestContextManager( + self._request(hdrs.METH_OPTIONS, path, **kwargs) + ) + + def head(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP HEAD request.""" + return _RequestContextManager(self._request(hdrs.METH_HEAD, path, **kwargs)) + + def put(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP PUT request.""" + return _RequestContextManager(self._request(hdrs.METH_PUT, path, **kwargs)) + + def patch(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP PATCH request.""" + return _RequestContextManager( + self._request(hdrs.METH_PATCH, path, **kwargs) + ) + + def delete(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP PATCH request.""" + return _RequestContextManager( + self._request(hdrs.METH_DELETE, path, **kwargs) + ) + + def ws_connect(self, path: StrOrURL, **kwargs: Any) -> _WSRequestContextManager: + """Initiate websocket connection. + + The api corresponds to aiohttp.ClientSession.ws_connect. + + """ + return _WSRequestContextManager(self._ws_connect(path, **kwargs)) + + async def _ws_connect( + self, path: StrOrURL, **kwargs: Any + ) -> ClientWebSocketResponse: + ws = await self._session.ws_connect(self.make_url(path), **kwargs) + self._websockets.append(ws) + return ws + + async def close(self) -> None: + """Close all fixtures created by the test client. + + After that point, the TestClient is no longer usable. + + This is an idempotent function: running close multiple times + will not have any additional effects. + + close is also run on exit when used as a(n) (asynchronous) + context manager. + + """ + if not self._closed: + for resp in self._responses: + resp.close() + for ws in self._websockets: + await ws.close() + await self._session.close() + await self._server.close() + self._closed = True + + def __enter__(self) -> None: + raise TypeError("Use async with instead") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + # __exit__ should exist in pair with __enter__ but never executed + pass # pragma: no cover + + async def __aenter__(self) -> Self: + await self.start_server() + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + await self.close() + + +class AioHTTPTestCase(IsolatedAsyncioTestCase): + """A base class to allow for unittest web applications using aiohttp. + + Provides the following: + + * self.client (aiohttp.test_utils.TestClient): an aiohttp test client. + * self.loop (asyncio.BaseEventLoop): the event loop in which the + application and server are running. + * self.app (aiohttp.web.Application): the application returned by + self.get_application() + + Note that the TestClient's methods are asynchronous: you have to + execute function on the test client using asynchronous methods. + """ + + async def get_application(self) -> Application: + """Get application. + + This method should be overridden + to return the aiohttp.web.Application + object to test. + """ + return self.get_app() + + def get_app(self) -> Application: + """Obsolete method used to constructing web application. + + Use .get_application() coroutine instead. + """ + raise RuntimeError("Did you forget to define get_application()?") + + async def asyncSetUp(self) -> None: + self.loop = asyncio.get_running_loop() + return await self.setUpAsync() + + async def setUpAsync(self) -> None: + self.app = await self.get_application() + self.server = await self.get_server(self.app) + self.client = await self.get_client(self.server) + + await self.client.start_server() + + async def asyncTearDown(self) -> None: + return await self.tearDownAsync() + + async def tearDownAsync(self) -> None: + await self.client.close() + + async def get_server(self, app: Application) -> TestServer: + """Return a TestServer instance.""" + return TestServer(app, loop=self.loop) + + async def get_client(self, server: TestServer) -> TestClient[Request, Application]: + """Return a TestClient instance.""" + return TestClient(server, loop=self.loop) + + +def unittest_run_loop(func: Any, *args: Any, **kwargs: Any) -> Any: + """ + A decorator dedicated to use with asynchronous AioHTTPTestCase test methods. + + In 3.8+, this does nothing. + """ + warnings.warn( + "Decorator `@unittest_run_loop` is no longer needed in aiohttp 3.8+", + DeprecationWarning, + stacklevel=2, + ) + return func + + +_LOOP_FACTORY = Callable[[], asyncio.AbstractEventLoop] + + +@contextlib.contextmanager +def loop_context( + loop_factory: _LOOP_FACTORY = asyncio.new_event_loop, fast: bool = False +) -> Iterator[asyncio.AbstractEventLoop]: + """A contextmanager that creates an event_loop, for test purposes. + + Handles the creation and cleanup of a test loop. + """ + loop = setup_test_loop(loop_factory) + yield loop + teardown_test_loop(loop, fast=fast) + + +def setup_test_loop( + loop_factory: _LOOP_FACTORY = asyncio.new_event_loop, +) -> asyncio.AbstractEventLoop: + """Create and return an asyncio.BaseEventLoop instance. + + The caller should also call teardown_test_loop, + once they are done with the loop. + """ + loop = loop_factory() + asyncio.set_event_loop(loop) + return loop + + +def teardown_test_loop(loop: asyncio.AbstractEventLoop, fast: bool = False) -> None: + """Teardown and cleanup an event_loop created by setup_test_loop.""" + closed = loop.is_closed() + if not closed: + loop.call_soon(loop.stop) + loop.run_forever() + loop.close() + + if not fast: + gc.collect() + + asyncio.set_event_loop(None) + + +def _create_app_mock() -> mock.MagicMock: + def get_dict(app: Any, key: str) -> Any: + return app.__app_dict[key] + + def set_dict(app: Any, key: str, value: Any) -> None: + app.__app_dict[key] = value + + app = mock.MagicMock(spec=Application) + app.__app_dict = {} + app.__getitem__ = get_dict + app.__setitem__ = set_dict + + app._debug = False + app.on_response_prepare = Signal(app) + app.on_response_prepare.freeze() + return app + + +def _create_transport(sslcontext: Optional[SSLContext] = None) -> mock.Mock: + transport = mock.Mock() + + def get_extra_info(key: str) -> Optional[SSLContext]: + if key == "sslcontext": + return sslcontext + else: + return None + + transport.get_extra_info.side_effect = get_extra_info + return transport + + +def make_mocked_request( + method: str, + path: str, + headers: Any = None, + *, + match_info: Any = sentinel, + version: HttpVersion = HttpVersion(1, 1), + closing: bool = False, + app: Any = None, + writer: Any = sentinel, + protocol: Any = sentinel, + transport: Any = sentinel, + payload: StreamReader = EMPTY_PAYLOAD, + sslcontext: Optional[SSLContext] = None, + client_max_size: int = 1024**2, + loop: Any = ..., +) -> Request: + """Creates mocked web.Request testing purposes. + + Useful in unit tests, when spinning full web server is overkill or + specific conditions and errors are hard to trigger. + """ + task = mock.Mock() + if loop is ...: + # no loop passed, try to get the current one if + # its is running as we need a real loop to create + # executor jobs to be able to do testing + # with a real executor + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = mock.Mock() + loop.create_future.return_value = () + + if version < HttpVersion(1, 1): + closing = True + + if headers: + headers = CIMultiDictProxy(CIMultiDict(headers)) + raw_hdrs = tuple( + (k.encode("utf-8"), v.encode("utf-8")) for k, v in headers.items() + ) + else: + headers = CIMultiDictProxy(CIMultiDict()) + raw_hdrs = () + + chunked = "chunked" in headers.get(hdrs.TRANSFER_ENCODING, "").lower() + + message = RawRequestMessage( + method, + path, + version, + headers, + raw_hdrs, + closing, + None, + False, + chunked, + URL(path), + ) + if app is None: + app = _create_app_mock() + + if transport is sentinel: + transport = _create_transport(sslcontext) + + if protocol is sentinel: + protocol = mock.Mock() + protocol.max_field_size = 8190 + protocol.max_line_length = 8190 + protocol.max_headers = 128 + protocol.transport = transport + type(protocol).peername = mock.PropertyMock( + return_value=transport.get_extra_info("peername") + ) + type(protocol).ssl_context = mock.PropertyMock(return_value=sslcontext) + + if writer is sentinel: + writer = mock.Mock() + writer.write_headers = make_mocked_coro(None) + writer.write = make_mocked_coro(None) + writer.write_eof = make_mocked_coro(None) + writer.drain = make_mocked_coro(None) + writer.transport = transport + + protocol.transport = transport + protocol.writer = writer + + req = Request( + message, payload, protocol, writer, task, loop, client_max_size=client_max_size + ) + + match_info = UrlMappingMatchInfo( + {} if match_info is sentinel else match_info, mock.Mock() + ) + match_info.add_app(app) + req._match_info = match_info + + return req + + +def make_mocked_coro( + return_value: Any = sentinel, raise_exception: Any = sentinel +) -> Any: + """Creates a coroutine mock.""" + + async def mock_coro(*args: Any, **kwargs: Any) -> Any: + if raise_exception is not sentinel: + raise raise_exception + if not inspect.isawaitable(return_value): + return return_value + await return_value + + return mock.Mock(wraps=mock_coro) diff --git a/venv/lib/python3.12/site-packages/aiohttp/tracing.py b/venv/lib/python3.12/site-packages/aiohttp/tracing.py new file mode 100644 index 00000000..568fa7f9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/tracing.py @@ -0,0 +1,455 @@ +from types import SimpleNamespace +from typing import TYPE_CHECKING, Mapping, Optional, Type, TypeVar + +import attr +from aiosignal import Signal +from multidict import CIMultiDict +from yarl import URL + +from .client_reqrep import ClientResponse + +if TYPE_CHECKING: + from .client import ClientSession + + _ParamT_contra = TypeVar("_ParamT_contra", contravariant=True) + _TracingSignal = Signal[ClientSession, SimpleNamespace, _ParamT_contra] + + +__all__ = ( + "TraceConfig", + "TraceRequestStartParams", + "TraceRequestEndParams", + "TraceRequestExceptionParams", + "TraceConnectionQueuedStartParams", + "TraceConnectionQueuedEndParams", + "TraceConnectionCreateStartParams", + "TraceConnectionCreateEndParams", + "TraceConnectionReuseconnParams", + "TraceDnsResolveHostStartParams", + "TraceDnsResolveHostEndParams", + "TraceDnsCacheHitParams", + "TraceDnsCacheMissParams", + "TraceRequestRedirectParams", + "TraceRequestChunkSentParams", + "TraceResponseChunkReceivedParams", + "TraceRequestHeadersSentParams", +) + + +class TraceConfig: + """First-class used to trace requests launched via ClientSession objects.""" + + def __init__( + self, trace_config_ctx_factory: Type[SimpleNamespace] = SimpleNamespace + ) -> None: + self._on_request_start: _TracingSignal[TraceRequestStartParams] = Signal(self) + self._on_request_chunk_sent: _TracingSignal[TraceRequestChunkSentParams] = ( + Signal(self) + ) + self._on_response_chunk_received: _TracingSignal[ + TraceResponseChunkReceivedParams + ] = Signal(self) + self._on_request_end: _TracingSignal[TraceRequestEndParams] = Signal(self) + self._on_request_exception: _TracingSignal[TraceRequestExceptionParams] = ( + Signal(self) + ) + self._on_request_redirect: _TracingSignal[TraceRequestRedirectParams] = Signal( + self + ) + self._on_connection_queued_start: _TracingSignal[ + TraceConnectionQueuedStartParams + ] = Signal(self) + self._on_connection_queued_end: _TracingSignal[ + TraceConnectionQueuedEndParams + ] = Signal(self) + self._on_connection_create_start: _TracingSignal[ + TraceConnectionCreateStartParams + ] = Signal(self) + self._on_connection_create_end: _TracingSignal[ + TraceConnectionCreateEndParams + ] = Signal(self) + self._on_connection_reuseconn: _TracingSignal[ + TraceConnectionReuseconnParams + ] = Signal(self) + self._on_dns_resolvehost_start: _TracingSignal[ + TraceDnsResolveHostStartParams + ] = Signal(self) + self._on_dns_resolvehost_end: _TracingSignal[TraceDnsResolveHostEndParams] = ( + Signal(self) + ) + self._on_dns_cache_hit: _TracingSignal[TraceDnsCacheHitParams] = Signal(self) + self._on_dns_cache_miss: _TracingSignal[TraceDnsCacheMissParams] = Signal(self) + self._on_request_headers_sent: _TracingSignal[TraceRequestHeadersSentParams] = ( + Signal(self) + ) + + self._trace_config_ctx_factory = trace_config_ctx_factory + + def trace_config_ctx( + self, trace_request_ctx: Optional[Mapping[str, str]] = None + ) -> SimpleNamespace: + """Return a new trace_config_ctx instance""" + return self._trace_config_ctx_factory(trace_request_ctx=trace_request_ctx) + + def freeze(self) -> None: + self._on_request_start.freeze() + self._on_request_chunk_sent.freeze() + self._on_response_chunk_received.freeze() + self._on_request_end.freeze() + self._on_request_exception.freeze() + self._on_request_redirect.freeze() + self._on_connection_queued_start.freeze() + self._on_connection_queued_end.freeze() + self._on_connection_create_start.freeze() + self._on_connection_create_end.freeze() + self._on_connection_reuseconn.freeze() + self._on_dns_resolvehost_start.freeze() + self._on_dns_resolvehost_end.freeze() + self._on_dns_cache_hit.freeze() + self._on_dns_cache_miss.freeze() + self._on_request_headers_sent.freeze() + + @property + def on_request_start(self) -> "_TracingSignal[TraceRequestStartParams]": + return self._on_request_start + + @property + def on_request_chunk_sent( + self, + ) -> "_TracingSignal[TraceRequestChunkSentParams]": + return self._on_request_chunk_sent + + @property + def on_response_chunk_received( + self, + ) -> "_TracingSignal[TraceResponseChunkReceivedParams]": + return self._on_response_chunk_received + + @property + def on_request_end(self) -> "_TracingSignal[TraceRequestEndParams]": + return self._on_request_end + + @property + def on_request_exception( + self, + ) -> "_TracingSignal[TraceRequestExceptionParams]": + return self._on_request_exception + + @property + def on_request_redirect( + self, + ) -> "_TracingSignal[TraceRequestRedirectParams]": + return self._on_request_redirect + + @property + def on_connection_queued_start( + self, + ) -> "_TracingSignal[TraceConnectionQueuedStartParams]": + return self._on_connection_queued_start + + @property + def on_connection_queued_end( + self, + ) -> "_TracingSignal[TraceConnectionQueuedEndParams]": + return self._on_connection_queued_end + + @property + def on_connection_create_start( + self, + ) -> "_TracingSignal[TraceConnectionCreateStartParams]": + return self._on_connection_create_start + + @property + def on_connection_create_end( + self, + ) -> "_TracingSignal[TraceConnectionCreateEndParams]": + return self._on_connection_create_end + + @property + def on_connection_reuseconn( + self, + ) -> "_TracingSignal[TraceConnectionReuseconnParams]": + return self._on_connection_reuseconn + + @property + def on_dns_resolvehost_start( + self, + ) -> "_TracingSignal[TraceDnsResolveHostStartParams]": + return self._on_dns_resolvehost_start + + @property + def on_dns_resolvehost_end( + self, + ) -> "_TracingSignal[TraceDnsResolveHostEndParams]": + return self._on_dns_resolvehost_end + + @property + def on_dns_cache_hit(self) -> "_TracingSignal[TraceDnsCacheHitParams]": + return self._on_dns_cache_hit + + @property + def on_dns_cache_miss(self) -> "_TracingSignal[TraceDnsCacheMissParams]": + return self._on_dns_cache_miss + + @property + def on_request_headers_sent( + self, + ) -> "_TracingSignal[TraceRequestHeadersSentParams]": + return self._on_request_headers_sent + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestStartParams: + """Parameters sent by the `on_request_start` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestChunkSentParams: + """Parameters sent by the `on_request_chunk_sent` signal""" + + method: str + url: URL + chunk: bytes + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceResponseChunkReceivedParams: + """Parameters sent by the `on_response_chunk_received` signal""" + + method: str + url: URL + chunk: bytes + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestEndParams: + """Parameters sent by the `on_request_end` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + response: ClientResponse + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestExceptionParams: + """Parameters sent by the `on_request_exception` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + exception: BaseException + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestRedirectParams: + """Parameters sent by the `on_request_redirect` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + response: ClientResponse + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionQueuedStartParams: + """Parameters sent by the `on_connection_queued_start` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionQueuedEndParams: + """Parameters sent by the `on_connection_queued_end` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionCreateStartParams: + """Parameters sent by the `on_connection_create_start` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionCreateEndParams: + """Parameters sent by the `on_connection_create_end` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionReuseconnParams: + """Parameters sent by the `on_connection_reuseconn` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsResolveHostStartParams: + """Parameters sent by the `on_dns_resolvehost_start` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsResolveHostEndParams: + """Parameters sent by the `on_dns_resolvehost_end` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsCacheHitParams: + """Parameters sent by the `on_dns_cache_hit` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsCacheMissParams: + """Parameters sent by the `on_dns_cache_miss` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestHeadersSentParams: + """Parameters sent by the `on_request_headers_sent` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + + +class Trace: + """Internal dependency holder class. + + Used to keep together the main dependencies used + at the moment of send a signal. + """ + + def __init__( + self, + session: "ClientSession", + trace_config: TraceConfig, + trace_config_ctx: SimpleNamespace, + ) -> None: + self._trace_config = trace_config + self._trace_config_ctx = trace_config_ctx + self._session = session + + async def send_request_start( + self, method: str, url: URL, headers: "CIMultiDict[str]" + ) -> None: + return await self._trace_config.on_request_start.send( + self._session, + self._trace_config_ctx, + TraceRequestStartParams(method, url, headers), + ) + + async def send_request_chunk_sent( + self, method: str, url: URL, chunk: bytes + ) -> None: + return await self._trace_config.on_request_chunk_sent.send( + self._session, + self._trace_config_ctx, + TraceRequestChunkSentParams(method, url, chunk), + ) + + async def send_response_chunk_received( + self, method: str, url: URL, chunk: bytes + ) -> None: + return await self._trace_config.on_response_chunk_received.send( + self._session, + self._trace_config_ctx, + TraceResponseChunkReceivedParams(method, url, chunk), + ) + + async def send_request_end( + self, + method: str, + url: URL, + headers: "CIMultiDict[str]", + response: ClientResponse, + ) -> None: + return await self._trace_config.on_request_end.send( + self._session, + self._trace_config_ctx, + TraceRequestEndParams(method, url, headers, response), + ) + + async def send_request_exception( + self, + method: str, + url: URL, + headers: "CIMultiDict[str]", + exception: BaseException, + ) -> None: + return await self._trace_config.on_request_exception.send( + self._session, + self._trace_config_ctx, + TraceRequestExceptionParams(method, url, headers, exception), + ) + + async def send_request_redirect( + self, + method: str, + url: URL, + headers: "CIMultiDict[str]", + response: ClientResponse, + ) -> None: + return await self._trace_config._on_request_redirect.send( + self._session, + self._trace_config_ctx, + TraceRequestRedirectParams(method, url, headers, response), + ) + + async def send_connection_queued_start(self) -> None: + return await self._trace_config.on_connection_queued_start.send( + self._session, self._trace_config_ctx, TraceConnectionQueuedStartParams() + ) + + async def send_connection_queued_end(self) -> None: + return await self._trace_config.on_connection_queued_end.send( + self._session, self._trace_config_ctx, TraceConnectionQueuedEndParams() + ) + + async def send_connection_create_start(self) -> None: + return await self._trace_config.on_connection_create_start.send( + self._session, self._trace_config_ctx, TraceConnectionCreateStartParams() + ) + + async def send_connection_create_end(self) -> None: + return await self._trace_config.on_connection_create_end.send( + self._session, self._trace_config_ctx, TraceConnectionCreateEndParams() + ) + + async def send_connection_reuseconn(self) -> None: + return await self._trace_config.on_connection_reuseconn.send( + self._session, self._trace_config_ctx, TraceConnectionReuseconnParams() + ) + + async def send_dns_resolvehost_start(self, host: str) -> None: + return await self._trace_config.on_dns_resolvehost_start.send( + self._session, self._trace_config_ctx, TraceDnsResolveHostStartParams(host) + ) + + async def send_dns_resolvehost_end(self, host: str) -> None: + return await self._trace_config.on_dns_resolvehost_end.send( + self._session, self._trace_config_ctx, TraceDnsResolveHostEndParams(host) + ) + + async def send_dns_cache_hit(self, host: str) -> None: + return await self._trace_config.on_dns_cache_hit.send( + self._session, self._trace_config_ctx, TraceDnsCacheHitParams(host) + ) + + async def send_dns_cache_miss(self, host: str) -> None: + return await self._trace_config.on_dns_cache_miss.send( + self._session, self._trace_config_ctx, TraceDnsCacheMissParams(host) + ) + + async def send_request_headers( + self, method: str, url: URL, headers: "CIMultiDict[str]" + ) -> None: + return await self._trace_config._on_request_headers_sent.send( + self._session, + self._trace_config_ctx, + TraceRequestHeadersSentParams(method, url, headers), + ) diff --git a/venv/lib/python3.12/site-packages/aiohttp/typedefs.py b/venv/lib/python3.12/site-packages/aiohttp/typedefs.py new file mode 100644 index 00000000..cc8c0825 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/typedefs.py @@ -0,0 +1,69 @@ +import json +import os +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Iterable, + Mapping, + Protocol, + Tuple, + Union, +) + +from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy, istr +from yarl import URL, Query as _Query + +Query = _Query + +DEFAULT_JSON_ENCODER = json.dumps +DEFAULT_JSON_DECODER = json.loads + +if TYPE_CHECKING: + _CIMultiDict = CIMultiDict[str] + _CIMultiDictProxy = CIMultiDictProxy[str] + _MultiDict = MultiDict[str] + _MultiDictProxy = MultiDictProxy[str] + from http.cookies import BaseCookie, Morsel + + from .web import Request, StreamResponse +else: + _CIMultiDict = CIMultiDict + _CIMultiDictProxy = CIMultiDictProxy + _MultiDict = MultiDict + _MultiDictProxy = MultiDictProxy + +Byteish = Union[bytes, bytearray, memoryview] +JSONEncoder = Callable[[Any], str] +JSONDecoder = Callable[[str], Any] +LooseHeaders = Union[ + Mapping[str, str], + Mapping[istr, str], + _CIMultiDict, + _CIMultiDictProxy, + Iterable[Tuple[Union[str, istr], str]], +] +RawHeaders = Tuple[Tuple[bytes, bytes], ...] +StrOrURL = Union[str, URL] + +LooseCookiesMappings = Mapping[str, Union[str, "BaseCookie[str]", "Morsel[Any]"]] +LooseCookiesIterables = Iterable[ + Tuple[str, Union[str, "BaseCookie[str]", "Morsel[Any]"]] +] +LooseCookies = Union[ + LooseCookiesMappings, + LooseCookiesIterables, + "BaseCookie[str]", +] + +Handler = Callable[["Request"], Awaitable["StreamResponse"]] + + +class Middleware(Protocol): + def __call__( + self, request: "Request", handler: Handler + ) -> Awaitable["StreamResponse"]: ... + + +PathLike = Union[str, "os.PathLike[str]"] diff --git a/venv/lib/python3.12/site-packages/aiohttp/web.py b/venv/lib/python3.12/site-packages/aiohttp/web.py new file mode 100644 index 00000000..5a1fc964 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web.py @@ -0,0 +1,592 @@ +import asyncio +import logging +import os +import socket +import sys +import warnings +from argparse import ArgumentParser +from collections.abc import Iterable +from contextlib import suppress +from importlib import import_module +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Iterable as TypingIterable, + List, + Optional, + Set, + Type, + Union, + cast, +) + +from .abc import AbstractAccessLogger +from .helpers import AppKey as AppKey +from .log import access_logger +from .typedefs import PathLike +from .web_app import Application as Application, CleanupError as CleanupError +from .web_exceptions import ( + HTTPAccepted as HTTPAccepted, + HTTPBadGateway as HTTPBadGateway, + HTTPBadRequest as HTTPBadRequest, + HTTPClientError as HTTPClientError, + HTTPConflict as HTTPConflict, + HTTPCreated as HTTPCreated, + HTTPError as HTTPError, + HTTPException as HTTPException, + HTTPExpectationFailed as HTTPExpectationFailed, + HTTPFailedDependency as HTTPFailedDependency, + HTTPForbidden as HTTPForbidden, + HTTPFound as HTTPFound, + HTTPGatewayTimeout as HTTPGatewayTimeout, + HTTPGone as HTTPGone, + HTTPInsufficientStorage as HTTPInsufficientStorage, + HTTPInternalServerError as HTTPInternalServerError, + HTTPLengthRequired as HTTPLengthRequired, + HTTPMethodNotAllowed as HTTPMethodNotAllowed, + HTTPMisdirectedRequest as HTTPMisdirectedRequest, + HTTPMove as HTTPMove, + HTTPMovedPermanently as HTTPMovedPermanently, + HTTPMultipleChoices as HTTPMultipleChoices, + HTTPNetworkAuthenticationRequired as HTTPNetworkAuthenticationRequired, + HTTPNoContent as HTTPNoContent, + HTTPNonAuthoritativeInformation as HTTPNonAuthoritativeInformation, + HTTPNotAcceptable as HTTPNotAcceptable, + HTTPNotExtended as HTTPNotExtended, + HTTPNotFound as HTTPNotFound, + HTTPNotImplemented as HTTPNotImplemented, + HTTPNotModified as HTTPNotModified, + HTTPOk as HTTPOk, + HTTPPartialContent as HTTPPartialContent, + HTTPPaymentRequired as HTTPPaymentRequired, + HTTPPermanentRedirect as HTTPPermanentRedirect, + HTTPPreconditionFailed as HTTPPreconditionFailed, + HTTPPreconditionRequired as HTTPPreconditionRequired, + HTTPProxyAuthenticationRequired as HTTPProxyAuthenticationRequired, + HTTPRedirection as HTTPRedirection, + HTTPRequestEntityTooLarge as HTTPRequestEntityTooLarge, + HTTPRequestHeaderFieldsTooLarge as HTTPRequestHeaderFieldsTooLarge, + HTTPRequestRangeNotSatisfiable as HTTPRequestRangeNotSatisfiable, + HTTPRequestTimeout as HTTPRequestTimeout, + HTTPRequestURITooLong as HTTPRequestURITooLong, + HTTPResetContent as HTTPResetContent, + HTTPSeeOther as HTTPSeeOther, + HTTPServerError as HTTPServerError, + HTTPServiceUnavailable as HTTPServiceUnavailable, + HTTPSuccessful as HTTPSuccessful, + HTTPTemporaryRedirect as HTTPTemporaryRedirect, + HTTPTooManyRequests as HTTPTooManyRequests, + HTTPUnauthorized as HTTPUnauthorized, + HTTPUnavailableForLegalReasons as HTTPUnavailableForLegalReasons, + HTTPUnprocessableEntity as HTTPUnprocessableEntity, + HTTPUnsupportedMediaType as HTTPUnsupportedMediaType, + HTTPUpgradeRequired as HTTPUpgradeRequired, + HTTPUseProxy as HTTPUseProxy, + HTTPVariantAlsoNegotiates as HTTPVariantAlsoNegotiates, + HTTPVersionNotSupported as HTTPVersionNotSupported, + NotAppKeyWarning as NotAppKeyWarning, +) +from .web_fileresponse import FileResponse as FileResponse +from .web_log import AccessLogger +from .web_middlewares import ( + middleware as middleware, + normalize_path_middleware as normalize_path_middleware, +) +from .web_protocol import ( + PayloadAccessError as PayloadAccessError, + RequestHandler as RequestHandler, + RequestPayloadError as RequestPayloadError, +) +from .web_request import ( + BaseRequest as BaseRequest, + FileField as FileField, + Request as Request, +) +from .web_response import ( + ContentCoding as ContentCoding, + Response as Response, + StreamResponse as StreamResponse, + json_response as json_response, +) +from .web_routedef import ( + AbstractRouteDef as AbstractRouteDef, + RouteDef as RouteDef, + RouteTableDef as RouteTableDef, + StaticDef as StaticDef, + delete as delete, + get as get, + head as head, + options as options, + patch as patch, + post as post, + put as put, + route as route, + static as static, + view as view, +) +from .web_runner import ( + AppRunner as AppRunner, + BaseRunner as BaseRunner, + BaseSite as BaseSite, + GracefulExit as GracefulExit, + NamedPipeSite as NamedPipeSite, + ServerRunner as ServerRunner, + SockSite as SockSite, + TCPSite as TCPSite, + UnixSite as UnixSite, +) +from .web_server import Server as Server +from .web_urldispatcher import ( + AbstractResource as AbstractResource, + AbstractRoute as AbstractRoute, + DynamicResource as DynamicResource, + PlainResource as PlainResource, + PrefixedSubAppResource as PrefixedSubAppResource, + Resource as Resource, + ResourceRoute as ResourceRoute, + StaticResource as StaticResource, + UrlDispatcher as UrlDispatcher, + UrlMappingMatchInfo as UrlMappingMatchInfo, + View as View, +) +from .web_ws import ( + WebSocketReady as WebSocketReady, + WebSocketResponse as WebSocketResponse, + WSMsgType as WSMsgType, +) + +__all__ = ( + # web_app + "AppKey", + "Application", + "CleanupError", + # web_exceptions + "NotAppKeyWarning", + "HTTPAccepted", + "HTTPBadGateway", + "HTTPBadRequest", + "HTTPClientError", + "HTTPConflict", + "HTTPCreated", + "HTTPError", + "HTTPException", + "HTTPExpectationFailed", + "HTTPFailedDependency", + "HTTPForbidden", + "HTTPFound", + "HTTPGatewayTimeout", + "HTTPGone", + "HTTPInsufficientStorage", + "HTTPInternalServerError", + "HTTPLengthRequired", + "HTTPMethodNotAllowed", + "HTTPMisdirectedRequest", + "HTTPMove", + "HTTPMovedPermanently", + "HTTPMultipleChoices", + "HTTPNetworkAuthenticationRequired", + "HTTPNoContent", + "HTTPNonAuthoritativeInformation", + "HTTPNotAcceptable", + "HTTPNotExtended", + "HTTPNotFound", + "HTTPNotImplemented", + "HTTPNotModified", + "HTTPOk", + "HTTPPartialContent", + "HTTPPaymentRequired", + "HTTPPermanentRedirect", + "HTTPPreconditionFailed", + "HTTPPreconditionRequired", + "HTTPProxyAuthenticationRequired", + "HTTPRedirection", + "HTTPRequestEntityTooLarge", + "HTTPRequestHeaderFieldsTooLarge", + "HTTPRequestRangeNotSatisfiable", + "HTTPRequestTimeout", + "HTTPRequestURITooLong", + "HTTPResetContent", + "HTTPSeeOther", + "HTTPServerError", + "HTTPServiceUnavailable", + "HTTPSuccessful", + "HTTPTemporaryRedirect", + "HTTPTooManyRequests", + "HTTPUnauthorized", + "HTTPUnavailableForLegalReasons", + "HTTPUnprocessableEntity", + "HTTPUnsupportedMediaType", + "HTTPUpgradeRequired", + "HTTPUseProxy", + "HTTPVariantAlsoNegotiates", + "HTTPVersionNotSupported", + # web_fileresponse + "FileResponse", + # web_middlewares + "middleware", + "normalize_path_middleware", + # web_protocol + "PayloadAccessError", + "RequestHandler", + "RequestPayloadError", + # web_request + "BaseRequest", + "FileField", + "Request", + # web_response + "ContentCoding", + "Response", + "StreamResponse", + "json_response", + # web_routedef + "AbstractRouteDef", + "RouteDef", + "RouteTableDef", + "StaticDef", + "delete", + "get", + "head", + "options", + "patch", + "post", + "put", + "route", + "static", + "view", + # web_runner + "AppRunner", + "BaseRunner", + "BaseSite", + "GracefulExit", + "ServerRunner", + "SockSite", + "TCPSite", + "UnixSite", + "NamedPipeSite", + # web_server + "Server", + # web_urldispatcher + "AbstractResource", + "AbstractRoute", + "DynamicResource", + "PlainResource", + "PrefixedSubAppResource", + "Resource", + "ResourceRoute", + "StaticResource", + "UrlDispatcher", + "UrlMappingMatchInfo", + "View", + # web_ws + "WebSocketReady", + "WebSocketResponse", + "WSMsgType", + # web + "run_app", +) + + +if TYPE_CHECKING: + from ssl import SSLContext +else: + try: + from ssl import SSLContext + except ImportError: # pragma: no cover + SSLContext = object # type: ignore[misc,assignment] + +# Only display warning when using -Wdefault, -We, -X dev or similar. +warnings.filterwarnings("ignore", category=NotAppKeyWarning, append=True) + +HostSequence = TypingIterable[str] + + +async def _run_app( + app: Union[Application, Awaitable[Application]], + *, + host: Optional[Union[str, HostSequence]] = None, + port: Optional[int] = None, + path: Union[PathLike, TypingIterable[PathLike], None] = None, + sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, + ssl_context: Optional[SSLContext] = None, + print: Optional[Callable[..., None]] = print, + backlog: int = 128, + reuse_address: Optional[bool] = None, + reuse_port: Optional[bool] = None, + **kwargs: Any, # TODO(PY311): Use Unpack +) -> None: + # An internal function to actually do all dirty job for application running + if asyncio.iscoroutine(app): + app = await app + + app = cast(Application, app) + + runner = AppRunner(app, **kwargs) + + await runner.setup() + + sites: List[BaseSite] = [] + + try: + if host is not None: + if isinstance(host, str): + sites.append( + TCPSite( + runner, + host, + port, + ssl_context=ssl_context, + backlog=backlog, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + else: + for h in host: + sites.append( + TCPSite( + runner, + h, + port, + ssl_context=ssl_context, + backlog=backlog, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + elif path is None and sock is None or port is not None: + sites.append( + TCPSite( + runner, + port=port, + ssl_context=ssl_context, + backlog=backlog, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + + if path is not None: + if isinstance(path, (str, os.PathLike)): + sites.append( + UnixSite( + runner, + path, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + else: + for p in path: + sites.append( + UnixSite( + runner, + p, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + + if sock is not None: + if not isinstance(sock, Iterable): + sites.append( + SockSite( + runner, + sock, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + else: + for s in sock: + sites.append( + SockSite( + runner, + s, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + for site in sites: + await site.start() + + if print: # pragma: no branch + names = sorted(str(s.name) for s in runner.sites) + print( + "======== Running on {} ========\n" + "(Press CTRL+C to quit)".format(", ".join(names)) + ) + + # sleep forever by 1 hour intervals, + while True: + await asyncio.sleep(3600) + finally: + await runner.cleanup() + + +def _cancel_tasks( + to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop +) -> None: + if not to_cancel: + return + + for task in to_cancel: + task.cancel() + + loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True)) + + for task in to_cancel: + if task.cancelled(): + continue + if task.exception() is not None: + loop.call_exception_handler( + { + "message": "unhandled exception during asyncio.run() shutdown", + "exception": task.exception(), + "task": task, + } + ) + + +def run_app( + app: Union[Application, Awaitable[Application]], + *, + host: Optional[Union[str, HostSequence]] = None, + port: Optional[int] = None, + path: Union[PathLike, TypingIterable[PathLike], None] = None, + sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, + shutdown_timeout: float = 60.0, + keepalive_timeout: float = 75.0, + ssl_context: Optional[SSLContext] = None, + print: Optional[Callable[..., None]] = print, + backlog: int = 128, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + access_log_format: str = AccessLogger.LOG_FORMAT, + access_log: Optional[logging.Logger] = access_logger, + handle_signals: bool = True, + reuse_address: Optional[bool] = None, + reuse_port: Optional[bool] = None, + handler_cancellation: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Any, +) -> None: + """Run an app locally""" + if loop is None: + loop = asyncio.new_event_loop() + + # Configure if and only if in debugging mode and using the default logger + if loop.get_debug() and access_log and access_log.name == "aiohttp.access": + if access_log.level == logging.NOTSET: + access_log.setLevel(logging.DEBUG) + if not access_log.hasHandlers(): + access_log.addHandler(logging.StreamHandler()) + + main_task = loop.create_task( + _run_app( + app, + host=host, + port=port, + path=path, + sock=sock, + shutdown_timeout=shutdown_timeout, + keepalive_timeout=keepalive_timeout, + ssl_context=ssl_context, + print=print, + backlog=backlog, + access_log_class=access_log_class, + access_log_format=access_log_format, + access_log=access_log, + handle_signals=handle_signals, + reuse_address=reuse_address, + reuse_port=reuse_port, + handler_cancellation=handler_cancellation, + **kwargs, + ) + ) + + try: + asyncio.set_event_loop(loop) + loop.run_until_complete(main_task) + except (GracefulExit, KeyboardInterrupt): # pragma: no cover + pass + finally: + try: + main_task.cancel() + with suppress(asyncio.CancelledError): + loop.run_until_complete(main_task) + finally: + _cancel_tasks(asyncio.all_tasks(loop), loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + loop.close() + + +def main(argv: List[str]) -> None: + arg_parser = ArgumentParser( + description="aiohttp.web Application server", prog="aiohttp.web" + ) + arg_parser.add_argument( + "entry_func", + help=( + "Callable returning the `aiohttp.web.Application` instance to " + "run. Should be specified in the 'module:function' syntax." + ), + metavar="entry-func", + ) + arg_parser.add_argument( + "-H", + "--hostname", + help="TCP/IP hostname to serve on (default: localhost)", + default=None, + ) + arg_parser.add_argument( + "-P", + "--port", + help="TCP/IP port to serve on (default: %(default)r)", + type=int, + default=8080, + ) + arg_parser.add_argument( + "-U", + "--path", + help="Unix file system path to serve on. Can be combined with hostname " + "to serve on both Unix and TCP.", + ) + args, extra_argv = arg_parser.parse_known_args(argv) + + # Import logic + mod_str, _, func_str = args.entry_func.partition(":") + if not func_str or not mod_str: + arg_parser.error("'entry-func' not in 'module:function' syntax") + if mod_str.startswith("."): + arg_parser.error("relative module names not supported") + try: + module = import_module(mod_str) + except ImportError as ex: + arg_parser.error(f"unable to import {mod_str}: {ex}") + try: + func = getattr(module, func_str) + except AttributeError: + arg_parser.error(f"module {mod_str!r} has no attribute {func_str!r}") + + # Compatibility logic + if args.path is not None and not hasattr(socket, "AF_UNIX"): + arg_parser.error( + "file system paths not supported by your operating environment" + ) + + logging.basicConfig(level=logging.DEBUG) + + if args.path and args.hostname is None: + host = port = None + else: + host = args.hostname or "localhost" + port = args.port + + app = func(extra_argv) + run_app(app, host=host, port=port, path=args.path) + arg_parser.exit(message="Stopped\n") + + +if __name__ == "__main__": # pragma: no branch + main(sys.argv[1:]) # pragma: no cover diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_app.py b/venv/lib/python3.12/site-packages/aiohttp/web_app.py new file mode 100644 index 00000000..619c0085 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_app.py @@ -0,0 +1,620 @@ +import asyncio +import logging +import warnings +from functools import lru_cache, partial, update_wrapper +from typing import ( + TYPE_CHECKING, + Any, + AsyncIterator, + Awaitable, + Callable, + Dict, + Iterable, + Iterator, + List, + Mapping, + MutableMapping, + Optional, + Sequence, + Tuple, + Type, + TypeVar, + Union, + cast, + overload, +) + +from aiosignal import Signal +from frozenlist import FrozenList + +from . import hdrs +from .abc import ( + AbstractAccessLogger, + AbstractMatchInfo, + AbstractRouter, + AbstractStreamWriter, +) +from .helpers import DEBUG, AppKey +from .http_parser import RawRequestMessage +from .log import web_logger +from .streams import StreamReader +from .typedefs import Handler, Middleware +from .web_exceptions import NotAppKeyWarning +from .web_log import AccessLogger +from .web_middlewares import _fix_request_current_app +from .web_protocol import RequestHandler +from .web_request import Request +from .web_response import StreamResponse +from .web_routedef import AbstractRouteDef +from .web_server import Server +from .web_urldispatcher import ( + AbstractResource, + AbstractRoute, + Domain, + MaskDomain, + MatchedSubAppResource, + PrefixedSubAppResource, + SystemRoute, + UrlDispatcher, +) + +__all__ = ("Application", "CleanupError") + + +if TYPE_CHECKING: + _AppSignal = Signal["Application"] + _RespPrepareSignal = Signal[Request, StreamResponse] + _Middlewares = FrozenList[Middleware] + _MiddlewaresHandlers = Optional[Sequence[Tuple[Middleware, bool]]] + _Subapps = List["Application"] +else: + # No type checker mode, skip types + _AppSignal = Signal + _RespPrepareSignal = Signal + _Middlewares = FrozenList + _MiddlewaresHandlers = Optional[Sequence] + _Subapps = List + +_T = TypeVar("_T") +_U = TypeVar("_U") +_Resource = TypeVar("_Resource", bound=AbstractResource) + + +def _build_middlewares( + handler: Handler, apps: Tuple["Application", ...] +) -> Callable[[Request], Awaitable[StreamResponse]]: + """Apply middlewares to handler.""" + for app in apps[::-1]: + for m, _ in app._middlewares_handlers: # type: ignore[union-attr] + handler = update_wrapper(partial(m, handler=handler), handler) + return handler + + +_cached_build_middleware = lru_cache(maxsize=1024)(_build_middlewares) + + +class Application(MutableMapping[Union[str, AppKey[Any]], Any]): + ATTRS = frozenset( + [ + "logger", + "_debug", + "_router", + "_loop", + "_handler_args", + "_middlewares", + "_middlewares_handlers", + "_has_legacy_middlewares", + "_run_middlewares", + "_state", + "_frozen", + "_pre_frozen", + "_subapps", + "_on_response_prepare", + "_on_startup", + "_on_shutdown", + "_on_cleanup", + "_client_max_size", + "_cleanup_ctx", + ] + ) + + def __init__( + self, + *, + logger: logging.Logger = web_logger, + router: Optional[UrlDispatcher] = None, + middlewares: Iterable[Middleware] = (), + handler_args: Optional[Mapping[str, Any]] = None, + client_max_size: int = 1024**2, + loop: Optional[asyncio.AbstractEventLoop] = None, + debug: Any = ..., # mypy doesn't support ellipsis + ) -> None: + if router is None: + router = UrlDispatcher() + else: + warnings.warn( + "router argument is deprecated", DeprecationWarning, stacklevel=2 + ) + assert isinstance(router, AbstractRouter), router + + if loop is not None: + warnings.warn( + "loop argument is deprecated", DeprecationWarning, stacklevel=2 + ) + + if debug is not ...: + warnings.warn( + "debug argument is deprecated", DeprecationWarning, stacklevel=2 + ) + self._debug = debug + self._router: UrlDispatcher = router + self._loop = loop + self._handler_args = handler_args + self.logger = logger + + self._middlewares: _Middlewares = FrozenList(middlewares) + + # initialized on freezing + self._middlewares_handlers: _MiddlewaresHandlers = None + # initialized on freezing + self._run_middlewares: Optional[bool] = None + self._has_legacy_middlewares: bool = True + + self._state: Dict[Union[AppKey[Any], str], object] = {} + self._frozen = False + self._pre_frozen = False + self._subapps: _Subapps = [] + + self._on_response_prepare: _RespPrepareSignal = Signal(self) + self._on_startup: _AppSignal = Signal(self) + self._on_shutdown: _AppSignal = Signal(self) + self._on_cleanup: _AppSignal = Signal(self) + self._cleanup_ctx = CleanupContext() + self._on_startup.append(self._cleanup_ctx._on_startup) + self._on_cleanup.append(self._cleanup_ctx._on_cleanup) + self._client_max_size = client_max_size + + def __init_subclass__(cls: Type["Application"]) -> None: + warnings.warn( + "Inheritance class {} from web.Application " + "is discouraged".format(cls.__name__), + DeprecationWarning, + stacklevel=3, + ) + + if DEBUG: # pragma: no cover + + def __setattr__(self, name: str, val: Any) -> None: + if name not in self.ATTRS: + warnings.warn( + "Setting custom web.Application.{} attribute " + "is discouraged".format(name), + DeprecationWarning, + stacklevel=2, + ) + super().__setattr__(name, val) + + # MutableMapping API + + def __eq__(self, other: object) -> bool: + return self is other + + @overload # type: ignore[override] + def __getitem__(self, key: AppKey[_T]) -> _T: ... + + @overload + def __getitem__(self, key: str) -> Any: ... + + def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any: + return self._state[key] + + def _check_frozen(self) -> None: + if self._frozen: + warnings.warn( + "Changing state of started or joined application is deprecated", + DeprecationWarning, + stacklevel=3, + ) + + @overload # type: ignore[override] + def __setitem__(self, key: AppKey[_T], value: _T) -> None: ... + + @overload + def __setitem__(self, key: str, value: Any) -> None: ... + + def __setitem__(self, key: Union[str, AppKey[_T]], value: Any) -> None: + self._check_frozen() + if not isinstance(key, AppKey): + warnings.warn( + "It is recommended to use web.AppKey instances for keys.\n" + + "https://docs.aiohttp.org/en/stable/web_advanced.html" + + "#application-s-config", + category=NotAppKeyWarning, + stacklevel=2, + ) + self._state[key] = value + + def __delitem__(self, key: Union[str, AppKey[_T]]) -> None: + self._check_frozen() + del self._state[key] + + def __len__(self) -> int: + return len(self._state) + + def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]: + return iter(self._state) + + def __hash__(self) -> int: + return id(self) + + @overload # type: ignore[override] + def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ... + + @overload + def get(self, key: AppKey[_T], default: _U) -> Union[_T, _U]: ... + + @overload + def get(self, key: str, default: Any = ...) -> Any: ... + + def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any: + return self._state.get(key, default) + + ######## + @property + def loop(self) -> asyncio.AbstractEventLoop: + # Technically the loop can be None + # but we mask it by explicit type cast + # to provide more convenient type annotation + warnings.warn("loop property is deprecated", DeprecationWarning, stacklevel=2) + return cast(asyncio.AbstractEventLoop, self._loop) + + def _set_loop(self, loop: Optional[asyncio.AbstractEventLoop]) -> None: + if loop is None: + loop = asyncio.get_event_loop() + if self._loop is not None and self._loop is not loop: + raise RuntimeError( + "web.Application instance initialized with different loop" + ) + + self._loop = loop + + # set loop debug + if self._debug is ...: + self._debug = loop.get_debug() + + # set loop to sub applications + for subapp in self._subapps: + subapp._set_loop(loop) + + @property + def pre_frozen(self) -> bool: + return self._pre_frozen + + def pre_freeze(self) -> None: + if self._pre_frozen: + return + + self._pre_frozen = True + self._middlewares.freeze() + self._router.freeze() + self._on_response_prepare.freeze() + self._cleanup_ctx.freeze() + self._on_startup.freeze() + self._on_shutdown.freeze() + self._on_cleanup.freeze() + self._middlewares_handlers = tuple(self._prepare_middleware()) + self._has_legacy_middlewares = any( + not new_style for _, new_style in self._middlewares_handlers + ) + + # If current app and any subapp do not have middlewares avoid run all + # of the code footprint that it implies, which have a middleware + # hardcoded per app that sets up the current_app attribute. If no + # middlewares are configured the handler will receive the proper + # current_app without needing all of this code. + self._run_middlewares = True if self.middlewares else False + + for subapp in self._subapps: + subapp.pre_freeze() + self._run_middlewares = self._run_middlewares or subapp._run_middlewares + + @property + def frozen(self) -> bool: + return self._frozen + + def freeze(self) -> None: + if self._frozen: + return + + self.pre_freeze() + self._frozen = True + for subapp in self._subapps: + subapp.freeze() + + @property + def debug(self) -> bool: + warnings.warn("debug property is deprecated", DeprecationWarning, stacklevel=2) + return self._debug # type: ignore[no-any-return] + + def _reg_subapp_signals(self, subapp: "Application") -> None: + def reg_handler(signame: str) -> None: + subsig = getattr(subapp, signame) + + async def handler(app: "Application") -> None: + await subsig.send(subapp) + + appsig = getattr(self, signame) + appsig.append(handler) + + reg_handler("on_startup") + reg_handler("on_shutdown") + reg_handler("on_cleanup") + + def add_subapp(self, prefix: str, subapp: "Application") -> PrefixedSubAppResource: + if not isinstance(prefix, str): + raise TypeError("Prefix must be str") + prefix = prefix.rstrip("/") + if not prefix: + raise ValueError("Prefix cannot be empty") + factory = partial(PrefixedSubAppResource, prefix, subapp) + return self._add_subapp(factory, subapp) + + def _add_subapp( + self, resource_factory: Callable[[], _Resource], subapp: "Application" + ) -> _Resource: + if self.frozen: + raise RuntimeError("Cannot add sub application to frozen application") + if subapp.frozen: + raise RuntimeError("Cannot add frozen application") + resource = resource_factory() + self.router.register_resource(resource) + self._reg_subapp_signals(subapp) + self._subapps.append(subapp) + subapp.pre_freeze() + if self._loop is not None: + subapp._set_loop(self._loop) + return resource + + def add_domain(self, domain: str, subapp: "Application") -> MatchedSubAppResource: + if not isinstance(domain, str): + raise TypeError("Domain must be str") + elif "*" in domain: + rule: Domain = MaskDomain(domain) + else: + rule = Domain(domain) + factory = partial(MatchedSubAppResource, rule, subapp) + return self._add_subapp(factory, subapp) + + def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]: + return self.router.add_routes(routes) + + @property + def on_response_prepare(self) -> _RespPrepareSignal: + return self._on_response_prepare + + @property + def on_startup(self) -> _AppSignal: + return self._on_startup + + @property + def on_shutdown(self) -> _AppSignal: + return self._on_shutdown + + @property + def on_cleanup(self) -> _AppSignal: + return self._on_cleanup + + @property + def cleanup_ctx(self) -> "CleanupContext": + return self._cleanup_ctx + + @property + def router(self) -> UrlDispatcher: + return self._router + + @property + def middlewares(self) -> _Middlewares: + return self._middlewares + + def _make_handler( + self, + *, + loop: Optional[asyncio.AbstractEventLoop] = None, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + **kwargs: Any, + ) -> Server: + + if not issubclass(access_log_class, AbstractAccessLogger): + raise TypeError( + "access_log_class must be subclass of " + "aiohttp.abc.AbstractAccessLogger, got {}".format(access_log_class) + ) + + self._set_loop(loop) + self.freeze() + + kwargs["debug"] = self._debug + kwargs["access_log_class"] = access_log_class + if self._handler_args: + for k, v in self._handler_args.items(): + kwargs[k] = v + + return Server( + self._handle, # type: ignore[arg-type] + request_factory=self._make_request, + loop=self._loop, + **kwargs, + ) + + def make_handler( + self, + *, + loop: Optional[asyncio.AbstractEventLoop] = None, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + **kwargs: Any, + ) -> Server: + + warnings.warn( + "Application.make_handler(...) is deprecated, use AppRunner API instead", + DeprecationWarning, + stacklevel=2, + ) + + return self._make_handler( + loop=loop, access_log_class=access_log_class, **kwargs + ) + + async def startup(self) -> None: + """Causes on_startup signal + + Should be called in the event loop along with the request handler. + """ + await self.on_startup.send(self) + + async def shutdown(self) -> None: + """Causes on_shutdown signal + + Should be called before cleanup() + """ + await self.on_shutdown.send(self) + + async def cleanup(self) -> None: + """Causes on_cleanup signal + + Should be called after shutdown() + """ + if self.on_cleanup.frozen: + await self.on_cleanup.send(self) + else: + # If an exception occurs in startup, ensure cleanup contexts are completed. + await self._cleanup_ctx._on_cleanup(self) + + def _make_request( + self, + message: RawRequestMessage, + payload: StreamReader, + protocol: RequestHandler, + writer: AbstractStreamWriter, + task: "asyncio.Task[None]", + _cls: Type[Request] = Request, + ) -> Request: + if TYPE_CHECKING: + assert self._loop is not None + return _cls( + message, + payload, + protocol, + writer, + task, + self._loop, + client_max_size=self._client_max_size, + ) + + def _prepare_middleware(self) -> Iterator[Tuple[Middleware, bool]]: + for m in reversed(self._middlewares): + if getattr(m, "__middleware_version__", None) == 1: + yield m, True + else: + warnings.warn( + f'old-style middleware "{m!r}" deprecated, see #2252', + DeprecationWarning, + stacklevel=2, + ) + yield m, False + + yield _fix_request_current_app(self), True + + async def _handle(self, request: Request) -> StreamResponse: + loop = asyncio.get_event_loop() + debug = loop.get_debug() + match_info = await self._router.resolve(request) + if debug: # pragma: no cover + if not isinstance(match_info, AbstractMatchInfo): + raise TypeError( + "match_info should be AbstractMatchInfo " + "instance, not {!r}".format(match_info) + ) + match_info.add_app(self) + + match_info.freeze() + + request._match_info = match_info + + if request.headers.get(hdrs.EXPECT): + resp = await match_info.expect_handler(request) + await request.writer.drain() + if resp is not None: + return resp + + handler = match_info.handler + + if self._run_middlewares: + # If its a SystemRoute, don't cache building the middlewares since + # they are constructed for every MatchInfoError as a new handler + # is made each time. + if not self._has_legacy_middlewares and not isinstance( + match_info.route, SystemRoute + ): + handler = _cached_build_middleware(handler, match_info.apps) + else: + for app in match_info.apps[::-1]: + for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] + if new_style: + handler = update_wrapper( + partial(m, handler=handler), handler + ) + else: + handler = await m(app, handler) # type: ignore[arg-type,assignment] + + return await handler(request) + + def __call__(self) -> "Application": + """gunicorn compatibility""" + return self + + def __repr__(self) -> str: + return f"" + + def __bool__(self) -> bool: + return True + + +class CleanupError(RuntimeError): + @property + def exceptions(self) -> List[BaseException]: + return cast(List[BaseException], self.args[1]) + + +if TYPE_CHECKING: + _CleanupContextBase = FrozenList[Callable[[Application], AsyncIterator[None]]] +else: + _CleanupContextBase = FrozenList + + +class CleanupContext(_CleanupContextBase): + def __init__(self) -> None: + super().__init__() + self._exits: List[AsyncIterator[None]] = [] + + async def _on_startup(self, app: Application) -> None: + for cb in self: + it = cb(app).__aiter__() + await it.__anext__() + self._exits.append(it) + + async def _on_cleanup(self, app: Application) -> None: + errors = [] + for it in reversed(self._exits): + try: + await it.__anext__() + except StopAsyncIteration: + pass + except (Exception, asyncio.CancelledError) as exc: + errors.append(exc) + else: + errors.append(RuntimeError(f"{it!r} has more than one 'yield'")) + if errors: + if len(errors) == 1: + raise errors[0] + else: + raise CleanupError("Multiple errors on cleanup stage", errors) diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_exceptions.py b/venv/lib/python3.12/site-packages/aiohttp/web_exceptions.py new file mode 100644 index 00000000..30792c28 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_exceptions.py @@ -0,0 +1,454 @@ +import warnings +from typing import Any, Dict, Iterable, List, Optional, Set # noqa + +from yarl import URL + +from .typedefs import LooseHeaders, StrOrURL +from .web_response import Response + +__all__ = ( + "HTTPException", + "HTTPError", + "HTTPRedirection", + "HTTPSuccessful", + "HTTPOk", + "HTTPCreated", + "HTTPAccepted", + "HTTPNonAuthoritativeInformation", + "HTTPNoContent", + "HTTPResetContent", + "HTTPPartialContent", + "HTTPMove", + "HTTPMultipleChoices", + "HTTPMovedPermanently", + "HTTPFound", + "HTTPSeeOther", + "HTTPNotModified", + "HTTPUseProxy", + "HTTPTemporaryRedirect", + "HTTPPermanentRedirect", + "HTTPClientError", + "HTTPBadRequest", + "HTTPUnauthorized", + "HTTPPaymentRequired", + "HTTPForbidden", + "HTTPNotFound", + "HTTPMethodNotAllowed", + "HTTPNotAcceptable", + "HTTPProxyAuthenticationRequired", + "HTTPRequestTimeout", + "HTTPConflict", + "HTTPGone", + "HTTPLengthRequired", + "HTTPPreconditionFailed", + "HTTPRequestEntityTooLarge", + "HTTPRequestURITooLong", + "HTTPUnsupportedMediaType", + "HTTPRequestRangeNotSatisfiable", + "HTTPExpectationFailed", + "HTTPMisdirectedRequest", + "HTTPUnprocessableEntity", + "HTTPFailedDependency", + "HTTPUpgradeRequired", + "HTTPPreconditionRequired", + "HTTPTooManyRequests", + "HTTPRequestHeaderFieldsTooLarge", + "HTTPUnavailableForLegalReasons", + "HTTPServerError", + "HTTPInternalServerError", + "HTTPNotImplemented", + "HTTPBadGateway", + "HTTPServiceUnavailable", + "HTTPGatewayTimeout", + "HTTPVersionNotSupported", + "HTTPVariantAlsoNegotiates", + "HTTPInsufficientStorage", + "HTTPNotExtended", + "HTTPNetworkAuthenticationRequired", +) + + +class NotAppKeyWarning(UserWarning): + """Warning when not using AppKey in Application.""" + + +############################################################ +# HTTP Exceptions +############################################################ + + +class HTTPException(Response, Exception): + + # You should set in subclasses: + # status = 200 + + status_code = -1 + empty_body = False + + __http_exception__ = True + + def __init__( + self, + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + if body is not None: + warnings.warn( + "body argument is deprecated for http web exceptions", + DeprecationWarning, + ) + if reason is not None and ("\r" in reason or "\n" in reason): + raise ValueError("Reason cannot contain \\r or \\n") + Response.__init__( + self, + status=self.status_code, + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + Exception.__init__(self, self.reason) + if self.body is None and not self.empty_body: + self.text = f"{self.status}: {self.reason}" + + def __bool__(self) -> bool: + return True + + +class HTTPError(HTTPException): + """Base class for exceptions with status codes in the 400s and 500s.""" + + +class HTTPRedirection(HTTPException): + """Base class for exceptions with status codes in the 300s.""" + + +class HTTPSuccessful(HTTPException): + """Base class for exceptions with status codes in the 200s.""" + + +class HTTPOk(HTTPSuccessful): + status_code = 200 + + +class HTTPCreated(HTTPSuccessful): + status_code = 201 + + +class HTTPAccepted(HTTPSuccessful): + status_code = 202 + + +class HTTPNonAuthoritativeInformation(HTTPSuccessful): + status_code = 203 + + +class HTTPNoContent(HTTPSuccessful): + status_code = 204 + empty_body = True + + +class HTTPResetContent(HTTPSuccessful): + status_code = 205 + empty_body = True + + +class HTTPPartialContent(HTTPSuccessful): + status_code = 206 + + +############################################################ +# 3xx redirection +############################################################ + + +class HTTPMove(HTTPRedirection): + def __init__( + self, + location: StrOrURL, + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + if not location: + raise ValueError("HTTP redirects need a location to redirect to.") + super().__init__( + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + self.headers["Location"] = str(URL(location)) + self.location = location + + +class HTTPMultipleChoices(HTTPMove): + status_code = 300 + + +class HTTPMovedPermanently(HTTPMove): + status_code = 301 + + +class HTTPFound(HTTPMove): + status_code = 302 + + +# This one is safe after a POST (the redirected location will be +# retrieved with GET): +class HTTPSeeOther(HTTPMove): + status_code = 303 + + +class HTTPNotModified(HTTPRedirection): + # FIXME: this should include a date or etag header + status_code = 304 + empty_body = True + + +class HTTPUseProxy(HTTPMove): + # Not a move, but looks a little like one + status_code = 305 + + +class HTTPTemporaryRedirect(HTTPMove): + status_code = 307 + + +class HTTPPermanentRedirect(HTTPMove): + status_code = 308 + + +############################################################ +# 4xx client error +############################################################ + + +class HTTPClientError(HTTPError): + pass + + +class HTTPBadRequest(HTTPClientError): + status_code = 400 + + +class HTTPUnauthorized(HTTPClientError): + status_code = 401 + + +class HTTPPaymentRequired(HTTPClientError): + status_code = 402 + + +class HTTPForbidden(HTTPClientError): + status_code = 403 + + +class HTTPNotFound(HTTPClientError): + status_code = 404 + + +class HTTPMethodNotAllowed(HTTPClientError): + status_code = 405 + + def __init__( + self, + method: str, + allowed_methods: Iterable[str], + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + allow = ",".join(sorted(allowed_methods)) + super().__init__( + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + self.headers["Allow"] = allow + self.allowed_methods: Set[str] = set(allowed_methods) + self.method = method.upper() + + +class HTTPNotAcceptable(HTTPClientError): + status_code = 406 + + +class HTTPProxyAuthenticationRequired(HTTPClientError): + status_code = 407 + + +class HTTPRequestTimeout(HTTPClientError): + status_code = 408 + + +class HTTPConflict(HTTPClientError): + status_code = 409 + + +class HTTPGone(HTTPClientError): + status_code = 410 + + +class HTTPLengthRequired(HTTPClientError): + status_code = 411 + + +class HTTPPreconditionFailed(HTTPClientError): + status_code = 412 + + +class HTTPRequestEntityTooLarge(HTTPClientError): + status_code = 413 + + def __init__(self, max_size: float, actual_size: float, **kwargs: Any) -> None: + kwargs.setdefault( + "text", + "Maximum request body size {} exceeded, " + "actual body size {}".format(max_size, actual_size), + ) + super().__init__(**kwargs) + + +class HTTPRequestURITooLong(HTTPClientError): + status_code = 414 + + +class HTTPUnsupportedMediaType(HTTPClientError): + status_code = 415 + + +class HTTPRequestRangeNotSatisfiable(HTTPClientError): + status_code = 416 + + +class HTTPExpectationFailed(HTTPClientError): + status_code = 417 + + +class HTTPMisdirectedRequest(HTTPClientError): + status_code = 421 + + +class HTTPUnprocessableEntity(HTTPClientError): + status_code = 422 + + +class HTTPFailedDependency(HTTPClientError): + status_code = 424 + + +class HTTPUpgradeRequired(HTTPClientError): + status_code = 426 + + +class HTTPPreconditionRequired(HTTPClientError): + status_code = 428 + + +class HTTPTooManyRequests(HTTPClientError): + status_code = 429 + + +class HTTPRequestHeaderFieldsTooLarge(HTTPClientError): + status_code = 431 + + +class HTTPUnavailableForLegalReasons(HTTPClientError): + status_code = 451 + + def __init__( + self, + link: Optional[StrOrURL], + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + super().__init__( + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + self._link = None + if link: + self._link = URL(link) + self.headers["Link"] = f'<{str(self._link)}>; rel="blocked-by"' + + @property + def link(self) -> Optional[URL]: + return self._link + + +############################################################ +# 5xx Server Error +############################################################ +# Response status codes beginning with the digit "5" indicate cases in +# which the server is aware that it has erred or is incapable of +# performing the request. Except when responding to a HEAD request, the +# server SHOULD include an entity containing an explanation of the error +# situation, and whether it is a temporary or permanent condition. User +# agents SHOULD display any included entity to the user. These response +# codes are applicable to any request method. + + +class HTTPServerError(HTTPError): + pass + + +class HTTPInternalServerError(HTTPServerError): + status_code = 500 + + +class HTTPNotImplemented(HTTPServerError): + status_code = 501 + + +class HTTPBadGateway(HTTPServerError): + status_code = 502 + + +class HTTPServiceUnavailable(HTTPServerError): + status_code = 503 + + +class HTTPGatewayTimeout(HTTPServerError): + status_code = 504 + + +class HTTPVersionNotSupported(HTTPServerError): + status_code = 505 + + +class HTTPVariantAlsoNegotiates(HTTPServerError): + status_code = 506 + + +class HTTPInsufficientStorage(HTTPServerError): + status_code = 507 + + +class HTTPNotExtended(HTTPServerError): + status_code = 510 + + +class HTTPNetworkAuthenticationRequired(HTTPServerError): + status_code = 511 diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_fileresponse.py b/venv/lib/python3.12/site-packages/aiohttp/web_fileresponse.py new file mode 100644 index 00000000..bba2c2c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_fileresponse.py @@ -0,0 +1,418 @@ +import asyncio +import io +import os +import pathlib +import sys +from contextlib import suppress +from enum import Enum, auto +from mimetypes import MimeTypes +from stat import S_ISREG +from types import MappingProxyType +from typing import ( # noqa + IO, + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Final, + Iterator, + List, + Optional, + Set, + Tuple, + Union, + cast, +) + +from . import hdrs +from .abc import AbstractStreamWriter +from .helpers import ETAG_ANY, ETag, must_be_empty_body +from .typedefs import LooseHeaders, PathLike +from .web_exceptions import ( + HTTPForbidden, + HTTPNotFound, + HTTPNotModified, + HTTPPartialContent, + HTTPPreconditionFailed, + HTTPRequestRangeNotSatisfiable, +) +from .web_response import StreamResponse + +__all__ = ("FileResponse",) + +if TYPE_CHECKING: + from .web_request import BaseRequest + + +_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]] + + +NOSENDFILE: Final[bool] = bool(os.environ.get("AIOHTTP_NOSENDFILE")) + +CONTENT_TYPES: Final[MimeTypes] = MimeTypes() + +# File extension to IANA encodings map that will be checked in the order defined. +ENCODING_EXTENSIONS = MappingProxyType( + {ext: CONTENT_TYPES.encodings_map[ext] for ext in (".br", ".gz")} +) + +FALLBACK_CONTENT_TYPE = "application/octet-stream" + +# Provide additional MIME type/extension pairs to be recognized. +# https://en.wikipedia.org/wiki/List_of_archive_formats#Compression_only +ADDITIONAL_CONTENT_TYPES = MappingProxyType( + { + "application/gzip": ".gz", + "application/x-brotli": ".br", + "application/x-bzip2": ".bz2", + "application/x-compress": ".Z", + "application/x-xz": ".xz", + } +) + + +class _FileResponseResult(Enum): + """The result of the file response.""" + + SEND_FILE = auto() # Ie a regular file to send + NOT_ACCEPTABLE = auto() # Ie a socket, or non-regular file + PRE_CONDITION_FAILED = auto() # Ie If-Match or If-None-Match failed + NOT_MODIFIED = auto() # 304 Not Modified + + +# Add custom pairs and clear the encodings map so guess_type ignores them. +CONTENT_TYPES.encodings_map.clear() +for content_type, extension in ADDITIONAL_CONTENT_TYPES.items(): + CONTENT_TYPES.add_type(content_type, extension) + + +_CLOSE_FUTURES: Set[asyncio.Future[None]] = set() + + +class FileResponse(StreamResponse): + """A response object can be used to send files.""" + + def __init__( + self, + path: PathLike, + chunk_size: int = 256 * 1024, + status: int = 200, + reason: Optional[str] = None, + headers: Optional[LooseHeaders] = None, + ) -> None: + super().__init__(status=status, reason=reason, headers=headers) + + self._path = pathlib.Path(path) + self._chunk_size = chunk_size + + def _seek_and_read(self, fobj: IO[Any], offset: int, chunk_size: int) -> bytes: + fobj.seek(offset) + return fobj.read(chunk_size) # type: ignore[no-any-return] + + async def _sendfile_fallback( + self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int + ) -> AbstractStreamWriter: + # To keep memory usage low,fobj is transferred in chunks + # controlled by the constructor's chunk_size argument. + + chunk_size = self._chunk_size + loop = asyncio.get_event_loop() + chunk = await loop.run_in_executor( + None, self._seek_and_read, fobj, offset, min(chunk_size, count) + ) + while chunk: + await writer.write(chunk) + count = count - len(chunk) + if count <= 0: + break + chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count)) + + await writer.drain() + return writer + + async def _sendfile( + self, request: "BaseRequest", fobj: IO[Any], offset: int, count: int + ) -> AbstractStreamWriter: + writer = await super().prepare(request) + assert writer is not None + + if NOSENDFILE or self.compression: + return await self._sendfile_fallback(writer, fobj, offset, count) + + loop = request._loop + transport = request.transport + assert transport is not None + + try: + await loop.sendfile(transport, fobj, offset, count) + except NotImplementedError: + return await self._sendfile_fallback(writer, fobj, offset, count) + + await super().write_eof() + return writer + + @staticmethod + def _etag_match(etag_value: str, etags: Tuple[ETag, ...], *, weak: bool) -> bool: + if len(etags) == 1 and etags[0].value == ETAG_ANY: + return True + return any( + etag.value == etag_value for etag in etags if weak or not etag.is_weak + ) + + async def _not_modified( + self, request: "BaseRequest", etag_value: str, last_modified: float + ) -> Optional[AbstractStreamWriter]: + self.set_status(HTTPNotModified.status_code) + self._length_check = False + self.etag = etag_value + self.last_modified = last_modified + # Delete any Content-Length headers provided by user. HTTP 304 + # should always have empty response body + return await super().prepare(request) + + async def _precondition_failed( + self, request: "BaseRequest" + ) -> Optional[AbstractStreamWriter]: + self.set_status(HTTPPreconditionFailed.status_code) + self.content_length = 0 + return await super().prepare(request) + + def _make_response( + self, request: "BaseRequest", accept_encoding: str + ) -> Tuple[ + _FileResponseResult, Optional[io.BufferedReader], os.stat_result, Optional[str] + ]: + """Return the response result, io object, stat result, and encoding. + + If an uncompressed file is returned, the encoding is set to + :py:data:`None`. + + This method should be called from a thread executor + since it calls os.stat which may block. + """ + file_path, st, file_encoding = self._get_file_path_stat_encoding( + accept_encoding + ) + if not file_path: + return _FileResponseResult.NOT_ACCEPTABLE, None, st, None + + etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}" + + # https://www.rfc-editor.org/rfc/rfc9110#section-13.1.1-2 + if (ifmatch := request.if_match) is not None and not self._etag_match( + etag_value, ifmatch, weak=False + ): + return _FileResponseResult.PRE_CONDITION_FAILED, None, st, file_encoding + + if ( + (unmodsince := request.if_unmodified_since) is not None + and ifmatch is None + and st.st_mtime > unmodsince.timestamp() + ): + return _FileResponseResult.PRE_CONDITION_FAILED, None, st, file_encoding + + # https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2-2 + if (ifnonematch := request.if_none_match) is not None and self._etag_match( + etag_value, ifnonematch, weak=True + ): + return _FileResponseResult.NOT_MODIFIED, None, st, file_encoding + + if ( + (modsince := request.if_modified_since) is not None + and ifnonematch is None + and st.st_mtime <= modsince.timestamp() + ): + return _FileResponseResult.NOT_MODIFIED, None, st, file_encoding + + fobj = file_path.open("rb") + with suppress(OSError): + # fstat() may not be available on all platforms + # Once we open the file, we want the fstat() to ensure + # the file has not changed between the first stat() + # and the open(). + st = os.stat(fobj.fileno()) + return _FileResponseResult.SEND_FILE, fobj, st, file_encoding + + def _get_file_path_stat_encoding( + self, accept_encoding: str + ) -> Tuple[Optional[pathlib.Path], os.stat_result, Optional[str]]: + file_path = self._path + for file_extension, file_encoding in ENCODING_EXTENSIONS.items(): + if file_encoding not in accept_encoding: + continue + + compressed_path = file_path.with_suffix(file_path.suffix + file_extension) + with suppress(OSError): + # Do not follow symlinks and ignore any non-regular files. + st = compressed_path.lstat() + if S_ISREG(st.st_mode): + return compressed_path, st, file_encoding + + # Fallback to the uncompressed file + st = file_path.stat() + return file_path if S_ISREG(st.st_mode) else None, st, None + + async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]: + loop = asyncio.get_running_loop() + # Encoding comparisons should be case-insensitive + # https://www.rfc-editor.org/rfc/rfc9110#section-8.4.1 + accept_encoding = request.headers.get(hdrs.ACCEPT_ENCODING, "").lower() + try: + response_result, fobj, st, file_encoding = await loop.run_in_executor( + None, self._make_response, request, accept_encoding + ) + except PermissionError: + self.set_status(HTTPForbidden.status_code) + return await super().prepare(request) + except OSError: + # Most likely to be FileNotFoundError or OSError for circular + # symlinks in python >= 3.13, so respond with 404. + self.set_status(HTTPNotFound.status_code) + return await super().prepare(request) + + # Forbid special files like sockets, pipes, devices, etc. + if response_result is _FileResponseResult.NOT_ACCEPTABLE: + self.set_status(HTTPForbidden.status_code) + return await super().prepare(request) + + if response_result is _FileResponseResult.PRE_CONDITION_FAILED: + return await self._precondition_failed(request) + + if response_result is _FileResponseResult.NOT_MODIFIED: + etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}" + last_modified = st.st_mtime + return await self._not_modified(request, etag_value, last_modified) + + assert fobj is not None + try: + return await self._prepare_open_file(request, fobj, st, file_encoding) + finally: + # We do not await here because we do not want to wait + # for the executor to finish before returning the response + # so the connection can begin servicing another request + # as soon as possible. + close_future = loop.run_in_executor(None, fobj.close) + # Hold a strong reference to the future to prevent it from being + # garbage collected before it completes. + _CLOSE_FUTURES.add(close_future) + close_future.add_done_callback(_CLOSE_FUTURES.remove) + + async def _prepare_open_file( + self, + request: "BaseRequest", + fobj: io.BufferedReader, + st: os.stat_result, + file_encoding: Optional[str], + ) -> Optional[AbstractStreamWriter]: + status = self._status + file_size: int = st.st_size + file_mtime: float = st.st_mtime + count: int = file_size + start: Optional[int] = None + + if (ifrange := request.if_range) is None or file_mtime <= ifrange.timestamp(): + # If-Range header check: + # condition = cached date >= last modification date + # return 206 if True else 200. + # if False: + # Range header would not be processed, return 200 + # if True but Range header missing + # return 200 + try: + rng = request.http_range + start = rng.start + end: Optional[int] = rng.stop + except ValueError: + # https://tools.ietf.org/html/rfc7233: + # A server generating a 416 (Range Not Satisfiable) response to + # a byte-range request SHOULD send a Content-Range header field + # with an unsatisfied-range value. + # The complete-length in a 416 response indicates the current + # length of the selected representation. + # + # Will do the same below. Many servers ignore this and do not + # send a Content-Range header with HTTP 416 + self._headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}" + self.set_status(HTTPRequestRangeNotSatisfiable.status_code) + return await super().prepare(request) + + # If a range request has been made, convert start, end slice + # notation into file pointer offset and count + if start is not None: + if start < 0 and end is None: # return tail of file + start += file_size + if start < 0: + # if Range:bytes=-1000 in request header but file size + # is only 200, there would be trouble without this + start = 0 + count = file_size - start + else: + # rfc7233:If the last-byte-pos value is + # absent, or if the value is greater than or equal to + # the current length of the representation data, + # the byte range is interpreted as the remainder + # of the representation (i.e., the server replaces the + # value of last-byte-pos with a value that is one less than + # the current length of the selected representation). + count = ( + min(end if end is not None else file_size, file_size) - start + ) + + if start >= file_size: + # HTTP 416 should be returned in this case. + # + # According to https://tools.ietf.org/html/rfc7233: + # If a valid byte-range-set includes at least one + # byte-range-spec with a first-byte-pos that is less than + # the current length of the representation, or at least one + # suffix-byte-range-spec with a non-zero suffix-length, + # then the byte-range-set is satisfiable. Otherwise, the + # byte-range-set is unsatisfiable. + self._headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}" + self.set_status(HTTPRequestRangeNotSatisfiable.status_code) + return await super().prepare(request) + + status = HTTPPartialContent.status_code + # Even though you are sending the whole file, you should still + # return a HTTP 206 for a Range request. + self.set_status(status) + + # If the Content-Type header is not already set, guess it based on the + # extension of the request path. The encoding returned by guess_type + # can be ignored since the map was cleared above. + if hdrs.CONTENT_TYPE not in self._headers: + if sys.version_info >= (3, 13): + guesser = CONTENT_TYPES.guess_file_type + else: + guesser = CONTENT_TYPES.guess_type + self.content_type = guesser(self._path)[0] or FALLBACK_CONTENT_TYPE + + if file_encoding: + self._headers[hdrs.CONTENT_ENCODING] = file_encoding + self._headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING + # Disable compression if we are already sending + # a compressed file since we don't want to double + # compress. + self._compression = False + + self.etag = f"{st.st_mtime_ns:x}-{st.st_size:x}" + self.last_modified = file_mtime + self.content_length = count + + self._headers[hdrs.ACCEPT_RANGES] = "bytes" + + if status == HTTPPartialContent.status_code: + real_start = start + assert real_start is not None + self._headers[hdrs.CONTENT_RANGE] = "bytes {}-{}/{}".format( + real_start, real_start + count - 1, file_size + ) + + # If we are sending 0 bytes calling sendfile() will throw a ValueError + if count == 0 or must_be_empty_body(request.method, status): + return await super().prepare(request) + + # be aware that start could be None or int=0 here. + offset = start or 0 + + return await self._sendfile(request, fobj, offset, count) diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_log.py b/venv/lib/python3.12/site-packages/aiohttp/web_log.py new file mode 100644 index 00000000..02a545ae --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_log.py @@ -0,0 +1,234 @@ +import datetime +import functools +import logging +import os +import re +import time as time_mod +from collections import namedtuple +from collections.abc import Iterable +from typing import Callable, ClassVar, Dict, List, Optional, Tuple + +from .abc import AbstractAccessLogger +from .web_request import BaseRequest +from .web_response import StreamResponse + +KeyMethod = namedtuple("KeyMethod", "key method") + + +class AccessLogger(AbstractAccessLogger): + """Helper object to log access. + + Usage: + log = logging.getLogger("spam") + log_format = "%a %{User-Agent}i" + access_logger = AccessLogger(log, log_format) + access_logger.log(request, response, time) + + Format: + %% The percent sign + %a Remote IP-address (IP-address of proxy if using reverse proxy) + %t Time when the request was started to process + %P The process ID of the child that serviced the request + %r First line of request + %s Response status code + %b Size of response in bytes, including HTTP headers + %T Time taken to serve the request, in seconds + %Tf Time taken to serve the request, in seconds with floating fraction + in .06f format + %D Time taken to serve the request, in microseconds + %{FOO}i request.headers['FOO'] + %{FOO}o response.headers['FOO'] + %{FOO}e os.environ['FOO'] + + """ + + LOG_FORMAT_MAP = { + "a": "remote_address", + "t": "request_start_time", + "P": "process_id", + "r": "first_request_line", + "s": "response_status", + "b": "response_size", + "T": "request_time", + "Tf": "request_time_frac", + "D": "request_time_micro", + "i": "request_header", + "o": "response_header", + } + + LOG_FORMAT = '%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"' + FORMAT_RE = re.compile(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbOD]|Tf?)") + CLEANUP_RE = re.compile(r"(%[^s])") + _FORMAT_CACHE: Dict[str, Tuple[str, List[KeyMethod]]] = {} + + _cached_tz: ClassVar[Optional[datetime.timezone]] = None + _cached_tz_expires: ClassVar[float] = 0.0 + + def __init__(self, logger: logging.Logger, log_format: str = LOG_FORMAT) -> None: + """Initialise the logger. + + logger is a logger object to be used for logging. + log_format is a string with apache compatible log format description. + + """ + super().__init__(logger, log_format=log_format) + + _compiled_format = AccessLogger._FORMAT_CACHE.get(log_format) + if not _compiled_format: + _compiled_format = self.compile_format(log_format) + AccessLogger._FORMAT_CACHE[log_format] = _compiled_format + + self._log_format, self._methods = _compiled_format + + def compile_format(self, log_format: str) -> Tuple[str, List[KeyMethod]]: + """Translate log_format into form usable by modulo formatting + + All known atoms will be replaced with %s + Also methods for formatting of those atoms will be added to + _methods in appropriate order + + For example we have log_format = "%a %t" + This format will be translated to "%s %s" + Also contents of _methods will be + [self._format_a, self._format_t] + These method will be called and results will be passed + to translated string format. + + Each _format_* method receive 'args' which is list of arguments + given to self.log + + Exceptions are _format_e, _format_i and _format_o methods which + also receive key name (by functools.partial) + + """ + # list of (key, method) tuples, we don't use an OrderedDict as users + # can repeat the same key more than once + methods = list() + + for atom in self.FORMAT_RE.findall(log_format): + if atom[1] == "": + format_key1 = self.LOG_FORMAT_MAP[atom[0]] + m = getattr(AccessLogger, "_format_%s" % atom[0]) + key_method = KeyMethod(format_key1, m) + else: + format_key2 = (self.LOG_FORMAT_MAP[atom[2]], atom[1]) + m = getattr(AccessLogger, "_format_%s" % atom[2]) + key_method = KeyMethod(format_key2, functools.partial(m, atom[1])) + + methods.append(key_method) + + log_format = self.FORMAT_RE.sub(r"%s", log_format) + log_format = self.CLEANUP_RE.sub(r"%\1", log_format) + return log_format, methods + + @staticmethod + def _format_i( + key: str, request: BaseRequest, response: StreamResponse, time: float + ) -> str: + if request is None: + return "(no headers)" + + # suboptimal, make istr(key) once + return request.headers.get(key, "-") + + @staticmethod + def _format_o( + key: str, request: BaseRequest, response: StreamResponse, time: float + ) -> str: + # suboptimal, make istr(key) once + return response.headers.get(key, "-") + + @staticmethod + def _format_a(request: BaseRequest, response: StreamResponse, time: float) -> str: + if request is None: + return "-" + ip = request.remote + return ip if ip is not None else "-" + + @classmethod + def _get_local_time(cls) -> datetime.datetime: + if cls._cached_tz is None or time_mod.time() >= cls._cached_tz_expires: + gmtoff = time_mod.localtime().tm_gmtoff + cls._cached_tz = tz = datetime.timezone(datetime.timedelta(seconds=gmtoff)) + + now = datetime.datetime.now(tz) + # Expire at every 30 mins, as any DST change should occur at 0/30 mins past. + d = now + datetime.timedelta(minutes=30) + d = d.replace(minute=30 if d.minute >= 30 else 0, second=0, microsecond=0) + cls._cached_tz_expires = d.timestamp() + return now + + return datetime.datetime.now(cls._cached_tz) + + @staticmethod + def _format_t(request: BaseRequest, response: StreamResponse, time: float) -> str: + now = AccessLogger._get_local_time() + start_time = now - datetime.timedelta(seconds=time) + return start_time.strftime("[%d/%b/%Y:%H:%M:%S %z]") + + @staticmethod + def _format_P(request: BaseRequest, response: StreamResponse, time: float) -> str: + return "<%s>" % os.getpid() + + @staticmethod + def _format_r(request: BaseRequest, response: StreamResponse, time: float) -> str: + if request is None: + return "-" + return "{} {} HTTP/{}.{}".format( + request.method, + request.path_qs, + request.version.major, + request.version.minor, + ) + + @staticmethod + def _format_s(request: BaseRequest, response: StreamResponse, time: float) -> int: + return response.status + + @staticmethod + def _format_b(request: BaseRequest, response: StreamResponse, time: float) -> int: + return response.body_length + + @staticmethod + def _format_T(request: BaseRequest, response: StreamResponse, time: float) -> str: + return str(round(time)) + + @staticmethod + def _format_Tf(request: BaseRequest, response: StreamResponse, time: float) -> str: + return "%06f" % time + + @staticmethod + def _format_D(request: BaseRequest, response: StreamResponse, time: float) -> str: + return str(round(time * 1000000)) + + def _format_line( + self, request: BaseRequest, response: StreamResponse, time: float + ) -> Iterable[Tuple[str, Callable[[BaseRequest, StreamResponse, float], str]]]: + return [(key, method(request, response, time)) for key, method in self._methods] + + @property + def enabled(self) -> bool: + """Check if logger is enabled.""" + # Avoid formatting the log line if it will not be emitted. + return self.logger.isEnabledFor(logging.INFO) + + def log(self, request: BaseRequest, response: StreamResponse, time: float) -> None: + try: + fmt_info = self._format_line(request, response, time) + + values = list() + extra = dict() + for key, value in fmt_info: + values.append(value) + + if key.__class__ is str: + extra[key] = value + else: + k1, k2 = key # type: ignore[misc] + dct = extra.get(k1, {}) # type: ignore[var-annotated,has-type] + dct[k2] = value # type: ignore[index,has-type] + extra[k1] = dct # type: ignore[has-type,assignment] + + self.logger.info(self._log_format % tuple(values), extra=extra) + except Exception: + self.logger.exception("Error in logging") diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_middlewares.py b/venv/lib/python3.12/site-packages/aiohttp/web_middlewares.py new file mode 100644 index 00000000..2f1f5f58 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_middlewares.py @@ -0,0 +1,121 @@ +import re +from typing import TYPE_CHECKING, Tuple, Type, TypeVar + +from .typedefs import Handler, Middleware +from .web_exceptions import HTTPMove, HTTPPermanentRedirect +from .web_request import Request +from .web_response import StreamResponse +from .web_urldispatcher import SystemRoute + +__all__ = ( + "middleware", + "normalize_path_middleware", +) + +if TYPE_CHECKING: + from .web_app import Application + +_Func = TypeVar("_Func") + + +async def _check_request_resolves(request: Request, path: str) -> Tuple[bool, Request]: + alt_request = request.clone(rel_url=path) + + match_info = await request.app.router.resolve(alt_request) + alt_request._match_info = match_info + + if match_info.http_exception is None: + return True, alt_request + + return False, request + + +def middleware(f: _Func) -> _Func: + f.__middleware_version__ = 1 # type: ignore[attr-defined] + return f + + +def normalize_path_middleware( + *, + append_slash: bool = True, + remove_slash: bool = False, + merge_slashes: bool = True, + redirect_class: Type[HTTPMove] = HTTPPermanentRedirect, +) -> Middleware: + """Factory for producing a middleware that normalizes the path of a request. + + Normalizing means: + - Add or remove a trailing slash to the path. + - Double slashes are replaced by one. + + The middleware returns as soon as it finds a path that resolves + correctly. The order if both merge and append/remove are enabled is + 1) merge slashes + 2) append/remove slash + 3) both merge slashes and append/remove slash. + If the path resolves with at least one of those conditions, it will + redirect to the new path. + + Only one of `append_slash` and `remove_slash` can be enabled. If both + are `True` the factory will raise an assertion error + + If `append_slash` is `True` the middleware will append a slash when + needed. If a resource is defined with trailing slash and the request + comes without it, it will append it automatically. + + If `remove_slash` is `True`, `append_slash` must be `False`. When enabled + the middleware will remove trailing slashes and redirect if the resource + is defined + + If merge_slashes is True, merge multiple consecutive slashes in the + path into one. + """ + correct_configuration = not (append_slash and remove_slash) + assert correct_configuration, "Cannot both remove and append slash" + + @middleware + async def impl(request: Request, handler: Handler) -> StreamResponse: + if isinstance(request.match_info.route, SystemRoute): + paths_to_check = [] + if "?" in request.raw_path: + path, query = request.raw_path.split("?", 1) + query = "?" + query + else: + query = "" + path = request.raw_path + + if merge_slashes: + paths_to_check.append(re.sub("//+", "/", path)) + if append_slash and not request.path.endswith("/"): + paths_to_check.append(path + "/") + if remove_slash and request.path.endswith("/"): + paths_to_check.append(path[:-1]) + if merge_slashes and append_slash: + paths_to_check.append(re.sub("//+", "/", path + "/")) + if merge_slashes and remove_slash: + merged_slashes = re.sub("//+", "/", path) + paths_to_check.append(merged_slashes[:-1]) + + for path in paths_to_check: + path = re.sub("^//+", "/", path) # SECURITY: GHSA-v6wp-4m6f-gcjg + resolves, request = await _check_request_resolves(request, path) + if resolves: + raise redirect_class(request.raw_path + query) + + return await handler(request) + + return impl + + +def _fix_request_current_app(app: "Application") -> Middleware: + @middleware + async def impl(request: Request, handler: Handler) -> StreamResponse: + match_info = request.match_info + prev = match_info.current_app + match_info.current_app = app + try: + return await handler(request) + finally: + match_info.current_app = prev + + return impl diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_protocol.py b/venv/lib/python3.12/site-packages/aiohttp/web_protocol.py new file mode 100644 index 00000000..84e70e59 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_protocol.py @@ -0,0 +1,799 @@ +import asyncio +import asyncio.streams +import sys +import traceback +import warnings +from collections import deque +from contextlib import suppress +from html import escape as html_escape +from http import HTTPStatus +from logging import Logger +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Deque, + Optional, + Sequence, + Tuple, + Type, + Union, + cast, +) + +import attr +import yarl +from propcache import under_cached_property + +from .abc import AbstractAccessLogger, AbstractStreamWriter +from .base_protocol import BaseProtocol +from .helpers import ceil_timeout +from .http import ( + HttpProcessingError, + HttpRequestParser, + HttpVersion10, + RawRequestMessage, + StreamWriter, +) +from .http_exceptions import BadHttpMethod +from .log import access_logger, server_logger +from .streams import EMPTY_PAYLOAD, StreamReader +from .tcp_helpers import tcp_keepalive +from .web_exceptions import HTTPException, HTTPInternalServerError +from .web_log import AccessLogger +from .web_request import BaseRequest +from .web_response import Response, StreamResponse + +__all__ = ("RequestHandler", "RequestPayloadError", "PayloadAccessError") + +if TYPE_CHECKING: + import ssl + + from .web_server import Server + + +_RequestFactory = Callable[ + [ + RawRequestMessage, + StreamReader, + "RequestHandler", + AbstractStreamWriter, + "asyncio.Task[None]", + ], + BaseRequest, +] + +_RequestHandler = Callable[[BaseRequest], Awaitable[StreamResponse]] + +ERROR = RawRequestMessage( + "UNKNOWN", + "/", + HttpVersion10, + {}, # type: ignore[arg-type] + {}, # type: ignore[arg-type] + True, + None, + False, + False, + yarl.URL("/"), +) + + +class RequestPayloadError(Exception): + """Payload parsing error.""" + + +class PayloadAccessError(Exception): + """Payload was accessed after response was sent.""" + + +_PAYLOAD_ACCESS_ERROR = PayloadAccessError() + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class _ErrInfo: + status: int + exc: BaseException + message: str + + +_MsgType = Tuple[Union[RawRequestMessage, _ErrInfo], StreamReader] + + +class RequestHandler(BaseProtocol): + """HTTP protocol implementation. + + RequestHandler handles incoming HTTP request. It reads request line, + request headers and request payload and calls handle_request() method. + By default it always returns with 404 response. + + RequestHandler handles errors in incoming request, like bad + status line, bad headers or incomplete payload. If any error occurs, + connection gets closed. + + keepalive_timeout -- number of seconds before closing + keep-alive connection + + tcp_keepalive -- TCP keep-alive is on, default is on + + debug -- enable debug mode + + logger -- custom logger object + + access_log_class -- custom class for access_logger + + access_log -- custom logging object + + access_log_format -- access log format string + + loop -- Optional event loop + + max_line_size -- Optional maximum header line size + + max_field_size -- Optional maximum header field size + + max_headers -- Optional maximum header size + + timeout_ceil_threshold -- Optional value to specify + threshold to ceil() timeout + values + + """ + + __slots__ = ( + "max_field_size", + "max_headers", + "max_line_size", + "_request_count", + "_keepalive", + "_manager", + "_request_handler", + "_request_factory", + "_tcp_keepalive", + "_next_keepalive_close_time", + "_keepalive_handle", + "_keepalive_timeout", + "_lingering_time", + "_messages", + "_message_tail", + "_handler_waiter", + "_waiter", + "_task_handler", + "_upgrade", + "_payload_parser", + "_request_parser", + "_reading_paused", + "logger", + "debug", + "access_log", + "access_logger", + "_close", + "_force_close", + "_current_request", + "_timeout_ceil_threshold", + "_request_in_progress", + "_logging_enabled", + "_cache", + ) + + def __init__( + self, + manager: "Server", + *, + loop: asyncio.AbstractEventLoop, + # Default should be high enough that it's likely longer than a reverse proxy. + keepalive_timeout: float = 3630, + tcp_keepalive: bool = True, + logger: Logger = server_logger, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + access_log: Logger = access_logger, + access_log_format: str = AccessLogger.LOG_FORMAT, + debug: bool = False, + max_line_size: int = 8190, + max_headers: int = 128, + max_field_size: int = 8190, + lingering_time: float = 10.0, + read_bufsize: int = 2**16, + auto_decompress: bool = True, + timeout_ceil_threshold: float = 5, + ): + super().__init__(loop) + + # _request_count is the number of requests processed with the same connection. + self._request_count = 0 + self._keepalive = False + self._current_request: Optional[BaseRequest] = None + self._manager: Optional[Server] = manager + self._request_handler: Optional[_RequestHandler] = manager.request_handler + self._request_factory: Optional[_RequestFactory] = manager.request_factory + + self.max_line_size = max_line_size + self.max_headers = max_headers + self.max_field_size = max_field_size + + self._tcp_keepalive = tcp_keepalive + # placeholder to be replaced on keepalive timeout setup + self._next_keepalive_close_time = 0.0 + self._keepalive_handle: Optional[asyncio.Handle] = None + self._keepalive_timeout = keepalive_timeout + self._lingering_time = float(lingering_time) + + self._messages: Deque[_MsgType] = deque() + self._message_tail = b"" + + self._waiter: Optional[asyncio.Future[None]] = None + self._handler_waiter: Optional[asyncio.Future[None]] = None + self._task_handler: Optional[asyncio.Task[None]] = None + + self._upgrade = False + self._payload_parser: Any = None + self._request_parser: Optional[HttpRequestParser] = HttpRequestParser( + self, + loop, + read_bufsize, + max_line_size=max_line_size, + max_field_size=max_field_size, + max_headers=max_headers, + payload_exception=RequestPayloadError, + auto_decompress=auto_decompress, + ) + + self._timeout_ceil_threshold: float = 5 + try: + self._timeout_ceil_threshold = float(timeout_ceil_threshold) + except (TypeError, ValueError): + pass + + self.logger = logger + self.debug = debug + self.access_log = access_log + if access_log: + self.access_logger: Optional[AbstractAccessLogger] = access_log_class( + access_log, access_log_format + ) + self._logging_enabled = self.access_logger.enabled + else: + self.access_logger = None + self._logging_enabled = False + + self._close = False + self._force_close = False + self._request_in_progress = False + self._cache: dict[str, Any] = {} + + def __repr__(self) -> str: + return "<{} {}>".format( + self.__class__.__name__, + "connected" if self.transport is not None else "disconnected", + ) + + @under_cached_property + def ssl_context(self) -> Optional["ssl.SSLContext"]: + """Return SSLContext if available.""" + return ( + None + if self.transport is None + else self.transport.get_extra_info("sslcontext") + ) + + @under_cached_property + def peername( + self, + ) -> Optional[Union[str, Tuple[str, int, int, int], Tuple[str, int]]]: + """Return peername if available.""" + return ( + None + if self.transport is None + else self.transport.get_extra_info("peername") + ) + + @property + def keepalive_timeout(self) -> float: + return self._keepalive_timeout + + async def shutdown(self, timeout: Optional[float] = 15.0) -> None: + """Do worker process exit preparations. + + We need to clean up everything and stop accepting requests. + It is especially important for keep-alive connections. + """ + self._force_close = True + + if self._keepalive_handle is not None: + self._keepalive_handle.cancel() + + # Wait for graceful handler completion + if self._request_in_progress: + # The future is only created when we are shutting + # down while the handler is still processing a request + # to avoid creating a future for every request. + self._handler_waiter = self._loop.create_future() + try: + async with ceil_timeout(timeout): + await self._handler_waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._handler_waiter = None + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + # Then cancel handler and wait + try: + async with ceil_timeout(timeout): + if self._current_request is not None: + self._current_request._cancel(asyncio.CancelledError()) + + if self._task_handler is not None and not self._task_handler.done(): + await asyncio.shield(self._task_handler) + except (asyncio.CancelledError, asyncio.TimeoutError): + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise + + # force-close non-idle handler + if self._task_handler is not None: + self._task_handler.cancel() + + self.force_close() + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + super().connection_made(transport) + + real_transport = cast(asyncio.Transport, transport) + if self._tcp_keepalive: + tcp_keepalive(real_transport) + + assert self._manager is not None + self._manager.connection_made(self, real_transport) + + loop = self._loop + if sys.version_info >= (3, 12): + task = asyncio.Task(self.start(), loop=loop, eager_start=True) + else: + task = loop.create_task(self.start()) + self._task_handler = task + + def connection_lost(self, exc: Optional[BaseException]) -> None: + if self._manager is None: + return + self._manager.connection_lost(self, exc) + + # Grab value before setting _manager to None. + handler_cancellation = self._manager.handler_cancellation + + self.force_close() + super().connection_lost(exc) + self._manager = None + self._request_factory = None + self._request_handler = None + self._request_parser = None + + if self._keepalive_handle is not None: + self._keepalive_handle.cancel() + + if self._current_request is not None: + if exc is None: + exc = ConnectionResetError("Connection lost") + self._current_request._cancel(exc) + + if handler_cancellation and self._task_handler is not None: + self._task_handler.cancel() + + self._task_handler = None + + if self._payload_parser is not None: + self._payload_parser.feed_eof() + self._payload_parser = None + + def set_parser(self, parser: Any) -> None: + # Actual type is WebReader + assert self._payload_parser is None + + self._payload_parser = parser + + if self._message_tail: + self._payload_parser.feed_data(self._message_tail) + self._message_tail = b"" + + def eof_received(self) -> None: + pass + + def data_received(self, data: bytes) -> None: + if self._force_close or self._close: + return + # parse http messages + messages: Sequence[_MsgType] + if self._payload_parser is None and not self._upgrade: + assert self._request_parser is not None + try: + messages, upgraded, tail = self._request_parser.feed_data(data) + except HttpProcessingError as exc: + messages = [ + (_ErrInfo(status=400, exc=exc, message=exc.message), EMPTY_PAYLOAD) + ] + upgraded = False + tail = b"" + + for msg, payload in messages or (): + self._request_count += 1 + self._messages.append((msg, payload)) + + waiter = self._waiter + if messages and waiter is not None and not waiter.done(): + # don't set result twice + waiter.set_result(None) + + self._upgrade = upgraded + if upgraded and tail: + self._message_tail = tail + + # no parser, just store + elif self._payload_parser is None and self._upgrade and data: + self._message_tail += data + + # feed payload + elif data: + eof, tail = self._payload_parser.feed_data(data) + if eof: + self.close() + + def keep_alive(self, val: bool) -> None: + """Set keep-alive connection mode. + + :param bool val: new state. + """ + self._keepalive = val + if self._keepalive_handle: + self._keepalive_handle.cancel() + self._keepalive_handle = None + + def close(self) -> None: + """Close connection. + + Stop accepting new pipelining messages and close + connection when handlers done processing messages. + """ + self._close = True + if self._waiter: + self._waiter.cancel() + + def force_close(self) -> None: + """Forcefully close connection.""" + self._force_close = True + if self._waiter: + self._waiter.cancel() + if self.transport is not None: + self.transport.close() + self.transport = None + + def log_access( + self, request: BaseRequest, response: StreamResponse, time: Optional[float] + ) -> None: + if self._logging_enabled and self.access_logger is not None: + if TYPE_CHECKING: + assert time is not None + self.access_logger.log(request, response, self._loop.time() - time) + + def log_debug(self, *args: Any, **kw: Any) -> None: + if self.debug: + self.logger.debug(*args, **kw) + + def log_exception(self, *args: Any, **kw: Any) -> None: + self.logger.exception(*args, **kw) + + def _process_keepalive(self) -> None: + self._keepalive_handle = None + if self._force_close or not self._keepalive: + return + + loop = self._loop + now = loop.time() + close_time = self._next_keepalive_close_time + if now < close_time: + # Keep alive close check fired too early, reschedule + self._keepalive_handle = loop.call_at(close_time, self._process_keepalive) + return + + # handler in idle state + if self._waiter and not self._waiter.done(): + self.force_close() + + async def _handle_request( + self, + request: BaseRequest, + start_time: Optional[float], + request_handler: Callable[[BaseRequest], Awaitable[StreamResponse]], + ) -> Tuple[StreamResponse, bool]: + self._request_in_progress = True + try: + try: + self._current_request = request + resp = await request_handler(request) + finally: + self._current_request = None + except HTTPException as exc: + resp = exc + resp, reset = await self.finish_response(request, resp, start_time) + except asyncio.CancelledError: + raise + except asyncio.TimeoutError as exc: + self.log_debug("Request handler timed out.", exc_info=exc) + resp = self.handle_error(request, 504) + resp, reset = await self.finish_response(request, resp, start_time) + except Exception as exc: + resp = self.handle_error(request, 500, exc) + resp, reset = await self.finish_response(request, resp, start_time) + else: + # Deprecation warning (See #2415) + if getattr(resp, "__http_exception__", False): + warnings.warn( + "returning HTTPException object is deprecated " + "(#2415) and will be removed, " + "please raise the exception instead", + DeprecationWarning, + ) + + resp, reset = await self.finish_response(request, resp, start_time) + finally: + self._request_in_progress = False + if self._handler_waiter is not None: + self._handler_waiter.set_result(None) + + return resp, reset + + async def start(self) -> None: + """Process incoming request. + + It reads request line, request headers and request payload, then + calls handle_request() method. Subclass has to override + handle_request(). start() handles various exceptions in request + or response handling. Connection is being closed always unless + keep_alive(True) specified. + """ + loop = self._loop + manager = self._manager + assert manager is not None + keepalive_timeout = self._keepalive_timeout + resp = None + assert self._request_factory is not None + assert self._request_handler is not None + + while not self._force_close: + if not self._messages: + try: + # wait for next request + self._waiter = loop.create_future() + await self._waiter + finally: + self._waiter = None + + message, payload = self._messages.popleft() + + # time is only fetched if logging is enabled as otherwise + # its thrown away and never used. + start = loop.time() if self._logging_enabled else None + + manager.requests_count += 1 + writer = StreamWriter(self, loop) + if isinstance(message, _ErrInfo): + # make request_factory work + request_handler = self._make_error_handler(message) + message = ERROR + else: + request_handler = self._request_handler + + # Important don't hold a reference to the current task + # as on traceback it will prevent the task from being + # collected and will cause a memory leak. + request = self._request_factory( + message, + payload, + self, + writer, + self._task_handler or asyncio.current_task(loop), # type: ignore[arg-type] + ) + try: + # a new task is used for copy context vars (#3406) + coro = self._handle_request(request, start, request_handler) + if sys.version_info >= (3, 12): + task = asyncio.Task(coro, loop=loop, eager_start=True) + else: + task = loop.create_task(coro) + try: + resp, reset = await task + except ConnectionError: + self.log_debug("Ignored premature client disconnection") + break + + # Drop the processed task from asyncio.Task.all_tasks() early + del task + if reset: + self.log_debug("Ignored premature client disconnection 2") + break + + # notify server about keep-alive + self._keepalive = bool(resp.keep_alive) + + # check payload + if not payload.is_eof(): + lingering_time = self._lingering_time + if not self._force_close and lingering_time: + self.log_debug( + "Start lingering close timer for %s sec.", lingering_time + ) + + now = loop.time() + end_t = now + lingering_time + + try: + while not payload.is_eof() and now < end_t: + async with ceil_timeout(end_t - now): + # read and ignore + await payload.readany() + now = loop.time() + except (asyncio.CancelledError, asyncio.TimeoutError): + if ( + sys.version_info >= (3, 11) + and (t := asyncio.current_task()) + and t.cancelling() + ): + raise + + # if payload still uncompleted + if not payload.is_eof() and not self._force_close: + self.log_debug("Uncompleted request.") + self.close() + + payload.set_exception(_PAYLOAD_ACCESS_ERROR) + + except asyncio.CancelledError: + self.log_debug("Ignored premature client disconnection") + self.force_close() + raise + except Exception as exc: + self.log_exception("Unhandled exception", exc_info=exc) + self.force_close() + except BaseException: + self.force_close() + raise + finally: + request._task = None # type: ignore[assignment] # Break reference cycle in case of exception + if self.transport is None and resp is not None: + self.log_debug("Ignored premature client disconnection.") + + if self._keepalive and not self._close and not self._force_close: + # start keep-alive timer + close_time = loop.time() + keepalive_timeout + self._next_keepalive_close_time = close_time + if self._keepalive_handle is None: + self._keepalive_handle = loop.call_at( + close_time, self._process_keepalive + ) + else: + break + + # remove handler, close transport if no handlers left + if not self._force_close: + self._task_handler = None + if self.transport is not None: + self.transport.close() + + async def finish_response( + self, request: BaseRequest, resp: StreamResponse, start_time: Optional[float] + ) -> Tuple[StreamResponse, bool]: + """Prepare the response and write_eof, then log access. + + This has to + be called within the context of any exception so the access logger + can get exception information. Returns True if the client disconnects + prematurely. + """ + request._finish() + if self._request_parser is not None: + self._request_parser.set_upgraded(False) + self._upgrade = False + if self._message_tail: + self._request_parser.feed_data(self._message_tail) + self._message_tail = b"" + try: + prepare_meth = resp.prepare + except AttributeError: + if resp is None: + self.log_exception("Missing return statement on request handler") + else: + self.log_exception( + "Web-handler should return a response instance, " + "got {!r}".format(resp) + ) + exc = HTTPInternalServerError() + resp = Response( + status=exc.status, reason=exc.reason, text=exc.text, headers=exc.headers + ) + prepare_meth = resp.prepare + try: + await prepare_meth(request) + await resp.write_eof() + except ConnectionError: + self.log_access(request, resp, start_time) + return resp, True + + self.log_access(request, resp, start_time) + return resp, False + + def handle_error( + self, + request: BaseRequest, + status: int = 500, + exc: Optional[BaseException] = None, + message: Optional[str] = None, + ) -> StreamResponse: + """Handle errors. + + Returns HTTP response with specific status code. Logs additional + information. It always closes current connection. + """ + if self._request_count == 1 and isinstance(exc, BadHttpMethod): + # BadHttpMethod is common when a client sends non-HTTP + # or encrypted traffic to an HTTP port. This is expected + # to happen when connected to the public internet so we log + # it at the debug level as to not fill logs with noise. + self.logger.debug( + "Error handling request from %s", request.remote, exc_info=exc + ) + else: + self.log_exception( + "Error handling request from %s", request.remote, exc_info=exc + ) + + # some data already got sent, connection is broken + if request.writer.output_size > 0: + raise ConnectionError( + "Response is sent already, cannot send another response " + "with the error message" + ) + + ct = "text/plain" + if status == HTTPStatus.INTERNAL_SERVER_ERROR: + title = "{0.value} {0.phrase}".format(HTTPStatus.INTERNAL_SERVER_ERROR) + msg = HTTPStatus.INTERNAL_SERVER_ERROR.description + tb = None + if self.debug: + with suppress(Exception): + tb = traceback.format_exc() + + if "text/html" in request.headers.get("Accept", ""): + if tb: + tb = html_escape(tb) + msg = f"

Traceback:

\n
{tb}
" + message = ( + "" + "{title}" + "\n

{title}

" + "\n{msg}\n\n" + ).format(title=title, msg=msg) + ct = "text/html" + else: + if tb: + msg = tb + message = title + "\n\n" + msg + + resp = Response(status=status, text=message, content_type=ct) + resp.force_close() + + return resp + + def _make_error_handler( + self, err_info: _ErrInfo + ) -> Callable[[BaseRequest], Awaitable[StreamResponse]]: + async def handler(request: BaseRequest) -> StreamResponse: + return self.handle_error( + request, err_info.status, err_info.exc, err_info.message + ) + + return handler diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_request.py b/venv/lib/python3.12/site-packages/aiohttp/web_request.py new file mode 100644 index 00000000..f2d4c511 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_request.py @@ -0,0 +1,927 @@ +import asyncio +import datetime +import io +import re +import socket +import string +import tempfile +import types +import warnings +from types import MappingProxyType +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Final, + Iterator, + Mapping, + MutableMapping, + Optional, + Pattern, + Tuple, + Union, + cast, +) +from urllib.parse import parse_qsl + +import attr +from multidict import ( + CIMultiDict, + CIMultiDictProxy, + MultiDict, + MultiDictProxy, + MultiMapping, +) +from yarl import URL + +from . import hdrs +from ._cookie_helpers import parse_cookie_header +from .abc import AbstractStreamWriter +from .helpers import ( + _SENTINEL, + DEBUG, + ETAG_ANY, + LIST_QUOTED_ETAG_RE, + ChainMapProxy, + ETag, + HeadersMixin, + parse_http_date, + reify, + sentinel, + set_exception, +) +from .http_parser import RawRequestMessage +from .http_writer import HttpVersion +from .multipart import BodyPartReader, MultipartReader +from .streams import EmptyStreamReader, StreamReader +from .typedefs import ( + DEFAULT_JSON_DECODER, + JSONDecoder, + LooseHeaders, + RawHeaders, + StrOrURL, +) +from .web_exceptions import HTTPRequestEntityTooLarge +from .web_response import StreamResponse + +__all__ = ("BaseRequest", "FileField", "Request") + + +if TYPE_CHECKING: + from .web_app import Application + from .web_protocol import RequestHandler + from .web_urldispatcher import UrlMappingMatchInfo + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class FileField: + name: str + filename: str + file: io.BufferedReader + content_type: str + headers: CIMultiDictProxy[str] + + +_TCHAR: Final[str] = string.digits + string.ascii_letters + r"!#$%&'*+.^_`|~-" +# '-' at the end to prevent interpretation as range in a char class + +_TOKEN: Final[str] = rf"[{_TCHAR}]+" + +_QDTEXT: Final[str] = r"[{}]".format( + r"".join(chr(c) for c in (0x09, 0x20, 0x21) + tuple(range(0x23, 0x7F))) +) +# qdtext includes 0x5C to escape 0x5D ('\]') +# qdtext excludes obs-text (because obsoleted, and encoding not specified) + +_QUOTED_PAIR: Final[str] = r"\\[\t !-~]" + +_QUOTED_STRING: Final[str] = r'"(?:{quoted_pair}|{qdtext})*"'.format( + qdtext=_QDTEXT, quoted_pair=_QUOTED_PAIR +) + +_FORWARDED_PAIR: Final[str] = ( + r"({token})=({token}|{quoted_string})(:\d{{1,4}})?".format( + token=_TOKEN, quoted_string=_QUOTED_STRING + ) +) + +_QUOTED_PAIR_REPLACE_RE: Final[Pattern[str]] = re.compile(r"\\([\t !-~])") +# same pattern as _QUOTED_PAIR but contains a capture group + +_FORWARDED_PAIR_RE: Final[Pattern[str]] = re.compile(_FORWARDED_PAIR) + +############################################################ +# HTTP Request +############################################################ + + +class BaseRequest(MutableMapping[str, Any], HeadersMixin): + + POST_METHODS = { + hdrs.METH_PATCH, + hdrs.METH_POST, + hdrs.METH_PUT, + hdrs.METH_TRACE, + hdrs.METH_DELETE, + } + + ATTRS = HeadersMixin.ATTRS | frozenset( + [ + "_message", + "_protocol", + "_payload_writer", + "_payload", + "_headers", + "_method", + "_version", + "_rel_url", + "_post", + "_read_bytes", + "_state", + "_cache", + "_task", + "_client_max_size", + "_loop", + "_transport_sslcontext", + "_transport_peername", + ] + ) + _post: Optional[MultiDictProxy[Union[str, bytes, FileField]]] = None + _read_bytes: Optional[bytes] = None + + def __init__( + self, + message: RawRequestMessage, + payload: StreamReader, + protocol: "RequestHandler", + payload_writer: AbstractStreamWriter, + task: "asyncio.Task[None]", + loop: asyncio.AbstractEventLoop, + *, + client_max_size: int = 1024**2, + state: Optional[Dict[str, Any]] = None, + scheme: Optional[str] = None, + host: Optional[str] = None, + remote: Optional[str] = None, + ) -> None: + self._message = message + self._protocol = protocol + self._payload_writer = payload_writer + + self._payload = payload + self._headers: CIMultiDictProxy[str] = message.headers + self._method = message.method + self._version = message.version + self._cache: Dict[str, Any] = {} + url = message.url + if url.absolute: + if scheme is not None: + url = url.with_scheme(scheme) + if host is not None: + url = url.with_host(host) + # absolute URL is given, + # override auto-calculating url, host, and scheme + # all other properties should be good + self._cache["url"] = url + self._cache["host"] = url.host + self._cache["scheme"] = url.scheme + self._rel_url = url.relative() + else: + self._rel_url = url + if scheme is not None: + self._cache["scheme"] = scheme + if host is not None: + self._cache["host"] = host + + self._state = {} if state is None else state + self._task = task + self._client_max_size = client_max_size + self._loop = loop + + self._transport_sslcontext = protocol.ssl_context + self._transport_peername = protocol.peername + + if remote is not None: + self._cache["remote"] = remote + + def clone( + self, + *, + method: Union[str, _SENTINEL] = sentinel, + rel_url: Union[StrOrURL, _SENTINEL] = sentinel, + headers: Union[LooseHeaders, _SENTINEL] = sentinel, + scheme: Union[str, _SENTINEL] = sentinel, + host: Union[str, _SENTINEL] = sentinel, + remote: Union[str, _SENTINEL] = sentinel, + client_max_size: Union[int, _SENTINEL] = sentinel, + ) -> "BaseRequest": + """Clone itself with replacement some attributes. + + Creates and returns a new instance of Request object. If no parameters + are given, an exact copy is returned. If a parameter is not passed, it + will reuse the one from the current request object. + """ + if self._read_bytes: + raise RuntimeError("Cannot clone request after reading its content") + + dct: Dict[str, Any] = {} + if method is not sentinel: + dct["method"] = method + if rel_url is not sentinel: + new_url: URL = URL(rel_url) + dct["url"] = new_url + dct["path"] = str(new_url) + if headers is not sentinel: + # a copy semantic + dct["headers"] = CIMultiDictProxy(CIMultiDict(headers)) + dct["raw_headers"] = tuple( + (k.encode("utf-8"), v.encode("utf-8")) + for k, v in dct["headers"].items() + ) + + message = self._message._replace(**dct) + + kwargs = {} + if scheme is not sentinel: + kwargs["scheme"] = scheme + if host is not sentinel: + kwargs["host"] = host + if remote is not sentinel: + kwargs["remote"] = remote + if client_max_size is sentinel: + client_max_size = self._client_max_size + + return self.__class__( + message, + self._payload, + self._protocol, + self._payload_writer, + self._task, + self._loop, + client_max_size=client_max_size, + state=self._state.copy(), + **kwargs, + ) + + @property + def task(self) -> "asyncio.Task[None]": + return self._task + + @property + def protocol(self) -> "RequestHandler": + return self._protocol + + @property + def transport(self) -> Optional[asyncio.Transport]: + if self._protocol is None: + return None + return self._protocol.transport + + @property + def writer(self) -> AbstractStreamWriter: + return self._payload_writer + + @property + def client_max_size(self) -> int: + return self._client_max_size + + @reify + def message(self) -> RawRequestMessage: + warnings.warn("Request.message is deprecated", DeprecationWarning, stacklevel=3) + return self._message + + @reify + def rel_url(self) -> URL: + return self._rel_url + + @reify + def loop(self) -> asyncio.AbstractEventLoop: + warnings.warn( + "request.loop property is deprecated", DeprecationWarning, stacklevel=2 + ) + return self._loop + + # MutableMapping API + + def __getitem__(self, key: str) -> Any: + return self._state[key] + + def __setitem__(self, key: str, value: Any) -> None: + self._state[key] = value + + def __delitem__(self, key: str) -> None: + del self._state[key] + + def __len__(self) -> int: + return len(self._state) + + def __iter__(self) -> Iterator[str]: + return iter(self._state) + + ######## + + @reify + def secure(self) -> bool: + """A bool indicating if the request is handled with SSL.""" + return self.scheme == "https" + + @reify + def forwarded(self) -> Tuple[Mapping[str, str], ...]: + """A tuple containing all parsed Forwarded header(s). + + Makes an effort to parse Forwarded headers as specified by RFC 7239: + + - It adds one (immutable) dictionary per Forwarded 'field-value', ie + per proxy. The element corresponds to the data in the Forwarded + field-value added by the first proxy encountered by the client. Each + subsequent item corresponds to those added by later proxies. + - It checks that every value has valid syntax in general as specified + in section 4: either a 'token' or a 'quoted-string'. + - It un-escapes found escape sequences. + - It does NOT validate 'by' and 'for' contents as specified in section + 6. + - It does NOT validate 'host' contents (Host ABNF). + - It does NOT validate 'proto' contents for valid URI scheme names. + + Returns a tuple containing one or more immutable dicts + """ + elems = [] + for field_value in self._message.headers.getall(hdrs.FORWARDED, ()): + length = len(field_value) + pos = 0 + need_separator = False + elem: Dict[str, str] = {} + elems.append(types.MappingProxyType(elem)) + while 0 <= pos < length: + match = _FORWARDED_PAIR_RE.match(field_value, pos) + if match is not None: # got a valid forwarded-pair + if need_separator: + # bad syntax here, skip to next comma + pos = field_value.find(",", pos) + else: + name, value, port = match.groups() + if value[0] == '"': + # quoted string: remove quotes and unescape + value = _QUOTED_PAIR_REPLACE_RE.sub(r"\1", value[1:-1]) + if port: + value += port + elem[name.lower()] = value + pos += len(match.group(0)) + need_separator = True + elif field_value[pos] == ",": # next forwarded-element + need_separator = False + elem = {} + elems.append(types.MappingProxyType(elem)) + pos += 1 + elif field_value[pos] == ";": # next forwarded-pair + need_separator = False + pos += 1 + elif field_value[pos] in " \t": + # Allow whitespace even between forwarded-pairs, though + # RFC 7239 doesn't. This simplifies code and is in line + # with Postel's law. + pos += 1 + else: + # bad syntax here, skip to next comma + pos = field_value.find(",", pos) + return tuple(elems) + + @reify + def scheme(self) -> str: + """A string representing the scheme of the request. + + Hostname is resolved in this order: + + - overridden value by .clone(scheme=new_scheme) call. + - type of connection to peer: HTTPS if socket is SSL, HTTP otherwise. + + 'http' or 'https'. + """ + if self._transport_sslcontext: + return "https" + else: + return "http" + + @reify + def method(self) -> str: + """Read only property for getting HTTP method. + + The value is upper-cased str like 'GET', 'POST', 'PUT' etc. + """ + return self._method + + @reify + def version(self) -> HttpVersion: + """Read only property for getting HTTP version of request. + + Returns aiohttp.protocol.HttpVersion instance. + """ + return self._version + + @reify + def host(self) -> str: + """Hostname of the request. + + Hostname is resolved in this order: + + - overridden value by .clone(host=new_host) call. + - HOST HTTP header + - socket.getfqdn() value + + For example, 'example.com' or 'localhost:8080'. + + For historical reasons, the port number may be included. + """ + host = self._message.headers.get(hdrs.HOST) + if host is not None: + return host + return socket.getfqdn() + + @reify + def remote(self) -> Optional[str]: + """Remote IP of client initiated HTTP request. + + The IP is resolved in this order: + + - overridden value by .clone(remote=new_remote) call. + - peername of opened socket + """ + if self._transport_peername is None: + return None + if isinstance(self._transport_peername, (list, tuple)): + return str(self._transport_peername[0]) + return str(self._transport_peername) + + @reify + def url(self) -> URL: + """The full URL of the request.""" + # authority is used here because it may include the port number + # and we want yarl to parse it correctly + return URL.build(scheme=self.scheme, authority=self.host).join(self._rel_url) + + @reify + def path(self) -> str: + """The URL including *PATH INFO* without the host or scheme. + + E.g., ``/app/blog`` + """ + return self._rel_url.path + + @reify + def path_qs(self) -> str: + """The URL including PATH_INFO and the query string. + + E.g, /app/blog?id=10 + """ + return str(self._rel_url) + + @reify + def raw_path(self) -> str: + """The URL including raw *PATH INFO* without the host or scheme. + + Warning, the path is unquoted and may contains non valid URL characters + + E.g., ``/my%2Fpath%7Cwith%21some%25strange%24characters`` + """ + return self._message.path + + @reify + def query(self) -> "MultiMapping[str]": + """A multidict with all the variables in the query string.""" + return self._rel_url.query + + @reify + def query_string(self) -> str: + """The query string in the URL. + + E.g., id=10 + """ + return self._rel_url.query_string + + @reify + def headers(self) -> CIMultiDictProxy[str]: + """A case-insensitive multidict proxy with all headers.""" + return self._headers + + @reify + def raw_headers(self) -> RawHeaders: + """A sequence of pairs for all headers.""" + return self._message.raw_headers + + @reify + def if_modified_since(self) -> Optional[datetime.datetime]: + """The value of If-Modified-Since HTTP header, or None. + + This header is represented as a `datetime` object. + """ + return parse_http_date(self.headers.get(hdrs.IF_MODIFIED_SINCE)) + + @reify + def if_unmodified_since(self) -> Optional[datetime.datetime]: + """The value of If-Unmodified-Since HTTP header, or None. + + This header is represented as a `datetime` object. + """ + return parse_http_date(self.headers.get(hdrs.IF_UNMODIFIED_SINCE)) + + @staticmethod + def _etag_values(etag_header: str) -> Iterator[ETag]: + """Extract `ETag` objects from raw header.""" + if etag_header == ETAG_ANY: + yield ETag( + is_weak=False, + value=ETAG_ANY, + ) + else: + for match in LIST_QUOTED_ETAG_RE.finditer(etag_header): + is_weak, value, garbage = match.group(2, 3, 4) + # Any symbol captured by 4th group means + # that the following sequence is invalid. + if garbage: + break + + yield ETag( + is_weak=bool(is_weak), + value=value, + ) + + @classmethod + def _if_match_or_none_impl( + cls, header_value: Optional[str] + ) -> Optional[Tuple[ETag, ...]]: + if not header_value: + return None + + return tuple(cls._etag_values(header_value)) + + @reify + def if_match(self) -> Optional[Tuple[ETag, ...]]: + """The value of If-Match HTTP header, or None. + + This header is represented as a `tuple` of `ETag` objects. + """ + return self._if_match_or_none_impl(self.headers.get(hdrs.IF_MATCH)) + + @reify + def if_none_match(self) -> Optional[Tuple[ETag, ...]]: + """The value of If-None-Match HTTP header, or None. + + This header is represented as a `tuple` of `ETag` objects. + """ + return self._if_match_or_none_impl(self.headers.get(hdrs.IF_NONE_MATCH)) + + @reify + def if_range(self) -> Optional[datetime.datetime]: + """The value of If-Range HTTP header, or None. + + This header is represented as a `datetime` object. + """ + return parse_http_date(self.headers.get(hdrs.IF_RANGE)) + + @reify + def keep_alive(self) -> bool: + """Is keepalive enabled by client?""" + return not self._message.should_close + + @reify + def cookies(self) -> Mapping[str, str]: + """Return request cookies. + + A read-only dictionary-like object. + """ + # Use parse_cookie_header for RFC 6265 compliant Cookie header parsing + # that accepts special characters in cookie names (fixes #2683) + parsed = parse_cookie_header(self.headers.get(hdrs.COOKIE, "")) + # Extract values from Morsel objects + return MappingProxyType({name: morsel.value for name, morsel in parsed}) + + @reify + def http_range(self) -> slice: + """The content of Range HTTP header. + + Return a slice instance. + + """ + rng = self._headers.get(hdrs.RANGE) + start, end = None, None + if rng is not None: + try: + pattern = r"^bytes=(\d*)-(\d*)$" + start, end = re.findall(pattern, rng, re.ASCII)[0] + except IndexError: # pattern was not found in header + raise ValueError("range not in acceptable format") + + end = int(end) if end else None + start = int(start) if start else None + + if start is None and end is not None: + # end with no start is to return tail of content + start = -end + end = None + + if start is not None and end is not None: + # end is inclusive in range header, exclusive for slice + end += 1 + + if start >= end: + raise ValueError("start cannot be after end") + + if start is end is None: # No valid range supplied + raise ValueError("No start or end of range specified") + + return slice(start, end, 1) + + @reify + def content(self) -> StreamReader: + """Return raw payload stream.""" + return self._payload + + @property + def has_body(self) -> bool: + """Return True if request's HTTP BODY can be read, False otherwise.""" + warnings.warn( + "Deprecated, use .can_read_body #2005", DeprecationWarning, stacklevel=2 + ) + return not self._payload.at_eof() + + @property + def can_read_body(self) -> bool: + """Return True if request's HTTP BODY can be read, False otherwise.""" + return not self._payload.at_eof() + + @reify + def body_exists(self) -> bool: + """Return True if request has HTTP BODY, False otherwise.""" + return type(self._payload) is not EmptyStreamReader + + async def release(self) -> None: + """Release request. + + Eat unread part of HTTP BODY if present. + """ + while not self._payload.at_eof(): + await self._payload.readany() + + async def read(self) -> bytes: + """Read request body if present. + + Returns bytes object with full request content. + """ + if self._read_bytes is None: + body = bytearray() + while True: + chunk = await self._payload.readany() + body.extend(chunk) + if self._client_max_size: + body_size = len(body) + if body_size >= self._client_max_size: + raise HTTPRequestEntityTooLarge( + max_size=self._client_max_size, actual_size=body_size + ) + if not chunk: + break + self._read_bytes = bytes(body) + return self._read_bytes + + async def text(self) -> str: + """Return BODY as text using encoding from .charset.""" + bytes_body = await self.read() + encoding = self.charset or "utf-8" + return bytes_body.decode(encoding) + + async def json(self, *, loads: JSONDecoder = DEFAULT_JSON_DECODER) -> Any: + """Return BODY as JSON.""" + body = await self.text() + return loads(body) + + async def multipart(self) -> MultipartReader: + """Return async iterator to process BODY as multipart.""" + return MultipartReader( + self._headers, + self._payload, + max_field_size=self._protocol.max_field_size, + max_headers=self._protocol.max_headers, + ) + + async def post(self) -> "MultiDictProxy[Union[str, bytes, FileField]]": + """Return POST parameters.""" + if self._post is not None: + return self._post + if self._method not in self.POST_METHODS: + self._post = MultiDictProxy(MultiDict()) + return self._post + + content_type = self.content_type + if content_type not in ( + "", + "application/x-www-form-urlencoded", + "multipart/form-data", + ): + self._post = MultiDictProxy(MultiDict()) + return self._post + + out: MultiDict[Union[str, bytes, FileField]] = MultiDict() + + if content_type == "multipart/form-data": + multipart = await self.multipart() + max_size = self._client_max_size + + size = 0 + while (field := await multipart.next()) is not None: + field_ct = field.headers.get(hdrs.CONTENT_TYPE) + + if isinstance(field, BodyPartReader): + if field.name is None: + raise ValueError("Multipart field missing name.") + + # Note that according to RFC 7578, the Content-Type header + # is optional, even for files, so we can't assume it's + # present. + # https://tools.ietf.org/html/rfc7578#section-4.4 + if field.filename: + # store file in temp file + tmp = await self._loop.run_in_executor( + None, tempfile.TemporaryFile + ) + while chunk := await field.read_chunk(size=2**18): + async for decoded_chunk in field.decode_iter(chunk): + await self._loop.run_in_executor( + None, tmp.write, decoded_chunk + ) + size += len(decoded_chunk) + if 0 < max_size < size: + await self._loop.run_in_executor(None, tmp.close) + raise HTTPRequestEntityTooLarge( + max_size=max_size, actual_size=size + ) + await self._loop.run_in_executor(None, tmp.seek, 0) + + if field_ct is None: + field_ct = "application/octet-stream" + + ff = FileField( + field.name, + field.filename, + cast(io.BufferedReader, tmp), + field_ct, + field.headers, + ) + out.add(field.name, ff) + else: + # deal with ordinary data + raw_data = bytearray() + while chunk := await field.read_chunk(): + size += len(chunk) + if 0 < max_size < size: + raise HTTPRequestEntityTooLarge( + max_size=max_size, actual_size=size + ) + raw_data.extend(chunk) + + value = bytearray() + # form-data doesn't support compression, so don't need to check size again. + async for d in field.decode_iter(raw_data): + value.extend(d) + + if field_ct is None or field_ct.startswith("text/"): + charset = field.get_charset(default="utf-8") + out.add(field.name, value.decode(charset)) + else: + out.add(field.name, value) + else: + raise ValueError( + "To decode nested multipart you need to use custom reader", + ) + else: + data = await self.read() + if data: + charset = self.charset or "utf-8" + out.extend( + parse_qsl( + data.rstrip().decode(charset), + keep_blank_values=True, + encoding=charset, + ) + ) + + self._post = MultiDictProxy(out) + return self._post + + def get_extra_info(self, name: str, default: Any = None) -> Any: + """Extra info from protocol transport""" + protocol = self._protocol + if protocol is None: + return default + + transport = protocol.transport + if transport is None: + return default + + return transport.get_extra_info(name, default) + + def __repr__(self) -> str: + ascii_encodable_path = self.path.encode("ascii", "backslashreplace").decode( + "ascii" + ) + return "<{} {} {} >".format( + self.__class__.__name__, self._method, ascii_encodable_path + ) + + def __eq__(self, other: object) -> bool: + return id(self) == id(other) + + def __bool__(self) -> bool: + return True + + async def _prepare_hook(self, response: StreamResponse) -> None: + return + + def _cancel(self, exc: BaseException) -> None: + set_exception(self._payload, exc) + + def _finish(self) -> None: + if self._post is None or self.content_type != "multipart/form-data": + return + + # NOTE: Release file descriptors for the + # NOTE: `tempfile.Temporaryfile`-created `_io.BufferedRandom` + # NOTE: instances of files sent within multipart request body + # NOTE: via HTTP POST request. + for file_name, file_field_object in self._post.items(): + if isinstance(file_field_object, FileField): + file_field_object.file.close() + + +class Request(BaseRequest): + + ATTRS = BaseRequest.ATTRS | frozenset(["_match_info"]) + + _match_info: Optional["UrlMappingMatchInfo"] = None + + if DEBUG: + + def __setattr__(self, name: str, val: Any) -> None: + if name not in self.ATTRS: + warnings.warn( + "Setting custom {}.{} attribute " + "is discouraged".format(self.__class__.__name__, name), + DeprecationWarning, + stacklevel=2, + ) + super().__setattr__(name, val) + + def clone( + self, + *, + method: Union[str, _SENTINEL] = sentinel, + rel_url: Union[StrOrURL, _SENTINEL] = sentinel, + headers: Union[LooseHeaders, _SENTINEL] = sentinel, + scheme: Union[str, _SENTINEL] = sentinel, + host: Union[str, _SENTINEL] = sentinel, + remote: Union[str, _SENTINEL] = sentinel, + client_max_size: Union[int, _SENTINEL] = sentinel, + ) -> "Request": + ret = super().clone( + method=method, + rel_url=rel_url, + headers=headers, + scheme=scheme, + host=host, + remote=remote, + client_max_size=client_max_size, + ) + new_ret = cast(Request, ret) + new_ret._match_info = self._match_info + return new_ret + + @reify + def match_info(self) -> "UrlMappingMatchInfo": + """Result of route resolving.""" + match_info = self._match_info + assert match_info is not None + return match_info + + @property + def app(self) -> "Application": + """Application instance.""" + match_info = self._match_info + assert match_info is not None + return match_info.current_app + + @property + def config_dict(self) -> ChainMapProxy: + match_info = self._match_info + assert match_info is not None + lst = match_info.apps + app = self.app + idx = lst.index(app) + sublist = list(reversed(lst[: idx + 1])) + return ChainMapProxy(sublist) + + async def _prepare_hook(self, response: StreamResponse) -> None: + match_info = self._match_info + if match_info is None: + return + for app in match_info._apps: + if on_response_prepare := app.on_response_prepare: + await on_response_prepare.send(self, response) diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_response.py b/venv/lib/python3.12/site-packages/aiohttp/web_response.py new file mode 100644 index 00000000..364270e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_response.py @@ -0,0 +1,856 @@ +import asyncio +import collections.abc +import datetime +import enum +import json +import math +import time +import warnings +from concurrent.futures import Executor +from http import HTTPStatus +from http.cookies import SimpleCookie +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Iterator, + MutableMapping, + Optional, + Union, + cast, +) + +from multidict import CIMultiDict, istr + +from . import hdrs, payload +from .abc import AbstractStreamWriter +from .compression_utils import ZLibCompressor +from .helpers import ( + ETAG_ANY, + QUOTED_ETAG_RE, + ETag, + HeadersMixin, + must_be_empty_body, + parse_http_date, + rfc822_formatted_time, + sentinel, + should_remove_content_length, + validate_etag_value, +) +from .http import SERVER_SOFTWARE, HttpVersion10, HttpVersion11 +from .payload import Payload +from .typedefs import JSONEncoder, LooseHeaders + +REASON_PHRASES = {http_status.value: http_status.phrase for http_status in HTTPStatus} +LARGE_BODY_SIZE = 1024**2 + +__all__ = ("ContentCoding", "StreamResponse", "Response", "json_response") + + +if TYPE_CHECKING: + from .web_request import BaseRequest + + BaseClass = MutableMapping[str, Any] +else: + BaseClass = collections.abc.MutableMapping + + +# TODO(py311): Convert to StrEnum for wider use +class ContentCoding(enum.Enum): + # The content codings that we have support for. + # + # Additional registered codings are listed at: + # https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding + deflate = "deflate" + gzip = "gzip" + identity = "identity" + + +CONTENT_CODINGS = {coding.value: coding for coding in ContentCoding} + +############################################################ +# HTTP Response classes +############################################################ + + +class StreamResponse(BaseClass, HeadersMixin): + + _body: Union[None, bytes, bytearray, Payload] + _length_check = True + _body = None + _keep_alive: Optional[bool] = None + _chunked: bool = False + _compression: bool = False + _compression_strategy: Optional[int] = None + _compression_force: Optional[ContentCoding] = None + _req: Optional["BaseRequest"] = None + _payload_writer: Optional[AbstractStreamWriter] = None + _eof_sent: bool = False + _must_be_empty_body: Optional[bool] = None + _body_length = 0 + _cookies: Optional[SimpleCookie] = None + _send_headers_immediately = True + + def __init__( + self, + *, + status: int = 200, + reason: Optional[str] = None, + headers: Optional[LooseHeaders] = None, + _real_headers: Optional[CIMultiDict[str]] = None, + ) -> None: + """Initialize a new stream response object. + + _real_headers is an internal parameter used to pass a pre-populated + headers object. It is used by the `Response` class to avoid copying + the headers when creating a new response object. It is not intended + to be used by external code. + """ + self._state: Dict[str, Any] = {} + + if _real_headers is not None: + self._headers = _real_headers + elif headers is not None: + self._headers: CIMultiDict[str] = CIMultiDict(headers) + else: + self._headers = CIMultiDict() + + self._set_status(status, reason) + + @property + def prepared(self) -> bool: + return self._eof_sent or self._payload_writer is not None + + @property + def task(self) -> "Optional[asyncio.Task[None]]": + if self._req: + return self._req.task + else: + return None + + @property + def status(self) -> int: + return self._status + + @property + def chunked(self) -> bool: + return self._chunked + + @property + def compression(self) -> bool: + return self._compression + + @property + def reason(self) -> str: + return self._reason + + def set_status( + self, + status: int, + reason: Optional[str] = None, + ) -> None: + assert ( + not self.prepared + ), "Cannot change the response status code after the headers have been sent" + self._set_status(status, reason) + + def _set_status(self, status: int, reason: Optional[str]) -> None: + self._status = int(status) + if reason is None: + reason = REASON_PHRASES.get(self._status, "") + elif "\r" in reason or "\n" in reason: + raise ValueError("Reason cannot contain \\r or \\n") + self._reason = reason + + @property + def keep_alive(self) -> Optional[bool]: + return self._keep_alive + + def force_close(self) -> None: + self._keep_alive = False + + @property + def body_length(self) -> int: + return self._body_length + + @property + def output_length(self) -> int: + warnings.warn("output_length is deprecated", DeprecationWarning) + assert self._payload_writer + return self._payload_writer.buffer_size + + def enable_chunked_encoding(self, chunk_size: Optional[int] = None) -> None: + """Enables automatic chunked transfer encoding.""" + if hdrs.CONTENT_LENGTH in self._headers: + raise RuntimeError( + "You can't enable chunked encoding when a content length is set" + ) + if chunk_size is not None: + warnings.warn("Chunk size is deprecated #1615", DeprecationWarning) + self._chunked = True + + def enable_compression( + self, + force: Optional[Union[bool, ContentCoding]] = None, + strategy: Optional[int] = None, + ) -> None: + """Enables response compression encoding.""" + # Backwards compatibility for when force was a bool <0.17. + if isinstance(force, bool): + force = ContentCoding.deflate if force else ContentCoding.identity + warnings.warn( + "Using boolean for force is deprecated #3318", DeprecationWarning + ) + elif force is not None: + assert isinstance( + force, ContentCoding + ), "force should one of None, bool or ContentEncoding" + + self._compression = True + self._compression_force = force + self._compression_strategy = strategy + + @property + def headers(self) -> "CIMultiDict[str]": + return self._headers + + @property + def cookies(self) -> SimpleCookie: + if self._cookies is None: + self._cookies = SimpleCookie() + return self._cookies + + def set_cookie( + self, + name: str, + value: str, + *, + expires: Optional[str] = None, + domain: Optional[str] = None, + max_age: Optional[Union[int, str]] = None, + path: str = "/", + secure: Optional[bool] = None, + httponly: Optional[bool] = None, + version: Optional[str] = None, + samesite: Optional[str] = None, + partitioned: Optional[bool] = None, + ) -> None: + """Set or update response cookie. + + Sets new cookie or updates existent with new value. + Also updates only those params which are not None. + """ + if self._cookies is None: + self._cookies = SimpleCookie() + + self._cookies[name] = value + c = self._cookies[name] + + if expires is not None: + c["expires"] = expires + elif c.get("expires") == "Thu, 01 Jan 1970 00:00:00 GMT": + del c["expires"] + + if domain is not None: + c["domain"] = domain + + if max_age is not None: + c["max-age"] = str(max_age) + elif "max-age" in c: + del c["max-age"] + + c["path"] = path + + if secure is not None: + c["secure"] = secure + if httponly is not None: + c["httponly"] = httponly + if version is not None: + c["version"] = version + if samesite is not None: + c["samesite"] = samesite + + if partitioned is not None: + c["partitioned"] = partitioned + + def del_cookie( + self, + name: str, + *, + domain: Optional[str] = None, + path: str = "/", + secure: Optional[bool] = None, + httponly: Optional[bool] = None, + samesite: Optional[str] = None, + ) -> None: + """Delete cookie. + + Creates new empty expired cookie. + """ + # TODO: do we need domain/path here? + if self._cookies is not None: + self._cookies.pop(name, None) + self.set_cookie( + name, + "", + max_age=0, + expires="Thu, 01 Jan 1970 00:00:00 GMT", + domain=domain, + path=path, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + @property + def content_length(self) -> Optional[int]: + # Just a placeholder for adding setter + return super().content_length + + @content_length.setter + def content_length(self, value: Optional[int]) -> None: + if value is not None: + value = int(value) + if self._chunked: + raise RuntimeError( + "You can't set content length when chunked encoding is enable" + ) + self._headers[hdrs.CONTENT_LENGTH] = str(value) + else: + self._headers.pop(hdrs.CONTENT_LENGTH, None) + + @property + def content_type(self) -> str: + # Just a placeholder for adding setter + return super().content_type + + @content_type.setter + def content_type(self, value: str) -> None: + self.content_type # read header values if needed + self._content_type = str(value) + self._generate_content_type_header() + + @property + def charset(self) -> Optional[str]: + # Just a placeholder for adding setter + return super().charset + + @charset.setter + def charset(self, value: Optional[str]) -> None: + ctype = self.content_type # read header values if needed + if ctype == "application/octet-stream": + raise RuntimeError( + "Setting charset for application/octet-stream " + "doesn't make sense, setup content_type first" + ) + assert self._content_dict is not None + if value is None: + self._content_dict.pop("charset", None) + else: + self._content_dict["charset"] = str(value).lower() + self._generate_content_type_header() + + @property + def last_modified(self) -> Optional[datetime.datetime]: + """The value of Last-Modified HTTP header, or None. + + This header is represented as a `datetime` object. + """ + return parse_http_date(self._headers.get(hdrs.LAST_MODIFIED)) + + @last_modified.setter + def last_modified( + self, value: Optional[Union[int, float, datetime.datetime, str]] + ) -> None: + if value is None: + self._headers.pop(hdrs.LAST_MODIFIED, None) + elif isinstance(value, (int, float)): + self._headers[hdrs.LAST_MODIFIED] = time.strftime( + "%a, %d %b %Y %H:%M:%S GMT", time.gmtime(math.ceil(value)) + ) + elif isinstance(value, datetime.datetime): + self._headers[hdrs.LAST_MODIFIED] = time.strftime( + "%a, %d %b %Y %H:%M:%S GMT", value.utctimetuple() + ) + elif isinstance(value, str): + self._headers[hdrs.LAST_MODIFIED] = value + else: + msg = f"Unsupported type for last_modified: {type(value).__name__}" + raise TypeError(msg) + + @property + def etag(self) -> Optional[ETag]: + quoted_value = self._headers.get(hdrs.ETAG) + if not quoted_value: + return None + elif quoted_value == ETAG_ANY: + return ETag(value=ETAG_ANY) + match = QUOTED_ETAG_RE.fullmatch(quoted_value) + if not match: + return None + is_weak, value = match.group(1, 2) + return ETag( + is_weak=bool(is_weak), + value=value, + ) + + @etag.setter + def etag(self, value: Optional[Union[ETag, str]]) -> None: + if value is None: + self._headers.pop(hdrs.ETAG, None) + elif (isinstance(value, str) and value == ETAG_ANY) or ( + isinstance(value, ETag) and value.value == ETAG_ANY + ): + self._headers[hdrs.ETAG] = ETAG_ANY + elif isinstance(value, str): + validate_etag_value(value) + self._headers[hdrs.ETAG] = f'"{value}"' + elif isinstance(value, ETag) and isinstance(value.value, str): + validate_etag_value(value.value) + hdr_value = f'W/"{value.value}"' if value.is_weak else f'"{value.value}"' + self._headers[hdrs.ETAG] = hdr_value + else: + raise ValueError( + f"Unsupported etag type: {type(value)}. " + f"etag must be str, ETag or None" + ) + + def _generate_content_type_header( + self, CONTENT_TYPE: istr = hdrs.CONTENT_TYPE + ) -> None: + assert self._content_dict is not None + assert self._content_type is not None + params = "; ".join(f"{k}={v}" for k, v in self._content_dict.items()) + if params: + ctype = self._content_type + "; " + params + else: + ctype = self._content_type + self._headers[CONTENT_TYPE] = ctype + + async def _do_start_compression(self, coding: ContentCoding) -> None: + if coding is ContentCoding.identity: + return + assert self._payload_writer is not None + self._headers[hdrs.CONTENT_ENCODING] = coding.value + self._payload_writer.enable_compression( + coding.value, self._compression_strategy + ) + # Compressed payload may have different content length, + # remove the header + self._headers.popall(hdrs.CONTENT_LENGTH, None) + + async def _start_compression(self, request: "BaseRequest") -> None: + if self._compression_force: + await self._do_start_compression(self._compression_force) + return + # Encoding comparisons should be case-insensitive + # https://www.rfc-editor.org/rfc/rfc9110#section-8.4.1 + accept_encoding = request.headers.get(hdrs.ACCEPT_ENCODING, "").lower() + for value, coding in CONTENT_CODINGS.items(): + if value in accept_encoding: + await self._do_start_compression(coding) + return + + async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]: + if self._eof_sent: + return None + if self._payload_writer is not None: + return self._payload_writer + self._must_be_empty_body = must_be_empty_body(request.method, self.status) + return await self._start(request) + + async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: + self._req = request + writer = self._payload_writer = request._payload_writer + + await self._prepare_headers() + await request._prepare_hook(self) + await self._write_headers() + + return writer + + async def _prepare_headers(self) -> None: + request = self._req + assert request is not None + writer = self._payload_writer + assert writer is not None + keep_alive = self._keep_alive + if keep_alive is None: + keep_alive = request.keep_alive + self._keep_alive = keep_alive + + version = request.version + + headers = self._headers + if self._cookies: + for cookie in self._cookies.values(): + value = cookie.output(header="")[1:] + headers.add(hdrs.SET_COOKIE, value) + + if self._compression: + await self._start_compression(request) + + if self._chunked: + if version != HttpVersion11: + raise RuntimeError( + "Using chunked encoding is forbidden " + "for HTTP/{0.major}.{0.minor}".format(request.version) + ) + if not self._must_be_empty_body: + writer.enable_chunking() + headers[hdrs.TRANSFER_ENCODING] = "chunked" + elif self._length_check: # Disabled for WebSockets + writer.length = self.content_length + if writer.length is None: + if version >= HttpVersion11: + if not self._must_be_empty_body: + writer.enable_chunking() + headers[hdrs.TRANSFER_ENCODING] = "chunked" + elif not self._must_be_empty_body: + keep_alive = False + + # HTTP 1.1: https://tools.ietf.org/html/rfc7230#section-3.3.2 + # HTTP 1.0: https://tools.ietf.org/html/rfc1945#section-10.4 + if self._must_be_empty_body: + if hdrs.CONTENT_LENGTH in headers and should_remove_content_length( + request.method, self.status + ): + del headers[hdrs.CONTENT_LENGTH] + # https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-10 + # https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-13 + if hdrs.TRANSFER_ENCODING in headers: + del headers[hdrs.TRANSFER_ENCODING] + elif (writer.length if self._length_check else self.content_length) != 0: + # https://www.rfc-editor.org/rfc/rfc9110#section-8.3-5 + headers.setdefault(hdrs.CONTENT_TYPE, "application/octet-stream") + headers.setdefault(hdrs.DATE, rfc822_formatted_time()) + headers.setdefault(hdrs.SERVER, SERVER_SOFTWARE) + + # connection header + if hdrs.CONNECTION not in headers: + if keep_alive: + if version == HttpVersion10: + headers[hdrs.CONNECTION] = "keep-alive" + elif version == HttpVersion11: + headers[hdrs.CONNECTION] = "close" + + async def _write_headers(self) -> None: + request = self._req + assert request is not None + writer = self._payload_writer + assert writer is not None + # status line + version = request.version + status_line = f"HTTP/{version[0]}.{version[1]} {self._status} {self._reason}" + await writer.write_headers(status_line, self._headers) + # Send headers immediately if not opted into buffering + if self._send_headers_immediately: + writer.send_headers() + + async def write(self, data: Union[bytes, bytearray, memoryview]) -> None: + assert isinstance( + data, (bytes, bytearray, memoryview) + ), "data argument must be byte-ish (%r)" % type(data) + + if self._eof_sent: + raise RuntimeError("Cannot call write() after write_eof()") + if self._payload_writer is None: + raise RuntimeError("Cannot call write() before prepare()") + + await self._payload_writer.write(data) + + async def drain(self) -> None: + assert not self._eof_sent, "EOF has already been sent" + assert self._payload_writer is not None, "Response has not been started" + warnings.warn( + "drain method is deprecated, use await resp.write()", + DeprecationWarning, + stacklevel=2, + ) + await self._payload_writer.drain() + + async def write_eof(self, data: bytes = b"") -> None: + assert isinstance( + data, (bytes, bytearray, memoryview) + ), "data argument must be byte-ish (%r)" % type(data) + + if self._eof_sent: + return + + assert self._payload_writer is not None, "Response has not been started" + + await self._payload_writer.write_eof(data) + self._eof_sent = True + self._req = None + self._body_length = self._payload_writer.output_size + self._payload_writer = None + + def __repr__(self) -> str: + if self._eof_sent: + info = "eof" + elif self.prepared: + assert self._req is not None + info = f"{self._req.method} {self._req.path} " + else: + info = "not prepared" + return f"<{self.__class__.__name__} {self.reason} {info}>" + + def __getitem__(self, key: str) -> Any: + return self._state[key] + + def __setitem__(self, key: str, value: Any) -> None: + self._state[key] = value + + def __delitem__(self, key: str) -> None: + del self._state[key] + + def __len__(self) -> int: + return len(self._state) + + def __iter__(self) -> Iterator[str]: + return iter(self._state) + + def __hash__(self) -> int: + return hash(id(self)) + + def __eq__(self, other: object) -> bool: + return self is other + + def __bool__(self) -> bool: + return True + + +class Response(StreamResponse): + + _compressed_body: Optional[bytes] = None + _send_headers_immediately = False + + def __init__( + self, + *, + body: Any = None, + status: int = 200, + reason: Optional[str] = None, + text: Optional[str] = None, + headers: Optional[LooseHeaders] = None, + content_type: Optional[str] = None, + charset: Optional[str] = None, + zlib_executor_size: Optional[int] = None, + zlib_executor: Optional[Executor] = None, + ) -> None: + if body is not None and text is not None: + raise ValueError("body and text are not allowed together") + + if headers is None: + real_headers: CIMultiDict[str] = CIMultiDict() + else: + real_headers = CIMultiDict(headers) + + if content_type is not None and "charset" in content_type: + raise ValueError("charset must not be in content_type argument") + + if text is not None: + if hdrs.CONTENT_TYPE in real_headers: + if content_type or charset: + raise ValueError( + "passing both Content-Type header and " + "content_type or charset params " + "is forbidden" + ) + else: + # fast path for filling headers + if not isinstance(text, str): + raise TypeError("text argument must be str (%r)" % type(text)) + if content_type is None: + content_type = "text/plain" + if charset is None: + charset = "utf-8" + real_headers[hdrs.CONTENT_TYPE] = content_type + "; charset=" + charset + body = text.encode(charset) + text = None + elif hdrs.CONTENT_TYPE in real_headers: + if content_type is not None or charset is not None: + raise ValueError( + "passing both Content-Type header and " + "content_type or charset params " + "is forbidden" + ) + elif content_type is not None: + if charset is not None: + content_type += "; charset=" + charset + real_headers[hdrs.CONTENT_TYPE] = content_type + + super().__init__(status=status, reason=reason, _real_headers=real_headers) + + if text is not None: + self.text = text + else: + self.body = body + + self._zlib_executor_size = zlib_executor_size + self._zlib_executor = zlib_executor + + @property + def body(self) -> Optional[Union[bytes, Payload]]: + return self._body + + @body.setter + def body(self, body: Any) -> None: + if body is None: + self._body = None + elif isinstance(body, (bytes, bytearray)): + self._body = body + else: + try: + self._body = body = payload.PAYLOAD_REGISTRY.get(body) + except payload.LookupError: + raise ValueError("Unsupported body type %r" % type(body)) + + headers = self._headers + + # set content-type + if hdrs.CONTENT_TYPE not in headers: + headers[hdrs.CONTENT_TYPE] = body.content_type + + # copy payload headers + if body.headers: + for key, value in body.headers.items(): + if key not in headers: + headers[key] = value + + self._compressed_body = None + + @property + def text(self) -> Optional[str]: + if self._body is None: + return None + # Note: When _body is a Payload (e.g. FilePayload), this may do blocking I/O + # This is generally safe as most common payloads (BytesPayload, StringPayload) + # don't do blocking I/O, but be careful with file-based payloads + return self._body.decode(self.charset or "utf-8") + + @text.setter + def text(self, text: str) -> None: + assert text is None or isinstance( + text, str + ), "text argument must be str (%r)" % type(text) + + if self.content_type == "application/octet-stream": + self.content_type = "text/plain" + if self.charset is None: + self.charset = "utf-8" + + self._body = text.encode(self.charset) + self._compressed_body = None + + @property + def content_length(self) -> Optional[int]: + if self._chunked: + return None + + if hdrs.CONTENT_LENGTH in self._headers: + return int(self._headers[hdrs.CONTENT_LENGTH]) + + if self._compressed_body is not None: + # Return length of the compressed body + return len(self._compressed_body) + elif isinstance(self._body, Payload): + # A payload without content length, or a compressed payload + return None + elif self._body is not None: + return len(self._body) + else: + return 0 + + @content_length.setter + def content_length(self, value: Optional[int]) -> None: + raise RuntimeError("Content length is set automatically") + + async def write_eof(self, data: bytes = b"") -> None: + if self._eof_sent: + return + if self._compressed_body is None: + body: Optional[Union[bytes, Payload]] = self._body + else: + body = self._compressed_body + assert not data, f"data arg is not supported, got {data!r}" + assert self._req is not None + assert self._payload_writer is not None + if body is None or self._must_be_empty_body: + await super().write_eof() + elif isinstance(self._body, Payload): + await self._body.write(self._payload_writer) + await self._body.close() + await super().write_eof() + else: + await super().write_eof(cast(bytes, body)) + + async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: + if hdrs.CONTENT_LENGTH in self._headers: + if should_remove_content_length(request.method, self.status): + del self._headers[hdrs.CONTENT_LENGTH] + elif not self._chunked: + if isinstance(self._body, Payload): + if (size := self._body.size) is not None: + self._headers[hdrs.CONTENT_LENGTH] = str(size) + else: + body_len = len(self._body) if self._body else "0" + # https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6-7 + if body_len != "0" or ( + self.status != 304 and request.method not in hdrs.METH_HEAD_ALL + ): + self._headers[hdrs.CONTENT_LENGTH] = str(body_len) + + return await super()._start(request) + + async def _do_start_compression(self, coding: ContentCoding) -> None: + if self._chunked or isinstance(self._body, Payload): + return await super()._do_start_compression(coding) + if coding is ContentCoding.identity: + return + # Instead of using _payload_writer.enable_compression, + # compress the whole body + compressor = ZLibCompressor( + encoding=coding.value, + max_sync_chunk_size=self._zlib_executor_size, + executor=self._zlib_executor, + ) + assert self._body is not None + if self._zlib_executor_size is None and len(self._body) > LARGE_BODY_SIZE: + warnings.warn( + "Synchronous compression of large response bodies " + f"({len(self._body)} bytes) might block the async event loop. " + "Consider providing a custom value to zlib_executor_size/" + "zlib_executor response properties or disabling compression on it." + ) + self._compressed_body = ( + await compressor.compress(self._body) + compressor.flush() + ) + self._headers[hdrs.CONTENT_ENCODING] = coding.value + self._headers[hdrs.CONTENT_LENGTH] = str(len(self._compressed_body)) + + +def json_response( + data: Any = sentinel, + *, + text: Optional[str] = None, + body: Optional[bytes] = None, + status: int = 200, + reason: Optional[str] = None, + headers: Optional[LooseHeaders] = None, + content_type: str = "application/json", + dumps: JSONEncoder = json.dumps, +) -> Response: + if data is not sentinel: + if text or body: + raise ValueError("only one of data, text, or body should be specified") + else: + text = dumps(data) + return Response( + text=text, + body=body, + status=status, + reason=reason, + headers=headers, + content_type=content_type, + ) diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_routedef.py b/venv/lib/python3.12/site-packages/aiohttp/web_routedef.py new file mode 100644 index 00000000..f51b6cd0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_routedef.py @@ -0,0 +1,214 @@ +import abc +import os # noqa +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterator, + List, + Optional, + Sequence, + Type, + Union, + overload, +) + +import attr + +from . import hdrs +from .abc import AbstractView +from .typedefs import Handler, PathLike + +if TYPE_CHECKING: + from .web_request import Request + from .web_response import StreamResponse + from .web_urldispatcher import AbstractRoute, UrlDispatcher +else: + Request = StreamResponse = UrlDispatcher = AbstractRoute = None + + +__all__ = ( + "AbstractRouteDef", + "RouteDef", + "StaticDef", + "RouteTableDef", + "head", + "options", + "get", + "post", + "patch", + "put", + "delete", + "route", + "view", + "static", +) + + +class AbstractRouteDef(abc.ABC): + @abc.abstractmethod + def register(self, router: UrlDispatcher) -> List[AbstractRoute]: + pass # pragma: no cover + + +_HandlerType = Union[Type[AbstractView], Handler] + + +@attr.s(auto_attribs=True, frozen=True, repr=False, slots=True) +class RouteDef(AbstractRouteDef): + method: str + path: str + handler: _HandlerType + kwargs: Dict[str, Any] + + def __repr__(self) -> str: + info = [] + for name, value in sorted(self.kwargs.items()): + info.append(f", {name}={value!r}") + return " {handler.__name__!r}{info}>".format( + method=self.method, path=self.path, handler=self.handler, info="".join(info) + ) + + def register(self, router: UrlDispatcher) -> List[AbstractRoute]: + if self.method in hdrs.METH_ALL: + reg = getattr(router, "add_" + self.method.lower()) + return [reg(self.path, self.handler, **self.kwargs)] + else: + return [ + router.add_route(self.method, self.path, self.handler, **self.kwargs) + ] + + +@attr.s(auto_attribs=True, frozen=True, repr=False, slots=True) +class StaticDef(AbstractRouteDef): + prefix: str + path: PathLike + kwargs: Dict[str, Any] + + def __repr__(self) -> str: + info = [] + for name, value in sorted(self.kwargs.items()): + info.append(f", {name}={value!r}") + return " {path}{info}>".format( + prefix=self.prefix, path=self.path, info="".join(info) + ) + + def register(self, router: UrlDispatcher) -> List[AbstractRoute]: + resource = router.add_static(self.prefix, self.path, **self.kwargs) + routes = resource.get_info().get("routes", {}) + return list(routes.values()) + + +def route(method: str, path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return RouteDef(method, path, handler, kwargs) + + +def head(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_HEAD, path, handler, **kwargs) + + +def options(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_OPTIONS, path, handler, **kwargs) + + +def get( + path: str, + handler: _HandlerType, + *, + name: Optional[str] = None, + allow_head: bool = True, + **kwargs: Any, +) -> RouteDef: + return route( + hdrs.METH_GET, path, handler, name=name, allow_head=allow_head, **kwargs + ) + + +def post(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_POST, path, handler, **kwargs) + + +def put(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_PUT, path, handler, **kwargs) + + +def patch(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_PATCH, path, handler, **kwargs) + + +def delete(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_DELETE, path, handler, **kwargs) + + +def view(path: str, handler: Type[AbstractView], **kwargs: Any) -> RouteDef: + return route(hdrs.METH_ANY, path, handler, **kwargs) + + +def static(prefix: str, path: PathLike, **kwargs: Any) -> StaticDef: + return StaticDef(prefix, path, kwargs) + + +_Deco = Callable[[_HandlerType], _HandlerType] + + +class RouteTableDef(Sequence[AbstractRouteDef]): + """Route definition table""" + + def __init__(self) -> None: + self._items: List[AbstractRouteDef] = [] + + def __repr__(self) -> str: + return f"" + + @overload + def __getitem__(self, index: int) -> AbstractRouteDef: ... + + @overload + def __getitem__(self, index: slice) -> List[AbstractRouteDef]: ... + + def __getitem__(self, index): # type: ignore[no-untyped-def] + return self._items[index] + + def __iter__(self) -> Iterator[AbstractRouteDef]: + return iter(self._items) + + def __len__(self) -> int: + return len(self._items) + + def __contains__(self, item: object) -> bool: + return item in self._items + + def route(self, method: str, path: str, **kwargs: Any) -> _Deco: + def inner(handler: _HandlerType) -> _HandlerType: + self._items.append(RouteDef(method, path, handler, kwargs)) + return handler + + return inner + + def head(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_HEAD, path, **kwargs) + + def get(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_GET, path, **kwargs) + + def post(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_POST, path, **kwargs) + + def put(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_PUT, path, **kwargs) + + def patch(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_PATCH, path, **kwargs) + + def delete(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_DELETE, path, **kwargs) + + def options(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_OPTIONS, path, **kwargs) + + def view(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_ANY, path, **kwargs) + + def static(self, prefix: str, path: PathLike, **kwargs: Any) -> None: + self._items.append(StaticDef(prefix, path, kwargs)) diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_runner.py b/venv/lib/python3.12/site-packages/aiohttp/web_runner.py new file mode 100644 index 00000000..bcfec727 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_runner.py @@ -0,0 +1,399 @@ +import asyncio +import signal +import socket +import warnings +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any, List, Optional, Set + +from yarl import URL + +from .typedefs import PathLike +from .web_app import Application +from .web_server import Server + +if TYPE_CHECKING: + from ssl import SSLContext +else: + try: + from ssl import SSLContext + except ImportError: # pragma: no cover + SSLContext = object # type: ignore[misc,assignment] + +__all__ = ( + "BaseSite", + "TCPSite", + "UnixSite", + "NamedPipeSite", + "SockSite", + "BaseRunner", + "AppRunner", + "ServerRunner", + "GracefulExit", +) + + +class GracefulExit(SystemExit): + code = 1 + + +def _raise_graceful_exit() -> None: + raise GracefulExit() + + +class BaseSite(ABC): + __slots__ = ("_runner", "_ssl_context", "_backlog", "_server") + + def __init__( + self, + runner: "BaseRunner", + *, + shutdown_timeout: float = 60.0, + ssl_context: Optional[SSLContext] = None, + backlog: int = 128, + ) -> None: + if runner.server is None: + raise RuntimeError("Call runner.setup() before making a site") + if shutdown_timeout != 60.0: + msg = "shutdown_timeout should be set on BaseRunner" + warnings.warn(msg, DeprecationWarning, stacklevel=2) + runner._shutdown_timeout = shutdown_timeout + self._runner = runner + self._ssl_context = ssl_context + self._backlog = backlog + self._server: Optional[asyncio.AbstractServer] = None + + @property + @abstractmethod + def name(self) -> str: + pass # pragma: no cover + + @abstractmethod + async def start(self) -> None: + self._runner._reg_site(self) + + async def stop(self) -> None: + self._runner._check_site(self) + if self._server is not None: # Maybe not started yet + self._server.close() + + self._runner._unreg_site(self) + + +class TCPSite(BaseSite): + __slots__ = ("_host", "_port", "_reuse_address", "_reuse_port") + + def __init__( + self, + runner: "BaseRunner", + host: Optional[str] = None, + port: Optional[int] = None, + *, + shutdown_timeout: float = 60.0, + ssl_context: Optional[SSLContext] = None, + backlog: int = 128, + reuse_address: Optional[bool] = None, + reuse_port: Optional[bool] = None, + ) -> None: + super().__init__( + runner, + shutdown_timeout=shutdown_timeout, + ssl_context=ssl_context, + backlog=backlog, + ) + self._host = host + if port is None: + port = 8443 if self._ssl_context else 8080 + self._port = port + self._reuse_address = reuse_address + self._reuse_port = reuse_port + + @property + def name(self) -> str: + scheme = "https" if self._ssl_context else "http" + host = "0.0.0.0" if not self._host else self._host + return str(URL.build(scheme=scheme, host=host, port=self._port)) + + async def start(self) -> None: + await super().start() + loop = asyncio.get_event_loop() + server = self._runner.server + assert server is not None + self._server = await loop.create_server( + server, + self._host, + self._port, + ssl=self._ssl_context, + backlog=self._backlog, + reuse_address=self._reuse_address, + reuse_port=self._reuse_port, + ) + + +class UnixSite(BaseSite): + __slots__ = ("_path",) + + def __init__( + self, + runner: "BaseRunner", + path: PathLike, + *, + shutdown_timeout: float = 60.0, + ssl_context: Optional[SSLContext] = None, + backlog: int = 128, + ) -> None: + super().__init__( + runner, + shutdown_timeout=shutdown_timeout, + ssl_context=ssl_context, + backlog=backlog, + ) + self._path = path + + @property + def name(self) -> str: + scheme = "https" if self._ssl_context else "http" + return f"{scheme}://unix:{self._path}:" + + async def start(self) -> None: + await super().start() + loop = asyncio.get_event_loop() + server = self._runner.server + assert server is not None + self._server = await loop.create_unix_server( + server, + self._path, + ssl=self._ssl_context, + backlog=self._backlog, + ) + + +class NamedPipeSite(BaseSite): + __slots__ = ("_path",) + + def __init__( + self, runner: "BaseRunner", path: str, *, shutdown_timeout: float = 60.0 + ) -> None: + loop = asyncio.get_event_loop() + if not isinstance( + loop, asyncio.ProactorEventLoop # type: ignore[attr-defined] + ): + raise RuntimeError( + "Named Pipes only available in proactor loop under windows" + ) + super().__init__(runner, shutdown_timeout=shutdown_timeout) + self._path = path + + @property + def name(self) -> str: + return self._path + + async def start(self) -> None: + await super().start() + loop = asyncio.get_event_loop() + server = self._runner.server + assert server is not None + _server = await loop.start_serving_pipe( # type: ignore[attr-defined] + server, self._path + ) + self._server = _server[0] + + +class SockSite(BaseSite): + __slots__ = ("_sock", "_name") + + def __init__( + self, + runner: "BaseRunner", + sock: socket.socket, + *, + shutdown_timeout: float = 60.0, + ssl_context: Optional[SSLContext] = None, + backlog: int = 128, + ) -> None: + super().__init__( + runner, + shutdown_timeout=shutdown_timeout, + ssl_context=ssl_context, + backlog=backlog, + ) + self._sock = sock + scheme = "https" if self._ssl_context else "http" + if hasattr(socket, "AF_UNIX") and sock.family == socket.AF_UNIX: + name = f"{scheme}://unix:{sock.getsockname()}:" + else: + host, port = sock.getsockname()[:2] + name = str(URL.build(scheme=scheme, host=host, port=port)) + self._name = name + + @property + def name(self) -> str: + return self._name + + async def start(self) -> None: + await super().start() + loop = asyncio.get_event_loop() + server = self._runner.server + assert server is not None + self._server = await loop.create_server( + server, sock=self._sock, ssl=self._ssl_context, backlog=self._backlog + ) + + +class BaseRunner(ABC): + __slots__ = ("_handle_signals", "_kwargs", "_server", "_sites", "_shutdown_timeout") + + def __init__( + self, + *, + handle_signals: bool = False, + shutdown_timeout: float = 60.0, + **kwargs: Any, + ) -> None: + self._handle_signals = handle_signals + self._kwargs = kwargs + self._server: Optional[Server] = None + self._sites: List[BaseSite] = [] + self._shutdown_timeout = shutdown_timeout + + @property + def server(self) -> Optional[Server]: + return self._server + + @property + def addresses(self) -> List[Any]: + ret: List[Any] = [] + for site in self._sites: + server = site._server + if server is not None: + sockets = server.sockets # type: ignore[attr-defined] + if sockets is not None: + for sock in sockets: + ret.append(sock.getsockname()) + return ret + + @property + def sites(self) -> Set[BaseSite]: + return set(self._sites) + + async def setup(self) -> None: + loop = asyncio.get_event_loop() + + if self._handle_signals: + try: + loop.add_signal_handler(signal.SIGINT, _raise_graceful_exit) + loop.add_signal_handler(signal.SIGTERM, _raise_graceful_exit) + except NotImplementedError: # pragma: no cover + # add_signal_handler is not implemented on Windows + pass + + self._server = await self._make_server() + + @abstractmethod + async def shutdown(self) -> None: + """Call any shutdown hooks to help server close gracefully.""" + + async def cleanup(self) -> None: + # The loop over sites is intentional, an exception on gather() + # leaves self._sites in unpredictable state. + # The loop guaranties that a site is either deleted on success or + # still present on failure + for site in list(self._sites): + await site.stop() + + if self._server: # If setup succeeded + # Yield to event loop to ensure incoming requests prior to stopping the sites + # have all started to be handled before we proceed to close idle connections. + await asyncio.sleep(0) + self._server.pre_shutdown() + await self.shutdown() + await self._server.shutdown(self._shutdown_timeout) + await self._cleanup_server() + + self._server = None + if self._handle_signals: + loop = asyncio.get_running_loop() + try: + loop.remove_signal_handler(signal.SIGINT) + loop.remove_signal_handler(signal.SIGTERM) + except NotImplementedError: # pragma: no cover + # remove_signal_handler is not implemented on Windows + pass + + @abstractmethod + async def _make_server(self) -> Server: + pass # pragma: no cover + + @abstractmethod + async def _cleanup_server(self) -> None: + pass # pragma: no cover + + def _reg_site(self, site: BaseSite) -> None: + if site in self._sites: + raise RuntimeError(f"Site {site} is already registered in runner {self}") + self._sites.append(site) + + def _check_site(self, site: BaseSite) -> None: + if site not in self._sites: + raise RuntimeError(f"Site {site} is not registered in runner {self}") + + def _unreg_site(self, site: BaseSite) -> None: + if site not in self._sites: + raise RuntimeError(f"Site {site} is not registered in runner {self}") + self._sites.remove(site) + + +class ServerRunner(BaseRunner): + """Low-level web server runner""" + + __slots__ = ("_web_server",) + + def __init__( + self, web_server: Server, *, handle_signals: bool = False, **kwargs: Any + ) -> None: + super().__init__(handle_signals=handle_signals, **kwargs) + self._web_server = web_server + + async def shutdown(self) -> None: + pass + + async def _make_server(self) -> Server: + return self._web_server + + async def _cleanup_server(self) -> None: + pass + + +class AppRunner(BaseRunner): + """Web Application runner""" + + __slots__ = ("_app",) + + def __init__( + self, app: Application, *, handle_signals: bool = False, **kwargs: Any + ) -> None: + super().__init__(handle_signals=handle_signals, **kwargs) + if not isinstance(app, Application): + raise TypeError( + "The first argument should be web.Application " + "instance, got {!r}".format(app) + ) + self._app = app + + @property + def app(self) -> Application: + return self._app + + async def shutdown(self) -> None: + await self._app.shutdown() + + async def _make_server(self) -> Server: + loop = asyncio.get_event_loop() + self._app._set_loop(loop) + self._app.on_startup.freeze() + await self._app.startup() + self._app.freeze() + + return self._app._make_handler(loop=loop, **self._kwargs) + + async def _cleanup_server(self) -> None: + await self._app.cleanup() diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_server.py b/venv/lib/python3.12/site-packages/aiohttp/web_server.py new file mode 100644 index 00000000..328aca1e --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_server.py @@ -0,0 +1,84 @@ +"""Low level HTTP server.""" + +import asyncio +from typing import Any, Awaitable, Callable, Dict, List, Optional # noqa + +from .abc import AbstractStreamWriter +from .http_parser import RawRequestMessage +from .streams import StreamReader +from .web_protocol import RequestHandler, _RequestFactory, _RequestHandler +from .web_request import BaseRequest + +__all__ = ("Server",) + + +class Server: + def __init__( + self, + handler: _RequestHandler, + *, + request_factory: Optional[_RequestFactory] = None, + handler_cancellation: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Any, + ) -> None: + self._loop = loop or asyncio.get_running_loop() + self._connections: Dict[RequestHandler, asyncio.Transport] = {} + self._kwargs = kwargs + # requests_count is the number of requests being processed by the server + # for the lifetime of the server. + self.requests_count = 0 + self.request_handler = handler + self.request_factory = request_factory or self._make_request + self.handler_cancellation = handler_cancellation + + @property + def connections(self) -> List[RequestHandler]: + return list(self._connections.keys()) + + def connection_made( + self, handler: RequestHandler, transport: asyncio.Transport + ) -> None: + self._connections[handler] = transport + + def connection_lost( + self, handler: RequestHandler, exc: Optional[BaseException] = None + ) -> None: + if handler in self._connections: + if handler._task_handler: + handler._task_handler.add_done_callback( + lambda f: self._connections.pop(handler, None) + ) + else: + del self._connections[handler] + + def _make_request( + self, + message: RawRequestMessage, + payload: StreamReader, + protocol: RequestHandler, + writer: AbstractStreamWriter, + task: "asyncio.Task[None]", + ) -> BaseRequest: + return BaseRequest(message, payload, protocol, writer, task, self._loop) + + def pre_shutdown(self) -> None: + for conn in self._connections: + conn.close() + + async def shutdown(self, timeout: Optional[float] = None) -> None: + coros = (conn.shutdown(timeout) for conn in self._connections) + await asyncio.gather(*coros) + self._connections.clear() + + def __call__(self) -> RequestHandler: + try: + return RequestHandler(self, loop=self._loop, **self._kwargs) + except TypeError: + # Failsafe creation: remove all custom handler_args + kwargs = { + k: v + for k, v in self._kwargs.items() + if k in ["debug", "access_log_class"] + } + return RequestHandler(self, loop=self._loop, **kwargs) diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_urldispatcher.py b/venv/lib/python3.12/site-packages/aiohttp/web_urldispatcher.py new file mode 100644 index 00000000..48465b5e --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_urldispatcher.py @@ -0,0 +1,1309 @@ +import abc +import asyncio +import base64 +import functools +import hashlib +import html +import inspect +import keyword +import os +import platform +import re +import sys +import warnings +from functools import wraps +from pathlib import Path +from types import MappingProxyType +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Container, + Dict, + Final, + Generator, + Iterable, + Iterator, + List, + Mapping, + NoReturn, + Optional, + Pattern, + Set, + Sized, + Tuple, + Type, + TypedDict, + Union, + cast, +) + +from yarl import URL, __version__ as yarl_version + +from . import hdrs +from .abc import AbstractMatchInfo, AbstractRouter, AbstractView +from .helpers import DEBUG +from .http import HttpVersion11 +from .typedefs import Handler, PathLike +from .web_exceptions import ( + HTTPException, + HTTPExpectationFailed, + HTTPForbidden, + HTTPMethodNotAllowed, + HTTPNotFound, +) +from .web_fileresponse import FileResponse +from .web_request import Request +from .web_response import Response, StreamResponse +from .web_routedef import AbstractRouteDef + +__all__ = ( + "UrlDispatcher", + "UrlMappingMatchInfo", + "AbstractResource", + "Resource", + "PlainResource", + "DynamicResource", + "AbstractRoute", + "ResourceRoute", + "StaticResource", + "View", +) + + +if TYPE_CHECKING: + from .web_app import Application + + BaseDict = Dict[str, str] +else: + BaseDict = dict + +CIRCULAR_SYMLINK_ERROR = ( + (OSError,) + if sys.version_info < (3, 10) and sys.platform.startswith("win32") + else (RuntimeError,) if sys.version_info < (3, 13) else () +) + +YARL_VERSION: Final[Tuple[int, ...]] = tuple(map(int, yarl_version.split(".")[:2])) + +HTTP_METHOD_RE: Final[Pattern[str]] = re.compile( + r"^[0-9A-Za-z!#\$%&'\*\+\-\.\^_`\|~]+$" +) +ROUTE_RE: Final[Pattern[str]] = re.compile( + r"(\{[_a-zA-Z][^{}]*(?:\{[^{}]*\}[^{}]*)*\})" +) +PATH_SEP: Final[str] = re.escape("/") + +IS_WINDOWS: Final[bool] = platform.system() == "Windows" + +_ExpectHandler = Callable[[Request], Awaitable[Optional[StreamResponse]]] +_Resolve = Tuple[Optional["UrlMappingMatchInfo"], Set[str]] + +html_escape = functools.partial(html.escape, quote=True) + + +class _InfoDict(TypedDict, total=False): + path: str + + formatter: str + pattern: Pattern[str] + + directory: Path + prefix: str + routes: Mapping[str, "AbstractRoute"] + + app: "Application" + + domain: str + + rule: "AbstractRuleMatching" + + http_exception: HTTPException + + +class AbstractResource(Sized, Iterable["AbstractRoute"]): + def __init__(self, *, name: Optional[str] = None) -> None: + self._name = name + + @property + def name(self) -> Optional[str]: + return self._name + + @property + @abc.abstractmethod + def canonical(self) -> str: + """Exposes the resource's canonical path. + + For example '/foo/bar/{name}' + + """ + + @abc.abstractmethod # pragma: no branch + def url_for(self, **kwargs: str) -> URL: + """Construct url for resource with additional params.""" + + @abc.abstractmethod # pragma: no branch + async def resolve(self, request: Request) -> _Resolve: + """Resolve resource. + + Return (UrlMappingMatchInfo, allowed_methods) pair. + """ + + @abc.abstractmethod + def add_prefix(self, prefix: str) -> None: + """Add a prefix to processed URLs. + + Required for subapplications support. + """ + + @abc.abstractmethod + def get_info(self) -> _InfoDict: + """Return a dict with additional info useful for introspection""" + + def freeze(self) -> None: + pass + + @abc.abstractmethod + def raw_match(self, path: str) -> bool: + """Perform a raw match against path""" + + +class AbstractRoute(abc.ABC): + def __init__( + self, + method: str, + handler: Union[Handler, Type[AbstractView]], + *, + expect_handler: Optional[_ExpectHandler] = None, + resource: Optional[AbstractResource] = None, + ) -> None: + + if expect_handler is None: + expect_handler = _default_expect_handler + + assert inspect.iscoroutinefunction(expect_handler) or ( + sys.version_info < (3, 14) and asyncio.iscoroutinefunction(expect_handler) + ), f"Coroutine is expected, got {expect_handler!r}" + + method = method.upper() + if not HTTP_METHOD_RE.match(method): + raise ValueError(f"{method} is not allowed HTTP method") + + assert callable(handler), handler + if inspect.iscoroutinefunction(handler) or ( + sys.version_info < (3, 14) and asyncio.iscoroutinefunction(handler) + ): + pass + elif inspect.isgeneratorfunction(handler): + if TYPE_CHECKING: + assert False + warnings.warn( + "Bare generators are deprecated, use @coroutine wrapper", + DeprecationWarning, + ) + elif isinstance(handler, type) and issubclass(handler, AbstractView): + pass + else: + warnings.warn( + "Bare functions are deprecated, use async ones", DeprecationWarning + ) + + @wraps(handler) + async def handler_wrapper(request: Request) -> StreamResponse: + result = old_handler(request) # type: ignore[call-arg] + if asyncio.iscoroutine(result): + result = await result + assert isinstance(result, StreamResponse) + return result + + old_handler = handler + handler = handler_wrapper + + self._method = method + self._handler = handler + self._expect_handler = expect_handler + self._resource = resource + + @property + def method(self) -> str: + return self._method + + @property + def handler(self) -> Handler: + return self._handler + + @property + @abc.abstractmethod + def name(self) -> Optional[str]: + """Optional route's name, always equals to resource's name.""" + + @property + def resource(self) -> Optional[AbstractResource]: + return self._resource + + @abc.abstractmethod + def get_info(self) -> _InfoDict: + """Return a dict with additional info useful for introspection""" + + @abc.abstractmethod # pragma: no branch + def url_for(self, *args: str, **kwargs: str) -> URL: + """Construct url for route with additional params.""" + + async def handle_expect_header(self, request: Request) -> Optional[StreamResponse]: + return await self._expect_handler(request) + + +class UrlMappingMatchInfo(BaseDict, AbstractMatchInfo): + + __slots__ = ("_route", "_apps", "_current_app", "_frozen") + + def __init__(self, match_dict: Dict[str, str], route: AbstractRoute) -> None: + super().__init__(match_dict) + self._route = route + self._apps: List[Application] = [] + self._current_app: Optional[Application] = None + self._frozen = False + + @property + def handler(self) -> Handler: + return self._route.handler + + @property + def route(self) -> AbstractRoute: + return self._route + + @property + def expect_handler(self) -> _ExpectHandler: + return self._route.handle_expect_header + + @property + def http_exception(self) -> Optional[HTTPException]: + return None + + def get_info(self) -> _InfoDict: # type: ignore[override] + return self._route.get_info() + + @property + def apps(self) -> Tuple["Application", ...]: + return tuple(self._apps) + + def add_app(self, app: "Application") -> None: + if self._frozen: + raise RuntimeError("Cannot change apps stack after .freeze() call") + if self._current_app is None: + self._current_app = app + self._apps.insert(0, app) + + @property + def current_app(self) -> "Application": + app = self._current_app + assert app is not None + return app + + @current_app.setter + def current_app(self, app: "Application") -> None: + if DEBUG: # pragma: no cover + if app not in self._apps: + raise RuntimeError( + "Expected one of the following apps {!r}, got {!r}".format( + self._apps, app + ) + ) + self._current_app = app + + def freeze(self) -> None: + self._frozen = True + + def __repr__(self) -> str: + return f"" + + +class MatchInfoError(UrlMappingMatchInfo): + + __slots__ = ("_exception",) + + def __init__(self, http_exception: HTTPException) -> None: + self._exception = http_exception + super().__init__({}, SystemRoute(self._exception)) + + @property + def http_exception(self) -> HTTPException: + return self._exception + + def __repr__(self) -> str: + return "".format( + self._exception.status, self._exception.reason + ) + + +async def _default_expect_handler(request: Request) -> None: + """Default handler for Expect header. + + Just send "100 Continue" to client. + raise HTTPExpectationFailed if value of header is not "100-continue" + """ + expect = request.headers.get(hdrs.EXPECT, "") + if request.version == HttpVersion11: + if expect.lower() == "100-continue": + await request.writer.write(b"HTTP/1.1 100 Continue\r\n\r\n") + # Reset output_size as we haven't started the main body yet. + request.writer.output_size = 0 + else: + raise HTTPExpectationFailed(text="Unknown Expect: %s" % expect) + + +class Resource(AbstractResource): + def __init__(self, *, name: Optional[str] = None) -> None: + super().__init__(name=name) + self._routes: Dict[str, ResourceRoute] = {} + self._any_route: Optional[ResourceRoute] = None + self._allowed_methods: Set[str] = set() + + def add_route( + self, + method: str, + handler: Union[Type[AbstractView], Handler], + *, + expect_handler: Optional[_ExpectHandler] = None, + ) -> "ResourceRoute": + if route := self._routes.get(method, self._any_route): + raise RuntimeError( + "Added route will never be executed, " + f"method {route.method} is already " + "registered" + ) + + route_obj = ResourceRoute(method, handler, self, expect_handler=expect_handler) + self.register_route(route_obj) + return route_obj + + def register_route(self, route: "ResourceRoute") -> None: + assert isinstance( + route, ResourceRoute + ), f"Instance of Route class is required, got {route!r}" + if route.method == hdrs.METH_ANY: + self._any_route = route + self._allowed_methods.add(route.method) + self._routes[route.method] = route + + async def resolve(self, request: Request) -> _Resolve: + if (match_dict := self._match(request.rel_url.path_safe)) is None: + return None, set() + if route := self._routes.get(request.method, self._any_route): + return UrlMappingMatchInfo(match_dict, route), self._allowed_methods + return None, self._allowed_methods + + @abc.abstractmethod + def _match(self, path: str) -> Optional[Dict[str, str]]: + pass # pragma: no cover + + def __len__(self) -> int: + return len(self._routes) + + def __iter__(self) -> Iterator["ResourceRoute"]: + return iter(self._routes.values()) + + # TODO: implement all abstract methods + + +class PlainResource(Resource): + def __init__(self, path: str, *, name: Optional[str] = None) -> None: + super().__init__(name=name) + assert not path or path.startswith("/") + self._path = path + + @property + def canonical(self) -> str: + return self._path + + def freeze(self) -> None: + if not self._path: + self._path = "/" + + def add_prefix(self, prefix: str) -> None: + assert prefix.startswith("/") + assert not prefix.endswith("/") + assert len(prefix) > 1 + self._path = prefix + self._path + + def _match(self, path: str) -> Optional[Dict[str, str]]: + # string comparison is about 10 times faster than regexp matching + if self._path == path: + return {} + return None + + def raw_match(self, path: str) -> bool: + return self._path == path + + def get_info(self) -> _InfoDict: + return {"path": self._path} + + def url_for(self) -> URL: # type: ignore[override] + return URL.build(path=self._path, encoded=True) + + def __repr__(self) -> str: + name = "'" + self.name + "' " if self.name is not None else "" + return f"" + + +class DynamicResource(Resource): + + DYN = re.compile(r"\{(?P[_a-zA-Z][_a-zA-Z0-9]*)\}") + DYN_WITH_RE = re.compile(r"\{(?P[_a-zA-Z][_a-zA-Z0-9]*):(?P.+)\}") + GOOD = r"[^{}/]+" + + def __init__(self, path: str, *, name: Optional[str] = None) -> None: + super().__init__(name=name) + self._orig_path = path + pattern = "" + formatter = "" + for part in ROUTE_RE.split(path): + match = self.DYN.fullmatch(part) + if match: + pattern += "(?P<{}>{})".format(match.group("var"), self.GOOD) + formatter += "{" + match.group("var") + "}" + continue + + match = self.DYN_WITH_RE.fullmatch(part) + if match: + pattern += "(?P<{var}>{re})".format(**match.groupdict()) + formatter += "{" + match.group("var") + "}" + continue + + if "{" in part or "}" in part: + raise ValueError(f"Invalid path '{path}'['{part}']") + + part = _requote_path(part) + formatter += part + pattern += re.escape(part) + + try: + compiled = re.compile(pattern) + except re.error as exc: + raise ValueError(f"Bad pattern '{pattern}': {exc}") from None + assert compiled.pattern.startswith(PATH_SEP) + assert formatter.startswith("/") + self._pattern = compiled + self._formatter = formatter + + @property + def canonical(self) -> str: + return self._formatter + + def add_prefix(self, prefix: str) -> None: + assert prefix.startswith("/") + assert not prefix.endswith("/") + assert len(prefix) > 1 + self._pattern = re.compile(re.escape(prefix) + self._pattern.pattern) + self._formatter = prefix + self._formatter + + def _match(self, path: str) -> Optional[Dict[str, str]]: + match = self._pattern.fullmatch(path) + if match is None: + return None + return { + key: _unquote_path_safe(value) for key, value in match.groupdict().items() + } + + def raw_match(self, path: str) -> bool: + return self._orig_path == path + + def get_info(self) -> _InfoDict: + return {"formatter": self._formatter, "pattern": self._pattern} + + def url_for(self, **parts: str) -> URL: + url = self._formatter.format_map({k: _quote_path(v) for k, v in parts.items()}) + return URL.build(path=url, encoded=True) + + def __repr__(self) -> str: + name = "'" + self.name + "' " if self.name is not None else "" + return "".format( + name=name, formatter=self._formatter + ) + + +class PrefixResource(AbstractResource): + def __init__(self, prefix: str, *, name: Optional[str] = None) -> None: + assert not prefix or prefix.startswith("/"), prefix + assert prefix in ("", "/") or not prefix.endswith("/"), prefix + super().__init__(name=name) + self._prefix = _requote_path(prefix) + self._prefix2 = self._prefix + "/" + + @property + def canonical(self) -> str: + return self._prefix + + def add_prefix(self, prefix: str) -> None: + assert prefix.startswith("/") + assert not prefix.endswith("/") + assert len(prefix) > 1 + self._prefix = prefix + self._prefix + self._prefix2 = self._prefix + "/" + + def raw_match(self, prefix: str) -> bool: + return False + + # TODO: impl missing abstract methods + + +class StaticResource(PrefixResource): + VERSION_KEY = "v" + + def __init__( + self, + prefix: str, + directory: PathLike, + *, + name: Optional[str] = None, + expect_handler: Optional[_ExpectHandler] = None, + chunk_size: int = 256 * 1024, + show_index: bool = False, + follow_symlinks: bool = False, + append_version: bool = False, + ) -> None: + super().__init__(prefix, name=name) + try: + directory = Path(directory).expanduser().resolve(strict=True) + except FileNotFoundError as error: + raise ValueError(f"'{directory}' does not exist") from error + if not directory.is_dir(): + raise ValueError(f"'{directory}' is not a directory") + self._directory = directory + self._show_index = show_index + self._chunk_size = chunk_size + self._follow_symlinks = follow_symlinks + self._expect_handler = expect_handler + self._append_version = append_version + + self._routes = { + "GET": ResourceRoute( + "GET", self._handle, self, expect_handler=expect_handler + ), + "HEAD": ResourceRoute( + "HEAD", self._handle, self, expect_handler=expect_handler + ), + } + self._allowed_methods = set(self._routes) + + def url_for( # type: ignore[override] + self, + *, + filename: PathLike, + append_version: Optional[bool] = None, + ) -> URL: + if append_version is None: + append_version = self._append_version + filename = str(filename).lstrip("/") + + url = URL.build(path=self._prefix, encoded=True) + # filename is not encoded + if YARL_VERSION < (1, 6): + url = url / filename.replace("%", "%25") + else: + url = url / filename + + if append_version: + unresolved_path = self._directory.joinpath(filename) + try: + if self._follow_symlinks: + normalized_path = Path(os.path.normpath(unresolved_path)) + normalized_path.relative_to(self._directory) + filepath = normalized_path.resolve() + else: + filepath = unresolved_path.resolve() + filepath.relative_to(self._directory) + except (ValueError, FileNotFoundError): + # ValueError for case when path point to symlink + # with follow_symlinks is False + return url # relatively safe + if filepath.is_file(): + # TODO cache file content + # with file watcher for cache invalidation + with filepath.open("rb") as f: + file_bytes = f.read() + h = self._get_file_hash(file_bytes) + url = url.with_query({self.VERSION_KEY: h}) + return url + return url + + @staticmethod + def _get_file_hash(byte_array: bytes) -> str: + m = hashlib.sha256() # todo sha256 can be configurable param + m.update(byte_array) + b64 = base64.urlsafe_b64encode(m.digest()) + return b64.decode("ascii") + + def get_info(self) -> _InfoDict: + return { + "directory": self._directory, + "prefix": self._prefix, + "routes": self._routes, + } + + def set_options_route(self, handler: Handler) -> None: + if "OPTIONS" in self._routes: + raise RuntimeError("OPTIONS route was set already") + self._routes["OPTIONS"] = ResourceRoute( + "OPTIONS", handler, self, expect_handler=self._expect_handler + ) + self._allowed_methods.add("OPTIONS") + + async def resolve(self, request: Request) -> _Resolve: + path = request.rel_url.path_safe + method = request.method + # We normalise here to avoid matches that traverse below the static root. + # e.g. /static/../../../../home/user/webapp/static/ + norm_path = os.path.normpath(path) + if IS_WINDOWS: + norm_path = norm_path.replace("\\", "/") + if not norm_path.startswith(self._prefix2) and norm_path != self._prefix: + return None, set() + + allowed_methods = self._allowed_methods + if method not in allowed_methods: + return None, allowed_methods + + match_dict = {"filename": _unquote_path_safe(path[len(self._prefix) + 1 :])} + return (UrlMappingMatchInfo(match_dict, self._routes[method]), allowed_methods) + + def __len__(self) -> int: + return len(self._routes) + + def __iter__(self) -> Iterator[AbstractRoute]: + return iter(self._routes.values()) + + async def _handle(self, request: Request) -> StreamResponse: + filename = request.match_info["filename"] + if Path(filename).is_absolute(): + # filename is an absolute path e.g. //network/share or D:\path + # which could be a UNC path leading to NTLM credential theft + raise HTTPNotFound() + unresolved_path = self._directory.joinpath(filename) + loop = asyncio.get_running_loop() + return await loop.run_in_executor( + None, self._resolve_path_to_response, unresolved_path + ) + + def _resolve_path_to_response(self, unresolved_path: Path) -> StreamResponse: + """Take the unresolved path and query the file system to form a response.""" + # Check for access outside the root directory. For follow symlinks, URI + # cannot traverse out, but symlinks can. Otherwise, no access outside + # root is permitted. + try: + if self._follow_symlinks: + normalized_path = Path(os.path.normpath(unresolved_path)) + normalized_path.relative_to(self._directory) + file_path = normalized_path.resolve() + else: + file_path = unresolved_path.resolve() + file_path.relative_to(self._directory) + except (ValueError, *CIRCULAR_SYMLINK_ERROR) as error: + # ValueError is raised for the relative check. Circular symlinks + # raise here on resolving for python < 3.13. + raise HTTPNotFound() from error + + # if path is a directory, return the contents if permitted. Note the + # directory check will raise if a segment is not readable. + try: + if file_path.is_dir(): + if self._show_index: + return Response( + text=self._directory_as_html(file_path), + content_type="text/html", + ) + else: + raise HTTPForbidden() + except PermissionError as error: + raise HTTPForbidden() from error + + # Return the file response, which handles all other checks. + return FileResponse(file_path, chunk_size=self._chunk_size) + + def _directory_as_html(self, dir_path: Path) -> str: + """returns directory's index as html.""" + assert dir_path.is_dir() + + relative_path_to_dir = dir_path.relative_to(self._directory).as_posix() + index_of = f"Index of /{html_escape(relative_path_to_dir)}" + h1 = f"

{index_of}

" + + index_list = [] + dir_index = dir_path.iterdir() + for _file in sorted(dir_index): + # show file url as relative to static path + rel_path = _file.relative_to(self._directory).as_posix() + quoted_file_url = _quote_path(f"{self._prefix}/{rel_path}") + + # if file is a directory, add '/' to the end of the name + if _file.is_dir(): + file_name = f"{_file.name}/" + else: + file_name = _file.name + + index_list.append( + f'
  • {html_escape(file_name)}
  • ' + ) + ul = "
      \n{}\n
    ".format("\n".join(index_list)) + body = f"\n{h1}\n{ul}\n" + + head_str = f"\n{index_of}\n" + html = f"\n{head_str}\n{body}\n" + + return html + + def __repr__(self) -> str: + name = "'" + self.name + "'" if self.name is not None else "" + return " {directory!r}>".format( + name=name, path=self._prefix, directory=self._directory + ) + + +class PrefixedSubAppResource(PrefixResource): + def __init__(self, prefix: str, app: "Application") -> None: + super().__init__(prefix) + self._app = app + self._add_prefix_to_resources(prefix) + + def add_prefix(self, prefix: str) -> None: + super().add_prefix(prefix) + self._add_prefix_to_resources(prefix) + + def _add_prefix_to_resources(self, prefix: str) -> None: + router = self._app.router + for resource in router.resources(): + # Since the canonical path of a resource is about + # to change, we need to unindex it and then reindex + router.unindex_resource(resource) + resource.add_prefix(prefix) + router.index_resource(resource) + + def url_for(self, *args: str, **kwargs: str) -> URL: + raise RuntimeError(".url_for() is not supported by sub-application root") + + def get_info(self) -> _InfoDict: + return {"app": self._app, "prefix": self._prefix} + + async def resolve(self, request: Request) -> _Resolve: + match_info = await self._app.router.resolve(request) + match_info.add_app(self._app) + if isinstance(match_info.http_exception, HTTPMethodNotAllowed): + methods = match_info.http_exception.allowed_methods + else: + methods = set() + return match_info, methods + + def __len__(self) -> int: + return len(self._app.router.routes()) + + def __iter__(self) -> Iterator[AbstractRoute]: + return iter(self._app.router.routes()) + + def __repr__(self) -> str: + return " {app!r}>".format( + prefix=self._prefix, app=self._app + ) + + +class AbstractRuleMatching(abc.ABC): + @abc.abstractmethod # pragma: no branch + async def match(self, request: Request) -> bool: + """Return bool if the request satisfies the criteria""" + + @abc.abstractmethod # pragma: no branch + def get_info(self) -> _InfoDict: + """Return a dict with additional info useful for introspection""" + + @property + @abc.abstractmethod # pragma: no branch + def canonical(self) -> str: + """Return a str""" + + +class Domain(AbstractRuleMatching): + re_part = re.compile(r"(?!-)[a-z\d-]{1,63}(? None: + super().__init__() + self._domain = self.validation(domain) + + @property + def canonical(self) -> str: + return self._domain + + def validation(self, domain: str) -> str: + if not isinstance(domain, str): + raise TypeError("Domain must be str") + domain = domain.rstrip(".").lower() + if not domain: + raise ValueError("Domain cannot be empty") + elif "://" in domain: + raise ValueError("Scheme not supported") + url = URL("http://" + domain) + assert url.raw_host is not None + if not all(self.re_part.fullmatch(x) for x in url.raw_host.split(".")): + raise ValueError("Domain not valid") + if url.port == 80: + return url.raw_host + return f"{url.raw_host}:{url.port}" + + async def match(self, request: Request) -> bool: + host = request.headers.get(hdrs.HOST) + if not host: + return False + return self.match_domain(host) + + def match_domain(self, host: str) -> bool: + return host.lower() == self._domain + + def get_info(self) -> _InfoDict: + return {"domain": self._domain} + + +class MaskDomain(Domain): + re_part = re.compile(r"(?!-)[a-z\d\*-]{1,63}(? None: + super().__init__(domain) + mask = self._domain.replace(".", r"\.").replace("*", ".*") + self._mask = re.compile(mask) + + @property + def canonical(self) -> str: + return self._mask.pattern + + def match_domain(self, host: str) -> bool: + return self._mask.fullmatch(host) is not None + + +class MatchedSubAppResource(PrefixedSubAppResource): + def __init__(self, rule: AbstractRuleMatching, app: "Application") -> None: + AbstractResource.__init__(self) + self._prefix = "" + self._app = app + self._rule = rule + + @property + def canonical(self) -> str: + return self._rule.canonical + + def get_info(self) -> _InfoDict: + return {"app": self._app, "rule": self._rule} + + async def resolve(self, request: Request) -> _Resolve: + if not await self._rule.match(request): + return None, set() + match_info = await self._app.router.resolve(request) + match_info.add_app(self._app) + if isinstance(match_info.http_exception, HTTPMethodNotAllowed): + methods = match_info.http_exception.allowed_methods + else: + methods = set() + return match_info, methods + + def __repr__(self) -> str: + return f" {self._app!r}>" + + +class ResourceRoute(AbstractRoute): + """A route with resource""" + + def __init__( + self, + method: str, + handler: Union[Handler, Type[AbstractView]], + resource: AbstractResource, + *, + expect_handler: Optional[_ExpectHandler] = None, + ) -> None: + super().__init__( + method, handler, expect_handler=expect_handler, resource=resource + ) + + def __repr__(self) -> str: + return " {handler!r}".format( + method=self.method, resource=self._resource, handler=self.handler + ) + + @property + def name(self) -> Optional[str]: + if self._resource is None: + return None + return self._resource.name + + def url_for(self, *args: str, **kwargs: str) -> URL: + """Construct url for route with additional params.""" + assert self._resource is not None + return self._resource.url_for(*args, **kwargs) + + def get_info(self) -> _InfoDict: + assert self._resource is not None + return self._resource.get_info() + + +class SystemRoute(AbstractRoute): + def __init__(self, http_exception: HTTPException) -> None: + super().__init__(hdrs.METH_ANY, self._handle) + self._http_exception = http_exception + + def url_for(self, *args: str, **kwargs: str) -> URL: + raise RuntimeError(".url_for() is not allowed for SystemRoute") + + @property + def name(self) -> Optional[str]: + return None + + def get_info(self) -> _InfoDict: + return {"http_exception": self._http_exception} + + async def _handle(self, request: Request) -> StreamResponse: + raise self._http_exception + + @property + def status(self) -> int: + return self._http_exception.status + + @property + def reason(self) -> str: + return self._http_exception.reason + + def __repr__(self) -> str: + return "".format(self=self) + + +class View(AbstractView): + async def _iter(self) -> StreamResponse: + if self.request.method not in hdrs.METH_ALL: + self._raise_allowed_methods() + method: Optional[Callable[[], Awaitable[StreamResponse]]] + method = getattr(self, self.request.method.lower(), None) + if method is None: + self._raise_allowed_methods() + ret = await method() + assert isinstance(ret, StreamResponse) + return ret + + def __await__(self) -> Generator[None, None, StreamResponse]: + return self._iter().__await__() + + def _raise_allowed_methods(self) -> NoReturn: + allowed_methods = {m for m in hdrs.METH_ALL if hasattr(self, m.lower())} + raise HTTPMethodNotAllowed(self.request.method, allowed_methods) + + +class ResourcesView(Sized, Iterable[AbstractResource], Container[AbstractResource]): + def __init__(self, resources: List[AbstractResource]) -> None: + self._resources = resources + + def __len__(self) -> int: + return len(self._resources) + + def __iter__(self) -> Iterator[AbstractResource]: + yield from self._resources + + def __contains__(self, resource: object) -> bool: + return resource in self._resources + + +class RoutesView(Sized, Iterable[AbstractRoute], Container[AbstractRoute]): + def __init__(self, resources: List[AbstractResource]): + self._routes: List[AbstractRoute] = [] + for resource in resources: + for route in resource: + self._routes.append(route) + + def __len__(self) -> int: + return len(self._routes) + + def __iter__(self) -> Iterator[AbstractRoute]: + yield from self._routes + + def __contains__(self, route: object) -> bool: + return route in self._routes + + +class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]): + + NAME_SPLIT_RE = re.compile(r"[.:-]") + + def __init__(self) -> None: + super().__init__() + self._resources: List[AbstractResource] = [] + self._named_resources: Dict[str, AbstractResource] = {} + self._resource_index: dict[str, list[AbstractResource]] = {} + self._matched_sub_app_resources: List[MatchedSubAppResource] = [] + + async def resolve(self, request: Request) -> UrlMappingMatchInfo: + resource_index = self._resource_index + allowed_methods: Set[str] = set() + + # MatchedSubAppResource is primarily used to match on domain names + # (though custom rules could match on other things). This means that + # the traversal algorithm below can't be applied, and that we likely + # need to check these first so a sub app that defines the same path + # as a parent app will get priority if there's a domain match. + # + # For most cases we do not expect there to be many of these since + # currently they are only added by `.add_domain()`. + for resource in self._matched_sub_app_resources: + match_dict, allowed = await resource.resolve(request) + if match_dict is not None: + return match_dict + else: + allowed_methods |= allowed + + # Walk the url parts looking for candidates. We walk the url backwards + # to ensure the most explicit match is found first. If there are multiple + # candidates for a given url part because there are multiple resources + # registered for the same canonical path, we resolve them in a linear + # fashion to ensure registration order is respected. + url_part = request.rel_url.path_safe + while url_part: + for candidate in resource_index.get(url_part, ()): + match_dict, allowed = await candidate.resolve(request) + if match_dict is not None: + return match_dict + else: + allowed_methods |= allowed + if url_part == "/": + break + url_part = url_part.rpartition("/")[0] or "/" + + if allowed_methods: + return MatchInfoError(HTTPMethodNotAllowed(request.method, allowed_methods)) + + return MatchInfoError(HTTPNotFound()) + + def __iter__(self) -> Iterator[str]: + return iter(self._named_resources) + + def __len__(self) -> int: + return len(self._named_resources) + + def __contains__(self, resource: object) -> bool: + return resource in self._named_resources + + def __getitem__(self, name: str) -> AbstractResource: + return self._named_resources[name] + + def resources(self) -> ResourcesView: + return ResourcesView(self._resources) + + def routes(self) -> RoutesView: + return RoutesView(self._resources) + + def named_resources(self) -> Mapping[str, AbstractResource]: + return MappingProxyType(self._named_resources) + + def register_resource(self, resource: AbstractResource) -> None: + assert isinstance( + resource, AbstractResource + ), f"Instance of AbstractResource class is required, got {resource!r}" + if self.frozen: + raise RuntimeError("Cannot register a resource into frozen router.") + + name = resource.name + + if name is not None: + parts = self.NAME_SPLIT_RE.split(name) + for part in parts: + if keyword.iskeyword(part): + raise ValueError( + f"Incorrect route name {name!r}, " + "python keywords cannot be used " + "for route name" + ) + if not part.isidentifier(): + raise ValueError( + "Incorrect route name {!r}, " + "the name should be a sequence of " + "python identifiers separated " + "by dash, dot or column".format(name) + ) + if name in self._named_resources: + raise ValueError( + "Duplicate {!r}, " + "already handled by {!r}".format(name, self._named_resources[name]) + ) + self._named_resources[name] = resource + self._resources.append(resource) + + if isinstance(resource, MatchedSubAppResource): + # We cannot index match sub-app resources because they have match rules + self._matched_sub_app_resources.append(resource) + else: + self.index_resource(resource) + + def _get_resource_index_key(self, resource: AbstractResource) -> str: + """Return a key to index the resource in the resource index.""" + if "{" in (index_key := resource.canonical): + # strip at the first { to allow for variables, and than + # rpartition at / to allow for variable parts in the path + # For example if the canonical path is `/core/locations{tail:.*}` + # the index key will be `/core` since index is based on the + # url parts split by `/` + index_key = index_key.partition("{")[0].rpartition("/")[0] + return index_key.rstrip("/") or "/" + + def index_resource(self, resource: AbstractResource) -> None: + """Add a resource to the resource index.""" + resource_key = self._get_resource_index_key(resource) + # There may be multiple resources for a canonical path + # so we keep them in a list to ensure that registration + # order is respected. + self._resource_index.setdefault(resource_key, []).append(resource) + + def unindex_resource(self, resource: AbstractResource) -> None: + """Remove a resource from the resource index.""" + resource_key = self._get_resource_index_key(resource) + self._resource_index[resource_key].remove(resource) + + def add_resource(self, path: str, *, name: Optional[str] = None) -> Resource: + if path and not path.startswith("/"): + raise ValueError("path should be started with / or be empty") + # Reuse last added resource if path and name are the same + if self._resources: + resource = self._resources[-1] + if resource.name == name and resource.raw_match(path): + return cast(Resource, resource) + if not ("{" in path or "}" in path or ROUTE_RE.search(path)): + resource = PlainResource(path, name=name) + self.register_resource(resource) + return resource + resource = DynamicResource(path, name=name) + self.register_resource(resource) + return resource + + def add_route( + self, + method: str, + path: str, + handler: Union[Handler, Type[AbstractView]], + *, + name: Optional[str] = None, + expect_handler: Optional[_ExpectHandler] = None, + ) -> AbstractRoute: + resource = self.add_resource(path, name=name) + return resource.add_route(method, handler, expect_handler=expect_handler) + + def add_static( + self, + prefix: str, + path: PathLike, + *, + name: Optional[str] = None, + expect_handler: Optional[_ExpectHandler] = None, + chunk_size: int = 256 * 1024, + show_index: bool = False, + follow_symlinks: bool = False, + append_version: bool = False, + ) -> AbstractResource: + """Add static files view. + + prefix - url prefix + path - folder with files + + """ + assert prefix.startswith("/") + if prefix.endswith("/"): + prefix = prefix[:-1] + resource = StaticResource( + prefix, + path, + name=name, + expect_handler=expect_handler, + chunk_size=chunk_size, + show_index=show_index, + follow_symlinks=follow_symlinks, + append_version=append_version, + ) + self.register_resource(resource) + return resource + + def add_head(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method HEAD.""" + return self.add_route(hdrs.METH_HEAD, path, handler, **kwargs) + + def add_options(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method OPTIONS.""" + return self.add_route(hdrs.METH_OPTIONS, path, handler, **kwargs) + + def add_get( + self, + path: str, + handler: Handler, + *, + name: Optional[str] = None, + allow_head: bool = True, + **kwargs: Any, + ) -> AbstractRoute: + """Shortcut for add_route with method GET. + + If allow_head is true, another + route is added allowing head requests to the same endpoint. + """ + resource = self.add_resource(path, name=name) + if allow_head: + resource.add_route(hdrs.METH_HEAD, handler, **kwargs) + return resource.add_route(hdrs.METH_GET, handler, **kwargs) + + def add_post(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method POST.""" + return self.add_route(hdrs.METH_POST, path, handler, **kwargs) + + def add_put(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method PUT.""" + return self.add_route(hdrs.METH_PUT, path, handler, **kwargs) + + def add_patch(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method PATCH.""" + return self.add_route(hdrs.METH_PATCH, path, handler, **kwargs) + + def add_delete(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method DELETE.""" + return self.add_route(hdrs.METH_DELETE, path, handler, **kwargs) + + def add_view( + self, path: str, handler: Type[AbstractView], **kwargs: Any + ) -> AbstractRoute: + """Shortcut for add_route with ANY methods for a class-based view.""" + return self.add_route(hdrs.METH_ANY, path, handler, **kwargs) + + def freeze(self) -> None: + super().freeze() + for resource in self._resources: + resource.freeze() + + def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]: + """Append routes to route table. + + Parameter should be a sequence of RouteDef objects. + + Returns a list of registered AbstractRoute instances. + """ + registered_routes = [] + for route_def in routes: + registered_routes.extend(route_def.register(self)) + return registered_routes + + +def _quote_path(value: str) -> str: + if YARL_VERSION < (1, 6): + value = value.replace("%", "%25") + return URL.build(path=value, encoded=False).raw_path + + +def _unquote_path_safe(value: str) -> str: + if "%" not in value: + return value + return value.replace("%2F", "/").replace("%25", "%") + + +def _requote_path(value: str) -> str: + # Quote non-ascii characters and other characters which must be quoted, + # but preserve existing %-sequences. + result = _quote_path(value) + if "%" in value: + result = result.replace("%25", "%") + return result diff --git a/venv/lib/python3.12/site-packages/aiohttp/web_ws.py b/venv/lib/python3.12/site-packages/aiohttp/web_ws.py new file mode 100644 index 00000000..575f9a3d --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/web_ws.py @@ -0,0 +1,631 @@ +import asyncio +import base64 +import binascii +import hashlib +import json +import sys +from typing import Any, Final, Iterable, Optional, Tuple, Union, cast + +import attr +from multidict import CIMultiDict + +from . import hdrs +from ._websocket.reader import WebSocketDataQueue +from ._websocket.writer import DEFAULT_LIMIT +from .abc import AbstractStreamWriter +from .client_exceptions import WSMessageTypeError +from .helpers import calculate_timeout_when, set_exception, set_result +from .http import ( + WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE, + WS_KEY, + WebSocketError, + WebSocketReader, + WebSocketWriter, + WSCloseCode, + WSMessage, + WSMsgType as WSMsgType, + ws_ext_gen, + ws_ext_parse, +) +from .http_websocket import _INTERNAL_RECEIVE_TYPES +from .log import ws_logger +from .streams import EofStream +from .typedefs import JSONDecoder, JSONEncoder +from .web_exceptions import HTTPBadRequest, HTTPException +from .web_request import BaseRequest +from .web_response import StreamResponse + +if sys.version_info >= (3, 11): + import asyncio as async_timeout +else: + import async_timeout + +__all__ = ( + "WebSocketResponse", + "WebSocketReady", + "WSMsgType", +) + +THRESHOLD_CONNLOST_ACCESS: Final[int] = 5 + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class WebSocketReady: + ok: bool + protocol: Optional[str] + + def __bool__(self) -> bool: + return self.ok + + +class WebSocketResponse(StreamResponse): + + _length_check: bool = False + _ws_protocol: Optional[str] = None + _writer: Optional[WebSocketWriter] = None + _reader: Optional[WebSocketDataQueue] = None + _closed: bool = False + _closing: bool = False + _conn_lost: int = 0 + _close_code: Optional[int] = None + _loop: Optional[asyncio.AbstractEventLoop] = None + _waiting: bool = False + _close_wait: Optional[asyncio.Future[None]] = None + _exception: Optional[BaseException] = None + _heartbeat_when: float = 0.0 + _heartbeat_cb: Optional[asyncio.TimerHandle] = None + _pong_response_cb: Optional[asyncio.TimerHandle] = None + _ping_task: Optional[asyncio.Task[None]] = None + + def __init__( + self, + *, + timeout: float = 10.0, + receive_timeout: Optional[float] = None, + autoclose: bool = True, + autoping: bool = True, + heartbeat: Optional[float] = None, + protocols: Iterable[str] = (), + compress: bool = True, + max_msg_size: int = 4 * 1024 * 1024, + writer_limit: int = DEFAULT_LIMIT, + ) -> None: + super().__init__(status=101) + self._protocols = protocols + self._timeout = timeout + self._receive_timeout = receive_timeout + self._autoclose = autoclose + self._autoping = autoping + self._heartbeat = heartbeat + if heartbeat is not None: + self._pong_heartbeat = heartbeat / 2.0 + self._compress: Union[bool, int] = compress + self._max_msg_size = max_msg_size + self._writer_limit = writer_limit + + def _cancel_heartbeat(self) -> None: + self._cancel_pong_response_cb() + if self._heartbeat_cb is not None: + self._heartbeat_cb.cancel() + self._heartbeat_cb = None + if self._ping_task is not None: + self._ping_task.cancel() + self._ping_task = None + + def _cancel_pong_response_cb(self) -> None: + if self._pong_response_cb is not None: + self._pong_response_cb.cancel() + self._pong_response_cb = None + + def _reset_heartbeat(self) -> None: + if self._heartbeat is None: + return + self._cancel_pong_response_cb() + req = self._req + timeout_ceil_threshold = ( + req._protocol._timeout_ceil_threshold if req is not None else 5 + ) + loop = self._loop + assert loop is not None + now = loop.time() + when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold) + self._heartbeat_when = when + if self._heartbeat_cb is None: + # We do not cancel the previous heartbeat_cb here because + # it generates a significant amount of TimerHandle churn + # which causes asyncio to rebuild the heap frequently. + # Instead _send_heartbeat() will reschedule the next + # heartbeat if it fires too early. + self._heartbeat_cb = loop.call_at(when, self._send_heartbeat) + + def _send_heartbeat(self) -> None: + self._heartbeat_cb = None + loop = self._loop + assert loop is not None and self._writer is not None + now = loop.time() + if now < self._heartbeat_when: + # Heartbeat fired too early, reschedule + self._heartbeat_cb = loop.call_at( + self._heartbeat_when, self._send_heartbeat + ) + return + + req = self._req + timeout_ceil_threshold = ( + req._protocol._timeout_ceil_threshold if req is not None else 5 + ) + when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold) + self._cancel_pong_response_cb() + self._pong_response_cb = loop.call_at(when, self._pong_not_received) + + coro = self._writer.send_frame(b"", WSMsgType.PING) + if sys.version_info >= (3, 12): + # Optimization for Python 3.12, try to send the ping + # immediately to avoid having to schedule + # the task on the event loop. + ping_task = asyncio.Task(coro, loop=loop, eager_start=True) + else: + ping_task = loop.create_task(coro) + + if not ping_task.done(): + self._ping_task = ping_task + ping_task.add_done_callback(self._ping_task_done) + else: + self._ping_task_done(ping_task) + + def _ping_task_done(self, task: "asyncio.Task[None]") -> None: + """Callback for when the ping task completes.""" + if not task.cancelled() and (exc := task.exception()): + self._handle_ping_pong_exception(exc) + self._ping_task = None + + def _pong_not_received(self) -> None: + if self._req is not None and self._req.transport is not None: + self._handle_ping_pong_exception( + asyncio.TimeoutError( + f"No PONG received after {self._pong_heartbeat} seconds" + ) + ) + + def _handle_ping_pong_exception(self, exc: BaseException) -> None: + """Handle exceptions raised during ping/pong processing.""" + if self._closed: + return + self._set_closed() + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + self._exception = exc + if self._waiting and not self._closing and self._reader is not None: + self._reader.feed_data(WSMessage(WSMsgType.ERROR, exc, None), 0) + + def _set_closed(self) -> None: + """Set the connection to closed. + + Cancel any heartbeat timers and set the closed flag. + """ + self._closed = True + self._cancel_heartbeat() + + async def prepare(self, request: BaseRequest) -> AbstractStreamWriter: + # make pre-check to don't hide it by do_handshake() exceptions + if self._payload_writer is not None: + return self._payload_writer + + protocol, writer = self._pre_start(request) + payload_writer = await super().prepare(request) + assert payload_writer is not None + self._post_start(request, protocol, writer) + await payload_writer.drain() + return payload_writer + + def _handshake( + self, request: BaseRequest + ) -> Tuple["CIMultiDict[str]", Optional[str], int, bool]: + headers = request.headers + if "websocket" != headers.get(hdrs.UPGRADE, "").lower().strip(): + raise HTTPBadRequest( + text=( + "No WebSocket UPGRADE hdr: {}\n Can " + '"Upgrade" only to "WebSocket".' + ).format(headers.get(hdrs.UPGRADE)) + ) + + if "upgrade" not in headers.get(hdrs.CONNECTION, "").lower(): + raise HTTPBadRequest( + text="No CONNECTION upgrade hdr: {}".format( + headers.get(hdrs.CONNECTION) + ) + ) + + # find common sub-protocol between client and server + protocol: Optional[str] = None + if hdrs.SEC_WEBSOCKET_PROTOCOL in headers: + req_protocols = [ + str(proto.strip()) + for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",") + ] + + for proto in req_protocols: + if proto in self._protocols: + protocol = proto + break + else: + # No overlap found: Return no protocol as per spec + ws_logger.warning( + "%s: Client protocols %r don’t overlap server-known ones %r", + request.remote, + req_protocols, + self._protocols, + ) + + # check supported version + version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, "") + if version not in ("13", "8", "7"): + raise HTTPBadRequest(text=f"Unsupported version: {version}") + + # check client handshake for validity + key = headers.get(hdrs.SEC_WEBSOCKET_KEY) + try: + if not key or len(base64.b64decode(key)) != 16: + raise HTTPBadRequest(text=f"Handshake error: {key!r}") + except binascii.Error: + raise HTTPBadRequest(text=f"Handshake error: {key!r}") from None + + accept_val = base64.b64encode( + hashlib.sha1(key.encode() + WS_KEY).digest() + ).decode() + response_headers = CIMultiDict( + { + hdrs.UPGRADE: "websocket", + hdrs.CONNECTION: "upgrade", + hdrs.SEC_WEBSOCKET_ACCEPT: accept_val, + } + ) + + notakeover = False + compress = 0 + if self._compress: + extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS) + # Server side always get return with no exception. + # If something happened, just drop compress extension + compress, notakeover = ws_ext_parse(extensions, isserver=True) + if compress: + enabledext = ws_ext_gen( + compress=compress, isserver=True, server_notakeover=notakeover + ) + response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext + + if protocol: + response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol + return ( + response_headers, + protocol, + compress, + notakeover, + ) + + def _pre_start(self, request: BaseRequest) -> Tuple[Optional[str], WebSocketWriter]: + self._loop = request._loop + + headers, protocol, compress, notakeover = self._handshake(request) + + self.set_status(101) + self.headers.update(headers) + self.force_close() + self._compress = compress + transport = request._protocol.transport + assert transport is not None + writer = WebSocketWriter( + request._protocol, + transport, + compress=compress, + notakeover=notakeover, + limit=self._writer_limit, + ) + + return protocol, writer + + def _post_start( + self, request: BaseRequest, protocol: Optional[str], writer: WebSocketWriter + ) -> None: + self._ws_protocol = protocol + self._writer = writer + + self._reset_heartbeat() + + loop = self._loop + assert loop is not None + self._reader = WebSocketDataQueue(request._protocol, 2**16, loop=loop) + request.protocol.set_parser( + WebSocketReader( + self._reader, self._max_msg_size, compress=bool(self._compress) + ) + ) + # disable HTTP keepalive for WebSocket + request.protocol.keep_alive(False) + + def can_prepare(self, request: BaseRequest) -> WebSocketReady: + if self._writer is not None: + raise RuntimeError("Already started") + try: + _, protocol, _, _ = self._handshake(request) + except HTTPException: + return WebSocketReady(False, None) + else: + return WebSocketReady(True, protocol) + + @property + def prepared(self) -> bool: + return self._writer is not None + + @property + def closed(self) -> bool: + return self._closed + + @property + def close_code(self) -> Optional[int]: + return self._close_code + + @property + def ws_protocol(self) -> Optional[str]: + return self._ws_protocol + + @property + def compress(self) -> Union[int, bool]: + return self._compress + + def get_extra_info(self, name: str, default: Any = None) -> Any: + """Get optional transport information. + + If no value associated with ``name`` is found, ``default`` is returned. + """ + writer = self._writer + if writer is None: + return default + transport = writer.transport + if transport is None: + return default + return transport.get_extra_info(name, default) + + def exception(self) -> Optional[BaseException]: + return self._exception + + async def ping(self, message: bytes = b"") -> None: + if self._writer is None: + raise RuntimeError("Call .prepare() first") + await self._writer.send_frame(message, WSMsgType.PING) + + async def pong(self, message: bytes = b"") -> None: + # unsolicited pong + if self._writer is None: + raise RuntimeError("Call .prepare() first") + await self._writer.send_frame(message, WSMsgType.PONG) + + async def send_frame( + self, message: bytes, opcode: WSMsgType, compress: Optional[int] = None + ) -> None: + """Send a frame over the websocket.""" + if self._writer is None: + raise RuntimeError("Call .prepare() first") + await self._writer.send_frame(message, opcode, compress) + + async def send_str(self, data: str, compress: Optional[int] = None) -> None: + if self._writer is None: + raise RuntimeError("Call .prepare() first") + if not isinstance(data, str): + raise TypeError("data argument must be str (%r)" % type(data)) + await self._writer.send_frame( + data.encode("utf-8"), WSMsgType.TEXT, compress=compress + ) + + async def send_bytes(self, data: bytes, compress: Optional[int] = None) -> None: + if self._writer is None: + raise RuntimeError("Call .prepare() first") + if not isinstance(data, (bytes, bytearray, memoryview)): + raise TypeError("data argument must be byte-ish (%r)" % type(data)) + await self._writer.send_frame(data, WSMsgType.BINARY, compress=compress) + + async def send_json( + self, + data: Any, + compress: Optional[int] = None, + *, + dumps: JSONEncoder = json.dumps, + ) -> None: + await self.send_str(dumps(data), compress=compress) + + async def write_eof(self) -> None: # type: ignore[override] + if self._eof_sent: + return + if self._payload_writer is None: + raise RuntimeError("Response has not been started") + + await self.close() + self._eof_sent = True + + async def close( + self, *, code: int = WSCloseCode.OK, message: bytes = b"", drain: bool = True + ) -> bool: + """Close websocket connection.""" + if self._writer is None: + raise RuntimeError("Call .prepare() first") + + if self._closed: + return False + self._set_closed() + + try: + await self._writer.close(code, message) + writer = self._payload_writer + assert writer is not None + if drain: + await writer.drain() + except (asyncio.CancelledError, asyncio.TimeoutError): + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + raise + except Exception as exc: + self._exception = exc + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + return True + + reader = self._reader + assert reader is not None + # we need to break `receive()` cycle before we can call + # `reader.read()` as `close()` may be called from different task + if self._waiting: + assert self._loop is not None + assert self._close_wait is None + self._close_wait = self._loop.create_future() + reader.feed_data(WS_CLOSING_MESSAGE, 0) + await self._close_wait + + if self._closing: + self._close_transport() + return True + + try: + async with async_timeout.timeout(self._timeout): + while True: + msg = await reader.read() + if msg.type is WSMsgType.CLOSE: + self._set_code_close_transport(msg.data) + return True + except asyncio.CancelledError: + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + raise + except Exception as exc: + self._exception = exc + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + return True + + def _set_closing(self, code: WSCloseCode) -> None: + """Set the close code and mark the connection as closing.""" + self._closing = True + self._close_code = code + self._cancel_heartbeat() + + def _set_code_close_transport(self, code: WSCloseCode) -> None: + """Set the close code and close the transport.""" + self._close_code = code + self._close_transport() + + def _close_transport(self) -> None: + """Close the transport.""" + if self._req is not None and self._req.transport is not None: + self._req.transport.close() + + async def receive(self, timeout: Optional[float] = None) -> WSMessage: + if self._reader is None: + raise RuntimeError("Call .prepare() first") + + receive_timeout = timeout or self._receive_timeout + while True: + if self._waiting: + raise RuntimeError("Concurrent call to receive() is not allowed") + + if self._closed: + self._conn_lost += 1 + if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS: + raise RuntimeError("WebSocket connection is closed.") + return WS_CLOSED_MESSAGE + elif self._closing: + return WS_CLOSING_MESSAGE + + try: + self._waiting = True + try: + if receive_timeout: + # Entering the context manager and creating + # Timeout() object can take almost 50% of the + # run time in this loop so we avoid it if + # there is no read timeout. + async with async_timeout.timeout(receive_timeout): + msg = await self._reader.read() + else: + msg = await self._reader.read() + self._reset_heartbeat() + finally: + self._waiting = False + if self._close_wait: + set_result(self._close_wait, None) + except asyncio.TimeoutError: + raise + except EofStream: + self._close_code = WSCloseCode.OK + await self.close() + return WSMessage(WSMsgType.CLOSED, None, None) + except WebSocketError as exc: + self._close_code = exc.code + await self.close(code=exc.code) + return WSMessage(WSMsgType.ERROR, exc, None) + except Exception as exc: + self._exception = exc + self._set_closing(WSCloseCode.ABNORMAL_CLOSURE) + await self.close() + return WSMessage(WSMsgType.ERROR, exc, None) + + if msg.type not in _INTERNAL_RECEIVE_TYPES: + # If its not a close/closing/ping/pong message + # we can return it immediately + return msg + + if msg.type is WSMsgType.CLOSE: + self._set_closing(msg.data) + # Could be closed while awaiting reader. + if not self._closed and self._autoclose: + # The client is likely going to close the + # connection out from under us so we do not + # want to drain any pending writes as it will + # likely result writing to a broken pipe. + await self.close(drain=False) + elif msg.type is WSMsgType.CLOSING: + self._set_closing(WSCloseCode.OK) + elif msg.type is WSMsgType.PING and self._autoping: + await self.pong(msg.data) + continue + elif msg.type is WSMsgType.PONG and self._autoping: + continue + + return msg + + async def receive_str(self, *, timeout: Optional[float] = None) -> str: + msg = await self.receive(timeout) + if msg.type is not WSMsgType.TEXT: + raise WSMessageTypeError( + f"Received message {msg.type}:{msg.data!r} is not WSMsgType.TEXT" + ) + return cast(str, msg.data) + + async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes: + msg = await self.receive(timeout) + if msg.type is not WSMsgType.BINARY: + raise WSMessageTypeError( + f"Received message {msg.type}:{msg.data!r} is not WSMsgType.BINARY" + ) + return cast(bytes, msg.data) + + async def receive_json( + self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None + ) -> Any: + data = await self.receive_str(timeout=timeout) + return loads(data) + + async def write(self, data: bytes) -> None: + raise RuntimeError("Cannot call .write() for websocket") + + def __aiter__(self) -> "WebSocketResponse": + return self + + async def __anext__(self) -> WSMessage: + msg = await self.receive() + if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED): + raise StopAsyncIteration + return msg + + def _cancel(self, exc: BaseException) -> None: + # web_protocol calls this from connection_lost + # or when the server is shutting down. + self._closing = True + self._cancel_heartbeat() + if self._reader is not None: + set_exception(self._reader, exc) diff --git a/venv/lib/python3.12/site-packages/aiohttp/worker.py b/venv/lib/python3.12/site-packages/aiohttp/worker.py new file mode 100644 index 00000000..00ec79ce --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiohttp/worker.py @@ -0,0 +1,262 @@ +"""Async gunicorn worker for aiohttp.web""" + +import asyncio +import inspect +import os +import re +import signal +import sys +from types import FrameType +from typing import TYPE_CHECKING, Any, Optional + +from gunicorn.config import AccessLogFormat as GunicornAccessLogFormat +from gunicorn.workers import base + +from aiohttp import web + +from .helpers import set_result +from .web_app import Application +from .web_log import AccessLogger + +if TYPE_CHECKING: + import ssl + + SSLContext = ssl.SSLContext +else: + try: + import ssl + + SSLContext = ssl.SSLContext + except ImportError: # pragma: no cover + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] + + +__all__ = ("GunicornWebWorker", "GunicornUVLoopWebWorker") + + +class GunicornWebWorker(base.Worker): # type: ignore[misc,no-any-unimported] + DEFAULT_AIOHTTP_LOG_FORMAT = AccessLogger.LOG_FORMAT + DEFAULT_GUNICORN_LOG_FORMAT = GunicornAccessLogFormat.default + + def __init__(self, *args: Any, **kw: Any) -> None: # pragma: no cover + super().__init__(*args, **kw) + + self._task: Optional[asyncio.Task[None]] = None + self.exit_code = 0 + self._notify_waiter: Optional[asyncio.Future[bool]] = None + + def init_process(self) -> None: + # create new event_loop after fork + try: + asyncio.get_event_loop().close() + except RuntimeError: + # No loop was running + pass + + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(self.loop) + + super().init_process() + + def run(self) -> None: + self._task = self.loop.create_task(self._run()) + + try: # ignore all finalization problems + self.loop.run_until_complete(self._task) + except Exception: + self.log.exception("Exception in gunicorn worker") + self.loop.run_until_complete(self.loop.shutdown_asyncgens()) + self.loop.close() + + sys.exit(self.exit_code) + + async def _run(self) -> None: + runner = None + if isinstance(self.wsgi, Application): + app = self.wsgi + elif inspect.iscoroutinefunction(self.wsgi) or ( + sys.version_info < (3, 14) and asyncio.iscoroutinefunction(self.wsgi) + ): + wsgi = await self.wsgi() + if isinstance(wsgi, web.AppRunner): + runner = wsgi + app = runner.app + else: + app = wsgi + else: + raise RuntimeError( + "wsgi app should be either Application or " + "async function returning Application, got {}".format(self.wsgi) + ) + + if runner is None: + access_log = self.log.access_log if self.cfg.accesslog else None + runner = web.AppRunner( + app, + logger=self.log, + keepalive_timeout=self.cfg.keepalive, + access_log=access_log, + access_log_format=self._get_valid_log_format( + self.cfg.access_log_format + ), + shutdown_timeout=self.cfg.graceful_timeout / 100 * 95, + ) + await runner.setup() + + ctx = self._create_ssl_context(self.cfg) if self.cfg.is_ssl else None + + runner = runner + assert runner is not None + server = runner.server + assert server is not None + for sock in self.sockets: + site = web.SockSite( + runner, + sock, + ssl_context=ctx, + ) + await site.start() + + # If our parent changed then we shut down. + pid = os.getpid() + try: + while self.alive: # type: ignore[has-type] + self.notify() + + cnt = server.requests_count + if self.max_requests and cnt > self.max_requests: + self.alive = False + self.log.info("Max requests, shutting down: %s", self) + + elif pid == os.getpid() and self.ppid != os.getppid(): + self.alive = False + self.log.info("Parent changed, shutting down: %s", self) + else: + await self._wait_next_notify() + except BaseException: + pass + + await runner.cleanup() + + def _wait_next_notify(self) -> "asyncio.Future[bool]": + self._notify_waiter_done() + + loop = self.loop + assert loop is not None + self._notify_waiter = waiter = loop.create_future() + self.loop.call_later(1.0, self._notify_waiter_done, waiter) + + return waiter + + def _notify_waiter_done( + self, waiter: Optional["asyncio.Future[bool]"] = None + ) -> None: + if waiter is None: + waiter = self._notify_waiter + if waiter is not None: + set_result(waiter, True) + + if waiter is self._notify_waiter: + self._notify_waiter = None + + def init_signals(self) -> None: + # Set up signals through the event loop API. + + self.loop.add_signal_handler( + signal.SIGQUIT, self.handle_quit, signal.SIGQUIT, None + ) + + self.loop.add_signal_handler( + signal.SIGTERM, self.handle_exit, signal.SIGTERM, None + ) + + self.loop.add_signal_handler( + signal.SIGINT, self.handle_quit, signal.SIGINT, None + ) + + self.loop.add_signal_handler( + signal.SIGWINCH, self.handle_winch, signal.SIGWINCH, None + ) + + self.loop.add_signal_handler( + signal.SIGUSR1, self.handle_usr1, signal.SIGUSR1, None + ) + + self.loop.add_signal_handler( + signal.SIGABRT, self.handle_abort, signal.SIGABRT, None + ) + + # Don't let SIGTERM and SIGUSR1 disturb active requests + # by interrupting system calls + signal.siginterrupt(signal.SIGTERM, False) + signal.siginterrupt(signal.SIGUSR1, False) + # Reset signals so Gunicorn doesn't swallow subprocess return codes + # See: https://github.com/aio-libs/aiohttp/issues/6130 + + def handle_quit(self, sig: int, frame: Optional[FrameType]) -> None: + self.alive = False + + # worker_int callback + self.cfg.worker_int(self) + + # wakeup closing process + self._notify_waiter_done() + + def handle_abort(self, sig: int, frame: Optional[FrameType]) -> None: + self.alive = False + self.exit_code = 1 + self.cfg.worker_abort(self) + sys.exit(1) + + @staticmethod + def _create_ssl_context(cfg: Any) -> "SSLContext": + """Creates SSLContext instance for usage in asyncio.create_server. + + See ssl.SSLSocket.__init__ for more details. + """ + if ssl is None: # pragma: no cover + raise RuntimeError("SSL is not supported.") + + ctx = ssl.SSLContext(cfg.ssl_version) + ctx.load_cert_chain(cfg.certfile, cfg.keyfile) + ctx.verify_mode = cfg.cert_reqs + if cfg.ca_certs: + ctx.load_verify_locations(cfg.ca_certs) + if cfg.ciphers: + ctx.set_ciphers(cfg.ciphers) + return ctx + + def _get_valid_log_format(self, source_format: str) -> str: + if source_format == self.DEFAULT_GUNICORN_LOG_FORMAT: + return self.DEFAULT_AIOHTTP_LOG_FORMAT + elif re.search(r"%\([^\)]+\)", source_format): + raise ValueError( + "Gunicorn's style options in form of `%(name)s` are not " + "supported for the log formatting. Please use aiohttp's " + "format specification to configure access log formatting: " + "http://docs.aiohttp.org/en/stable/logging.html" + "#format-specification" + ) + else: + return source_format + + +class GunicornUVLoopWebWorker(GunicornWebWorker): + def init_process(self) -> None: + import uvloop + + # Close any existing event loop before setting a + # new policy. + try: + asyncio.get_event_loop().close() + except RuntimeError: + # No loop was running + pass + + # Setup uvloop policy, so that every + # asyncio.get_event_loop() will create an instance + # of uvloop event loop. + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + + super().init_process() diff --git a/venv/lib/python3.12/site-packages/idna-3.11.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/INSTALLER similarity index 100% rename from venv/lib/python3.12/site-packages/idna-3.11.dist-info/INSTALLER rename to venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/INSTALLER diff --git a/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/METADATA new file mode 100644 index 00000000..03a6f0f7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/METADATA @@ -0,0 +1,112 @@ +Metadata-Version: 2.4 +Name: aiosignal +Version: 1.4.0 +Summary: aiosignal: a list of registered asynchronous callbacks +Home-page: https://github.com/aio-libs/aiosignal +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache 2.0 +Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby +Project-URL: CI: GitHub Actions, https://github.com/aio-libs/aiosignal/actions +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/aiosignal +Project-URL: Docs: RTD, https://docs.aiosignal.org +Project-URL: GitHub: issues, https://github.com/aio-libs/aiosignal/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/aiosignal +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Development Status :: 5 - Production/Stable +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Framework :: AsyncIO +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: frozenlist>=1.1.0 +Requires-Dist: typing-extensions>=4.2; python_version < "3.13" +Dynamic: license-file + +========= +aiosignal +========= + +.. image:: https://github.com/aio-libs/aiosignal/workflows/CI/badge.svg + :target: https://github.com/aio-libs/aiosignal/actions?query=workflow%3ACI + :alt: GitHub status for master branch + +.. image:: https://codecov.io/gh/aio-libs/aiosignal/branch/master/graph/badge.svg?flag=pytest + :target: https://codecov.io/gh/aio-libs/aiosignal?flags[0]=pytest + :alt: codecov.io status for master branch + +.. image:: https://badge.fury.io/py/aiosignal.svg + :target: https://pypi.org/project/aiosignal + :alt: Latest PyPI package version + +.. image:: https://readthedocs.org/projects/aiosignal/badge/?version=latest + :target: https://aiosignal.readthedocs.io/ + :alt: Latest Read The Docs + +.. image:: https://img.shields.io/discourse/topics?server=https%3A%2F%2Faio-libs.discourse.group%2F + :target: https://aio-libs.discourse.group/ + :alt: Discourse group for io-libs + +.. image:: https://badges.gitter.im/Join%20Chat.svg + :target: https://gitter.im/aio-libs/Lobby + :alt: Chat on Gitter + +Introduction +============ + +A project to manage callbacks in `asyncio` projects. + +``Signal`` is a list of registered asynchronous callbacks. + +The signal's life-cycle has two stages: after creation its content +could be filled by using standard list operations: ``sig.append()`` +etc. + +After you call ``sig.freeze()`` the signal is *frozen*: adding, removing +and dropping callbacks is forbidden. + +The only available operation is calling the previously registered +callbacks by using ``await sig.send(data)``. + +For concrete usage examples see the `Signals + +section of the `Web Server Advanced +` chapter of the `aiohttp +documentation`_. + + +Installation +------------ + +:: + + $ pip install aiosignal + + +Documentation +============= + +https://aiosignal.readthedocs.io/ + +License +======= + +``aiosignal`` is offered under the Apache 2 license. + +Source code +=========== + +The project is hosted on GitHub_ + +Please file an issue in the `bug tracker +`_ if you have found a bug +or have some suggestions to improve the library. + +.. _GitHub: https://github.com/aio-libs/aiosignal +.. _aiohttp documentation: https://docs.aiohttp.org/ diff --git a/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/RECORD new file mode 100644 index 00000000..df5ca06f --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/RECORD @@ -0,0 +1,9 @@ +aiosignal-1.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aiosignal-1.4.0.dist-info/METADATA,sha256=CSR-8dqLxpZyjUcTDnAuQwf299EB1sSFv_nzpxznAI0,3662 +aiosignal-1.4.0.dist-info/RECORD,, +aiosignal-1.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +aiosignal-1.4.0.dist-info/licenses/LICENSE,sha256=b9UkPpLdf5jsacesN3co50kFcJ_1J6W_mNbQJjwE9bY,11332 +aiosignal-1.4.0.dist-info/top_level.txt,sha256=z45aNOKGDdrI1roqZY3BGXQ22kJFPHBmVdwtLYLtXC0,10 +aiosignal/__init__.py,sha256=TIkmUG9HTBt4dfq2nISYBiZiRB2xwvFtEZydLP0HPL4,1537 +aiosignal/__pycache__/__init__.cpython-312.pyc,, +aiosignal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/WHEEL b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/WHEEL similarity index 65% rename from venv/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/WHEEL rename to venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/WHEEL index 1ef55833..e7fa31b6 100644 --- a/venv/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/WHEEL +++ b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: setuptools (82.0.0) +Generator: setuptools (80.9.0) Root-Is-Purelib: true Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/licenses/LICENSE new file mode 100644 index 00000000..7082a2d5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013-2019 Nikolay Kim and Andrew Svetlov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/top_level.txt new file mode 100644 index 00000000..ac6df3af --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiosignal-1.4.0.dist-info/top_level.txt @@ -0,0 +1 @@ +aiosignal diff --git a/venv/lib/python3.12/site-packages/aiosignal/__init__.py b/venv/lib/python3.12/site-packages/aiosignal/__init__.py new file mode 100644 index 00000000..5ede0090 --- /dev/null +++ b/venv/lib/python3.12/site-packages/aiosignal/__init__.py @@ -0,0 +1,59 @@ +import sys +from typing import Any, Awaitable, Callable, TypeVar + +from frozenlist import FrozenList + +if sys.version_info >= (3, 11): + from typing import Unpack +else: + from typing_extensions import Unpack + +if sys.version_info >= (3, 13): + from typing import TypeVarTuple +else: + from typing_extensions import TypeVarTuple + +_T = TypeVar("_T") +_Ts = TypeVarTuple("_Ts", default=Unpack[tuple[()]]) + +__version__ = "1.4.0" + +__all__ = ("Signal",) + + +class Signal(FrozenList[Callable[[Unpack[_Ts]], Awaitable[object]]]): + """Coroutine-based signal implementation. + + To connect a callback to a signal, use any list method. + + Signals are fired using the send() coroutine, which takes named + arguments. + """ + + __slots__ = ("_owner",) + + def __init__(self, owner: object): + super().__init__() + self._owner = owner + + def __repr__(self) -> str: + return "".format( + self._owner, self.frozen, list(self) + ) + + async def send(self, *args: Unpack[_Ts], **kwargs: Any) -> None: + """ + Sends data to all registered receivers. + """ + if not self.frozen: + raise RuntimeError("Cannot send non-frozen signal.") + + for receiver in self: + await receiver(*args, **kwargs) + + def __call__( + self, func: Callable[[Unpack[_Ts]], Awaitable[_T]] + ) -> Callable[[Unpack[_Ts]], Awaitable[_T]]: + """Decorator to add a function to this Signal.""" + self.append(func) + return func diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__init__.py b/venv/lib/python3.12/site-packages/aiosignal/py.typed similarity index 100% rename from venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__init__.py rename to venv/lib/python3.12/site-packages/aiosignal/py.typed diff --git a/venv/lib/python3.12/site-packages/attr/__init__.py b/venv/lib/python3.12/site-packages/attr/__init__.py new file mode 100644 index 00000000..5c6e0650 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/__init__.py @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: MIT + +""" +Classes Without Boilerplate +""" + +from functools import partial +from typing import Callable, Literal, Protocol + +from . import converters, exceptions, filters, setters, validators +from ._cmp import cmp_using +from ._config import get_run_validators, set_run_validators +from ._funcs import asdict, assoc, astuple, has, resolve_types +from ._make import ( + NOTHING, + Attribute, + Converter, + Factory, + _Nothing, + attrib, + attrs, + evolve, + fields, + fields_dict, + make_class, + validate, +) +from ._next_gen import define, field, frozen, mutable +from ._version_info import VersionInfo + + +s = attributes = attrs +ib = attr = attrib +dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) + + +class AttrsInstance(Protocol): + pass + + +NothingType = Literal[_Nothing.NOTHING] + +__all__ = [ + "NOTHING", + "Attribute", + "AttrsInstance", + "Converter", + "Factory", + "NothingType", + "asdict", + "assoc", + "astuple", + "attr", + "attrib", + "attributes", + "attrs", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "field", + "fields", + "fields_dict", + "filters", + "frozen", + "get_run_validators", + "has", + "ib", + "make_class", + "mutable", + "resolve_types", + "s", + "set_run_validators", + "setters", + "validate", + "validators", +] + + +def _make_getattr(mod_name: str) -> Callable: + """ + Create a metadata proxy for packaging information that uses *mod_name* in + its warnings and errors. + """ + + def __getattr__(name: str) -> str: + if name not in ("__version__", "__version_info__"): + msg = f"module {mod_name} has no attribute {name}" + raise AttributeError(msg) + + from importlib.metadata import metadata + + meta = metadata("attrs") + + if name == "__version_info__": + return VersionInfo._from_version_string(meta["version"]) + + return meta["version"] + + return __getattr__ + + +__getattr__ = _make_getattr(__name__) diff --git a/venv/lib/python3.12/site-packages/attr/__init__.pyi b/venv/lib/python3.12/site-packages/attr/__init__.pyi new file mode 100644 index 00000000..758decf8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/__init__.pyi @@ -0,0 +1,389 @@ +import enum +import sys + +from typing import ( + Any, + Callable, + Generic, + Literal, + Mapping, + Protocol, + Sequence, + TypeVar, + overload, +) + +# `import X as X` is required to make these public +from . import converters as converters +from . import exceptions as exceptions +from . import filters as filters +from . import setters as setters +from . import validators as validators +from ._cmp import cmp_using as cmp_using +from ._typing_compat import AttrsInstance_ +from ._version_info import VersionInfo +from attrs import ( + define as define, + field as field, + mutable as mutable, + frozen as frozen, + _EqOrderType, + _ValidatorType, + _ConverterType, + _ReprArgType, + _OnSetAttrType, + _OnSetAttrArgType, + _FieldTransformer, + _ValidatorArgType, +) + +if sys.version_info >= (3, 10): + from typing import TypeGuard, TypeAlias +else: + from typing_extensions import TypeGuard, TypeAlias + +if sys.version_info >= (3, 11): + from typing import dataclass_transform +else: + from typing_extensions import dataclass_transform + +__version__: str +__version_info__: VersionInfo +__title__: str +__description__: str +__url__: str +__uri__: str +__author__: str +__email__: str +__license__: str +__copyright__: str + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_FilterType = Callable[["Attribute[_T]", _T], bool] + +# We subclass this here to keep the protocol's qualified name clean. +class AttrsInstance(AttrsInstance_, Protocol): + pass + +_A = TypeVar("_A", bound=type[AttrsInstance]) + +class _Nothing(enum.Enum): + NOTHING = enum.auto() + +NOTHING = _Nothing.NOTHING +NothingType: TypeAlias = Literal[_Nothing.NOTHING] + +# NOTE: Factory lies about its return type to make this possible: +# `x: List[int] # = Factory(list)` +# Work around mypy issue #4554 in the common case by using an overload. + +@overload +def Factory(factory: Callable[[], _T]) -> _T: ... +@overload +def Factory( + factory: Callable[[Any], _T], + takes_self: Literal[True], +) -> _T: ... +@overload +def Factory( + factory: Callable[[], _T], + takes_self: Literal[False], +) -> _T: ... + +In = TypeVar("In") +Out = TypeVar("Out") + +class Converter(Generic[In, Out]): + @overload + def __init__(self, converter: Callable[[In], Out]) -> None: ... + @overload + def __init__( + self, + converter: Callable[[In, AttrsInstance, Attribute], Out], + *, + takes_self: Literal[True], + takes_field: Literal[True], + ) -> None: ... + @overload + def __init__( + self, + converter: Callable[[In, Attribute], Out], + *, + takes_field: Literal[True], + ) -> None: ... + @overload + def __init__( + self, + converter: Callable[[In, AttrsInstance], Out], + *, + takes_self: Literal[True], + ) -> None: ... + +class Attribute(Generic[_T]): + name: str + default: _T | None + validator: _ValidatorType[_T] | None + repr: _ReprArgType + cmp: _EqOrderType + eq: _EqOrderType + order: _EqOrderType + hash: bool | None + init: bool + converter: Converter | None + metadata: dict[Any, Any] + type: type[_T] | None + kw_only: bool + on_setattr: _OnSetAttrType + alias: str | None + + def evolve(self, **changes: Any) -> "Attribute[Any]": ... + +# NOTE: We had several choices for the annotation to use for type arg: +# 1) Type[_T] +# - Pros: Handles simple cases correctly +# - Cons: Might produce less informative errors in the case of conflicting +# TypeVars e.g. `attr.ib(default='bad', type=int)` +# 2) Callable[..., _T] +# - Pros: Better error messages than #1 for conflicting TypeVars +# - Cons: Terrible error messages for validator checks. +# e.g. attr.ib(type=int, validator=validate_str) +# -> error: Cannot infer function type argument +# 3) type (and do all of the work in the mypy plugin) +# - Pros: Simple here, and we could customize the plugin with our own errors. +# - Cons: Would need to write mypy plugin code to handle all the cases. +# We chose option #1. + +# `attr` lies about its return type to make the following possible: +# attr() -> Any +# attr(8) -> int +# attr(validator=) -> Whatever the callable expects. +# This makes this type of assignments possible: +# x: int = attr(8) +# +# This form catches explicit None or no default but with no other arguments +# returns Any. +@overload +def attrib( + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def attrib( + default: None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: type[_T] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def attrib( + default: _T, + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: type[_T] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def attrib( + default: _T | None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + type: object = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., +) -> Any: ... +@overload +@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) +def attrs( + maybe_cls: _C, + these: dict[str, Any] | None = ..., + repr_ns: str | None = ..., + repr: bool = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., + unsafe_hash: bool | None = ..., +) -> _C: ... +@overload +@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) +def attrs( + maybe_cls: None = ..., + these: dict[str, Any] | None = ..., + repr_ns: str | None = ..., + repr: bool = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., + unsafe_hash: bool | None = ..., +) -> Callable[[_C], _C]: ... +def fields(cls: type[AttrsInstance] | AttrsInstance) -> Any: ... +def fields_dict(cls: type[AttrsInstance]) -> dict[str, Attribute[Any]]: ... +def validate(inst: AttrsInstance) -> None: ... +def resolve_types( + cls: _A, + globalns: dict[str, Any] | None = ..., + localns: dict[str, Any] | None = ..., + attribs: list[Attribute[Any]] | None = ..., + include_extras: bool = ..., +) -> _A: ... + +# TODO: add support for returning a proper attrs class from the mypy plugin +# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', +# [attr.ib()])` is valid +def make_class( + name: str, + attrs: list[str] | tuple[str, ...] | dict[str, Any], + bases: tuple[type, ...] = ..., + class_body: dict[str, Any] | None = ..., + repr_ns: str | None = ..., + repr: bool = ..., + cmp: _EqOrderType | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + collect_by_mro: bool = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., +) -> type: ... + +# _funcs -- + +# TODO: add support for returning TypedDict from the mypy plugin +# FIXME: asdict/astuple do not honor their factory args. Waiting on one of +# these: +# https://github.com/python/mypy/issues/4236 +# https://github.com/python/typing/issues/253 +# XXX: remember to fix attrs.asdict/astuple too! +def asdict( + inst: AttrsInstance, + recurse: bool = ..., + filter: _FilterType[Any] | None = ..., + dict_factory: type[Mapping[Any, Any]] = ..., + retain_collection_types: bool = ..., + value_serializer: Callable[[type, Attribute[Any], Any], Any] | None = ..., + tuple_keys: bool | None = ..., +) -> dict[str, Any]: ... + +# TODO: add support for returning NamedTuple from the mypy plugin +def astuple( + inst: AttrsInstance, + recurse: bool = ..., + filter: _FilterType[Any] | None = ..., + tuple_factory: type[Sequence[Any]] = ..., + retain_collection_types: bool = ..., +) -> tuple[Any, ...]: ... +def has(cls: type) -> TypeGuard[type[AttrsInstance]]: ... +def assoc(inst: _T, **changes: Any) -> _T: ... +def evolve(inst: _T, **changes: Any) -> _T: ... + +# _config -- + +def set_run_validators(run: bool) -> None: ... +def get_run_validators() -> bool: ... + +# aliases -- + +s = attributes = attrs +ib = attr = attrib +dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;) diff --git a/venv/lib/python3.12/site-packages/attr/_cmp.py b/venv/lib/python3.12/site-packages/attr/_cmp.py new file mode 100644 index 00000000..09bab491 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_cmp.py @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: MIT + + +import functools +import types + +from ._make import __ne__ + + +_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="} + + +def cmp_using( + eq=None, + lt=None, + le=None, + gt=None, + ge=None, + require_same_type=True, + class_name="Comparable", +): + """ + Create a class that can be passed into `attrs.field`'s ``eq``, ``order``, + and ``cmp`` arguments to customize field comparison. + + The resulting class will have a full set of ordering methods if at least + one of ``{lt, le, gt, ge}`` and ``eq`` are provided. + + Args: + eq (typing.Callable | None): + Callable used to evaluate equality of two objects. + + lt (typing.Callable | None): + Callable used to evaluate whether one object is less than another + object. + + le (typing.Callable | None): + Callable used to evaluate whether one object is less than or equal + to another object. + + gt (typing.Callable | None): + Callable used to evaluate whether one object is greater than + another object. + + ge (typing.Callable | None): + Callable used to evaluate whether one object is greater than or + equal to another object. + + require_same_type (bool): + When `True`, equality and ordering methods will return + `NotImplemented` if objects are not of the same type. + + class_name (str | None): Name of class. Defaults to "Comparable". + + See `comparison` for more details. + + .. versionadded:: 21.1.0 + """ + + body = { + "__slots__": ["value"], + "__init__": _make_init(), + "_requirements": [], + "_is_comparable_to": _is_comparable_to, + } + + # Add operations. + num_order_functions = 0 + has_eq_function = False + + if eq is not None: + has_eq_function = True + body["__eq__"] = _make_operator("eq", eq) + body["__ne__"] = __ne__ + + if lt is not None: + num_order_functions += 1 + body["__lt__"] = _make_operator("lt", lt) + + if le is not None: + num_order_functions += 1 + body["__le__"] = _make_operator("le", le) + + if gt is not None: + num_order_functions += 1 + body["__gt__"] = _make_operator("gt", gt) + + if ge is not None: + num_order_functions += 1 + body["__ge__"] = _make_operator("ge", ge) + + type_ = types.new_class( + class_name, (object,), {}, lambda ns: ns.update(body) + ) + + # Add same type requirement. + if require_same_type: + type_._requirements.append(_check_same_type) + + # Add total ordering if at least one operation was defined. + if 0 < num_order_functions < 4: + if not has_eq_function: + # functools.total_ordering requires __eq__ to be defined, + # so raise early error here to keep a nice stack. + msg = "eq must be define is order to complete ordering from lt, le, gt, ge." + raise ValueError(msg) + type_ = functools.total_ordering(type_) + + return type_ + + +def _make_init(): + """ + Create __init__ method. + """ + + def __init__(self, value): + """ + Initialize object with *value*. + """ + self.value = value + + return __init__ + + +def _make_operator(name, func): + """ + Create operator method. + """ + + def method(self, other): + if not self._is_comparable_to(other): + return NotImplemented + + result = func(self.value, other.value) + if result is NotImplemented: + return NotImplemented + + return result + + method.__name__ = f"__{name}__" + method.__doc__ = ( + f"Return a {_operation_names[name]} b. Computed by attrs." + ) + + return method + + +def _is_comparable_to(self, other): + """ + Check whether `other` is comparable to `self`. + """ + return all(func(self, other) for func in self._requirements) + + +def _check_same_type(self, other): + """ + Return True if *self* and *other* are of the same type, False otherwise. + """ + return other.value.__class__ is self.value.__class__ diff --git a/venv/lib/python3.12/site-packages/attr/_cmp.pyi b/venv/lib/python3.12/site-packages/attr/_cmp.pyi new file mode 100644 index 00000000..cc7893b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_cmp.pyi @@ -0,0 +1,13 @@ +from typing import Any, Callable + +_CompareWithType = Callable[[Any, Any], bool] + +def cmp_using( + eq: _CompareWithType | None = ..., + lt: _CompareWithType | None = ..., + le: _CompareWithType | None = ..., + gt: _CompareWithType | None = ..., + ge: _CompareWithType | None = ..., + require_same_type: bool = ..., + class_name: str = ..., +) -> type: ... diff --git a/venv/lib/python3.12/site-packages/attr/_compat.py b/venv/lib/python3.12/site-packages/attr/_compat.py new file mode 100644 index 00000000..bc68ed9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_compat.py @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: MIT + +import inspect +import platform +import sys +import threading + +from collections.abc import Mapping, Sequence # noqa: F401 +from typing import _GenericAlias + + +PYPY = platform.python_implementation() == "PyPy" +PY_3_10_PLUS = sys.version_info[:2] >= (3, 10) +PY_3_11_PLUS = sys.version_info[:2] >= (3, 11) +PY_3_12_PLUS = sys.version_info[:2] >= (3, 12) +PY_3_13_PLUS = sys.version_info[:2] >= (3, 13) +PY_3_14_PLUS = sys.version_info[:2] >= (3, 14) + + +if PY_3_14_PLUS: + import annotationlib + + # We request forward-ref annotations to not break in the presence of + # forward references. + + def _get_annotations(cls): + return annotationlib.get_annotations( + cls, format=annotationlib.Format.FORWARDREF + ) + +else: + + def _get_annotations(cls): + """ + Get annotations for *cls*. + """ + return cls.__dict__.get("__annotations__", {}) + + +class _AnnotationExtractor: + """ + Extract type annotations from a callable, returning None whenever there + is none. + """ + + __slots__ = ["sig"] + + def __init__(self, callable): + try: + self.sig = inspect.signature(callable) + except (ValueError, TypeError): # inspect failed + self.sig = None + + def get_first_param_type(self): + """ + Return the type annotation of the first argument if it's not empty. + """ + if not self.sig: + return None + + params = list(self.sig.parameters.values()) + if params and params[0].annotation is not inspect.Parameter.empty: + return params[0].annotation + + return None + + def get_return_type(self): + """ + Return the return type if it's not empty. + """ + if ( + self.sig + and self.sig.return_annotation is not inspect.Signature.empty + ): + return self.sig.return_annotation + + return None + + +# Thread-local global to track attrs instances which are already being repr'd. +# This is needed because there is no other (thread-safe) way to pass info +# about the instances that are already being repr'd through the call stack +# in order to ensure we don't perform infinite recursion. +# +# For instance, if an instance contains a dict which contains that instance, +# we need to know that we're already repr'ing the outside instance from within +# the dict's repr() call. +# +# This lives here rather than in _make.py so that the functions in _make.py +# don't have a direct reference to the thread-local in their globals dict. +# If they have such a reference, it breaks cloudpickle. +repr_context = threading.local() + + +def get_generic_base(cl): + """If this is a generic class (A[str]), return the generic base for it.""" + if cl.__class__ is _GenericAlias: + return cl.__origin__ + return None diff --git a/venv/lib/python3.12/site-packages/attr/_config.py b/venv/lib/python3.12/site-packages/attr/_config.py new file mode 100644 index 00000000..4b257726 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_config.py @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: MIT + +__all__ = ["get_run_validators", "set_run_validators"] + +_run_validators = True + + +def set_run_validators(run): + """ + Set whether or not validators are run. By default, they are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.set_disabled()` + instead. + """ + if not isinstance(run, bool): + msg = "'run' must be bool." + raise TypeError(msg) + global _run_validators + _run_validators = run + + +def get_run_validators(): + """ + Return whether or not validators are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.get_disabled()` + instead. + """ + return _run_validators diff --git a/venv/lib/python3.12/site-packages/attr/_funcs.py b/venv/lib/python3.12/site-packages/attr/_funcs.py new file mode 100644 index 00000000..1adb5002 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_funcs.py @@ -0,0 +1,497 @@ +# SPDX-License-Identifier: MIT + + +import copy + +from ._compat import get_generic_base +from ._make import _OBJ_SETATTR, NOTHING, fields +from .exceptions import AttrsAttributeNotFoundError + + +_ATOMIC_TYPES = frozenset( + { + type(None), + bool, + int, + float, + str, + complex, + bytes, + type(...), + type, + range, + property, + } +) + + +def asdict( + inst, + recurse=True, + filter=None, + dict_factory=dict, + retain_collection_types=False, + value_serializer=None, +): + """ + Return the *attrs* attribute values of *inst* as a dict. + + Optionally recurse into other *attrs*-decorated classes. + + Args: + inst: Instance of an *attrs*-decorated class. + + recurse (bool): Recurse into classes that are also *attrs*-decorated. + + filter (~typing.Callable): + A callable whose return code determines whether an attribute or + element is included (`True`) or dropped (`False`). Is called with + the `attrs.Attribute` as the first argument and the value as the + second argument. + + dict_factory (~typing.Callable): + A callable to produce dictionaries from. For example, to produce + ordered dictionaries instead of normal Python dictionaries, pass in + ``collections.OrderedDict``. + + retain_collection_types (bool): + Do not convert to `list` when encountering an attribute whose type + is `tuple` or `set`. Only meaningful if *recurse* is `True`. + + value_serializer (typing.Callable | None): + A hook that is called for every attribute or dict key/value. It + receives the current instance, field and value and must return the + (updated) value. The hook is run *after* the optional *filter* has + been applied. + + Returns: + Return type of *dict_factory*. + + Raises: + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. versionadded:: 16.0.0 *dict_factory* + .. versionadded:: 16.1.0 *retain_collection_types* + .. versionadded:: 20.3.0 *value_serializer* + .. versionadded:: 21.3.0 + If a dict has a collection for a key, it is serialized as a tuple. + """ + attrs = fields(inst.__class__) + rv = dict_factory() + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + + if value_serializer is not None: + v = value_serializer(inst, a, v) + + if recurse is True: + value_type = type(v) + if value_type in _ATOMIC_TYPES: + rv[a.name] = v + elif has(value_type): + rv[a.name] = asdict( + v, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif issubclass(value_type, (tuple, list, set, frozenset)): + cf = value_type if retain_collection_types is True else list + items = [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in v + ] + try: + rv[a.name] = cf(items) + except TypeError: + if not issubclass(cf, tuple): + raise + # Workaround for TypeError: cf.__new__() missing 1 required + # positional argument (which appears, for a namedturle) + rv[a.name] = cf(*items) + elif issubclass(value_type, dict): + df = dict_factory + rv[a.name] = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in v.items() + ) + else: + rv[a.name] = v + else: + rv[a.name] = v + return rv + + +def _asdict_anything( + val, + is_key, + filter, + dict_factory, + retain_collection_types, + value_serializer, +): + """ + ``asdict`` only works on attrs instances, this works on anything. + """ + val_type = type(val) + if val_type in _ATOMIC_TYPES: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + elif getattr(val_type, "__attrs_attrs__", None) is not None: + # Attrs class. + rv = asdict( + val, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif issubclass(val_type, (tuple, list, set, frozenset)): + if retain_collection_types is True: + cf = val.__class__ + elif is_key: + cf = tuple + else: + cf = list + + rv = cf( + [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in val + ] + ) + elif issubclass(val_type, dict): + df = dict_factory + rv = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in val.items() + ) + else: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + + return rv + + +def astuple( + inst, + recurse=True, + filter=None, + tuple_factory=tuple, + retain_collection_types=False, +): + """ + Return the *attrs* attribute values of *inst* as a tuple. + + Optionally recurse into other *attrs*-decorated classes. + + Args: + inst: Instance of an *attrs*-decorated class. + + recurse (bool): + Recurse into classes that are also *attrs*-decorated. + + filter (~typing.Callable): + A callable whose return code determines whether an attribute or + element is included (`True`) or dropped (`False`). Is called with + the `attrs.Attribute` as the first argument and the value as the + second argument. + + tuple_factory (~typing.Callable): + A callable to produce tuples from. For example, to produce lists + instead of tuples. + + retain_collection_types (bool): + Do not convert to `list` or `dict` when encountering an attribute + which type is `tuple`, `dict` or `set`. Only meaningful if + *recurse* is `True`. + + Returns: + Return type of *tuple_factory* + + Raises: + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. versionadded:: 16.2.0 + """ + attrs = fields(inst.__class__) + rv = [] + retain = retain_collection_types # Very long. :/ + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + value_type = type(v) + if recurse is True: + if value_type in _ATOMIC_TYPES: + rv.append(v) + elif has(value_type): + rv.append( + astuple( + v, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + ) + elif issubclass(value_type, (tuple, list, set, frozenset)): + cf = v.__class__ if retain is True else list + items = [ + ( + astuple( + j, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(j.__class__) + else j + ) + for j in v + ] + try: + rv.append(cf(items)) + except TypeError: + if not issubclass(cf, tuple): + raise + # Workaround for TypeError: cf.__new__() missing 1 required + # positional argument (which appears, for a namedturle) + rv.append(cf(*items)) + elif issubclass(value_type, dict): + df = value_type if retain is True else dict + rv.append( + df( + ( + ( + astuple( + kk, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(kk.__class__) + else kk + ), + ( + astuple( + vv, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(vv.__class__) + else vv + ), + ) + for kk, vv in v.items() + ) + ) + else: + rv.append(v) + else: + rv.append(v) + + return rv if tuple_factory is list else tuple_factory(rv) + + +def has(cls): + """ + Check whether *cls* is a class with *attrs* attributes. + + Args: + cls (type): Class to introspect. + + Raises: + TypeError: If *cls* is not a class. + + Returns: + bool: + """ + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is not None: + return True + + # No attrs, maybe it's a specialized generic (A[str])? + generic_base = get_generic_base(cls) + if generic_base is not None: + generic_attrs = getattr(generic_base, "__attrs_attrs__", None) + if generic_attrs is not None: + # Stick it on here for speed next time. + cls.__attrs_attrs__ = generic_attrs + return generic_attrs is not None + return False + + +def assoc(inst, **changes): + """ + Copy *inst* and apply *changes*. + + This is different from `evolve` that applies the changes to the arguments + that create the new instance. + + `evolve`'s behavior is preferable, but there are `edge cases`_ where it + doesn't work. Therefore `assoc` is deprecated, but will not be removed. + + .. _`edge cases`: https://github.com/python-attrs/attrs/issues/251 + + Args: + inst: Instance of a class with *attrs* attributes. + + changes: Keyword changes in the new copy. + + Returns: + A copy of inst with *changes* incorporated. + + Raises: + attrs.exceptions.AttrsAttributeNotFoundError: + If *attr_name* couldn't be found on *cls*. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. deprecated:: 17.1.0 + Use `attrs.evolve` instead if you can. This function will not be + removed du to the slightly different approach compared to + `attrs.evolve`, though. + """ + new = copy.copy(inst) + attrs = fields(inst.__class__) + for k, v in changes.items(): + a = getattr(attrs, k, NOTHING) + if a is NOTHING: + msg = f"{k} is not an attrs attribute on {new.__class__}." + raise AttrsAttributeNotFoundError(msg) + _OBJ_SETATTR(new, k, v) + return new + + +def resolve_types( + cls, globalns=None, localns=None, attribs=None, include_extras=True +): + """ + Resolve any strings and forward annotations in type annotations. + + This is only required if you need concrete types in :class:`Attribute`'s + *type* field. In other words, you don't need to resolve your types if you + only use them for static type checking. + + With no arguments, names will be looked up in the module in which the class + was created. If this is not what you want, for example, if the name only + exists inside a method, you may pass *globalns* or *localns* to specify + other dictionaries in which to look up these names. See the docs of + `typing.get_type_hints` for more details. + + Args: + cls (type): Class to resolve. + + globalns (dict | None): Dictionary containing global variables. + + localns (dict | None): Dictionary containing local variables. + + attribs (list | None): + List of attribs for the given class. This is necessary when calling + from inside a ``field_transformer`` since *cls* is not an *attrs* + class yet. + + include_extras (bool): + Resolve more accurately, if possible. Pass ``include_extras`` to + ``typing.get_hints``, if supported by the typing module. On + supported Python versions (3.9+), this resolves the types more + accurately. + + Raises: + TypeError: If *cls* is not a class. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class and you didn't pass any attribs. + + NameError: If types cannot be resolved because of missing variables. + + Returns: + *cls* so you can use this function also as a class decorator. Please + note that you have to apply it **after** `attrs.define`. That means the + decorator has to come in the line **before** `attrs.define`. + + .. versionadded:: 20.1.0 + .. versionadded:: 21.1.0 *attribs* + .. versionadded:: 23.1.0 *include_extras* + """ + # Since calling get_type_hints is expensive we cache whether we've + # done it already. + if getattr(cls, "__attrs_types_resolved__", None) != cls: + import typing + + kwargs = { + "globalns": globalns, + "localns": localns, + "include_extras": include_extras, + } + + hints = typing.get_type_hints(cls, **kwargs) + for field in fields(cls) if attribs is None else attribs: + if field.name in hints: + # Since fields have been frozen we must work around it. + _OBJ_SETATTR(field, "type", hints[field.name]) + # We store the class we resolved so that subclasses know they haven't + # been resolved. + cls.__attrs_types_resolved__ = cls + + # Return the class so you can use it as a decorator too. + return cls diff --git a/venv/lib/python3.12/site-packages/attr/_make.py b/venv/lib/python3.12/site-packages/attr/_make.py new file mode 100644 index 00000000..4b32d6a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_make.py @@ -0,0 +1,3406 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +import abc +import contextlib +import copy +import enum +import inspect +import itertools +import linecache +import sys +import types +import unicodedata +import weakref + +from collections.abc import Callable, Mapping +from functools import cached_property +from typing import Any, NamedTuple, TypeVar + +# We need to import _compat itself in addition to the _compat members to avoid +# having the thread-local in the globals here. +from . import _compat, _config, setters +from ._compat import ( + PY_3_10_PLUS, + PY_3_11_PLUS, + PY_3_13_PLUS, + _AnnotationExtractor, + _get_annotations, + get_generic_base, +) +from .exceptions import ( + DefaultAlreadySetError, + FrozenInstanceError, + NotAnAttrsClassError, + UnannotatedAttributeError, +) + + +# This is used at least twice, so cache it here. +_OBJ_SETATTR = object.__setattr__ +_INIT_FACTORY_PAT = "__attr_factory_%s" +_CLASSVAR_PREFIXES = ( + "typing.ClassVar", + "t.ClassVar", + "ClassVar", + "typing_extensions.ClassVar", +) +# we don't use a double-underscore prefix because that triggers +# name mangling when trying to create a slot for the field +# (when slots=True) +_HASH_CACHE_FIELD = "_attrs_cached_hash" + +_EMPTY_METADATA_SINGLETON = types.MappingProxyType({}) + +# Unique object for unequivocal getattr() defaults. +_SENTINEL = object() + +_DEFAULT_ON_SETATTR = setters.pipe(setters.convert, setters.validate) + + +class _Nothing(enum.Enum): + """ + Sentinel to indicate the lack of a value when `None` is ambiguous. + + If extending attrs, you can use ``typing.Literal[NOTHING]`` to show + that a value may be ``NOTHING``. + + .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. + .. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant. + """ + + NOTHING = enum.auto() + + def __repr__(self): + return "NOTHING" + + def __bool__(self): + return False + + +NOTHING = _Nothing.NOTHING +""" +Sentinel to indicate the lack of a value when `None` is ambiguous. + +When using in 3rd party code, use `attrs.NothingType` for type annotations. +""" + + +class _CacheHashWrapper(int): + """ + An integer subclass that pickles / copies as None + + This is used for non-slots classes with ``cache_hash=True``, to avoid + serializing a potentially (even likely) invalid hash value. Since `None` + is the default value for uncalculated hashes, whenever this is copied, + the copy's value for the hash should automatically reset. + + See GH #613 for more details. + """ + + def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008 + return _none_constructor, _args + + +def attrib( + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=None, + eq=None, + order=None, + on_setattr=None, + alias=None, +): + """ + Create a new field / attribute on a class. + + Identical to `attrs.field`, except it's not keyword-only. + + Consider using `attrs.field` in new code (``attr.ib`` will *never* go away, + though). + + .. warning:: + + Does **nothing** unless the class is also decorated with + `attr.s` (or similar)! + + + .. versionadded:: 15.2.0 *convert* + .. versionadded:: 16.3.0 *metadata* + .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. + .. versionchanged:: 17.1.0 + *hash* is `None` and therefore mirrors *eq* by default. + .. versionadded:: 17.3.0 *type* + .. deprecated:: 17.4.0 *convert* + .. versionadded:: 17.4.0 + *converter* as a replacement for the deprecated *convert* to achieve + consistency with other noun-based arguments. + .. versionadded:: 18.1.0 + ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. + .. versionadded:: 18.2.0 *kw_only* + .. versionchanged:: 19.2.0 *convert* keyword argument removed. + .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 22.2.0 *alias* + .. versionchanged:: 25.4.0 + *kw_only* can now be None, and its default is also changed from False to + None. + """ + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq, order, True + ) + + if hash is not None and hash is not True and hash is not False: + msg = "Invalid value for hash. Must be True, False, or None." + raise TypeError(msg) + + if factory is not None: + if default is not NOTHING: + msg = ( + "The `default` and `factory` arguments are mutually exclusive." + ) + raise ValueError(msg) + if not callable(factory): + msg = "The `factory` argument must be a callable." + raise ValueError(msg) + default = Factory(factory) + + if metadata is None: + metadata = {} + + # Apply syntactic sugar by auto-wrapping. + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + if validator and isinstance(validator, (list, tuple)): + validator = and_(*validator) + + if converter and isinstance(converter, (list, tuple)): + converter = pipe(*converter) + + return _CountingAttr( + default=default, + validator=validator, + repr=repr, + cmp=None, + hash=hash, + init=init, + converter=converter, + metadata=metadata, + type=type, + kw_only=kw_only, + eq=eq, + eq_key=eq_key, + order=order, + order_key=order_key, + on_setattr=on_setattr, + alias=alias, + ) + + +def _compile_and_eval( + script: str, + globs: dict[str, Any] | None, + locs: Mapping[str, object] | None = None, + filename: str = "", +) -> None: + """ + Evaluate the script with the given global (globs) and local (locs) + variables. + """ + bytecode = compile(script, filename, "exec") + eval(bytecode, globs, locs) + + +def _linecache_and_compile( + script: str, + filename: str, + globs: dict[str, Any] | None, + locals: Mapping[str, object] | None = None, +) -> dict[str, Any]: + """ + Cache the script with _linecache_, compile it and return the _locals_. + """ + + locs = {} if locals is None else locals + + # In order of debuggers like PDB being able to step through the code, + # we add a fake linecache entry. + count = 1 + base_filename = filename + while True: + linecache_tuple = ( + len(script), + None, + script.splitlines(True), + filename, + ) + old_val = linecache.cache.setdefault(filename, linecache_tuple) + if old_val == linecache_tuple: + break + + filename = f"{base_filename[:-1]}-{count}>" + count += 1 + + _compile_and_eval(script, globs, locs, filename) + + return locs + + +def _make_attr_tuple_class(cls_name: str, attr_names: list[str]) -> type: + """ + Create a tuple subclass to hold `Attribute`s for an `attrs` class. + + The subclass is a bare tuple with properties for names. + + class MyClassAttributes(tuple): + __slots__ = () + x = property(itemgetter(0)) + """ + attr_class_name = f"{cls_name}Attributes" + body = {} + for i, attr_name in enumerate(attr_names): + + def getter(self, i=i): + return self[i] + + body[attr_name] = property(getter) + return type(attr_class_name, (tuple,), body) + + +# Tuple class for extracted attributes from a class definition. +# `base_attrs` is a subset of `attrs`. +class _Attributes(NamedTuple): + attrs: type + base_attrs: list[Attribute] + base_attrs_map: dict[str, type] + + +def _is_class_var(annot): + """ + Check whether *annot* is a typing.ClassVar. + + The string comparison hack is used to avoid evaluating all string + annotations which would put attrs-based classes at a performance + disadvantage compared to plain old classes. + """ + annot = str(annot) + + # Annotation can be quoted. + if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): + annot = annot[1:-1] + + return annot.startswith(_CLASSVAR_PREFIXES) + + +def _has_own_attribute(cls, attrib_name): + """ + Check whether *cls* defines *attrib_name* (and doesn't just inherit it). + """ + return attrib_name in cls.__dict__ + + +def _collect_base_attrs( + cls, taken_attr_names +) -> tuple[list[Attribute], dict[str, type]]: + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in reversed(cls.__mro__[1:-1]): + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.inherited or a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) # noqa: PLW2901 + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + # For each name, only keep the freshest definition i.e. the furthest at the + # back. base_attr_map is fine because it gets overwritten with every new + # instance. + filtered = [] + seen = set() + for a in reversed(base_attrs): + if a.name in seen: + continue + filtered.insert(0, a) + seen.add(a.name) + + return filtered, base_attr_map + + +def _collect_base_attrs_broken(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + + N.B. *taken_attr_names* will be mutated. + + Adhere to the old incorrect behavior. + + Notably it collects from the front and considers inherited attributes which + leads to the buggy behavior reported in #428. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in cls.__mro__[1:-1]: + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) # noqa: PLW2901 + taken_attr_names.add(a.name) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + return base_attrs, base_attr_map + + +def _transform_attrs( + cls, + these, + auto_attribs, + kw_only, + collect_by_mro, + field_transformer, +) -> _Attributes: + """ + Transform all `_CountingAttr`s on a class into `Attribute`s. + + If *these* is passed, use that and don't look for them on the class. + + If *collect_by_mro* is True, collect them in the correct MRO order, + otherwise use the old -- incorrect -- order. See #428. + + Return an `_Attributes`. + """ + cd = cls.__dict__ + anns = _get_annotations(cls) + + if these is not None: + ca_list = list(these.items()) + elif auto_attribs is True: + ca_names = { + name + for name, attr in cd.items() + if attr.__class__ is _CountingAttr + } + ca_list = [] + annot_names = set() + for attr_name, type in anns.items(): + if _is_class_var(type): + continue + annot_names.add(attr_name) + a = cd.get(attr_name, NOTHING) + + if a.__class__ is not _CountingAttr: + a = attrib(a) + ca_list.append((attr_name, a)) + + unannotated = ca_names - annot_names + if unannotated: + raise UnannotatedAttributeError( + "The following `attr.ib`s lack a type annotation: " + + ", ".join( + sorted(unannotated, key=lambda n: cd.get(n).counter) + ) + + "." + ) + else: + ca_list = sorted( + ( + (name, attr) + for name, attr in cd.items() + if attr.__class__ is _CountingAttr + ), + key=lambda e: e[1].counter, + ) + + fca = Attribute.from_counting_attr + no = ClassProps.KeywordOnly.NO + own_attrs = [ + fca( + attr_name, + ca, + kw_only is not no, + anns.get(attr_name), + ) + for attr_name, ca in ca_list + ] + + if collect_by_mro: + base_attrs, base_attr_map = _collect_base_attrs( + cls, {a.name for a in own_attrs} + ) + else: + base_attrs, base_attr_map = _collect_base_attrs_broken( + cls, {a.name for a in own_attrs} + ) + + if kw_only is ClassProps.KeywordOnly.FORCE: + own_attrs = [a.evolve(kw_only=True) for a in own_attrs] + base_attrs = [a.evolve(kw_only=True) for a in base_attrs] + + attrs = base_attrs + own_attrs + + # Resolve default field alias before executing field_transformer, so that + # the transformer receives fully populated Attribute objects with usable + # alias values. + for a in attrs: + if not a.alias: + # Evolve is very slow, so we hold our nose and do it dirty. + _OBJ_SETATTR.__get__(a)("alias", _default_init_alias_for(a.name)) + _OBJ_SETATTR.__get__(a)("alias_is_default", True) + + if field_transformer is not None: + attrs = tuple(field_transformer(cls, attrs)) + + # Check attr order after executing the field_transformer. + # Mandatory vs non-mandatory attr order only matters when they are part of + # the __init__ signature and when they aren't kw_only (which are moved to + # the end and can be mandatory or non-mandatory in any order, as they will + # be specified as keyword args anyway). Check the order of those attrs: + had_default = False + for a in (a for a in attrs if a.init is not False and a.kw_only is False): + if had_default is True and a.default is NOTHING: + msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}" + raise ValueError(msg) + + if had_default is False and a.default is not NOTHING: + had_default = True + + # Resolve default field alias for any new attributes that the + # field_transformer may have added without setting an alias. + for a in attrs: + if not a.alias: + _OBJ_SETATTR.__get__(a)("alias", _default_init_alias_for(a.name)) + _OBJ_SETATTR.__get__(a)("alias_is_default", True) + + # Create AttrsClass *after* applying the field_transformer since it may + # add or remove attributes! + attr_names = [a.name for a in attrs] + AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) + + return _Attributes(AttrsClass(attrs), base_attrs, base_attr_map) + + +def _make_cached_property_getattr(cached_properties, original_getattr, cls): + lines = [ + # Wrapped to get `__class__` into closure cell for super() + # (It will be replaced with the newly constructed class after construction). + "def wrapper(_cls):", + " __class__ = _cls", + " def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):", + " func = cached_properties.get(item)", + " if func is not None:", + " result = func(self)", + " _setter = _cached_setattr_get(self)", + " _setter(item, result)", + " return result", + ] + if original_getattr is not None: + lines.append( + " return original_getattr(self, item)", + ) + else: + lines.extend( + [ + " try:", + " return super().__getattribute__(item)", + " except AttributeError:", + " if not hasattr(super(), '__getattr__'):", + " raise", + " return super().__getattr__(item)", + " original_error = f\"'{self.__class__.__name__}' object has no attribute '{item}'\"", + " raise AttributeError(original_error)", + ] + ) + + lines.extend( + [ + " return __getattr__", + "__getattr__ = wrapper(_cls)", + ] + ) + + unique_filename = _generate_unique_filename(cls, "getattr") + + glob = { + "cached_properties": cached_properties, + "_cached_setattr_get": _OBJ_SETATTR.__get__, + "original_getattr": original_getattr, + } + + return _linecache_and_compile( + "\n".join(lines), unique_filename, glob, locals={"_cls": cls} + )["__getattr__"] + + +def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + if isinstance(self, BaseException) and name in ( + "__cause__", + "__context__", + "__traceback__", + "__suppress_context__", + "__notes__", + ): + BaseException.__setattr__(self, name, value) + return + + raise FrozenInstanceError + + +def _frozen_delattrs(self, name): + """ + Attached to frozen classes as __delattr__. + """ + if isinstance(self, BaseException) and name == "__notes__": + BaseException.__delattr__(self, name) + return + + raise FrozenInstanceError + + +def evolve(*args, **changes): + """ + Create a new instance, based on the first positional argument with + *changes* applied. + + .. tip:: + + On Python 3.13 and later, you can also use `copy.replace` instead. + + Args: + + inst: + Instance of a class with *attrs* attributes. *inst* must be passed + as a positional argument. + + changes: + Keyword changes in the new copy. + + Returns: + A copy of inst with *changes* incorporated. + + Raises: + TypeError: + If *attr_name* couldn't be found in the class ``__init__``. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + .. versionadded:: 17.1.0 + .. deprecated:: 23.1.0 + It is now deprecated to pass the instance using the keyword argument + *inst*. It will raise a warning until at least April 2024, after which + it will become an error. Always pass the instance as a positional + argument. + .. versionchanged:: 24.1.0 + *inst* can't be passed as a keyword argument anymore. + """ + try: + (inst,) = args + except ValueError: + msg = ( + f"evolve() takes 1 positional argument, but {len(args)} were given" + ) + raise TypeError(msg) from None + + cls = inst.__class__ + attrs = fields(cls) + for a in attrs: + if not a.init: + continue + attr_name = a.name # To deal with private attributes. + init_name = a.alias + if init_name not in changes: + changes[init_name] = getattr(inst, attr_name) + + return cls(**changes) + + +class _ClassBuilder: + """ + Iteratively build *one* class. + """ + + __slots__ = ( + "_add_method_dunders", + "_attr_names", + "_attrs", + "_base_attr_map", + "_base_names", + "_cache_hash", + "_cls", + "_cls_dict", + "_delete_attribs", + "_frozen", + "_has_custom_setattr", + "_has_post_init", + "_has_pre_init", + "_is_exc", + "_on_setattr", + "_pre_init_has_args", + "_repr_added", + "_script_snippets", + "_slots", + "_weakref_slot", + "_wrote_own_setattr", + ) + + def __init__( + self, + cls: type, + these, + auto_attribs: bool, + props: ClassProps, + has_custom_setattr: bool, + ): + attrs, base_attrs, base_map = _transform_attrs( + cls, + these, + auto_attribs, + props.kw_only, + props.collected_fields_by_mro, + props.field_transformer, + ) + + self._cls = cls + self._cls_dict = dict(cls.__dict__) if props.is_slotted else {} + self._attrs = attrs + self._base_names = {a.name for a in base_attrs} + self._base_attr_map = base_map + self._attr_names = tuple(a.name for a in attrs) + self._slots = props.is_slotted + self._frozen = props.is_frozen + self._weakref_slot = props.has_weakref_slot + self._cache_hash = ( + props.hashability is ClassProps.Hashability.HASHABLE_CACHED + ) + self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) + self._pre_init_has_args = False + if self._has_pre_init: + # Check if the pre init method has more arguments than just `self` + # We want to pass arguments if pre init expects arguments + pre_init_func = cls.__attrs_pre_init__ + pre_init_signature = inspect.signature(pre_init_func) + self._pre_init_has_args = len(pre_init_signature.parameters) > 1 + self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) + self._delete_attribs = not bool(these) + self._is_exc = props.is_exception + self._on_setattr = props.on_setattr_hook + + self._has_custom_setattr = has_custom_setattr + self._wrote_own_setattr = False + + self._cls_dict["__attrs_attrs__"] = self._attrs + self._cls_dict["__attrs_props__"] = props + + if props.is_frozen: + self._cls_dict["__setattr__"] = _frozen_setattrs + self._cls_dict["__delattr__"] = _frozen_delattrs + + self._wrote_own_setattr = True + elif self._on_setattr in ( + _DEFAULT_ON_SETATTR, + setters.validate, + setters.convert, + ): + has_validator = has_converter = False + for a in attrs: + if a.validator is not None: + has_validator = True + if a.converter is not None: + has_converter = True + + if has_validator and has_converter: + break + if ( + ( + self._on_setattr == _DEFAULT_ON_SETATTR + and not (has_validator or has_converter) + ) + or (self._on_setattr == setters.validate and not has_validator) + or (self._on_setattr == setters.convert and not has_converter) + ): + # If class-level on_setattr is set to convert + validate, but + # there's no field to convert or validate, pretend like there's + # no on_setattr. + self._on_setattr = None + + if props.added_pickling: + ( + self._cls_dict["__getstate__"], + self._cls_dict["__setstate__"], + ) = self._make_getstate_setstate() + + # tuples of script, globs, hook + self._script_snippets: list[ + tuple[str, dict, Callable[[dict, dict], Any]] + ] = [] + self._repr_added = False + + # We want to only do this check once; in 99.9% of cases these + # exist. + if not hasattr(self._cls, "__module__") or not hasattr( + self._cls, "__qualname__" + ): + self._add_method_dunders = self._add_method_dunders_safe + else: + self._add_method_dunders = self._add_method_dunders_unsafe + + def __repr__(self): + return f"<_ClassBuilder(cls={self._cls.__name__})>" + + def _eval_snippets(self) -> None: + """ + Evaluate any registered snippets in one go. + """ + script = "\n".join([snippet[0] for snippet in self._script_snippets]) + globs = {} + for _, snippet_globs, _ in self._script_snippets: + globs.update(snippet_globs) + + locs = _linecache_and_compile( + script, + _generate_unique_filename(self._cls, "methods"), + globs, + ) + + for _, _, hook in self._script_snippets: + hook(self._cls_dict, locs) + + def build_class(self): + """ + Finalize class based on the accumulated configuration. + + Builder cannot be used after calling this method. + """ + self._eval_snippets() + if self._slots is True: + cls = self._create_slots_class() + self._cls.__attrs_base_of_slotted__ = weakref.ref(cls) + else: + cls = self._patch_original_class() + if PY_3_10_PLUS: + cls = abc.update_abstractmethods(cls) + + # The method gets only called if it's not inherited from a base class. + # _has_own_attribute does NOT work properly for classmethods. + if ( + getattr(cls, "__attrs_init_subclass__", None) + and "__attrs_init_subclass__" not in cls.__dict__ + ): + cls.__attrs_init_subclass__() + + return cls + + def _patch_original_class(self): + """ + Apply accumulated methods and return the class. + """ + cls = self._cls + base_names = self._base_names + + # Clean class of attribute definitions (`attr.ib()`s). + if self._delete_attribs: + for name in self._attr_names: + if ( + name not in base_names + and getattr(cls, name, _SENTINEL) is not _SENTINEL + ): + # An AttributeError can happen if a base class defines a + # class variable and we want to set an attribute with the + # same name by using only a type annotation. + with contextlib.suppress(AttributeError): + delattr(cls, name) + + # Attach our dunder methods. + for name, value in self._cls_dict.items(): + setattr(cls, name, value) + + # If we've inherited an attrs __setattr__ and don't write our own, + # reset it to object's. + if not self._wrote_own_setattr and getattr( + cls, "__attrs_own_setattr__", False + ): + cls.__attrs_own_setattr__ = False + + if not self._has_custom_setattr: + cls.__setattr__ = _OBJ_SETATTR + + return cls + + def _create_slots_class(self): + """ + Build and return a new class with a `__slots__` attribute. + """ + cd = { + k: v + for k, v in self._cls_dict.items() + if k not in (*tuple(self._attr_names), "__dict__", "__weakref__") + } + + # 3.14.0rc2+ + if hasattr(sys, "_clear_type_descriptors"): + sys._clear_type_descriptors(self._cls) + + # If our class doesn't have its own implementation of __setattr__ + # (either from the user or by us), check the bases, if one of them has + # an attrs-made __setattr__, that needs to be reset. We don't walk the + # MRO because we only care about our immediate base classes. + # XXX: This can be confused by subclassing a slotted attrs class with + # XXX: a non-attrs class and subclass the resulting class with an attrs + # XXX: class. See `test_slotted_confused` for details. For now that's + # XXX: OK with us. + if not self._wrote_own_setattr: + cd["__attrs_own_setattr__"] = False + + if not self._has_custom_setattr: + for base_cls in self._cls.__bases__: + if base_cls.__dict__.get("__attrs_own_setattr__", False): + cd["__setattr__"] = _OBJ_SETATTR + break + + # Traverse the MRO to collect existing slots + # and check for an existing __weakref__. + existing_slots = {} + weakref_inherited = False + for base_cls in self._cls.__mro__[1:-1]: + if base_cls.__dict__.get("__weakref__", None) is not None: + weakref_inherited = True + existing_slots.update( + { + name: getattr(base_cls, name) + for name in getattr(base_cls, "__slots__", []) + } + ) + + base_names = set(self._base_names) + + names = self._attr_names + if ( + self._weakref_slot + and "__weakref__" not in getattr(self._cls, "__slots__", ()) + and "__weakref__" not in names + and not weakref_inherited + ): + names += ("__weakref__",) + + cached_properties = { + name: cached_prop.func + for name, cached_prop in cd.items() + if isinstance(cached_prop, cached_property) + } + + # Collect methods with a `__class__` reference that are shadowed in the new class. + # To know to update them. + additional_closure_functions_to_update = [] + if cached_properties: + class_annotations = _get_annotations(self._cls) + for name, func in cached_properties.items(): + # Add cached properties to names for slotting. + names += (name,) + # Clear out function from class to avoid clashing. + del cd[name] + additional_closure_functions_to_update.append(func) + annotation = inspect.signature(func).return_annotation + if annotation is not inspect.Parameter.empty: + class_annotations[name] = annotation + + original_getattr = cd.get("__getattr__") + if original_getattr is not None: + additional_closure_functions_to_update.append(original_getattr) + + cd["__getattr__"] = _make_cached_property_getattr( + cached_properties, original_getattr, self._cls + ) + + # We only add the names of attributes that aren't inherited. + # Setting __slots__ to inherited attributes wastes memory. + slot_names = [name for name in names if name not in base_names] + + # There are slots for attributes from current class + # that are defined in parent classes. + # As their descriptors may be overridden by a child class, + # we collect them here and update the class dict + reused_slots = { + slot: slot_descriptor + for slot, slot_descriptor in existing_slots.items() + if slot in slot_names + } + slot_names = [name for name in slot_names if name not in reused_slots] + cd.update(reused_slots) + if self._cache_hash: + slot_names.append(_HASH_CACHE_FIELD) + + cd["__slots__"] = tuple(slot_names) + + cd["__qualname__"] = self._cls.__qualname__ + + # Create new class based on old class and our methods. + cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) + + # The following is a fix for + # . + # If a method mentions `__class__` or uses the no-arg super(), the + # compiler will bake a reference to the class in the method itself + # as `method.__closure__`. Since we replace the class with a + # clone, we rewrite these references so it keeps working. + for item in itertools.chain( + cls.__dict__.values(), additional_closure_functions_to_update + ): + if isinstance(item, (classmethod, staticmethod)): + # Class- and staticmethods hide their functions inside. + # These might need to be rewritten as well. + closure_cells = getattr(item.__func__, "__closure__", None) + elif isinstance(item, property): + # Workaround for property `super()` shortcut (PY3-only). + # There is no universal way for other descriptors. + closure_cells = getattr(item.fget, "__closure__", None) + else: + closure_cells = getattr(item, "__closure__", None) + + if not closure_cells: # Catch None or the empty list. + continue + for cell in closure_cells: + try: + match = cell.cell_contents is self._cls + except ValueError: # noqa: PERF203 + # ValueError: Cell is empty + pass + else: + if match: + cell.cell_contents = cls + return cls + + def add_repr(self, ns): + script, globs = _make_repr_script(self._attrs, ns) + + def _attach_repr(cls_dict, globs): + cls_dict["__repr__"] = self._add_method_dunders(globs["__repr__"]) + + self._script_snippets.append((script, globs, _attach_repr)) + self._repr_added = True + return self + + def add_str(self): + if not self._repr_added: + msg = "__str__ can only be generated if a __repr__ exists." + raise ValueError(msg) + + def __str__(self): + return self.__repr__() + + self._cls_dict["__str__"] = self._add_method_dunders(__str__) + return self + + def _make_getstate_setstate(self): + """ + Create custom __setstate__ and __getstate__ methods. + """ + # __weakref__ is not writable. + state_attr_names = tuple( + an for an in self._attr_names if an != "__weakref__" + ) + + def slots_getstate(self): + """ + Automatically created by attrs. + """ + return {name: getattr(self, name) for name in state_attr_names} + + hash_caching_enabled = self._cache_hash + + def slots_setstate(self, state): + """ + Automatically created by attrs. + """ + __bound_setattr = _OBJ_SETATTR.__get__(self) + if isinstance(state, tuple): + # Backward compatibility with attrs instances pickled with + # attrs versions before v22.2.0 which stored tuples. + for name, value in zip(state_attr_names, state): + __bound_setattr(name, value) + else: + for name in state_attr_names: + if name in state: + __bound_setattr(name, state[name]) + + # The hash code cache is not included when the object is + # serialized, but it still needs to be initialized to None to + # indicate that the first call to __hash__ should be a cache + # miss. + if hash_caching_enabled: + __bound_setattr(_HASH_CACHE_FIELD, None) + + return slots_getstate, slots_setstate + + def make_unhashable(self): + self._cls_dict["__hash__"] = None + return self + + def add_hash(self): + script, globs = _make_hash_script( + self._cls, + self._attrs, + frozen=self._frozen, + cache_hash=self._cache_hash, + ) + + def attach_hash(cls_dict: dict, locs: dict) -> None: + cls_dict["__hash__"] = self._add_method_dunders(locs["__hash__"]) + + self._script_snippets.append((script, globs, attach_hash)) + + return self + + def add_init(self): + script, globs, annotations = _make_init_script( + self._cls, + self._attrs, + self._has_pre_init, + self._pre_init_has_args, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=False, + ) + + def _attach_init(cls_dict, globs): + init = globs["__init__"] + init.__annotations__ = annotations + cls_dict["__init__"] = self._add_method_dunders(init) + + self._script_snippets.append((script, globs, _attach_init)) + + return self + + def add_replace(self): + self._cls_dict["__replace__"] = self._add_method_dunders(evolve) + return self + + def add_match_args(self): + self._cls_dict["__match_args__"] = tuple( + field.name + for field in self._attrs + if field.init and not field.kw_only + ) + + def add_attrs_init(self): + script, globs, annotations = _make_init_script( + self._cls, + self._attrs, + self._has_pre_init, + self._pre_init_has_args, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=True, + ) + + def _attach_attrs_init(cls_dict, globs): + init = globs["__attrs_init__"] + init.__annotations__ = annotations + cls_dict["__attrs_init__"] = self._add_method_dunders(init) + + self._script_snippets.append((script, globs, _attach_attrs_init)) + + return self + + def add_eq(self): + cd = self._cls_dict + + script, globs = _make_eq_script(self._attrs) + + def _attach_eq(cls_dict, globs): + cls_dict["__eq__"] = self._add_method_dunders(globs["__eq__"]) + + self._script_snippets.append((script, globs, _attach_eq)) + + cd["__ne__"] = __ne__ + + return self + + def add_order(self): + cd = self._cls_dict + + cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = ( + self._add_method_dunders(meth) + for meth in _make_order(self._cls, self._attrs) + ) + + return self + + def add_setattr(self): + sa_attrs = {} + for a in self._attrs: + on_setattr = a.on_setattr or self._on_setattr + if on_setattr and on_setattr is not setters.NO_OP: + sa_attrs[a.name] = a, on_setattr + + if not sa_attrs: + return self + + if self._has_custom_setattr: + # We need to write a __setattr__ but there already is one! + msg = "Can't combine custom __setattr__ with on_setattr hooks." + raise ValueError(msg) + + # docstring comes from _add_method_dunders + def __setattr__(self, name, val): + try: + a, hook = sa_attrs[name] + except KeyError: + nval = val + else: + nval = hook(self, a, val) + + _OBJ_SETATTR(self, name, nval) + + self._cls_dict["__attrs_own_setattr__"] = True + self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) + self._wrote_own_setattr = True + + return self + + def _add_method_dunders_unsafe(self, method: Callable) -> Callable: + """ + Add __module__ and __qualname__ to a *method*. + """ + method.__module__ = self._cls.__module__ + + method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" + + method.__doc__ = ( + f"Method generated by attrs for class {self._cls.__qualname__}." + ) + + return method + + def _add_method_dunders_safe(self, method: Callable) -> Callable: + """ + Add __module__ and __qualname__ to a *method* if possible. + """ + with contextlib.suppress(AttributeError): + method.__module__ = self._cls.__module__ + + with contextlib.suppress(AttributeError): + method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" + + with contextlib.suppress(AttributeError): + method.__doc__ = f"Method generated by attrs for class {self._cls.__qualname__}." + + return method + + +def _determine_attrs_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + msg = "Don't mix `cmp` with `eq' and `order`." + raise ValueError(msg) + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + return cmp, cmp + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq = default_eq + + if order is None: + order = eq + + if eq is False and order is True: + msg = "`order` can only be True if `eq` is True too." + raise ValueError(msg) + + return eq, order + + +def _determine_attrib_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + msg = "Don't mix `cmp` with `eq' and `order`." + raise ValueError(msg) + + def decide_callable_or_boolean(value): + """ + Decide whether a key function is used. + """ + if callable(value): + value, key = True, value + else: + key = None + return value, key + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + cmp, cmp_key = decide_callable_or_boolean(cmp) + return cmp, cmp_key, cmp, cmp_key + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq, eq_key = default_eq, None + else: + eq, eq_key = decide_callable_or_boolean(eq) + + if order is None: + order, order_key = eq, eq_key + else: + order, order_key = decide_callable_or_boolean(order) + + if eq is False and order is True: + msg = "`order` can only be True if `eq` is True too." + raise ValueError(msg) + + return eq, eq_key, order, order_key + + +def _determine_whether_to_implement( + cls, flag, auto_detect, dunders, default=True +): + """ + Check whether we should implement a set of methods for *cls*. + + *flag* is the argument passed into @attr.s like 'init', *auto_detect* the + same as passed into @attr.s and *dunders* is a tuple of attribute names + whose presence signal that the user has implemented it themselves. + + Return *default* if no reason for either for or against is found. + """ + if flag is True or flag is False: + return flag + + if flag is None and auto_detect is False: + return default + + # Logically, flag is None and auto_detect is True here. + for dunder in dunders: + if _has_own_attribute(cls, dunder): + return False + + return default + + +def attrs( + maybe_cls=None, + these=None, + repr_ns=None, + repr=None, + cmp=None, + hash=None, + init=None, + slots=False, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=False, + kw_only=False, + cache_hash=False, + auto_exc=False, + eq=None, + order=None, + auto_detect=False, + collect_by_mro=False, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, + unsafe_hash=None, + force_kw_only=True, +): + r""" + A class decorator that adds :term:`dunder methods` according to the + specified attributes using `attr.ib` or the *these* argument. + + Consider using `attrs.define` / `attrs.frozen` in new code (``attr.s`` will + *never* go away, though). + + Args: + repr_ns (str): + When using nested classes, there was no way in Python 2 to + automatically detect that. This argument allows to set a custom + name for a more meaningful ``repr`` output. This argument is + pointless in Python 3 and is therefore deprecated. + + .. caution:: + Refer to `attrs.define` for the rest of the parameters, but note that they + can have different defaults. + + Notably, leaving *on_setattr* as `None` will **not** add any hooks. + + .. versionadded:: 16.0.0 *slots* + .. versionadded:: 16.1.0 *frozen* + .. versionadded:: 16.3.0 *str* + .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``. + .. versionchanged:: 17.1.0 + *hash* supports `None` as value which is also the default now. + .. versionadded:: 17.3.0 *auto_attribs* + .. versionchanged:: 18.1.0 + If *these* is passed, no attributes are deleted from the class body. + .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained. + .. versionadded:: 18.2.0 *weakref_slot* + .. deprecated:: 18.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a + `DeprecationWarning` if the classes compared are subclasses of + each other. ``__eq`` and ``__ne__`` never tried to compared subclasses + to each other. + .. versionchanged:: 19.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider + subclasses comparable anymore. + .. versionadded:: 18.2.0 *kw_only* + .. versionadded:: 18.2.0 *cache_hash* + .. versionadded:: 19.1.0 *auto_exc* + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *auto_detect* + .. versionadded:: 20.1.0 *collect_by_mro* + .. versionadded:: 20.1.0 *getstate_setstate* + .. versionadded:: 20.1.0 *on_setattr* + .. versionadded:: 20.3.0 *field_transformer* + .. versionchanged:: 21.1.0 + ``init=False`` injects ``__attrs_init__`` + .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` + .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 21.3.0 *match_args* + .. versionadded:: 22.2.0 + *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). + .. deprecated:: 24.1.0 *repr_ns* + .. versionchanged:: 24.1.0 + Instances are not compared as tuples of attributes anymore, but using a + big ``and`` condition. This is faster and has more correct behavior for + uncomparable values like `math.nan`. + .. versionadded:: 24.1.0 + If a class has an *inherited* classmethod called + ``__attrs_init_subclass__``, it is executed after the class is created. + .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*. + .. versionchanged:: 25.4.0 + *kw_only* now only applies to attributes defined in the current class, + and respects attribute-level ``kw_only=False`` settings. + .. versionadded:: 25.4.0 *force_kw_only* + """ + if repr_ns is not None: + import warnings + + warnings.warn( + DeprecationWarning( + "The `repr_ns` argument is deprecated and will be removed in or after August 2025." + ), + stacklevel=2, + ) + + eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) + + # unsafe_hash takes precedence due to PEP 681. + if unsafe_hash is not None: + hash = unsafe_hash + + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + def wrap(cls): + nonlocal hash + is_frozen = frozen or _has_frozen_base_class(cls) + is_exc = auto_exc is True and issubclass(cls, BaseException) + has_own_setattr = auto_detect and _has_own_attribute( + cls, "__setattr__" + ) + + if has_own_setattr and is_frozen: + msg = "Can't freeze a class with a custom __setattr__." + raise ValueError(msg) + + eq = not is_exc and _determine_whether_to_implement( + cls, eq_, auto_detect, ("__eq__", "__ne__") + ) + + Hashability = ClassProps.Hashability + + if is_exc: + hashability = Hashability.LEAVE_ALONE + elif hash is True: + hashability = ( + Hashability.HASHABLE_CACHED + if cache_hash + else Hashability.HASHABLE + ) + elif hash is False: + hashability = Hashability.LEAVE_ALONE + elif hash is None: + if auto_detect is True and _has_own_attribute(cls, "__hash__"): + hashability = Hashability.LEAVE_ALONE + elif eq is True and is_frozen is True: + hashability = ( + Hashability.HASHABLE_CACHED + if cache_hash + else Hashability.HASHABLE + ) + elif eq is False: + hashability = Hashability.LEAVE_ALONE + else: + hashability = Hashability.UNHASHABLE + else: + msg = "Invalid value for hash. Must be True, False, or None." + raise TypeError(msg) + + KeywordOnly = ClassProps.KeywordOnly + if kw_only: + kwo = KeywordOnly.FORCE if force_kw_only else KeywordOnly.YES + else: + kwo = KeywordOnly.NO + + props = ClassProps( + is_exception=is_exc, + is_frozen=is_frozen, + is_slotted=slots, + collected_fields_by_mro=collect_by_mro, + added_init=_determine_whether_to_implement( + cls, init, auto_detect, ("__init__",) + ), + added_repr=_determine_whether_to_implement( + cls, repr, auto_detect, ("__repr__",) + ), + added_eq=eq, + added_ordering=not is_exc + and _determine_whether_to_implement( + cls, + order_, + auto_detect, + ("__lt__", "__le__", "__gt__", "__ge__"), + ), + hashability=hashability, + added_match_args=match_args, + kw_only=kwo, + has_weakref_slot=weakref_slot, + added_str=str, + added_pickling=_determine_whether_to_implement( + cls, + getstate_setstate, + auto_detect, + ("__getstate__", "__setstate__"), + default=slots, + ), + on_setattr_hook=on_setattr, + field_transformer=field_transformer, + ) + + if not props.is_hashable and cache_hash: + msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled." + raise TypeError(msg) + + builder = _ClassBuilder( + cls, + these, + auto_attribs=auto_attribs, + props=props, + has_custom_setattr=has_own_setattr, + ) + + if props.added_repr: + builder.add_repr(repr_ns) + + if props.added_str: + builder.add_str() + + if props.added_eq: + builder.add_eq() + if props.added_ordering: + builder.add_order() + + if not frozen: + builder.add_setattr() + + if props.is_hashable: + builder.add_hash() + elif props.hashability is Hashability.UNHASHABLE: + builder.make_unhashable() + + if props.added_init: + builder.add_init() + else: + builder.add_attrs_init() + if cache_hash: + msg = "Invalid value for cache_hash. To use hash caching, init must be True." + raise TypeError(msg) + + if PY_3_13_PLUS and not _has_own_attribute(cls, "__replace__"): + builder.add_replace() + + if ( + PY_3_10_PLUS + and match_args + and not _has_own_attribute(cls, "__match_args__") + ): + builder.add_match_args() + + return builder.build_class() + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but `None` if used as `@attrs()`. + if maybe_cls is None: + return wrap + + return wrap(maybe_cls) + + +_attrs = attrs +""" +Internal alias so we can use it in functions that take an argument called +*attrs*. +""" + + +def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return cls.__setattr__ is _frozen_setattrs + + +def _generate_unique_filename(cls: type, func_name: str) -> str: + """ + Create a "filename" suitable for a function being generated. + """ + return ( + f"" + ) + + +def _make_hash_script( + cls: type, attrs: list[Attribute], frozen: bool, cache_hash: bool +) -> tuple[str, dict]: + attrs = tuple( + a for a in attrs if a.hash is True or (a.hash is None and a.eq is True) + ) + + tab = " " + + type_hash = hash(_generate_unique_filename(cls, "hash")) + # If eq is custom generated, we need to include the functions in globs + globs = {} + + hash_def = "def __hash__(self" + hash_func = "hash((" + closing_braces = "))" + if not cache_hash: + hash_def += "):" + else: + hash_def += ", *" + + hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):" + hash_func = "_cache_wrapper(" + hash_func + closing_braces += ")" + + method_lines = [hash_def] + + def append_hash_computation_lines(prefix, indent): + """ + Generate the code for actually computing the hash code. + Below this will either be returned directly or used to compute + a value which is then cached, depending on the value of cache_hash + """ + + method_lines.extend( + [ + indent + prefix + hash_func, + indent + f" {type_hash},", + ] + ) + + for a in attrs: + if a.eq_key: + cmp_name = f"_{a.name}_key" + globs[cmp_name] = a.eq_key + method_lines.append( + indent + f" {cmp_name}(self.{a.name})," + ) + else: + method_lines.append(indent + f" self.{a.name},") + + method_lines.append(indent + " " + closing_braces) + + if cache_hash: + method_lines.append(tab + f"if self.{_HASH_CACHE_FIELD} is None:") + if frozen: + append_hash_computation_lines( + f"object.__setattr__(self, '{_HASH_CACHE_FIELD}', ", tab * 2 + ) + method_lines.append(tab * 2 + ")") # close __setattr__ + else: + append_hash_computation_lines( + f"self.{_HASH_CACHE_FIELD} = ", tab * 2 + ) + method_lines.append(tab + f"return self.{_HASH_CACHE_FIELD}") + else: + append_hash_computation_lines("return ", tab) + + script = "\n".join(method_lines) + return script, globs + + +def _add_hash(cls: type, attrs: list[Attribute]): + """ + Add a hash method to *cls*. + """ + script, globs = _make_hash_script( + cls, attrs, frozen=False, cache_hash=False + ) + _compile_and_eval( + script, globs, filename=_generate_unique_filename(cls, "__hash__") + ) + cls.__hash__ = globs["__hash__"] + return cls + + +def __ne__(self, other): + """ + Check equality and either forward a NotImplemented or + return the result negated. + """ + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + + return not result + + +def _make_eq_script(attrs: list) -> tuple[str, dict]: + """ + Create __eq__ method for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.eq] + + lines = [ + "def __eq__(self, other):", + " if other.__class__ is not self.__class__:", + " return NotImplemented", + ] + + globs = {} + if attrs: + lines.append(" return (") + for a in attrs: + if a.eq_key: + cmp_name = f"_{a.name}_key" + # Add the key function to the global namespace + # of the evaluated function. + globs[cmp_name] = a.eq_key + lines.append( + f" {cmp_name}(self.{a.name}) == {cmp_name}(other.{a.name})" + ) + else: + lines.append(f" self.{a.name} == other.{a.name}") + if a is not attrs[-1]: + lines[-1] = f"{lines[-1]} and" + lines.append(" )") + else: + lines.append(" return True") + + script = "\n".join(lines) + + return script, globs + + +def _make_order(cls, attrs): + """ + Create ordering methods for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.order] + + def attrs_to_tuple(obj): + """ + Save us some typing. + """ + return tuple( + key(value) if key else value + for value, key in ( + (getattr(obj, a.name), a.order_key) for a in attrs + ) + ) + + def __lt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) < attrs_to_tuple(other) + + return NotImplemented + + def __le__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) <= attrs_to_tuple(other) + + return NotImplemented + + def __gt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) > attrs_to_tuple(other) + + return NotImplemented + + def __ge__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) >= attrs_to_tuple(other) + + return NotImplemented + + return __lt__, __le__, __gt__, __ge__ + + +def _add_eq(cls, attrs=None): + """ + Add equality methods to *cls* with *attrs*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + script, globs = _make_eq_script(attrs) + _compile_and_eval( + script, globs, filename=_generate_unique_filename(cls, "__eq__") + ) + cls.__eq__ = globs["__eq__"] + cls.__ne__ = __ne__ + + return cls + + +def _make_repr_script(attrs, ns) -> tuple[str, dict]: + """ + Create the source and globs for a __repr__ and return it. + """ + # Figure out which attributes to include, and which function to use to + # format them. The a.repr value can be either bool or a custom + # callable. + attr_names_with_reprs = tuple( + (a.name, (repr if a.repr is True else a.repr), a.init) + for a in attrs + if a.repr is not False + ) + globs = { + name + "_repr": r for name, r, _ in attr_names_with_reprs if r != repr + } + globs["_compat"] = _compat + globs["AttributeError"] = AttributeError + globs["NOTHING"] = NOTHING + attribute_fragments = [] + for name, r, i in attr_names_with_reprs: + accessor = ( + "self." + name if i else 'getattr(self, "' + name + '", NOTHING)' + ) + fragment = ( + "%s={%s!r}" % (name, accessor) + if r == repr + else "%s={%s_repr(%s)}" % (name, name, accessor) + ) + attribute_fragments.append(fragment) + repr_fragment = ", ".join(attribute_fragments) + + if ns is None: + cls_name_fragment = '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}' + else: + cls_name_fragment = ns + ".{self.__class__.__name__}" + + lines = [ + "def __repr__(self):", + " try:", + " already_repring = _compat.repr_context.already_repring", + " except AttributeError:", + " already_repring = {id(self),}", + " _compat.repr_context.already_repring = already_repring", + " else:", + " if id(self) in already_repring:", + " return '...'", + " else:", + " already_repring.add(id(self))", + " try:", + f" return f'{cls_name_fragment}({repr_fragment})'", + " finally:", + " already_repring.remove(id(self))", + ] + + return "\n".join(lines), globs + + +def _add_repr(cls, ns=None, attrs=None): + """ + Add a repr method to *cls*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + script, globs = _make_repr_script(attrs, ns) + _compile_and_eval( + script, globs, filename=_generate_unique_filename(cls, "__repr__") + ) + cls.__repr__ = globs["__repr__"] + return cls + + +def fields(cls): + """ + Return the tuple of *attrs* attributes for a class or instance. + + The tuple also allows accessing the fields by their names (see below for + examples). + + Args: + cls (type): Class or instance to introspect. + + Raises: + TypeError: If *cls* is neither a class nor an *attrs* instance. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + Returns: + tuple (with name accessors) of `attrs.Attribute` + + .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields + by name. + .. versionchanged:: 23.1.0 Add support for generic classes. + .. versionchanged:: 26.1.0 Add support for instances. + """ + generic_base = get_generic_base(cls) + + if generic_base is None and not isinstance(cls, type): + type_ = type(cls) + if getattr(type_, "__attrs_attrs__", None) is None: + msg = "Passed object must be a class or attrs instance." + raise TypeError(msg) + + return fields(type_) + + attrs = getattr(cls, "__attrs_attrs__", None) + + if attrs is None: + if generic_base is not None: + attrs = getattr(generic_base, "__attrs_attrs__", None) + if attrs is not None: + # Even though this is global state, stick it on here to speed + # it up. We rely on `cls` being cached for this to be + # efficient. + cls.__attrs_attrs__ = attrs + return attrs + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) + + return attrs + + +def fields_dict(cls): + """ + Return an ordered dictionary of *attrs* attributes for a class, whose keys + are the attribute names. + + Args: + cls (type): Class to introspect. + + Raises: + TypeError: If *cls* is not a class. + + attrs.exceptions.NotAnAttrsClassError: + If *cls* is not an *attrs* class. + + Returns: + dict[str, attrs.Attribute]: Dict of attribute name to definition + + .. versionadded:: 18.1.0 + """ + if not isinstance(cls, type): + msg = "Passed object must be a class." + raise TypeError(msg) + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) + return {a.name: a for a in attrs} + + +def validate(inst): + """ + Validate all attributes on *inst* that have a validator. + + Leaves all exceptions through. + + Args: + inst: Instance of a class with *attrs* attributes. + """ + if _config._run_validators is False: + return + + for a in fields(inst.__class__): + v = a.validator + if v is not None: + v(inst, a, getattr(inst, a.name)) + + +def _is_slot_attr(a_name, base_attr_map): + """ + Check if the attribute name comes from a slot class. + """ + cls = base_attr_map.get(a_name) + return cls and "__slots__" in cls.__dict__ + + +def _make_init_script( + cls, + attrs, + pre_init, + pre_init_has_args, + post_init, + frozen, + slots, + cache_hash, + base_attr_map, + is_exc, + cls_on_setattr, + attrs_init, +) -> tuple[str, dict, dict]: + has_cls_on_setattr = ( + cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP + ) + + if frozen and has_cls_on_setattr: + msg = "Frozen classes can't use on_setattr." + raise ValueError(msg) + + needs_cached_setattr = cache_hash or frozen + filtered_attrs = [] + attr_dict = {} + for a in attrs: + if not a.init and a.default is NOTHING: + continue + + filtered_attrs.append(a) + attr_dict[a.name] = a + + if a.on_setattr is not None: + if frozen is True and a.on_setattr is not setters.NO_OP: + msg = "Frozen classes can't use on_setattr." + raise ValueError(msg) + + needs_cached_setattr = True + elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP: + needs_cached_setattr = True + + script, globs, annotations = _attrs_to_init_script( + filtered_attrs, + frozen, + slots, + pre_init, + pre_init_has_args, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_cls_on_setattr, + "__attrs_init__" if attrs_init else "__init__", + ) + if cls.__module__ in sys.modules: + # This makes typing.get_type_hints(CLS.__init__) resolve string types. + globs.update(sys.modules[cls.__module__].__dict__) + + globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) + + if needs_cached_setattr: + # Save the lookup overhead in __init__ if we need to circumvent + # setattr hooks. + globs["_cached_setattr_get"] = _OBJ_SETATTR.__get__ + + return script, globs, annotations + + +def _setattr(attr_name: str, value_var: str, has_on_setattr: bool) -> str: + """ + Use the cached object.setattr to set *attr_name* to *value_var*. + """ + return f"_setattr('{attr_name}', {value_var})" + + +def _setattr_with_converter( + attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter +) -> str: + """ + Use the cached object.setattr to set *attr_name* to *value_var*, but run + its converter first. + """ + return f"_setattr('{attr_name}', {converter._fmt_converter_call(attr_name, value_var)})" + + +def _assign(attr_name: str, value: str, has_on_setattr: bool) -> str: + """ + Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise + relegate to _setattr. + """ + if has_on_setattr: + return _setattr(attr_name, value, True) + + return f"self.{attr_name} = {value}" + + +def _assign_with_converter( + attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter +) -> str: + """ + Unless *attr_name* has an on_setattr hook, use normal assignment after + conversion. Otherwise relegate to _setattr_with_converter. + """ + if has_on_setattr: + return _setattr_with_converter(attr_name, value_var, True, converter) + + return f"self.{attr_name} = {converter._fmt_converter_call(attr_name, value_var)}" + + +def _determine_setters( + frozen: bool, slots: bool, base_attr_map: dict[str, type] +): + """ + Determine the correct setter functions based on whether a class is frozen + and/or slotted. + """ + if frozen is True: + if slots is True: + return (), _setattr, _setattr_with_converter + + # Dict frozen classes assign directly to __dict__. + # But only if the attribute doesn't come from an ancestor slot + # class. + # Note _inst_dict will be used again below if cache_hash is True + + def fmt_setter( + attr_name: str, value_var: str, has_on_setattr: bool + ) -> str: + if _is_slot_attr(attr_name, base_attr_map): + return _setattr(attr_name, value_var, has_on_setattr) + + return f"_inst_dict['{attr_name}'] = {value_var}" + + def fmt_setter_with_converter( + attr_name: str, + value_var: str, + has_on_setattr: bool, + converter: Converter, + ) -> str: + if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): + return _setattr_with_converter( + attr_name, value_var, has_on_setattr, converter + ) + + return f"_inst_dict['{attr_name}'] = {converter._fmt_converter_call(attr_name, value_var)}" + + return ( + ("_inst_dict = self.__dict__",), + fmt_setter, + fmt_setter_with_converter, + ) + + # Not frozen -- we can just assign directly. + return (), _assign, _assign_with_converter + + +def _attrs_to_init_script( + attrs: list[Attribute], + is_frozen: bool, + is_slotted: bool, + call_pre_init: bool, + pre_init_has_args: bool, + call_post_init: bool, + does_cache_hash: bool, + base_attr_map: dict[str, type], + is_exc: bool, + needs_cached_setattr: bool, + has_cls_on_setattr: bool, + method_name: str, +) -> tuple[str, dict, dict]: + """ + Return a script of an initializer for *attrs*, a dict of globals, and + annotations for the initializer. + + The globals are required by the generated script. + """ + lines = ["self.__attrs_pre_init__()"] if call_pre_init else [] + + if needs_cached_setattr: + lines.append( + # Circumvent the __setattr__ descriptor to save one lookup per + # assignment. Note _setattr will be used again below if + # does_cache_hash is True. + "_setattr = _cached_setattr_get(self)" + ) + + extra_lines, fmt_setter, fmt_setter_with_converter = _determine_setters( + is_frozen, is_slotted, base_attr_map + ) + lines.extend(extra_lines) + + args = [] # Parameters in the definition of __init__ + pre_init_args = [] # Parameters in the call to __attrs_pre_init__ + kw_only_args = [] # Used for both 'args' and 'pre_init_args' above + attrs_to_validate = [] + + # This is a dictionary of names to validator and converter callables. + # Injecting this into __init__ globals lets us avoid lookups. + names_for_globals = {} + annotations = {"return": None} + + for a in attrs: + if a.validator: + attrs_to_validate.append(a) + + attr_name = a.name + has_on_setattr = a.on_setattr is not None or ( + a.on_setattr is not setters.NO_OP and has_cls_on_setattr + ) + # a.alias is set to maybe-mangled attr_name in _ClassBuilder if not + # explicitly provided + arg_name = a.alias + + has_factory = isinstance(a.default, Factory) + maybe_self = "self" if has_factory and a.default.takes_self else "" + + if a.converter is not None and not isinstance(a.converter, Converter): + converter = Converter(a.converter) + else: + converter = a.converter + + if a.init is False: + if has_factory: + init_factory_name = _INIT_FACTORY_PAT % (a.name,) + if converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + init_factory_name + f"({maybe_self})", + has_on_setattr, + converter, + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append( + fmt_setter( + attr_name, + init_factory_name + f"({maybe_self})", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + elif converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + f"attr_dict['{attr_name}'].default", + has_on_setattr, + converter, + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append( + fmt_setter( + attr_name, + f"attr_dict['{attr_name}'].default", + has_on_setattr, + ) + ) + elif a.default is not NOTHING and not has_factory: + arg = f"{arg_name}=attr_dict['{attr_name}'].default" + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + pre_init_args.append(arg_name) + + if converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr, converter + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + elif has_factory: + arg = f"{arg_name}=NOTHING" + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + pre_init_args.append(arg_name) + lines.append(f"if {arg_name} is not NOTHING:") + + init_factory_name = _INIT_FACTORY_PAT % (a.name,) + if converter is not None: + lines.append( + " " + + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr, converter + ) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter_with_converter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + converter, + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append( + " " + fmt_setter(attr_name, arg_name, has_on_setattr) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.kw_only: + kw_only_args.append(arg_name) + else: + args.append(arg_name) + pre_init_args.append(arg_name) + + if converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr, converter + ) + ) + names_for_globals[converter._get_global_name(a.name)] = ( + converter.converter + ) + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + if a.init is True: + if a.type is not None and converter is None: + annotations[arg_name] = a.type + elif converter is not None and converter._first_param_type: + # Use the type from the converter if present. + annotations[arg_name] = converter._first_param_type + + if attrs_to_validate: # we can skip this if there are no validators. + names_for_globals["_config"] = _config + lines.append("if _config._run_validators is True:") + for a in attrs_to_validate: + val_name = "__attr_validator_" + a.name + attr_name = "__attr_" + a.name + lines.append(f" {val_name}(self, {attr_name}, self.{a.name})") + names_for_globals[val_name] = a.validator + names_for_globals[attr_name] = a + + if call_post_init: + lines.append("self.__attrs_post_init__()") + + # Because this is set only after __attrs_post_init__ is called, a crash + # will result if post-init tries to access the hash code. This seemed + # preferable to setting this beforehand, in which case alteration to field + # values during post-init combined with post-init accessing the hash code + # would result in silent bugs. + if does_cache_hash: + if is_frozen: + if is_slotted: + init_hash_cache = f"_setattr('{_HASH_CACHE_FIELD}', None)" + else: + init_hash_cache = f"_inst_dict['{_HASH_CACHE_FIELD}'] = None" + else: + init_hash_cache = f"self.{_HASH_CACHE_FIELD} = None" + lines.append(init_hash_cache) + + # For exceptions we rely on BaseException.__init__ for proper + # initialization. + if is_exc: + vals = ",".join(f"self.{a.name}" for a in attrs if a.init) + + lines.append(f"BaseException.__init__(self, {vals})") + + args = ", ".join(args) + pre_init_args = ", ".join(pre_init_args) + if kw_only_args: + # leading comma & kw_only args + args += f"{', ' if args else ''}*, {', '.join(kw_only_args)}" + pre_init_kw_only_args = ", ".join( + [ + f"{kw_arg_name}={kw_arg_name}" + # We need to remove the defaults from the kw_only_args. + for kw_arg_name in (kwa.split("=")[0] for kwa in kw_only_args) + ] + ) + pre_init_args += ", " if pre_init_args else "" + pre_init_args += pre_init_kw_only_args + + if call_pre_init and pre_init_has_args: + # If pre init method has arguments, pass the values given to __init__. + lines[0] = f"self.__attrs_pre_init__({pre_init_args})" + + # Python <3.12 doesn't allow backslashes in f-strings. + NL = "\n " + return ( + f"""def {method_name}(self, {args}): + {NL.join(lines) if lines else "pass"} +""", + names_for_globals, + annotations, + ) + + +def _default_init_alias_for(name: str) -> str: + """ + The default __init__ parameter name for a field. + + This performs private-name adjustment via leading-unscore stripping, + and is the default value of Attribute.alias if not provided. + """ + + return name.lstrip("_") + + +class Attribute: + """ + *Read-only* representation of an attribute. + + .. warning:: + + You should never instantiate this class yourself. + + The class has *all* arguments of `attr.ib` (except for ``factory`` which is + only syntactic sugar for ``default=Factory(...)`` plus the following: + + - ``name`` (`str`): The name of the attribute. + - ``alias`` (`str`): The __init__ parameter name of the attribute, after + any explicit overrides and default private-attribute-name handling. + - ``alias_is_default`` (`bool`): Whether the ``alias`` was automatically + generated (``True``) or explicitly provided by the user (``False``). + - ``inherited`` (`bool`): Whether or not that attribute has been inherited + from a base class. + - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The + callables that are used for comparing and ordering objects by this + attribute, respectively. These are set by passing a callable to + `attr.ib`'s ``eq``, ``order``, or ``cmp`` arguments. See also + :ref:`comparison customization `. + + Instances of this class are frequently used for introspection purposes + like: + + - `fields` returns a tuple of them. + - Validators get them passed as the first argument. + - The :ref:`field transformer ` hook receives a list of + them. + - The ``alias`` property exposes the __init__ parameter name of the field, + with any overrides and default private-attribute handling applied. + + + .. versionadded:: 20.1.0 *inherited* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.2.0 *inherited* is not taken into account for + equality checks and hashing anymore. + .. versionadded:: 21.1.0 *eq_key* and *order_key* + .. versionadded:: 22.2.0 *alias* + .. versionadded:: 26.1.0 *alias_is_default* + + For the full version history of the fields, see `attr.ib`. + """ + + # These slots must NOT be reordered because we use them later for + # instantiation. + __slots__ = ( # noqa: RUF023 + "name", + "default", + "validator", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "type", + "converter", + "kw_only", + "inherited", + "on_setattr", + "alias", + "alias_is_default", + ) + + def __init__( + self, + name, + default, + validator, + repr, + cmp, # XXX: unused, remove along with other cmp code. + hash, + init, + inherited, + metadata=None, + type=None, + converter=None, + kw_only=False, + eq=None, + eq_key=None, + order=None, + order_key=None, + on_setattr=None, + alias=None, + alias_is_default=None, + ): + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq_key or eq, order_key or order, True + ) + + # Cache this descriptor here to speed things up later. + bound_setattr = _OBJ_SETATTR.__get__(self) + + # Despite the big red warning, people *do* instantiate `Attribute` + # themselves. + bound_setattr("name", name) + bound_setattr("default", default) + bound_setattr("validator", validator) + bound_setattr("repr", repr) + bound_setattr("eq", eq) + bound_setattr("eq_key", eq_key) + bound_setattr("order", order) + bound_setattr("order_key", order_key) + bound_setattr("hash", hash) + bound_setattr("init", init) + bound_setattr("converter", converter) + bound_setattr( + "metadata", + ( + types.MappingProxyType(dict(metadata)) # Shallow copy + if metadata + else _EMPTY_METADATA_SINGLETON + ), + ) + bound_setattr("type", type) + bound_setattr("kw_only", kw_only) + bound_setattr("inherited", inherited) + bound_setattr("on_setattr", on_setattr) + bound_setattr("alias", alias) + bound_setattr( + "alias_is_default", + alias is None if alias_is_default is None else alias_is_default, + ) + + def __setattr__(self, name, value): + raise FrozenInstanceError + + @classmethod + def from_counting_attr( + cls, name: str, ca: _CountingAttr, kw_only: bool, type=None + ): + # The 'kw_only' argument is the class-level setting, and is used if the + # attribute itself does not explicitly set 'kw_only'. + # type holds the annotated value. deal with conflicts: + if type is None: + type = ca.type + elif ca.type is not None: + msg = f"Type annotation and type argument cannot both be present for '{name}'." + raise ValueError(msg) + return cls( + name, + ca._default, + ca._validator, + ca.repr, + None, + ca.hash, + ca.init, + False, + ca.metadata, + type, + ca.converter, + kw_only if ca.kw_only is None else ca.kw_only, + ca.eq, + ca.eq_key, + ca.order, + ca.order_key, + ca.on_setattr, + ca.alias, + ca.alias is None, + ) + + # Don't use attrs.evolve since fields(Attribute) doesn't work + def evolve(self, **changes): + """ + Copy *self* and apply *changes*. + + This works similarly to `attrs.evolve` but that function does not work + with :class:`attrs.Attribute`. + + It is mainly meant to be used for `transform-fields`. + + .. versionadded:: 20.3.0 + """ + new = copy.copy(self) + + new._setattrs(changes.items()) + + if "alias" in changes and "alias_is_default" not in changes: + # Explicit alias provided -- no longer the default. + _OBJ_SETATTR.__get__(new)("alias_is_default", False) + elif ( + "name" in changes + and "alias" not in changes + # Don't auto-generate alias if the user picked picked the old one. + and self.alias_is_default + ): + # Name changed, alias was auto-generated -- update it. + _OBJ_SETATTR.__get__(new)( + "alias", _default_init_alias_for(new.name) + ) + + return new + + # Don't use _add_pickle since fields(Attribute) doesn't work + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple( + getattr(self, name) if name != "metadata" else dict(self.metadata) + for name in self.__slots__ + ) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + if len(state) < len(self.__slots__): + # Pre-26.1.0 pickle without alias_is_default -- infer it + # heuristically. + state_dict = dict(zip(self.__slots__, state)) + alias_is_default = state_dict.get( + "alias" + ) is None or state_dict.get("alias") == _default_init_alias_for( + state_dict["name"] + ) + state = (*state, alias_is_default) + + self._setattrs(zip(self.__slots__, state)) + + def _setattrs(self, name_values_pairs): + bound_setattr = _OBJ_SETATTR.__get__(self) + for name, value in name_values_pairs: + if name != "metadata": + bound_setattr(name, value) + else: + bound_setattr( + name, + ( + types.MappingProxyType(dict(value)) + if value + else _EMPTY_METADATA_SINGLETON + ), + ) + + +_a = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=(name != "alias_is_default"), + cmp=None, + eq=True, + order=False, + hash=(name != "metadata"), + init=True, + inherited=False, + alias=_default_init_alias_for(name), + ) + for name in Attribute.__slots__ +] + +Attribute = _add_hash( + _add_eq( + _add_repr(Attribute, attrs=_a), + attrs=[a for a in _a if a.name != "inherited"], + ), + attrs=[a for a in _a if a.hash and a.name != "inherited"], +) + + +class _CountingAttr: + """ + Intermediate representation of attributes that uses a counter to preserve + the order in which the attributes have been defined. + + *Internal* data structure of the attrs library. Running into is most + likely the result of a bug like a forgotten `@attr.s` decorator. + """ + + __slots__ = ( + "_default", + "_validator", + "alias", + "converter", + "counter", + "eq", + "eq_key", + "hash", + "init", + "kw_only", + "metadata", + "on_setattr", + "order", + "order_key", + "repr", + "type", + ) + __attrs_attrs__ = ( + *tuple( + Attribute( + name=name, + alias=_default_init_alias_for(name), + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=True, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ) + for name in ( + "counter", + "_default", + "repr", + "eq", + "order", + "hash", + "init", + "on_setattr", + "alias", + ) + ), + Attribute( + name="metadata", + alias="metadata", + default=None, + validator=None, + repr=True, + cmp=None, + hash=False, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ), + ) + cls_counter = 0 + + def __init__( + self, + default, + validator, + repr, + cmp, + hash, + init, + converter, + metadata, + type, + kw_only, + eq, + eq_key, + order, + order_key, + on_setattr, + alias, + ): + _CountingAttr.cls_counter += 1 + self.counter = _CountingAttr.cls_counter + self._default = default + self._validator = validator + self.converter = converter + self.repr = repr + self.eq = eq + self.eq_key = eq_key + self.order = order + self.order_key = order_key + self.hash = hash + self.init = init + self.metadata = metadata + self.type = type + self.kw_only = kw_only + self.on_setattr = on_setattr + self.alias = alias + + def validator(self, meth): + """ + Decorator that adds *meth* to the list of validators. + + Returns *meth* unchanged. + + .. versionadded:: 17.1.0 + """ + if self._validator is None: + self._validator = meth + else: + self._validator = and_(self._validator, meth) + return meth + + def default(self, meth): + """ + Decorator that allows to set the default for an attribute. + + Returns *meth* unchanged. + + Raises: + DefaultAlreadySetError: If default has been set before. + + .. versionadded:: 17.1.0 + """ + if self._default is not NOTHING: + raise DefaultAlreadySetError + + self._default = Factory(meth, takes_self=True) + + return meth + + +_CountingAttr = _add_eq(_add_repr(_CountingAttr)) + + +class ClassProps: + """ + Effective class properties as derived from parameters to `attr.s()` or + `define()` decorators. + + This is the same data structure that *attrs* uses internally to decide how + to construct the final class. + + Warning: + + This feature is currently **experimental** and is not covered by our + strict backwards-compatibility guarantees. + + + Attributes: + is_exception (bool): + Whether the class is treated as an exception class. + + is_slotted (bool): + Whether the class is `slotted `. + + has_weakref_slot (bool): + Whether the class has a slot for weak references. + + is_frozen (bool): + Whether the class is frozen. + + kw_only (KeywordOnly): + Whether / how the class enforces keyword-only arguments on the + ``__init__`` method. + + collected_fields_by_mro (bool): + Whether the class fields were collected by method resolution order. + That is, correctly but unlike `dataclasses`. + + added_init (bool): + Whether the class has an *attrs*-generated ``__init__`` method. + + added_repr (bool): + Whether the class has an *attrs*-generated ``__repr__`` method. + + added_eq (bool): + Whether the class has *attrs*-generated equality methods. + + added_ordering (bool): + Whether the class has *attrs*-generated ordering methods. + + hashability (Hashability): How `hashable ` the class is. + + added_match_args (bool): + Whether the class supports positional `match ` over its + fields. + + added_str (bool): + Whether the class has an *attrs*-generated ``__str__`` method. + + added_pickling (bool): + Whether the class has *attrs*-generated ``__getstate__`` and + ``__setstate__`` methods for `pickle`. + + on_setattr_hook (Callable[[Any, Attribute[Any], Any], Any] | None): + The class's ``__setattr__`` hook. + + field_transformer (Callable[[Attribute[Any]], Attribute[Any]] | None): + The class's `field transformers `. + + .. versionadded:: 25.4.0 + """ + + class Hashability(enum.Enum): + """ + The hashability of a class. + + .. versionadded:: 25.4.0 + """ + + HASHABLE = "hashable" + """Write a ``__hash__``.""" + HASHABLE_CACHED = "hashable_cache" + """Write a ``__hash__`` and cache the hash.""" + UNHASHABLE = "unhashable" + """Set ``__hash__`` to ``None``.""" + LEAVE_ALONE = "leave_alone" + """Don't touch ``__hash__``.""" + + class KeywordOnly(enum.Enum): + """ + How attributes should be treated regarding keyword-only parameters. + + .. versionadded:: 25.4.0 + """ + + NO = "no" + """Attributes are not keyword-only.""" + YES = "yes" + """Attributes in current class without kw_only=False are keyword-only.""" + FORCE = "force" + """All attributes are keyword-only.""" + + __slots__ = ( # noqa: RUF023 -- order matters for __init__ + "is_exception", + "is_slotted", + "has_weakref_slot", + "is_frozen", + "kw_only", + "collected_fields_by_mro", + "added_init", + "added_repr", + "added_eq", + "added_ordering", + "hashability", + "added_match_args", + "added_str", + "added_pickling", + "on_setattr_hook", + "field_transformer", + ) + + def __init__( + self, + is_exception, + is_slotted, + has_weakref_slot, + is_frozen, + kw_only, + collected_fields_by_mro, + added_init, + added_repr, + added_eq, + added_ordering, + hashability, + added_match_args, + added_str, + added_pickling, + on_setattr_hook, + field_transformer, + ): + self.is_exception = is_exception + self.is_slotted = is_slotted + self.has_weakref_slot = has_weakref_slot + self.is_frozen = is_frozen + self.kw_only = kw_only + self.collected_fields_by_mro = collected_fields_by_mro + self.added_init = added_init + self.added_repr = added_repr + self.added_eq = added_eq + self.added_ordering = added_ordering + self.hashability = hashability + self.added_match_args = added_match_args + self.added_str = added_str + self.added_pickling = added_pickling + self.on_setattr_hook = on_setattr_hook + self.field_transformer = field_transformer + + @property + def is_hashable(self): + return ( + self.hashability is ClassProps.Hashability.HASHABLE + or self.hashability is ClassProps.Hashability.HASHABLE_CACHED + ) + + +_cas = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + alias=_default_init_alias_for(name), + ) + for name in ClassProps.__slots__ +] + +ClassProps = _add_eq(_add_repr(ClassProps, attrs=_cas), attrs=_cas) + + +class Factory: + """ + Stores a factory callable. + + If passed as the default value to `attrs.field`, the factory is used to + generate a new value. + + Args: + factory (typing.Callable): + A callable that takes either none or exactly one mandatory + positional argument depending on *takes_self*. + + takes_self (bool): + Pass the partially initialized instance that is being initialized + as a positional argument. + + .. versionadded:: 17.1.0 *takes_self* + """ + + __slots__ = ("factory", "takes_self") + + def __init__(self, factory, takes_self=False): + self.factory = factory + self.takes_self = takes_self + + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple(getattr(self, name) for name in self.__slots__) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + for name, value in zip(self.__slots__, state): + setattr(self, name, value) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in Factory.__slots__ +] + +Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) + + +class Converter: + """ + Stores a converter callable. + + Allows for the wrapped converter to take additional arguments. The + arguments are passed in the order they are documented. + + Args: + converter (Callable): A callable that converts the passed value. + + takes_self (bool): + Pass the partially initialized instance that is being initialized + as a positional argument. (default: `False`) + + takes_field (bool): + Pass the field definition (an :class:`Attribute`) into the + converter as a positional argument. (default: `False`) + + .. versionadded:: 24.1.0 + """ + + __slots__ = ( + "__call__", + "_first_param_type", + "_global_name", + "converter", + "takes_field", + "takes_self", + ) + + def __init__(self, converter, *, takes_self=False, takes_field=False): + self.converter = converter + self.takes_self = takes_self + self.takes_field = takes_field + + ex = _AnnotationExtractor(converter) + self._first_param_type = ex.get_first_param_type() + + if not (self.takes_self or self.takes_field): + self.__call__ = lambda value, _, __: self.converter(value) + elif self.takes_self and not self.takes_field: + self.__call__ = lambda value, instance, __: self.converter( + value, instance + ) + elif not self.takes_self and self.takes_field: + self.__call__ = lambda value, __, field: self.converter( + value, field + ) + else: + self.__call__ = self.converter + + rt = ex.get_return_type() + if rt is not None: + self.__call__.__annotations__["return"] = rt + + @staticmethod + def _get_global_name(attr_name: str) -> str: + """ + Return the name that a converter for an attribute name *attr_name* + would have. + """ + return f"__attr_converter_{attr_name}" + + def _fmt_converter_call(self, attr_name: str, value_var: str) -> str: + """ + Return a string that calls the converter for an attribute name + *attr_name* and the value in variable named *value_var* according to + `self.takes_self` and `self.takes_field`. + """ + if not (self.takes_self or self.takes_field): + return f"{self._get_global_name(attr_name)}({value_var})" + + if self.takes_self and self.takes_field: + return f"{self._get_global_name(attr_name)}({value_var}, self, attr_dict['{attr_name}'])" + + if self.takes_self: + return f"{self._get_global_name(attr_name)}({value_var}, self)" + + return f"{self._get_global_name(attr_name)}({value_var}, attr_dict['{attr_name}'])" + + def __getstate__(self): + """ + Return a dict containing only converter and takes_self -- the rest gets + computed when loading. + """ + return { + "converter": self.converter, + "takes_self": self.takes_self, + "takes_field": self.takes_field, + } + + def __setstate__(self, state): + """ + Load instance from state. + """ + self.__init__(**state) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in ("converter", "takes_self", "takes_field") +] + +Converter = _add_hash( + _add_eq(_add_repr(Converter, attrs=_f), attrs=_f), attrs=_f +) + + +def make_class( + name, attrs, bases=(object,), class_body=None, **attributes_arguments +): + r""" + A quick way to create a new class called *name* with *attrs*. + + .. note:: + + ``make_class()`` is a thin wrapper around `attr.s`, not `attrs.define` + which means that it doesn't come with some of the improved defaults. + + For example, if you want the same ``on_setattr`` behavior as in + `attrs.define`, you have to pass the hooks yourself: ``make_class(..., + on_setattr=setters.pipe(setters.convert, setters.validate)`` + + .. warning:: + + It is *your* duty to ensure that the class name and the attribute names + are valid identifiers. ``make_class()`` will *not* validate them for + you. + + Args: + name (str): The name for the new class. + + attrs (list | dict): + A list of names or a dictionary of mappings of names to `attr.ib`\ + s / `attrs.field`\ s. + + The order is deduced from the order of the names or attributes + inside *attrs*. Otherwise the order of the definition of the + attributes is used. + + bases (tuple[type, ...]): Classes that the new class will subclass. + + class_body (dict): + An optional dictionary of class attributes for the new class. + + attributes_arguments: Passed unmodified to `attr.s`. + + Returns: + type: A new class with *attrs*. + + .. versionadded:: 17.1.0 *bases* + .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained. + .. versionchanged:: 23.2.0 *class_body* + .. versionchanged:: 25.2.0 Class names can now be unicode. + """ + # Class identifiers are converted into the normal form NFKC while parsing + name = unicodedata.normalize("NFKC", name) + + if isinstance(attrs, dict): + cls_dict = attrs + elif isinstance(attrs, (list, tuple)): + cls_dict = {a: attrib() for a in attrs} + else: + msg = "attrs argument must be a dict or a list." + raise TypeError(msg) + + pre_init = cls_dict.pop("__attrs_pre_init__", None) + post_init = cls_dict.pop("__attrs_post_init__", None) + user_init = cls_dict.pop("__init__", None) + + body = {} + if class_body is not None: + body.update(class_body) + if pre_init is not None: + body["__attrs_pre_init__"] = pre_init + if post_init is not None: + body["__attrs_post_init__"] = post_init + if user_init is not None: + body["__init__"] = user_init + + type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body)) + + # For pickling to work, the __module__ variable needs to be set to the + # frame where the class is created. Bypass this step in environments where + # sys._getframe is not defined (Jython for example) or sys._getframe is not + # defined for arguments greater than 0 (IronPython). + with contextlib.suppress(AttributeError, ValueError): + type_.__module__ = sys._getframe(1).f_globals.get( + "__name__", "__main__" + ) + + # We do it here for proper warnings with meaningful stacklevel. + cmp = attributes_arguments.pop("cmp", None) + ( + attributes_arguments["eq"], + attributes_arguments["order"], + ) = _determine_attrs_eq_order( + cmp, + attributes_arguments.get("eq"), + attributes_arguments.get("order"), + True, + ) + + cls = _attrs(these=cls_dict, **attributes_arguments)(type_) + # Only add type annotations now or "_attrs()" will complain: + cls.__annotations__ = { + k: v.type for k, v in cls_dict.items() if v.type is not None + } + return cls + + +# These are required by within this module so we define them here and merely +# import into .validators / .converters. + + +@attrs(slots=True, unsafe_hash=True) +class _AndValidator: + """ + Compose many validators to a single one. + """ + + _validators = attrib() + + def __call__(self, inst, attr, value): + for v in self._validators: + v(inst, attr, value) + + +def and_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators. + + Args: + validators (~collections.abc.Iterable[typing.Callable]): + Arbitrary number of validators. + + .. versionadded:: 17.1.0 + """ + vals = [] + for validator in validators: + vals.extend( + validator._validators + if isinstance(validator, _AndValidator) + else [validator] + ) + + return _AndValidator(tuple(vals)) + + +def pipe(*converters): + """ + A converter that composes multiple converters into one. + + When called on a value, it runs all wrapped converters, returning the + *last* value. + + Type annotations will be inferred from the wrapped converters', if they + have any. + + converters (~collections.abc.Iterable[typing.Callable]): + Arbitrary number of converters. + + .. versionadded:: 20.1.0 + """ + + return_instance = any(isinstance(c, Converter) for c in converters) + + if return_instance: + + def pipe_converter(val, inst, field): + for c in converters: + val = ( + c(val, inst, field) if isinstance(c, Converter) else c(val) + ) + + return val + + else: + + def pipe_converter(val): + for c in converters: + val = c(val) + + return val + + if not converters: + # If the converter list is empty, pipe_converter is the identity. + A = TypeVar("A") + pipe_converter.__annotations__.update({"val": A, "return": A}) + else: + # Get parameter type from first converter. + t = _AnnotationExtractor(converters[0]).get_first_param_type() + if t: + pipe_converter.__annotations__["val"] = t + + last = converters[-1] + if not PY_3_11_PLUS and isinstance(last, Converter): + last = last.__call__ + + # Get return type from last converter. + rt = _AnnotationExtractor(last).get_return_type() + if rt: + pipe_converter.__annotations__["return"] = rt + + if return_instance: + return Converter(pipe_converter, takes_self=True, takes_field=True) + return pipe_converter diff --git a/venv/lib/python3.12/site-packages/attr/_next_gen.py b/venv/lib/python3.12/site-packages/attr/_next_gen.py new file mode 100644 index 00000000..4ccd0da2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_next_gen.py @@ -0,0 +1,674 @@ +# SPDX-License-Identifier: MIT + +""" +These are keyword-only APIs that call `attr.s` and `attr.ib` with different +default values. +""" + +from functools import partial + +from . import setters +from ._funcs import asdict as _asdict +from ._funcs import astuple as _astuple +from ._make import ( + _DEFAULT_ON_SETATTR, + NOTHING, + _frozen_setattrs, + attrib, + attrs, +) +from .exceptions import NotAnAttrsClassError, UnannotatedAttributeError + + +def define( + maybe_cls=None, + *, + these=None, + repr=None, + unsafe_hash=None, + hash=None, + init=None, + slots=True, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=None, + kw_only=False, + cache_hash=False, + auto_exc=True, + eq=None, + order=False, + auto_detect=True, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, + force_kw_only=False, +): + r""" + A class decorator that adds :term:`dunder methods` according to + :term:`fields ` specified using :doc:`type annotations `, + `field()` calls, or the *these* argument. + + Since *attrs* patches or replaces an existing class, you cannot use + `object.__init_subclass__` with *attrs* classes, because it runs too early. + As a replacement, you can define ``__attrs_init_subclass__`` on your class. + It will be called by *attrs* classes that subclass it after they're + created. See also :ref:`init-subclass`. + + Args: + slots (bool): + Create a :term:`slotted class ` that's more + memory-efficient. Slotted classes are generally superior to the + default dict classes, but have some gotchas you should know about, + so we encourage you to read the :term:`glossary entry `. + + auto_detect (bool): + Instead of setting the *init*, *repr*, *eq*, and *hash* arguments + explicitly, assume they are set to True **unless any** of the + involved methods for one of the arguments is implemented in the + *current* class (meaning, it is *not* inherited from some base + class). + + So, for example by implementing ``__eq__`` on a class yourself, + *attrs* will deduce ``eq=False`` and will create *neither* + ``__eq__`` *nor* ``__ne__`` (but Python classes come with a + sensible ``__ne__`` by default, so it *should* be enough to only + implement ``__eq__`` in most cases). + + Passing :data:`True` or :data:`False` to *init*, *repr*, *eq*, or *hash* + overrides whatever *auto_detect* would determine. + + auto_exc (bool): + If the class subclasses `BaseException` (which implicitly includes + any subclass of any exception), the following happens to behave + like a well-behaved Python exception class: + + - the values for *eq*, *order*, and *hash* are ignored and the + instances compare and hash by the instance's ids [#]_ , + - all attributes that are either passed into ``__init__`` or have a + default value are additionally available as a tuple in the + ``args`` attribute, + - the value of *str* is ignored leaving ``__str__`` to base + classes. + + .. [#] + Note that *attrs* will *not* remove existing implementations of + ``__hash__`` or the equality methods. It just won't add own + ones. + + on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): + A callable that is run whenever the user attempts to set an + attribute (either by assignment like ``i.x = 42`` or by using + `setattr` like ``setattr(i, "x", 42)``). It receives the same + arguments as validators: the instance, the attribute that is being + modified, and the new value. + + If no exception is raised, the attribute is set to the return value + of the callable. + + If a list of callables is passed, they're automatically wrapped in + an `attrs.setters.pipe`. + + If left None, the default behavior is to run converters and + validators whenever an attribute is set. + + init (bool): + Create a ``__init__`` method that initializes the *attrs* + attributes. Leading underscores are stripped for the argument name, + unless an alias is set on the attribute. + + .. seealso:: + `init` shows advanced ways to customize the generated + ``__init__`` method, including executing code before and after. + + repr(bool): + Create a ``__repr__`` method with a human readable representation + of *attrs* attributes. + + str (bool): + Create a ``__str__`` method that is identical to ``__repr__``. This + is usually not necessary except for `Exception`\ s. + + eq (bool | None): + If True or None (default), add ``__eq__`` and ``__ne__`` methods + that check two instances for equality. + + .. seealso:: + `comparison` describes how to customize the comparison behavior + going as far comparing NumPy arrays. + + order (bool | None): + If True, add ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` + methods that behave like *eq* above and allow instances to be + ordered. + + They compare the instances as if they were tuples of their *attrs* + attributes if and only if the types of both classes are + *identical*. + + If `None` mirror value of *eq*. + + .. seealso:: `comparison` + + unsafe_hash (bool | None): + If None (default), the ``__hash__`` method is generated according + how *eq* and *frozen* are set. + + 1. If *both* are True, *attrs* will generate a ``__hash__`` for + you. + 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set + to None, marking it unhashable (which it is). + 3. If *eq* is False, ``__hash__`` will be left untouched meaning + the ``__hash__`` method of the base class will be used. If the + base class is `object`, this means it will fall back to id-based + hashing. + + Although not recommended, you can decide for yourself and force + *attrs* to create one (for example, if the class is immutable even + though you didn't freeze it programmatically) by passing True or + not. Both of these cases are rather special and should be used + carefully. + + .. seealso:: + + - Our documentation on `hashing`, + - Python's documentation on `object.__hash__`, + - and the `GitHub issue that led to the default \ behavior + `_ for more + details. + + hash (bool | None): + Deprecated alias for *unsafe_hash*. *unsafe_hash* takes precedence. + + cache_hash (bool): + Ensure that the object's hash code is computed only once and stored + on the object. If this is set to True, hashing must be either + explicitly or implicitly enabled for this class. If the hash code + is cached, avoid any reassignments of fields involved in hash code + computation or mutations of the objects those fields point to after + object creation. If such changes occur, the behavior of the + object's hash code is undefined. + + frozen (bool): + Make instances immutable after initialization. If someone attempts + to modify a frozen instance, `attrs.exceptions.FrozenInstanceError` + is raised. + + .. note:: + + 1. This is achieved by installing a custom ``__setattr__`` + method on your class, so you can't implement your own. + + 2. True immutability is impossible in Python. + + 3. This *does* have a minor a runtime performance `impact + ` when initializing new instances. In other + words: ``__init__`` is slightly slower with ``frozen=True``. + + 4. If a class is frozen, you cannot modify ``self`` in + ``__attrs_post_init__`` or a self-written ``__init__``. You + can circumvent that limitation by using + ``object.__setattr__(self, "attribute_name", value)``. + + 5. Subclasses of a frozen class are frozen too. + + kw_only (bool): + Make attributes keyword-only in the generated ``__init__`` (if + *init* is False, this parameter is ignored). Attributes that + explicitly set ``kw_only=False`` are not affected; base class + attributes are also not affected. + + Also see *force_kw_only*. + + weakref_slot (bool): + Make instances weak-referenceable. This has no effect unless + *slots* is True. + + field_transformer (~typing.Callable | None): + A function that is called with the original class object and all + fields right before *attrs* finalizes the class. You can use this, + for example, to automatically add converters or validators to + fields based on their types. + + .. seealso:: `transform-fields` + + match_args (bool): + If True (default), set ``__match_args__`` on the class to support + :pep:`634` (*Structural Pattern Matching*). It is a tuple of all + non-keyword-only ``__init__`` parameter names on Python 3.10 and + later. Ignored on older Python versions. + + collect_by_mro (bool): + If True, *attrs* collects attributes from base classes correctly + according to the `method resolution order + `_. If False, *attrs* + will mimic the (wrong) behavior of `dataclasses` and :pep:`681`. + + See also `issue #428 + `_. + + force_kw_only (bool): + A back-compat flag for restoring pre-25.4.0 behavior. If True and + ``kw_only=True``, all attributes are made keyword-only, including + base class attributes, and those set to ``kw_only=False`` at the + attribute level. Defaults to False. + + See also `issue #980 + `_. + + getstate_setstate (bool | None): + .. note:: + + This is usually only interesting for slotted classes and you + should probably just set *auto_detect* to True. + + If True, ``__getstate__`` and ``__setstate__`` are generated and + attached to the class. This is necessary for slotted classes to be + pickleable. If left None, it's True by default for slotted classes + and False for dict classes. + + If *auto_detect* is True, and *getstate_setstate* is left None, and + **either** ``__getstate__`` or ``__setstate__`` is detected + directly on the class (meaning: not inherited), it is set to False + (this is usually what you want). + + auto_attribs (bool | None): + If True, look at type annotations to determine which attributes to + use, like `dataclasses`. If False, it will only look for explicit + :func:`field` class attributes, like classic *attrs*. + + If left None, it will guess: + + 1. If any attributes are annotated and no unannotated + `attrs.field`\ s are found, it assumes *auto_attribs=True*. + 2. Otherwise it assumes *auto_attribs=False* and tries to collect + `attrs.field`\ s. + + If *attrs* decides to look at type annotations, **all** fields + **must** be annotated. If *attrs* encounters a field that is set to + a :func:`field` / `attr.ib` but lacks a type annotation, an + `attrs.exceptions.UnannotatedAttributeError` is raised. Use + ``field_name: typing.Any = field(...)`` if you don't want to set a + type. + + .. warning:: + + For features that use the attribute name to create decorators + (for example, :ref:`validators `), you still *must* + assign :func:`field` / `attr.ib` to them. Otherwise Python will + either not find the name or try to use the default value to + call, for example, ``validator`` on it. + + Attributes annotated as `typing.ClassVar`, and attributes that are + neither annotated nor set to an `field()` are **ignored**. + + these (dict[str, object]): + A dictionary of name to the (private) return value of `field()` + mappings. This is useful to avoid the definition of your attributes + within the class body because you can't (for example, if you want + to add ``__repr__`` methods to Django models) or don't want to. + + If *these* is not `None`, *attrs* will *not* search the class body + for attributes and will *not* remove any attributes from it. + + The order is deduced from the order of the attributes inside + *these*. + + Arguably, this is a rather obscure feature. + + .. versionadded:: 20.1.0 + .. versionchanged:: 21.3.0 Converters are also run ``on_setattr``. + .. versionadded:: 22.2.0 + *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). + .. versionchanged:: 24.1.0 + Instances are not compared as tuples of attributes anymore, but using a + big ``and`` condition. This is faster and has more correct behavior for + uncomparable values like `math.nan`. + .. versionadded:: 24.1.0 + If a class has an *inherited* classmethod called + ``__attrs_init_subclass__``, it is executed after the class is created. + .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*. + .. versionadded:: 24.3.0 + Unless already present, a ``__replace__`` method is automatically + created for `copy.replace` (Python 3.13+ only). + .. versionchanged:: 25.4.0 + *kw_only* now only applies to attributes defined in the current class, + and respects attribute-level ``kw_only=False`` settings. + .. versionadded:: 25.4.0 + Added *force_kw_only* to go back to the previous *kw_only* behavior. + + .. note:: + + The main differences to the classic `attr.s` are: + + - Automatically detect whether or not *auto_attribs* should be `True` + (c.f. *auto_attribs* parameter). + - Converters and validators run when attributes are set by default -- + if *frozen* is `False`. + - *slots=True* + + Usually, this has only upsides and few visible effects in everyday + programming. But it *can* lead to some surprising behaviors, so + please make sure to read :term:`slotted classes`. + + - *auto_exc=True* + - *auto_detect=True* + - *order=False* + - *force_kw_only=False* + - Some options that were only relevant on Python 2 or were kept around + for backwards-compatibility have been removed. + + """ + + def do_it(cls, auto_attribs): + return attrs( + maybe_cls=cls, + these=these, + repr=repr, + hash=hash, + unsafe_hash=unsafe_hash, + init=init, + slots=slots, + frozen=frozen, + weakref_slot=weakref_slot, + str=str, + auto_attribs=auto_attribs, + kw_only=kw_only, + cache_hash=cache_hash, + auto_exc=auto_exc, + eq=eq, + order=order, + auto_detect=auto_detect, + collect_by_mro=True, + getstate_setstate=getstate_setstate, + on_setattr=on_setattr, + field_transformer=field_transformer, + match_args=match_args, + force_kw_only=force_kw_only, + ) + + def wrap(cls): + """ + Making this a wrapper ensures this code runs during class creation. + + We also ensure that frozen-ness of classes is inherited. + """ + nonlocal frozen, on_setattr + + had_on_setattr = on_setattr not in (None, setters.NO_OP) + + # By default, mutable classes convert & validate on setattr. + if frozen is False and on_setattr is None: + on_setattr = _DEFAULT_ON_SETATTR + + # However, if we subclass a frozen class, we inherit the immutability + # and disable on_setattr. + for base_cls in cls.__bases__: + if base_cls.__setattr__ is _frozen_setattrs: + if had_on_setattr: + msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)." + raise ValueError(msg) + + on_setattr = setters.NO_OP + break + + if auto_attribs is not None: + return do_it(cls, auto_attribs) + + try: + return do_it(cls, True) + except UnannotatedAttributeError: + return do_it(cls, False) + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but `None` if used as `@attrs()`. + if maybe_cls is None: + return wrap + + return wrap(maybe_cls) + + +mutable = define +frozen = partial(define, frozen=True, on_setattr=None) + + +def field( + *, + default=NOTHING, + validator=None, + repr=True, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=None, + eq=None, + order=None, + on_setattr=None, + alias=None, +): + """ + Create a new :term:`field` / :term:`attribute` on a class. + + .. warning:: + + Does **nothing** unless the class is also decorated with + `attrs.define` (or similar)! + + Args: + default: + A value that is used if an *attrs*-generated ``__init__`` is used + and no value is passed while instantiating or the attribute is + excluded using ``init=False``. + + If the value is an instance of `attrs.Factory`, its callable will + be used to construct a new value (useful for mutable data types + like lists or dicts). + + If a default is not set (or set manually to `attrs.NOTHING`), a + value *must* be supplied when instantiating; otherwise a + `TypeError` will be raised. + + .. seealso:: `defaults` + + factory (~typing.Callable): + Syntactic sugar for ``default=attr.Factory(factory)``. + + validator (~typing.Callable | list[~typing.Callable]): + Callable that is called by *attrs*-generated ``__init__`` methods + after the instance has been initialized. They receive the + initialized instance, the :func:`~attrs.Attribute`, and the passed + value. + + The return value is *not* inspected so the validator has to throw + an exception itself. + + If a `list` is passed, its items are treated as validators and must + all pass. + + Validators can be globally disabled and re-enabled using + `attrs.validators.get_disabled` / `attrs.validators.set_disabled`. + + The validator can also be set using decorator notation as shown + below. + + .. seealso:: :ref:`validators` + + repr (bool | ~typing.Callable): + Include this attribute in the generated ``__repr__`` method. If + True, include the attribute; if False, omit it. By default, the + built-in ``repr()`` function is used. To override how the attribute + value is formatted, pass a ``callable`` that takes a single value + and returns a string. Note that the resulting string is used as-is, + which means it will be used directly *instead* of calling + ``repr()`` (the default). + + eq (bool | ~typing.Callable): + If True (default), include this attribute in the generated + ``__eq__`` and ``__ne__`` methods that check two instances for + equality. To override how the attribute value is compared, pass a + callable that takes a single value and returns the value to be + compared. + + .. seealso:: `comparison` + + order (bool | ~typing.Callable): + If True (default), include this attributes in the generated + ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To + override how the attribute value is ordered, pass a callable that + takes a single value and returns the value to be ordered. + + .. seealso:: `comparison` + + hash (bool | None): + Include this attribute in the generated ``__hash__`` method. If + None (default), mirror *eq*'s value. This is the correct behavior + according the Python spec. Setting this value to anything else + than None is *discouraged*. + + .. seealso:: `hashing` + + init (bool): + Include this attribute in the generated ``__init__`` method. + + It is possible to set this to False and set a default value. In + that case this attributed is unconditionally initialized with the + specified default value or factory. + + .. seealso:: `init` + + converter (typing.Callable | Converter): + A callable that is called by *attrs*-generated ``__init__`` methods + to convert attribute's value to the desired format. + + If a vanilla callable is passed, it is given the passed-in value as + the only positional argument. It is possible to receive additional + arguments by wrapping the callable in a `Converter`. + + Either way, the returned value will be used as the new value of the + attribute. The value is converted before being passed to the + validator, if any. + + .. seealso:: :ref:`converters` + + metadata (dict | None): + An arbitrary mapping, to be used by third-party code. + + .. seealso:: `extending-metadata`. + + type (type): + The type of the attribute. Nowadays, the preferred method to + specify the type is using a variable annotation (see :pep:`526`). + This argument is provided for backwards-compatibility and for usage + with `make_class`. Regardless of the approach used, the type will + be stored on ``Attribute.type``. + + Please note that *attrs* doesn't do anything with this metadata by + itself. You can use it as part of your own code or for `static type + checking `. + + kw_only (bool | None): + Make this attribute keyword-only in the generated ``__init__`` (if + *init* is False, this parameter is ignored). If None (default), + mirror the setting from `attrs.define`. + + on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): + Allows to overwrite the *on_setattr* setting from `attr.s`. If left + None, the *on_setattr* value from `attr.s` is used. Set to + `attrs.setters.NO_OP` to run **no** `setattr` hooks for this + attribute -- regardless of the setting in `define()`. + + alias (str | None): + Override this attribute's parameter name in the generated + ``__init__`` method. If left None, default to ``name`` stripped + of leading underscores. See `private-attributes`. + + .. versionadded:: 20.1.0 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionadded:: 22.2.0 *alias* + .. versionadded:: 23.1.0 + The *type* parameter has been re-added; mostly for `attrs.make_class`. + Please note that type checkers ignore this metadata. + .. versionchanged:: 25.4.0 + *kw_only* can now be None, and its default is also changed from False to + None. + + .. seealso:: + + `attr.ib` + """ + return attrib( + default=default, + validator=validator, + repr=repr, + hash=hash, + init=init, + metadata=metadata, + type=type, + converter=converter, + factory=factory, + kw_only=kw_only, + eq=eq, + order=order, + on_setattr=on_setattr, + alias=alias, + ) + + +def asdict(inst, *, recurse=True, filter=None, value_serializer=None): + """ + Same as `attr.asdict`, except that collections types are always retained + and dict is always used as *dict_factory*. + + .. versionadded:: 21.3.0 + """ + return _asdict( + inst=inst, + recurse=recurse, + filter=filter, + value_serializer=value_serializer, + retain_collection_types=True, + ) + + +def astuple(inst, *, recurse=True, filter=None): + """ + Same as `attr.astuple`, except that collections types are always retained + and `tuple` is always used as the *tuple_factory*. + + .. versionadded:: 21.3.0 + """ + return _astuple( + inst=inst, recurse=recurse, filter=filter, retain_collection_types=True + ) + + +def inspect(cls): + """ + Inspect the class and return its effective build parameters. + + Warning: + This feature is currently **experimental** and is not covered by our + strict backwards-compatibility guarantees. + + Args: + cls: The *attrs*-decorated class to inspect. + + Returns: + The effective build parameters of the class. + + Raises: + NotAnAttrsClassError: If the class is not an *attrs*-decorated class. + + .. versionadded:: 25.4.0 + """ + try: + return cls.__dict__["__attrs_props__"] + except KeyError: + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) from None diff --git a/venv/lib/python3.12/site-packages/attr/_typing_compat.pyi b/venv/lib/python3.12/site-packages/attr/_typing_compat.pyi new file mode 100644 index 00000000..ca7b71e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_typing_compat.pyi @@ -0,0 +1,15 @@ +from typing import Any, ClassVar, Protocol + +# MYPY is a special constant in mypy which works the same way as `TYPE_CHECKING`. +MYPY = False + +if MYPY: + # A protocol to be able to statically accept an attrs class. + class AttrsInstance_(Protocol): + __attrs_attrs__: ClassVar[Any] + +else: + # For type checkers without plug-in support use an empty protocol that + # will (hopefully) be combined into a union. + class AttrsInstance_(Protocol): + pass diff --git a/venv/lib/python3.12/site-packages/attr/_version_info.py b/venv/lib/python3.12/site-packages/attr/_version_info.py new file mode 100644 index 00000000..27f18884 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_version_info.py @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: MIT + + +from functools import total_ordering + +from ._funcs import astuple +from ._make import attrib, attrs + + +@total_ordering +@attrs(eq=False, order=False, slots=True, frozen=True) +class VersionInfo: + """ + A version object that can be compared to tuple of length 1--4: + + >>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2) + True + >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1) + True + >>> vi = attr.VersionInfo(19, 2, 0, "final") + >>> vi < (19, 1, 1) + False + >>> vi < (19,) + False + >>> vi == (19, 2,) + True + >>> vi == (19, 2, 1) + False + + .. versionadded:: 19.2 + """ + + year = attrib(type=int) + minor = attrib(type=int) + micro = attrib(type=int) + releaselevel = attrib(type=str) + + @classmethod + def _from_version_string(cls, s): + """ + Parse *s* and return a _VersionInfo. + """ + v = s.split(".") + if len(v) == 3: + v.append("final") + + return cls( + year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3] + ) + + def _ensure_tuple(self, other): + """ + Ensure *other* is a tuple of a valid length. + + Returns a possibly transformed *other* and ourselves as a tuple of + the same length as *other*. + """ + + if self.__class__ is other.__class__: + other = astuple(other) + + if not isinstance(other, tuple): + raise NotImplementedError + + if not (1 <= len(other) <= 4): + raise NotImplementedError + + return astuple(self)[: len(other)], other + + def __eq__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + return us == them + + def __lt__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't + # have to do anything special with releaselevel for now. + return us < them + + def __hash__(self): + return hash((self.year, self.minor, self.micro, self.releaselevel)) diff --git a/venv/lib/python3.12/site-packages/attr/_version_info.pyi b/venv/lib/python3.12/site-packages/attr/_version_info.pyi new file mode 100644 index 00000000..45ced086 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/_version_info.pyi @@ -0,0 +1,9 @@ +class VersionInfo: + @property + def year(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def micro(self) -> int: ... + @property + def releaselevel(self) -> str: ... diff --git a/venv/lib/python3.12/site-packages/attr/converters.py b/venv/lib/python3.12/site-packages/attr/converters.py new file mode 100644 index 00000000..0a79deef --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/converters.py @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful converters. +""" + +import typing + +from ._compat import _AnnotationExtractor +from ._make import NOTHING, Converter, Factory, pipe + + +__all__ = [ + "default_if_none", + "optional", + "pipe", + "to_bool", +] + + +def optional(converter): + """ + A converter that allows an attribute to be optional. An optional attribute + is one which can be set to `None`. + + Type annotations will be inferred from the wrapped converter's, if it has + any. + + Args: + converter (typing.Callable): + the converter that is used for non-`None` values. + + .. versionadded:: 17.1.0 + """ + + if isinstance(converter, Converter): + + def optional_converter(val, inst, field): + if val is None: + return None + return converter(val, inst, field) + + else: + + def optional_converter(val): + if val is None: + return None + return converter(val) + + xtr = _AnnotationExtractor(converter) + + t = xtr.get_first_param_type() + if t: + optional_converter.__annotations__["val"] = typing.Optional[t] + + rt = xtr.get_return_type() + if rt: + optional_converter.__annotations__["return"] = typing.Optional[rt] + + if isinstance(converter, Converter): + return Converter(optional_converter, takes_self=True, takes_field=True) + + return optional_converter + + +def default_if_none(default=NOTHING, factory=None): + """ + A converter that allows to replace `None` values by *default* or the result + of *factory*. + + Args: + default: + Value to be used if `None` is passed. Passing an instance of + `attrs.Factory` is supported, however the ``takes_self`` option is + *not*. + + factory (typing.Callable): + A callable that takes no parameters whose result is used if `None` + is passed. + + Raises: + TypeError: If **neither** *default* or *factory* is passed. + + TypeError: If **both** *default* and *factory* are passed. + + ValueError: + If an instance of `attrs.Factory` is passed with + ``takes_self=True``. + + .. versionadded:: 18.2.0 + """ + if default is NOTHING and factory is None: + msg = "Must pass either `default` or `factory`." + raise TypeError(msg) + + if default is not NOTHING and factory is not None: + msg = "Must pass either `default` or `factory` but not both." + raise TypeError(msg) + + if factory is not None: + default = Factory(factory) + + if isinstance(default, Factory): + if default.takes_self: + msg = "`takes_self` is not supported by default_if_none." + raise ValueError(msg) + + def default_if_none_converter(val): + if val is not None: + return val + + return default.factory() + + else: + + def default_if_none_converter(val): + if val is not None: + return val + + return default + + return default_if_none_converter + + +def to_bool(val): + """ + Convert "boolean" strings (for example, from environment variables) to real + booleans. + + Values mapping to `True`: + + - ``True`` + - ``"true"`` / ``"t"`` + - ``"yes"`` / ``"y"`` + - ``"on"`` + - ``"1"`` + - ``1`` + + Values mapping to `False`: + + - ``False`` + - ``"false"`` / ``"f"`` + - ``"no"`` / ``"n"`` + - ``"off"`` + - ``"0"`` + - ``0`` + + Raises: + ValueError: For any other value. + + .. versionadded:: 21.3.0 + """ + if isinstance(val, str): + val = val.lower() + + if val in (True, "true", "t", "yes", "y", "on", "1", 1): + return True + if val in (False, "false", "f", "no", "n", "off", "0", 0): + return False + + msg = f"Cannot convert value to bool: {val!r}" + raise ValueError(msg) diff --git a/venv/lib/python3.12/site-packages/attr/converters.pyi b/venv/lib/python3.12/site-packages/attr/converters.pyi new file mode 100644 index 00000000..12bd0c4f --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/converters.pyi @@ -0,0 +1,19 @@ +from typing import Callable, Any, overload + +from attrs import _ConverterType, _CallableConverterType + +@overload +def pipe(*validators: _CallableConverterType) -> _CallableConverterType: ... +@overload +def pipe(*validators: _ConverterType) -> _ConverterType: ... +@overload +def optional(converter: _CallableConverterType) -> _CallableConverterType: ... +@overload +def optional(converter: _ConverterType) -> _ConverterType: ... +@overload +def default_if_none(default: Any) -> _CallableConverterType: ... +@overload +def default_if_none( + *, factory: Callable[[], Any] +) -> _CallableConverterType: ... +def to_bool(val: str | int | bool) -> bool: ... diff --git a/venv/lib/python3.12/site-packages/attr/exceptions.py b/venv/lib/python3.12/site-packages/attr/exceptions.py new file mode 100644 index 00000000..a207df40 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/exceptions.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + + +class FrozenError(AttributeError): + """ + A frozen/immutable instance or attribute have been attempted to be + modified. + + It mirrors the behavior of ``namedtuples`` by using the same error message + and subclassing `AttributeError`. + + .. versionadded:: 20.1.0 + """ + + def __init__(self): + msg = "can't set attribute" + super().__init__(msg) + self.msg = msg + + +class FrozenInstanceError(FrozenError): + """ + A frozen instance has been attempted to be modified. + + .. versionadded:: 16.1.0 + """ + + +class FrozenAttributeError(FrozenError): + """ + A frozen attribute has been attempted to be modified. + + .. versionadded:: 20.1.0 + """ + + +class AttrsAttributeNotFoundError(ValueError): + """ + An *attrs* function couldn't find an attribute that the user asked for. + + .. versionadded:: 16.2.0 + """ + + +class NotAnAttrsClassError(ValueError): + """ + A non-*attrs* class has been passed into an *attrs* function. + + .. versionadded:: 16.2.0 + """ + + +class DefaultAlreadySetError(RuntimeError): + """ + A default has been set when defining the field and is attempted to be reset + using the decorator. + + .. versionadded:: 17.1.0 + """ + + +class UnannotatedAttributeError(RuntimeError): + """ + A class with ``auto_attribs=True`` has a field without a type annotation. + + .. versionadded:: 17.3.0 + """ + + +class PythonTooOldError(RuntimeError): + """ + It was attempted to use an *attrs* feature that requires a newer Python + version. + + .. versionadded:: 18.2.0 + """ + + +class NotCallableError(TypeError): + """ + A field requiring a callable has been set with a value that is not + callable. + + .. versionadded:: 19.2.0 + """ + + def __init__(self, msg, value): + super(TypeError, self).__init__(msg, value) + self.msg = msg + self.value = value + + def __str__(self): + return str(self.msg) diff --git a/venv/lib/python3.12/site-packages/attr/exceptions.pyi b/venv/lib/python3.12/site-packages/attr/exceptions.pyi new file mode 100644 index 00000000..f2680118 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/exceptions.pyi @@ -0,0 +1,17 @@ +from typing import Any + +class FrozenError(AttributeError): + msg: str = ... + +class FrozenInstanceError(FrozenError): ... +class FrozenAttributeError(FrozenError): ... +class AttrsAttributeNotFoundError(ValueError): ... +class NotAnAttrsClassError(ValueError): ... +class DefaultAlreadySetError(RuntimeError): ... +class UnannotatedAttributeError(RuntimeError): ... +class PythonTooOldError(RuntimeError): ... + +class NotCallableError(TypeError): + msg: str = ... + value: Any = ... + def __init__(self, msg: str, value: Any) -> None: ... diff --git a/venv/lib/python3.12/site-packages/attr/filters.py b/venv/lib/python3.12/site-packages/attr/filters.py new file mode 100644 index 00000000..689b1705 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/filters.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful filters for `attrs.asdict` and `attrs.astuple`. +""" + +from ._make import Attribute + + +def _split_what(what): + """ + Returns a tuple of `frozenset`s of classes and attributes. + """ + return ( + frozenset(cls for cls in what if isinstance(cls, type)), + frozenset(cls for cls in what if isinstance(cls, str)), + frozenset(cls for cls in what if isinstance(cls, Attribute)), + ) + + +def include(*what): + """ + Create a filter that only allows *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to include. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.1.0 Accept strings with field names. + """ + cls, names, attrs = _split_what(what) + + def include_(attribute, value): + return ( + value.__class__ in cls + or attribute.name in names + or attribute in attrs + ) + + return include_ + + +def exclude(*what): + """ + Create a filter that does **not** allow *what*. + + Args: + what (list[type, str, attrs.Attribute]): + What to exclude. Can be a type, a name, or an attribute. + + Returns: + Callable: + A callable that can be passed to `attrs.asdict`'s and + `attrs.astuple`'s *filter* argument. + + .. versionchanged:: 23.3.0 Accept field name string as input argument + """ + cls, names, attrs = _split_what(what) + + def exclude_(attribute, value): + return not ( + value.__class__ in cls + or attribute.name in names + or attribute in attrs + ) + + return exclude_ diff --git a/venv/lib/python3.12/site-packages/attr/filters.pyi b/venv/lib/python3.12/site-packages/attr/filters.pyi new file mode 100644 index 00000000..974abdcd --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/filters.pyi @@ -0,0 +1,6 @@ +from typing import Any + +from . import Attribute, _FilterType + +def include(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ... +def exclude(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ... diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__init__.py b/venv/lib/python3.12/site-packages/attr/py.typed similarity index 100% rename from venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__init__.py rename to venv/lib/python3.12/site-packages/attr/py.typed diff --git a/venv/lib/python3.12/site-packages/attr/setters.py b/venv/lib/python3.12/site-packages/attr/setters.py new file mode 100644 index 00000000..78b08398 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/setters.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly used hooks for on_setattr. +""" + +from . import _config +from .exceptions import FrozenAttributeError + + +def pipe(*setters): + """ + Run all *setters* and return the return value of the last one. + + .. versionadded:: 20.1.0 + """ + + def wrapped_pipe(instance, attrib, new_value): + rv = new_value + + for setter in setters: + rv = setter(instance, attrib, rv) + + return rv + + return wrapped_pipe + + +def frozen(_, __, ___): + """ + Prevent an attribute to be modified. + + .. versionadded:: 20.1.0 + """ + raise FrozenAttributeError + + +def validate(instance, attrib, new_value): + """ + Run *attrib*'s validator on *new_value* if it has one. + + .. versionadded:: 20.1.0 + """ + if _config._run_validators is False: + return new_value + + v = attrib.validator + if not v: + return new_value + + v(instance, attrib, new_value) + + return new_value + + +def convert(instance, attrib, new_value): + """ + Run *attrib*'s converter -- if it has one -- on *new_value* and return the + result. + + .. versionadded:: 20.1.0 + """ + c = attrib.converter + if c: + # This can be removed once we drop 3.8 and use attrs.Converter instead. + from ._make import Converter + + if not isinstance(c, Converter): + return c(new_value) + + return c(new_value, instance, attrib) + + return new_value + + +# Sentinel for disabling class-wide *on_setattr* hooks for certain attributes. +# Sphinx's autodata stopped working, so the docstring is inlined in the API +# docs. +NO_OP = object() diff --git a/venv/lib/python3.12/site-packages/attr/setters.pyi b/venv/lib/python3.12/site-packages/attr/setters.pyi new file mode 100644 index 00000000..73abf36e --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/setters.pyi @@ -0,0 +1,20 @@ +from typing import Any, NewType, NoReturn, TypeVar + +from . import Attribute +from attrs import _OnSetAttrType + +_T = TypeVar("_T") + +def frozen( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> NoReturn: ... +def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ... +def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ... + +# convert is allowed to return Any, because they can be chained using pipe. +def convert( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> Any: ... + +_NoOpType = NewType("_NoOpType", object) +NO_OP: _NoOpType diff --git a/venv/lib/python3.12/site-packages/attr/validators.py b/venv/lib/python3.12/site-packages/attr/validators.py new file mode 100644 index 00000000..0b1a2944 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/validators.py @@ -0,0 +1,750 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful validators. +""" + +import operator +import re + +from contextlib import contextmanager +from re import Pattern + +from ._config import get_run_validators, set_run_validators +from ._make import _AndValidator, and_, attrib, attrs +from .converters import default_if_none +from .exceptions import NotCallableError + + +__all__ = [ + "and_", + "deep_iterable", + "deep_mapping", + "disabled", + "ge", + "get_disabled", + "gt", + "in_", + "instance_of", + "is_callable", + "le", + "lt", + "matches_re", + "max_len", + "min_len", + "not_", + "optional", + "or_", + "set_disabled", +] + + +def set_disabled(disabled): + """ + Globally disable or enable running validators. + + By default, they are run. + + Args: + disabled (bool): If `True`, disable running all validators. + + .. warning:: + + This function is not thread-safe! + + .. versionadded:: 21.3.0 + """ + set_run_validators(not disabled) + + +def get_disabled(): + """ + Return a bool indicating whether validators are currently disabled or not. + + Returns: + bool:`True` if validators are currently disabled. + + .. versionadded:: 21.3.0 + """ + return not get_run_validators() + + +@contextmanager +def disabled(): + """ + Context manager that disables running validators within its context. + + .. warning:: + + This context manager is not thread-safe! + + .. versionadded:: 21.3.0 + .. versionchanged:: 26.1.0 The contextmanager is nestable. + """ + prev = get_run_validators() + set_run_validators(False) + try: + yield + finally: + set_run_validators(prev) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _InstanceOfValidator: + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not isinstance(value, self.type): + msg = f"'{attr.name}' must be {self.type!r} (got {value!r} that is a {value.__class__!r})." + raise TypeError( + msg, + attr, + self.type, + value, + ) + + def __repr__(self): + return f"" + + +def instance_of(type): + """ + A validator that raises a `TypeError` if the initializer is called with a + wrong type for this particular attribute (checks are performed using + `isinstance` therefore it's also valid to pass a tuple of types). + + Args: + type (type | tuple[type]): The type to check for. + + Raises: + TypeError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the expected type, and the value it got. + """ + return _InstanceOfValidator(type) + + +@attrs(repr=False, frozen=True, slots=True) +class _MatchesReValidator: + pattern = attrib() + match_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.match_func(value): + msg = f"'{attr.name}' must match regex {self.pattern.pattern!r} ({value!r} doesn't)" + raise ValueError( + msg, + attr, + self.pattern, + value, + ) + + def __repr__(self): + return f"" + + +def matches_re(regex, flags=0, func=None): + r""" + A validator that raises `ValueError` if the initializer is called with a + string that doesn't match *regex*. + + Args: + regex (str, re.Pattern): + A regex string or precompiled pattern to match against + + flags (int): + Flags that will be passed to the underlying re function (default 0) + + func (typing.Callable): + Which underlying `re` function to call. Valid options are + `re.fullmatch`, `re.search`, and `re.match`; the default `None` + means `re.fullmatch`. For performance reasons, the pattern is + always precompiled using `re.compile`. + + .. versionadded:: 19.2.0 + .. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern. + """ + valid_funcs = (re.fullmatch, None, re.search, re.match) + if func not in valid_funcs: + msg = "'func' must be one of {}.".format( + ", ".join( + sorted((e and e.__name__) or "None" for e in set(valid_funcs)) + ) + ) + raise ValueError(msg) + + if isinstance(regex, Pattern): + if flags: + msg = "'flags' can only be used with a string pattern; pass flags to re.compile() instead" + raise TypeError(msg) + pattern = regex + else: + pattern = re.compile(regex, flags) + + if func is re.match: + match_func = pattern.match + elif func is re.search: + match_func = pattern.search + else: + match_func = pattern.fullmatch + + return _MatchesReValidator(pattern, match_func) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _OptionalValidator: + validator = attrib() + + def __call__(self, inst, attr, value): + if value is None: + return + + self.validator(inst, attr, value) + + def __repr__(self): + return f"" + + +def optional(validator): + """ + A validator that makes an attribute optional. An optional attribute is one + which can be set to `None` in addition to satisfying the requirements of + the sub-validator. + + Args: + validator + (typing.Callable | tuple[typing.Callable] | list[typing.Callable]): + A validator (or validators) that is used for non-`None` values. + + .. versionadded:: 15.1.0 + .. versionchanged:: 17.1.0 *validator* can be a list of validators. + .. versionchanged:: 23.1.0 *validator* can also be a tuple of validators. + """ + if isinstance(validator, (list, tuple)): + return _OptionalValidator(_AndValidator(validator)) + + return _OptionalValidator(validator) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _InValidator: + options = attrib() + _original_options = attrib(hash=False) + + def __call__(self, inst, attr, value): + try: + in_options = value in self.options + except TypeError: # e.g. `1 in "abc"` + in_options = False + + if not in_options: + msg = f"'{attr.name}' must be in {self._original_options!r} (got {value!r})" + raise ValueError( + msg, + attr, + self._original_options, + value, + ) + + def __repr__(self): + return f"" + + +def in_(options): + """ + A validator that raises a `ValueError` if the initializer is called with a + value that does not belong in the *options* provided. + + The check is performed using ``value in options``, so *options* has to + support that operation. + + To keep the validator hashable, dicts, lists, and sets are transparently + transformed into a `tuple`. + + Args: + options: Allowed options. + + Raises: + ValueError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the expected options, and the value it got. + + .. versionadded:: 17.1.0 + .. versionchanged:: 22.1.0 + The ValueError was incomplete until now and only contained the human + readable error message. Now it contains all the information that has + been promised since 17.1.0. + .. versionchanged:: 24.1.0 + *options* that are a list, dict, or a set are now transformed into a + tuple to keep the validator hashable. + """ + repr_options = options + if isinstance(options, (list, dict, set)): + options = tuple(options) + + return _InValidator(options, repr_options) + + +@attrs(repr=False, slots=False, unsafe_hash=True) +class _IsCallableValidator: + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not callable(value): + message = ( + "'{name}' must be callable " + "(got {value!r} that is a {actual!r})." + ) + raise NotCallableError( + msg=message.format( + name=attr.name, value=value, actual=value.__class__ + ), + value=value, + ) + + def __repr__(self): + return "" + + +def is_callable(): + """ + A validator that raises a `attrs.exceptions.NotCallableError` if the + initializer is called with a value for this particular attribute that is + not callable. + + .. versionadded:: 19.1.0 + + Raises: + attrs.exceptions.NotCallableError: + With a human readable error message containing the attribute + (`attrs.Attribute`) name, and the value it got. + """ + return _IsCallableValidator() + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _DeepIterable: + member_validator = attrib(validator=is_callable()) + iterable_validator = attrib( + default=None, validator=optional(is_callable()) + ) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.iterable_validator is not None: + self.iterable_validator(inst, attr, value) + + for member in value: + self.member_validator(inst, attr, member) + + def __repr__(self): + iterable_identifier = ( + "" + if self.iterable_validator is None + else f" {self.iterable_validator!r}" + ) + return ( + f"" + ) + + +def deep_iterable(member_validator, iterable_validator=None): + """ + A validator that performs deep validation of an iterable. + + Args: + member_validator: Validator(s) to apply to iterable members. + + iterable_validator: + Validator(s) to apply to iterable itself (optional). + + Raises + TypeError: if any sub-validators fail + + .. versionadded:: 19.1.0 + + .. versionchanged:: 25.4.0 + *member_validator* and *iterable_validator* can now be a list or tuple + of validators. + """ + if isinstance(member_validator, (list, tuple)): + member_validator = and_(*member_validator) + if isinstance(iterable_validator, (list, tuple)): + iterable_validator = and_(*iterable_validator) + return _DeepIterable(member_validator, iterable_validator) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _DeepMapping: + key_validator = attrib(validator=optional(is_callable())) + value_validator = attrib(validator=optional(is_callable())) + mapping_validator = attrib(validator=optional(is_callable())) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.mapping_validator is not None: + self.mapping_validator(inst, attr, value) + + for key in value: + if self.key_validator is not None: + self.key_validator(inst, attr, key) + if self.value_validator is not None: + self.value_validator(inst, attr, value[key]) + + def __repr__(self): + return f"" + + +def deep_mapping( + key_validator=None, value_validator=None, mapping_validator=None +): + """ + A validator that performs deep validation of a dictionary. + + All validators are optional, but at least one of *key_validator* or + *value_validator* must be provided. + + Args: + key_validator: Validator(s) to apply to dictionary keys. + + value_validator: Validator(s) to apply to dictionary values. + + mapping_validator: + Validator(s) to apply to top-level mapping attribute. + + .. versionadded:: 19.1.0 + + .. versionchanged:: 25.4.0 + *key_validator* and *value_validator* are now optional, but at least one + of them must be provided. + + .. versionchanged:: 25.4.0 + *key_validator*, *value_validator*, and *mapping_validator* can now be a + list or tuple of validators. + + Raises: + TypeError: If any sub-validator fails on validation. + + ValueError: + If neither *key_validator* nor *value_validator* is provided on + instantiation. + """ + if key_validator is None and value_validator is None: + msg = ( + "At least one of key_validator or value_validator must be provided" + ) + raise ValueError(msg) + + if isinstance(key_validator, (list, tuple)): + key_validator = and_(*key_validator) + if isinstance(value_validator, (list, tuple)): + value_validator = and_(*value_validator) + if isinstance(mapping_validator, (list, tuple)): + mapping_validator = and_(*mapping_validator) + + return _DeepMapping(key_validator, value_validator, mapping_validator) + + +@attrs(repr=False, frozen=True, slots=True) +class _NumberValidator: + bound = attrib() + compare_op = attrib() + compare_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.compare_func(value, self.bound): + msg = f"'{attr.name}' must be {self.compare_op} {self.bound}: {value}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def lt(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number larger or equal to *val*. + + The validator uses `operator.lt` to compare the values. + + Args: + val: Exclusive upper bound for values. + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<", operator.lt) + + +def le(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number greater than *val*. + + The validator uses `operator.le` to compare the values. + + Args: + val: Inclusive upper bound for values. + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<=", operator.le) + + +def ge(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number smaller than *val*. + + The validator uses `operator.ge` to compare the values. + + Args: + val: Inclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">=", operator.ge) + + +def gt(val): + """ + A validator that raises `ValueError` if the initializer is called with a + number smaller or equal to *val*. + + The validator uses `operator.gt` to compare the values. + + Args: + val: Exclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">", operator.gt) + + +@attrs(repr=False, frozen=True, slots=True) +class _MaxLengthValidator: + max_length = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if len(value) > self.max_length: + msg = f"Length of '{attr.name}' must be <= {self.max_length}: {len(value)}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def max_len(length): + """ + A validator that raises `ValueError` if the initializer is called + with a string or iterable that is longer than *length*. + + Args: + length (int): Maximum length of the string or iterable + + .. versionadded:: 21.3.0 + """ + return _MaxLengthValidator(length) + + +@attrs(repr=False, frozen=True, slots=True) +class _MinLengthValidator: + min_length = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if len(value) < self.min_length: + msg = f"Length of '{attr.name}' must be >= {self.min_length}: {len(value)}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def min_len(length): + """ + A validator that raises `ValueError` if the initializer is called + with a string or iterable that is shorter than *length*. + + Args: + length (int): Minimum length of the string or iterable + + .. versionadded:: 22.1.0 + """ + return _MinLengthValidator(length) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _SubclassOfValidator: + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not issubclass(value, self.type): + msg = f"'{attr.name}' must be a subclass of {self.type!r} (got {value!r})." + raise TypeError( + msg, + attr, + self.type, + value, + ) + + def __repr__(self): + return f"" + + +def _subclass_of(type): + """ + A validator that raises a `TypeError` if the initializer is called with a + wrong type for this particular attribute (checks are performed using + `issubclass` therefore it's also valid to pass a tuple of types). + + Args: + type (type | tuple[type, ...]): The type(s) to check for. + + Raises: + TypeError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the expected type, and the value it got. + """ + return _SubclassOfValidator(type) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _NotValidator: + validator = attrib() + msg = attrib( + converter=default_if_none( + "not_ validator child '{validator!r}' " + "did not raise a captured error" + ) + ) + exc_types = attrib( + validator=deep_iterable( + member_validator=_subclass_of(Exception), + iterable_validator=instance_of(tuple), + ), + ) + + def __call__(self, inst, attr, value): + try: + self.validator(inst, attr, value) + except self.exc_types: + pass # suppress error to invert validity + else: + raise ValueError( + self.msg.format( + validator=self.validator, + exc_types=self.exc_types, + ), + attr, + self.validator, + value, + self.exc_types, + ) + + def __repr__(self): + return f"" + + +def not_(validator, *, msg=None, exc_types=(ValueError, TypeError)): + """ + A validator that wraps and logically 'inverts' the validator passed to it. + It will raise a `ValueError` if the provided validator *doesn't* raise a + `ValueError` or `TypeError` (by default), and will suppress the exception + if the provided validator *does*. + + Intended to be used with existing validators to compose logic without + needing to create inverted variants, for example, ``not_(in_(...))``. + + Args: + validator: A validator to be logically inverted. + + msg (str): + Message to raise if validator fails. Formatted with keys + ``exc_types`` and ``validator``. + + exc_types (tuple[type, ...]): + Exception type(s) to capture. Other types raised by child + validators will not be intercepted and pass through. + + Raises: + ValueError: + With a human readable error message, the attribute (of type + `attrs.Attribute`), the validator that failed to raise an + exception, the value it got, and the expected exception types. + + .. versionadded:: 22.2.0 + """ + try: + exc_types = tuple(exc_types) + except TypeError: + exc_types = (exc_types,) + return _NotValidator(validator, msg, exc_types) + + +@attrs(repr=False, slots=True, unsafe_hash=True) +class _OrValidator: + validators = attrib() + + def __call__(self, inst, attr, value): + for v in self.validators: + try: + v(inst, attr, value) + except Exception: # noqa: BLE001, PERF203, S112 + continue + else: + return + + msg = f"None of {self.validators!r} satisfied for value {value!r}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def or_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators until one of them is + satisfied. + + Args: + validators (~collections.abc.Iterable[typing.Callable]): + Arbitrary number of validators. + + Raises: + ValueError: + If no validator is satisfied. Raised with a human-readable error + message listing all the wrapped validators and the value that + failed all of them. + + .. versionadded:: 24.1.0 + """ + vals = [] + for v in validators: + vals.extend(v.validators if isinstance(v, _OrValidator) else [v]) + + return _OrValidator(tuple(vals)) diff --git a/venv/lib/python3.12/site-packages/attr/validators.pyi b/venv/lib/python3.12/site-packages/attr/validators.pyi new file mode 100644 index 00000000..18fb112c --- /dev/null +++ b/venv/lib/python3.12/site-packages/attr/validators.pyi @@ -0,0 +1,140 @@ +from types import UnionType +from typing import ( + Any, + AnyStr, + Callable, + Container, + ContextManager, + Iterable, + Mapping, + Match, + Pattern, + TypeVar, + overload, +) + +from attrs import _ValidatorType +from attrs import _ValidatorArgType + +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_T4 = TypeVar("_T4") +_T5 = TypeVar("_T5") +_T6 = TypeVar("_T6") +_I = TypeVar("_I", bound=Iterable) +_K = TypeVar("_K") +_V = TypeVar("_V") +_M = TypeVar("_M", bound=Mapping) + +def set_disabled(run: bool) -> None: ... +def get_disabled() -> bool: ... +def disabled() -> ContextManager[None]: ... + +# To be more precise on instance_of use some overloads. +# If there are more than 3 items in the tuple then we fall back to Any +@overload +def instance_of(type: type[_T]) -> _ValidatorType[_T]: ... +@overload +def instance_of(type: tuple[type[_T]]) -> _ValidatorType[_T]: ... +@overload +def instance_of( + type: tuple[type[_T1], type[_T2]], +) -> _ValidatorType[_T1 | _T2]: ... +@overload +def instance_of( + type: tuple[type[_T1], type[_T2], type[_T3]], +) -> _ValidatorType[_T1 | _T2 | _T3]: ... +@overload +def instance_of(type: tuple[type, ...]) -> _ValidatorType[Any]: ... +@overload +def instance_of(type: UnionType) -> _ValidatorType[Any]: ... +def optional( + validator: ( + _ValidatorType[_T] + | list[_ValidatorType[_T]] + | tuple[_ValidatorType[_T], ...] + ), +) -> _ValidatorType[_T | None]: ... +def in_(options: Container[_T]) -> _ValidatorType[_T]: ... +def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ... +def matches_re( + regex: Pattern[AnyStr] | AnyStr, + flags: int = ..., + func: Callable[[AnyStr, AnyStr, int], Match[AnyStr] | None] | None = ..., +) -> _ValidatorType[AnyStr]: ... +def deep_iterable( + member_validator: _ValidatorArgType[_T], + iterable_validator: _ValidatorArgType[_I] | None = ..., +) -> _ValidatorType[_I]: ... +@overload +def deep_mapping( + key_validator: _ValidatorArgType[_K], + value_validator: _ValidatorArgType[_V] | None = ..., + mapping_validator: _ValidatorArgType[_M] | None = ..., +) -> _ValidatorType[_M]: ... +@overload +def deep_mapping( + key_validator: _ValidatorArgType[_K] | None = ..., + value_validator: _ValidatorArgType[_V] = ..., + mapping_validator: _ValidatorArgType[_M] | None = ..., +) -> _ValidatorType[_M]: ... +def is_callable() -> _ValidatorType[_T]: ... +def lt(val: _T) -> _ValidatorType[_T]: ... +def le(val: _T) -> _ValidatorType[_T]: ... +def ge(val: _T) -> _ValidatorType[_T]: ... +def gt(val: _T) -> _ValidatorType[_T]: ... +def max_len(length: int) -> _ValidatorType[_T]: ... +def min_len(length: int) -> _ValidatorType[_T]: ... +def not_( + validator: _ValidatorType[_T], + *, + msg: str | None = None, + exc_types: type[Exception] | Iterable[type[Exception]] = ..., +) -> _ValidatorType[_T]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], +) -> _ValidatorType[_T1 | _T2]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], +) -> _ValidatorType[_T1 | _T2 | _T3]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], + __v4: _ValidatorType[_T4], +) -> _ValidatorType[_T1 | _T2 | _T3 | _T4]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], + __v4: _ValidatorType[_T4], + __v5: _ValidatorType[_T5], +) -> _ValidatorType[_T1 | _T2 | _T3 | _T4 | _T5]: ... +@overload +def or_( + __v1: _ValidatorType[_T1], + __v2: _ValidatorType[_T2], + __v3: _ValidatorType[_T3], + __v4: _ValidatorType[_T4], + __v5: _ValidatorType[_T5], + __v6: _ValidatorType[_T6], +) -> _ValidatorType[_T1 | _T2 | _T3 | _T4 | _T5 | _T6]: ... +@overload +def or_( + __v1: _ValidatorType[Any], + __v2: _ValidatorType[Any], + __v3: _ValidatorType[Any], + __v4: _ValidatorType[Any], + __v5: _ValidatorType[Any], + __v6: _ValidatorType[Any], + *validators: _ValidatorType[Any], +) -> _ValidatorType[Any]: ... diff --git a/venv/lib/python3.12/site-packages/pip-24.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/INSTALLER similarity index 100% rename from venv/lib/python3.12/site-packages/pip-24.0.dist-info/INSTALLER rename to venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/INSTALLER diff --git a/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA new file mode 100644 index 00000000..5cf16a04 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA @@ -0,0 +1,199 @@ +Metadata-Version: 2.4 +Name: attrs +Version: 26.1.0 +Summary: Classes Without Boilerplate +Project-URL: Documentation, https://www.attrs.org/ +Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html +Project-URL: GitHub, https://github.com/python-attrs/attrs +Project-URL: Funding, https://github.com/sponsors/hynek +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi +Author-email: Hynek Schlawack +License-Expression: MIT +License-File: LICENSE +Keywords: attribute,boilerplate,class +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Typing :: Typed +Requires-Python: >=3.9 +Description-Content-Type: text/markdown + +

    + + attrs + +

    + + +*attrs* is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka [dunder methods](https://www.attrs.org/en/latest/glossary.html#term-dunder-methods)). +Trusted by NASA for [Mars missions since 2020](https://github.com/readme/featured/nasa-ingenuity-helicopter)! + +Its main goal is to help you to write **concise** and **correct** software without slowing down your code. + + +## Sponsors + +*attrs* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek). +Especially those generously supporting us at the *The Organization* tier and higher: + + + +

    + + + + + + + + + + + +

    + + + +

    + Please consider joining them to help make attrs’s maintenance more sustainable! +

    + + + +## Example + +*attrs* gives you a class decorator and a way to declaratively define the attributes on that class: + + + +```pycon +>>> from attrs import asdict, define, make_class, Factory + +>>> @define +... class SomeClass: +... a_number: int = 42 +... list_of_numbers: list[int] = Factory(list) +... +... def hard_math(self, another_number): +... return self.a_number + sum(self.list_of_numbers) * another_number + + +>>> sc = SomeClass(1, [1, 2, 3]) +>>> sc +SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + +>>> sc.hard_math(3) +19 +>>> sc == SomeClass(1, [1, 2, 3]) +True +>>> sc != SomeClass(2, [3, 2, 1]) +True + +>>> asdict(sc) +{'a_number': 1, 'list_of_numbers': [1, 2, 3]} + +>>> SomeClass() +SomeClass(a_number=42, list_of_numbers=[]) + +>>> C = make_class("C", ["a", "b"]) +>>> C("foo", "bar") +C(a='foo', b='bar') +``` + +After *declaring* your attributes, *attrs* gives you: + +- a concise and explicit overview of the class's attributes, +- a nice human-readable `__repr__`, +- equality-checking methods, +- an initializer, +- and much more, + +*without* writing dull boilerplate code again and again and *without* runtime performance penalties. + +--- + +This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0. +The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**. + +Check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for an in-depth explanation! + + +### Hate Type Annotations!? + +No problem! +Types are entirely **optional** with *attrs*. +Simply assign `attrs.field()` to the attributes instead of annotating them with types: + +```python +from attrs import define, field + +@define +class SomeClass: + a_number = field(default=42) + list_of_numbers = field(factory=list) +``` + + +## Data Classes + +On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*). +In practice it does a lot more and is more flexible. +For instance, it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization), has a replacement for `__init_subclass__`, and allows for stepping through the generated methods using a debugger. + +For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes), but generally speaking, we are more likely to commit crimes against nature to make things work that one would expect to work, but that are quite complicated in practice. + + +## Project Information + +- [**Changelog**](https://www.attrs.org/en/stable/changelog.html) +- [**Documentation**](https://www.attrs.org/) +- [**PyPI**](https://pypi.org/project/attrs/) +- [**Source Code**](https://github.com/python-attrs/attrs) +- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md) +- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs) +- **Get Help**: use the `python-attrs` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/python-attrs) + + +### *attrs* for Enterprise + +Available as part of the [Tidelift Subscription](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek). + +The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. + +## Release Information + +### Backwards-incompatible Changes + +- Field aliases are now resolved *before* calling `field_transformer`, so transformers receive fully populated `Attribute` objects with usable `alias` values instead of `None`. + The new `Attribute.alias_is_default` flag indicates whether the alias was auto-generated (`True`) or explicitly set by the user (`False`). + [#1509](https://github.com/python-attrs/attrs/issues/1509) + + +### Changes + +- Fix type annotations for `attrs.validators.optional()`, so it no longer rejects tuples with more than one validator. + [#1496](https://github.com/python-attrs/attrs/issues/1496) +- The `attrs.validators.disabled()` contextmanager can now be nested. + [#1513](https://github.com/python-attrs/attrs/issues/1513) +- Frozen classes can set `on_setattr=attrs.setters.NO_OP` in addition to `None`. + [#1515](https://github.com/python-attrs/attrs/issues/1515) +- It's now possible to pass *attrs* **instances** in addition to *attrs* **classes** to `attrs.fields()`. + [#1529](https://github.com/python-attrs/attrs/issues/1529) + + + +--- + +[Full changelog →](https://www.attrs.org/en/stable/changelog.html) diff --git a/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD new file mode 100644 index 00000000..2cf8b836 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD @@ -0,0 +1,55 @@ +attr/__init__.py,sha256=fOYIvt1eGSqQre4uCS3sJWKZ0mwAuC8UD6qba5OS9_U,2057 +attr/__init__.pyi,sha256=pVGImAUVovq2_TYl_r_HIYnGlyOaoCuEhxo-EvsnnSc,11325 +attr/__pycache__/__init__.cpython-312.pyc,, +attr/__pycache__/_cmp.cpython-312.pyc,, +attr/__pycache__/_compat.cpython-312.pyc,, +attr/__pycache__/_config.cpython-312.pyc,, +attr/__pycache__/_funcs.cpython-312.pyc,, +attr/__pycache__/_make.cpython-312.pyc,, +attr/__pycache__/_next_gen.cpython-312.pyc,, +attr/__pycache__/_version_info.cpython-312.pyc,, +attr/__pycache__/converters.cpython-312.pyc,, +attr/__pycache__/exceptions.cpython-312.pyc,, +attr/__pycache__/filters.cpython-312.pyc,, +attr/__pycache__/setters.cpython-312.pyc,, +attr/__pycache__/validators.cpython-312.pyc,, +attr/_cmp.py,sha256=3Nn1TjxllUYiX_nJoVnEkXoDk0hM1DYKj5DE7GZe4i0,4117 +attr/_cmp.pyi,sha256=U-_RU_UZOyPUEQzXE6RMYQQcjkZRY25wTH99sN0s7MM,368 +attr/_compat.py,sha256=x0g7iEUOnBVJC72zyFCgb1eKqyxS-7f2LGnNyZ_r95s,2829 +attr/_config.py,sha256=dGq3xR6fgZEF6UBt_L0T-eUHIB4i43kRmH0P28sJVw8,843 +attr/_funcs.py,sha256=Ix5IETTfz5F01F-12MF_CSFomIn2h8b67EVVz2gCtBE,16479 +attr/_make.py,sha256=H7OH2eWS5CnBzLUjNFE1WymfPrmF1r8fv2RPdt9MuYA,106129 +attr/_next_gen.py,sha256=BQtCUlzwg2gWHTYXBQvrEYBnzBUrDvO57u0Py6UCPhc,26274 +attr/_typing_compat.pyi,sha256=XDP54TUn-ZKhD62TOQebmzrwFyomhUCoGRpclb6alRA,469 +attr/_version_info.py,sha256=w4R-FYC3NK_kMkGUWJlYP4cVAlH9HRaC-um3fcjYkHM,2222 +attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 +attr/converters.py,sha256=GlDeOzPeTFgeBBLbj9G57Ez5lAk68uhSALRYJ_exe84,3861 +attr/converters.pyi,sha256=orU2bff-VjQa2kMDyvnMQV73oJT2WRyQuw4ZR1ym1bE,643 +attr/exceptions.py,sha256=b4vMbnoQ3VpwWZhqrYi_ssXVCK8o2c4HQSS09cSUM9o,1990 +attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539 +attr/filters.py,sha256=ZBiKWLp3R0LfCZsq7X11pn9WX8NslS2wXM4jsnLOGc8,1795 +attr/filters.pyi,sha256=3J5BG-dTxltBk1_-RuNRUHrv2qu1v8v4aDNAQ7_mifA,208 +attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attr/setters.py,sha256=5-dcT63GQK35ONEzSgfXCkbB7pPkaR-qv15mm4PVSzQ,1617 +attr/setters.pyi,sha256=NnVkaFU1BB4JB8E4JuXyrzTUgvtMpj8p3wBdJY7uix4,584 +attr/validators.py,sha256=m3QRzZTANr4f2C4eVdUoFg11NgXWak8Wat4qQTGhvcs,21553 +attr/validators.pyi,sha256=gM1ZmHaBckyYWI2EirpRNzqm3B19cw5Iq6B4Kno9YCM,4087 +attrs-26.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +attrs-26.1.0.dist-info/METADATA,sha256=TNQOaQ8jvzfLytNO_WdY4GLfHfB8hoM_fjzpW_H6OMw,8754 +attrs-26.1.0.dist-info/RECORD,, +attrs-26.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87 +attrs-26.1.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109 +attrs/__init__.py,sha256=RxaAZNwYiEh-fcvHLZNpQ_DWKni73M_jxEPEftiq1Zc,1183 +attrs/__init__.pyi,sha256=2gV79g9UxJppGSM48hAZJ6h_MHb70dZoJL31ZNJeZYI,9416 +attrs/__pycache__/__init__.cpython-312.pyc,, +attrs/__pycache__/converters.cpython-312.pyc,, +attrs/__pycache__/exceptions.cpython-312.pyc,, +attrs/__pycache__/filters.cpython-312.pyc,, +attrs/__pycache__/setters.cpython-312.pyc,, +attrs/__pycache__/validators.cpython-312.pyc,, +attrs/converters.py,sha256=8kQljrVwfSTRu8INwEk8SI0eGrzmWftsT7rM0EqyohM,76 +attrs/exceptions.py,sha256=ACCCmg19-vDFaDPY9vFl199SPXCQMN_bENs4DALjzms,76 +attrs/filters.py,sha256=VOUMZug9uEU6dUuA0dF1jInUK0PL3fLgP0VBS5d-CDE,73 +attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attrs/setters.py,sha256=eL1YidYQV3T2h9_SYIZSZR1FAcHGb1TuCTy0E0Lv2SU,73 +attrs/validators.py,sha256=xcy6wD5TtTkdCG1f4XWbocPSO0faBjk5IfVJfP6SUj0,76 diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/WHEEL similarity index 100% rename from venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/WHEEL rename to venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/WHEEL diff --git a/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE new file mode 100644 index 00000000..2bd6453d --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Hynek Schlawack and the attrs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/attrs/__init__.py b/venv/lib/python3.12/site-packages/attrs/__init__.py new file mode 100644 index 00000000..dc1ce4b9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/__init__.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: MIT + +from attr import ( + NOTHING, + Attribute, + AttrsInstance, + Converter, + Factory, + NothingType, + _make_getattr, + assoc, + cmp_using, + define, + evolve, + field, + fields, + fields_dict, + frozen, + has, + make_class, + mutable, + resolve_types, + validate, +) +from attr._make import ClassProps +from attr._next_gen import asdict, astuple, inspect + +from . import converters, exceptions, filters, setters, validators + + +__all__ = [ + "NOTHING", + "Attribute", + "AttrsInstance", + "ClassProps", + "Converter", + "Factory", + "NothingType", + "__author__", + "__copyright__", + "__description__", + "__doc__", + "__email__", + "__license__", + "__title__", + "__url__", + "__version__", + "__version_info__", + "asdict", + "assoc", + "astuple", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "field", + "fields", + "fields_dict", + "filters", + "frozen", + "has", + "inspect", + "make_class", + "mutable", + "resolve_types", + "setters", + "validate", + "validators", +] + +__getattr__ = _make_getattr(__name__) diff --git a/venv/lib/python3.12/site-packages/attrs/__init__.pyi b/venv/lib/python3.12/site-packages/attrs/__init__.pyi new file mode 100644 index 00000000..6364bac4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/__init__.pyi @@ -0,0 +1,314 @@ +import sys + +from typing import ( + Any, + Callable, + Mapping, + Sequence, + overload, + TypeVar, +) + +# Because we need to type our own stuff, we have to make everything from +# attr explicitly public too. +from attr import __author__ as __author__ +from attr import __copyright__ as __copyright__ +from attr import __description__ as __description__ +from attr import __email__ as __email__ +from attr import __license__ as __license__ +from attr import __title__ as __title__ +from attr import __url__ as __url__ +from attr import __version__ as __version__ +from attr import __version_info__ as __version_info__ +from attr import assoc as assoc +from attr import Attribute as Attribute +from attr import AttrsInstance as AttrsInstance +from attr import cmp_using as cmp_using +from attr import converters as converters +from attr import Converter as Converter +from attr import evolve as evolve +from attr import exceptions as exceptions +from attr import Factory as Factory +from attr import fields as fields +from attr import fields_dict as fields_dict +from attr import filters as filters +from attr import has as has +from attr import make_class as make_class +from attr import NOTHING as NOTHING +from attr import resolve_types as resolve_types +from attr import setters as setters +from attr import validate as validate +from attr import validators as validators +from attr import attrib, asdict as asdict, astuple as astuple +from attr import NothingType as NothingType + +if sys.version_info >= (3, 11): + from typing import dataclass_transform +else: + from typing_extensions import dataclass_transform + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_EqOrderType = bool | Callable[[Any], Any] +_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any] +_CallableConverterType = Callable[[Any], Any] +_ConverterType = _CallableConverterType | Converter[Any, Any] +_ReprType = Callable[[Any], str] +_ReprArgType = bool | _ReprType +_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any] +_OnSetAttrArgType = _OnSetAttrType | list[_OnSetAttrType] | setters._NoOpType +_FieldTransformer = Callable[ + [type, list["Attribute[Any]"]], list["Attribute[Any]"] +] +# FIXME: in reality, if multiple validators are passed they must be in a list +# or tuple, but those are invariant and so would prevent subtypes of +# _ValidatorType from working when passed in a list or tuple. +_ValidatorArgType = _ValidatorType[_T] | Sequence[_ValidatorType[_T]] + +@overload +def field( + *, + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool | None = ..., + eq: bool | None = ..., + order: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def field( + *, + default: None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def field( + *, + default: _T, + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def field( + *, + default: _T | None = ..., + validator: _ValidatorArgType[_T] | None = ..., + repr: _ReprArgType = ..., + hash: bool | None = ..., + init: bool = ..., + metadata: Mapping[Any, Any] | None = ..., + converter: _ConverterType + | list[_ConverterType] + | tuple[_ConverterType, ...] + | None = ..., + factory: Callable[[], _T] | None = ..., + kw_only: bool | None = ..., + eq: _EqOrderType | None = ..., + order: _EqOrderType | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + alias: str | None = ..., + type: type | None = ..., +) -> Any: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: _C, + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: None = ..., + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +mutable = define + +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: _C, + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: None = ..., + *, + these: dict[str, Any] | None = ..., + repr: bool = ..., + unsafe_hash: bool | None = ..., + hash: bool | None = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: bool | None = ..., + order: bool | None = ..., + auto_detect: bool = ..., + getstate_setstate: bool | None = ..., + on_setattr: _OnSetAttrArgType | None = ..., + field_transformer: _FieldTransformer | None = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +class ClassProps: + # XXX: somehow when defining/using enums Mypy starts looking at our own + # (untyped) code and causes tons of errors. + Hashability: Any + KeywordOnly: Any + + is_exception: bool + is_slotted: bool + has_weakref_slot: bool + is_frozen: bool + # kw_only: ClassProps.KeywordOnly + kw_only: Any + collected_fields_by_mro: bool + added_init: bool + added_repr: bool + added_eq: bool + added_ordering: bool + # hashability: ClassProps.Hashability + hashability: Any + added_match_args: bool + added_str: bool + added_pickling: bool + on_setattr_hook: _OnSetAttrType | None + field_transformer: Callable[[Attribute[Any]], Attribute[Any]] | None + + def __init__( + self, + is_exception: bool, + is_slotted: bool, + has_weakref_slot: bool, + is_frozen: bool, + # kw_only: ClassProps.KeywordOnly + kw_only: Any, + collected_fields_by_mro: bool, + added_init: bool, + added_repr: bool, + added_eq: bool, + added_ordering: bool, + # hashability: ClassProps.Hashability + hashability: Any, + added_match_args: bool, + added_str: bool, + added_pickling: bool, + on_setattr_hook: _OnSetAttrType, + field_transformer: Callable[[Attribute[Any]], Attribute[Any]], + ) -> None: ... + @property + def is_hashable(self) -> bool: ... + +def inspect(cls: type) -> ClassProps: ... diff --git a/venv/lib/python3.12/site-packages/attrs/converters.py b/venv/lib/python3.12/site-packages/attrs/converters.py new file mode 100644 index 00000000..7821f6c0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/converters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.converters import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/attrs/exceptions.py b/venv/lib/python3.12/site-packages/attrs/exceptions.py new file mode 100644 index 00000000..3323f9d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/exceptions.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.exceptions import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/attrs/filters.py b/venv/lib/python3.12/site-packages/attrs/filters.py new file mode 100644 index 00000000..3080f483 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/filters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.filters import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__init__.py b/venv/lib/python3.12/site-packages/attrs/py.typed similarity index 100% rename from venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__init__.py rename to venv/lib/python3.12/site-packages/attrs/py.typed diff --git a/venv/lib/python3.12/site-packages/attrs/setters.py b/venv/lib/python3.12/site-packages/attrs/setters.py new file mode 100644 index 00000000..f3d73bb7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/setters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.setters import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/attrs/validators.py b/venv/lib/python3.12/site-packages/attrs/validators.py new file mode 100644 index 00000000..037e124f --- /dev/null +++ b/venv/lib/python3.12/site-packages/attrs/validators.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.validators import * # noqa: F403 diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER similarity index 100% rename from venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/INSTALLER rename to venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/INSTALLER diff --git a/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA new file mode 100644 index 00000000..7fd97b76 --- /dev/null +++ b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/METADATA @@ -0,0 +1,123 @@ +Metadata-Version: 2.4 +Name: beautifulsoup4 +Version: 4.14.3 +Summary: Screen-scraping library +Project-URL: Download, https://www.crummy.com/software/BeautifulSoup/bs4/download/ +Project-URL: Homepage, https://www.crummy.com/software/BeautifulSoup/bs4/ +Author-email: Leonard Richardson +License: MIT License +License-File: AUTHORS +License-File: LICENSE +Keywords: HTML,XML,parse,soup +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Topic :: Text Processing :: Markup :: SGML +Classifier: Topic :: Text Processing :: Markup :: XML +Requires-Python: >=3.7.0 +Requires-Dist: soupsieve>=1.6.1 +Requires-Dist: typing-extensions>=4.0.0 +Provides-Extra: cchardet +Requires-Dist: cchardet; extra == 'cchardet' +Provides-Extra: chardet +Requires-Dist: chardet; extra == 'chardet' +Provides-Extra: charset-normalizer +Requires-Dist: charset-normalizer; extra == 'charset-normalizer' +Provides-Extra: html5lib +Requires-Dist: html5lib; extra == 'html5lib' +Provides-Extra: lxml +Requires-Dist: lxml; extra == 'lxml' +Description-Content-Type: text/markdown + +Beautiful Soup is a library that makes it easy to scrape information +from web pages. It sits atop an HTML or XML parser, providing Pythonic +idioms for iterating, searching, and modifying the parse tree. + +# Quick start + +``` +>>> from bs4 import BeautifulSoup +>>> soup = BeautifulSoup("

    SomebadHTML") +>>> print(soup.prettify()) + + +

    + Some + + bad + + HTML + + +

    + + +>>> soup.find(string="bad") +'bad' +>>> soup.i +HTML +# +>>> soup = BeautifulSoup("SomebadXML", "xml") +# +>>> print(soup.prettify()) + + + Some + + bad + + XML + + +``` + +To go beyond the basics, [comprehensive documentation is available](https://www.crummy.com/software/BeautifulSoup/bs4/doc/). + +# Links + +* [Homepage](https://www.crummy.com/software/BeautifulSoup/bs4/) +* [Documentation](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) +* [Discussion group](https://groups.google.com/group/beautifulsoup/) +* [Development](https://code.launchpad.net/beautifulsoup/) +* [Bug tracker](https://bugs.launchpad.net/beautifulsoup/) +* [Complete changelog](https://git.launchpad.net/beautifulsoup/tree/CHANGELOG) + +# Note on Python 2 sunsetting + +Beautiful Soup's support for Python 2 was discontinued on December 31, +2020: one year after the sunset date for Python 2 itself. From this +point onward, new Beautiful Soup development will exclusively target +Python 3. The final release of Beautiful Soup 4 to support Python 2 +was 4.9.3. + +# Supporting the project + +If you use Beautiful Soup as part of your professional work, please consider a +[Tidelift subscription](https://tidelift.com/subscription/pkg/pypi-beautifulsoup4?utm_source=pypi-beautifulsoup4&utm_medium=referral&utm_campaign=readme). +This will support many of the free software projects your organization +depends on, not just Beautiful Soup. + +If you use Beautiful Soup for personal projects, the best way to say +thank you is to read +[Tool Safety](https://www.crummy.com/software/BeautifulSoup/zine/), a zine I +wrote about what Beautiful Soup has taught me about software +development. + +# Building the documentation + +The bs4/doc/ directory contains full documentation in Sphinx +format. Run `make html` in that directory to create HTML +documentation. + +# Running the unit tests + +Beautiful Soup supports unit test discovery using Pytest: + +``` +$ pytest +``` + diff --git a/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD new file mode 100644 index 00000000..252891d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/RECORD @@ -0,0 +1,38 @@ +beautifulsoup4-4.14.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +beautifulsoup4-4.14.3.dist-info/METADATA,sha256=Ac93vA8Xp9FtgOcKXFM8ESfVdztimUfJ3WUpVlhKtsY,3812 +beautifulsoup4-4.14.3.dist-info/RECORD,, +beautifulsoup4-4.14.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +beautifulsoup4-4.14.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS,sha256=uYkjiRjh_aweRnF8tAW2PpJJeickE68NmJwd9siry28,2201 +beautifulsoup4-4.14.3.dist-info/licenses/LICENSE,sha256=VbTY1LHlvIbRDvrJG3TIe8t3UmsPW57a-LnNKtxzl7I,1441 +bs4/__init__.py,sha256=E7wiVp7oQK0JhdAYxpehZa8drv3W_sJv5oeTFiBfR5o,44386 +bs4/__pycache__/__init__.cpython-312.pyc,, +bs4/__pycache__/_deprecation.cpython-312.pyc,, +bs4/__pycache__/_typing.cpython-312.pyc,, +bs4/__pycache__/_warnings.cpython-312.pyc,, +bs4/__pycache__/css.cpython-312.pyc,, +bs4/__pycache__/dammit.cpython-312.pyc,, +bs4/__pycache__/diagnose.cpython-312.pyc,, +bs4/__pycache__/element.cpython-312.pyc,, +bs4/__pycache__/exceptions.cpython-312.pyc,, +bs4/__pycache__/filter.cpython-312.pyc,, +bs4/__pycache__/formatter.cpython-312.pyc,, +bs4/_deprecation.py,sha256=niHJCk37APg8KEuFOa57ZXaxLdBmc_2V6uuaJqu7r30,2408 +bs4/_typing.py,sha256=zNcx7R1yCTK8WwtumP28hc7CJ3pMyZXj_VAeYaNXMZA,7549 +bs4/_warnings.py,sha256=ZuOETgcnEbZgw2N0nnNXn6wvtrn2ut7AF0d98bvkMFc,4711 +bs4/builder/__init__.py,sha256=Rl4qjOXvdyyyjayOFqbkgoUoo81IgoyKD-RwWeVK59g,31194 +bs4/builder/__pycache__/__init__.cpython-312.pyc,, +bs4/builder/__pycache__/_html5lib.cpython-312.pyc,, +bs4/builder/__pycache__/_htmlparser.cpython-312.pyc,, +bs4/builder/__pycache__/_lxml.cpython-312.pyc,, +bs4/builder/_html5lib.py,sha256=hL6xUk4_I2i5CMguFoYFlrI26cY4Dut7fOEQrUctHIM,23607 +bs4/builder/_htmlparser.py,sha256=CnULPQV2rm4vLojJABpQ7Xm9diddnEZx2Wcz_VTC1Mg,17445 +bs4/builder/_lxml.py,sha256=ks1e8boA_nOA2oomAhxeudccR6ThbEE-EllFqHRoPLA,18969 +bs4/css.py,sha256=_m_l_4SGWHnY620VJ21j_qQH1RX3p91sYVemgKxaLsM,12713 +bs4/dammit.py,sha256=ZJWa9K32X6N2imFHleqUq0ekf592weU1lvULN_WYWYk,57024 +bs4/diagnose.py,sha256=at98iuxyOrqec4V8iwkTIbNUqBCsq9Lr3fDAQx2129Y,7846 +bs4/element.py,sha256=oXmj7LG_2NpsDK90mq73q0PMK0FjFBIGSeTTJLVwwTc,120237 +bs4/exceptions.py,sha256=Q9FOadNe8QRvzDMaKSXe2Wtl8JK_oAZW7mbFZBVP_GE,951 +bs4/filter.py,sha256=rw8ZNhTDLEJVCEiSifou5tZR_3zBLeuvAyouY82qU_E,29201 +bs4/formatter.py,sha256=uBT0k6W8O5kJ9PCuJYjra97yoUqC-dlM9D_v-oRM0r8,10478 +bs4/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/REQUESTED b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/REQUESTED similarity index 100% rename from venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/REQUESTED rename to venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/REQUESTED diff --git a/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL new file mode 100644 index 00000000..12228d41 --- /dev/null +++ b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS new file mode 100644 index 00000000..18926c29 --- /dev/null +++ b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/AUTHORS @@ -0,0 +1,49 @@ +Behold, mortal, the origins of Beautiful Soup... +================================================ + +Leonard Richardson is the primary maintainer. + +Aaron DeVore, Isaac Muse and Chris Papademetrious have made +significant contributions to the code base. + +Mark Pilgrim provided the encoding detection code that forms the base +of UnicodeDammit. + +Thomas Kluyver and Ezio Melotti finished the work of getting Beautiful +Soup 4 working under Python 3. + +Simon Willison wrote soupselect, which was used to make Beautiful Soup +support CSS selectors. Isaac Muse wrote SoupSieve, which made it +possible to _remove_ the CSS selector code from Beautiful Soup. + +Sam Ruby helped with a lot of edge cases. + +Jonathan Ellis was awarded the prestigious Beau Potage D'Or for his +work in solving the nestable tags conundrum. + +An incomplete list of people have contributed patches to Beautiful +Soup: + + Istvan Albert, Andrew Lin, Anthony Baxter, Oliver Beattie, Andrew +Boyko, Tony Chang, Francisco Canas, "Delong", Zephyr Fang, Fuzzy, +Roman Gaufman, Yoni Gilad, Richie Hindle, Toshihiro Kamiya, Peteris +Krumins, Kent Johnson, Marek Kapolka, Andreas Kostyrka, Roel Kramer, +Ben Last, Robert Leftwich, Stefaan Lippens, "liquider", Staffan +Malmgren, Ksenia Marasanova, JP Moins, Adam Monsen, John Nagle, "Jon", +Ed Oskiewicz, Martijn Peters, Greg Phillips, Giles Radford, Stefano +Revera, Arthur Rudolph, Marko Samastur, James Salter, Jouni Seppänen, +Alexander Schmolck, Tim Shirley, Geoffrey Sneddon, Ville Skyttä, +"Vikas", Jens Svalgaard, Andy Theyers, Eric Weiser, Glyn Webster, John +Wiseman, Paul Wright, Danny Yoo + +An incomplete list of people who made suggestions or found bugs or +found ways to break Beautiful Soup: + + Hanno Böck, Matteo Bertini, Chris Curvey, Simon Cusack, Bruce Eckel, + Matt Ernst, Michael Foord, Tom Harris, Bill de hOra, Donald Howes, + Matt Patterson, Scott Roberts, Steve Strassmann, Mike Williams, + warchild at redho dot com, Sami Kuisma, Carlos Rocha, Bob Hutchison, + Joren Mc, Michal Migurski, John Kleven, Tim Heaney, Tripp Lilley, Ed + Summers, Dennis Sutch, Chris Smith, Aaron Swartz, Stuart + Turner, Greg Edwards, Kevin J Kalupson, Nikos Kouremenos, Artur de + Sousa Rocha, Yichun Wei, Per Vognsen diff --git a/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE new file mode 100644 index 00000000..08e3a9cf --- /dev/null +++ b/venv/lib/python3.12/site-packages/beautifulsoup4-4.14.3.dist-info/licenses/LICENSE @@ -0,0 +1,31 @@ +Beautiful Soup is made available under the MIT license: + + Copyright (c) Leonard Richardson + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Beautiful Soup incorporates code from the html5lib library, which is +also made available under the MIT license. Copyright (c) James Graham +and other contributors + +Beautiful Soup has an optional dependency on the soupsieve library, +which is also made available under the MIT license. Copyright (c) +Isaac Muse diff --git a/venv/lib/python3.12/site-packages/bs4/__init__.py b/venv/lib/python3.12/site-packages/bs4/__init__.py new file mode 100644 index 00000000..19b7c0ed --- /dev/null +++ b/venv/lib/python3.12/site-packages/bs4/__init__.py @@ -0,0 +1,1174 @@ +"""Beautiful Soup Elixir and Tonic - "The Screen-Scraper's Friend". + +http://www.crummy.com/software/BeautifulSoup/ + +Beautiful Soup uses a pluggable XML or HTML parser to parse a +(possibly invalid) document into a tree representation. Beautiful Soup +provides methods and Pythonic idioms that make it easy to navigate, +search, and modify the parse tree. + +Beautiful Soup works with Python 3.7 and up. It works better if lxml +and/or html5lib is installed, but they are not required. + +For more than you ever wanted to know about Beautiful Soup, see the +documentation: http://www.crummy.com/software/BeautifulSoup/bs4/doc/ +""" + +__author__ = "Leonard Richardson (leonardr@segfault.org)" +__version__ = "4.14.3" +__copyright__ = "Copyright (c) 2004-2025 Leonard Richardson" +# Use of this source code is governed by the MIT license. +__license__ = "MIT" + +__all__ = [ + "AttributeResemblesVariableWarning", + "BeautifulSoup", + "Comment", + "Declaration", + "ProcessingInstruction", + "ResultSet", + "CSS", + "Script", + "Stylesheet", + "Tag", + "TemplateString", + "ElementFilter", + "UnicodeDammit", + "CData", + "Doctype", + + # Exceptions + "FeatureNotFound", + "ParserRejectedMarkup", + "StopParsing", + + # Warnings + "AttributeResemblesVariableWarning", + "GuessedAtParserWarning", + "MarkupResemblesLocatorWarning", + "UnusualUsageWarning", + "XMLParsedAsHTMLWarning", +] + +from collections import Counter +import io +import sys +import warnings + +# The very first thing we do is give a useful error if someone is +# running this code under Python 2. +if sys.version_info.major < 3: + raise ImportError( + "You are trying to use a Python 3-specific version of Beautiful Soup under Python 2. This will not work. The final version of Beautiful Soup to support Python 2 was 4.9.3." + ) + +from .builder import ( + builder_registry, + TreeBuilder, +) +from .builder._htmlparser import HTMLParserTreeBuilder +from .dammit import UnicodeDammit +from .css import CSS +from ._deprecation import ( + _deprecated, +) +from .element import ( + CData, + Comment, + DEFAULT_OUTPUT_ENCODING, + Declaration, + Doctype, + NavigableString, + PageElement, + ProcessingInstruction, + PYTHON_SPECIFIC_ENCODINGS, + ResultSet, + Script, + Stylesheet, + Tag, + TemplateString, +) +from .formatter import Formatter +from .filter import ( + ElementFilter, + SoupStrainer, +) +from typing import ( + Any, + cast, + Counter as CounterType, + Dict, + Iterator, + List, + Sequence, + Sized, + Optional, + Type, + Union, +) + +from bs4._typing import ( + _Encoding, + _Encodings, + _IncomingMarkup, + _InsertableElement, + _RawAttributeValue, + _RawAttributeValues, + _RawMarkup, +) + +# Import all warnings and exceptions into the main package. +from bs4.exceptions import ( + FeatureNotFound, + ParserRejectedMarkup, + StopParsing, +) +from bs4._warnings import ( + AttributeResemblesVariableWarning, + GuessedAtParserWarning, + MarkupResemblesLocatorWarning, + UnusualUsageWarning, + XMLParsedAsHTMLWarning, +) + + +class BeautifulSoup(Tag): + """A data structure representing a parsed HTML or XML document. + + Most of the methods you'll call on a BeautifulSoup object are inherited from + PageElement or Tag. + + Internally, this class defines the basic interface called by the + tree builders when converting an HTML/XML document into a data + structure. The interface abstracts away the differences between + parsers. To write a new tree builder, you'll need to understand + these methods as a whole. + + These methods will be called by the BeautifulSoup constructor: + * reset() + * feed(markup) + + The tree builder may call these methods from its feed() implementation: + * handle_starttag(name, attrs) # See note about return value + * handle_endtag(name) + * handle_data(data) # Appends to the current data node + * endData(containerClass) # Ends the current data node + + No matter how complicated the underlying parser is, you should be + able to build a tree using 'start tag' events, 'end tag' events, + 'data' events, and "done with data" events. + + If you encounter an empty-element tag (aka a self-closing tag, + like HTML's
    tag), call handle_starttag and then + handle_endtag. + """ + + #: Since `BeautifulSoup` subclasses `Tag`, it's possible to treat it as + #: a `Tag` with a `Tag.name`. Hoever, this name makes it clear the + #: `BeautifulSoup` object isn't a real markup tag. + ROOT_TAG_NAME: str = "[document]" + + #: If the end-user gives no indication which tree builder they + #: want, look for one with these features. + DEFAULT_BUILDER_FEATURES: Sequence[str] = ["html", "fast"] + + #: A string containing all ASCII whitespace characters, used in + #: during parsing to detect data chunks that seem 'empty'. + ASCII_SPACES: str = "\x20\x0a\x09\x0c\x0d" + + # FUTURE PYTHON: + element_classes: Dict[Type[PageElement], Type[PageElement]] #: :meta private: + builder: TreeBuilder #: :meta private: + is_xml: bool + known_xml: Optional[bool] + parse_only: Optional[SoupStrainer] #: :meta private: + + # These members are only used while parsing markup. + markup: Optional[_RawMarkup] #: :meta private: + current_data: List[str] #: :meta private: + currentTag: Optional[Tag] #: :meta private: + tagStack: List[Tag] #: :meta private: + open_tag_counter: CounterType[str] #: :meta private: + preserve_whitespace_tag_stack: List[Tag] #: :meta private: + string_container_stack: List[Tag] #: :meta private: + _most_recent_element: Optional[PageElement] #: :meta private: + + #: Beautiful Soup's best guess as to the character encoding of the + #: original document. + original_encoding: Optional[_Encoding] + + #: The character encoding, if any, that was explicitly defined + #: in the original document. This may or may not match + #: `BeautifulSoup.original_encoding`. + declared_html_encoding: Optional[_Encoding] + + #: This is True if the markup that was parsed contains + #: U+FFFD REPLACEMENT_CHARACTER characters which were not present + #: in the original markup. These mark character sequences that + #: could not be represented in Unicode. + contains_replacement_characters: bool + + def __init__( + self, + markup: _IncomingMarkup = "", + features: Optional[Union[str, Sequence[str]]] = None, + builder: Optional[Union[TreeBuilder, Type[TreeBuilder]]] = None, + parse_only: Optional[SoupStrainer] = None, + from_encoding: Optional[_Encoding] = None, + exclude_encodings: Optional[_Encodings] = None, + element_classes: Optional[Dict[Type[PageElement], Type[PageElement]]] = None, + **kwargs: Any, + ): + """Constructor. + + :param markup: A string or a file-like object representing + markup to be parsed. + + :param features: Desirable features of the parser to be + used. This may be the name of a specific parser ("lxml", + "lxml-xml", "html.parser", or "html5lib") or it may be the + type of markup to be used ("html", "html5", "xml"). It's + recommended that you name a specific parser, so that + Beautiful Soup gives you the same results across platforms + and virtual environments. + + :param builder: A TreeBuilder subclass to instantiate (or + instance to use) instead of looking one up based on + `features`. You only need to use this if you've implemented a + custom TreeBuilder. + + :param parse_only: A SoupStrainer. Only parts of the document + matching the SoupStrainer will be considered. This is useful + when parsing part of a document that would otherwise be too + large to fit into memory. + + :param from_encoding: A string indicating the encoding of the + document to be parsed. Pass this in if Beautiful Soup is + guessing wrongly about the document's encoding. + + :param exclude_encodings: A list of strings indicating + encodings known to be wrong. Pass this in if you don't know + the document's encoding but you know Beautiful Soup's guess is + wrong. + + :param element_classes: A dictionary mapping BeautifulSoup + classes like Tag and NavigableString, to other classes you'd + like to be instantiated instead as the parse tree is + built. This is useful for subclassing Tag or NavigableString + to modify default behavior. + + :param kwargs: For backwards compatibility purposes, the + constructor accepts certain keyword arguments used in + Beautiful Soup 3. None of these arguments do anything in + Beautiful Soup 4; they will result in a warning and then be + ignored. + + Apart from this, any keyword arguments passed into the + BeautifulSoup constructor are propagated to the TreeBuilder + constructor. This makes it possible to configure a + TreeBuilder by passing in arguments, not just by saying which + one to use. + """ + if "convertEntities" in kwargs: + del kwargs["convertEntities"] + warnings.warn( + "BS4 does not respect the convertEntities argument to the " + "BeautifulSoup constructor. Entities are always converted " + "to Unicode characters." + ) + + if "markupMassage" in kwargs: + del kwargs["markupMassage"] + warnings.warn( + "BS4 does not respect the markupMassage argument to the " + "BeautifulSoup constructor. The tree builder is responsible " + "for any necessary markup massage." + ) + + if "smartQuotesTo" in kwargs: + del kwargs["smartQuotesTo"] + warnings.warn( + "BS4 does not respect the smartQuotesTo argument to the " + "BeautifulSoup constructor. Smart quotes are always converted " + "to Unicode characters." + ) + + if "selfClosingTags" in kwargs: + del kwargs["selfClosingTags"] + warnings.warn( + "Beautiful Soup 4 does not respect the selfClosingTags argument to the " + "BeautifulSoup constructor. The tree builder is responsible " + "for understanding self-closing tags." + ) + + if "isHTML" in kwargs: + del kwargs["isHTML"] + warnings.warn( + "Beautiful Soup 4 does not respect the isHTML argument to the " + "BeautifulSoup constructor. Suggest you use " + "features='lxml' for HTML and features='lxml-xml' for " + "XML." + ) + + def deprecated_argument(old_name: str, new_name: str) -> Optional[Any]: + if old_name in kwargs: + warnings.warn( + 'The "%s" argument to the BeautifulSoup constructor ' + 'was renamed to "%s" in Beautiful Soup 4.0.0' + % (old_name, new_name), + DeprecationWarning, + stacklevel=3, + ) + return kwargs.pop(old_name) + return None + + parse_only = parse_only or deprecated_argument("parseOnlyThese", "parse_only") + if parse_only is not None: + # Issue a warning if we can tell in advance that + # parse_only will exclude the entire tree. + if parse_only.excludes_everything: + warnings.warn( + f"The given value for parse_only will exclude everything: {parse_only}", + UserWarning, + stacklevel=3, + ) + + from_encoding = from_encoding or deprecated_argument( + "fromEncoding", "from_encoding" + ) + + if from_encoding and isinstance(markup, str): + warnings.warn( + "You provided Unicode markup but also provided a value for from_encoding. Your from_encoding will be ignored." + ) + from_encoding = None + + self.element_classes = element_classes or dict() + + # We need this information to track whether or not the builder + # was specified well enough that we can omit the 'you need to + # specify a parser' warning. + original_builder = builder + original_features = features + + builder_class: Optional[Type[TreeBuilder]] = None + if isinstance(builder, type): + # A builder class was passed in; it needs to be instantiated. + builder_class = builder + builder = None + elif builder is None: + if isinstance(features, str): + features = [features] + if features is None or len(features) == 0: + features = self.DEFAULT_BUILDER_FEATURES + possible_builder_class = builder_registry.lookup(*features) + if possible_builder_class is None: + raise FeatureNotFound( + "Couldn't find a tree builder with the features you " + "requested: %s. Do you need to install a parser library?" + % ",".join(features) + ) + builder_class = possible_builder_class + + # At this point either we have a TreeBuilder instance in + # builder, or we have a builder_class that we can instantiate + # with the remaining **kwargs. + if builder is None: + assert builder_class is not None + builder = builder_class(**kwargs) + if ( + not original_builder + and not ( + original_features == builder.NAME + or ( + isinstance(original_features, str) + and original_features in builder.ALTERNATE_NAMES + ) + ) + and markup + ): + # The user did not tell us which TreeBuilder to use, + # and we had to guess. Issue a warning. + if builder.is_xml: + markup_type = "XML" + else: + markup_type = "HTML" + + # This code adapted from warnings.py so that we get the same line + # of code as our warnings.warn() call gets, even if the answer is wrong + # (as it may be in a multithreading situation). + caller = None + try: + caller = sys._getframe(1) + except ValueError: + pass + if caller: + globals = caller.f_globals + line_number = caller.f_lineno + else: + globals = sys.__dict__ + line_number = 1 + filename = globals.get("__file__") + if filename: + fnl = filename.lower() + if fnl.endswith((".pyc", ".pyo")): + filename = filename[:-1] + if filename: + # If there is no filename at all, the user is most likely in a REPL, + # and the warning is not necessary. + values = dict( + filename=filename, + line_number=line_number, + parser=builder.NAME, + markup_type=markup_type, + ) + warnings.warn( + GuessedAtParserWarning.MESSAGE % values, + GuessedAtParserWarning, + stacklevel=2, + ) + else: + if kwargs: + warnings.warn( + "Keyword arguments to the BeautifulSoup constructor will be ignored. These would normally be passed into the TreeBuilder constructor, but a TreeBuilder instance was passed in as `builder`." + ) + + self.builder = builder + self.is_xml = builder.is_xml + self.known_xml = self.is_xml + self._namespaces = dict() + self.parse_only = parse_only + + if hasattr(markup, "read"): # It's a file-type object. + markup = cast(io.IOBase, markup).read() + elif not isinstance(markup, (bytes, str)) and not hasattr(markup, "__len__"): + raise TypeError( + f"Incoming markup is of an invalid type: {markup!r}. Markup must be a string, a bytestring, or an open filehandle." + ) + elif isinstance(markup, Sized) and len(markup) <= 256 and ( + (isinstance(markup, bytes) and b"<" not in markup and b"\n" not in markup) + or (isinstance(markup, str) and "<" not in markup and "\n" not in markup) + ): + # Issue warnings for a couple beginner problems + # involving passing non-markup to Beautiful Soup. + # Beautiful Soup will still parse the input as markup, + # since that is sometimes the intended behavior. + if not self._markup_is_url(markup): + self._markup_resembles_filename(markup) + + # At this point we know markup is a string or bytestring. If + # it was a file-type object, we've read from it. + markup = cast(_RawMarkup, markup) + + rejections = [] + success = False + for ( + self.markup, + self.original_encoding, + self.declared_html_encoding, + self.contains_replacement_characters, + ) in self.builder.prepare_markup( + markup, from_encoding, exclude_encodings=exclude_encodings + ): + self.reset() + self.builder.initialize_soup(self) + try: + self._feed() + success = True + break + except ParserRejectedMarkup as e: + rejections.append(e) + pass + + if not success: + other_exceptions = [str(e) for e in rejections] + raise ParserRejectedMarkup( + "The markup you provided was rejected by the parser. Trying a different parser or a different encoding may help.\n\nOriginal exception(s) from parser:\n " + + "\n ".join(other_exceptions) + ) + + # Clear out the markup and remove the builder's circular + # reference to this object. + self.markup = None + self.builder.soup = None + + def copy_self(self) -> "BeautifulSoup": + """Create a new BeautifulSoup object with the same TreeBuilder, + but not associated with any markup. + + This is the first step of the deepcopy process. + """ + clone = type(self)("", None, self.builder) + + # Keep track of the encoding of the original document, + # since we won't be parsing it again. + clone.original_encoding = self.original_encoding + return clone + + def __getstate__(self) -> Dict[str, Any]: + # Frequently a tree builder can't be pickled. + d = dict(self.__dict__) + if "builder" in d and d["builder"] is not None and not self.builder.picklable: + d["builder"] = type(self.builder) + # Store the contents as a Unicode string. + d["contents"] = [] + d["markup"] = self.decode() + + # If _most_recent_element is present, it's a Tag object left + # over from initial parse. It might not be picklable and we + # don't need it. + if "_most_recent_element" in d: + del d["_most_recent_element"] + return d + + def __setstate__(self, state: Dict[str, Any]) -> None: + # If necessary, restore the TreeBuilder by looking it up. + self.__dict__ = state + if isinstance(self.builder, type): + self.builder = self.builder() + elif not self.builder: + # We don't know which builder was used to build this + # parse tree, so use a default we know is always available. + self.builder = HTMLParserTreeBuilder() + self.builder.soup = self + self.reset() + self._feed() + + @classmethod + @_deprecated( + replaced_by="nothing (private method, will be removed)", version="4.13.0" + ) + def _decode_markup(cls, markup: _RawMarkup) -> str: + """Ensure `markup` is Unicode so it's safe to send into warnings.warn. + + warnings.warn had this problem back in 2010 but fortunately + not anymore. This has not been used for a long time; I just + noticed that fact while working on 4.13.0. + """ + if isinstance(markup, bytes): + decoded = markup.decode("utf-8", "replace") + else: + decoded = markup + return decoded + + @classmethod + def _markup_is_url(cls, markup: _RawMarkup) -> bool: + """Error-handling method to raise a warning if incoming markup looks + like a URL. + + :param markup: A string of markup. + :return: Whether or not the markup resembled a URL + closely enough to justify issuing a warning. + """ + problem: bool = False + if isinstance(markup, bytes): + problem = ( + any(markup.startswith(prefix) for prefix in (b"http:", b"https:")) + and b" " not in markup + ) + elif isinstance(markup, str): + problem = ( + any(markup.startswith(prefix) for prefix in ("http:", "https:")) + and " " not in markup + ) + else: + return False + + if not problem: + return False + warnings.warn( + MarkupResemblesLocatorWarning.URL_MESSAGE % dict(what="URL"), + MarkupResemblesLocatorWarning, + stacklevel=3, + ) + return True + + @classmethod + def _markup_resembles_filename(cls, markup: _RawMarkup) -> bool: + """Error-handling method to issue a warning if incoming markup + resembles a filename. + + :param markup: A string of markup. + :return: Whether or not the markup resembled a filename + closely enough to justify issuing a warning. + """ + markup_b: bytes + + # We're only checking ASCII characters, so rather than write + # the same tests twice, convert Unicode to a bytestring and + # operate on the bytestring. + if isinstance(markup, str): + markup_b = markup.encode("utf8") + else: + markup_b = markup + + # Step 1: does it end with a common textual file extension? + filelike = False + lower = markup_b.lower() + extensions = [b".html", b".htm", b".xml", b".xhtml", b".txt"] + if any(lower.endswith(ext) for ext in extensions): + filelike = True + if not filelike: + return False + + # Step 2: it _might_ be a file, but there are a few things + # we can look for that aren't very common in filenames. + + # Characters that have special meaning to Unix shells. (< was + # excluded before this method was called.) + # + # Many of these are also reserved characters that cannot + # appear in Windows filenames. + for byte in markup_b: + if byte in b"?*#&;>$|": + return False + + # Two consecutive forward slashes (as seen in a URL) or two + # consecutive spaces (as seen in fixed-width data). + # + # (Paths to Windows network shares contain consecutive + # backslashes, so checking that doesn't seem as helpful.) + if b"//" in markup_b: + return False + if b" " in markup_b: + return False + + # A colon in any position other than position 1 (e.g. after a + # Windows drive letter). + if markup_b.startswith(b":"): + return False + colon_i = markup_b.rfind(b":") + if colon_i not in (-1, 1): + return False + + # Step 3: If it survived all of those checks, it's similar + # enough to a file to justify issuing a warning. + warnings.warn( + MarkupResemblesLocatorWarning.FILENAME_MESSAGE % dict(what="filename"), + MarkupResemblesLocatorWarning, + stacklevel=3, + ) + return True + + def _feed(self) -> None: + """Internal method that parses previously set markup, creating a large + number of Tag and NavigableString objects. + """ + # Convert the document to Unicode. + self.builder.reset() + + if self.markup is not None: + self.builder.feed(self.markup) + # Close out any unfinished strings and close all the open tags. + self.endData() + while ( + self.currentTag is not None and self.currentTag.name != self.ROOT_TAG_NAME + ): + self.popTag() + + def reset(self) -> None: + """Reset this object to a state as though it had never parsed any + markup. + """ + Tag.__init__(self, self, self.builder, self.ROOT_TAG_NAME) + self.hidden = True + self.builder.reset() + self.current_data = [] + self.currentTag = None + self.tagStack = [] + self.open_tag_counter = Counter() + self.preserve_whitespace_tag_stack = [] + self.string_container_stack = [] + self._most_recent_element = None + self.pushTag(self) + + def new_tag( + self, + name: str, + namespace: Optional[str] = None, + nsprefix: Optional[str] = None, + attrs: Optional[_RawAttributeValues] = None, + sourceline: Optional[int] = None, + sourcepos: Optional[int] = None, + string: Optional[str] = None, + **kwattrs: _RawAttributeValue, + ) -> Tag: + """Create a new Tag associated with this BeautifulSoup object. + + :param name: The name of the new Tag. + :param namespace: The URI of the new Tag's XML namespace, if any. + :param prefix: The prefix for the new Tag's XML namespace, if any. + :param attrs: A dictionary of this Tag's attribute values; can + be used instead of ``kwattrs`` for attributes like 'class' + that are reserved words in Python. + :param sourceline: The line number where this tag was + (purportedly) found in its source document. + :param sourcepos: The character position within ``sourceline`` where this + tag was (purportedly) found. + :param string: String content for the new Tag, if any. + :param kwattrs: Keyword arguments for the new Tag's attribute values. + + """ + attr_container = self.builder.attribute_dict_class(**kwattrs) + if attrs is not None: + attr_container.update(attrs) + tag_class = self.element_classes.get(Tag, Tag) + + # Assume that this is either Tag or a subclass of Tag. If not, + # the user brought type-unsafety upon themselves. + tag_class = cast(Type[Tag], tag_class) + tag = tag_class( + None, + self.builder, + name, + namespace, + nsprefix, + attr_container, + sourceline=sourceline, + sourcepos=sourcepos, + ) + + if string is not None: + tag.string = string + return tag + + def string_container( + self, base_class: Optional[Type[NavigableString]] = None + ) -> Type[NavigableString]: + """Find the class that should be instantiated to hold a given kind of + string. + + This may be a built-in Beautiful Soup class or a custom class passed + in to the BeautifulSoup constructor. + """ + container = base_class or NavigableString + + # The user may want us to use some other class (hopefully a + # custom subclass) instead of the one we'd use normally. + container = cast( + Type[NavigableString], self.element_classes.get(container, container) + ) + + # On top of that, we may be inside a tag that needs a special + # container class. + if self.string_container_stack and container is NavigableString: + container = self.builder.string_containers.get( + self.string_container_stack[-1].name, container + ) + return container + + def new_string( + self, s: str, subclass: Optional[Type[NavigableString]] = None + ) -> NavigableString: + """Create a new `NavigableString` associated with this `BeautifulSoup` + object. + + :param s: The string content of the `NavigableString` + :param subclass: The subclass of `NavigableString`, if any, to + use. If a document is being processed, an appropriate + subclass for the current location in the document will + be determined automatically. + """ + container = self.string_container(subclass) + return container(s) + + def insert_before(self, *args: _InsertableElement) -> List[PageElement]: + """This method is part of the PageElement API, but `BeautifulSoup` doesn't implement + it because there is nothing before or after it in the parse tree. + """ + raise NotImplementedError( + "BeautifulSoup objects don't support insert_before()." + ) + + def insert_after(self, *args: _InsertableElement) -> List[PageElement]: + """This method is part of the PageElement API, but `BeautifulSoup` doesn't implement + it because there is nothing before or after it in the parse tree. + """ + raise NotImplementedError("BeautifulSoup objects don't support insert_after().") + + def popTag(self) -> Optional[Tag]: + """Internal method called by _popToTag when a tag is closed. + + :meta private: + """ + if not self.tagStack: + # Nothing to pop. This shouldn't happen. + return None + tag = self.tagStack.pop() + if tag.name in self.open_tag_counter: + self.open_tag_counter[tag.name] -= 1 + if ( + self.preserve_whitespace_tag_stack + and tag == self.preserve_whitespace_tag_stack[-1] + ): + self.preserve_whitespace_tag_stack.pop() + if self.string_container_stack and tag == self.string_container_stack[-1]: + self.string_container_stack.pop() + # print("Pop", tag.name) + if self.tagStack: + self.currentTag = self.tagStack[-1] + return self.currentTag + + def pushTag(self, tag: Tag) -> None: + """Internal method called by handle_starttag when a tag is opened. + + :meta private: + """ + # print("Push", tag.name) + if self.currentTag is not None: + self.currentTag.contents.append(tag) + self.tagStack.append(tag) + self.currentTag = self.tagStack[-1] + if tag.name != self.ROOT_TAG_NAME: + self.open_tag_counter[tag.name] += 1 + if tag.name in self.builder.preserve_whitespace_tags: + self.preserve_whitespace_tag_stack.append(tag) + if tag.name in self.builder.string_containers: + self.string_container_stack.append(tag) + + def endData(self, containerClass: Optional[Type[NavigableString]] = None) -> None: + """Method called by the TreeBuilder when the end of a data segment + occurs. + + :param containerClass: The class to use when incorporating the + data segment into the parse tree. + + :meta private: + """ + if self.current_data: + current_data = "".join(self.current_data) + # If whitespace is not preserved, and this string contains + # nothing but ASCII spaces, replace it with a single space + # or newline. + if not self.preserve_whitespace_tag_stack: + strippable = True + for i in current_data: + if i not in self.ASCII_SPACES: + strippable = False + break + if strippable: + if "\n" in current_data: + current_data = "\n" + else: + current_data = " " + + # Reset the data collector. + self.current_data = [] + + # Should we add this string to the tree at all? + if ( + self.parse_only + and len(self.tagStack) <= 1 + and (not self.parse_only.allow_string_creation(current_data)) + ): + return + + containerClass = self.string_container(containerClass) + o = containerClass(current_data) + self.object_was_parsed(o) + + def object_was_parsed( + self, + o: PageElement, + parent: Optional[Tag] = None, + most_recent_element: Optional[PageElement] = None, + ) -> None: + """Method called by the TreeBuilder to integrate an object into the + parse tree. + + :meta private: + """ + if parent is None: + parent = self.currentTag + assert parent is not None + previous_element: Optional[PageElement] + if most_recent_element is not None: + previous_element = most_recent_element + else: + previous_element = self._most_recent_element + + next_element = previous_sibling = next_sibling = None + if isinstance(o, Tag): + next_element = o.next_element + next_sibling = o.next_sibling + previous_sibling = o.previous_sibling + if previous_element is None: + previous_element = o.previous_element + + fix = parent.next_element is not None + + o.setup(parent, previous_element, next_element, previous_sibling, next_sibling) + + self._most_recent_element = o + parent.contents.append(o) + + # Check if we are inserting into an already parsed node. + if fix: + self._linkage_fixer(parent) + + def _linkage_fixer(self, el: Tag) -> None: + """Make sure linkage of this fragment is sound.""" + + first = el.contents[0] + child = el.contents[-1] + descendant: PageElement = child + + if child is first and el.parent is not None: + # Parent should be linked to first child + el.next_element = child + # We are no longer linked to whatever this element is + prev_el = child.previous_element + if prev_el is not None and prev_el is not el: + prev_el.next_element = None + # First child should be linked to the parent, and no previous siblings. + child.previous_element = el + child.previous_sibling = None + + # We have no sibling as we've been appended as the last. + child.next_sibling = None + + # This index is a tag, dig deeper for a "last descendant" + if isinstance(child, Tag) and child.contents: + # _last_decendant is typed as returning Optional[PageElement], + # but the value can't be None here, because el is a Tag + # which we know has contents. + descendant = cast(PageElement, child._last_descendant(False)) + + # As the final step, link last descendant. It should be linked + # to the parent's next sibling (if found), else walk up the chain + # and find a parent with a sibling. It should have no next sibling. + descendant.next_element = None + descendant.next_sibling = None + + target: Optional[Tag] = el + while True: + if target is None: + break + elif target.next_sibling is not None: + descendant.next_element = target.next_sibling + target.next_sibling.previous_element = child + break + target = target.parent + + def _popToTag( + self, name: str, nsprefix: Optional[str] = None, inclusivePop: bool = True + ) -> Optional[Tag]: + """Pops the tag stack up to and including the most recent + instance of the given tag. + + If there are no open tags with the given name, nothing will be + popped. + + :param name: Pop up to the most recent tag with this name. + :param nsprefix: The namespace prefix that goes with `name`. + :param inclusivePop: It this is false, pops the tag stack up + to but *not* including the most recent instqance of the + given tag. + + :meta private: + """ + # print("Popping to %s" % name) + if name == self.ROOT_TAG_NAME: + # The BeautifulSoup object itself can never be popped. + return None + + most_recently_popped = None + + stack_size = len(self.tagStack) + for i in range(stack_size - 1, 0, -1): + if not self.open_tag_counter.get(name): + break + t = self.tagStack[i] + if name == t.name and nsprefix == t.prefix: + if inclusivePop: + most_recently_popped = self.popTag() + break + most_recently_popped = self.popTag() + + return most_recently_popped + + def handle_starttag( + self, + name: str, + namespace: Optional[str], + nsprefix: Optional[str], + attrs: _RawAttributeValues, + sourceline: Optional[int] = None, + sourcepos: Optional[int] = None, + namespaces: Optional[Dict[str, str]] = None, + ) -> Optional[Tag]: + """Called by the tree builder when a new tag is encountered. + + :param name: Name of the tag. + :param nsprefix: Namespace prefix for the tag. + :param attrs: A dictionary of attribute values. Note that + attribute values are expected to be simple strings; processing + of multi-valued attributes such as "class" comes later. + :param sourceline: The line number where this tag was found in its + source document. + :param sourcepos: The character position within `sourceline` where this + tag was found. + :param namespaces: A dictionary of all namespace prefix mappings + currently in scope in the document. + + If this method returns None, the tag was rejected by an active + `ElementFilter`. You should proceed as if the tag had not occurred + in the document. For instance, if this was a self-closing tag, + don't call handle_endtag. + + :meta private: + """ + # print("Start tag %s: %s" % (name, attrs)) + self.endData() + + if ( + self.parse_only + and len(self.tagStack) <= 1 + and not self.parse_only.allow_tag_creation(nsprefix, name, attrs) + ): + return None + + tag_class = self.element_classes.get(Tag, Tag) + # Assume that this is either Tag or a subclass of Tag. If not, + # the user brought type-unsafety upon themselves. + tag_class = cast(Type[Tag], tag_class) + tag = tag_class( + self, + self.builder, + name, + namespace, + nsprefix, + attrs, + self.currentTag, + self._most_recent_element, + sourceline=sourceline, + sourcepos=sourcepos, + namespaces=namespaces, + ) + if tag is None: + return tag + if self._most_recent_element is not None: + self._most_recent_element.next_element = tag + self._most_recent_element = tag + self.pushTag(tag) + return tag + + def handle_endtag(self, name: str, nsprefix: Optional[str] = None) -> None: + """Called by the tree builder when an ending tag is encountered. + + :param name: Name of the tag. + :param nsprefix: Namespace prefix for the tag. + + :meta private: + """ + # print("End tag: " + name) + self.endData() + self._popToTag(name, nsprefix) + + def handle_data(self, data: str) -> None: + """Called by the tree builder when a chunk of textual data is + encountered. + + :meta private: + """ + self.current_data.append(data) + + def decode( + self, + indent_level: Optional[int] = None, + eventual_encoding: _Encoding = DEFAULT_OUTPUT_ENCODING, + formatter: Union[Formatter, str] = "minimal", + iterator: Optional[Iterator[PageElement]] = None, + **kwargs: Any, + ) -> str: + """Returns a string representation of the parse tree + as a full HTML or XML document. + + :param indent_level: Each line of the rendering will be + indented this many levels. (The ``formatter`` decides what a + 'level' means, in terms of spaces or other characters + output.) This is used internally in recursive calls while + pretty-printing. + :param eventual_encoding: The encoding of the final document. + If this is None, the document will be a Unicode string. + :param formatter: Either a `Formatter` object, or a string naming one of + the standard formatters. + :param iterator: The iterator to use when navigating over the + parse tree. This is only used by `Tag.decode_contents` and + you probably won't need to use it. + """ + if self.is_xml: + # Print the XML declaration + encoding_part = "" + declared_encoding: Optional[str] = eventual_encoding + if eventual_encoding in PYTHON_SPECIFIC_ENCODINGS: + # This is a special Python encoding; it can't actually + # go into an XML document because it means nothing + # outside of Python. + declared_encoding = None + if declared_encoding is not None: + encoding_part = ' encoding="%s"' % declared_encoding + prefix = '\n' % encoding_part + else: + prefix = "" + + # Prior to 4.13.0, the first argument to this method was a + # bool called pretty_print, which gave the method a different + # signature from its superclass implementation, Tag.decode. + # + # The signatures of the two methods now match, but just in + # case someone is still passing a boolean in as the first + # argument to this method (or a keyword argument with the old + # name), we can handle it and put out a DeprecationWarning. + warning: Optional[str] = None + pretty_print: Optional[bool] = None + if isinstance(indent_level, bool): + if indent_level is True: + indent_level = 0 + elif indent_level is False: + indent_level = None + warning = f"As of 4.13.0, the first argument to BeautifulSoup.decode has been changed from bool to int, to match Tag.decode. Pass in a value of {indent_level} instead." + else: + pretty_print = kwargs.pop("pretty_print", None) + assert not kwargs + if pretty_print is not None: + if pretty_print is True: + indent_level = 0 + elif pretty_print is False: + indent_level = None + warning = f"As of 4.13.0, the pretty_print argument to BeautifulSoup.decode has been removed, to match Tag.decode. Pass in a value of indent_level={indent_level} instead." + + if warning: + warnings.warn(warning, DeprecationWarning, stacklevel=2) + elif indent_level is False or pretty_print is False: + indent_level = None + return prefix + super(BeautifulSoup, self).decode( + indent_level, eventual_encoding, formatter, iterator + ) + + +# Aliases to make it easier to get started quickly, e.g. 'from bs4 import _soup' +_s = BeautifulSoup +_soup = BeautifulSoup + + +class BeautifulStoneSoup(BeautifulSoup): + """Deprecated interface to an XML parser.""" + + def __init__(self, *args: Any, **kwargs: Any): + kwargs["features"] = "xml" + warnings.warn( + "The BeautifulStoneSoup class was deprecated in version 4.0.0. Instead of using " + 'it, pass features="xml" into the BeautifulSoup constructor.', + DeprecationWarning, + stacklevel=2, + ) + super(BeautifulStoneSoup, self).__init__(*args, **kwargs) + + +# If this file is run as a script, act as an HTML pretty-printer. +if __name__ == "__main__": + import sys + + soup = BeautifulSoup(sys.stdin) + print((soup.prettify())) diff --git a/venv/lib/python3.12/site-packages/bs4/_deprecation.py b/venv/lib/python3.12/site-packages/bs4/_deprecation.py new file mode 100644 index 00000000..a7b5685b --- /dev/null +++ b/venv/lib/python3.12/site-packages/bs4/_deprecation.py @@ -0,0 +1,80 @@ +"""Helper functions for deprecation. + +This interface is itself unstable and may change without warning. Do +not use these functions yourself, even as a joke. The underscores are +there for a reason. No support will be given. + +In particular, most of this will go away without warning once +Beautiful Soup drops support for Python 3.11, since Python 3.12 +defines a `@typing.deprecated() +decorator. `_ +""" + +import functools +import warnings + +from typing import ( + Any, + Callable, +) + + +def _deprecated_alias(old_name: str, new_name: str, version: str): + """Alias one attribute name to another for backward compatibility + + :meta private: + """ + + @property # type:ignore + def alias(self) -> Any: + ":meta private:" + warnings.warn( + f"Access to deprecated property {old_name}. (Replaced by {new_name}) -- Deprecated since version {version}.", + DeprecationWarning, + stacklevel=2, + ) + return getattr(self, new_name) + + @alias.setter + def alias(self, value: str) -> None: + ":meta private:" + warnings.warn( + f"Write to deprecated property {old_name}. (Replaced by {new_name}) -- Deprecated since version {version}.", + DeprecationWarning, + stacklevel=2, + ) + return setattr(self, new_name, value) + + return alias + + +def _deprecated_function_alias( + old_name: str, new_name: str, version: str +) -> Callable[[Any], Any]: + def alias(self, *args: Any, **kwargs: Any) -> Any: + ":meta private:" + warnings.warn( + f"Call to deprecated method {old_name}. (Replaced by {new_name}) -- Deprecated since version {version}.", + DeprecationWarning, + stacklevel=2, + ) + return getattr(self, new_name)(*args, **kwargs) + + return alias + + +def _deprecated(replaced_by: str, version: str) -> Callable: + def deprecate(func: Callable) -> Callable: + @functools.wraps(func) + def with_warning(*args: Any, **kwargs: Any) -> Any: + ":meta private:" + warnings.warn( + f"Call to deprecated method {func.__name__}. (Replaced by {replaced_by}) -- Deprecated since version {version}.", + DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return with_warning + + return deprecate diff --git a/venv/lib/python3.12/site-packages/bs4/_typing.py b/venv/lib/python3.12/site-packages/bs4/_typing.py new file mode 100644 index 00000000..0ab69df1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/bs4/_typing.py @@ -0,0 +1,205 @@ +# Custom type aliases used throughout Beautiful Soup to improve readability. + +# Notes on improvements to the type system in newer versions of Python +# that can be used once Beautiful Soup drops support for older +# versions: +# +# * ClassVar can be put on class variables now. +# * In 3.10, x|y is an accepted shorthand for Union[x,y]. +# * In 3.10, TypeAlias gains capabilities that can be used to +# improve the tree matching types (I don't remember what, exactly). +# * In 3.9 it's possible to specialize the re.Match type, +# e.g. re.Match[str]. In 3.8 there's a typing.re namespace for this, +# but it's removed in 3.12, so to support the widest possible set of +# versions I'm not using it. + +from typing_extensions import ( + runtime_checkable, + Protocol, + TypeAlias, +) +from typing import ( + Any, + Callable, + Dict, + IO, + Iterable, + Mapping, + Optional, + Pattern, + TYPE_CHECKING, + Union, +) + +if TYPE_CHECKING: + from bs4.element import ( + AttributeValueList, + NamespacedAttribute, + NavigableString, + PageElement, + ResultSet, + Tag, + ) + + +@runtime_checkable +class _RegularExpressionProtocol(Protocol): + """A protocol object which can accept either Python's built-in + `re.Pattern` objects, or the similar ``Regex`` objects defined by the + third-party ``regex`` package. + """ + + def search( + self, string: str, pos: int = ..., endpos: int = ... + ) -> Optional[Any]: ... + + @property + def pattern(self) -> str: ... + + +# Aliases for markup in various stages of processing. +# +#: The rawest form of markup: either a string, bytestring, or an open filehandle. +_IncomingMarkup: TypeAlias = Union[str, bytes, IO[str], IO[bytes]] + +#: Markup that is in memory but has (potentially) yet to be converted +#: to Unicode. +_RawMarkup: TypeAlias = Union[str, bytes] + +# Aliases for character encodings +# + +#: A data encoding. +_Encoding: TypeAlias = str + +#: One or more data encodings. +_Encodings: TypeAlias = Iterable[_Encoding] + +# Aliases for XML namespaces +# + +#: The prefix for an XML namespace. +_NamespacePrefix: TypeAlias = str + +#: The URL of an XML namespace +_NamespaceURL: TypeAlias = str + +#: A mapping of prefixes to namespace URLs. +_NamespaceMapping: TypeAlias = Dict[_NamespacePrefix, _NamespaceURL] + +#: A mapping of namespace URLs to prefixes +_InvertedNamespaceMapping: TypeAlias = Dict[_NamespaceURL, _NamespacePrefix] + +# Aliases for the attribute values associated with HTML/XML tags. +# + +#: The value associated with an HTML or XML attribute. This is the +#: relatively unprocessed value Beautiful Soup expects to come from a +#: `TreeBuilder`. +_RawAttributeValue: TypeAlias = str + +#: A dictionary of names to `_RawAttributeValue` objects. This is how +#: Beautiful Soup expects a `TreeBuilder` to represent a tag's +#: attribute values. +_RawAttributeValues: TypeAlias = ( + "Mapping[Union[str, NamespacedAttribute], _RawAttributeValue]" +) + +#: An attribute value in its final form, as stored in the +# `Tag` class, after it has been processed and (in some cases) +# split into a list of strings. +_AttributeValue: TypeAlias = Union[str, "AttributeValueList"] + +#: A dictionary of names to :py:data:`_AttributeValue` objects. This is what +#: a tag's attributes look like after processing. +_AttributeValues: TypeAlias = Dict[str, _AttributeValue] + +#: The methods that deal with turning :py:data:`_RawAttributeValue` into +#: :py:data:`_AttributeValue` may be called several times, even after the values +#: are already processed (e.g. when cloning a tag), so they need to +#: be able to acommodate both possibilities. +_RawOrProcessedAttributeValues: TypeAlias = Union[_RawAttributeValues, _AttributeValues] + +#: A number of tree manipulation methods can take either a `PageElement` or a +#: normal Python string (which will be converted to a `NavigableString`). +_InsertableElement: TypeAlias = Union["PageElement", str] + +# Aliases to represent the many possibilities for matching bits of a +# parse tree. +# +# This is very complicated because we're applying a formal type system +# to some very DWIM code. The types we end up with will be the types +# of the arguments to the SoupStrainer constructor and (more +# familiarly to Beautiful Soup users) the find* methods. + +#: A function that takes a PageElement and returns a yes-or-no answer. +_PageElementMatchFunction: TypeAlias = Callable[["PageElement"], bool] + +#: A function that takes the raw parsed ingredients of a markup tag +#: and returns a yes-or-no answer. +# Not necessary at the moment. +# _AllowTagCreationFunction:TypeAlias = Callable[[Optional[str], str, Optional[_RawAttributeValues]], bool] + +#: A function that takes the raw parsed ingredients of a markup string node +#: and returns a yes-or-no answer. +# Not necessary at the moment. +# _AllowStringCreationFunction:TypeAlias = Callable[[Optional[str]], bool] + +#: A function that takes a `Tag` and returns a yes-or-no answer. +#: A `TagNameMatchRule` expects this kind of function, if you're +#: going to pass it a function. +_TagMatchFunction: TypeAlias = Callable[["Tag"], bool] + +#: A function that takes a string (or None) and returns a yes-or-no +#: answer. An `AttributeValueMatchRule` expects this kind of function, if +#: you're going to pass it a function. +_NullableStringMatchFunction: TypeAlias = Callable[[Optional[str]], bool] + +#: A function that takes a string and returns a yes-or-no answer. A +# `StringMatchRule` expects this kind of function, if you're going to +# pass it a function. +_StringMatchFunction: TypeAlias = Callable[[str], bool] + +#: Either a tag name, an attribute value or a string can be matched +#: against a string, bytestring, regular expression, or a boolean. +_BaseStrainable: TypeAlias = Union[str, bytes, Pattern[str], bool] + +#: A tag can be matched either with the `_BaseStrainable` options, or +#: using a function that takes the `Tag` as its sole argument. +_BaseStrainableElement: TypeAlias = Union[_BaseStrainable, _TagMatchFunction] + +#: A tag's attribute value can be matched either with the +#: `_BaseStrainable` options, or using a function that takes that +#: value as its sole argument. +_BaseStrainableAttribute: TypeAlias = Union[_BaseStrainable, _NullableStringMatchFunction] + +#: A tag can be matched using either a single criterion or a list of +#: criteria. +_StrainableElement: TypeAlias = Union[ + _BaseStrainableElement, Iterable[_BaseStrainableElement] +] + +#: An attribute value can be matched using either a single criterion +#: or a list of criteria. +_StrainableAttribute: TypeAlias = Union[ + _BaseStrainableAttribute, Iterable[_BaseStrainableAttribute] +] + +#: An string can be matched using the same techniques as +#: an attribute value. +_StrainableString: TypeAlias = _StrainableAttribute + +#: A dictionary may be used to match against multiple attribute vlaues at once. +_StrainableAttributes: TypeAlias = Dict[str, _StrainableAttribute] + +#: Many Beautiful soup methods return a PageElement or an ResultSet of +#: PageElements. A PageElement is either a Tag or a NavigableString. +#: These convenience aliases make it easier for IDE users to see which methods +#: are available on the objects they're dealing with. +_OneElement: TypeAlias = Union["PageElement", "Tag", "NavigableString"] +_AtMostOneElement: TypeAlias = Optional[_OneElement] +_AtMostOneTag: TypeAlias = Optional["Tag"] +_AtMostOneNavigableString: TypeAlias = Optional["NavigableString"] +_QueryResults: TypeAlias = "ResultSet[_OneElement]" +_SomeTags: TypeAlias = "ResultSet[Tag]" +_SomeNavigableStrings: TypeAlias = "ResultSet[NavigableString]" diff --git a/venv/lib/python3.12/site-packages/bs4/_warnings.py b/venv/lib/python3.12/site-packages/bs4/_warnings.py new file mode 100644 index 00000000..43094730 --- /dev/null +++ b/venv/lib/python3.12/site-packages/bs4/_warnings.py @@ -0,0 +1,98 @@ +"""Define some custom warnings.""" + + +class GuessedAtParserWarning(UserWarning): + """The warning issued when BeautifulSoup has to guess what parser to + use -- probably because no parser was specified in the constructor. + """ + + MESSAGE: str = """No parser was explicitly specified, so I'm using the best available %(markup_type)s parser for this system ("%(parser)s"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently. + +The code that caused this warning is on line %(line_number)s of the file %(filename)s. To get rid of this warning, pass the additional argument 'features="%(parser)s"' to the BeautifulSoup constructor. +""" + + +class UnusualUsageWarning(UserWarning): + """A superclass for warnings issued when Beautiful Soup sees + something that is typically the result of a mistake in the calling + code, but might be intentional on the part of the user. If it is + in fact intentional, you can filter the individual warning class + to get rid of the warning. If you don't like Beautiful Soup + second-guessing what you are doing, you can filter the + UnusualUsageWarningclass itself and get rid of these entirely. + """ + + +class MarkupResemblesLocatorWarning(UnusualUsageWarning): + """The warning issued when BeautifulSoup is given 'markup' that + actually looks like a resource locator -- a URL or a path to a file + on disk. + """ + + #: :meta private: + GENERIC_MESSAGE: str = """ + +However, if you want to parse some data that happens to look like a %(what)s, then nothing has gone wrong: you are using Beautiful Soup correctly, and this warning is spurious and can be filtered. To make this warning go away, run this code before calling the BeautifulSoup constructor: + + from bs4 import MarkupResemblesLocatorWarning + import warnings + + warnings.filterwarnings("ignore", category=MarkupResemblesLocatorWarning) + """ + + URL_MESSAGE: str = ( + """The input passed in on this line looks more like a URL than HTML or XML. + +If you meant to use Beautiful Soup to parse the web page found at a certain URL, then something has gone wrong. You should use an Python package like 'requests' to fetch the content behind the URL. Once you have the content as a string, you can feed that string into Beautiful Soup.""" + + GENERIC_MESSAGE + ) + + FILENAME_MESSAGE: str = ( + """The input passed in on this line looks more like a filename than HTML or XML. + +If you meant to use Beautiful Soup to parse the contents of a file on disk, then something has gone wrong. You should open the file first, using code like this: + + filehandle = open(your filename) + +You can then feed the open filehandle into Beautiful Soup instead of using the filename.""" + + GENERIC_MESSAGE + ) + + +class AttributeResemblesVariableWarning(UnusualUsageWarning, SyntaxWarning): + """The warning issued when Beautiful Soup suspects a provided + attribute name may actually be the misspelled name of a Beautiful + Soup variable. Generally speaking, this is only used in cases like + "_class" where it's very unlikely the user would be referencing an + XML attribute with that name. + """ + + MESSAGE: str = """%(original)r is an unusual attribute name and is a common misspelling for %(autocorrect)r. + +If you meant %(autocorrect)r, change your code to use it, and this warning will go away. + +If you really did mean to check the %(original)r attribute, this warning is spurious and can be filtered. To make it go away, run this code before creating your BeautifulSoup object: + + from bs4 import AttributeResemblesVariableWarning + import warnings + + warnings.filterwarnings("ignore", category=AttributeResemblesVariableWarning) +""" + + +class XMLParsedAsHTMLWarning(UnusualUsageWarning): + """The warning issued when an HTML parser is used to parse + XML that is not (as far as we can tell) XHTML. + """ + + MESSAGE: str = """It looks like you're using an HTML parser to parse an XML document. + +Assuming this really is an XML document, what you're doing might work, but you should know that using an XML parser will be more reliable. To parse this document as XML, make sure you have the Python package 'lxml' installed, and pass the keyword argument `features="xml"` into the BeautifulSoup constructor. + +If you want or need to use an HTML parser on this document, you can make this warning go away by filtering it. To do that, run this code before calling the BeautifulSoup constructor: + + from bs4 import XMLParsedAsHTMLWarning + import warnings + + warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning) +""" diff --git a/venv/lib/python3.12/site-packages/bs4/builder/__init__.py b/venv/lib/python3.12/site-packages/bs4/builder/__init__.py new file mode 100644 index 00000000..4aae4d33 --- /dev/null +++ b/venv/lib/python3.12/site-packages/bs4/builder/__init__.py @@ -0,0 +1,848 @@ +from __future__ import annotations + +# Use of this source code is governed by the MIT license. +__license__ = "MIT" + +from collections import defaultdict +import re +from types import ModuleType +from typing import ( + Any, + cast, + Dict, + Iterable, + List, + Optional, + Pattern, + Set, + Tuple, + Type, + TYPE_CHECKING, +) +import warnings +import sys +from bs4.element import ( + AttributeDict, + AttributeValueList, + CharsetMetaAttributeValue, + ContentMetaAttributeValue, + RubyParenthesisString, + RubyTextString, + Stylesheet, + Script, + TemplateString, + nonwhitespace_re, +) + +# Exceptions were moved to their own module in 4.13. Import here for +# backwards compatibility. +from bs4.exceptions import ParserRejectedMarkup + +from bs4._typing import ( + _AttributeValues, + _RawAttributeValue, +) + +from bs4._warnings import XMLParsedAsHTMLWarning + +if TYPE_CHECKING: + from bs4 import BeautifulSoup + from bs4.element import ( + NavigableString, + Tag, + ) + from bs4._typing import ( + _AttributeValue, + _Encoding, + _Encodings, + _RawOrProcessedAttributeValues, + _RawMarkup, + ) + +__all__ = [ + "HTMLTreeBuilder", + "SAXTreeBuilder", + "TreeBuilder", + "TreeBuilderRegistry", +] + +# Some useful features for a TreeBuilder to have. +FAST = "fast" +PERMISSIVE = "permissive" +STRICT = "strict" +XML = "xml" +HTML = "html" +HTML_5 = "html5" + +__all__ = [ + "TreeBuilderRegistry", + "TreeBuilder", + "HTMLTreeBuilder", + "DetectsXMLParsedAsHTML", + + "ParserRejectedMarkup", # backwards compatibility only as of 4.13.0 +] + +class TreeBuilderRegistry(object): + """A way of looking up TreeBuilder subclasses by their name or by desired + features. + """ + + builders_for_feature: Dict[str, List[Type[TreeBuilder]]] + builders: List[Type[TreeBuilder]] + + def __init__(self) -> None: + self.builders_for_feature = defaultdict(list) + self.builders = [] + + def register(self, treebuilder_class: type[TreeBuilder]) -> None: + """Register a treebuilder based on its advertised features. + + :param treebuilder_class: A subclass of `TreeBuilder`. its + `TreeBuilder.features` attribute should list its features. + """ + for feature in treebuilder_class.features: + self.builders_for_feature[feature].insert(0, treebuilder_class) + self.builders.insert(0, treebuilder_class) + + def lookup(self, *features: str) -> Optional[Type[TreeBuilder]]: + """Look up a TreeBuilder subclass with the desired features. + + :param features: A list of features to look for. If none are + provided, the most recently registered TreeBuilder subclass + will be used. + :return: A TreeBuilder subclass, or None if there's no + registered subclass with all the requested features. + """ + if len(self.builders) == 0: + # There are no builders at all. + return None + + if len(features) == 0: + # They didn't ask for any features. Give them the most + # recently registered builder. + return self.builders[0] + + # Go down the list of features in order, and eliminate any builders + # that don't match every feature. + feature_list = list(features) + feature_list.reverse() + candidates = None + candidate_set = None + while len(feature_list) > 0: + feature = feature_list.pop() + we_have_the_feature = self.builders_for_feature.get(feature, []) + if len(we_have_the_feature) > 0: + if candidates is None: + candidates = we_have_the_feature + candidate_set = set(candidates) + elif candidate_set is not None: + # Eliminate any candidates that don't have this feature. + candidate_set = candidate_set.intersection(set(we_have_the_feature)) + + # The only valid candidates are the ones in candidate_set. + # Go through the original list of candidates and pick the first one + # that's in candidate_set. + if candidate_set is None or candidates is None: + return None + for candidate in candidates: + if candidate in candidate_set: + return candidate + return None + + +#: The `BeautifulSoup` constructor will take a list of features +#: and use it to look up `TreeBuilder` classes in this registry. +builder_registry: TreeBuilderRegistry = TreeBuilderRegistry() + + +class TreeBuilder(object): + """Turn a textual document into a Beautiful Soup object tree. + + This is an abstract superclass which smooths out the behavior of + different parser libraries into a single, unified interface. + + :param multi_valued_attributes: If this is set to None, the + TreeBuilder will not turn any values for attributes like + 'class' into lists. Setting this to a dictionary will + customize this behavior; look at :py:attr:`bs4.builder.HTMLTreeBuilder.DEFAULT_CDATA_LIST_ATTRIBUTES` + for an example. + + Internally, these are called "CDATA list attributes", but that + probably doesn't make sense to an end-user, so the argument name + is ``multi_valued_attributes``. + + :param preserve_whitespace_tags: A set of tags to treat + the way
     tags are treated in HTML. Tags in this set
    +     are immune from pretty-printing; their contents will always be
    +     output as-is.
    +
    +    :param string_containers: A dictionary mapping tag names to
    +     the classes that should be instantiated to contain the textual
    +     contents of those tags. The default is to use NavigableString
    +     for every tag, no matter what the name. You can override the
    +     default by changing :py:attr:`DEFAULT_STRING_CONTAINERS`.
    +
    +    :param store_line_numbers: If the parser keeps track of the line
    +     numbers and positions of the original markup, that information
    +     will, by default, be stored in each corresponding
    +     :py:class:`bs4.element.Tag` object. You can turn this off by
    +     passing store_line_numbers=False; then Tag.sourcepos and
    +     Tag.sourceline will always be None. If the parser you're using
    +     doesn't keep track of this information, then store_line_numbers
    +     is irrelevant.
    +
    +    :param attribute_dict_class: The value of a multi-valued attribute
    +      (such as HTML's 'class') willl be stored in an instance of this
    +      class.  The default is Beautiful Soup's built-in
    +      `AttributeValueList`, which is a normal Python list, and you
    +      will probably never need to change it.
    +    """
    +
    +    USE_DEFAULT: Any = object()  #: :meta private:
    +
    +    def __init__(
    +        self,
    +        multi_valued_attributes: Dict[str, Set[str]] = USE_DEFAULT,
    +        preserve_whitespace_tags: Set[str] = USE_DEFAULT,
    +        store_line_numbers: bool = USE_DEFAULT,
    +        string_containers: Dict[str, Type[NavigableString]] = USE_DEFAULT,
    +        empty_element_tags: Set[str] = USE_DEFAULT,
    +        attribute_dict_class: Type[AttributeDict] = AttributeDict,
    +        attribute_value_list_class: Type[AttributeValueList] = AttributeValueList,
    +    ):
    +        self.soup = None
    +        if multi_valued_attributes is self.USE_DEFAULT:
    +            multi_valued_attributes = self.DEFAULT_CDATA_LIST_ATTRIBUTES
    +        self.cdata_list_attributes = multi_valued_attributes
    +        if preserve_whitespace_tags is self.USE_DEFAULT:
    +            preserve_whitespace_tags = self.DEFAULT_PRESERVE_WHITESPACE_TAGS
    +        self.preserve_whitespace_tags = preserve_whitespace_tags
    +        if empty_element_tags is self.USE_DEFAULT:
    +            self.empty_element_tags = self.DEFAULT_EMPTY_ELEMENT_TAGS
    +        else:
    +            self.empty_element_tags = empty_element_tags
    +        # TODO: store_line_numbers is probably irrelevant now that
    +        # the behavior of sourceline and sourcepos has been made consistent
    +        # everywhere.
    +        if store_line_numbers == self.USE_DEFAULT:
    +            store_line_numbers = self.TRACKS_LINE_NUMBERS
    +        self.store_line_numbers = store_line_numbers
    +        if string_containers == self.USE_DEFAULT:
    +            string_containers = self.DEFAULT_STRING_CONTAINERS
    +        self.string_containers = string_containers
    +        self.attribute_dict_class = attribute_dict_class
    +        self.attribute_value_list_class = attribute_value_list_class
    +
    +    NAME: str = "[Unknown tree builder]"
    +    ALTERNATE_NAMES: Iterable[str] = []
    +    features: Iterable[str] = []
    +
    +    is_xml: bool = False
    +    picklable: bool = False
    +
    +    soup: Optional[BeautifulSoup]  #: :meta private:
    +
    +    #: A tag will be considered an empty-element
    +    #: tag when and only when it has no contents.
    +    empty_element_tags: Optional[Set[str]] = None  #: :meta private:
    +    cdata_list_attributes: Dict[str, Set[str]]  #: :meta private:
    +    preserve_whitespace_tags: Set[str]  #: :meta private:
    +    string_containers: Dict[str, Type[NavigableString]]  #: :meta private:
    +    tracks_line_numbers: bool  #: :meta private:
    +
    +    #: A value for these tag/attribute combinations is a space- or
    +    #: comma-separated list of CDATA, rather than a single CDATA.
    +    DEFAULT_CDATA_LIST_ATTRIBUTES: Dict[str, Set[str]] = defaultdict(set)
    +
    +    #: Whitespace should be preserved inside these tags.
    +    DEFAULT_PRESERVE_WHITESPACE_TAGS: Set[str] = set()
    +
    +    #: The textual contents of tags with these names should be
    +    #: instantiated with some class other than `bs4.element.NavigableString`.
    +    DEFAULT_STRING_CONTAINERS: Dict[str, Type[bs4.element.NavigableString]] = {} # type:ignore
    +
    +    #: By default, tags are treated as empty-element tags if they have
    +    #: no contents--that is, using XML rules. HTMLTreeBuilder
    +    #: defines a different set of DEFAULT_EMPTY_ELEMENT_TAGS based on the
    +    #: HTML 4 and HTML5 standards.
    +    DEFAULT_EMPTY_ELEMENT_TAGS: Optional[Set[str]] = None
    +
    +    #: Most parsers don't keep track of line numbers.
    +    TRACKS_LINE_NUMBERS: bool = False
    +
    +    def initialize_soup(self, soup: BeautifulSoup) -> None:
    +        """The BeautifulSoup object has been initialized and is now
    +        being associated with the TreeBuilder.
    +
    +        :param soup: A BeautifulSoup object.
    +        """
    +        self.soup = soup
    +
    +    def reset(self) -> None:
    +        """Do any work necessary to reset the underlying parser
    +        for a new document.
    +
    +        By default, this does nothing.
    +        """
    +        pass
    +
    +    def can_be_empty_element(self, tag_name: str) -> bool:
    +        """Might a tag with this name be an empty-element tag?
    +
    +        The final markup may or may not actually present this tag as
    +        self-closing.
    +
    +        For instance: an HTMLBuilder does not consider a 

    tag to be + an empty-element tag (it's not in + HTMLBuilder.empty_element_tags). This means an empty

    tag + will be presented as "

    ", not "

    " or "

    ". + + The default implementation has no opinion about which tags are + empty-element tags, so a tag will be presented as an + empty-element tag if and only if it has no children. + "" will become "", and "bar" will + be left alone. + + :param tag_name: The name of a markup tag. + """ + if self.empty_element_tags is None: + return True + return tag_name in self.empty_element_tags + + def feed(self, markup: _RawMarkup) -> None: + """Run incoming markup through some parsing process.""" + raise NotImplementedError() + + def prepare_markup( + self, + markup: _RawMarkup, + user_specified_encoding: Optional[_Encoding] = None, + document_declared_encoding: Optional[_Encoding] = None, + exclude_encodings: Optional[_Encodings] = None, + ) -> Iterable[Tuple[_RawMarkup, Optional[_Encoding], Optional[_Encoding], bool]]: + """Run any preliminary steps necessary to make incoming markup + acceptable to the parser. + + :param markup: The markup that's about to be parsed. + :param user_specified_encoding: The user asked to try this encoding + to convert the markup into a Unicode string. + :param document_declared_encoding: The markup itself claims to be + in this encoding. NOTE: This argument is not used by the + calling code and can probably be removed. + :param exclude_encodings: The user asked *not* to try any of + these encodings. + + :yield: A series of 4-tuples: (markup, encoding, declared encoding, + has undergone character replacement) + + Each 4-tuple represents a strategy that the parser can try + to convert the document to Unicode and parse it. Each + strategy will be tried in turn. + + By default, the only strategy is to parse the markup + as-is. See `LXMLTreeBuilderForXML` and + `HTMLParserTreeBuilder` for implementations that take into + account the quirks of particular parsers. + + :meta private: + + """ + yield markup, None, None, False + + def test_fragment_to_document(self, fragment: str) -> str: + """Wrap an HTML fragment to make it look like a document. + + Different parsers do this differently. For instance, lxml + introduces an empty tag, and html5lib + doesn't. Abstracting this away lets us write simple tests + which run HTML fragments through the parser and compare the + results against other HTML fragments. + + This method should not be used outside of unit tests. + + :param fragment: A fragment of HTML. + :return: A full HTML document. + :meta private: + """ + return fragment + + def set_up_substitutions(self, tag: Tag) -> bool: + """Set up any substitutions that will need to be performed on + a `Tag` when it's output as a string. + + By default, this does nothing. See `HTMLTreeBuilder` for a + case where this is used. + + :return: Whether or not a substitution was performed. + :meta private: + """ + return False + + def _replace_cdata_list_attribute_values( + self, tag_name: str, attrs: _RawOrProcessedAttributeValues + ) -> _AttributeValues: + """When an attribute value is associated with a tag that can + have multiple values for that attribute, convert the string + value to a list of strings. + + Basically, replaces class="foo bar" with class=["foo", "bar"] + + NOTE: This method modifies its input in place. + + :param tag_name: The name of a tag. + :param attrs: A dictionary containing the tag's attributes. + Any appropriate attribute values will be modified in place. + :return: The modified dictionary that was originally passed in. + """ + + # First, cast the attrs dict to _AttributeValues. This might + # not be accurate yet, but it will be by the time this method + # returns. + modified_attrs = cast(_AttributeValues, attrs) + if not modified_attrs or not self.cdata_list_attributes: + # Nothing to do. + return modified_attrs + + # There is at least a possibility that we need to modify one of + # the attribute values. + universal: Set[str] = self.cdata_list_attributes.get("*", set()) + tag_specific = self.cdata_list_attributes.get(tag_name.lower(), None) + for attr in list(modified_attrs.keys()): + modified_value: _AttributeValue + if attr in universal or (tag_specific and attr in tag_specific): + # We have a "class"-type attribute whose string + # value is a whitespace-separated list of + # values. Split it into a list. + original_value: _AttributeValue = modified_attrs[attr] + if isinstance(original_value, _RawAttributeValue): + # This is a _RawAttributeValue (a string) that + # needs to be split and converted to a + # AttributeValueList so it can be an + # _AttributeValue. + modified_value = self.attribute_value_list_class( + nonwhitespace_re.findall(original_value) + ) + else: + # html5lib calls setAttributes twice for the + # same tag when rearranging the parse tree. On + # the second call the attribute value here is + # already a list. This can also happen when a + # Tag object is cloned. If this happens, leave + # the value alone rather than trying to split + # it again. + modified_value = original_value + modified_attrs[attr] = modified_value + return modified_attrs + + +class SAXTreeBuilder(TreeBuilder): + """A Beautiful Soup treebuilder that listens for SAX events. + + This is not currently used for anything, and it will be removed + soon. It was a good idea, but it wasn't properly integrated into the + rest of Beautiful Soup, so there have been long stretches where it + hasn't worked properly. + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + warnings.warn( + "The SAXTreeBuilder class was deprecated in 4.13.0 and will be removed soon thereafter. It is completely untested and probably doesn't work; do not use it.", + DeprecationWarning, + stacklevel=2, + ) + super(SAXTreeBuilder, self).__init__(*args, **kwargs) + + def feed(self, markup: _RawMarkup) -> None: + raise NotImplementedError() + + def close(self) -> None: + pass + + def startElement(self, name: str, attrs: Dict[str, str]) -> None: + attrs = AttributeDict((key[1], value) for key, value in list(attrs.items())) + # print("Start %s, %r" % (name, attrs)) + assert self.soup is not None + self.soup.handle_starttag(name, None, None, attrs) + + def endElement(self, name: str) -> None: + # print("End %s" % name) + assert self.soup is not None + self.soup.handle_endtag(name) + + def startElementNS( + self, nsTuple: Tuple[str, str], nodeName: str, attrs: Dict[str, str] + ) -> None: + # Throw away (ns, nodeName) for now. + self.startElement(nodeName, attrs) + + def endElementNS(self, nsTuple: Tuple[str, str], nodeName: str) -> None: + # Throw away (ns, nodeName) for now. + self.endElement(nodeName) + # handler.endElementNS((ns, node.nodeName), node.nodeName) + + def startPrefixMapping(self, prefix: str, nodeValue: str) -> None: + # Ignore the prefix for now. + pass + + def endPrefixMapping(self, prefix: str) -> None: + # Ignore the prefix for now. + # handler.endPrefixMapping(prefix) + pass + + def characters(self, content: str) -> None: + assert self.soup is not None + self.soup.handle_data(content) + + def startDocument(self) -> None: + pass + + def endDocument(self) -> None: + pass + + +class HTMLTreeBuilder(TreeBuilder): + """This TreeBuilder knows facts about HTML, such as which tags are treated + specially by the HTML standard. + """ + + #: Some HTML tags are defined as having no contents. Beautiful Soup + #: treats these specially. + DEFAULT_EMPTY_ELEMENT_TAGS: Optional[Set[str]] = set( + [ + # These are from HTML5. + "area", + "base", + "br", + "col", + "embed", + "hr", + "img", + "input", + "keygen", + "link", + "menuitem", + "meta", + "param", + "source", + "track", + "wbr", + # These are from earlier versions of HTML and are removed in HTML5. + "basefont", + "bgsound", + "command", + "frame", + "image", + "isindex", + "nextid", + "spacer", + ] + ) + + #: The HTML standard defines these tags as block-level elements. Beautiful + #: Soup does not treat these elements differently from other elements, + #: but it may do so eventually, and this information is available if + #: you need to use it. + DEFAULT_BLOCK_ELEMENTS: Set[str] = set( + [ + "address", + "article", + "aside", + "blockquote", + "canvas", + "dd", + "div", + "dl", + "dt", + "fieldset", + "figcaption", + "figure", + "footer", + "form", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "header", + "hr", + "li", + "main", + "nav", + "noscript", + "ol", + "output", + "p", + "pre", + "section", + "table", + "tfoot", + "ul", + "video", + ] + ) + + #: These HTML tags need special treatment so they can be + #: represented by a string class other than `bs4.element.NavigableString`. + #: + #: For some of these tags, it's because the HTML standard defines + #: an unusual content model for them. I made this list by going + #: through the HTML spec + #: (https://html.spec.whatwg.org/#metadata-content) and looking for + #: "metadata content" elements that can contain strings. + #: + #: The Ruby tags ( and ) are here despite being normal + #: "phrasing content" tags, because the content they contain is + #: qualitatively different from other text in the document, and it + #: can be useful to be able to distinguish it. + #: + #: TODO: Arguably

    %(title)s

    - -''' - -DOC_HEADER_EXTERNALCSS = '''\ - - - - - %(title)s - - - - -

    %(title)s

    - -''' - -DOC_FOOTER = '''\ - - -''' - - -class HtmlFormatter(Formatter): - r""" - Format tokens as HTML 4 ```` tags. By default, the content is enclosed - in a ``
    `` tag, itself wrapped in a ``
    `` tag (but see the `nowrap` option). - The ``
    ``'s CSS class can be set by the `cssclass` option. - - If the `linenos` option is set to ``"table"``, the ``
    `` is
    -    additionally wrapped inside a ```` which has one row and two
    -    cells: one containing the line numbers and one containing the code.
    -    Example:
    -
    -    .. sourcecode:: html
    -
    -        
    -
    - - -
    -
    1
    -            2
    -
    -
    def foo(bar):
    -              pass
    -            
    -
    - - (whitespace added to improve clarity). - - A list of lines can be specified using the `hl_lines` option to make these - lines highlighted (as of Pygments 0.11). - - With the `full` option, a complete HTML 4 document is output, including - the style definitions inside a `` - {% else %} - {{ head | safe }} - {% endif %} -{% if not embed %} - - -{% endif %} -{{ body | safe }} -{% for diagram in diagrams %} -
    -

    {{ diagram.title }}

    -
    {{ diagram.text }}
    -
    - {{ diagram.svg }} -
    -
    -{% endfor %} -{% if not embed %} - - -{% endif %} -""" - -template = Template(jinja2_template_source) - -# Note: ideally this would be a dataclass, but we're supporting Python 3.5+ so we can't do this yet -NamedDiagram = NamedTuple( - "NamedDiagram", - [("name", str), ("diagram", typing.Optional[railroad.DiagramItem]), ("index", int)], -) -""" -A simple structure for associating a name with a railroad diagram -""" - -T = TypeVar("T") - - -class EachItem(railroad.Group): - """ - Custom railroad item to compose a: - - Group containing a - - OneOrMore containing a - - Choice of the elements in the Each - with the group label indicating that all must be matched - """ - - all_label = "[ALL]" - - def __init__(self, *items): - choice_item = railroad.Choice(len(items) - 1, *items) - one_or_more_item = railroad.OneOrMore(item=choice_item) - super().__init__(one_or_more_item, label=self.all_label) - - -class AnnotatedItem(railroad.Group): - """ - Simple subclass of Group that creates an annotation label - """ - - def __init__(self, label: str, item): - super().__init__(item=item, label="[{}]".format(label) if label else label) - - -class EditablePartial(Generic[T]): - """ - Acts like a functools.partial, but can be edited. In other words, it represents a type that hasn't yet been - constructed. - """ - - # We need this here because the railroad constructors actually transform the data, so can't be called until the - # entire tree is assembled - - def __init__(self, func: Callable[..., T], args: list, kwargs: dict): - self.func = func - self.args = args - self.kwargs = kwargs - - @classmethod - def from_call(cls, func: Callable[..., T], *args, **kwargs) -> "EditablePartial[T]": - """ - If you call this function in the same way that you would call the constructor, it will store the arguments - as you expect. For example EditablePartial.from_call(Fraction, 1, 3)() == Fraction(1, 3) - """ - return EditablePartial(func=func, args=list(args), kwargs=kwargs) - - @property - def name(self): - return self.kwargs["name"] - - def __call__(self) -> T: - """ - Evaluate the partial and return the result - """ - args = self.args.copy() - kwargs = self.kwargs.copy() - - # This is a helpful hack to allow you to specify varargs parameters (e.g. *args) as keyword args (e.g. - # args=['list', 'of', 'things']) - arg_spec = inspect.getfullargspec(self.func) - if arg_spec.varargs in self.kwargs: - args += kwargs.pop(arg_spec.varargs) - - return self.func(*args, **kwargs) - - -def railroad_to_html(diagrams: List[NamedDiagram], embed=False, **kwargs) -> str: - """ - Given a list of NamedDiagram, produce a single HTML string that visualises those diagrams - :params kwargs: kwargs to be passed in to the template - """ - data = [] - for diagram in diagrams: - if diagram.diagram is None: - continue - io = StringIO() - try: - css = kwargs.get('css') - diagram.diagram.writeStandalone(io.write, css=css) - except AttributeError: - diagram.diagram.writeSvg(io.write) - title = diagram.name - if diagram.index == 0: - title += " (root)" - data.append({"title": title, "text": "", "svg": io.getvalue()}) - - return template.render(diagrams=data, embed=embed, **kwargs) - - -def resolve_partial(partial: "EditablePartial[T]") -> T: - """ - Recursively resolves a collection of Partials into whatever type they are - """ - if isinstance(partial, EditablePartial): - partial.args = resolve_partial(partial.args) - partial.kwargs = resolve_partial(partial.kwargs) - return partial() - elif isinstance(partial, list): - return [resolve_partial(x) for x in partial] - elif isinstance(partial, dict): - return {key: resolve_partial(x) for key, x in partial.items()} - else: - return partial - - -def to_railroad( - element: pyparsing.ParserElement, - diagram_kwargs: typing.Optional[dict] = None, - vertical: int = 3, - show_results_names: bool = False, - show_groups: bool = False, -) -> List[NamedDiagram]: - """ - Convert a pyparsing element tree into a list of diagrams. This is the recommended entrypoint to diagram - creation if you want to access the Railroad tree before it is converted to HTML - :param element: base element of the parser being diagrammed - :param diagram_kwargs: kwargs to pass to the Diagram() constructor - :param vertical: (optional) - int - limit at which number of alternatives should be - shown vertically instead of horizontally - :param show_results_names - bool to indicate whether results name annotations should be - included in the diagram - :param show_groups - bool to indicate whether groups should be highlighted with an unlabeled - surrounding box - """ - # Convert the whole tree underneath the root - lookup = ConverterState(diagram_kwargs=diagram_kwargs or {}) - _to_diagram_element( - element, - lookup=lookup, - parent=None, - vertical=vertical, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - root_id = id(element) - # Convert the root if it hasn't been already - if root_id in lookup: - if not element.customName: - lookup[root_id].name = "" - lookup[root_id].mark_for_extraction(root_id, lookup, force=True) - - # Now that we're finished, we can convert from intermediate structures into Railroad elements - diags = list(lookup.diagrams.values()) - if len(diags) > 1: - # collapse out duplicate diags with the same name - seen = set() - deduped_diags = [] - for d in diags: - # don't extract SkipTo elements, they are uninformative as subdiagrams - if d.name == "...": - continue - if d.name is not None and d.name not in seen: - seen.add(d.name) - deduped_diags.append(d) - resolved = [resolve_partial(partial) for partial in deduped_diags] - else: - # special case - if just one diagram, always display it, even if - # it has no name - resolved = [resolve_partial(partial) for partial in diags] - return sorted(resolved, key=lambda diag: diag.index) - - -def _should_vertical( - specification: int, exprs: Iterable[pyparsing.ParserElement] -) -> bool: - """ - Returns true if we should return a vertical list of elements - """ - if specification is None: - return False - else: - return len(_visible_exprs(exprs)) >= specification - - -class ElementState: - """ - State recorded for an individual pyparsing Element - """ - - # Note: this should be a dataclass, but we have to support Python 3.5 - def __init__( - self, - element: pyparsing.ParserElement, - converted: EditablePartial, - parent: EditablePartial, - number: int, - name: str = None, - parent_index: typing.Optional[int] = None, - ): - #: The pyparsing element that this represents - self.element: pyparsing.ParserElement = element - #: The name of the element - self.name: typing.Optional[str] = name - #: The output Railroad element in an unconverted state - self.converted: EditablePartial = converted - #: The parent Railroad element, which we store so that we can extract this if it's duplicated - self.parent: EditablePartial = parent - #: The order in which we found this element, used for sorting diagrams if this is extracted into a diagram - self.number: int = number - #: The index of this inside its parent - self.parent_index: typing.Optional[int] = parent_index - #: If true, we should extract this out into a subdiagram - self.extract: bool = False - #: If true, all of this element's children have been filled out - self.complete: bool = False - - def mark_for_extraction( - self, el_id: int, state: "ConverterState", name: str = None, force: bool = False - ): - """ - Called when this instance has been seen twice, and thus should eventually be extracted into a sub-diagram - :param el_id: id of the element - :param state: element/diagram state tracker - :param name: name to use for this element's text - :param force: If true, force extraction now, regardless of the state of this. Only useful for extracting the - root element when we know we're finished - """ - self.extract = True - - # Set the name - if not self.name: - if name: - # Allow forcing a custom name - self.name = name - elif self.element.customName: - self.name = self.element.customName - else: - self.name = "" - - # Just because this is marked for extraction doesn't mean we can do it yet. We may have to wait for children - # to be added - # Also, if this is just a string literal etc, don't bother extracting it - if force or (self.complete and _worth_extracting(self.element)): - state.extract_into_diagram(el_id) - - -class ConverterState: - """ - Stores some state that persists between recursions into the element tree - """ - - def __init__(self, diagram_kwargs: typing.Optional[dict] = None): - #: A dictionary mapping ParserElements to state relating to them - self._element_diagram_states: Dict[int, ElementState] = {} - #: A dictionary mapping ParserElement IDs to subdiagrams generated from them - self.diagrams: Dict[int, EditablePartial[NamedDiagram]] = {} - #: The index of the next unnamed element - self.unnamed_index: int = 1 - #: The index of the next element. This is used for sorting - self.index: int = 0 - #: Shared kwargs that are used to customize the construction of diagrams - self.diagram_kwargs: dict = diagram_kwargs or {} - self.extracted_diagram_names: Set[str] = set() - - def __setitem__(self, key: int, value: ElementState): - self._element_diagram_states[key] = value - - def __getitem__(self, key: int) -> ElementState: - return self._element_diagram_states[key] - - def __delitem__(self, key: int): - del self._element_diagram_states[key] - - def __contains__(self, key: int): - return key in self._element_diagram_states - - def generate_unnamed(self) -> int: - """ - Generate a number used in the name of an otherwise unnamed diagram - """ - self.unnamed_index += 1 - return self.unnamed_index - - def generate_index(self) -> int: - """ - Generate a number used to index a diagram - """ - self.index += 1 - return self.index - - def extract_into_diagram(self, el_id: int): - """ - Used when we encounter the same token twice in the same tree. When this - happens, we replace all instances of that token with a terminal, and - create a new subdiagram for the token - """ - position = self[el_id] - - # Replace the original definition of this element with a regular block - if position.parent: - ret = EditablePartial.from_call(railroad.NonTerminal, text=position.name) - if "item" in position.parent.kwargs: - position.parent.kwargs["item"] = ret - elif "items" in position.parent.kwargs: - position.parent.kwargs["items"][position.parent_index] = ret - - # If the element we're extracting is a group, skip to its content but keep the title - if position.converted.func == railroad.Group: - content = position.converted.kwargs["item"] - else: - content = position.converted - - self.diagrams[el_id] = EditablePartial.from_call( - NamedDiagram, - name=position.name, - diagram=EditablePartial.from_call( - railroad.Diagram, content, **self.diagram_kwargs - ), - index=position.number, - ) - - del self[el_id] - - -def _worth_extracting(element: pyparsing.ParserElement) -> bool: - """ - Returns true if this element is worth having its own sub-diagram. Simply, if any of its children - themselves have children, then its complex enough to extract - """ - children = element.recurse() - return any(child.recurse() for child in children) - - -def _apply_diagram_item_enhancements(fn): - """ - decorator to ensure enhancements to a diagram item (such as results name annotations) - get applied on return from _to_diagram_element (we do this since there are several - returns in _to_diagram_element) - """ - - def _inner( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, - ) -> typing.Optional[EditablePartial]: - ret = fn( - element, - parent, - lookup, - vertical, - index, - name_hint, - show_results_names, - show_groups, - ) - - # apply annotation for results name, if present - if show_results_names and ret is not None: - element_results_name = element.resultsName - if element_results_name: - # add "*" to indicate if this is a "list all results" name - element_results_name += "" if element.modalResults else "*" - ret = EditablePartial.from_call( - railroad.Group, item=ret, label=element_results_name - ) - - return ret - - return _inner - - -def _visible_exprs(exprs: Iterable[pyparsing.ParserElement]): - non_diagramming_exprs = ( - pyparsing.ParseElementEnhance, - pyparsing.PositionToken, - pyparsing.And._ErrorStop, - ) - return [ - e - for e in exprs - if not (e.customName or e.resultsName or isinstance(e, non_diagramming_exprs)) - ] - - -@_apply_diagram_item_enhancements -def _to_diagram_element( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, -) -> typing.Optional[EditablePartial]: - """ - Recursively converts a PyParsing Element to a railroad Element - :param lookup: The shared converter state that keeps track of useful things - :param index: The index of this element within the parent - :param parent: The parent of this element in the output tree - :param vertical: Controls at what point we make a list of elements vertical. If this is an integer (the default), - it sets the threshold of the number of items before we go vertical. If True, always go vertical, if False, never - do so - :param name_hint: If provided, this will override the generated name - :param show_results_names: bool flag indicating whether to add annotations for results names - :returns: The converted version of the input element, but as a Partial that hasn't yet been constructed - :param show_groups: bool flag indicating whether to show groups using bounding box - """ - exprs = element.recurse() - name = name_hint or element.customName or element.__class__.__name__ - - # Python's id() is used to provide a unique identifier for elements - el_id = id(element) - - element_results_name = element.resultsName - - # Here we basically bypass processing certain wrapper elements if they contribute nothing to the diagram - if not element.customName: - if isinstance( - element, - ( - # pyparsing.TokenConverter, - # pyparsing.Forward, - pyparsing.Located, - ), - ): - # However, if this element has a useful custom name, and its child does not, we can pass it on to the child - if exprs: - if not exprs[0].customName: - propagated_name = name - else: - propagated_name = None - - return _to_diagram_element( - element.expr, - parent=parent, - lookup=lookup, - vertical=vertical, - index=index, - name_hint=propagated_name, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # If the element isn't worth extracting, we always treat it as the first time we say it - if _worth_extracting(element): - if el_id in lookup: - # If we've seen this element exactly once before, we are only just now finding out that it's a duplicate, - # so we have to extract it into a new diagram. - looked_up = lookup[el_id] - looked_up.mark_for_extraction(el_id, lookup, name=name_hint) - ret = EditablePartial.from_call(railroad.NonTerminal, text=looked_up.name) - return ret - - elif el_id in lookup.diagrams: - # If we have seen the element at least twice before, and have already extracted it into a subdiagram, we - # just put in a marker element that refers to the sub-diagram - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - return ret - - # Recursively convert child elements - # Here we find the most relevant Railroad element for matching pyparsing Element - # We use ``items=[]`` here to hold the place for where the child elements will go once created - if isinstance(element, pyparsing.And): - # detect And's created with ``expr*N`` notation - for these use a OneOrMore with a repeat - # (all will have the same name, and resultsName) - if not exprs: - return None - if len(set((e.name, e.resultsName) for e in exprs)) == 1: - ret = EditablePartial.from_call( - railroad.OneOrMore, item="", repeat=str(len(exprs)) - ) - elif _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Stack, items=[]) - else: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif isinstance(element, (pyparsing.Or, pyparsing.MatchFirst)): - if not exprs: - return None - if _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Choice, 0, items=[]) - else: - ret = EditablePartial.from_call(railroad.HorizontalChoice, items=[]) - elif isinstance(element, pyparsing.Each): - if not exprs: - return None - ret = EditablePartial.from_call(EachItem, items=[]) - elif isinstance(element, pyparsing.NotAny): - ret = EditablePartial.from_call(AnnotatedItem, label="NOT", item="") - elif isinstance(element, pyparsing.FollowedBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKAHEAD", item="") - elif isinstance(element, pyparsing.PrecededBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKBEHIND", item="") - elif isinstance(element, pyparsing.Group): - if show_groups: - ret = EditablePartial.from_call(AnnotatedItem, label="", item="") - else: - ret = EditablePartial.from_call(railroad.Group, label="", item="") - elif isinstance(element, pyparsing.TokenConverter): - label = type(element).__name__.lower() - if label == "tokenconverter": - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - else: - ret = EditablePartial.from_call(AnnotatedItem, label=label, item="") - elif isinstance(element, pyparsing.Opt): - ret = EditablePartial.from_call(railroad.Optional, item="") - elif isinstance(element, pyparsing.OneOrMore): - ret = EditablePartial.from_call(railroad.OneOrMore, item="") - elif isinstance(element, pyparsing.ZeroOrMore): - ret = EditablePartial.from_call(railroad.ZeroOrMore, item="") - elif isinstance(element, pyparsing.Group): - ret = EditablePartial.from_call( - railroad.Group, item=None, label=element_results_name - ) - elif isinstance(element, pyparsing.Empty) and not element.customName: - # Skip unnamed "Empty" elements - ret = None - elif isinstance(element, pyparsing.ParseElementEnhance): - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif len(exprs) > 0 and not element_results_name: - ret = EditablePartial.from_call(railroad.Group, item="", label=name) - elif len(exprs) > 0: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - else: - terminal = EditablePartial.from_call(railroad.Terminal, element.defaultName) - ret = terminal - - if ret is None: - return - - # Indicate this element's position in the tree so we can extract it if necessary - lookup[el_id] = ElementState( - element=element, - converted=ret, - parent=parent, - parent_index=index, - number=lookup.generate_index(), - ) - if element.customName: - lookup[el_id].mark_for_extraction(el_id, lookup, element.customName) - - i = 0 - for expr in exprs: - # Add a placeholder index in case we have to extract the child before we even add it to the parent - if "items" in ret.kwargs: - ret.kwargs["items"].insert(i, None) - - item = _to_diagram_element( - expr, - parent=ret, - lookup=lookup, - vertical=vertical, - index=i, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # Some elements don't need to be shown in the diagram - if item is not None: - if "item" in ret.kwargs: - ret.kwargs["item"] = item - elif "items" in ret.kwargs: - # If we've already extracted the child, don't touch this index, since it's occupied by a nonterminal - ret.kwargs["items"][i] = item - i += 1 - elif "items" in ret.kwargs: - # If we're supposed to skip this element, remove it from the parent - del ret.kwargs["items"][i] - - # If all this items children are none, skip this item - if ret and ( - ("items" in ret.kwargs and len(ret.kwargs["items"]) == 0) - or ("item" in ret.kwargs and ret.kwargs["item"] is None) - ): - ret = EditablePartial.from_call(railroad.Terminal, name) - - # Mark this element as "complete", ie it has all of its children - if el_id in lookup: - lookup[el_id].complete = True - - if el_id in lookup and lookup[el_id].extract and lookup[el_id].complete: - lookup.extract_into_diagram(el_id) - if ret is not None: - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - - return ret diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/exceptions.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/exceptions.py deleted file mode 100644 index 12219f12..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/exceptions.py +++ /dev/null @@ -1,299 +0,0 @@ -# exceptions.py - -import re -import sys -import typing - -from .util import ( - col, - line, - lineno, - _collapse_string_to_ranges, - replaced_by_pep8, -) -from .unicode import pyparsing_unicode as ppu - - -class ExceptionWordUnicode(ppu.Latin1, ppu.LatinA, ppu.LatinB, ppu.Greek, ppu.Cyrillic): - pass - - -_extract_alphanums = _collapse_string_to_ranges(ExceptionWordUnicode.alphanums) -_exception_word_extractor = re.compile("([" + _extract_alphanums + "]{1,16})|.") - - -class ParseBaseException(Exception): - """base exception class for all parsing runtime exceptions""" - - loc: int - msg: str - pstr: str - parser_element: typing.Any # "ParserElement" - args: typing.Tuple[str, int, typing.Optional[str]] - - __slots__ = ( - "loc", - "msg", - "pstr", - "parser_element", - "args", - ) - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, - pstr: str, - loc: int = 0, - msg: typing.Optional[str] = None, - elem=None, - ): - self.loc = loc - if msg is None: - self.msg = pstr - self.pstr = "" - else: - self.msg = msg - self.pstr = pstr - self.parser_element = elem - self.args = (pstr, loc, msg) - - @staticmethod - def explain_exception(exc, depth=16): - """ - Method to take an exception and translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - exc - exception raised during parsing (need not be a ParseException, in support - of Python exceptions that might be raised in a parse action) - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - """ - import inspect - from .core import ParserElement - - if depth is None: - depth = sys.getrecursionlimit() - ret = [] - if isinstance(exc, ParseBaseException): - ret.append(exc.line) - ret.append(" " * (exc.column - 1) + "^") - ret.append(f"{type(exc).__name__}: {exc}") - - if depth > 0: - callers = inspect.getinnerframes(exc.__traceback__, context=depth) - seen = set() - for i, ff in enumerate(callers[-depth:]): - frm = ff[0] - - f_self = frm.f_locals.get("self", None) - if isinstance(f_self, ParserElement): - if not frm.f_code.co_name.startswith( - ("parseImpl", "_parseNoCache") - ): - continue - if id(f_self) in seen: - continue - seen.add(id(f_self)) - - self_type = type(f_self) - ret.append( - f"{self_type.__module__}.{self_type.__name__} - {f_self}" - ) - - elif f_self is not None: - self_type = type(f_self) - ret.append(f"{self_type.__module__}.{self_type.__name__}") - - else: - code = frm.f_code - if code.co_name in ("wrapper", ""): - continue - - ret.append(code.co_name) - - depth -= 1 - if not depth: - break - - return "\n".join(ret) - - @classmethod - def _from_exception(cls, pe): - """ - internal factory method to simplify creating one type of ParseException - from another - avoids having __init__ signature conflicts among subclasses - """ - return cls(pe.pstr, pe.loc, pe.msg, pe.parser_element) - - @property - def line(self) -> str: - """ - Return the line of text where the exception occurred. - """ - return line(self.loc, self.pstr) - - @property - def lineno(self) -> int: - """ - Return the 1-based line number of text where the exception occurred. - """ - return lineno(self.loc, self.pstr) - - @property - def col(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - @property - def column(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - # pre-PEP8 compatibility - @property - def parserElement(self): - return self.parser_element - - @parserElement.setter - def parserElement(self, elem): - self.parser_element = elem - - def __str__(self) -> str: - if self.pstr: - if self.loc >= len(self.pstr): - foundstr = ", found end of text" - else: - # pull out next word at error location - found_match = _exception_word_extractor.match(self.pstr, self.loc) - if found_match is not None: - found = found_match.group(0) - else: - found = self.pstr[self.loc : self.loc + 1] - foundstr = (", found %r" % found).replace(r"\\", "\\") - else: - foundstr = "" - return f"{self.msg}{foundstr} (at char {self.loc}), (line:{self.lineno}, col:{self.column})" - - def __repr__(self): - return str(self) - - def mark_input_line( - self, marker_string: typing.Optional[str] = None, *, markerString: str = ">!<" - ) -> str: - """ - Extracts the exception line from the input string, and marks - the location of the exception with a special symbol. - """ - markerString = marker_string if marker_string is not None else markerString - line_str = self.line - line_column = self.column - 1 - if markerString: - line_str = "".join( - (line_str[:line_column], markerString, line_str[line_column:]) - ) - return line_str.strip() - - def explain(self, depth=16) -> str: - """ - Method to translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - - Example:: - - expr = pp.Word(pp.nums) * 3 - try: - expr.parse_string("123 456 A789") - except pp.ParseException as pe: - print(pe.explain(depth=0)) - - prints:: - - 123 456 A789 - ^ - ParseException: Expected W:(0-9), found 'A' (at char 8), (line:1, col:9) - - Note: the diagnostic output will include string representations of the expressions - that failed to parse. These representations will be more helpful if you use `set_name` to - give identifiable names to your expressions. Otherwise they will use the default string - forms, which may be cryptic to read. - - Note: pyparsing's default truncation of exception tracebacks may also truncate the - stack of expressions that are displayed in the ``explain`` output. To get the full listing - of parser expressions, you may have to set ``ParserElement.verbose_stacktrace = True`` - """ - return self.explain_exception(self, depth) - - # fmt: off - @replaced_by_pep8(mark_input_line) - def markInputline(self): ... - # fmt: on - - -class ParseException(ParseBaseException): - """ - Exception thrown when a parse expression doesn't match the input string - - Example:: - - try: - Word(nums).set_name("integer").parse_string("ABC") - except ParseException as pe: - print(pe) - print("column: {}".format(pe.column)) - - prints:: - - Expected integer (at char 0), (line:1, col:1) - column: 1 - - """ - - -class ParseFatalException(ParseBaseException): - """ - User-throwable exception thrown when inconsistent parse content - is found; stops all parsing immediately - """ - - -class ParseSyntaxException(ParseFatalException): - """ - Just like :class:`ParseFatalException`, but thrown internally - when an :class:`ErrorStop` ('-' operator) indicates - that parsing is to stop immediately because an unbacktrackable - syntax error has been found. - """ - - -class RecursiveGrammarException(Exception): - """ - Exception thrown by :class:`ParserElement.validate` if the - grammar could be left-recursive; parser may need to enable - left recursion using :class:`ParserElement.enable_left_recursion` - """ - - def __init__(self, parseElementList): - self.parseElementTrace = parseElementList - - def __str__(self) -> str: - return f"RecursiveGrammarException: {self.parseElementTrace}" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/helpers.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/helpers.py deleted file mode 100644 index 018f0d6a..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/helpers.py +++ /dev/null @@ -1,1100 +0,0 @@ -# helpers.py -import html.entities -import re -import sys -import typing - -from . import __diag__ -from .core import * -from .util import ( - _bslash, - _flatten, - _escape_regex_range_chars, - replaced_by_pep8, -) - - -# -# global helpers -# -def counted_array( - expr: ParserElement, - int_expr: typing.Optional[ParserElement] = None, - *, - intExpr: typing.Optional[ParserElement] = None, -) -> ParserElement: - """Helper to define a counted list of expressions. - - This helper defines a pattern of the form:: - - integer expr expr expr... - - where the leading integer tells how many expr expressions follow. - The matched tokens returns the array of expr tokens as a list - the - leading count token is suppressed. - - If ``int_expr`` is specified, it should be a pyparsing expression - that produces an integer value. - - Example:: - - counted_array(Word(alphas)).parse_string('2 ab cd ef') # -> ['ab', 'cd'] - - # in this parser, the leading integer value is given in binary, - # '10' indicating that 2 values are in the array - binary_constant = Word('01').set_parse_action(lambda t: int(t[0], 2)) - counted_array(Word(alphas), int_expr=binary_constant).parse_string('10 ab cd ef') # -> ['ab', 'cd'] - - # if other fields must be parsed after the count but before the - # list items, give the fields results names and they will - # be preserved in the returned ParseResults: - count_with_metadata = integer + Word(alphas)("type") - typed_array = counted_array(Word(alphanums), int_expr=count_with_metadata)("items") - result = typed_array.parse_string("3 bool True True False") - print(result.dump()) - - # prints - # ['True', 'True', 'False'] - # - items: ['True', 'True', 'False'] - # - type: 'bool' - """ - intExpr = intExpr or int_expr - array_expr = Forward() - - def count_field_parse_action(s, l, t): - nonlocal array_expr - n = t[0] - array_expr <<= (expr * n) if n else Empty() - # clear list contents, but keep any named results - del t[:] - - if intExpr is None: - intExpr = Word(nums).set_parse_action(lambda t: int(t[0])) - else: - intExpr = intExpr.copy() - intExpr.set_name("arrayLen") - intExpr.add_parse_action(count_field_parse_action, call_during_try=True) - return (intExpr + array_expr).set_name("(len) " + str(expr) + "...") - - -def match_previous_literal(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_literal(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches a previous literal, will also match the leading - ``"1:1"`` in ``"1:10"``. If this is not desired, use - :class:`match_previous_expr`. Do *not* use with packrat parsing - enabled. - """ - rep = Forward() - - def copy_token_to_repeater(s, l, t): - if t: - if len(t) == 1: - rep << t[0] - else: - # flatten t tokens - tflat = _flatten(t.as_list()) - rep << And(Literal(tt) for tt in tflat) - else: - rep << Empty() - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def match_previous_expr(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_expr(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches by expressions, will *not* match the leading ``"1:1"`` - in ``"1:10"``; the expressions are evaluated first, and then - compared, so ``"1"`` is compared with ``"10"``. Do *not* use - with packrat parsing enabled. - """ - rep = Forward() - e2 = expr.copy() - rep <<= e2 - - def copy_token_to_repeater(s, l, t): - matchTokens = _flatten(t.as_list()) - - def must_match_these_tokens(s, l, t): - theseTokens = _flatten(t.as_list()) - if theseTokens != matchTokens: - raise ParseException( - s, l, f"Expected {matchTokens}, found{theseTokens}" - ) - - rep.set_parse_action(must_match_these_tokens, callDuringTry=True) - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def one_of( - strs: Union[typing.Iterable[str], str], - caseless: bool = False, - use_regex: bool = True, - as_keyword: bool = False, - *, - useRegex: bool = True, - asKeyword: bool = False, -) -> ParserElement: - """Helper to quickly define a set of alternative :class:`Literal` s, - and makes sure to do longest-first testing when there is a conflict, - regardless of the input order, but returns - a :class:`MatchFirst` for best performance. - - Parameters: - - - ``strs`` - a string of space-delimited literals, or a collection of - string literals - - ``caseless`` - treat all literals as caseless - (default= ``False``) - - ``use_regex`` - as an optimization, will - generate a :class:`Regex` object; otherwise, will generate - a :class:`MatchFirst` object (if ``caseless=True`` or ``as_keyword=True``, or if - creating a :class:`Regex` raises an exception) - (default= ``True``) - - ``as_keyword`` - enforce :class:`Keyword`-style matching on the - generated expressions - (default= ``False``) - - ``asKeyword`` and ``useRegex`` are retained for pre-PEP8 compatibility, - but will be removed in a future release - - Example:: - - comp_oper = one_of("< = > <= >= !=") - var = Word(alphas) - number = Word(nums) - term = var | number - comparison_expr = term + comp_oper + term - print(comparison_expr.search_string("B = 12 AA=23 B<=AA AA>12")) - - prints:: - - [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] - """ - asKeyword = asKeyword or as_keyword - useRegex = useRegex and use_regex - - if ( - isinstance(caseless, str_type) - and __diag__.warn_on_multiple_string_args_to_oneof - ): - warnings.warn( - "More than one string argument passed to one_of, pass" - " choices as a list or space-delimited string", - stacklevel=2, - ) - - if caseless: - isequal = lambda a, b: a.upper() == b.upper() - masks = lambda a, b: b.upper().startswith(a.upper()) - parseElementClass = CaselessKeyword if asKeyword else CaselessLiteral - else: - isequal = lambda a, b: a == b - masks = lambda a, b: b.startswith(a) - parseElementClass = Keyword if asKeyword else Literal - - symbols: List[str] = [] - if isinstance(strs, str_type): - strs = typing.cast(str, strs) - symbols = strs.split() - elif isinstance(strs, Iterable): - symbols = list(strs) - else: - raise TypeError("Invalid argument to one_of, expected string or iterable") - if not symbols: - return NoMatch() - - # reorder given symbols to take care to avoid masking longer choices with shorter ones - # (but only if the given symbols are not just single characters) - if any(len(sym) > 1 for sym in symbols): - i = 0 - while i < len(symbols) - 1: - cur = symbols[i] - for j, other in enumerate(symbols[i + 1 :]): - if isequal(other, cur): - del symbols[i + j + 1] - break - elif masks(cur, other): - del symbols[i + j + 1] - symbols.insert(i, other) - break - else: - i += 1 - - if useRegex: - re_flags: int = re.IGNORECASE if caseless else 0 - - try: - if all(len(sym) == 1 for sym in symbols): - # symbols are just single characters, create range regex pattern - patt = f"[{''.join(_escape_regex_range_chars(sym) for sym in symbols)}]" - else: - patt = "|".join(re.escape(sym) for sym in symbols) - - # wrap with \b word break markers if defining as keywords - if asKeyword: - patt = rf"\b(?:{patt})\b" - - ret = Regex(patt, flags=re_flags).set_name(" | ".join(symbols)) - - if caseless: - # add parse action to return symbols as specified, not in random - # casing as found in input string - symbol_map = {sym.lower(): sym for sym in symbols} - ret.add_parse_action(lambda s, l, t: symbol_map[t[0].lower()]) - - return ret - - except re.error: - warnings.warn( - "Exception creating Regex for one_of, building MatchFirst", stacklevel=2 - ) - - # last resort, just use MatchFirst - return MatchFirst(parseElementClass(sym) for sym in symbols).set_name( - " | ".join(symbols) - ) - - -def dict_of(key: ParserElement, value: ParserElement) -> ParserElement: - """Helper to easily and clearly define a dictionary by specifying - the respective patterns for the key and value. Takes care of - defining the :class:`Dict`, :class:`ZeroOrMore`, and - :class:`Group` tokens in the proper order. The key pattern - can include delimiting markers or punctuation, as long as they are - suppressed, thereby leaving the significant key text. The value - pattern can include named results, so that the :class:`Dict` results - can include named token fields. - - Example:: - - text = "shape: SQUARE posn: upper left color: light blue texture: burlap" - attr_expr = (label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - print(attr_expr[1, ...].parse_string(text).dump()) - - attr_label = label - attr_value = Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join) - - # similar to Dict, but simpler call format - result = dict_of(attr_label, attr_value).parse_string(text) - print(result.dump()) - print(result['shape']) - print(result.shape) # object attribute access works too - print(result.as_dict()) - - prints:: - - [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - - color: 'light blue' - - posn: 'upper left' - - shape: 'SQUARE' - - texture: 'burlap' - SQUARE - SQUARE - {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} - """ - return Dict(OneOrMore(Group(key + value))) - - -def original_text_for( - expr: ParserElement, as_string: bool = True, *, asString: bool = True -) -> ParserElement: - """Helper to return the original, untokenized text for a given - expression. Useful to restore the parsed fields of an HTML start - tag into the raw tag text itself, or to revert separate tokens with - intervening whitespace back to the original matching input text. By - default, returns a string containing the original parsed text. - - If the optional ``as_string`` argument is passed as - ``False``, then the return value is - a :class:`ParseResults` containing any results names that - were originally matched, and a single token containing the original - matched text from the input string. So if the expression passed to - :class:`original_text_for` contains expressions with defined - results names, you must set ``as_string`` to ``False`` if you - want to preserve those results name values. - - The ``asString`` pre-PEP8 argument is retained for compatibility, - but will be removed in a future release. - - Example:: - - src = "this is test bold text normal text " - for tag in ("b", "i"): - opener, closer = make_html_tags(tag) - patt = original_text_for(opener + ... + closer) - print(patt.search_string(src)[0]) - - prints:: - - [' bold text '] - ['text'] - """ - asString = asString and as_string - - locMarker = Empty().set_parse_action(lambda s, loc, t: loc) - endlocMarker = locMarker.copy() - endlocMarker.callPreparse = False - matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") - if asString: - extractText = lambda s, l, t: s[t._original_start : t._original_end] - else: - - def extractText(s, l, t): - t[:] = [s[t.pop("_original_start") : t.pop("_original_end")]] - - matchExpr.set_parse_action(extractText) - matchExpr.ignoreExprs = expr.ignoreExprs - matchExpr.suppress_warning(Diagnostics.warn_ungrouped_named_tokens_in_collection) - return matchExpr - - -def ungroup(expr: ParserElement) -> ParserElement: - """Helper to undo pyparsing's default grouping of And expressions, - even if all but one are non-empty. - """ - return TokenConverter(expr).add_parse_action(lambda t: t[0]) - - -def locatedExpr(expr: ParserElement) -> ParserElement: - """ - (DEPRECATED - future code should use the :class:`Located` class) - Helper to decorate a returned token with its starting and ending - locations in the input string. - - This helper adds the following results names: - - - ``locn_start`` - location where matched expression begins - - ``locn_end`` - location where matched expression ends - - ``value`` - the actual parsed results - - Be careful if the input text contains ```` characters, you - may want to call :class:`ParserElement.parse_with_tabs` - - Example:: - - wd = Word(alphas) - for match in locatedExpr(wd).search_string("ljsdf123lksdjjf123lkkjj1222"): - print(match) - - prints:: - - [[0, 'ljsdf', 5]] - [[8, 'lksdjjf', 15]] - [[18, 'lkkjj', 23]] - """ - locator = Empty().set_parse_action(lambda ss, ll, tt: ll) - return Group( - locator("locn_start") - + expr("value") - + locator.copy().leaveWhitespace()("locn_end") - ) - - -def nested_expr( - opener: Union[str, ParserElement] = "(", - closer: Union[str, ParserElement] = ")", - content: typing.Optional[ParserElement] = None, - ignore_expr: ParserElement = quoted_string(), - *, - ignoreExpr: ParserElement = quoted_string(), -) -> ParserElement: - """Helper method for defining nested lists enclosed in opening and - closing delimiters (``"("`` and ``")"`` are the default). - - Parameters: - - - ``opener`` - opening character for a nested list - (default= ``"("``); can also be a pyparsing expression - - ``closer`` - closing character for a nested list - (default= ``")"``); can also be a pyparsing expression - - ``content`` - expression for items within the nested lists - (default= ``None``) - - ``ignore_expr`` - expression for ignoring opening and closing delimiters - (default= :class:`quoted_string`) - - ``ignoreExpr`` - this pre-PEP8 argument is retained for compatibility - but will be removed in a future release - - If an expression is not provided for the content argument, the - nested expression will capture all whitespace-delimited content - between delimiters as a list of separate values. - - Use the ``ignore_expr`` argument to define expressions that may - contain opening or closing characters that should not be treated as - opening or closing characters for nesting, such as quoted_string or - a comment expression. Specify multiple expressions using an - :class:`Or` or :class:`MatchFirst`. The default is - :class:`quoted_string`, but if no expressions are to be ignored, then - pass ``None`` for this argument. - - Example:: - - data_type = one_of("void int short long char float double") - decl_data_type = Combine(data_type + Opt(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR, RPAR = map(Suppress, "()") - - code_body = nested_expr('{', '}', ignore_expr=(quoted_string | c_style_comment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Opt(DelimitedList(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(c_style_comment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.search_string(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - - prints:: - - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if ignoreExpr != ignore_expr: - ignoreExpr = ignore_expr if ignoreExpr == quoted_string() else ignoreExpr - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener, str_type) and isinstance(closer, str_type): - opener = typing.cast(str, opener) - closer = typing.cast(str, closer) - if len(opener) == 1 and len(closer) == 1: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS, - exact=1, - ) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = empty.copy() + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS - ).set_parse_action(lambda t: t[0].strip()) - else: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = Combine( - OneOrMore( - ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - raise ValueError( - "opening and closing arguments must be strings if no content expression is given" - ) - ret = Forward() - if ignoreExpr is not None: - ret <<= Group( - Suppress(opener) + ZeroOrMore(ignoreExpr | ret | content) + Suppress(closer) - ) - else: - ret <<= Group(Suppress(opener) + ZeroOrMore(ret | content) + Suppress(closer)) - ret.set_name("nested %s%s expression" % (opener, closer)) - return ret - - -def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")): - """Internal helper to construct opening and closing tag expressions, given a tag name""" - if isinstance(tagStr, str_type): - resname = tagStr - tagStr = Keyword(tagStr, caseless=not xml) - else: - resname = tagStr.name - - tagAttrName = Word(alphas, alphanums + "_-:") - if xml: - tagAttrValue = dbl_quoted_string.copy().set_parse_action(remove_quotes) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict(ZeroOrMore(Group(tagAttrName + Suppress("=") + tagAttrValue))) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - else: - tagAttrValue = quoted_string.copy().set_parse_action(remove_quotes) | Word( - printables, exclude_chars=">" - ) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict( - ZeroOrMore( - Group( - tagAttrName.set_parse_action(lambda t: t[0].lower()) - + Opt(Suppress("=") + tagAttrValue) - ) - ) - ) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - closeTag = Combine(Literal("", adjacent=False) - - openTag.set_name("<%s>" % resname) - # add start results name in parse action now that ungrouped names are not reported at two levels - openTag.add_parse_action( - lambda t: t.__setitem__( - "start" + "".join(resname.replace(":", " ").title().split()), t.copy() - ) - ) - closeTag = closeTag( - "end" + "".join(resname.replace(":", " ").title().split()) - ).set_name("" % resname) - openTag.tag = resname - closeTag.tag = resname - openTag.tag_body = SkipTo(closeTag()) - return openTag, closeTag - - -def make_html_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for HTML, - given a tag name. Matches tags in either upper or lower case, - attributes with namespaces and with quoted or unquoted values. - - Example:: - - text = 'More info at the pyparsing wiki page' - # make_html_tags returns pyparsing expressions for the opening and - # closing tags as a 2-tuple - a, a_end = make_html_tags("A") - link_expr = a + SkipTo(a_end)("link_text") + a_end - - for link in link_expr.search_string(text): - # attributes in the tag (like "href" shown here) are - # also accessible as named results - print(link.link_text, '->', link.href) - - prints:: - - pyparsing -> https://github.com/pyparsing/pyparsing/wiki - """ - return _makeTags(tag_str, False) - - -def make_xml_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for XML, - given a tag name. Matches tags only in the given upper/lower case. - - Example: similar to :class:`make_html_tags` - """ - return _makeTags(tag_str, True) - - -any_open_tag: ParserElement -any_close_tag: ParserElement -any_open_tag, any_close_tag = make_html_tags( - Word(alphas, alphanums + "_:").set_name("any tag") -) - -_htmlEntityMap = {k.rstrip(";"): v for k, v in html.entities.html5.items()} -common_html_entity = Regex("&(?P" + "|".join(_htmlEntityMap) + ");").set_name( - "common HTML entity" -) - - -def replace_html_entity(s, l, t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - - -class OpAssoc(Enum): - """Enumeration of operator associativity - - used in constructing InfixNotationOperatorSpec for :class:`infix_notation`""" - - LEFT = 1 - RIGHT = 2 - - -InfixNotationOperatorArgType = Union[ - ParserElement, str, Tuple[Union[ParserElement, str], Union[ParserElement, str]] -] -InfixNotationOperatorSpec = Union[ - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - typing.Optional[ParseAction], - ], - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - ], -] - - -def infix_notation( - base_expr: ParserElement, - op_list: List[InfixNotationOperatorSpec], - lpar: Union[str, ParserElement] = Suppress("("), - rpar: Union[str, ParserElement] = Suppress(")"), -) -> ParserElement: - """Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary - or binary, left- or right-associative. Parse actions can also be - attached to operator expressions. The generated parser will also - recognize the use of parentheses to override operator precedences - (see example below). - - Note: if you define a deep operator list, you may see performance - issues when using infix_notation. See - :class:`ParserElement.enable_packrat` for a mechanism to potentially - improve your parser performance. - - Parameters: - - - ``base_expr`` - expression representing the most basic operand to - be used in the expression - - ``op_list`` - list of tuples, one for each operator precedence level - in the expression grammar; each tuple is of the form ``(op_expr, - num_operands, right_left_assoc, (optional)parse_action)``, where: - - - ``op_expr`` is the pyparsing expression for the operator; may also - be a string, which will be converted to a Literal; if ``num_operands`` - is 3, ``op_expr`` is a tuple of two expressions, for the two - operators separating the 3 terms - - ``num_operands`` is the number of terms for this operator (must be 1, - 2, or 3) - - ``right_left_assoc`` is the indicator whether the operator is right - or left associative, using the pyparsing-defined constants - ``OpAssoc.RIGHT`` and ``OpAssoc.LEFT``. - - ``parse_action`` is the parse action to be associated with - expressions matching this operator expression (the parse action - tuple member may be omitted); if the parse action is passed - a tuple or list of functions, this is equivalent to calling - ``set_parse_action(*fn)`` - (:class:`ParserElement.set_parse_action`) - - ``lpar`` - expression for matching left-parentheses; if passed as a - str, then will be parsed as ``Suppress(lpar)``. If lpar is passed as - an expression (such as ``Literal('(')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress('(')``) - - ``rpar`` - expression for matching right-parentheses; if passed as a - str, then will be parsed as ``Suppress(rpar)``. If rpar is passed as - an expression (such as ``Literal(')')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress(')')``) - - Example:: - - # simple example of four-function arithmetic with ints and - # variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infix_notation(integer | varname, - [ - ('-', 1, OpAssoc.RIGHT), - (one_of('* /'), 2, OpAssoc.LEFT), - (one_of('+ -'), 2, OpAssoc.LEFT), - ]) - - arith_expr.run_tests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', full_dump=False) - - prints:: - - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - (5+x)*y - [[[5, '+', 'x'], '*', 'y']] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - - # captive version of FollowedBy that does not do parse actions or capture results names - class _FB(FollowedBy): - def parseImpl(self, instring, loc, doActions=True): - self.expr.try_parse(instring, loc) - return loc, [] - - _FB.__name__ = "FollowedBy>" - - ret = Forward() - if isinstance(lpar, str): - lpar = Suppress(lpar) - if isinstance(rpar, str): - rpar = Suppress(rpar) - - # if lpar and rpar are not suppressed, wrap in group - if not (isinstance(rpar, Suppress) and isinstance(rpar, Suppress)): - lastExpr = base_expr | Group(lpar + ret + rpar) - else: - lastExpr = base_expr | (lpar + ret + rpar) - - arity: int - rightLeftAssoc: opAssoc - pa: typing.Optional[ParseAction] - opExpr1: ParserElement - opExpr2: ParserElement - for i, operDef in enumerate(op_list): - opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4] # type: ignore[assignment] - if isinstance(opExpr, str_type): - opExpr = ParserElement._literalStringClass(opExpr) - opExpr = typing.cast(ParserElement, opExpr) - if arity == 3: - if not isinstance(opExpr, (tuple, list)) or len(opExpr) != 2: - raise ValueError( - "if numterms=3, opExpr must be a tuple or list of two expressions" - ) - opExpr1, opExpr2 = opExpr - term_name = f"{opExpr1}{opExpr2} term" - else: - term_name = f"{opExpr} term" - - if not 1 <= arity <= 3: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - - if rightLeftAssoc not in (OpAssoc.LEFT, OpAssoc.RIGHT): - raise ValueError("operator must indicate right or left associativity") - - thisExpr: ParserElement = Forward().set_name(term_name) - thisExpr = typing.cast(Forward, thisExpr) - if rightLeftAssoc is OpAssoc.LEFT: - if arity == 1: - matchExpr = _FB(lastExpr + opExpr) + Group(lastExpr + opExpr[1, ...]) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + lastExpr) + Group( - lastExpr + (opExpr + lastExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + lastExpr) + Group(lastExpr[2, ...]) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr - ) + Group(lastExpr + OneOrMore(opExpr1 + lastExpr + opExpr2 + lastExpr)) - elif rightLeftAssoc is OpAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Opt): - opExpr = Opt(opExpr) - matchExpr = _FB(opExpr.expr + thisExpr) + Group(opExpr + thisExpr) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + thisExpr) + Group( - lastExpr + (opExpr + thisExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + thisExpr) + Group( - lastExpr + thisExpr[1, ...] - ) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr - ) + Group(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.set_parse_action(*pa) - else: - matchExpr.set_parse_action(pa) - thisExpr <<= (matchExpr | lastExpr).setName(term_name) - lastExpr = thisExpr - ret <<= lastExpr - return ret - - -def indentedBlock(blockStatementExpr, indentStack, indent=True, backup_stacks=[]): - """ - (DEPRECATED - use :class:`IndentedBlock` class instead) - Helper method for defining space-delimited indentation blocks, - such as those used to define block statements in Python source code. - - Parameters: - - - ``blockStatementExpr`` - expression defining syntax of statement that - is repeated within the indented block - - ``indentStack`` - list created by caller to manage indentation stack - (multiple ``statementWithIndentedBlock`` expressions within a single - grammar should share a common ``indentStack``) - - ``indent`` - boolean indicating whether block must be indented beyond - the current level; set to ``False`` for block of left-most statements - (default= ``True``) - - A valid block must contain at least one ``blockStatement``. - - (Note that indentedBlock uses internal parse actions which make it - incompatible with packrat parsing.) - - Example:: - - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group("(" + Opt(delimitedList(identifier)) + ")") + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group(funcDecl + func_body) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Opt(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << (funcDef | assignment | identifier) - - module_body = stmt[1, ...] - - parseTree = module_body.parseString(data) - parseTree.pprint() - - prints:: - - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - backup_stacks.append(indentStack[:]) - - def reset_stack(): - indentStack[:] = backup_stacks[-1] - - def checkPeerIndent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseException(s, l, "illegal nesting") - raise ParseException(s, l, "not a peer entry") - - def checkSubIndent(s, l, t): - curCol = col(l, s) - if curCol > indentStack[-1]: - indentStack.append(curCol) - else: - raise ParseException(s, l, "not a subentry") - - def checkUnindent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if not (indentStack and curCol in indentStack): - raise ParseException(s, l, "not an unindent") - if curCol < indentStack[-1]: - indentStack.pop() - - NL = OneOrMore(LineEnd().set_whitespace_chars("\t ").suppress()) - INDENT = (Empty() + Empty().set_parse_action(checkSubIndent)).set_name("INDENT") - PEER = Empty().set_parse_action(checkPeerIndent).set_name("") - UNDENT = Empty().set_parse_action(checkUnindent).set_name("UNINDENT") - if indent: - smExpr = Group( - Opt(NL) - + INDENT - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + UNDENT - ) - else: - smExpr = Group( - Opt(NL) - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + Opt(UNDENT) - ) - - # add a parse action to remove backup_stack from list of backups - smExpr.add_parse_action( - lambda: backup_stacks.pop(-1) and None if backup_stacks else None - ) - smExpr.set_fail_action(lambda a, b, c, d: reset_stack()) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.set_name("indented block") - - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -c_style_comment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/").set_name( - "C style comment" -) -"Comment of the form ``/* ... */``" - -html_comment = Regex(r"").set_name("HTML comment") -"Comment of the form ````" - -rest_of_line = Regex(r".*").leave_whitespace().set_name("rest of line") -dbl_slash_comment = Regex(r"//(?:\\\n|[^\n])*").set_name("// comment") -"Comment of the form ``// ... (to end of line)``" - -cpp_style_comment = Combine( - Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/" | dbl_slash_comment -).set_name("C++ style comment") -"Comment of either form :class:`c_style_comment` or :class:`dbl_slash_comment`" - -java_style_comment = cpp_style_comment -"Same as :class:`cpp_style_comment`" - -python_style_comment = Regex(r"#.*").set_name("Python style comment") -"Comment of the form ``# ... (to end of line)``" - - -# build list of built-in expressions, for future reference if a global default value -# gets updated -_builtin_exprs: List[ParserElement] = [ - v for v in vars().values() if isinstance(v, ParserElement) -] - - -# compatibility function, superseded by DelimitedList class -def delimited_list( - expr: Union[str, ParserElement], - delim: Union[str, ParserElement] = ",", - combine: bool = False, - min: typing.Optional[int] = None, - max: typing.Optional[int] = None, - *, - allow_trailing_delim: bool = False, -) -> ParserElement: - """(DEPRECATED - use :class:`DelimitedList` class)""" - return DelimitedList( - expr, delim, combine, min, max, allow_trailing_delim=allow_trailing_delim - ) - - -# pre-PEP8 compatible names -# fmt: off -opAssoc = OpAssoc -anyOpenTag = any_open_tag -anyCloseTag = any_close_tag -commonHTMLEntity = common_html_entity -cStyleComment = c_style_comment -htmlComment = html_comment -restOfLine = rest_of_line -dblSlashComment = dbl_slash_comment -cppStyleComment = cpp_style_comment -javaStyleComment = java_style_comment -pythonStyleComment = python_style_comment - -@replaced_by_pep8(DelimitedList) -def delimitedList(): ... - -@replaced_by_pep8(DelimitedList) -def delimited_list(): ... - -@replaced_by_pep8(counted_array) -def countedArray(): ... - -@replaced_by_pep8(match_previous_literal) -def matchPreviousLiteral(): ... - -@replaced_by_pep8(match_previous_expr) -def matchPreviousExpr(): ... - -@replaced_by_pep8(one_of) -def oneOf(): ... - -@replaced_by_pep8(dict_of) -def dictOf(): ... - -@replaced_by_pep8(original_text_for) -def originalTextFor(): ... - -@replaced_by_pep8(nested_expr) -def nestedExpr(): ... - -@replaced_by_pep8(make_html_tags) -def makeHTMLTags(): ... - -@replaced_by_pep8(make_xml_tags) -def makeXMLTags(): ... - -@replaced_by_pep8(replace_html_entity) -def replaceHTMLEntity(): ... - -@replaced_by_pep8(infix_notation) -def infixNotation(): ... -# fmt: on diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/results.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/results.py deleted file mode 100644 index 03130497..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/results.py +++ /dev/null @@ -1,796 +0,0 @@ -# results.py -from collections.abc import ( - MutableMapping, - Mapping, - MutableSequence, - Iterator, - Sequence, - Container, -) -import pprint -from typing import Tuple, Any, Dict, Set, List - -str_type: Tuple[type, ...] = (str, bytes) -_generator_type = type((_ for _ in ())) - - -class _ParseResultsWithOffset: - tup: Tuple["ParseResults", int] - __slots__ = ["tup"] - - def __init__(self, p1: "ParseResults", p2: int): - self.tup: Tuple[ParseResults, int] = (p1, p2) - - def __getitem__(self, i): - return self.tup[i] - - def __getstate__(self): - return self.tup - - def __setstate__(self, *args): - self.tup = args[0] - - -class ParseResults: - """Structured parse results, to provide multiple means of access to - the parsed data: - - - as a list (``len(results)``) - - by list index (``results[0], results[1]``, etc.) - - by attribute (``results.`` - see :class:`ParserElement.set_results_name`) - - Example:: - - integer = Word(nums) - date_str = (integer.set_results_name("year") + '/' - + integer.set_results_name("month") + '/' - + integer.set_results_name("day")) - # equivalent form: - # date_str = (integer("year") + '/' - # + integer("month") + '/' - # + integer("day")) - - # parse_string returns a ParseResults object - result = date_str.parse_string("1999/12/31") - - def test(s, fn=repr): - print(f"{s} -> {fn(eval(s))}") - test("list(result)") - test("result[0]") - test("result['month']") - test("result.day") - test("'month' in result") - test("'minutes' in result") - test("result.dump()", str) - - prints:: - - list(result) -> ['1999', '/', '12', '/', '31'] - result[0] -> '1999' - result['month'] -> '12' - result.day -> '31' - 'month' in result -> True - 'minutes' in result -> False - result.dump() -> ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - - _null_values: Tuple[Any, ...] = (None, [], ()) - - _name: str - _parent: "ParseResults" - _all_names: Set[str] - _modal: bool - _toklist: List[Any] - _tokdict: Dict[str, Any] - - __slots__ = ( - "_name", - "_parent", - "_all_names", - "_modal", - "_toklist", - "_tokdict", - ) - - class List(list): - """ - Simple wrapper class to distinguish parsed list results that should be preserved - as actual Python lists, instead of being converted to :class:`ParseResults`:: - - LBRACK, RBRACK = map(pp.Suppress, "[]") - element = pp.Forward() - item = ppc.integer - element_list = LBRACK + pp.DelimitedList(element) + RBRACK - - # add parse actions to convert from ParseResults to actual Python collection types - def as_python_list(t): - return pp.ParseResults.List(t.as_list()) - element_list.add_parse_action(as_python_list) - - element <<= item | element_list - - element.run_tests(''' - 100 - [2,3,4] - [[2, 1],3,4] - [(2, 1),3,4] - (2,3,4) - ''', post_parse=lambda s, r: (r[0], type(r[0]))) - - prints:: - - 100 - (100, ) - - [2,3,4] - ([2, 3, 4], ) - - [[2, 1],3,4] - ([[2, 1], 3, 4], ) - - (Used internally by :class:`Group` when `aslist=True`.) - """ - - def __new__(cls, contained=None): - if contained is None: - contained = [] - - if not isinstance(contained, list): - raise TypeError( - f"{cls.__name__} may only be constructed with a list, not {type(contained).__name__}" - ) - - return list.__new__(cls) - - def __new__(cls, toklist=None, name=None, **kwargs): - if isinstance(toklist, ParseResults): - return toklist - self = object.__new__(cls) - self._name = None - self._parent = None - self._all_names = set() - - if toklist is None: - self._toklist = [] - elif isinstance(toklist, (list, _generator_type)): - self._toklist = ( - [toklist[:]] - if isinstance(toklist, ParseResults.List) - else list(toklist) - ) - else: - self._toklist = [toklist] - self._tokdict = dict() - return self - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance - ): - self._tokdict: Dict[str, _ParseResultsWithOffset] - self._modal = modal - if name is not None and name != "": - if isinstance(name, int): - name = str(name) - if not modal: - self._all_names = {name} - self._name = name - if toklist not in self._null_values: - if isinstance(toklist, (str_type, type)): - toklist = [toklist] - if asList: - if isinstance(toklist, ParseResults): - self[name] = _ParseResultsWithOffset( - ParseResults(toklist._toklist), 0 - ) - else: - self[name] = _ParseResultsWithOffset( - ParseResults(toklist[0]), 0 - ) - self[name]._name = name - else: - try: - self[name] = toklist[0] - except (KeyError, TypeError, IndexError): - if toklist is not self: - self[name] = toklist - else: - self._name = name - - def __getitem__(self, i): - if isinstance(i, (int, slice)): - return self._toklist[i] - else: - if i not in self._all_names: - return self._tokdict[i][-1][0] - else: - return ParseResults([v[0] for v in self._tokdict[i]]) - - def __setitem__(self, k, v, isinstance=isinstance): - if isinstance(v, _ParseResultsWithOffset): - self._tokdict[k] = self._tokdict.get(k, list()) + [v] - sub = v[0] - elif isinstance(k, (int, slice)): - self._toklist[k] = v - sub = v - else: - self._tokdict[k] = self._tokdict.get(k, list()) + [ - _ParseResultsWithOffset(v, 0) - ] - sub = v - if isinstance(sub, ParseResults): - sub._parent = self - - def __delitem__(self, i): - if isinstance(i, (int, slice)): - mylen = len(self._toklist) - del self._toklist[i] - - # convert int to slice - if isinstance(i, int): - if i < 0: - i += mylen - i = slice(i, i + 1) - # get removed indices - removed = list(range(*i.indices(mylen))) - removed.reverse() - # fixup indices in token dictionary - for name, occurrences in self._tokdict.items(): - for j in removed: - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position - (position > j) - ) - else: - del self._tokdict[i] - - def __contains__(self, k) -> bool: - return k in self._tokdict - - def __len__(self) -> int: - return len(self._toklist) - - def __bool__(self) -> bool: - return not not (self._toklist or self._tokdict) - - def __iter__(self) -> Iterator: - return iter(self._toklist) - - def __reversed__(self) -> Iterator: - return iter(self._toklist[::-1]) - - def keys(self): - return iter(self._tokdict) - - def values(self): - return (self[k] for k in self.keys()) - - def items(self): - return ((k, self[k]) for k in self.keys()) - - def haskeys(self) -> bool: - """ - Since ``keys()`` returns an iterator, this method is helpful in bypassing - code that looks for the existence of any defined results names.""" - return not not self._tokdict - - def pop(self, *args, **kwargs): - """ - Removes and returns item at specified index (default= ``last``). - Supports both ``list`` and ``dict`` semantics for ``pop()``. If - passed no argument or an integer argument, it will use ``list`` - semantics and pop tokens from the list of parsed tokens. If passed - a non-integer argument (most likely a string), it will use ``dict`` - semantics and pop the corresponding value from any defined results - names. A second default return value argument is supported, just as in - ``dict.pop()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - def remove_first(tokens): - tokens.pop(0) - numlist.add_parse_action(remove_first) - print(numlist.parse_string("0 123 321")) # -> ['123', '321'] - - label = Word(alphas) - patt = label("LABEL") + Word(nums)[1, ...] - print(patt.parse_string("AAB 123 321").dump()) - - # Use pop() in a parse action to remove named result (note that corresponding value is not - # removed from list form of results) - def remove_LABEL(tokens): - tokens.pop("LABEL") - return tokens - patt.add_parse_action(remove_LABEL) - print(patt.parse_string("AAB 123 321").dump()) - - prints:: - - ['AAB', '123', '321'] - - LABEL: 'AAB' - - ['AAB', '123', '321'] - """ - if not args: - args = [-1] - for k, v in kwargs.items(): - if k == "default": - args = (args[0], v) - else: - raise TypeError(f"pop() got an unexpected keyword argument {k!r}") - if isinstance(args[0], int) or len(args) == 1 or args[0] in self: - index = args[0] - ret = self[index] - del self[index] - return ret - else: - defaultvalue = args[1] - return defaultvalue - - def get(self, key, default_value=None): - """ - Returns named result matching the given key, or if there is no - such name, then returns the given ``default_value`` or ``None`` if no - ``default_value`` is specified. - - Similar to ``dict.get()``. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string("1999/12/31") - print(result.get("year")) # -> '1999' - print(result.get("hour", "not specified")) # -> 'not specified' - print(result.get("hour")) # -> None - """ - if key in self: - return self[key] - else: - return default_value - - def insert(self, index, ins_string): - """ - Inserts new element at location index in the list of parsed tokens. - - Similar to ``list.insert()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to insert the parse location in the front of the parsed results - def insert_locn(locn, tokens): - tokens.insert(0, locn) - numlist.add_parse_action(insert_locn) - print(numlist.parse_string("0 123 321")) # -> [0, '0', '123', '321'] - """ - self._toklist.insert(index, ins_string) - # fixup indices in token dictionary - for name, occurrences in self._tokdict.items(): - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position + (position > index) - ) - - def append(self, item): - """ - Add single element to end of ``ParseResults`` list of elements. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to compute the sum of the parsed integers, and add it to the end - def append_sum(tokens): - tokens.append(sum(map(int, tokens))) - numlist.add_parse_action(append_sum) - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321', 444] - """ - self._toklist.append(item) - - def extend(self, itemseq): - """ - Add sequence of elements to end of ``ParseResults`` list of elements. - - Example:: - - patt = Word(alphas)[1, ...] - - # use a parse action to append the reverse of the matched strings, to make a palindrome - def make_palindrome(tokens): - tokens.extend(reversed([t[::-1] for t in tokens])) - return ''.join(tokens) - patt.add_parse_action(make_palindrome) - print(patt.parse_string("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' - """ - if isinstance(itemseq, ParseResults): - self.__iadd__(itemseq) - else: - self._toklist.extend(itemseq) - - def clear(self): - """ - Clear all elements and results names. - """ - del self._toklist[:] - self._tokdict.clear() - - def __getattr__(self, name): - try: - return self[name] - except KeyError: - if name.startswith("__"): - raise AttributeError(name) - return "" - - def __add__(self, other: "ParseResults") -> "ParseResults": - ret = self.copy() - ret += other - return ret - - def __iadd__(self, other: "ParseResults") -> "ParseResults": - if not other: - return self - - if other._tokdict: - offset = len(self._toklist) - addoffset = lambda a: offset if a < 0 else a + offset - otheritems = other._tokdict.items() - otherdictitems = [ - (k, _ParseResultsWithOffset(v[0], addoffset(v[1]))) - for k, vlist in otheritems - for v in vlist - ] - for k, v in otherdictitems: - self[k] = v - if isinstance(v[0], ParseResults): - v[0]._parent = self - - self._toklist += other._toklist - self._all_names |= other._all_names - return self - - def __radd__(self, other) -> "ParseResults": - if isinstance(other, int) and other == 0: - # useful for merging many ParseResults using sum() builtin - return self.copy() - else: - # this may raise a TypeError - so be it - return other + self - - def __repr__(self) -> str: - return f"{type(self).__name__}({self._toklist!r}, {self.as_dict()})" - - def __str__(self) -> str: - return ( - "[" - + ", ".join( - [ - str(i) if isinstance(i, ParseResults) else repr(i) - for i in self._toklist - ] - ) - + "]" - ) - - def _asStringList(self, sep=""): - out = [] - for item in self._toklist: - if out and sep: - out.append(sep) - if isinstance(item, ParseResults): - out += item._asStringList() - else: - out.append(str(item)) - return out - - def as_list(self) -> list: - """ - Returns the parse results as a nested list of matching tokens, all converted to strings. - - Example:: - - patt = Word(alphas)[1, ...] - result = patt.parse_string("sldkj lsdkj sldkj") - # even though the result prints in string-like form, it is actually a pyparsing ParseResults - print(type(result), result) # -> ['sldkj', 'lsdkj', 'sldkj'] - - # Use as_list() to create an actual list - result_list = result.as_list() - print(type(result_list), result_list) # -> ['sldkj', 'lsdkj', 'sldkj'] - """ - return [ - res.as_list() if isinstance(res, ParseResults) else res - for res in self._toklist - ] - - def as_dict(self) -> dict: - """ - Returns the named parse results as a nested dictionary. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('12/31/1999') - print(type(result), repr(result)) # -> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) - - result_dict = result.as_dict() - print(type(result_dict), repr(result_dict)) # -> {'day': '1999', 'year': '12', 'month': '31'} - - # even though a ParseResults supports dict-like access, sometime you just need to have a dict - import json - print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable - print(json.dumps(result.as_dict())) # -> {"month": "31", "day": "1999", "year": "12"} - """ - - def to_item(obj): - if isinstance(obj, ParseResults): - return obj.as_dict() if obj.haskeys() else [to_item(v) for v in obj] - else: - return obj - - return dict((k, to_item(v)) for k, v in self.items()) - - def copy(self) -> "ParseResults": - """ - Returns a new shallow copy of a :class:`ParseResults` object. `ParseResults` - items contained within the source are shared with the copy. Use - :class:`ParseResults.deepcopy()` to create a copy with its own separate - content values. - """ - ret = ParseResults(self._toklist) - ret._tokdict = self._tokdict.copy() - ret._parent = self._parent - ret._all_names |= self._all_names - ret._name = self._name - return ret - - def deepcopy(self) -> "ParseResults": - """ - Returns a new deep copy of a :class:`ParseResults` object. - """ - ret = self.copy() - # replace values with copies if they are of known mutable types - for i, obj in enumerate(self._toklist): - if isinstance(obj, ParseResults): - self._toklist[i] = obj.deepcopy() - elif isinstance(obj, (str, bytes)): - pass - elif isinstance(obj, MutableMapping): - self._toklist[i] = dest = type(obj)() - for k, v in obj.items(): - dest[k] = v.deepcopy() if isinstance(v, ParseResults) else v - elif isinstance(obj, Container): - self._toklist[i] = type(obj)( - v.deepcopy() if isinstance(v, ParseResults) else v for v in obj - ) - return ret - - def get_name(self): - r""" - Returns the results name for this token expression. Useful when several - different expressions might match at a particular location. - - Example:: - - integer = Word(nums) - ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") - house_number_expr = Suppress('#') + Word(nums, alphanums) - user_data = (Group(house_number_expr)("house_number") - | Group(ssn_expr)("ssn") - | Group(integer)("age")) - user_info = user_data[1, ...] - - result = user_info.parse_string("22 111-22-3333 #221B") - for item in result: - print(item.get_name(), ':', item[0]) - - prints:: - - age : 22 - ssn : 111-22-3333 - house_number : 221B - """ - if self._name: - return self._name - elif self._parent: - par: "ParseResults" = self._parent - parent_tokdict_items = par._tokdict.items() - return next( - ( - k - for k, vlist in parent_tokdict_items - for v, loc in vlist - if v is self - ), - None, - ) - elif ( - len(self) == 1 - and len(self._tokdict) == 1 - and next(iter(self._tokdict.values()))[0][1] in (0, -1) - ): - return next(iter(self._tokdict.keys())) - else: - return None - - def dump(self, indent="", full=True, include_list=True, _depth=0) -> str: - """ - Diagnostic method for listing out the contents of - a :class:`ParseResults`. Accepts an optional ``indent`` argument so - that this string can be embedded in a nested display of other data. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('1999/12/31') - print(result.dump()) - - prints:: - - ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - out = [] - NL = "\n" - out.append(indent + str(self.as_list()) if include_list else "") - - if full: - if self.haskeys(): - items = sorted((str(k), v) for k, v in self.items()) - for k, v in items: - if out: - out.append(NL) - out.append(f"{indent}{(' ' * _depth)}- {k}: ") - if isinstance(v, ParseResults): - if v: - out.append( - v.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ) - ) - else: - out.append(str(v)) - else: - out.append(repr(v)) - if any(isinstance(vv, ParseResults) for vv in self): - v = self - for i, vv in enumerate(v): - if isinstance(vv, ParseResults): - out.append( - "\n{}{}[{}]:\n{}{}{}".format( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - vv.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ), - ) - ) - else: - out.append( - "\n%s%s[%d]:\n%s%s%s" - % ( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - str(vv), - ) - ) - - return "".join(out) - - def pprint(self, *args, **kwargs): - """ - Pretty-printer for parsed results as a list, using the - `pprint `_ module. - Accepts additional positional or keyword args as defined for - `pprint.pprint `_ . - - Example:: - - ident = Word(alphas, alphanums) - num = Word(nums) - func = Forward() - term = ident | num | Group('(' + func + ')') - func <<= ident + Group(Optional(DelimitedList(term))) - result = func.parse_string("fna a,b,(fnb c,d,200),100") - result.pprint(width=40) - - prints:: - - ['fna', - ['a', - 'b', - ['(', 'fnb', ['c', 'd', '200'], ')'], - '100']] - """ - pprint.pprint(self.as_list(), *args, **kwargs) - - # add support for pickle protocol - def __getstate__(self): - return ( - self._toklist, - ( - self._tokdict.copy(), - None, - self._all_names, - self._name, - ), - ) - - def __setstate__(self, state): - self._toklist, (self._tokdict, par, inAccumNames, self._name) = state - self._all_names = set(inAccumNames) - self._parent = None - - def __getnewargs__(self): - return self._toklist, self._name - - def __dir__(self): - return dir(type(self)) + list(self.keys()) - - @classmethod - def from_dict(cls, other, name=None) -> "ParseResults": - """ - Helper classmethod to construct a ``ParseResults`` from a ``dict``, preserving the - name-value relations as results names. If an optional ``name`` argument is - given, a nested ``ParseResults`` will be returned. - """ - - def is_iterable(obj): - try: - iter(obj) - except Exception: - return False - # str's are iterable, but in pyparsing, we don't want to iterate over them - else: - return not isinstance(obj, str_type) - - ret = cls([]) - for k, v in other.items(): - if isinstance(v, Mapping): - ret += cls.from_dict(v, name=k) - else: - ret += cls([v], name=k, asList=is_iterable(v)) - if name is not None: - ret = cls([ret], name=name) - return ret - - asList = as_list - """Deprecated - use :class:`as_list`""" - asDict = as_dict - """Deprecated - use :class:`as_dict`""" - getName = get_name - """Deprecated - use :class:`get_name`""" - - -MutableMapping.register(ParseResults) -MutableSequence.register(ParseResults) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/testing.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/testing.py deleted file mode 100644 index 6a254c1c..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/testing.py +++ /dev/null @@ -1,331 +0,0 @@ -# testing.py - -from contextlib import contextmanager -import typing - -from .core import ( - ParserElement, - ParseException, - Keyword, - __diag__, - __compat__, -) - - -class pyparsing_test: - """ - namespace class for classes useful in writing unit tests - """ - - class reset_pyparsing_context: - """ - Context manager to be used when writing unit tests that modify pyparsing config values: - - packrat parsing - - bounded recursion parsing - - default whitespace characters. - - default keyword characters - - literal string auto-conversion class - - __diag__ settings - - Example:: - - with reset_pyparsing_context(): - # test that literals used to construct a grammar are automatically suppressed - ParserElement.inlineLiteralsUsing(Suppress) - - term = Word(alphas) | Word(nums) - group = Group('(' + term[...] + ')') - - # assert that the '()' characters are not included in the parsed tokens - self.assertParseAndCheckList(group, "(abc 123 def)", ['abc', '123', 'def']) - - # after exiting context manager, literals are converted to Literal expressions again - """ - - def __init__(self): - self._save_context = {} - - def save(self): - self._save_context["default_whitespace"] = ParserElement.DEFAULT_WHITE_CHARS - self._save_context["default_keyword_chars"] = Keyword.DEFAULT_KEYWORD_CHARS - - self._save_context[ - "literal_string_class" - ] = ParserElement._literalStringClass - - self._save_context["verbose_stacktrace"] = ParserElement.verbose_stacktrace - - self._save_context["packrat_enabled"] = ParserElement._packratEnabled - if ParserElement._packratEnabled: - self._save_context[ - "packrat_cache_size" - ] = ParserElement.packrat_cache.size - else: - self._save_context["packrat_cache_size"] = None - self._save_context["packrat_parse"] = ParserElement._parse - self._save_context[ - "recursion_enabled" - ] = ParserElement._left_recursion_enabled - - self._save_context["__diag__"] = { - name: getattr(__diag__, name) for name in __diag__._all_names - } - - self._save_context["__compat__"] = { - "collect_all_And_tokens": __compat__.collect_all_And_tokens - } - - return self - - def restore(self): - # reset pyparsing global state - if ( - ParserElement.DEFAULT_WHITE_CHARS - != self._save_context["default_whitespace"] - ): - ParserElement.set_default_whitespace_chars( - self._save_context["default_whitespace"] - ) - - ParserElement.verbose_stacktrace = self._save_context["verbose_stacktrace"] - - Keyword.DEFAULT_KEYWORD_CHARS = self._save_context["default_keyword_chars"] - ParserElement.inlineLiteralsUsing( - self._save_context["literal_string_class"] - ) - - for name, value in self._save_context["__diag__"].items(): - (__diag__.enable if value else __diag__.disable)(name) - - ParserElement._packratEnabled = False - if self._save_context["packrat_enabled"]: - ParserElement.enable_packrat(self._save_context["packrat_cache_size"]) - else: - ParserElement._parse = self._save_context["packrat_parse"] - ParserElement._left_recursion_enabled = self._save_context[ - "recursion_enabled" - ] - - __compat__.collect_all_And_tokens = self._save_context["__compat__"] - - return self - - def copy(self): - ret = type(self)() - ret._save_context.update(self._save_context) - return ret - - def __enter__(self): - return self.save() - - def __exit__(self, *args): - self.restore() - - class TestParseResultsAsserts: - """ - A mixin class to add parse results assertion methods to normal unittest.TestCase classes. - """ - - def assertParseResultsEquals( - self, result, expected_list=None, expected_dict=None, msg=None - ): - """ - Unit test assertion to compare a :class:`ParseResults` object with an optional ``expected_list``, - and compare any defined results names with an optional ``expected_dict``. - """ - if expected_list is not None: - self.assertEqual(expected_list, result.as_list(), msg=msg) - if expected_dict is not None: - self.assertEqual(expected_dict, result.as_dict(), msg=msg) - - def assertParseAndCheckList( - self, expr, test_string, expected_list, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asList()`` is equal to the ``expected_list``. - """ - result = expr.parse_string(test_string, parse_all=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_list=expected_list, msg=msg) - - def assertParseAndCheckDict( - self, expr, test_string, expected_dict, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asDict()`` is equal to the ``expected_dict``. - """ - result = expr.parse_string(test_string, parseAll=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_dict=expected_dict, msg=msg) - - def assertRunTestResults( - self, run_tests_report, expected_parse_results=None, msg=None - ): - """ - Unit test assertion to evaluate output of ``ParserElement.runTests()``. If a list of - list-dict tuples is given as the ``expected_parse_results`` argument, then these are zipped - with the report tuples returned by ``runTests`` and evaluated using ``assertParseResultsEquals``. - Finally, asserts that the overall ``runTests()`` success value is ``True``. - - :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests - :param expected_parse_results (optional): [tuple(str, list, dict, Exception)] - """ - run_test_success, run_test_results = run_tests_report - - if expected_parse_results is not None: - merged = [ - (*rpt, expected) - for rpt, expected in zip(run_test_results, expected_parse_results) - ] - for test_string, result, expected in merged: - # expected should be a tuple containing a list and/or a dict or an exception, - # and optional failure message string - # an empty tuple will skip any result validation - fail_msg = next( - (exp for exp in expected if isinstance(exp, str)), None - ) - expected_exception = next( - ( - exp - for exp in expected - if isinstance(exp, type) and issubclass(exp, Exception) - ), - None, - ) - if expected_exception is not None: - with self.assertRaises( - expected_exception=expected_exception, msg=fail_msg or msg - ): - if isinstance(result, Exception): - raise result - else: - expected_list = next( - (exp for exp in expected if isinstance(exp, list)), None - ) - expected_dict = next( - (exp for exp in expected if isinstance(exp, dict)), None - ) - if (expected_list, expected_dict) != (None, None): - self.assertParseResultsEquals( - result, - expected_list=expected_list, - expected_dict=expected_dict, - msg=fail_msg or msg, - ) - else: - # warning here maybe? - print(f"no validation for {test_string!r}") - - # do this last, in case some specific test results can be reported instead - self.assertTrue( - run_test_success, msg=msg if msg is not None else "failed runTests" - ) - - @contextmanager - def assertRaisesParseException(self, exc_type=ParseException, msg=None): - with self.assertRaises(exc_type, msg=msg): - yield - - @staticmethod - def with_line_numbers( - s: str, - start_line: typing.Optional[int] = None, - end_line: typing.Optional[int] = None, - expand_tabs: bool = True, - eol_mark: str = "|", - mark_spaces: typing.Optional[str] = None, - mark_control: typing.Optional[str] = None, - ) -> str: - """ - Helpful method for debugging a parser - prints a string with line and column numbers. - (Line and column numbers are 1-based.) - - :param s: tuple(bool, str - string to be printed with line and column numbers - :param start_line: int - (optional) starting line number in s to print (default=1) - :param end_line: int - (optional) ending line number in s to print (default=len(s)) - :param expand_tabs: bool - (optional) expand tabs to spaces, to match the pyparsing default - :param eol_mark: str - (optional) string to mark the end of lines, helps visualize trailing spaces (default="|") - :param mark_spaces: str - (optional) special character to display in place of spaces - :param mark_control: str - (optional) convert non-printing control characters to a placeholding - character; valid values: - - "unicode" - replaces control chars with Unicode symbols, such as "␍" and "␊" - - any single character string - replace control characters with given string - - None (default) - string is displayed as-is - - :return: str - input string with leading line numbers and column number headers - """ - if expand_tabs: - s = s.expandtabs() - if mark_control is not None: - mark_control = typing.cast(str, mark_control) - if mark_control == "unicode": - transtable_map = { - c: u for c, u in zip(range(0, 33), range(0x2400, 0x2433)) - } - transtable_map[127] = 0x2421 - tbl = str.maketrans(transtable_map) - eol_mark = "" - else: - ord_mark_control = ord(mark_control) - tbl = str.maketrans( - {c: ord_mark_control for c in list(range(0, 32)) + [127]} - ) - s = s.translate(tbl) - if mark_spaces is not None and mark_spaces != " ": - if mark_spaces == "unicode": - tbl = str.maketrans({9: 0x2409, 32: 0x2423}) - s = s.translate(tbl) - else: - s = s.replace(" ", mark_spaces) - if start_line is None: - start_line = 1 - if end_line is None: - end_line = len(s) - end_line = min(end_line, len(s)) - start_line = min(max(1, start_line), end_line) - - if mark_control != "unicode": - s_lines = s.splitlines()[start_line - 1 : end_line] - else: - s_lines = [line + "␊" for line in s.split("␊")[start_line - 1 : end_line]] - if not s_lines: - return "" - - lineno_width = len(str(end_line)) - max_line_len = max(len(line) for line in s_lines) - lead = " " * (lineno_width + 1) - if max_line_len >= 99: - header0 = ( - lead - + "".join( - f"{' ' * 99}{(i + 1) % 100}" - for i in range(max(max_line_len // 100, 1)) - ) - + "\n" - ) - else: - header0 = "" - header1 = ( - header0 - + lead - + "".join(f" {(i + 1) % 10}" for i in range(-(-max_line_len // 10))) - + "\n" - ) - header2 = lead + "1234567890" * (-(-max_line_len // 10)) + "\n" - return ( - header1 - + header2 - + "\n".join( - f"{i:{lineno_width}d}:{line}{eol_mark}" - for i, line in enumerate(s_lines, start=start_line) - ) - + "\n" - ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/unicode.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/unicode.py deleted file mode 100644 index ec0b3a4f..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/unicode.py +++ /dev/null @@ -1,361 +0,0 @@ -# unicode.py - -import sys -from itertools import filterfalse -from typing import List, Tuple, Union - - -class _lazyclassproperty: - def __init__(self, fn): - self.fn = fn - self.__doc__ = fn.__doc__ - self.__name__ = fn.__name__ - - def __get__(self, obj, cls): - if cls is None: - cls = type(obj) - if not hasattr(cls, "_intern") or any( - cls._intern is getattr(superclass, "_intern", []) - for superclass in cls.__mro__[1:] - ): - cls._intern = {} - attrname = self.fn.__name__ - if attrname not in cls._intern: - cls._intern[attrname] = self.fn(cls) - return cls._intern[attrname] - - -UnicodeRangeList = List[Union[Tuple[int, int], Tuple[int]]] - - -class unicode_set: - """ - A set of Unicode characters, for language-specific strings for - ``alphas``, ``nums``, ``alphanums``, and ``printables``. - A unicode_set is defined by a list of ranges in the Unicode character - set, in a class attribute ``_ranges``. Ranges can be specified using - 2-tuples or a 1-tuple, such as:: - - _ranges = [ - (0x0020, 0x007e), - (0x00a0, 0x00ff), - (0x0100,), - ] - - Ranges are left- and right-inclusive. A 1-tuple of (x,) is treated as (x, x). - - A unicode set can also be defined using multiple inheritance of other unicode sets:: - - class CJK(Chinese, Japanese, Korean): - pass - """ - - _ranges: UnicodeRangeList = [] - - @_lazyclassproperty - def _chars_for_ranges(cls): - ret = [] - for cc in cls.__mro__: - if cc is unicode_set: - break - for rr in getattr(cc, "_ranges", ()): - ret.extend(range(rr[0], rr[-1] + 1)) - return [chr(c) for c in sorted(set(ret))] - - @_lazyclassproperty - def printables(cls): - """all non-whitespace characters in this range""" - return "".join(filterfalse(str.isspace, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphas(cls): - """all alphabetic characters in this range""" - return "".join(filter(str.isalpha, cls._chars_for_ranges)) - - @_lazyclassproperty - def nums(cls): - """all numeric digit characters in this range""" - return "".join(filter(str.isdigit, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphanums(cls): - """all alphanumeric characters in this range""" - return cls.alphas + cls.nums - - @_lazyclassproperty - def identchars(cls): - """all characters in this range that are valid identifier characters, plus underscore '_'""" - return "".join( - sorted( - set( - "".join(filter(str.isidentifier, cls._chars_for_ranges)) - + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµº" - + "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ" - + "_" - ) - ) - ) - - @_lazyclassproperty - def identbodychars(cls): - """ - all characters in this range that are valid identifier body characters, - plus the digits 0-9, and · (Unicode MIDDLE DOT) - """ - return "".join( - sorted( - set( - cls.identchars - + "0123456789·" - + "".join( - [c for c in cls._chars_for_ranges if ("_" + c).isidentifier()] - ) - ) - ) - ) - - @_lazyclassproperty - def identifier(cls): - """ - a pyparsing Word expression for an identifier using this range's definitions for - identchars and identbodychars - """ - from pip._vendor.pyparsing import Word - - return Word(cls.identchars, cls.identbodychars) - - -class pyparsing_unicode(unicode_set): - """ - A namespace class for defining common language unicode_sets. - """ - - # fmt: off - - # define ranges in language character sets - _ranges: UnicodeRangeList = [ - (0x0020, sys.maxunicode), - ] - - class BasicMultilingualPlane(unicode_set): - """Unicode set for the Basic Multilingual Plane""" - _ranges: UnicodeRangeList = [ - (0x0020, 0xFFFF), - ] - - class Latin1(unicode_set): - """Unicode set for Latin-1 Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0020, 0x007E), - (0x00A0, 0x00FF), - ] - - class LatinA(unicode_set): - """Unicode set for Latin-A Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0100, 0x017F), - ] - - class LatinB(unicode_set): - """Unicode set for Latin-B Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0180, 0x024F), - ] - - class Greek(unicode_set): - """Unicode set for Greek Unicode Character Ranges""" - _ranges: UnicodeRangeList = [ - (0x0342, 0x0345), - (0x0370, 0x0377), - (0x037A, 0x037F), - (0x0384, 0x038A), - (0x038C,), - (0x038E, 0x03A1), - (0x03A3, 0x03E1), - (0x03F0, 0x03FF), - (0x1D26, 0x1D2A), - (0x1D5E,), - (0x1D60,), - (0x1D66, 0x1D6A), - (0x1F00, 0x1F15), - (0x1F18, 0x1F1D), - (0x1F20, 0x1F45), - (0x1F48, 0x1F4D), - (0x1F50, 0x1F57), - (0x1F59,), - (0x1F5B,), - (0x1F5D,), - (0x1F5F, 0x1F7D), - (0x1F80, 0x1FB4), - (0x1FB6, 0x1FC4), - (0x1FC6, 0x1FD3), - (0x1FD6, 0x1FDB), - (0x1FDD, 0x1FEF), - (0x1FF2, 0x1FF4), - (0x1FF6, 0x1FFE), - (0x2129,), - (0x2719, 0x271A), - (0xAB65,), - (0x10140, 0x1018D), - (0x101A0,), - (0x1D200, 0x1D245), - (0x1F7A1, 0x1F7A7), - ] - - class Cyrillic(unicode_set): - """Unicode set for Cyrillic Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0400, 0x052F), - (0x1C80, 0x1C88), - (0x1D2B,), - (0x1D78,), - (0x2DE0, 0x2DFF), - (0xA640, 0xA672), - (0xA674, 0xA69F), - (0xFE2E, 0xFE2F), - ] - - class Chinese(unicode_set): - """Unicode set for Chinese Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x2E80, 0x2E99), - (0x2E9B, 0x2EF3), - (0x31C0, 0x31E3), - (0x3400, 0x4DB5), - (0x4E00, 0x9FEF), - (0xA700, 0xA707), - (0xF900, 0xFA6D), - (0xFA70, 0xFAD9), - (0x16FE2, 0x16FE3), - (0x1F210, 0x1F212), - (0x1F214, 0x1F23B), - (0x1F240, 0x1F248), - (0x20000, 0x2A6D6), - (0x2A700, 0x2B734), - (0x2B740, 0x2B81D), - (0x2B820, 0x2CEA1), - (0x2CEB0, 0x2EBE0), - (0x2F800, 0x2FA1D), - ] - - class Japanese(unicode_set): - """Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges""" - - class Kanji(unicode_set): - "Unicode set for Kanji Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x4E00, 0x9FBF), - (0x3000, 0x303F), - ] - - class Hiragana(unicode_set): - """Unicode set for Hiragana Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x3041, 0x3096), - (0x3099, 0x30A0), - (0x30FC,), - (0xFF70,), - (0x1B001,), - (0x1B150, 0x1B152), - (0x1F200,), - ] - - class Katakana(unicode_set): - """Unicode set for Katakana Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x3099, 0x309C), - (0x30A0, 0x30FF), - (0x31F0, 0x31FF), - (0x32D0, 0x32FE), - (0xFF65, 0xFF9F), - (0x1B000,), - (0x1B164, 0x1B167), - (0x1F201, 0x1F202), - (0x1F213,), - ] - - 漢字 = Kanji - カタカナ = Katakana - ひらがな = Hiragana - - _ranges = ( - Kanji._ranges - + Hiragana._ranges - + Katakana._ranges - ) - - class Hangul(unicode_set): - """Unicode set for Hangul (Korean) Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x1100, 0x11FF), - (0x302E, 0x302F), - (0x3131, 0x318E), - (0x3200, 0x321C), - (0x3260, 0x327B), - (0x327E,), - (0xA960, 0xA97C), - (0xAC00, 0xD7A3), - (0xD7B0, 0xD7C6), - (0xD7CB, 0xD7FB), - (0xFFA0, 0xFFBE), - (0xFFC2, 0xFFC7), - (0xFFCA, 0xFFCF), - (0xFFD2, 0xFFD7), - (0xFFDA, 0xFFDC), - ] - - Korean = Hangul - - class CJK(Chinese, Japanese, Hangul): - """Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range""" - - class Thai(unicode_set): - """Unicode set for Thai Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0E01, 0x0E3A), - (0x0E3F, 0x0E5B) - ] - - class Arabic(unicode_set): - """Unicode set for Arabic Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0600, 0x061B), - (0x061E, 0x06FF), - (0x0700, 0x077F), - ] - - class Hebrew(unicode_set): - """Unicode set for Hebrew Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0591, 0x05C7), - (0x05D0, 0x05EA), - (0x05EF, 0x05F4), - (0xFB1D, 0xFB36), - (0xFB38, 0xFB3C), - (0xFB3E,), - (0xFB40, 0xFB41), - (0xFB43, 0xFB44), - (0xFB46, 0xFB4F), - ] - - class Devanagari(unicode_set): - """Unicode set for Devanagari Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0900, 0x097F), - (0xA8E0, 0xA8FF) - ] - - BMP = BasicMultilingualPlane - - # add language identifiers using language Unicode - العربية = Arabic - 中文 = Chinese - кириллица = Cyrillic - Ελληνικά = Greek - עִברִית = Hebrew - 日本語 = Japanese - 한국어 = Korean - ไทย = Thai - देवनागरी = Devanagari - - # fmt: on diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/util.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/util.py deleted file mode 100644 index d8d3f414..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/util.py +++ /dev/null @@ -1,284 +0,0 @@ -# util.py -import inspect -import warnings -import types -import collections -import itertools -from functools import lru_cache, wraps -from typing import Callable, List, Union, Iterable, TypeVar, cast - -_bslash = chr(92) -C = TypeVar("C", bound=Callable) - - -class __config_flags: - """Internal class for defining compatibility and debugging flags""" - - _all_names: List[str] = [] - _fixed_names: List[str] = [] - _type_desc = "configuration" - - @classmethod - def _set(cls, dname, value): - if dname in cls._fixed_names: - warnings.warn( - f"{cls.__name__}.{dname} {cls._type_desc} is {str(getattr(cls, dname)).upper()}" - f" and cannot be overridden", - stacklevel=3, - ) - return - if dname in cls._all_names: - setattr(cls, dname, value) - else: - raise ValueError(f"no such {cls._type_desc} {dname!r}") - - enable = classmethod(lambda cls, name: cls._set(name, True)) - disable = classmethod(lambda cls, name: cls._set(name, False)) - - -@lru_cache(maxsize=128) -def col(loc: int, strg: str) -> int: - """ - Returns current column within a string, counting newlines as line separators. - The first column is number 1. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See - :class:`ParserElement.parse_string` for more - information on parsing strings containing ```` s, and suggested - methods to maintain a consistent view of the parsed string, the parse - location, and line and column positions within the parsed string. - """ - s = strg - return 1 if 0 < loc < len(s) and s[loc - 1] == "\n" else loc - s.rfind("\n", 0, loc) - - -@lru_cache(maxsize=128) -def lineno(loc: int, strg: str) -> int: - """Returns current line number within a string, counting newlines as line separators. - The first line is number 1. - - Note - the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See :class:`ParserElement.parse_string` - for more information on parsing strings containing ```` s, and - suggested methods to maintain a consistent view of the parsed string, the - parse location, and line and column positions within the parsed string. - """ - return strg.count("\n", 0, loc) + 1 - - -@lru_cache(maxsize=128) -def line(loc: int, strg: str) -> str: - """ - Returns the line of text containing loc within a string, counting newlines as line separators. - """ - last_cr = strg.rfind("\n", 0, loc) - next_cr = strg.find("\n", loc) - return strg[last_cr + 1 : next_cr] if next_cr >= 0 else strg[last_cr + 1 :] - - -class _UnboundedCache: - def __init__(self): - cache = {} - cache_get = cache.get - self.not_in_cache = not_in_cache = object() - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - - def clear(_): - cache.clear() - - self.size = None - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class _FifoCache: - def __init__(self, size): - self.not_in_cache = not_in_cache = object() - cache = {} - keyring = [object()] * size - cache_get = cache.get - cache_pop = cache.pop - keyiter = itertools.cycle(range(size)) - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - i = next(keyiter) - cache_pop(keyring[i], None) - keyring[i] = key - - def clear(_): - cache.clear() - keyring[:] = [object()] * size - - self.size = size - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class LRUMemo: - """ - A memoizing mapping that retains `capacity` deleted items - - The memo tracks retained items by their access order; once `capacity` items - are retained, the least recently used item is discarded. - """ - - def __init__(self, capacity): - self._capacity = capacity - self._active = {} - self._memory = collections.OrderedDict() - - def __getitem__(self, key): - try: - return self._active[key] - except KeyError: - self._memory.move_to_end(key) - return self._memory[key] - - def __setitem__(self, key, value): - self._memory.pop(key, None) - self._active[key] = value - - def __delitem__(self, key): - try: - value = self._active.pop(key) - except KeyError: - pass - else: - while len(self._memory) >= self._capacity: - self._memory.popitem(last=False) - self._memory[key] = value - - def clear(self): - self._active.clear() - self._memory.clear() - - -class UnboundedMemo(dict): - """ - A memoizing mapping that retains all deleted items - """ - - def __delitem__(self, key): - pass - - -def _escape_regex_range_chars(s: str) -> str: - # escape these chars: ^-[] - for c in r"\^-[]": - s = s.replace(c, _bslash + c) - s = s.replace("\n", r"\n") - s = s.replace("\t", r"\t") - return str(s) - - -def _collapse_string_to_ranges( - s: Union[str, Iterable[str]], re_escape: bool = True -) -> str: - def is_consecutive(c): - c_int = ord(c) - is_consecutive.prev, prev = c_int, is_consecutive.prev - if c_int - prev > 1: - is_consecutive.value = next(is_consecutive.counter) - return is_consecutive.value - - is_consecutive.prev = 0 # type: ignore [attr-defined] - is_consecutive.counter = itertools.count() # type: ignore [attr-defined] - is_consecutive.value = -1 # type: ignore [attr-defined] - - def escape_re_range_char(c): - return "\\" + c if c in r"\^-][" else c - - def no_escape_re_range_char(c): - return c - - if not re_escape: - escape_re_range_char = no_escape_re_range_char - - ret = [] - s = "".join(sorted(set(s))) - if len(s) > 3: - for _, chars in itertools.groupby(s, key=is_consecutive): - first = last = next(chars) - last = collections.deque( - itertools.chain(iter([last]), chars), maxlen=1 - ).pop() - if first == last: - ret.append(escape_re_range_char(first)) - else: - sep = "" if ord(last) == ord(first) + 1 else "-" - ret.append( - f"{escape_re_range_char(first)}{sep}{escape_re_range_char(last)}" - ) - else: - ret = [escape_re_range_char(c) for c in s] - - return "".join(ret) - - -def _flatten(ll: list) -> list: - ret = [] - for i in ll: - if isinstance(i, list): - ret.extend(_flatten(i)) - else: - ret.append(i) - return ret - - -def _make_synonym_function(compat_name: str, fn: C) -> C: - # In a future version, uncomment the code in the internal _inner() functions - # to begin emitting DeprecationWarnings. - - # Unwrap staticmethod/classmethod - fn = getattr(fn, "__func__", fn) - - # (Presence of 'self' arg in signature is used by explain_exception() methods, so we take - # some extra steps to add it if present in decorated function.) - if "self" == list(inspect.signature(fn).parameters)[0]: - - @wraps(fn) - def _inner(self, *args, **kwargs): - # warnings.warn( - # f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3 - # ) - return fn(self, *args, **kwargs) - - else: - - @wraps(fn) - def _inner(*args, **kwargs): - # warnings.warn( - # f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=3 - # ) - return fn(*args, **kwargs) - - _inner.__doc__ = f"""Deprecated - use :class:`{fn.__name__}`""" - _inner.__name__ = compat_name - _inner.__annotations__ = fn.__annotations__ - if isinstance(fn, types.FunctionType): - _inner.__kwdefaults__ = fn.__kwdefaults__ - elif isinstance(fn, type) and hasattr(fn, "__init__"): - _inner.__kwdefaults__ = fn.__init__.__kwdefaults__ - else: - _inner.__kwdefaults__ = None - _inner.__qualname__ = fn.__qualname__ - return cast(C, _inner) - - -def replaced_by_pep8(fn: C) -> Callable[[Callable], C]: - """ - Decorator for pre-PEP8 compatibility synonyms, to link them to the new function. - """ - return lambda other: _make_synonym_function(other.__name__, fn) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE new file mode 100644 index 00000000..b0ae9dbc --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Thomas Kluyver + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py index ddfcf7f7..746b89f7 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py @@ -1,8 +1,9 @@ """Wrappers to call pyproject.toml-based build backend hooks. """ +from typing import TYPE_CHECKING + from ._impl import ( - BackendInvalid, BackendUnavailable, BuildBackendHookCaller, HookMissing, @@ -11,13 +12,20 @@ from ._impl import ( quiet_subprocess_runner, ) -__version__ = '1.0.0' +__version__ = "1.2.0" __all__ = [ - 'BackendUnavailable', - 'BackendInvalid', - 'HookMissing', - 'UnsupportedOperation', - 'default_subprocess_runner', - 'quiet_subprocess_runner', - 'BuildBackendHookCaller', + "BackendUnavailable", + "BackendInvalid", + "HookMissing", + "UnsupportedOperation", + "default_subprocess_runner", + "quiet_subprocess_runner", + "BuildBackendHookCaller", ] + +BackendInvalid = BackendUnavailable # Deprecated alias, previously a separate exception + +if TYPE_CHECKING: + from ._impl import SubprocessRunner + + __all__ += ["SubprocessRunner"] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_compat.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_compat.py deleted file mode 100644 index 95e509c0..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_compat.py +++ /dev/null @@ -1,8 +0,0 @@ -__all__ = ("tomllib",) - -import sys - -if sys.version_info >= (3, 11): - import tomllib -else: - from pip._vendor import tomli as tomllib diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py index 37b0e653..d1e9d7bb 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py @@ -6,48 +6,72 @@ from contextlib import contextmanager from os.path import abspath from os.path import join as pjoin from subprocess import STDOUT, check_call, check_output +from typing import TYPE_CHECKING, Any, Iterator, Mapping, Optional, Sequence from ._in_process import _in_proc_script_path +if TYPE_CHECKING: + from typing import Protocol -def write_json(obj, path, **kwargs): - with open(path, 'w', encoding='utf-8') as f: + class SubprocessRunner(Protocol): + """A protocol for the subprocess runner.""" + + def __call__( + self, + cmd: Sequence[str], + cwd: Optional[str] = None, + extra_environ: Optional[Mapping[str, str]] = None, + ) -> None: + ... + + +def write_json(obj: Mapping[str, Any], path: str, **kwargs) -> None: + with open(path, "w", encoding="utf-8") as f: json.dump(obj, f, **kwargs) -def read_json(path): - with open(path, encoding='utf-8') as f: +def read_json(path: str) -> Mapping[str, Any]: + with open(path, encoding="utf-8") as f: return json.load(f) class BackendUnavailable(Exception): """Will be raised if the backend cannot be imported in the hook process.""" - def __init__(self, traceback): - self.traceback = traceback - -class BackendInvalid(Exception): - """Will be raised if the backend is invalid.""" - def __init__(self, backend_name, backend_path, message): - super().__init__(message) + def __init__( + self, + traceback: str, + message: Optional[str] = None, + backend_name: Optional[str] = None, + backend_path: Optional[Sequence[str]] = None, + ) -> None: + # Preserving arg order for the sake of API backward compatibility. self.backend_name = backend_name self.backend_path = backend_path + self.traceback = traceback + super().__init__(message or "Error while importing backend") class HookMissing(Exception): """Will be raised on missing hooks (if a fallback can't be used).""" - def __init__(self, hook_name): + + def __init__(self, hook_name: str) -> None: super().__init__(hook_name) self.hook_name = hook_name class UnsupportedOperation(Exception): """May be raised by build_sdist if the backend indicates that it can't.""" - def __init__(self, traceback): + + def __init__(self, traceback: str) -> None: self.traceback = traceback -def default_subprocess_runner(cmd, cwd=None, extra_environ=None): +def default_subprocess_runner( + cmd: Sequence[str], + cwd: Optional[str] = None, + extra_environ: Optional[Mapping[str, str]] = None, +) -> None: """The default method of calling the wrapper subprocess. This uses :func:`subprocess.check_call` under the hood. @@ -59,7 +83,11 @@ def default_subprocess_runner(cmd, cwd=None, extra_environ=None): check_call(cmd, cwd=cwd, env=env) -def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None): +def quiet_subprocess_runner( + cmd: Sequence[str], + cwd: Optional[str] = None, + extra_environ: Optional[Mapping[str, str]] = None, +) -> None: """Call the subprocess while suppressing output. This uses :func:`subprocess.check_output` under the hood. @@ -71,7 +99,7 @@ def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None): check_output(cmd, cwd=cwd, env=env, stderr=STDOUT) -def norm_and_check(source_tree, requested): +def norm_and_check(source_tree: str, requested: str) -> str: """Normalise and check a backend path. Ensure that the requested backend path is specified as a relative path, @@ -96,17 +124,16 @@ def norm_and_check(source_tree, requested): class BuildBackendHookCaller: - """A wrapper to call the build backend hooks for a source directory. - """ + """A wrapper to call the build backend hooks for a source directory.""" def __init__( - self, - source_dir, - build_backend, - backend_path=None, - runner=None, - python_executable=None, - ): + self, + source_dir: str, + build_backend: str, + backend_path: Optional[Sequence[str]] = None, + runner: Optional["SubprocessRunner"] = None, + python_executable: Optional[str] = None, + ) -> None: """ :param source_dir: The source directory to invoke the build backend for :param build_backend: The build backend spec @@ -121,9 +148,7 @@ class BuildBackendHookCaller: self.source_dir = abspath(source_dir) self.build_backend = build_backend if backend_path: - backend_path = [ - norm_and_check(self.source_dir, p) for p in backend_path - ] + backend_path = [norm_and_check(self.source_dir, p) for p in backend_path] self.backend_path = backend_path self._subprocess_runner = runner if not python_executable: @@ -131,10 +156,12 @@ class BuildBackendHookCaller: self.python_executable = python_executable @contextmanager - def subprocess_runner(self, runner): + def subprocess_runner(self, runner: "SubprocessRunner") -> Iterator[None]: """A context manager for temporarily overriding the default :ref:`subprocess runner `. + :param runner: The new subprocess runner to use within the context. + .. code-block:: python hook_caller = BuildBackendHookCaller(...) @@ -148,33 +175,44 @@ class BuildBackendHookCaller: finally: self._subprocess_runner = prev - def _supported_features(self): + def _supported_features(self) -> Sequence[str]: """Return the list of optional features supported by the backend.""" - return self._call_hook('_supported_features', {}) + return self._call_hook("_supported_features", {}) - def get_requires_for_build_wheel(self, config_settings=None): + def get_requires_for_build_wheel( + self, + config_settings: Optional[Mapping[str, Any]] = None, + ) -> Sequence[str]: """Get additional dependencies required for building a wheel. + :param config_settings: The configuration settings for the build backend :returns: A list of :pep:`dependency specifiers <508>`. - :rtype: list[str] .. admonition:: Fallback If the build backend does not defined a hook with this name, an empty list will be returned. """ - return self._call_hook('get_requires_for_build_wheel', { - 'config_settings': config_settings - }) + return self._call_hook( + "get_requires_for_build_wheel", {"config_settings": config_settings} + ) def prepare_metadata_for_build_wheel( - self, metadata_directory, config_settings=None, - _allow_fallback=True): + self, + metadata_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + _allow_fallback: bool = True, + ) -> str: """Prepare a ``*.dist-info`` folder with metadata for this project. + :param metadata_directory: The directory to write the metadata to + :param config_settings: The configuration settings for the build backend + :param _allow_fallback: + Whether to allow the fallback to building a wheel and extracting + the metadata from it. Should be passed as a keyword argument only. + :returns: Name of the newly created subfolder within ``metadata_directory``, containing the metadata. - :rtype: str .. admonition:: Fallback @@ -183,17 +221,26 @@ class BuildBackendHookCaller: wheel via the ``build_wheel`` hook and the dist-info extracted from that will be returned. """ - return self._call_hook('prepare_metadata_for_build_wheel', { - 'metadata_directory': abspath(metadata_directory), - 'config_settings': config_settings, - '_allow_fallback': _allow_fallback, - }) + return self._call_hook( + "prepare_metadata_for_build_wheel", + { + "metadata_directory": abspath(metadata_directory), + "config_settings": config_settings, + "_allow_fallback": _allow_fallback, + }, + ) def build_wheel( - self, wheel_directory, config_settings=None, - metadata_directory=None): + self, + wheel_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + metadata_directory: Optional[str] = None, + ) -> str: """Build a wheel from this project. + :param wheel_directory: The directory to write the wheel to + :param config_settings: The configuration settings for the build backend + :param metadata_directory: The directory to reuse existing metadata from :returns: The name of the newly created wheel within ``wheel_directory``. @@ -206,35 +253,48 @@ class BuildBackendHookCaller: """ if metadata_directory is not None: metadata_directory = abspath(metadata_directory) - return self._call_hook('build_wheel', { - 'wheel_directory': abspath(wheel_directory), - 'config_settings': config_settings, - 'metadata_directory': metadata_directory, - }) + return self._call_hook( + "build_wheel", + { + "wheel_directory": abspath(wheel_directory), + "config_settings": config_settings, + "metadata_directory": metadata_directory, + }, + ) - def get_requires_for_build_editable(self, config_settings=None): + def get_requires_for_build_editable( + self, + config_settings: Optional[Mapping[str, Any]] = None, + ) -> Sequence[str]: """Get additional dependencies required for building an editable wheel. + :param config_settings: The configuration settings for the build backend :returns: A list of :pep:`dependency specifiers <508>`. - :rtype: list[str] .. admonition:: Fallback If the build backend does not defined a hook with this name, an empty list will be returned. """ - return self._call_hook('get_requires_for_build_editable', { - 'config_settings': config_settings - }) + return self._call_hook( + "get_requires_for_build_editable", {"config_settings": config_settings} + ) def prepare_metadata_for_build_editable( - self, metadata_directory, config_settings=None, - _allow_fallback=True): + self, + metadata_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + _allow_fallback: bool = True, + ) -> Optional[str]: """Prepare a ``*.dist-info`` folder with metadata for this project. + :param metadata_directory: The directory to write the metadata to + :param config_settings: The configuration settings for the build backend + :param _allow_fallback: + Whether to allow the fallback to building a wheel and extracting + the metadata from it. Should be passed as a keyword argument only. :returns: Name of the newly created subfolder within ``metadata_directory``, containing the metadata. - :rtype: str .. admonition:: Fallback @@ -243,17 +303,26 @@ class BuildBackendHookCaller: wheel via the ``build_editable`` hook and the dist-info extracted from that will be returned. """ - return self._call_hook('prepare_metadata_for_build_editable', { - 'metadata_directory': abspath(metadata_directory), - 'config_settings': config_settings, - '_allow_fallback': _allow_fallback, - }) + return self._call_hook( + "prepare_metadata_for_build_editable", + { + "metadata_directory": abspath(metadata_directory), + "config_settings": config_settings, + "_allow_fallback": _allow_fallback, + }, + ) def build_editable( - self, wheel_directory, config_settings=None, - metadata_directory=None): + self, + wheel_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + metadata_directory: Optional[str] = None, + ) -> str: """Build an editable wheel from this project. + :param wheel_directory: The directory to write the wheel to + :param config_settings: The configuration settings for the build backend + :param metadata_directory: The directory to reuse existing metadata from :returns: The name of the newly created wheel within ``wheel_directory``. @@ -267,43 +336,55 @@ class BuildBackendHookCaller: """ if metadata_directory is not None: metadata_directory = abspath(metadata_directory) - return self._call_hook('build_editable', { - 'wheel_directory': abspath(wheel_directory), - 'config_settings': config_settings, - 'metadata_directory': metadata_directory, - }) + return self._call_hook( + "build_editable", + { + "wheel_directory": abspath(wheel_directory), + "config_settings": config_settings, + "metadata_directory": metadata_directory, + }, + ) - def get_requires_for_build_sdist(self, config_settings=None): + def get_requires_for_build_sdist( + self, + config_settings: Optional[Mapping[str, Any]] = None, + ) -> Sequence[str]: """Get additional dependencies required for building an sdist. :returns: A list of :pep:`dependency specifiers <508>`. - :rtype: list[str] """ - return self._call_hook('get_requires_for_build_sdist', { - 'config_settings': config_settings - }) + return self._call_hook( + "get_requires_for_build_sdist", {"config_settings": config_settings} + ) - def build_sdist(self, sdist_directory, config_settings=None): + def build_sdist( + self, + sdist_directory: str, + config_settings: Optional[Mapping[str, Any]] = None, + ) -> str: """Build an sdist from this project. :returns: The name of the newly created sdist within ``wheel_directory``. """ - return self._call_hook('build_sdist', { - 'sdist_directory': abspath(sdist_directory), - 'config_settings': config_settings, - }) + return self._call_hook( + "build_sdist", + { + "sdist_directory": abspath(sdist_directory), + "config_settings": config_settings, + }, + ) - def _call_hook(self, hook_name, kwargs): - extra_environ = {'PEP517_BUILD_BACKEND': self.build_backend} + def _call_hook(self, hook_name: str, kwargs: Mapping[str, Any]) -> Any: + extra_environ = {"_PYPROJECT_HOOKS_BUILD_BACKEND": self.build_backend} if self.backend_path: backend_path = os.pathsep.join(self.backend_path) - extra_environ['PEP517_BACKEND_PATH'] = backend_path + extra_environ["_PYPROJECT_HOOKS_BACKEND_PATH"] = backend_path with tempfile.TemporaryDirectory() as td: - hook_input = {'kwargs': kwargs} - write_json(hook_input, pjoin(td, 'input.json'), indent=2) + hook_input = {"kwargs": kwargs} + write_json(hook_input, pjoin(td, "input.json"), indent=2) # Run the hook in a subprocess with _in_proc_script_path() as script: @@ -311,20 +392,19 @@ class BuildBackendHookCaller: self._subprocess_runner( [python, abspath(str(script)), hook_name, td], cwd=self.source_dir, - extra_environ=extra_environ + extra_environ=extra_environ, ) - data = read_json(pjoin(td, 'output.json')) - if data.get('unsupported'): - raise UnsupportedOperation(data.get('traceback', '')) - if data.get('no_backend'): - raise BackendUnavailable(data.get('traceback', '')) - if data.get('backend_invalid'): - raise BackendInvalid( + data = read_json(pjoin(td, "output.json")) + if data.get("unsupported"): + raise UnsupportedOperation(data.get("traceback", "")) + if data.get("no_backend"): + raise BackendUnavailable( + data.get("traceback", ""), + message=data.get("backend_error", ""), backend_name=self.build_backend, backend_path=self.backend_path, - message=data.get('backend_error', '') ) - if data.get('hook_missing'): - raise HookMissing(data.get('missing_hook_name') or hook_name) - return data['return_val'] + if data.get("hook_missing"): + raise HookMissing(data.get("missing_hook_name") or hook_name) + return data["return_val"] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py index 917fa065..906d0ba2 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py @@ -11,8 +11,11 @@ try: except AttributeError: # Python 3.8 compatibility def _in_proc_script_path(): - return resources.path(__package__, '_in_process.py') + return resources.path(__package__, "_in_process.py") + else: + def _in_proc_script_path(): return resources.as_file( - resources.files(__package__).joinpath('_in_process.py')) + resources.files(__package__).joinpath("_in_process.py") + ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py index ee511ff2..d689bab7 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py @@ -3,8 +3,8 @@ It expects: - Command line args: hook_name, control_dir - Environment variables: - PEP517_BUILD_BACKEND=entry.point:spec - PEP517_BACKEND_PATH=paths (separated with os.pathsep) + _PYPROJECT_HOOKS_BUILD_BACKEND=entry.point:spec + _PYPROJECT_HOOKS_BACKEND_PATH=paths (separated with os.pathsep) - control_dir/input.json: - {"kwargs": {...}} @@ -21,6 +21,7 @@ import sys import traceback from glob import glob from importlib import import_module +from importlib.machinery import PathFinder from os.path import join as pjoin # This file is run as a script, and `import wrappers` is not zip-safe, so we @@ -28,69 +29,93 @@ from os.path import join as pjoin def write_json(obj, path, **kwargs): - with open(path, 'w', encoding='utf-8') as f: + with open(path, "w", encoding="utf-8") as f: json.dump(obj, f, **kwargs) def read_json(path): - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: return json.load(f) class BackendUnavailable(Exception): """Raised if we cannot import the backend""" - def __init__(self, traceback): - self.traceback = traceback - -class BackendInvalid(Exception): - """Raised if the backend is invalid""" - def __init__(self, message): + def __init__(self, message, traceback=None): + super().__init__(message) self.message = message + self.traceback = traceback class HookMissing(Exception): """Raised if a hook is missing and we are not executing the fallback""" + def __init__(self, hook_name=None): super().__init__(hook_name) self.hook_name = hook_name -def contained_in(filename, directory): - """Test if a file is located within the given directory.""" - filename = os.path.normcase(os.path.abspath(filename)) - directory = os.path.normcase(os.path.abspath(directory)) - return os.path.commonprefix([filename, directory]) == directory - - def _build_backend(): """Find and load the build backend""" - # Add in-tree backend directories to the front of sys.path. - backend_path = os.environ.get('PEP517_BACKEND_PATH') - if backend_path: - extra_pathitems = backend_path.split(os.pathsep) - sys.path[:0] = extra_pathitems + backend_path = os.environ.get("_PYPROJECT_HOOKS_BACKEND_PATH") + ep = os.environ["_PYPROJECT_HOOKS_BUILD_BACKEND"] + mod_path, _, obj_path = ep.partition(":") + + if backend_path: + # Ensure in-tree backend directories have the highest priority when importing. + extra_pathitems = backend_path.split(os.pathsep) + sys.meta_path.insert(0, _BackendPathFinder(extra_pathitems, mod_path)) - ep = os.environ['PEP517_BUILD_BACKEND'] - mod_path, _, obj_path = ep.partition(':') try: obj = import_module(mod_path) except ImportError: - raise BackendUnavailable(traceback.format_exc()) - - if backend_path: - if not any( - contained_in(obj.__file__, path) - for path in extra_pathitems - ): - raise BackendInvalid("Backend was not loaded from backend-path") + msg = f"Cannot import {mod_path!r}" + raise BackendUnavailable(msg, traceback.format_exc()) if obj_path: - for path_part in obj_path.split('.'): + for path_part in obj_path.split("."): obj = getattr(obj, path_part) return obj +class _BackendPathFinder: + """Implements the MetaPathFinder interface to locate modules in ``backend-path``. + + Since the environment provided by the frontend can contain all sorts of + MetaPathFinders, the only way to ensure the backend is loaded from the + right place is to prepend our own. + """ + + def __init__(self, backend_path, backend_module): + self.backend_path = backend_path + self.backend_module = backend_module + self.backend_parent, _, _ = backend_module.partition(".") + + def find_spec(self, fullname, _path, _target=None): + if "." in fullname: + # Rely on importlib to find nested modules based on parent's path + return None + + # Ignore other items in _path or sys.path and use backend_path instead: + spec = PathFinder.find_spec(fullname, path=self.backend_path) + if spec is None and fullname == self.backend_parent: + # According to the spec, the backend MUST be loaded from backend-path. + # Therefore, we can halt the import machinery and raise a clean error. + msg = f"Cannot find module {self.backend_module!r} in {self.backend_path!r}" + raise BackendUnavailable(msg) + + return spec + + if sys.version_info >= (3, 8): + + def find_distributions(self, context=None): + # Delayed import: Python 3.7 does not contain importlib.metadata + from importlib.metadata import DistributionFinder, MetadataPathFinder + + context = DistributionFinder.Context(path=self.backend_path) + return MetadataPathFinder.find_distributions(context=context) + + def _supported_features(): """Return the list of options features supported by the backend. @@ -133,7 +158,8 @@ def get_requires_for_build_editable(config_settings): def prepare_metadata_for_build_wheel( - metadata_directory, config_settings, _allow_fallback): + metadata_directory, config_settings, _allow_fallback +): """Invoke optional prepare_metadata_for_build_wheel Implements a fallback by building a wheel if the hook isn't defined, @@ -150,12 +176,14 @@ def prepare_metadata_for_build_wheel( # fallback to build_wheel outside the try block to avoid exception chaining # which can be confusing to users and is not relevant whl_basename = backend.build_wheel(metadata_directory, config_settings) - return _get_wheel_metadata_from_wheel(whl_basename, metadata_directory, - config_settings) + return _get_wheel_metadata_from_wheel( + whl_basename, metadata_directory, config_settings + ) def prepare_metadata_for_build_editable( - metadata_directory, config_settings, _allow_fallback): + metadata_directory, config_settings, _allow_fallback +): """Invoke optional prepare_metadata_for_build_editable Implements a fallback by building an editable wheel if the hook isn't @@ -171,24 +199,24 @@ def prepare_metadata_for_build_editable( try: build_hook = backend.build_editable except AttributeError: - raise HookMissing(hook_name='build_editable') + raise HookMissing(hook_name="build_editable") else: whl_basename = build_hook(metadata_directory, config_settings) - return _get_wheel_metadata_from_wheel(whl_basename, - metadata_directory, - config_settings) + return _get_wheel_metadata_from_wheel( + whl_basename, metadata_directory, config_settings + ) else: return hook(metadata_directory, config_settings) -WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL' +WHEEL_BUILT_MARKER = "PYPROJECT_HOOKS_ALREADY_BUILT_WHEEL" def _dist_info_files(whl_zip): """Identify the .dist-info folder inside a wheel ZipFile.""" res = [] for path in whl_zip.namelist(): - m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path) + m = re.match(r"[^/\\]+-[^/\\]+\.dist-info/", path) if m: res.append(path) if res: @@ -196,40 +224,41 @@ def _dist_info_files(whl_zip): raise Exception("No .dist-info folder found in wheel") -def _get_wheel_metadata_from_wheel( - whl_basename, metadata_directory, config_settings): +def _get_wheel_metadata_from_wheel(whl_basename, metadata_directory, config_settings): """Extract the metadata from a wheel. Fallback for when the build backend does not define the 'get_wheel_metadata' hook. """ from zipfile import ZipFile - with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'): + + with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), "wb"): pass # Touch marker file whl_file = os.path.join(metadata_directory, whl_basename) with ZipFile(whl_file) as zipf: dist_info = _dist_info_files(zipf) zipf.extractall(path=metadata_directory, members=dist_info) - return dist_info[0].split('/')[0] + return dist_info[0].split("/")[0] def _find_already_built_wheel(metadata_directory): - """Check for a wheel already built during the get_wheel_metadata hook. - """ + """Check for a wheel already built during the get_wheel_metadata hook.""" if not metadata_directory: return None metadata_parent = os.path.dirname(metadata_directory) if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)): return None - whl_files = glob(os.path.join(metadata_parent, '*.whl')) + whl_files = glob(os.path.join(metadata_parent, "*.whl")) if not whl_files: - print('Found wheel built marker, but no .whl files') + print("Found wheel built marker, but no .whl files") return None if len(whl_files) > 1: - print('Found multiple .whl files; unspecified behaviour. ' - 'Will call build_wheel.') + print( + "Found multiple .whl files; unspecified behaviour. " + "Will call build_wheel." + ) return None # Exactly one .whl file @@ -248,8 +277,9 @@ def build_wheel(wheel_directory, config_settings, metadata_directory=None): shutil.copy2(prebuilt_whl, wheel_directory) return os.path.basename(prebuilt_whl) - return _build_backend().build_wheel(wheel_directory, config_settings, - metadata_directory) + return _build_backend().build_wheel( + wheel_directory, config_settings, metadata_directory + ) def build_editable(wheel_directory, config_settings, metadata_directory=None): @@ -293,6 +323,7 @@ class _DummyException(Exception): class GotUnsupportedOperation(Exception): """For internal use when backend raises UnsupportedOperation""" + def __init__(self, traceback): self.traceback = traceback @@ -302,20 +333,20 @@ def build_sdist(sdist_directory, config_settings): backend = _build_backend() try: return backend.build_sdist(sdist_directory, config_settings) - except getattr(backend, 'UnsupportedOperation', _DummyException): + except getattr(backend, "UnsupportedOperation", _DummyException): raise GotUnsupportedOperation(traceback.format_exc()) HOOK_NAMES = { - 'get_requires_for_build_wheel', - 'prepare_metadata_for_build_wheel', - 'build_wheel', - 'get_requires_for_build_editable', - 'prepare_metadata_for_build_editable', - 'build_editable', - 'get_requires_for_build_sdist', - 'build_sdist', - '_supported_features', + "get_requires_for_build_wheel", + "prepare_metadata_for_build_wheel", + "build_wheel", + "get_requires_for_build_editable", + "prepare_metadata_for_build_editable", + "build_editable", + "get_requires_for_build_sdist", + "build_sdist", + "_supported_features", } @@ -326,28 +357,33 @@ def main(): control_dir = sys.argv[2] if hook_name not in HOOK_NAMES: sys.exit("Unknown hook: %s" % hook_name) + + # Remove the parent directory from sys.path to avoid polluting the backend + # import namespace with this directory. + here = os.path.dirname(__file__) + if here in sys.path: + sys.path.remove(here) + hook = globals()[hook_name] - hook_input = read_json(pjoin(control_dir, 'input.json')) + hook_input = read_json(pjoin(control_dir, "input.json")) - json_out = {'unsupported': False, 'return_val': None} + json_out = {"unsupported": False, "return_val": None} try: - json_out['return_val'] = hook(**hook_input['kwargs']) + json_out["return_val"] = hook(**hook_input["kwargs"]) except BackendUnavailable as e: - json_out['no_backend'] = True - json_out['traceback'] = e.traceback - except BackendInvalid as e: - json_out['backend_invalid'] = True - json_out['backend_error'] = e.message + json_out["no_backend"] = True + json_out["traceback"] = e.traceback + json_out["backend_error"] = e.message except GotUnsupportedOperation as e: - json_out['unsupported'] = True - json_out['traceback'] = e.traceback + json_out["unsupported"] = True + json_out["traceback"] = e.traceback except HookMissing as e: - json_out['hook_missing'] = True - json_out['missing_hook_name'] = e.hook_name or hook_name + json_out["hook_missing"] = True + json_out["missing_hook_name"] = e.hook_name or hook_name - write_json(json_out, pjoin(control_dir, 'output.json'), indent=2) + write_json(json_out, pjoin(control_dir, "output.json"), indent=2) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE new file mode 100644 index 00000000..67db8588 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py index 10ff67ff..04230fc8 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py @@ -45,11 +45,7 @@ from pip._vendor import urllib3 from .exceptions import RequestsDependencyWarning charset_normalizer_version = None - -try: - from pip._vendor.chardet import __version__ as chardet_version -except ImportError: - chardet_version = None +chardet_version = None def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version): @@ -80,7 +76,8 @@ def check_compatibility(urllib3_version, chardet_version, charset_normalizer_ver # charset_normalizer >= 2.0.0 < 4.0.0 assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0) else: - raise Exception("You need either charset_normalizer or chardet installed") + # pip does not need or use character detection + pass def _check_cryptography(cryptography_version): diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py index 5063c3f8..effdd98c 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py @@ -5,10 +5,10 @@ __title__ = "requests" __description__ = "Python HTTP for Humans." __url__ = "https://requests.readthedocs.io" -__version__ = "2.31.0" -__build__ = 0x023100 +__version__ = "2.32.5" +__build__ = 0x023205 __author__ = "Kenneth Reitz" __author_email__ = "me@kennethreitz.org" -__license__ = "Apache 2.0" +__license__ = "Apache-2.0" __copyright__ = "Copyright Kenneth Reitz" __cake__ = "\u2728 \U0001f370 \u2728" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py index 10c17679..67ccebcb 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py @@ -8,6 +8,8 @@ and maintain connections. import os.path import socket # noqa: F401 +import typing +import warnings from pip._vendor.urllib3.exceptions import ClosedPoolError, ConnectTimeoutError from pip._vendor.urllib3.exceptions import HTTPError as _HTTPError @@ -61,12 +63,53 @@ except ImportError: raise InvalidSchema("Missing dependencies for SOCKS support.") +if typing.TYPE_CHECKING: + from .models import PreparedRequest + + DEFAULT_POOLBLOCK = False DEFAULT_POOLSIZE = 10 DEFAULT_RETRIES = 0 DEFAULT_POOL_TIMEOUT = None +def _urllib3_request_context( + request: "PreparedRequest", + verify: "bool | str | None", + client_cert: "typing.Tuple[str, str] | str | None", + poolmanager: "PoolManager", +) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])": + host_params = {} + pool_kwargs = {} + parsed_request_url = urlparse(request.url) + scheme = parsed_request_url.scheme.lower() + port = parsed_request_url.port + + cert_reqs = "CERT_REQUIRED" + if verify is False: + cert_reqs = "CERT_NONE" + elif isinstance(verify, str): + if not os.path.isdir(verify): + pool_kwargs["ca_certs"] = verify + else: + pool_kwargs["ca_cert_dir"] = verify + pool_kwargs["cert_reqs"] = cert_reqs + if client_cert is not None: + if isinstance(client_cert, tuple) and len(client_cert) == 2: + pool_kwargs["cert_file"] = client_cert[0] + pool_kwargs["key_file"] = client_cert[1] + else: + # According to our docs, we allow users to specify just the client + # cert path + pool_kwargs["cert_file"] = client_cert + host_params = { + "scheme": scheme, + "host": parsed_request_url.hostname, + "port": port, + } + return host_params, pool_kwargs + + class BaseAdapter: """The Base Transport Adapter""" @@ -247,7 +290,6 @@ class HTTPAdapter(BaseAdapter): :param cert: The SSL certificate to verify. """ if url.lower().startswith("https") and verify: - cert_loc = None # Allow self-specified cert location. @@ -328,8 +370,110 @@ class HTTPAdapter(BaseAdapter): return response + def build_connection_pool_key_attributes(self, request, verify, cert=None): + """Build the PoolKey attributes used by urllib3 to return a connection. + + This looks at the PreparedRequest, the user-specified verify value, + and the value of the cert parameter to determine what PoolKey values + to use to select a connection from a given urllib3 Connection Pool. + + The SSL related pool key arguments are not consistently set. As of + this writing, use the following to determine what keys may be in that + dictionary: + + * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the + default Requests SSL Context + * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but + ``"cert_reqs"`` will be set + * If ``verify`` is a string, (i.e., it is a user-specified trust bundle) + ``"ca_certs"`` will be set if the string is not a directory recognized + by :py:func:`os.path.isdir`, otherwise ``"ca_cert_dir"`` will be + set. + * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If + ``"cert"`` is a tuple with a second item, ``"key_file"`` will also + be present + + To override these settings, one may subclass this class, call this + method and use the above logic to change parameters as desired. For + example, if one wishes to use a custom :py:class:`ssl.SSLContext` one + must both set ``"ssl_context"`` and based on what else they require, + alter the other keys to ensure the desired behaviour. + + :param request: + The PreparedReqest being sent over the connection. + :type request: + :class:`~requests.models.PreparedRequest` + :param verify: + Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use. + :param cert: + (optional) Any user-provided SSL certificate for client + authentication (a.k.a., mTLS). This may be a string (i.e., just + the path to a file which holds both certificate and key) or a + tuple of length 2 with the certificate file path and key file + path. + :returns: + A tuple of two dictionaries. The first is the "host parameters" + portion of the Pool Key including scheme, hostname, and port. The + second is a dictionary of SSLContext related parameters. + """ + return _urllib3_request_context(request, verify, cert, self.poolmanager) + + def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None): + """Returns a urllib3 connection for the given request and TLS settings. + This should not be called from user code, and is only exposed for use + when subclassing the :class:`HTTPAdapter `. + + :param request: + The :class:`PreparedRequest ` object to be sent + over the connection. + :param verify: + Either a boolean, in which case it controls whether we verify the + server's TLS certificate, or a string, in which case it must be a + path to a CA bundle to use. + :param proxies: + (optional) The proxies dictionary to apply to the request. + :param cert: + (optional) Any user-provided SSL certificate to be used for client + authentication (a.k.a., mTLS). + :rtype: + urllib3.ConnectionPool + """ + proxy = select_proxy(request.url, proxies) + try: + host_params, pool_kwargs = self.build_connection_pool_key_attributes( + request, + verify, + cert, + ) + except ValueError as e: + raise InvalidURL(e, request=request) + if proxy: + proxy = prepend_scheme_if_needed(proxy, "http") + proxy_url = parse_url(proxy) + if not proxy_url.host: + raise InvalidProxyURL( + "Please check proxy URL. It is malformed " + "and could be missing the host." + ) + proxy_manager = self.proxy_manager_for(proxy) + conn = proxy_manager.connection_from_host( + **host_params, pool_kwargs=pool_kwargs + ) + else: + # Only scheme should be lower case + conn = self.poolmanager.connection_from_host( + **host_params, pool_kwargs=pool_kwargs + ) + + return conn + def get_connection(self, url, proxies=None): - """Returns a urllib3 connection for the given URL. This should not be + """DEPRECATED: Users should move to `get_connection_with_tls_context` + for all subclasses of HTTPAdapter using Requests>=2.32.2. + + Returns a urllib3 connection for the given URL. This should not be called from user code, and is only exposed for use when subclassing the :class:`HTTPAdapter `. @@ -337,6 +481,15 @@ class HTTPAdapter(BaseAdapter): :param proxies: (optional) A Requests-style dictionary of proxies used on this request. :rtype: urllib3.ConnectionPool """ + warnings.warn( + ( + "`get_connection` has been deprecated in favor of " + "`get_connection_with_tls_context`. Custom HTTPAdapter subclasses " + "will need to migrate for Requests>=2.32.2. Please see " + "https://github.com/psf/requests/pull/6710 for more details." + ), + DeprecationWarning, + ) proxy = select_proxy(url, proxies) if proxy: @@ -391,6 +544,9 @@ class HTTPAdapter(BaseAdapter): using_socks_proxy = proxy_scheme.startswith("socks") url = request.path_url + if url.startswith("//"): # Don't confuse urllib3 + url = f"/{url.lstrip('/')}" + if is_proxied_http_request and not using_socks_proxy: url = urldefragauth(request.url) @@ -451,7 +607,9 @@ class HTTPAdapter(BaseAdapter): """ try: - conn = self.get_connection(request.url, proxies) + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) except LocationValueError as e: raise InvalidURL(e, request=request) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py index cd0b3eea..59607445 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py @@ -25,7 +25,7 @@ def request(method, url, **kwargs): :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` - or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string + or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers to add for the file. :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py index 9733686d..4a7ce6dc 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py @@ -258,7 +258,6 @@ class HTTPDigestAuth(AuthBase): s_auth = r.headers.get("www-authenticate", "") if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2: - self._thread_local.num_401_calls += 1 pat = re.compile(r"digest ", flags=re.IGNORECASE) self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1)) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py index 38696a1f..2743144b 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py @@ -11,14 +11,7 @@ If you are packaging Requests, e.g., for a Linux distribution or a managed environment, you can change the definition of where() to return a separately packaged CA bundle. """ - -import os - -if "_PIP_STANDALONE_CERT" not in os.environ: - from pip._vendor.certifi import where -else: - def where(): - return os.environ["_PIP_STANDALONE_CERT"] +from pip._vendor.certifi import where if __name__ == "__main__": print(where()) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py index 9ab2bb48..b95a9214 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py @@ -7,10 +7,33 @@ between Python 2 and Python 3. It remains for backwards compatibility until the next major version. """ -from pip._vendor import chardet - import sys +# ------- +# urllib3 +# ------- +from pip._vendor.urllib3 import __version__ as urllib3_version + +# Detect which major version of urllib3 is being used. +try: + is_urllib3_1 = int(urllib3_version.split(".")[0]) == 1 +except (TypeError, AttributeError): + # If we can't discern a version, prefer old functionality. + is_urllib3_1 = True + +# ------------------- +# Character Detection +# ------------------- + + +def _resolve_char_detection(): + """Find supported character detection libraries.""" + chardet = None + return chardet + + +chardet = _resolve_char_detection() + # ------- # Pythons # ------- diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py index bf54ab23..f69d0cda 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py @@ -2,7 +2,7 @@ requests.cookies ~~~~~~~~~~~~~~~~ -Compatibility code to be able to use `cookielib.CookieJar` with requests. +Compatibility code to be able to use `http.cookiejar.CookieJar` with requests. requests.utils imports from here, so be careful with imports. """ @@ -23,7 +23,7 @@ except ImportError: class MockRequest: """Wraps a `requests.Request` to mimic a `urllib2.Request`. - The code in `cookielib.CookieJar` expects this interface in order to correctly + The code in `http.cookiejar.CookieJar` expects this interface in order to correctly manage cookie policies, i.e., determine whether a cookie can be set, given the domains of the request and the cookie. @@ -76,7 +76,7 @@ class MockRequest: return self._r.headers.get(name, self._new_headers.get(name, default)) def add_header(self, key, val): - """cookielib has no legitimate use for this method; add it back if you find one.""" + """cookiejar has no legitimate use for this method; add it back if you find one.""" raise NotImplementedError( "Cookie headers should be added with add_unredirected_header()" ) @@ -104,11 +104,11 @@ class MockResponse: """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. ...what? Basically, expose the parsed HTTP headers from the server response - the way `cookielib` expects to see them. + the way `http.cookiejar` expects to see them. """ def __init__(self, headers): - """Make a MockResponse for `cookielib` to read. + """Make a MockResponse for `cookiejar` to read. :param headers: a httplib.HTTPMessage or analogous carrying the headers """ @@ -124,7 +124,7 @@ class MockResponse: def extract_cookies_to_jar(jar, request, response): """Extract the cookies from the response into a CookieJar. - :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar) + :param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar) :param request: our own requests.Request object :param response: urllib3.HTTPResponse object """ @@ -174,7 +174,7 @@ class CookieConflictError(RuntimeError): class RequestsCookieJar(cookielib.CookieJar, MutableMapping): - """Compatibility class; is a cookielib.CookieJar, but exposes a dict + """Compatibility class; is a http.cookiejar.CookieJar, but exposes a dict interface. This is the CookieJar we create by default for requests and sessions that @@ -341,7 +341,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping): self.set(name, value) def __delitem__(self, name): - """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s + """Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s ``remove_cookie_by_name()``. """ remove_cookie_by_name(self, name) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py index 168d0739..7f3660f0 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py @@ -41,6 +41,16 @@ class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError): CompatJSONDecodeError.__init__(self, *args) InvalidJSONError.__init__(self, *self.args, **kwargs) + def __reduce__(self): + """ + The __reduce__ method called when pickling the object must + be the one from the JSONDecodeError (be it json/simplejson) + as it expects all the arguments for instantiation, not just + one like the IOError, and the MRO would by default call the + __reduce__ method from the IOError due to the inheritance order. + """ + return CompatJSONDecodeError.__reduce__(self) + class HTTPError(RequestException): """An HTTP error occurred.""" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py index 2d292c2f..ddbb6150 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py @@ -11,11 +11,7 @@ from pip._vendor import urllib3 from . import __version__ as requests_version charset_normalizer = None - -try: - from pip._vendor import chardet -except ImportError: - chardet = None +chardet = None try: from pip._vendor.urllib3.contrib import pyopenssl diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py index 76e6f199..22de95c0 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py @@ -170,7 +170,7 @@ class RequestEncodingMixin: ) ) - for (k, v) in files: + for k, v in files: # support for explicit filename ft = None fh = None @@ -268,7 +268,6 @@ class Request(RequestHooksMixin): hooks=None, json=None, ): - # Default empty dicts for dict params. data = [] if data is None else data files = [] if files is None else files @@ -277,7 +276,7 @@ class Request(RequestHooksMixin): hooks = {} if hooks is None else hooks self.hooks = default_hooks() - for (k, v) in list(hooks.items()): + for k, v in list(hooks.items()): self.register_hook(event=k, hook=v) self.method = method @@ -790,7 +789,12 @@ class Response: @property def apparent_encoding(self): """The apparent encoding, provided by the charset_normalizer or chardet libraries.""" - return chardet.detect(self.content)["encoding"] + if chardet is not None: + return chardet.detect(self.content)["encoding"] + else: + # If no character detection library is available, we'll fall back + # to a standard Python utf-8 str. + return "utf-8" def iter_content(self, chunk_size=1, decode_unicode=False): """Iterates over the response data. When stream=True is set on the @@ -865,7 +869,6 @@ class Response: for chunk in self.iter_content( chunk_size=chunk_size, decode_unicode=decode_unicode ): - if pending is not None: chunk = pending + chunk @@ -942,7 +945,9 @@ class Response: return content def json(self, **kwargs): - r"""Returns the json-encoded content of a response, if any. + r"""Decodes the JSON response body (if any) as a Python object. + + This may return a dictionary, list, etc. depending on what is in the response. :param \*\*kwargs: Optional arguments that ``json.loads`` takes. :raises requests.exceptions.JSONDecodeError: If the response body does not diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py index 9582fa73..200c3828 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py @@ -1,9 +1,11 @@ import sys +from .compat import chardet + # This code exists for backwards compatibility reasons. # I don't like it either. Just look the other way. :) -for package in ('urllib3', 'idna', 'chardet'): +for package in ("urllib3", "idna"): vendored_package = "pip._vendor." + package locals()[package] = __import__(vendored_package) # This traversal is apparently necessary such that the identities are @@ -13,4 +15,11 @@ for package in ('urllib3', 'idna', 'chardet'): unprefixed_mod = mod[len("pip._vendor."):] sys.modules['pip._vendor.requests.packages.' + unprefixed_mod] = sys.modules[mod] -# Kinda cool, though, right? +if chardet is not None: + target = chardet.__name__ + for mod in list(sys.modules): + if mod == target or mod.startswith(f"{target}."): + imported_mod = sys.modules[mod] + sys.modules[f"requests.packages.{mod}"] = imported_mod + mod = mod.replace(target, "chardet") + sys.modules[f"requests.packages.{mod}"] = imported_mod diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py index dbcf2a7b..731550de 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py @@ -262,7 +262,6 @@ class SessionRedirectMixin: if yield_requests: yield req else: - resp = self.send( req, stream=stream, @@ -326,7 +325,7 @@ class SessionRedirectMixin: # urllib3 handles proxy authorization for us in the standard adapter. # Avoid appending this to TLS tunneled requests where it may be leaked. - if not scheme.startswith('https') and username and password: + if not scheme.startswith("https") and username and password: headers["Proxy-Authorization"] = _basic_auth_str(username, password) return new_proxies @@ -389,7 +388,6 @@ class Session(SessionRedirectMixin): ] def __init__(self): - #: A case-insensitive dictionary of headers to be sent on each #: :class:`Request ` sent from this #: :class:`Session `. @@ -537,7 +535,7 @@ class Session(SessionRedirectMixin): for multipart encoding upload. :param auth: (optional) Auth tuple or callable to enable Basic/Digest/Custom HTTP Auth. - :param timeout: (optional) How long to wait for the server to send + :param timeout: (optional) How many seconds to wait for the server to send data before giving up, as a float, or a :ref:`(connect timeout, read timeout) ` tuple. :type timeout: float or tuple @@ -545,6 +543,8 @@ class Session(SessionRedirectMixin): :type allow_redirects: bool :param proxies: (optional) Dictionary mapping protocol or protocol and hostname to the URL of the proxy. + :param hooks: (optional) Dictionary mapping hook name to one event or + list of events, event must be callable. :param stream: (optional) whether to immediately download the response content. Defaults to ``False``. :param verify: (optional) Either a boolean, in which case it controls whether we verify @@ -711,7 +711,6 @@ class Session(SessionRedirectMixin): # Persist cookies if r.history: - # If the hooks create history then we want those cookies too for resp in r.history: extract_cookies_to_jar(self.cookies, resp.request, resp.raw) @@ -759,7 +758,7 @@ class Session(SessionRedirectMixin): # Set environment's proxies. no_proxy = proxies.get("no_proxy") if proxies is not None else None env_proxies = get_environ_proxies(url, no_proxy=no_proxy) - for (k, v) in env_proxies.items(): + for k, v in env_proxies.items(): proxies.setdefault(k, v) # Look for requests environment configuration @@ -785,8 +784,7 @@ class Session(SessionRedirectMixin): :rtype: requests.adapters.BaseAdapter """ - for (prefix, adapter) in self.adapters.items(): - + for prefix, adapter in self.adapters.items(): if url.lower().startswith(prefix.lower()): return adapter diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py index 4bd072be..c7945a2f 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py @@ -24,7 +24,7 @@ _codes = { # Informational. 100: ("continue",), 101: ("switching_protocols",), - 102: ("processing",), + 102: ("processing", "early-hints"), 103: ("checkpoint",), 122: ("uri_too_long", "request_uri_too_long"), 200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"), @@ -65,8 +65,8 @@ _codes = { 410: ("gone",), 411: ("length_required",), 412: ("precondition_failed", "precondition"), - 413: ("request_entity_too_large",), - 414: ("request_uri_too_large",), + 413: ("request_entity_too_large", "content_too_large"), + 414: ("request_uri_too_large", "uri_too_long"), 415: ("unsupported_media_type", "unsupported_media", "media_type"), 416: ( "requested_range_not_satisfiable", @@ -76,10 +76,10 @@ _codes = { 417: ("expectation_failed",), 418: ("im_a_teapot", "teapot", "i_am_a_teapot"), 421: ("misdirected_request",), - 422: ("unprocessable_entity", "unprocessable"), + 422: ("unprocessable_entity", "unprocessable", "unprocessable_content"), 423: ("locked",), 424: ("failed_dependency", "dependency"), - 425: ("unordered_collection", "unordered"), + 425: ("unordered_collection", "unordered", "too_early"), 426: ("upgrade_required", "upgrade"), 428: ("precondition_required", "precondition"), 429: ("too_many_requests", "too_many"), diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py b/venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py index 6adec332..e8ea5ad3 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py @@ -38,6 +38,7 @@ from .compat import ( getproxies, getproxies_environment, integer_types, + is_urllib3_1, ) from .compat import parse_http_list as _parse_list_header from .compat import ( @@ -97,6 +98,8 @@ if sys.platform == "win32": # '' string by the localhost entry and the corresponding # canonical entry. proxyOverride = proxyOverride.split(";") + # filter out empty strings to avoid re.match return true in the following code. + proxyOverride = filter(None, proxyOverride) # now check if we match one of the registry values. for test in proxyOverride: if test == "": @@ -134,6 +137,11 @@ def super_len(o): total_length = None current_position = 0 + if not is_urllib3_1 and isinstance(o, str): + # urllib3 2.x+ treats all strings as utf-8 instead + # of latin-1 (iso-8859-1) like http.client. + o = o.encode("utf-8") + if hasattr(o, "__len__"): total_length = len(o) @@ -211,14 +219,7 @@ def get_netrc_auth(url, raise_errors=False): netrc_path = None for f in netrc_locations: - try: - loc = os.path.expanduser(f) - except KeyError: - # os.path.expanduser can fail when $HOME is undefined and - # getpwuid fails. See https://bugs.python.org/issue20164 & - # https://github.com/psf/requests/issues/1846 - return - + loc = os.path.expanduser(f) if os.path.exists(loc): netrc_path = loc break @@ -460,11 +461,7 @@ def dict_from_cookiejar(cj): :rtype: dict """ - cookie_dict = {} - - for cookie in cj: - cookie_dict[cookie.name] = cookie.value - + cookie_dict = {cookie.name: cookie.value for cookie in cj} return cookie_dict @@ -761,6 +758,7 @@ def should_bypass_proxies(url, no_proxy): :rtype: bool """ + # Prioritize lowercase environment variables over uppercase # to keep a consistent behaviour with other http projects (curl, wget). def get_proxy(key): @@ -856,7 +854,7 @@ def select_proxy(url, proxies): def resolve_proxies(request, proxies, trust_env=True): """This method takes proxy information from a request and configuration input to resolve a mapping of target proxies. This will consider settings - such a NO_PROXY to strip proxy configurations. + such as NO_PROXY to strip proxy configurations. :param request: Request or PreparedRequest :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs @@ -1048,7 +1046,7 @@ def _validate_header_part(header, header_part, header_validator_index): if not validator.match(header_part): header_kind = "name" if header_validator_index == 0 else "value" raise InvalidHeader( - f"Invalid leading whitespace, reserved character(s), or return" + f"Invalid leading whitespace, reserved character(s), or return " f"character(s) in header {header_kind}: {header_part!r}" ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE new file mode 100644 index 00000000..b9077766 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2018, Tzu-ping Chung + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py index d92acc7b..4c7f815a 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py @@ -1,22 +1,23 @@ __all__ = [ - "__version__", "AbstractProvider", "AbstractResolver", "BaseReporter", "InconsistentCandidate", - "Resolver", "RequirementsConflicted", "ResolutionError", "ResolutionImpossible", "ResolutionTooDeep", + "Resolver", + "__version__", ] -__version__ = "1.0.1" +__version__ = "1.2.1" -from .providers import AbstractProvider, AbstractResolver +from .providers import AbstractProvider from .reporters import BaseReporter from .resolvers import ( + AbstractResolver, InconsistentCandidate, RequirementsConflicted, ResolutionError, diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py deleted file mode 100644 index 1becc509..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py +++ /dev/null @@ -1,6 +0,0 @@ -__all__ = ["Mapping", "Sequence"] - -try: - from collections.abc import Mapping, Sequence -except ImportError: - from collections import Mapping, Sequence diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py index e99d87ee..524e3d83 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py @@ -1,30 +1,58 @@ -class AbstractProvider(object): +from __future__ import annotations + +from typing import ( + TYPE_CHECKING, + Generic, + Iterable, + Iterator, + Mapping, + Sequence, +) + +from .structs import CT, KT, RT, Matches, RequirementInformation + +if TYPE_CHECKING: + from typing import Any, Protocol + + class Preference(Protocol): + def __lt__(self, __other: Any) -> bool: ... + + +class AbstractProvider(Generic[RT, CT, KT]): """Delegate class to provide the required interface for the resolver.""" - def identify(self, requirement_or_candidate): - """Given a requirement, return an identifier for it. + def identify(self, requirement_or_candidate: RT | CT) -> KT: + """Given a requirement or candidate, return an identifier for it. - This is used to identify a requirement, e.g. whether two requirements - should have their specifier parts merged. + This is used to identify, e.g. whether two requirements + should have their specifier parts merged or a candidate matches a + requirement via ``find_matches()``. """ raise NotImplementedError def get_preference( self, - identifier, - resolutions, - candidates, - information, - backtrack_causes, - ): + identifier: KT, + resolutions: Mapping[KT, CT], + candidates: Mapping[KT, Iterator[CT]], + information: Mapping[KT, Iterator[RequirementInformation[RT, CT]]], + backtrack_causes: Sequence[RequirementInformation[RT, CT]], + ) -> Preference: """Produce a sort key for given requirement based on preference. + As this is a sort key it will be called O(n) times per backtrack + step, where n is the number of `identifier`s, if you have a check + which is expensive in some sense. E.g. It needs to make O(n) checks + per call or takes significant wall clock time, consider using + `narrow_requirement_selection` to filter the `identifier`s, which + is applied before this sort key is called. + The preference is defined as "I think this requirement should be resolved first". The lower the return value is, the more preferred this group of arguments is. :param identifier: An identifier as returned by ``identify()``. This - identifies the dependency matches which should be returned. + identifies the requirement being considered. :param resolutions: Mapping of candidates currently pinned by the resolver. Each key is an identifier, and the value is a candidate. The candidate may conflict with requirements from ``information``. @@ -32,8 +60,9 @@ class AbstractProvider(object): Each value is an iterator of candidates. :param information: Mapping of requirement information of each package. Each value is an iterator of *requirement information*. - :param backtrack_causes: Sequence of requirement information that were - the requirements that caused the resolver to most recently backtrack. + :param backtrack_causes: Sequence of *requirement information* that are + the requirements that caused the resolver to most recently + backtrack. A *requirement information* instance is a named tuple with two members: @@ -60,15 +89,21 @@ class AbstractProvider(object): """ raise NotImplementedError - def find_matches(self, identifier, requirements, incompatibilities): + def find_matches( + self, + identifier: KT, + requirements: Mapping[KT, Iterator[RT]], + incompatibilities: Mapping[KT, Iterator[CT]], + ) -> Matches[CT]: """Find all possible candidates that satisfy the given constraints. - :param identifier: An identifier as returned by ``identify()``. This - identifies the dependency matches of which should be returned. + :param identifier: An identifier as returned by ``identify()``. All + candidates returned by this method should produce the same + identifier. :param requirements: A mapping of requirements that all returned candidates must satisfy. Each key is an identifier, and the value an iterator of requirements for that dependency. - :param incompatibilities: A mapping of known incompatibilities of + :param incompatibilities: A mapping of known incompatibile candidates of each dependency. Each key is an identifier, and the value an iterator of incompatibilities known to the resolver. All incompatibilities *must* be excluded from the return value. @@ -89,7 +124,7 @@ class AbstractProvider(object): """ raise NotImplementedError - def is_satisfied_by(self, requirement, candidate): + def is_satisfied_by(self, requirement: RT, candidate: CT) -> bool: """Whether the given requirement can be satisfied by a candidate. The candidate is guaranteed to have been generated from the @@ -100,7 +135,7 @@ class AbstractProvider(object): """ raise NotImplementedError - def get_dependencies(self, candidate): + def get_dependencies(self, candidate: CT) -> Iterable[RT]: """Get dependencies of a candidate. This should return a collection of requirements that `candidate` @@ -108,26 +143,54 @@ class AbstractProvider(object): """ raise NotImplementedError - -class AbstractResolver(object): - """The thing that performs the actual resolution work.""" - - base_exception = Exception - - def __init__(self, provider, reporter): - self.provider = provider - self.reporter = reporter - - def resolve(self, requirements, **kwargs): - """Take a collection of constraints, spit out the resolution result. - - This returns a representation of the final resolution state, with one - guarenteed attribute ``mapping`` that contains resolved candidates as - values. The keys are their respective identifiers. - - :param requirements: A collection of constraints. - :param kwargs: Additional keyword arguments that subclasses may accept. - - :raises: ``self.base_exception`` or its subclass. + def narrow_requirement_selection( + self, + identifiers: Iterable[KT], + resolutions: Mapping[KT, CT], + candidates: Mapping[KT, Iterator[CT]], + information: Mapping[KT, Iterator[RequirementInformation[RT, CT]]], + backtrack_causes: Sequence[RequirementInformation[RT, CT]], + ) -> Iterable[KT]: """ - raise NotImplementedError + An optional method to narrow the selection of requirements being + considered during resolution. This method is called O(1) time per + backtrack step. + + :param identifiers: An iterable of `identifiers` as returned by + ``identify()``. These identify all requirements currently being + considered. + :param resolutions: A mapping of candidates currently pinned by the + resolver. Each key is an identifier, and the value is a candidate + that may conflict with requirements from ``information``. + :param candidates: A mapping of each dependency's possible candidates. + Each value is an iterator of candidates. + :param information: A mapping of requirement information for each package. + Each value is an iterator of *requirement information*. + :param backtrack_causes: A sequence of *requirement information* that are + the requirements causing the resolver to most recently + backtrack. + + A *requirement information* instance is a named tuple with two members: + + * ``requirement`` specifies a requirement contributing to the current + list of candidates. + * ``parent`` specifies the candidate that provides (is depended on for) + the requirement, or ``None`` to indicate a root requirement. + + Must return a non-empty subset of `identifiers`, with the default + implementation being to return `identifiers` unchanged. Those `identifiers` + will then be passed to the sort key `get_preference` to pick the most + prefered requirement to attempt to pin, unless `narrow_requirement_selection` + returns only 1 requirement, in which case that will be used without + calling the sort key `get_preference`. + + This method is designed to be used by the provider to optimize the + dependency resolution, e.g. if a check cost is O(m) and it can be done + against all identifiers at once then filtering the requirement selection + here will cost O(m) but making it part of the sort key in `get_preference` + will cost O(m*n), where n is the number of `identifiers`. + + Returns: + Iterable[KT]: A non-empty subset of `identifiers`. + """ + return identifiers diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py index 688b5e10..6c142204 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py @@ -1,26 +1,36 @@ -class BaseReporter(object): - """Delegate class to provider progress reporting for the resolver.""" +from __future__ import annotations - def starting(self): +from typing import TYPE_CHECKING, Collection, Generic + +from .structs import CT, KT, RT, RequirementInformation, State + +if TYPE_CHECKING: + from .resolvers import Criterion + + +class BaseReporter(Generic[RT, CT, KT]): + """Delegate class to provide progress reporting for the resolver.""" + + def starting(self) -> None: """Called before the resolution actually starts.""" - def starting_round(self, index): + def starting_round(self, index: int) -> None: """Called before each round of resolution starts. The index is zero-based. """ - def ending_round(self, index, state): + def ending_round(self, index: int, state: State[RT, CT, KT]) -> None: """Called before each round of resolution ends. This is NOT called if the resolution ends at this round. Use `ending` if you want to report finalization. The index is zero-based. """ - def ending(self, state): + def ending(self, state: State[RT, CT, KT]) -> None: """Called before the resolution ends successfully.""" - def adding_requirement(self, requirement, parent): + def adding_requirement(self, requirement: RT, parent: CT | None) -> None: """Called when adding a new requirement into the resolve criteria. :param requirement: The additional requirement to be applied to filter @@ -30,14 +40,16 @@ class BaseReporter(object): requirements passed in from ``Resolver.resolve()``. """ - def resolving_conflicts(self, causes): + def resolving_conflicts( + self, causes: Collection[RequirementInformation[RT, CT]] + ) -> None: """Called when starting to attempt requirement conflict resolution. :param causes: The information on the collision that caused the backtracking. """ - def rejecting_candidate(self, criterion, candidate): + def rejecting_candidate(self, criterion: Criterion[RT, CT], candidate: CT) -> None: """Called when rejecting a candidate during backtracking.""" - def pinning(self, candidate): + def pinning(self, candidate: CT) -> None: """Called when adding a candidate to the potential solution.""" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py new file mode 100644 index 00000000..b2492215 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py @@ -0,0 +1,27 @@ +from ..structs import RequirementInformation +from .abstract import AbstractResolver, Result +from .criterion import Criterion +from .exceptions import ( + InconsistentCandidate, + RequirementsConflicted, + ResolutionError, + ResolutionImpossible, + ResolutionTooDeep, + ResolverException, +) +from .resolution import Resolution, Resolver + +__all__ = [ + "AbstractResolver", + "Criterion", + "InconsistentCandidate", + "RequirementInformation", + "RequirementsConflicted", + "Resolution", + "ResolutionError", + "ResolutionImpossible", + "ResolutionTooDeep", + "Resolver", + "ResolverException", + "Result", +] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py new file mode 100644 index 00000000..db32b3b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +import collections +from typing import TYPE_CHECKING, Any, Generic, Iterable, NamedTuple + +from ..structs import CT, KT, RT, DirectedGraph + +if TYPE_CHECKING: + from ..providers import AbstractProvider + from ..reporters import BaseReporter + from .criterion import Criterion + + class Result(NamedTuple, Generic[RT, CT, KT]): + mapping: dict[KT, CT] + graph: DirectedGraph[KT | None] + criteria: dict[KT, Criterion[RT, CT]] + +else: + Result = collections.namedtuple("Result", ["mapping", "graph", "criteria"]) + + +class AbstractResolver(Generic[RT, CT, KT]): + """The thing that performs the actual resolution work.""" + + base_exception = Exception + + def __init__( + self, + provider: AbstractProvider[RT, CT, KT], + reporter: BaseReporter[RT, CT, KT], + ) -> None: + self.provider = provider + self.reporter = reporter + + def resolve(self, requirements: Iterable[RT], **kwargs: Any) -> Result[RT, CT, KT]: + """Take a collection of constraints, spit out the resolution result. + + This returns a representation of the final resolution state, with one + guarenteed attribute ``mapping`` that contains resolved candidates as + values. The keys are their respective identifiers. + + :param requirements: A collection of constraints. + :param kwargs: Additional keyword arguments that subclasses may accept. + + :raises: ``self.base_exception`` or its subclass. + """ + raise NotImplementedError diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py new file mode 100644 index 00000000..ee5019cc --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from typing import Collection, Generic, Iterable, Iterator + +from ..structs import CT, RT, RequirementInformation + + +class Criterion(Generic[RT, CT]): + """Representation of possible resolution results of a package. + + This holds three attributes: + + * `information` is a collection of `RequirementInformation` pairs. + Each pair is a requirement contributing to this criterion, and the + candidate that provides the requirement. + * `incompatibilities` is a collection of all known not-to-work candidates + to exclude from consideration. + * `candidates` is a collection containing all possible candidates deducted + from the union of contributing requirements and known incompatibilities. + It should never be empty, except when the criterion is an attribute of a + raised `RequirementsConflicted` (in which case it is always empty). + + .. note:: + This class is intended to be externally immutable. **Do not** mutate + any of its attribute containers. + """ + + def __init__( + self, + candidates: Iterable[CT], + information: Collection[RequirementInformation[RT, CT]], + incompatibilities: Collection[CT], + ) -> None: + self.candidates = candidates + self.information = information + self.incompatibilities = incompatibilities + + def __repr__(self) -> str: + requirements = ", ".join( + f"({req!r}, via={parent!r})" for req, parent in self.information + ) + return f"Criterion({requirements})" + + def iter_requirement(self) -> Iterator[RT]: + return (i.requirement for i in self.information) + + def iter_parent(self) -> Iterator[CT | None]: + return (i.parent for i in self.information) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py new file mode 100644 index 00000000..35e27557 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Collection, Generic + +from ..structs import CT, RT, RequirementInformation + +if TYPE_CHECKING: + from .criterion import Criterion + + +class ResolverException(Exception): + """A base class for all exceptions raised by this module. + + Exceptions derived by this class should all be handled in this module. Any + bubbling pass the resolver should be treated as a bug. + """ + + +class RequirementsConflicted(ResolverException, Generic[RT, CT]): + def __init__(self, criterion: Criterion[RT, CT]) -> None: + super().__init__(criterion) + self.criterion = criterion + + def __str__(self) -> str: + return "Requirements conflict: {}".format( + ", ".join(repr(r) for r in self.criterion.iter_requirement()), + ) + + +class InconsistentCandidate(ResolverException, Generic[RT, CT]): + def __init__(self, candidate: CT, criterion: Criterion[RT, CT]): + super().__init__(candidate, criterion) + self.candidate = candidate + self.criterion = criterion + + def __str__(self) -> str: + return "Provided candidate {!r} does not satisfy {}".format( + self.candidate, + ", ".join(repr(r) for r in self.criterion.iter_requirement()), + ) + + +class ResolutionError(ResolverException): + pass + + +class ResolutionImpossible(ResolutionError, Generic[RT, CT]): + def __init__(self, causes: Collection[RequirementInformation[RT, CT]]): + super().__init__(causes) + # causes is a list of RequirementInformation objects + self.causes = causes + + +class ResolutionTooDeep(ResolutionError): + def __init__(self, round_count: int) -> None: + super().__init__(round_count) + self.round_count = round_count diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py similarity index 56% rename from venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py rename to venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py index 2c3d0e30..8c13d3b2 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py @@ -1,127 +1,99 @@ +from __future__ import annotations + import collections import itertools import operator +from typing import TYPE_CHECKING, Generic -from .providers import AbstractResolver -from .structs import DirectedGraph, IteratorMapping, build_iter_view - -RequirementInformation = collections.namedtuple( - "RequirementInformation", ["requirement", "parent"] +from ..structs import ( + CT, + KT, + RT, + DirectedGraph, + IterableView, + IteratorMapping, + RequirementInformation, + State, + build_iter_view, +) +from .abstract import AbstractResolver, Result +from .criterion import Criterion +from .exceptions import ( + InconsistentCandidate, + RequirementsConflicted, + ResolutionImpossible, + ResolutionTooDeep, + ResolverException, ) +if TYPE_CHECKING: + from collections.abc import Collection, Iterable, Mapping -class ResolverException(Exception): - """A base class for all exceptions raised by this module. + from ..providers import AbstractProvider, Preference + from ..reporters import BaseReporter - Exceptions derived by this class should all be handled in this module. Any - bubbling pass the resolver should be treated as a bug. - """ +_OPTIMISTIC_BACKJUMPING_RATIO: float = 0.1 -class RequirementsConflicted(ResolverException): - def __init__(self, criterion): - super(RequirementsConflicted, self).__init__(criterion) - self.criterion = criterion +def _build_result(state: State[RT, CT, KT]) -> Result[RT, CT, KT]: + mapping = state.mapping + all_keys: dict[int, KT | None] = {id(v): k for k, v in mapping.items()} + all_keys[id(None)] = None - def __str__(self): - return "Requirements conflict: {}".format( - ", ".join(repr(r) for r in self.criterion.iter_requirement()), - ) + graph: DirectedGraph[KT | None] = DirectedGraph() + graph.add(None) # Sentinel as root dependencies' parent. + + connected: set[KT | None] = {None} + for key, criterion in state.criteria.items(): + if not _has_route_to_root(state.criteria, key, all_keys, connected): + continue + if key not in graph: + graph.add(key) + for p in criterion.iter_parent(): + try: + pkey = all_keys[id(p)] + except KeyError: + continue + if pkey not in graph: + graph.add(pkey) + graph.connect(pkey, key) + + return Result( + mapping={k: v for k, v in mapping.items() if k in connected}, + graph=graph, + criteria=state.criteria, + ) -class InconsistentCandidate(ResolverException): - def __init__(self, candidate, criterion): - super(InconsistentCandidate, self).__init__(candidate, criterion) - self.candidate = candidate - self.criterion = criterion - - def __str__(self): - return "Provided candidate {!r} does not satisfy {}".format( - self.candidate, - ", ".join(repr(r) for r in self.criterion.iter_requirement()), - ) - - -class Criterion(object): - """Representation of possible resolution results of a package. - - This holds three attributes: - - * `information` is a collection of `RequirementInformation` pairs. - Each pair is a requirement contributing to this criterion, and the - candidate that provides the requirement. - * `incompatibilities` is a collection of all known not-to-work candidates - to exclude from consideration. - * `candidates` is a collection containing all possible candidates deducted - from the union of contributing requirements and known incompatibilities. - It should never be empty, except when the criterion is an attribute of a - raised `RequirementsConflicted` (in which case it is always empty). - - .. note:: - This class is intended to be externally immutable. **Do not** mutate - any of its attribute containers. - """ - - def __init__(self, candidates, information, incompatibilities): - self.candidates = candidates - self.information = information - self.incompatibilities = incompatibilities - - def __repr__(self): - requirements = ", ".join( - "({!r}, via={!r})".format(req, parent) - for req, parent in self.information - ) - return "Criterion({})".format(requirements) - - def iter_requirement(self): - return (i.requirement for i in self.information) - - def iter_parent(self): - return (i.parent for i in self.information) - - -class ResolutionError(ResolverException): - pass - - -class ResolutionImpossible(ResolutionError): - def __init__(self, causes): - super(ResolutionImpossible, self).__init__(causes) - # causes is a list of RequirementInformation objects - self.causes = causes - - -class ResolutionTooDeep(ResolutionError): - def __init__(self, round_count): - super(ResolutionTooDeep, self).__init__(round_count) - self.round_count = round_count - - -# Resolution state in a round. -State = collections.namedtuple("State", "mapping criteria backtrack_causes") - - -class Resolution(object): +class Resolution(Generic[RT, CT, KT]): """Stateful resolution object. This is designed as a one-off object that holds information to kick start the resolution process, and holds the results afterwards. """ - def __init__(self, provider, reporter): + def __init__( + self, + provider: AbstractProvider[RT, CT, KT], + reporter: BaseReporter[RT, CT, KT], + ) -> None: self._p = provider self._r = reporter - self._states = [] + self._states: list[State[RT, CT, KT]] = [] + + # Optimistic backjumping variables + self._optimistic_backjumping_ratio = _OPTIMISTIC_BACKJUMPING_RATIO + self._save_states: list[State[RT, CT, KT]] | None = None + self._optimistic_start_round: int | None = None @property - def state(self): + def state(self) -> State[RT, CT, KT]: try: return self._states[-1] - except IndexError: - raise AttributeError("state") + except IndexError as e: + raise AttributeError("state") from e - def _push_new_state(self): + def _push_new_state(self) -> None: """Push a new state into history. This new state will be used to hold resolution results of the next @@ -135,7 +107,12 @@ class Resolution(object): ) self._states.append(state) - def _add_to_criteria(self, criteria, requirement, parent): + def _add_to_criteria( + self, + criteria: dict[KT, Criterion[RT, CT]], + requirement: RT, + parent: CT | None, + ) -> None: self._r.adding_requirement(requirement=requirement, parent=parent) identifier = self._p.identify(requirement_or_candidate=requirement) @@ -174,7 +151,9 @@ class Resolution(object): raise RequirementsConflicted(criterion) criteria[identifier] = criterion - def _remove_information_from_criteria(self, criteria, parents): + def _remove_information_from_criteria( + self, criteria: dict[KT, Criterion[RT, CT]], parents: Collection[KT] + ) -> None: """Remove information from parents of criteria. Concretely, removes all values from each criterion's ``information`` @@ -199,7 +178,7 @@ class Resolution(object): criterion.incompatibilities, ) - def _get_preference(self, name): + def _get_preference(self, name: KT) -> Preference: return self._p.get_preference( identifier=name, resolutions=self.state.mapping, @@ -214,7 +193,9 @@ class Resolution(object): backtrack_causes=self.state.backtrack_causes, ) - def _is_current_pin_satisfying(self, name, criterion): + def _is_current_pin_satisfying( + self, name: KT, criterion: Criterion[RT, CT] + ) -> bool: try: current_pin = self.state.mapping[name] except KeyError: @@ -224,16 +205,16 @@ class Resolution(object): for r in criterion.iter_requirement() ) - def _get_updated_criteria(self, candidate): + def _get_updated_criteria(self, candidate: CT) -> dict[KT, Criterion[RT, CT]]: criteria = self.state.criteria.copy() for requirement in self._p.get_dependencies(candidate=candidate): self._add_to_criteria(criteria, requirement, parent=candidate) return criteria - def _attempt_to_pin_criterion(self, name): + def _attempt_to_pin_criterion(self, name: KT) -> list[Criterion[RT, CT]]: criterion = self.state.criteria[name] - causes = [] + causes: list[Criterion[RT, CT]] = [] for candidate in criterion.candidates: try: criteria = self._get_updated_criteria(candidate) @@ -267,7 +248,61 @@ class Resolution(object): # end, signal for backtracking. return causes - def _backjump(self, causes): + def _patch_criteria( + self, incompatibilities_from_broken: list[tuple[KT, list[CT]]] + ) -> bool: + # Create a new state from the last known-to-work one, and apply + # the previously gathered incompatibility information. + for k, incompatibilities in incompatibilities_from_broken: + if not incompatibilities: + continue + try: + criterion = self.state.criteria[k] + except KeyError: + continue + matches = self._p.find_matches( + identifier=k, + requirements=IteratorMapping( + self.state.criteria, + operator.methodcaller("iter_requirement"), + ), + incompatibilities=IteratorMapping( + self.state.criteria, + operator.attrgetter("incompatibilities"), + {k: incompatibilities}, + ), + ) + candidates: IterableView[CT] = build_iter_view(matches) + if not candidates: + return False + incompatibilities.extend(criterion.incompatibilities) + self.state.criteria[k] = Criterion( + candidates=candidates, + information=list(criterion.information), + incompatibilities=incompatibilities, + ) + return True + + def _save_state(self) -> None: + """Save states for potential rollback if optimistic backjumping fails.""" + if self._save_states is None: + self._save_states = [ + State( + mapping=s.mapping.copy(), + criteria=s.criteria.copy(), + backtrack_causes=s.backtrack_causes[:], + ) + for s in self._states + ] + + def _rollback_states(self) -> None: + """Rollback states and disable optimistic backjumping.""" + self._optimistic_backjumping_ratio = 0.0 + if self._save_states: + self._states = self._save_states + self._save_states = None + + def _backjump(self, causes: list[RequirementInformation[RT, CT]]) -> bool: """Perform backjumping. When we enter here, the stack is like this:: @@ -298,7 +333,7 @@ class Resolution(object): the new Z and go back to step 2. 5b. If the incompatibilities apply cleanly, end backtracking. """ - incompatible_reqs = itertools.chain( + incompatible_reqs: Iterable[CT | RT] = itertools.chain( (c.parent for c in causes if c.parent is not None), (c.requirement for c in causes), ) @@ -307,66 +342,62 @@ class Resolution(object): # Remove the state that triggered backtracking. del self._states[-1] - # Ensure to backtrack to a state that caused the incompatibility - incompatible_state = False - while not incompatible_state: + # Optimistically backtrack to a state that caused the incompatibility + broken_state = self.state + while True: # Retrieve the last candidate pin and known incompatibilities. try: broken_state = self._states.pop() name, candidate = broken_state.mapping.popitem() except (IndexError, KeyError): - raise ResolutionImpossible(causes) + raise ResolutionImpossible(causes) from None + + if ( + not self._optimistic_backjumping_ratio + and name not in incompatible_deps + ): + # For safe backjumping only backjump if the current dependency + # is not the same as the incompatible dependency + break + + # On the first time a non-safe backjump is done the state + # is saved so we can restore it later if the resolution fails + if ( + self._optimistic_backjumping_ratio + and self._save_states is None + and name not in incompatible_deps + ): + self._save_state() + + # If the current dependencies and the incompatible dependencies + # are overlapping then we have likely found a cause of the + # incompatibility current_dependencies = { - self._p.identify(d) - for d in self._p.get_dependencies(candidate) + self._p.identify(d) for d in self._p.get_dependencies(candidate) } - incompatible_state = not current_dependencies.isdisjoint( - incompatible_deps - ) + if not current_dependencies.isdisjoint(incompatible_deps): + break + + # Fallback: We should not backtrack to the point where + # broken_state.mapping is empty, so stop backtracking for + # a chance for the resolution to recover + if not broken_state.mapping: + break + + # Guard: We need at least two state to remain to both + # backtrack and push a new state + if len(self._states) <= 1: + raise ResolutionImpossible(causes) incompatibilities_from_broken = [ - (k, list(v.incompatibilities)) - for k, v in broken_state.criteria.items() + (k, list(v.incompatibilities)) for k, v in broken_state.criteria.items() ] # Also mark the newly known incompatibility. incompatibilities_from_broken.append((name, [candidate])) - # Create a new state from the last known-to-work one, and apply - # the previously gathered incompatibility information. - def _patch_criteria(): - for k, incompatibilities in incompatibilities_from_broken: - if not incompatibilities: - continue - try: - criterion = self.state.criteria[k] - except KeyError: - continue - matches = self._p.find_matches( - identifier=k, - requirements=IteratorMapping( - self.state.criteria, - operator.methodcaller("iter_requirement"), - ), - incompatibilities=IteratorMapping( - self.state.criteria, - operator.attrgetter("incompatibilities"), - {k: incompatibilities}, - ), - ) - candidates = build_iter_view(matches) - if not candidates: - return False - incompatibilities.extend(criterion.incompatibilities) - self.state.criteria[k] = Criterion( - candidates=candidates, - information=list(criterion.information), - incompatibilities=incompatibilities, - ) - return True - self._push_new_state() - success = _patch_criteria() + success = self._patch_criteria(incompatibilities_from_broken) # It works! Let's work on this new state. if success: @@ -378,7 +409,13 @@ class Resolution(object): # No way to backtrack anymore. return False - def resolve(self, requirements, max_rounds): + def _extract_causes( + self, criteron: list[Criterion[RT, CT]] + ) -> list[RequirementInformation[RT, CT]]: + """Extract causes from list of criterion and deduplicate""" + return list({id(i): i for c in criteron for i in c.information}.values()) + + def resolve(self, requirements: Iterable[RT], max_rounds: int) -> State[RT, CT, KT]: if self._states: raise RuntimeError("already resolved") @@ -396,16 +433,39 @@ class Resolution(object): try: self._add_to_criteria(self.state.criteria, r, parent=None) except RequirementsConflicted as e: - raise ResolutionImpossible(e.criterion.information) + raise ResolutionImpossible(e.criterion.information) from e # The root state is saved as a sentinel so the first ever pin can have # something to backtrack to if it fails. The root state is basically # pinning the virtual "root" package in the graph. self._push_new_state() + # Variables for optimistic backjumping + optimistic_rounds_cutoff: int | None = None + optimistic_backjumping_start_round: int | None = None + for round_index in range(max_rounds): self._r.starting_round(index=round_index) + # Handle if optimistic backjumping has been running for too long + if self._optimistic_backjumping_ratio and self._save_states is not None: + if optimistic_backjumping_start_round is None: + optimistic_backjumping_start_round = round_index + optimistic_rounds_cutoff = int( + (max_rounds - round_index) * self._optimistic_backjumping_ratio + ) + + if optimistic_rounds_cutoff <= 0: + self._rollback_states() + continue + elif optimistic_rounds_cutoff is not None: + if ( + round_index - optimistic_backjumping_start_round + >= optimistic_rounds_cutoff + ): + self._rollback_states() + continue + unsatisfied_names = [ key for key, criterion in self.state.criteria.items() @@ -418,25 +478,68 @@ class Resolution(object): return self.state # keep track of satisfied names to calculate diff after pinning - satisfied_names = set(self.state.criteria.keys()) - set( - unsatisfied_names - ) + satisfied_names = set(self.state.criteria.keys()) - set(unsatisfied_names) - # Choose the most preferred unpinned criterion to try. - name = min(unsatisfied_names, key=self._get_preference) - failure_causes = self._attempt_to_pin_criterion(name) + if len(unsatisfied_names) > 1: + narrowed_unstatisfied_names = list( + self._p.narrow_requirement_selection( + identifiers=unsatisfied_names, + resolutions=self.state.mapping, + candidates=IteratorMapping( + self.state.criteria, + operator.attrgetter("candidates"), + ), + information=IteratorMapping( + self.state.criteria, + operator.attrgetter("information"), + ), + backtrack_causes=self.state.backtrack_causes, + ) + ) + else: + narrowed_unstatisfied_names = unsatisfied_names - if failure_causes: - causes = [i for c in failure_causes for i in c.information] + # If there are no unsatisfied names use unsatisfied names + if not narrowed_unstatisfied_names: + raise RuntimeError("narrow_requirement_selection returned 0 names") + + # If there is only 1 unsatisfied name skip calling self._get_preference + if len(narrowed_unstatisfied_names) > 1: + # Choose the most preferred unpinned criterion to try. + name = min(narrowed_unstatisfied_names, key=self._get_preference) + else: + name = narrowed_unstatisfied_names[0] + + failure_criterion = self._attempt_to_pin_criterion(name) + + if failure_criterion: + causes = self._extract_causes(failure_criterion) # Backjump if pinning fails. The backjump process puts us in # an unpinned state, so we can work on it in the next round. self._r.resolving_conflicts(causes=causes) - success = self._backjump(causes) - self.state.backtrack_causes[:] = causes - # Dead ends everywhere. Give up. - if not success: - raise ResolutionImpossible(self.state.backtrack_causes) + try: + success = self._backjump(causes) + except ResolutionImpossible: + if self._optimistic_backjumping_ratio and self._save_states: + failed_optimistic_backjumping = True + else: + raise + else: + failed_optimistic_backjumping = bool( + not success + and self._optimistic_backjumping_ratio + and self._save_states + ) + + if failed_optimistic_backjumping and self._save_states: + self._rollback_states() + else: + self.state.backtrack_causes[:] = causes + + # Dead ends everywhere. Give up. + if not success: + raise ResolutionImpossible(self.state.backtrack_causes) else: # discard as information sources any invalidated names # (unsatisfied names that were previously satisfied) @@ -457,64 +560,16 @@ class Resolution(object): raise ResolutionTooDeep(max_rounds) -def _has_route_to_root(criteria, key, all_keys, connected): - if key in connected: - return True - if key not in criteria: - return False - for p in criteria[key].iter_parent(): - try: - pkey = all_keys[id(p)] - except KeyError: - continue - if pkey in connected: - connected.add(key) - return True - if _has_route_to_root(criteria, pkey, all_keys, connected): - connected.add(key) - return True - return False - - -Result = collections.namedtuple("Result", "mapping graph criteria") - - -def _build_result(state): - mapping = state.mapping - all_keys = {id(v): k for k, v in mapping.items()} - all_keys[id(None)] = None - - graph = DirectedGraph() - graph.add(None) # Sentinel as root dependencies' parent. - - connected = {None} - for key, criterion in state.criteria.items(): - if not _has_route_to_root(state.criteria, key, all_keys, connected): - continue - if key not in graph: - graph.add(key) - for p in criterion.iter_parent(): - try: - pkey = all_keys[id(p)] - except KeyError: - continue - if pkey not in graph: - graph.add(pkey) - graph.connect(pkey, key) - - return Result( - mapping={k: v for k, v in mapping.items() if k in connected}, - graph=graph, - criteria=state.criteria, - ) - - -class Resolver(AbstractResolver): +class Resolver(AbstractResolver[RT, CT, KT]): """The thing that performs the actual resolution work.""" base_exception = ResolverException - def resolve(self, requirements, max_rounds=100): + def resolve( # type: ignore[override] + self, + requirements: Iterable[RT], + max_rounds: int = 100, + ) -> Result[RT, CT, KT]: """Take a collection of constraints, spit out the resolution result. The return value is a representation to the final resolution result. It @@ -545,3 +600,28 @@ class Resolver(AbstractResolver): resolution = Resolution(self.provider, self.reporter) state = resolution.resolve(requirements, max_rounds=max_rounds) return _build_result(state) + + +def _has_route_to_root( + criteria: Mapping[KT, Criterion[RT, CT]], + key: KT | None, + all_keys: dict[int, KT | None], + connected: set[KT | None], +) -> bool: + if key in connected: + return True + if key not in criteria: + return False + assert key is not None + for p in criteria[key].iter_parent(): + try: + pkey = all_keys[id(p)] + except KeyError: + continue + if pkey in connected: + connected.add(key) + return True + if _has_route_to_root(criteria, pkey, all_keys, connected): + connected.add(key) + return True + return False diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py index 359a34f6..18c74d41 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py @@ -1,34 +1,73 @@ +from __future__ import annotations + import itertools +from collections import namedtuple +from typing import ( + TYPE_CHECKING, + Callable, + Generic, + Iterable, + Iterator, + Mapping, + NamedTuple, + Sequence, + TypeVar, + Union, +) -from .compat import collections_abc +KT = TypeVar("KT") # Identifier. +RT = TypeVar("RT") # Requirement. +CT = TypeVar("CT") # Candidate. + +Matches = Union[Iterable[CT], Callable[[], Iterable[CT]]] + +if TYPE_CHECKING: + from .resolvers.criterion import Criterion + + class RequirementInformation(NamedTuple, Generic[RT, CT]): + requirement: RT + parent: CT | None + + class State(NamedTuple, Generic[RT, CT, KT]): + """Resolution state in a round.""" + + mapping: dict[KT, CT] + criteria: dict[KT, Criterion[RT, CT]] + backtrack_causes: list[RequirementInformation[RT, CT]] + +else: + RequirementInformation = namedtuple( + "RequirementInformation", ["requirement", "parent"] + ) + State = namedtuple("State", ["mapping", "criteria", "backtrack_causes"]) -class DirectedGraph(object): +class DirectedGraph(Generic[KT]): """A graph structure with directed edges.""" - def __init__(self): - self._vertices = set() - self._forwards = {} # -> Set[] - self._backwards = {} # -> Set[] + def __init__(self) -> None: + self._vertices: set[KT] = set() + self._forwards: dict[KT, set[KT]] = {} # -> Set[] + self._backwards: dict[KT, set[KT]] = {} # -> Set[] - def __iter__(self): + def __iter__(self) -> Iterator[KT]: return iter(self._vertices) - def __len__(self): + def __len__(self) -> int: return len(self._vertices) - def __contains__(self, key): + def __contains__(self, key: KT) -> bool: return key in self._vertices - def copy(self): + def copy(self) -> DirectedGraph[KT]: """Return a shallow copy of this graph.""" - other = DirectedGraph() + other = type(self)() other._vertices = set(self._vertices) other._forwards = {k: set(v) for k, v in self._forwards.items()} other._backwards = {k: set(v) for k, v in self._backwards.items()} return other - def add(self, key): + def add(self, key: KT) -> None: """Add a new vertex to the graph.""" if key in self._vertices: raise ValueError("vertex exists") @@ -36,7 +75,7 @@ class DirectedGraph(object): self._forwards[key] = set() self._backwards[key] = set() - def remove(self, key): + def remove(self, key: KT) -> None: """Remove a vertex from the graph, disconnecting all edges from/to it.""" self._vertices.remove(key) for f in self._forwards.pop(key): @@ -44,10 +83,10 @@ class DirectedGraph(object): for t in self._backwards.pop(key): self._forwards[t].remove(key) - def connected(self, f, t): + def connected(self, f: KT, t: KT) -> bool: return f in self._backwards[t] and t in self._forwards[f] - def connect(self, f, t): + def connect(self, f: KT, t: KT) -> None: """Connect two existing vertices. Nothing happens if the vertices are already connected. @@ -57,56 +96,59 @@ class DirectedGraph(object): self._forwards[f].add(t) self._backwards[t].add(f) - def iter_edges(self): + def iter_edges(self) -> Iterator[tuple[KT, KT]]: for f, children in self._forwards.items(): for t in children: yield f, t - def iter_children(self, key): + def iter_children(self, key: KT) -> Iterator[KT]: return iter(self._forwards[key]) - def iter_parents(self, key): + def iter_parents(self, key: KT) -> Iterator[KT]: return iter(self._backwards[key]) -class IteratorMapping(collections_abc.Mapping): - def __init__(self, mapping, accessor, appends=None): +class IteratorMapping(Mapping[KT, Iterator[CT]], Generic[RT, CT, KT]): + def __init__( + self, + mapping: Mapping[KT, RT], + accessor: Callable[[RT], Iterable[CT]], + appends: Mapping[KT, Iterable[CT]] | None = None, + ) -> None: self._mapping = mapping self._accessor = accessor - self._appends = appends or {} + self._appends: Mapping[KT, Iterable[CT]] = appends or {} - def __repr__(self): + def __repr__(self) -> str: return "IteratorMapping({!r}, {!r}, {!r})".format( self._mapping, self._accessor, self._appends, ) - def __bool__(self): + def __bool__(self) -> bool: return bool(self._mapping or self._appends) - __nonzero__ = __bool__ # XXX: Python 2. - - def __contains__(self, key): + def __contains__(self, key: object) -> bool: return key in self._mapping or key in self._appends - def __getitem__(self, k): + def __getitem__(self, k: KT) -> Iterator[CT]: try: v = self._mapping[k] except KeyError: return iter(self._appends[k]) return itertools.chain(self._accessor(v), self._appends.get(k, ())) - def __iter__(self): + def __iter__(self) -> Iterator[KT]: more = (k for k in self._appends if k not in self._mapping) return itertools.chain(self._mapping, more) - def __len__(self): + def __len__(self) -> int: more = sum(1 for k in self._appends if k not in self._mapping) return len(self._mapping) + more -class _FactoryIterableView(object): +class _FactoryIterableView(Iterable[RT]): """Wrap an iterator factory returned by `find_matches()`. Calling `iter()` on this class would invoke the underlying iterator @@ -115,56 +157,53 @@ class _FactoryIterableView(object): built-in Python sequence types. """ - def __init__(self, factory): + def __init__(self, factory: Callable[[], Iterable[RT]]) -> None: self._factory = factory - self._iterable = None + self._iterable: Iterable[RT] | None = None - def __repr__(self): - return "{}({})".format(type(self).__name__, list(self)) + def __repr__(self) -> str: + return f"{type(self).__name__}({list(self)})" - def __bool__(self): + def __bool__(self) -> bool: try: next(iter(self)) except StopIteration: return False return True - __nonzero__ = __bool__ # XXX: Python 2. - - def __iter__(self): - iterable = ( - self._factory() if self._iterable is None else self._iterable - ) + def __iter__(self) -> Iterator[RT]: + iterable = self._factory() if self._iterable is None else self._iterable self._iterable, current = itertools.tee(iterable) return current -class _SequenceIterableView(object): +class _SequenceIterableView(Iterable[RT]): """Wrap an iterable returned by find_matches(). This is essentially just a proxy to the underlying sequence that provides the same interface as `_FactoryIterableView`. """ - def __init__(self, sequence): + def __init__(self, sequence: Sequence[RT]): self._sequence = sequence - def __repr__(self): - return "{}({})".format(type(self).__name__, self._sequence) + def __repr__(self) -> str: + return f"{type(self).__name__}({self._sequence})" - def __bool__(self): + def __bool__(self) -> bool: return bool(self._sequence) - __nonzero__ = __bool__ # XXX: Python 2. - - def __iter__(self): + def __iter__(self) -> Iterator[RT]: return iter(self._sequence) -def build_iter_view(matches): +def build_iter_view(matches: Matches[CT]) -> Iterable[CT]: """Build an iterable view from the value returned by `find_matches()`.""" if callable(matches): return _FactoryIterableView(matches) - if not isinstance(matches, collections_abc.Sequence): + if not isinstance(matches, Sequence): matches = list(matches) return _SequenceIterableView(matches) + + +IterableView = Iterable diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE new file mode 100644 index 00000000..44155055 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Will McGugan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py index 270629fd..a583f1ef 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py @@ -207,6 +207,7 @@ Supports much of the *markdown* __syntax__! if __name__ == "__main__": # pragma: no cover + from pip._vendor.rich.panel import Panel console = Console( file=io.StringIO(), @@ -228,47 +229,17 @@ if __name__ == "__main__": # pragma: no cover c = Console(record=True) c.print(test_card) - print(f"rendered in {pre_cache_taken}ms (cold cache)") - print(f"rendered in {taken}ms (warm cache)") - - from pip._vendor.rich.panel import Panel - console = Console() - - sponsor_message = Table.grid(padding=1) - sponsor_message.add_column(style="green", justify="right") - sponsor_message.add_column(no_wrap=True) - - sponsor_message.add_row( - "Textualize", - "[u blue link=https://github.com/textualize]https://github.com/textualize", - ) - sponsor_message.add_row( - "Twitter", - "[u blue link=https://twitter.com/willmcgugan]https://twitter.com/willmcgugan", - ) - - intro_message = Text.from_markup( - """\ -We hope you enjoy using Rich! - -Rich is maintained with [red]:heart:[/] by [link=https://www.textualize.io]Textualize.io[/] - -- Will McGugan""" - ) - - message = Table.grid(padding=2) - message.add_column() - message.add_column(no_wrap=True) - message.add_row(intro_message, sponsor_message) - + console.print(f"[dim]rendered in [not dim]{pre_cache_taken}ms[/] (cold cache)") + console.print(f"[dim]rendered in [not dim]{taken}ms[/] (warm cache)") + console.print() console.print( Panel.fit( - message, - box=box.ROUNDED, - padding=(1, 2), - title="[b red]Thanks for trying out Rich!", - border_style="bright_blue", - ), - justify="center", + "[b magenta]Hope you enjoy using Rich![/]\n\n" + "Please consider sponsoring me if you get value from my work.\n\n" + "Even the price of a ☕ can brighten my day!\n\n" + "https://github.com/sponsors/willmcgugan", + border_style="red", + title="Help ensure Rich is maintained", + ) ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py index 36286df3..608ae3a7 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py @@ -4,6 +4,7 @@ CELL_WIDTHS = [ (0, 0, 0), (1, 31, -1), (127, 159, -1), + (173, 173, 0), (768, 879, 0), (1155, 1161, 0), (1425, 1469, 0), @@ -11,13 +12,16 @@ CELL_WIDTHS = [ (1473, 1474, 0), (1476, 1477, 0), (1479, 1479, 0), + (1536, 1541, 0), (1552, 1562, 0), + (1564, 1564, 0), (1611, 1631, 0), (1648, 1648, 0), - (1750, 1756, 0), + (1750, 1757, 0), (1759, 1764, 0), (1767, 1768, 0), (1770, 1773, 0), + (1807, 1807, 0), (1809, 1809, 0), (1840, 1866, 0), (1958, 1968, 0), @@ -28,149 +32,137 @@ CELL_WIDTHS = [ (2085, 2087, 0), (2089, 2093, 0), (2137, 2139, 0), - (2259, 2273, 0), - (2275, 2306, 0), - (2362, 2362, 0), - (2364, 2364, 0), - (2369, 2376, 0), - (2381, 2381, 0), + (2192, 2193, 0), + (2200, 2207, 0), + (2250, 2307, 0), + (2362, 2364, 0), + (2366, 2383, 0), (2385, 2391, 0), (2402, 2403, 0), - (2433, 2433, 0), + (2433, 2435, 0), (2492, 2492, 0), - (2497, 2500, 0), - (2509, 2509, 0), + (2494, 2500, 0), + (2503, 2504, 0), + (2507, 2509, 0), + (2519, 2519, 0), (2530, 2531, 0), (2558, 2558, 0), - (2561, 2562, 0), + (2561, 2563, 0), (2620, 2620, 0), - (2625, 2626, 0), + (2622, 2626, 0), (2631, 2632, 0), (2635, 2637, 0), (2641, 2641, 0), (2672, 2673, 0), (2677, 2677, 0), - (2689, 2690, 0), + (2689, 2691, 0), (2748, 2748, 0), - (2753, 2757, 0), - (2759, 2760, 0), - (2765, 2765, 0), + (2750, 2757, 0), + (2759, 2761, 0), + (2763, 2765, 0), (2786, 2787, 0), (2810, 2815, 0), - (2817, 2817, 0), + (2817, 2819, 0), (2876, 2876, 0), - (2879, 2879, 0), - (2881, 2884, 0), - (2893, 2893, 0), - (2901, 2902, 0), + (2878, 2884, 0), + (2887, 2888, 0), + (2891, 2893, 0), + (2901, 2903, 0), (2914, 2915, 0), (2946, 2946, 0), - (3008, 3008, 0), - (3021, 3021, 0), - (3072, 3072, 0), - (3076, 3076, 0), - (3134, 3136, 0), + (3006, 3010, 0), + (3014, 3016, 0), + (3018, 3021, 0), + (3031, 3031, 0), + (3072, 3076, 0), + (3132, 3132, 0), + (3134, 3140, 0), (3142, 3144, 0), (3146, 3149, 0), (3157, 3158, 0), (3170, 3171, 0), - (3201, 3201, 0), + (3201, 3203, 0), (3260, 3260, 0), - (3263, 3263, 0), - (3270, 3270, 0), - (3276, 3277, 0), + (3262, 3268, 0), + (3270, 3272, 0), + (3274, 3277, 0), + (3285, 3286, 0), (3298, 3299, 0), - (3328, 3329, 0), + (3315, 3315, 0), + (3328, 3331, 0), (3387, 3388, 0), - (3393, 3396, 0), - (3405, 3405, 0), + (3390, 3396, 0), + (3398, 3400, 0), + (3402, 3405, 0), + (3415, 3415, 0), (3426, 3427, 0), - (3457, 3457, 0), + (3457, 3459, 0), (3530, 3530, 0), - (3538, 3540, 0), + (3535, 3540, 0), (3542, 3542, 0), + (3544, 3551, 0), + (3570, 3571, 0), (3633, 3633, 0), (3636, 3642, 0), (3655, 3662, 0), (3761, 3761, 0), (3764, 3772, 0), - (3784, 3789, 0), + (3784, 3790, 0), (3864, 3865, 0), (3893, 3893, 0), (3895, 3895, 0), (3897, 3897, 0), - (3953, 3966, 0), - (3968, 3972, 0), + (3902, 3903, 0), + (3953, 3972, 0), (3974, 3975, 0), (3981, 3991, 0), (3993, 4028, 0), (4038, 4038, 0), - (4141, 4144, 0), - (4146, 4151, 0), - (4153, 4154, 0), - (4157, 4158, 0), - (4184, 4185, 0), + (4139, 4158, 0), + (4182, 4185, 0), (4190, 4192, 0), + (4194, 4196, 0), + (4199, 4205, 0), (4209, 4212, 0), - (4226, 4226, 0), - (4229, 4230, 0), - (4237, 4237, 0), - (4253, 4253, 0), + (4226, 4237, 0), + (4239, 4239, 0), + (4250, 4253, 0), (4352, 4447, 2), + (4448, 4607, 0), (4957, 4959, 0), - (5906, 5908, 0), + (5906, 5909, 0), (5938, 5940, 0), (5970, 5971, 0), (6002, 6003, 0), - (6068, 6069, 0), - (6071, 6077, 0), - (6086, 6086, 0), - (6089, 6099, 0), + (6068, 6099, 0), (6109, 6109, 0), - (6155, 6157, 0), + (6155, 6159, 0), (6277, 6278, 0), (6313, 6313, 0), - (6432, 6434, 0), - (6439, 6440, 0), - (6450, 6450, 0), - (6457, 6459, 0), - (6679, 6680, 0), - (6683, 6683, 0), - (6742, 6742, 0), - (6744, 6750, 0), - (6752, 6752, 0), - (6754, 6754, 0), - (6757, 6764, 0), - (6771, 6780, 0), + (6432, 6443, 0), + (6448, 6459, 0), + (6679, 6683, 0), + (6741, 6750, 0), + (6752, 6780, 0), (6783, 6783, 0), - (6832, 6848, 0), - (6912, 6915, 0), - (6964, 6964, 0), - (6966, 6970, 0), - (6972, 6972, 0), - (6978, 6978, 0), + (6832, 6862, 0), + (6912, 6916, 0), + (6964, 6980, 0), (7019, 7027, 0), - (7040, 7041, 0), - (7074, 7077, 0), - (7080, 7081, 0), - (7083, 7085, 0), - (7142, 7142, 0), - (7144, 7145, 0), - (7149, 7149, 0), - (7151, 7153, 0), - (7212, 7219, 0), - (7222, 7223, 0), + (7040, 7042, 0), + (7073, 7085, 0), + (7142, 7155, 0), + (7204, 7223, 0), (7376, 7378, 0), - (7380, 7392, 0), - (7394, 7400, 0), + (7380, 7400, 0), (7405, 7405, 0), (7412, 7412, 0), - (7416, 7417, 0), - (7616, 7673, 0), - (7675, 7679, 0), + (7415, 7417, 0), + (7616, 7679, 0), (8203, 8207, 0), (8232, 8238, 0), - (8288, 8291, 0), + (8288, 8292, 0), + (8294, 8303, 0), (8400, 8432, 0), (8986, 8987, 2), (9001, 9002, 2), @@ -212,17 +204,16 @@ CELL_WIDTHS = [ (11904, 11929, 2), (11931, 12019, 2), (12032, 12245, 2), - (12272, 12283, 2), - (12288, 12329, 2), - (12330, 12333, 0), - (12334, 12350, 2), + (12272, 12329, 2), + (12330, 12335, 0), + (12336, 12350, 2), (12353, 12438, 2), (12441, 12442, 0), (12443, 12543, 2), (12549, 12591, 2), (12593, 12686, 2), (12688, 12771, 2), - (12784, 12830, 2), + (12783, 12830, 2), (12832, 12871, 2), (12880, 19903, 2), (19968, 42124, 2), @@ -234,36 +225,33 @@ CELL_WIDTHS = [ (43010, 43010, 0), (43014, 43014, 0), (43019, 43019, 0), - (43045, 43046, 0), + (43043, 43047, 0), (43052, 43052, 0), - (43204, 43205, 0), + (43136, 43137, 0), + (43188, 43205, 0), (43232, 43249, 0), (43263, 43263, 0), (43302, 43309, 0), - (43335, 43345, 0), + (43335, 43347, 0), (43360, 43388, 2), - (43392, 43394, 0), - (43443, 43443, 0), - (43446, 43449, 0), - (43452, 43453, 0), + (43392, 43395, 0), + (43443, 43456, 0), (43493, 43493, 0), - (43561, 43566, 0), - (43569, 43570, 0), - (43573, 43574, 0), + (43561, 43574, 0), (43587, 43587, 0), - (43596, 43596, 0), - (43644, 43644, 0), + (43596, 43597, 0), + (43643, 43645, 0), (43696, 43696, 0), (43698, 43700, 0), (43703, 43704, 0), (43710, 43711, 0), (43713, 43713, 0), - (43756, 43757, 0), - (43766, 43766, 0), - (44005, 44005, 0), - (44008, 44008, 0), - (44013, 44013, 0), + (43755, 43759, 0), + (43765, 43766, 0), + (44003, 44010, 0), + (44012, 44013, 0), (44032, 55203, 2), + (55216, 55295, 0), (63744, 64255, 2), (64286, 64286, 0), (65024, 65039, 0), @@ -272,8 +260,10 @@ CELL_WIDTHS = [ (65072, 65106, 2), (65108, 65126, 2), (65128, 65131, 2), + (65279, 65279, 0), (65281, 65376, 2), (65504, 65510, 2), + (65529, 65531, 0), (66045, 66045, 0), (66272, 66272, 0), (66422, 66426, 0), @@ -285,102 +275,108 @@ CELL_WIDTHS = [ (68325, 68326, 0), (68900, 68903, 0), (69291, 69292, 0), + (69373, 69375, 0), (69446, 69456, 0), - (69633, 69633, 0), + (69506, 69509, 0), + (69632, 69634, 0), (69688, 69702, 0), - (69759, 69761, 0), - (69811, 69814, 0), - (69817, 69818, 0), + (69744, 69744, 0), + (69747, 69748, 0), + (69759, 69762, 0), + (69808, 69818, 0), + (69821, 69821, 0), + (69826, 69826, 0), + (69837, 69837, 0), (69888, 69890, 0), - (69927, 69931, 0), - (69933, 69940, 0), + (69927, 69940, 0), + (69957, 69958, 0), (70003, 70003, 0), - (70016, 70017, 0), - (70070, 70078, 0), + (70016, 70018, 0), + (70067, 70080, 0), (70089, 70092, 0), - (70095, 70095, 0), - (70191, 70193, 0), - (70196, 70196, 0), - (70198, 70199, 0), + (70094, 70095, 0), + (70188, 70199, 0), (70206, 70206, 0), - (70367, 70367, 0), - (70371, 70378, 0), - (70400, 70401, 0), + (70209, 70209, 0), + (70367, 70378, 0), + (70400, 70403, 0), (70459, 70460, 0), - (70464, 70464, 0), + (70462, 70468, 0), + (70471, 70472, 0), + (70475, 70477, 0), + (70487, 70487, 0), + (70498, 70499, 0), (70502, 70508, 0), (70512, 70516, 0), - (70712, 70719, 0), - (70722, 70724, 0), - (70726, 70726, 0), + (70709, 70726, 0), (70750, 70750, 0), - (70835, 70840, 0), - (70842, 70842, 0), - (70847, 70848, 0), - (70850, 70851, 0), - (71090, 71093, 0), - (71100, 71101, 0), - (71103, 71104, 0), + (70832, 70851, 0), + (71087, 71093, 0), + (71096, 71104, 0), (71132, 71133, 0), - (71219, 71226, 0), - (71229, 71229, 0), - (71231, 71232, 0), - (71339, 71339, 0), - (71341, 71341, 0), - (71344, 71349, 0), - (71351, 71351, 0), - (71453, 71455, 0), - (71458, 71461, 0), - (71463, 71467, 0), - (71727, 71735, 0), - (71737, 71738, 0), - (71995, 71996, 0), - (71998, 71998, 0), - (72003, 72003, 0), - (72148, 72151, 0), - (72154, 72155, 0), - (72160, 72160, 0), + (71216, 71232, 0), + (71339, 71351, 0), + (71453, 71467, 0), + (71724, 71738, 0), + (71984, 71989, 0), + (71991, 71992, 0), + (71995, 71998, 0), + (72000, 72000, 0), + (72002, 72003, 0), + (72145, 72151, 0), + (72154, 72160, 0), + (72164, 72164, 0), (72193, 72202, 0), - (72243, 72248, 0), + (72243, 72249, 0), (72251, 72254, 0), (72263, 72263, 0), - (72273, 72278, 0), - (72281, 72283, 0), - (72330, 72342, 0), - (72344, 72345, 0), - (72752, 72758, 0), - (72760, 72765, 0), - (72767, 72767, 0), + (72273, 72283, 0), + (72330, 72345, 0), + (72751, 72758, 0), + (72760, 72767, 0), (72850, 72871, 0), - (72874, 72880, 0), - (72882, 72883, 0), - (72885, 72886, 0), + (72873, 72886, 0), (73009, 73014, 0), (73018, 73018, 0), (73020, 73021, 0), (73023, 73029, 0), (73031, 73031, 0), + (73098, 73102, 0), (73104, 73105, 0), - (73109, 73109, 0), - (73111, 73111, 0), - (73459, 73460, 0), + (73107, 73111, 0), + (73459, 73462, 0), + (73472, 73473, 0), + (73475, 73475, 0), + (73524, 73530, 0), + (73534, 73538, 0), + (78896, 78912, 0), + (78919, 78933, 0), (92912, 92916, 0), (92976, 92982, 0), (94031, 94031, 0), + (94033, 94087, 0), (94095, 94098, 0), (94176, 94179, 2), (94180, 94180, 0), - (94192, 94193, 2), + (94192, 94193, 0), (94208, 100343, 2), (100352, 101589, 2), (101632, 101640, 2), - (110592, 110878, 2), + (110576, 110579, 2), + (110581, 110587, 2), + (110589, 110590, 2), + (110592, 110882, 2), + (110898, 110898, 2), (110928, 110930, 2), + (110933, 110933, 2), (110948, 110951, 2), (110960, 111355, 2), (113821, 113822, 0), - (119143, 119145, 0), - (119163, 119170, 0), + (113824, 113827, 0), + (118528, 118573, 0), + (118576, 118598, 0), + (119141, 119145, 0), + (119149, 119170, 0), (119173, 119179, 0), (119210, 119213, 0), (119362, 119364, 0), @@ -395,8 +391,11 @@ CELL_WIDTHS = [ (122907, 122913, 0), (122915, 122916, 0), (122918, 122922, 0), + (123023, 123023, 0), (123184, 123190, 0), + (123566, 123566, 0), (123628, 123631, 0), + (124140, 124143, 0), (125136, 125142, 0), (125252, 125258, 0), (126980, 126980, 2), @@ -416,7 +415,9 @@ CELL_WIDTHS = [ (127951, 127955, 2), (127968, 127984, 2), (127988, 127988, 2), - (127992, 128062, 2), + (127992, 127994, 2), + (127995, 127999, 0), + (128000, 128062, 2), (128064, 128064, 2), (128066, 128252, 2), (128255, 128317, 2), @@ -430,22 +431,24 @@ CELL_WIDTHS = [ (128716, 128716, 2), (128720, 128722, 2), (128725, 128727, 2), + (128732, 128735, 2), (128747, 128748, 2), (128756, 128764, 2), (128992, 129003, 2), + (129008, 129008, 2), (129292, 129338, 2), (129340, 129349, 2), - (129351, 129400, 2), - (129402, 129483, 2), - (129485, 129535, 2), - (129648, 129652, 2), - (129656, 129658, 2), - (129664, 129670, 2), - (129680, 129704, 2), - (129712, 129718, 2), - (129728, 129730, 2), - (129744, 129750, 2), + (129351, 129535, 2), + (129648, 129660, 2), + (129664, 129672, 2), + (129680, 129725, 2), + (129727, 129733, 2), + (129742, 129755, 2), + (129760, 129768, 2), + (129776, 129784, 2), (131072, 196605, 2), (196608, 262141, 2), + (917505, 917505, 0), + (917536, 917631, 0), (917760, 917999, 0), ] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py index 094d2dc2..e7527e52 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py @@ -1,5 +1,6 @@ CONSOLE_HTML_FORMAT = """\ + - -
    {code}
    +
    {code}
    """ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py index 30446ceb..27d65cec 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import inspect from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature from typing import Any, Collection, Iterable, Optional, Tuple, Type, Union @@ -216,7 +214,7 @@ class Inspect(JupyterMixin): def _get_formatted_doc(self, object_: Any) -> Optional[str]: """ Extract the docstring of an object, process it and returns it. - The processing consists in cleaning up the doctring's indentation, + The processing consists in cleaning up the docstring's indentation, taking only its 1st paragraph if `self.help` is not True, and escape its control codes. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py index b659673e..6ae05d3e 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py @@ -46,7 +46,7 @@ class NullFile(IO[str]): return iter([""]) def __enter__(self) -> IO[str]: - pass + return self def __exit__( self, diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py index e8a3a674..5fd5a383 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py @@ -1,12 +1,6 @@ -import sys from fractions import Fraction from math import ceil -from typing import cast, List, Optional, Sequence - -if sys.version_info >= (3, 8): - from typing import Protocol -else: - from pip._vendor.typing_extensions import Protocol # pragma: no cover +from typing import cast, List, Optional, Sequence, Protocol class Edge(Protocol): @@ -151,7 +145,6 @@ if __name__ == "__main__": @dataclass class E: - size: Optional[int] = None ratio: int = 1 minimum_size: int = 1 diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py index 81b10829..2eba1b9b 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py @@ -2,6 +2,7 @@ The API that this module wraps is documented at https://docs.microsoft.com/en-us/windows/console/console-functions """ + import ctypes import sys from typing import Any @@ -380,7 +381,7 @@ class LegacyWindowsTerm: WindowsCoordinates: The current cursor position. """ coord: COORD = GetConsoleScreenBufferInfo(self._handle).dwCursorPosition - return WindowsCoordinates(row=cast(int, coord.Y), col=cast(int, coord.X)) + return WindowsCoordinates(row=coord.Y, col=coord.X) @property def screen_size(self) -> WindowsCoordinates: @@ -390,9 +391,7 @@ class LegacyWindowsTerm: WindowsCoordinates: The width and height of the screen as WindowsCoordinates. """ screen_size: COORD = GetConsoleScreenBufferInfo(self._handle).dwSize - return WindowsCoordinates( - row=cast(int, screen_size.Y), col=cast(int, screen_size.X) - ) + return WindowsCoordinates(row=screen_size.Y, col=screen_size.X) def write_text(self, text: str) -> None: """Write text directly to the terminal without any modification of styles diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py index 10fc0d7e..7520a9f9 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py @@ -30,7 +30,6 @@ try: ) except (AttributeError, ImportError, ValueError): - # Fallback if we can't load the Windows DLL def get_windows_console_features() -> WindowsConsoleFeatures: features = WindowsConsoleFeatures() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py index c45f193f..2e94ff6f 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import re -from typing import Iterable, List, Tuple +from typing import Iterable from ._loop import loop_last from .cells import cell_len, chop_cells @@ -7,7 +9,11 @@ from .cells import cell_len, chop_cells re_word = re.compile(r"\s*\S+\s*") -def words(text: str) -> Iterable[Tuple[int, int, str]]: +def words(text: str) -> Iterable[tuple[int, int, str]]: + """Yields each word from the text as a tuple + containing (start_index, end_index, word). A "word" in this context may + include the actual word and any whitespace to the right. + """ position = 0 word_match = re_word.match(text, position) while word_match is not None: @@ -17,35 +23,59 @@ def words(text: str) -> Iterable[Tuple[int, int, str]]: word_match = re_word.match(text, end) -def divide_line(text: str, width: int, fold: bool = True) -> List[int]: - divides: List[int] = [] - append = divides.append - line_position = 0 +def divide_line(text: str, width: int, fold: bool = True) -> list[int]: + """Given a string of text, and a width (measured in cells), return a list + of cell offsets which the string should be split at in order for it to fit + within the given width. + + Args: + text: The text to examine. + width: The available cell width. + fold: If True, words longer than `width` will be folded onto a new line. + + Returns: + A list of indices to break the line at. + """ + break_positions: list[int] = [] # offsets to insert the breaks at + append = break_positions.append + cell_offset = 0 _cell_len = cell_len + for start, _end, word in words(text): word_length = _cell_len(word.rstrip()) - if line_position + word_length > width: + remaining_space = width - cell_offset + word_fits_remaining_space = remaining_space >= word_length + + if word_fits_remaining_space: + # Simplest case - the word fits within the remaining width for this line. + cell_offset += _cell_len(word) + else: + # Not enough space remaining for this word on the current line. if word_length > width: + # The word doesn't fit on any line, so we can't simply + # place it on the next line... if fold: - chopped_words = chop_cells(word, max_size=width, position=0) - for last, line in loop_last(chopped_words): + # Fold the word across multiple lines. + folded_word = chop_cells(word, width=width) + for last, line in loop_last(folded_word): if start: append(start) - if last: - line_position = _cell_len(line) + cell_offset = _cell_len(line) else: start += len(line) else: + # Folding isn't allowed, so crop the word. if start: append(start) - line_position = _cell_len(word) - elif line_position and start: + cell_offset = _cell_len(word) + elif cell_offset and start: + # The word doesn't fit within the remaining space on the current + # line, but it *can* fit on to the next (empty) line. append(start) - line_position = _cell_len(word) - else: - line_position += _cell_len(word) - return divides + cell_offset = _cell_len(word) + + return break_positions if __name__ == "__main__": # pragma: no cover @@ -53,4 +83,11 @@ if __name__ == "__main__": # pragma: no cover console = Console(width=10) console.print("12345 abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ 12345") - print(chop_cells("abcdefghijklmnopqrstuvwxyz", 10, position=2)) + print(chop_cells("abcdefghijklmnopqrstuvwxyz", 10)) + + console = Console(width=20) + console.rule() + console.print("TextualはPythonの高速アプリケーション開発フレームワークです") + + console.rule() + console.print("アプリケーションは1670万色を使用でき") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py index c310b66e..e65dc5ba 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py @@ -1,11 +1,5 @@ -import sys from itertools import chain -from typing import TYPE_CHECKING, Iterable, Optional - -if sys.version_info >= (3, 8): - from typing import Literal -else: - from pip._vendor.typing_extensions import Literal # pragma: no cover +from typing import TYPE_CHECKING, Iterable, Optional, Literal from .constrain import Constrain from .jupyter import JupyterMixin @@ -27,7 +21,7 @@ class Align(JupyterMixin): renderable (RenderableType): A console renderable. align (AlignMethod): One of "left", "center", or "right"" style (StyleType, optional): An optional style to apply to the background. - vertical (Optional[VerticalAlginMethod], optional): Optional vertical align, one of "top", "middle", or "bottom". Defaults to None. + vertical (Optional[VerticalAlignMethod], optional): Optional vertical align, one of "top", "middle", or "bottom". Defaults to None. pad (bool, optional): Pad the right with spaces. Defaults to True. width (int, optional): Restrict contents to given width, or None to use default width. Defaults to None. height (int, optional): Set height of align renderable, or None to fit to contents. Defaults to None. @@ -240,6 +234,7 @@ class VerticalCenter(JupyterMixin): Args: renderable (RenderableType): A renderable object. + style (StyleType, optional): An optional style to apply to the background. Defaults to None. """ def __init__( diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py index 66365e65..7de86ce5 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py @@ -9,6 +9,7 @@ from .text import Text re_ansi = re.compile( r""" +(?:\x1b[0-?])| (?:\x1b\](.*?)\x1b\\)| (?:\x1b([(@-Z\\-_]|\[[0-?]*[ -/]*[@-~])) """, diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py index ed86a552..022284b5 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py @@ -48,7 +48,6 @@ class Bar(JupyterMixin): def __rich_console__( self, console: Console, options: ConsoleOptions ) -> RenderResult: - width = min( self.width if self.width is not None else options.max_width, options.max_width, diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py index 97d2a944..3f330ccb 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py @@ -1,10 +1,4 @@ -import sys -from typing import TYPE_CHECKING, Iterable, List - -if sys.version_info >= (3, 8): - from typing import Literal -else: - from pip._vendor.typing_extensions import Literal # pragma: no cover +from typing import TYPE_CHECKING, Iterable, List, Literal from ._loop import loop_last @@ -188,260 +182,224 @@ class Box: return "".join(parts) +# fmt: off ASCII: Box = Box( - """\ -+--+ -| || -|-+| -| || -|-+| -|-+| -| || -+--+ -""", + "+--+\n" + "| ||\n" + "|-+|\n" + "| ||\n" + "|-+|\n" + "|-+|\n" + "| ||\n" + "+--+\n", ascii=True, ) ASCII2: Box = Box( - """\ -+-++ -| || -+-++ -| || -+-++ -+-++ -| || -+-++ -""", + "+-++\n" + "| ||\n" + "+-++\n" + "| ||\n" + "+-++\n" + "+-++\n" + "| ||\n" + "+-++\n", ascii=True, ) ASCII_DOUBLE_HEAD: Box = Box( - """\ -+-++ -| || -+=++ -| || -+-++ -+-++ -| || -+-++ -""", + "+-++\n" + "| ||\n" + "+=++\n" + "| ||\n" + "+-++\n" + "+-++\n" + "| ||\n" + "+-++\n", ascii=True, ) SQUARE: Box = Box( - """\ -┌─┬┐ -│ ││ -├─┼┤ -│ ││ -├─┼┤ -├─┼┤ -│ ││ -└─┴┘ -""" + "┌─┬┐\n" + "│ ││\n" + "├─┼┤\n" + "│ ││\n" + "├─┼┤\n" + "├─┼┤\n" + "│ ││\n" + "└─┴┘\n" ) SQUARE_DOUBLE_HEAD: Box = Box( - """\ -┌─┬┐ -│ ││ -╞═╪╡ -│ ││ -├─┼┤ -├─┼┤ -│ ││ -└─┴┘ -""" + "┌─┬┐\n" + "│ ││\n" + "╞═╪╡\n" + "│ ││\n" + "├─┼┤\n" + "├─┼┤\n" + "│ ││\n" + "└─┴┘\n" ) MINIMAL: Box = Box( - """\ - ╷ - │ -╶─┼╴ - │ -╶─┼╴ -╶─┼╴ - │ - ╵ -""" + " ╷ \n" + " │ \n" + "╶─┼╴\n" + " │ \n" + "╶─┼╴\n" + "╶─┼╴\n" + " │ \n" + " ╵ \n" ) MINIMAL_HEAVY_HEAD: Box = Box( - """\ - ╷ - │ -╺━┿╸ - │ -╶─┼╴ -╶─┼╴ - │ - ╵ -""" + " ╷ \n" + " │ \n" + "╺━┿╸\n" + " │ \n" + "╶─┼╴\n" + "╶─┼╴\n" + " │ \n" + " ╵ \n" ) MINIMAL_DOUBLE_HEAD: Box = Box( - """\ - ╷ - │ - ═╪ - │ - ─┼ - ─┼ - │ - ╵ -""" + " ╷ \n" + " │ \n" + " ═╪ \n" + " │ \n" + " ─┼ \n" + " ─┼ \n" + " │ \n" + " ╵ \n" ) SIMPLE: Box = Box( - """\ - - - ── - - - ── - - -""" + " \n" + " \n" + " ── \n" + " \n" + " \n" + " ── \n" + " \n" + " \n" ) SIMPLE_HEAD: Box = Box( - """\ - - - ── - - - - - -""" + " \n" + " \n" + " ── \n" + " \n" + " \n" + " \n" + " \n" + " \n" ) SIMPLE_HEAVY: Box = Box( - """\ - - - ━━ - - - ━━ - - -""" + " \n" + " \n" + " ━━ \n" + " \n" + " \n" + " ━━ \n" + " \n" + " \n" ) HORIZONTALS: Box = Box( - """\ - ── - - ── - - ── - ── - - ── -""" + " ── \n" + " \n" + " ── \n" + " \n" + " ── \n" + " ── \n" + " \n" + " ── \n" ) ROUNDED: Box = Box( - """\ -╭─┬╮ -│ ││ -├─┼┤ -│ ││ -├─┼┤ -├─┼┤ -│ ││ -╰─┴╯ -""" + "╭─┬╮\n" + "│ ││\n" + "├─┼┤\n" + "│ ││\n" + "├─┼┤\n" + "├─┼┤\n" + "│ ││\n" + "╰─┴╯\n" ) HEAVY: Box = Box( - """\ -┏━┳┓ -┃ ┃┃ -┣━╋┫ -┃ ┃┃ -┣━╋┫ -┣━╋┫ -┃ ┃┃ -┗━┻┛ -""" + "┏━┳┓\n" + "┃ ┃┃\n" + "┣━╋┫\n" + "┃ ┃┃\n" + "┣━╋┫\n" + "┣━╋┫\n" + "┃ ┃┃\n" + "┗━┻┛\n" ) HEAVY_EDGE: Box = Box( - """\ -┏━┯┓ -┃ │┃ -┠─┼┨ -┃ │┃ -┠─┼┨ -┠─┼┨ -┃ │┃ -┗━┷┛ -""" + "┏━┯┓\n" + "┃ │┃\n" + "┠─┼┨\n" + "┃ │┃\n" + "┠─┼┨\n" + "┠─┼┨\n" + "┃ │┃\n" + "┗━┷┛\n" ) HEAVY_HEAD: Box = Box( - """\ -┏━┳┓ -┃ ┃┃ -┡━╇┩ -│ ││ -├─┼┤ -├─┼┤ -│ ││ -└─┴┘ -""" + "┏━┳┓\n" + "┃ ┃┃\n" + "┡━╇┩\n" + "│ ││\n" + "├─┼┤\n" + "├─┼┤\n" + "│ ││\n" + "└─┴┘\n" ) DOUBLE: Box = Box( - """\ -╔═╦╗ -║ ║║ -╠═╬╣ -║ ║║ -╠═╬╣ -╠═╬╣ -║ ║║ -╚═╩╝ -""" + "╔═╦╗\n" + "║ ║║\n" + "╠═╬╣\n" + "║ ║║\n" + "╠═╬╣\n" + "╠═╬╣\n" + "║ ║║\n" + "╚═╩╝\n" ) DOUBLE_EDGE: Box = Box( - """\ -╔═╤╗ -║ │║ -╟─┼╢ -║ │║ -╟─┼╢ -╟─┼╢ -║ │║ -╚═╧╝ -""" + "╔═╤╗\n" + "║ │║\n" + "╟─┼╢\n" + "║ │║\n" + "╟─┼╢\n" + "╟─┼╢\n" + "║ │║\n" + "╚═╧╝\n" ) MARKDOWN: Box = Box( - """\ - -| || -|-|| -| || -|-|| -|-|| -| || - -""", + " \n" + "| ||\n" + "|-||\n" + "| ||\n" + "|-||\n" + "|-||\n" + "| ||\n" + " \n", ascii=True, ) +# fmt: on # Map Boxes that don't render with raster fonts on to equivalent that do LEGACY_WINDOWS_SUBSTITUTIONS = { @@ -464,7 +422,6 @@ PLAIN_HEADED_SUBSTITUTIONS = { if __name__ == "__main__": # pragma: no cover - from pip._vendor.rich.columns import Columns from pip._vendor.rich.panel import Panel diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py index 9354f9e3..a8546227 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py @@ -1,11 +1,33 @@ -import re +from __future__ import annotations + from functools import lru_cache -from typing import Callable, List +from typing import Callable from ._cell_widths import CELL_WIDTHS -# Regex to match sequence of the most common character ranges -_is_single_cell_widths = re.compile("^[\u0020-\u006f\u00a0\u02ff\u0370-\u0482]*$").match +# Ranges of unicode ordinals that produce a 1-cell wide character +# This is non-exhaustive, but covers most common Western characters +_SINGLE_CELL_UNICODE_RANGES: list[tuple[int, int]] = [ + (0x20, 0x7E), # Latin (excluding non-printable) + (0xA0, 0xAC), + (0xAE, 0x002FF), + (0x00370, 0x00482), # Greek / Cyrillic + (0x02500, 0x025FC), # Box drawing, box elements, geometric shapes + (0x02800, 0x028FF), # Braille +] + +# A set of characters that are a single cell wide +_SINGLE_CELLS = frozenset( + [ + character + for _start, _end in _SINGLE_CELL_UNICODE_RANGES + for character in map(chr, range(_start, _end + 1)) + ] +) + +# When called with a string this will return True if all +# characters are single-cell, otherwise False +_is_single_cell_widths: Callable[[str], bool] = _SINGLE_CELLS.issuperset @lru_cache(4096) @@ -21,9 +43,9 @@ def cached_cell_len(text: str) -> int: Returns: int: Get the number of cells required to display text. """ - _get_size = get_character_cell_size - total_size = sum(_get_size(character) for character in text) - return total_size + if _is_single_cell_widths(text): + return len(text) + return sum(map(get_character_cell_size, text)) def cell_len(text: str, _cell_len: Callable[[str], int] = cached_cell_len) -> int: @@ -37,9 +59,9 @@ def cell_len(text: str, _cell_len: Callable[[str], int] = cached_cell_len) -> in """ if len(text) < 512: return _cell_len(text) - _get_size = get_character_cell_size - total_size = sum(_get_size(character) for character in text) - return total_size + if _is_single_cell_widths(text): + return len(text) + return sum(map(get_character_cell_size, text)) @lru_cache(maxsize=4096) @@ -52,20 +74,7 @@ def get_character_cell_size(character: str) -> int: Returns: int: Number of cells (0, 1 or 2) occupied by that character. """ - return _get_codepoint_cell_size(ord(character)) - - -@lru_cache(maxsize=4096) -def _get_codepoint_cell_size(codepoint: int) -> int: - """Get the cell size of a character. - - Args: - codepoint (int): Codepoint of a character. - - Returns: - int: Number of cells (0, 1 or 2) occupied by that character. - """ - + codepoint = ord(character) _table = CELL_WIDTHS lower_bound = 0 upper_bound = len(_table) - 1 @@ -119,33 +128,44 @@ def set_cell_size(text: str, total: int) -> str: start = pos -# TODO: This is inefficient -# TODO: This might not work with CWJ type characters -def chop_cells(text: str, max_size: int, position: int = 0) -> List[str]: - """Break text in to equal (cell) length strings, returning the characters in reverse - order""" - _get_character_cell_size = get_character_cell_size - characters = [ - (character, _get_character_cell_size(character)) for character in text - ] - total_size = position - lines: List[List[str]] = [[]] - append = lines[-1].append +def chop_cells( + text: str, + width: int, +) -> list[str]: + """Split text into lines such that each line fits within the available (cell) width. - for character, size in reversed(characters): - if total_size + size > max_size: - lines.append([character]) - append = lines[-1].append - total_size = size + Args: + text: The text to fold such that it fits in the given width. + width: The width available (number of cells). + + Returns: + A list of strings such that each string in the list has cell width + less than or equal to the available width. + """ + _get_character_cell_size = get_character_cell_size + lines: list[list[str]] = [[]] + + append_new_line = lines.append + append_to_last_line = lines[-1].append + + total_width = 0 + + for character in text: + cell_width = _get_character_cell_size(character) + char_doesnt_fit = total_width + cell_width > width + + if char_doesnt_fit: + append_new_line([character]) + append_to_last_line = lines[-1].append + total_width = cell_width else: - total_size += size - append(character) + append_to_last_line(character) + total_width += cell_width return ["".join(line) for line in lines] if __name__ == "__main__": # pragma: no cover - print(get_character_cell_size("😽")) for line in chop_cells("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", 8): print(line) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py index dfe45593..e2c23a6a 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py @@ -1,5 +1,5 @@ -import platform import re +import sys from colorsys import rgb_to_hls from enum import IntEnum from functools import lru_cache @@ -15,7 +15,7 @@ if TYPE_CHECKING: # pragma: no cover from .text import Text -WINDOWS = platform.system() == "Windows" +WINDOWS = sys.platform == "win32" class ColorSystem(IntEnum): @@ -592,7 +592,6 @@ def blend_rgb( if __name__ == "__main__": # pragma: no cover - from .console import Console from .table import Table from .text import Text diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py index e559cbb4..db2ba55a 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py @@ -1,6 +1,5 @@ import inspect import os -import platform import sys import threading import zlib @@ -23,27 +22,21 @@ from typing import ( Dict, Iterable, List, + Literal, Mapping, NamedTuple, Optional, + Protocol, TextIO, Tuple, Type, Union, cast, + runtime_checkable, ) from pip._vendor.rich._null_file import NULL_FILE -if sys.version_info >= (3, 8): - from typing import Literal, Protocol, runtime_checkable -else: - from pip._vendor.typing_extensions import ( - Literal, - Protocol, - runtime_checkable, - ) # pragma: no cover - from . import errors, themes from ._emoji_replace import _emoji_replace from ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT @@ -76,7 +69,7 @@ if TYPE_CHECKING: JUPYTER_DEFAULT_COLUMNS = 115 JUPYTER_DEFAULT_LINES = 100 -WINDOWS = platform.system() == "Windows" +WINDOWS = sys.platform == "win32" HighlighterType = Callable[[Union[str, "Text"]], "Text"] JustifyMethod = Literal["default", "left", "center", "right", "full"] @@ -90,15 +83,15 @@ class NoChange: NO_CHANGE = NoChange() try: - _STDIN_FILENO = sys.__stdin__.fileno() + _STDIN_FILENO = sys.__stdin__.fileno() # type: ignore[union-attr] except Exception: _STDIN_FILENO = 0 try: - _STDOUT_FILENO = sys.__stdout__.fileno() + _STDOUT_FILENO = sys.__stdout__.fileno() # type: ignore[union-attr] except Exception: _STDOUT_FILENO = 1 try: - _STDERR_FILENO = sys.__stderr__.fileno() + _STDERR_FILENO = sys.__stderr__.fileno() # type: ignore[union-attr] except Exception: _STDERR_FILENO = 2 @@ -278,6 +271,7 @@ class ConsoleRenderable(Protocol): # A type that may be rendered by Console. RenderableType = Union[ConsoleRenderable, RichCast, str] +"""A string or any object that may be rendered by Rich.""" # The result of calling a __rich_console__ method. RenderResult = Iterable[Union[RenderableType, Segment]] @@ -500,7 +494,7 @@ def group(fit: bool = True) -> Callable[..., Callable[..., Group]]: """ def decorator( - method: Callable[..., Iterable[RenderableType]] + method: Callable[..., Iterable[RenderableType]], ) -> Callable[..., Group]: """Convert a method that returns an iterable of renderables in to a Group.""" @@ -735,8 +729,18 @@ class Console: self.get_time = get_time or monotonic self.style = style self.no_color = ( - no_color if no_color is not None else "NO_COLOR" in self._environ + no_color + if no_color is not None + else self._environ.get("NO_COLOR", "") != "" ) + if force_interactive is None: + tty_interactive = self._environ.get("TTY_INTERACTIVE", None) + if tty_interactive is not None: + if tty_interactive == "0": + force_interactive = False + elif tty_interactive == "1": + force_interactive = True + self.is_interactive = ( (self.is_terminal and not self.is_dumb_terminal) if force_interactive is None @@ -749,7 +753,7 @@ class Console: ) self._record_buffer: List[Segment] = [] self._render_hooks: List[RenderHook] = [] - self._live: Optional["Live"] = None + self._live_stack: List[Live] = [] self._is_alt_screen = False def __repr__(self) -> str: @@ -821,24 +825,26 @@ class Console: self._buffer_index -= 1 self._check_buffer() - def set_live(self, live: "Live") -> None: - """Set Live instance. Used by Live context manager. + def set_live(self, live: "Live") -> bool: + """Set Live instance. Used by Live context manager (no need to call directly). Args: live (Live): Live instance using this Console. + Returns: + Boolean that indicates if the live is the topmost of the stack. + Raises: errors.LiveError: If this Console has a Live context currently active. """ with self._lock: - if self._live is not None: - raise errors.LiveError("Only one live display may be active at once") - self._live = live + self._live_stack.append(live) + return len(self._live_stack) == 1 def clear_live(self) -> None: - """Clear the Live instance.""" + """Clear the Live instance. Used by the Live context manager (no need to call directly).""" with self._lock: - self._live = None + self._live_stack.pop() def push_render_hook(self, hook: RenderHook) -> None: """Add a new render hook to the stack. @@ -933,11 +939,13 @@ class Console: Returns: bool: True if the console writing to a device capable of - understanding terminal codes, otherwise False. + understanding escape sequences, otherwise False. """ + # If dev has explicitly set this value, return it if self._force_terminal is not None: return self._force_terminal + # Fudge for Idle if hasattr(sys.stdin, "__module__") and sys.stdin.__module__.startswith( "idlelib" ): @@ -948,12 +956,22 @@ class Console: # return False for Jupyter, which may have FORCE_COLOR set return False - # If FORCE_COLOR env var has any value at all, we assume a terminal. - force_color = self._environ.get("FORCE_COLOR") - if force_color is not None: - self._force_terminal = True + environ = self._environ + + tty_compatible = environ.get("TTY_COMPATIBLE", "") + # 0 indicates device is not tty compatible + if tty_compatible == "0": + return False + # 1 indicates device is tty compatible + if tty_compatible == "1": return True + # https://force-color.org/ + force_color = environ.get("FORCE_COLOR") + if force_color is not None: + return force_color != "" + + # Any other value defaults to auto detect isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None) try: return False if isatty is None else isatty() @@ -978,12 +996,13 @@ class Console: @property def options(self) -> ConsoleOptions: """Get default console options.""" + size = self.size return ConsoleOptions( - max_height=self.size.height, - size=self.size, + max_height=size.height, + size=size, legacy_windows=self.legacy_windows, min_width=1, - max_width=self.width, + max_width=size.width, encoding=self.encoding, is_terminal=self.is_terminal, ) @@ -1005,19 +1024,14 @@ class Console: width: Optional[int] = None height: Optional[int] = None - if WINDOWS: # pragma: no cover + streams = _STD_STREAMS_OUTPUT if WINDOWS else _STD_STREAMS + for file_descriptor in streams: try: - width, height = os.get_terminal_size() + width, height = os.get_terminal_size(file_descriptor) except (AttributeError, ValueError, OSError): # Probably not a terminal pass - else: - for file_descriptor in _STD_STREAMS: - try: - width, height = os.get_terminal_size(file_descriptor) - except (AttributeError, ValueError, OSError): - pass - else: - break + else: + break columns = self._environ.get("COLUMNS") if columns is not None and columns.isdigit(): @@ -1308,7 +1322,7 @@ class Console: renderable = rich_cast(renderable) if hasattr(renderable, "__rich_console__") and not isclass(renderable): - render_iterable = renderable.__rich_console__(self, _options) # type: ignore[union-attr] + render_iterable = renderable.__rich_console__(self, _options) elif isinstance(renderable, str): text_renderable = self.render_str( renderable, highlight=_options.highlight, markup=_options.markup @@ -1385,9 +1399,14 @@ class Console: extra_lines = render_options.height - len(lines) if extra_lines > 0: pad_line = [ - [Segment(" " * render_options.max_width, style), Segment("\n")] - if new_lines - else [Segment(" " * render_options.max_width, style)] + ( + [ + Segment(" " * render_options.max_width, style), + Segment("\n"), + ] + if new_lines + else [Segment(" " * render_options.max_width, style)] + ) ] lines.extend(pad_line * extra_lines) @@ -1436,9 +1455,11 @@ class Console: rich_text.overflow = overflow else: rich_text = Text( - _emoji_replace(text, default_variant=self._emoji_variant) - if emoji_enabled - else text, + ( + _emoji_replace(text, default_variant=self._emoji_variant) + if emoji_enabled + else text + ), justify=justify, overflow=overflow, style=style, @@ -1535,7 +1556,11 @@ class Console: if isinstance(renderable, str): append_text( self.render_str( - renderable, emoji=emoji, markup=markup, highlighter=_highlighter + renderable, + emoji=emoji, + markup=markup, + highlight=highlight, + highlighter=_highlighter, ) ) elif isinstance(renderable, Text): @@ -1925,7 +1950,6 @@ class Console: end (str, optional): String to write at end of print data. Defaults to "\\\\n". style (Union[str, Style], optional): A style to apply to output. Defaults to None. justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``. - overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None. emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None. markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None. highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None. @@ -1986,6 +2010,20 @@ class Console: ): buffer_extend(line) + def on_broken_pipe(self) -> None: + """This function is called when a `BrokenPipeError` is raised. + + This can occur when piping Textual output in Linux and macOS. + The default implementation is to exit the app, but you could implement + this method in a subclass to change the behavior. + + See https://docs.python.org/3/library/signal.html#note-on-sigpipe for details. + """ + self.quiet = True + devnull = os.open(os.devnull, os.O_WRONLY) + os.dup2(devnull, sys.stdout.fileno()) + raise SystemExit(1) + def _check_buffer(self) -> None: """Check if the buffer may be rendered. Render it if it can (e.g. Console.quiet is False) Rendering is supported on Windows, Unix and Jupyter environments. For @@ -1995,8 +2033,17 @@ class Console: if self.quiet: del self._buffer[:] return + + try: + self._write_buffer() + except BrokenPipeError: + self.on_broken_pipe() + + def _write_buffer(self) -> None: + """Write the buffer to the output file.""" + with self._lock: - if self.record: + if self.record and not self._buffer_index: with self._record_buffer_lock: self._record_buffer.extend(self._buffer[:]) @@ -2166,7 +2213,7 @@ class Console: """ text = self.export_text(clear=clear, styles=styles) - with open(path, "wt", encoding="utf-8") as write_file: + with open(path, "w", encoding="utf-8") as write_file: write_file.write(text) def export_html( @@ -2272,7 +2319,7 @@ class Console: code_format=code_format, inline_styles=inline_styles, ) - with open(path, "wt", encoding="utf-8") as write_file: + with open(path, "w", encoding="utf-8") as write_file: write_file.write(html) def export_svg( @@ -2561,7 +2608,7 @@ class Console: font_aspect_ratio=font_aspect_ratio, unique_id=unique_id, ) - with open(path, "wt", encoding="utf-8") as write_file: + with open(path, "w", encoding="utf-8") as write_file: write_file.write(svg) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py index e29cf368..901ff8ba 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py @@ -1,13 +1,13 @@ from itertools import zip_longest from typing import ( - Iterator, + TYPE_CHECKING, Iterable, + Iterator, List, Optional, + TypeVar, Union, overload, - TypeVar, - TYPE_CHECKING, ) if TYPE_CHECKING: @@ -119,7 +119,7 @@ class Lines: Args: console (Console): Console instance. - width (int): Number of characters per line. + width (int): Number of cells available per line. justify (str, optional): Default justify method for text: "left", "center", "full" or "right". Defaults to "left". overflow (str, optional): Default overflow for text: "crop", "fold", or "ellipsis". Defaults to "fold". diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py index 88fcb929..84963e9d 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py @@ -1,11 +1,5 @@ -import sys import time -from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union - -if sys.version_info >= (3, 8): - from typing import Final -else: - from pip._vendor.typing_extensions import Final # pragma: no cover +from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union, Final from .segment import ControlCode, ControlType, Segment diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py index dca37193..61797bf3 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py @@ -54,7 +54,7 @@ DEFAULT_STYLES: Dict[str, Style] = { "logging.level.notset": Style(dim=True), "logging.level.debug": Style(color="green"), "logging.level.info": Style(color="blue"), - "logging.level.warning": Style(color="red"), + "logging.level.warning": Style(color="yellow"), "logging.level.error": Style(color="red", bold=True), "logging.level.critical": Style(color="red", bold=True, reverse=True), "log.level": Style.null(), @@ -120,6 +120,9 @@ DEFAULT_STYLES: Dict[str, Style] = { "traceback.exc_type": Style(color="bright_red", bold=True), "traceback.exc_value": Style.null(), "traceback.offset": Style(color="bright_red", bold=True), + "traceback.error_range": Style(underline=True, bold=True), + "traceback.note": Style(color="green", bold=True), + "traceback.group.border": Style(color="magenta"), "bar.back": Style(color="grey23"), "bar.complete": Style(color="rgb(249,38,114)"), "bar.finished": Style(color="rgb(114,156,31)"), diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py index ad361838..92893b32 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py @@ -15,16 +15,18 @@ def report() -> None: # pragma: no cover inspect(features) env_names = ( - "TERM", - "COLORTERM", "CLICOLOR", - "NO_COLOR", - "TERM_PROGRAM", + "COLORTERM", "COLUMNS", - "LINES", + "JPY_PARENT_PID", "JUPYTER_COLUMNS", "JUPYTER_LINES", - "JPY_PARENT_PID", + "LINES", + "NO_COLOR", + "TERM_PROGRAM", + "TERM", + "TTY_COMPATIBLE", + "TTY_INTERACTIVE", "VSCODE_VERBOSE_LOGGING", ) env = {name: os.getenv(name) for name in env_names} diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py index 791f0465..4a667edd 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py @@ -1,5 +1,5 @@ import sys -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING, Optional, Union, Literal from .jupyter import JupyterMixin from .segment import Segment @@ -7,11 +7,6 @@ from .style import Style from ._emoji_codes import EMOJI from ._emoji_replace import _emoji_replace -if sys.version_info >= (3, 8): - from typing import Literal -else: - from pip._vendor.typing_extensions import Literal # pragma: no cover - if TYPE_CHECKING: from .console import Console, ConsoleOptions, RenderResult diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py index 99f118e2..83bc9118 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py @@ -1,4 +1,3 @@ -# coding: utf-8 """Functions for reporting filesizes. Borrowed from https://github.com/PyFilesystem/pyfilesystem2 The functions declared in this module should cover the different @@ -27,7 +26,7 @@ def _to_str( if size == 1: return "1 byte" elif size < base: - return "{:,} bytes".format(size) + return f"{size:,} bytes" for i, suffix in enumerate(suffixes, 2): # noqa: B007 unit = base**i diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py index c2646794..e4c462e2 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py @@ -98,7 +98,7 @@ class ReprHighlighter(RegexHighlighter): r"(?P(?\B(/[-\w._+]+)*\/)(?P[-\w._+]*)?", r"(?b?'''.*?(?(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#]*)", + r"(?P(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#~@]*)", ), ] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/json.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/json.py index ea94493f..4087c79b 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/json.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/json.py @@ -103,7 +103,6 @@ class JSON: if __name__ == "__main__": - import argparse import sys diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/layout.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/layout.py index 849356ea..a6f1a31b 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/layout.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/layout.py @@ -227,7 +227,6 @@ class Layout: from pip._vendor.rich.tree import Tree def summary(layout: "Layout") -> Table: - icon = layout.splitter.get_tree_icon() table = Table.grid(padding=(0, 1, 0, 0)) @@ -403,7 +402,7 @@ class Layout: self._render_map = render_map layout_lines: List[List[Segment]] = [[] for _ in range(height)] _islice = islice - for (region, lines) in render_map.values(): + for region, lines in render_map.values(): _x, y, _layout_width, layout_height = region for row, line in zip( _islice(layout_lines, y, y + layout_height), lines diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/live.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/live.py index 3ebbbc4c..cc3a39bd 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/live.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/live.py @@ -1,10 +1,12 @@ +from __future__ import annotations + import sys from threading import Event, RLock, Thread from types import TracebackType -from typing import IO, Any, Callable, List, Optional, TextIO, Type, cast +from typing import IO, TYPE_CHECKING, Any, Callable, List, Optional, TextIO, Type, cast from . import get_console -from .console import Console, ConsoleRenderable, RenderableType, RenderHook +from .console import Console, ConsoleRenderable, Group, RenderableType, RenderHook from .control import Control from .file_proxy import FileProxy from .jupyter import JupyterMixin @@ -12,6 +14,10 @@ from .live_render import LiveRender, VerticalOverflowMethod from .screen import Screen from .text import Text +if TYPE_CHECKING: + # Can be replaced with `from typing import Self` in Python 3.11+ + from typing_extensions import Self # pragma: no cover + class _RefreshThread(Thread): """A thread that calls refresh() at regular intervals.""" @@ -37,7 +43,7 @@ class Live(JupyterMixin, RenderHook): Args: renderable (RenderableType, optional): The renderable to live display. Defaults to displaying nothing. - console (Console, optional): Optional Console instance. Default will an internal Console instance writing to stdout. + console (Console, optional): Optional Console instance. Defaults to an internal Console instance writing to stdout. screen (bool, optional): Enable alternate screen mode. Defaults to False. auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()` or `update()` with refresh flag. Defaults to True refresh_per_second (float, optional): Number of times per second to refresh the live display. Defaults to 4. @@ -87,6 +93,7 @@ class Live(JupyterMixin, RenderHook): self._live_render = LiveRender( self.get_renderable(), vertical_overflow=vertical_overflow ) + self._nested = False @property def is_started(self) -> bool: @@ -110,8 +117,12 @@ class Live(JupyterMixin, RenderHook): with self._lock: if self._started: return - self.console.set_live(self) self._started = True + + if not self.console.set_live(self): + self._nested = True + return + if self._screen: self._alt_screen = self.console.set_alt_screen(True) self.console.show_cursor(False) @@ -136,8 +147,12 @@ class Live(JupyterMixin, RenderHook): with self._lock: if not self._started: return - self.console.clear_live() self._started = False + self.console.clear_live() + if self._nested: + if not self.transient: + self.console.print(self.renderable) + return if self.auto_refresh and self._refresh_thread is not None: self._refresh_thread.stop() @@ -156,13 +171,12 @@ class Live(JupyterMixin, RenderHook): self.console.show_cursor(True) if self._alt_screen: self.console.set_alt_screen(False) - if self.transient and not self._alt_screen: self.console.control(self._live_render.restore_cursor()) if self.ipy_widget is not None and self.transient: self.ipy_widget.close() # pragma: no cover - def __enter__(self) -> "Live": + def __enter__(self) -> Self: self.start(refresh=self._renderable is not None) return self @@ -200,7 +214,13 @@ class Live(JupyterMixin, RenderHook): Returns: RenderableType: Displayed renderable. """ - renderable = self.get_renderable() + live_stack = self.console._live_stack + renderable: RenderableType + if live_stack and self is live_stack[0]: + # The first Live instance will render everything in the Live stack + renderable = Group(*[live.get_renderable() for live in live_stack]) + else: + renderable = self.get_renderable() return Screen(renderable) if self._alt_screen else renderable def update(self, renderable: RenderableType, *, refresh: bool = False) -> None: @@ -221,6 +241,11 @@ class Live(JupyterMixin, RenderHook): """Update the display of the Live Render.""" with self._lock: self._live_render.set_renderable(self.renderable) + if self._nested: + if self.console._live_stack: + self.console._live_stack[0].refresh() + return + if self.console.is_jupyter: # pragma: no cover try: from IPython.display import display @@ -362,7 +387,7 @@ if __name__ == "__main__": # pragma: no cover table.add_column("Destination Currency") table.add_column("Exchange Rate") - for ((source, dest), exchange_rate) in exchange_rate_dict.items(): + for (source, dest), exchange_rate in exchange_rate_dict.items(): table.add_row( source, dest, diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/live_render.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/live_render.py index b90fbf7f..d3da5111 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/live_render.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/live_render.py @@ -1,10 +1,4 @@ -import sys -from typing import Optional, Tuple - -if sys.version_info >= (3, 8): - from typing import Literal -else: - from pip._vendor.typing_extensions import Literal # pragma: no cover +from typing import Optional, Tuple, Literal from ._loop import loop_last @@ -82,7 +76,6 @@ class LiveRender: def __rich_console__( self, console: Console, options: ConsoleOptions ) -> RenderResult: - renderable = self.renderable style = console.get_style(self.style) lines = console.render_lines(renderable, options, style=style, pad=False) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/logging.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/logging.py index 91368dda..08f34c70 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/logging.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/logging.py @@ -36,11 +36,13 @@ class RichHandler(Handler): markup (bool, optional): Enable console markup in log messages. Defaults to False. rich_tracebacks (bool, optional): Enable rich tracebacks with syntax highlighting and formatting. Defaults to False. tracebacks_width (Optional[int], optional): Number of characters used to render tracebacks, or None for full width. Defaults to None. + tracebacks_code_width (int, optional): Number of code characters used to render tracebacks, or None for full width. Defaults to 88. tracebacks_extra_lines (int, optional): Additional lines of code to render tracebacks, or None for full width. Defaults to None. tracebacks_theme (str, optional): Override pygments theme used in traceback. tracebacks_word_wrap (bool, optional): Enable word wrapping of long tracebacks lines. Defaults to True. tracebacks_show_locals (bool, optional): Enable display of locals in tracebacks. Defaults to False. tracebacks_suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback. + tracebacks_max_frames (int, optional): Optional maximum number of frames returned by traceback. locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation. Defaults to 10. locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80. @@ -74,11 +76,13 @@ class RichHandler(Handler): markup: bool = False, rich_tracebacks: bool = False, tracebacks_width: Optional[int] = None, + tracebacks_code_width: Optional[int] = 88, tracebacks_extra_lines: int = 3, tracebacks_theme: Optional[str] = None, tracebacks_word_wrap: bool = True, tracebacks_show_locals: bool = False, tracebacks_suppress: Iterable[Union[str, ModuleType]] = (), + tracebacks_max_frames: int = 100, locals_max_length: int = 10, locals_max_string: int = 80, log_time_format: Union[str, FormatTimeCallable] = "[%x %X]", @@ -104,6 +108,8 @@ class RichHandler(Handler): self.tracebacks_word_wrap = tracebacks_word_wrap self.tracebacks_show_locals = tracebacks_show_locals self.tracebacks_suppress = tracebacks_suppress + self.tracebacks_max_frames = tracebacks_max_frames + self.tracebacks_code_width = tracebacks_code_width self.locals_max_length = locals_max_length self.locals_max_string = locals_max_string self.keywords = keywords @@ -140,6 +146,7 @@ class RichHandler(Handler): exc_value, exc_traceback, width=self.tracebacks_width, + code_width=self.tracebacks_code_width, extra_lines=self.tracebacks_extra_lines, theme=self.tracebacks_theme, word_wrap=self.tracebacks_word_wrap, @@ -147,6 +154,7 @@ class RichHandler(Handler): locals_max_length=self.locals_max_length, locals_max_string=self.locals_max_string, suppress=self.tracebacks_suppress, + max_frames=self.tracebacks_max_frames, ) message = record.getMessage() if self.formatter: diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/markup.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/markup.py index fd80d8c1..f6171878 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/markup.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/markup.py @@ -64,6 +64,9 @@ def escape( return f"{backslashes}{backslashes}\\{text}" markup = _escape(escape_backslashes, markup) + if markup.endswith("\\") and not markup.endswith("\\\\"): + return markup + "\\" + return markup @@ -110,7 +113,10 @@ def render( Args: markup (str): A string containing console markup. + style: (Union[str, Style]): The style to use. emoji (bool, optional): Also render emoji code. Defaults to True. + emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None. + Raises: MarkupError: If there is a syntax error in the markup. @@ -226,7 +232,6 @@ def render( if __name__ == "__main__": # pragma: no cover - MARKUP = [ "[red]Hello World[/red]", "[magenta]Hello [b]World[/b]", diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/padding.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/padding.py index 1b2204f5..0161cd18 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/padding.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/padding.py @@ -1,4 +1,4 @@ -from typing import cast, List, Optional, Tuple, TYPE_CHECKING, Union +from typing import TYPE_CHECKING, List, Optional, Tuple, Union if TYPE_CHECKING: from .console import ( @@ -7,11 +7,11 @@ if TYPE_CHECKING: RenderableType, RenderResult, ) + from .jupyter import JupyterMixin from .measure import Measurement -from .style import Style from .segment import Segment - +from .style import Style PaddingDimensions = Union[int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int]] @@ -66,10 +66,10 @@ class Padding(JupyterMixin): _pad = pad[0] return (_pad, _pad, _pad, _pad) if len(pad) == 2: - pad_top, pad_right = cast(Tuple[int, int], pad) + pad_top, pad_right = pad return (pad_top, pad_right, pad_top, pad_right) if len(pad) == 4: - top, right, bottom, left = cast(Tuple[int, int, int, int], pad) + top, right, bottom, left = pad return (top, right, bottom, left) raise ValueError(f"1, 2 or 4 integers required for padding; {len(pad)} given") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/panel.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/panel.py index d522d80b..07587e3b 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/panel.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/panel.py @@ -22,11 +22,13 @@ class Panel(JupyterMixin): Args: renderable (RenderableType): A console renderable object. - box (Box, optional): A Box instance that defines the look of the border (see :ref:`appendix_box`. - Defaults to box.ROUNDED. + box (Box): A Box instance that defines the look of the border (see :ref:`appendix_box`. Defaults to box.ROUNDED. + title (Optional[TextType], optional): Optional title displayed in panel header. Defaults to None. + title_align (AlignMethod, optional): Alignment of title. Defaults to "center". + subtitle (Optional[TextType], optional): Optional subtitle displayed in panel footer. Defaults to None. + subtitle_align (AlignMethod, optional): Alignment of subtitle. Defaults to "center". safe_box (bool, optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True. - expand (bool, optional): If True the panel will stretch to fill the console - width, otherwise it will be sized to fit the contents. Defaults to True. + expand (bool, optional): If True the panel will stretch to fill the console width, otherwise it will be sized to fit the contents. Defaults to True. style (str, optional): The style of the panel (border and contents). Defaults to "none". border_style (str, optional): The style of the border. Defaults to "none". width (Optional[int], optional): Optional width of panel. Defaults to None to auto-detect. @@ -82,7 +84,9 @@ class Panel(JupyterMixin): style: StyleType = "none", border_style: StyleType = "none", width: Optional[int] = None, + height: Optional[int] = None, padding: PaddingDimensions = (0, 1), + highlight: bool = False, ) -> "Panel": """An alternative constructor that sets expand=False.""" return cls( @@ -96,7 +100,9 @@ class Panel(JupyterMixin): style=style, border_style=border_style, width=width, + height=height, padding=padding, + highlight=highlight, expand=False, ) @@ -168,6 +174,9 @@ class Panel(JupyterMixin): text = text.copy() text.truncate(width) excess_space = width - cell_len(text.plain) + if text.style: + text.stylize(console.get_style(text.style)) + if excess_space: if align == "left": return Text.assemble( diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/pretty.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/pretty.py index 2bd9eb00..c4a274f8 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/pretty.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/pretty.py @@ -3,6 +3,7 @@ import collections import dataclasses import inspect import os +import reprlib import sys from array import array from collections import Counter, UserDict, UserList, defaultdict, deque @@ -15,6 +16,7 @@ from typing import ( Any, Callable, DefaultDict, + Deque, Dict, Iterable, List, @@ -77,7 +79,10 @@ def _is_dataclass_repr(obj: object) -> bool: # Digging in to a lot of internals here # Catching all exceptions in case something is missing on a non CPython implementation try: - return obj.__repr__.__code__.co_filename == dataclasses.__file__ + return obj.__repr__.__code__.co_filename in ( + dataclasses.__file__, + reprlib.__file__, + ) except Exception: # pragma: no coverage return False @@ -130,17 +135,19 @@ def _ipy_display_hook( if _safe_isinstance(value, ConsoleRenderable): console.line() console.print( - value - if _safe_isinstance(value, RichRenderable) - else Pretty( - value, - overflow=overflow, - indent_guides=indent_guides, - max_length=max_length, - max_string=max_string, - max_depth=max_depth, - expand_all=expand_all, - margin=12, + ( + value + if _safe_isinstance(value, RichRenderable) + else Pretty( + value, + overflow=overflow, + indent_guides=indent_guides, + max_length=max_length, + max_string=max_string, + max_depth=max_depth, + expand_all=expand_all, + margin=12, + ) ), crop=crop, new_line_start=True, @@ -196,23 +203,28 @@ def install( assert console is not None builtins._ = None # type: ignore[attr-defined] console.print( - value - if _safe_isinstance(value, RichRenderable) - else Pretty( - value, - overflow=overflow, - indent_guides=indent_guides, - max_length=max_length, - max_string=max_string, - max_depth=max_depth, - expand_all=expand_all, + ( + value + if _safe_isinstance(value, RichRenderable) + else Pretty( + value, + overflow=overflow, + indent_guides=indent_guides, + max_length=max_length, + max_string=max_string, + max_depth=max_depth, + expand_all=expand_all, + ) ), crop=crop, ) builtins._ = value # type: ignore[attr-defined] - if "get_ipython" in globals(): + try: ip = get_ipython() # type: ignore[name-defined] + except NameError: + sys.displayhook = display_hook + else: from IPython.core.formatters import BaseFormatter class RichFormatter(BaseFormatter): # type: ignore[misc] @@ -236,8 +248,6 @@ def install( # replace plain text formatter with rich formatter rich_formatter = RichFormatter() ip.display_formatter.formatters["text/plain"] = rich_formatter - else: - sys.displayhook = display_hook class Pretty(JupyterMixin): @@ -352,6 +362,16 @@ def _get_braces_for_defaultdict(_object: DefaultDict[Any, Any]) -> Tuple[str, st ) +def _get_braces_for_deque(_object: Deque[Any]) -> Tuple[str, str, str]: + if _object.maxlen is None: + return ("deque([", "])", "deque()") + return ( + "deque([", + f"], maxlen={_object.maxlen})", + f"deque(maxlen={_object.maxlen})", + ) + + def _get_braces_for_array(_object: "array[Any]") -> Tuple[str, str, str]: return (f"array({_object.typecode!r}, [", "])", f"array({_object.typecode!r})") @@ -361,7 +381,7 @@ _BRACES: Dict[type, Callable[[Any], Tuple[str, str, str]]] = { array: _get_braces_for_array, defaultdict: _get_braces_for_defaultdict, Counter: lambda _object: ("Counter({", "})", "Counter()"), - deque: lambda _object: ("deque([", "])", "deque()"), + deque: _get_braces_for_deque, dict: lambda _object: ("{", "}", "{}"), UserDict: lambda _object: ("{", "}", "{}"), frozenset: lambda _object: ("frozenset({", "})", "frozenset()"), @@ -708,9 +728,9 @@ def traverse( last=root, ) - def iter_attrs() -> Iterable[ - Tuple[str, Any, Optional[Callable[[Any], str]]] - ]: + def iter_attrs() -> ( + Iterable[Tuple[str, Any, Optional[Callable[[Any], str]]]] + ): """Iterate over attr fields and values.""" for attr in attr_fields: if attr.repr: @@ -761,7 +781,9 @@ def traverse( ) for last, field in loop_last( - field for field in fields(obj) if field.repr + field + for field in fields(obj) + if field.repr and hasattr(obj, field.name) ): child_node = _traverse(getattr(obj, field.name), depth=depth + 1) child_node.key_repr = field.name @@ -845,7 +867,7 @@ def traverse( pop_visited(obj_id) else: node = Node(value_repr=to_repr(obj), last=root) - node.is_tuple = _safe_isinstance(obj, tuple) + node.is_tuple = type(obj) == tuple node.is_namedtuple = _is_namedtuple(obj) return node @@ -985,7 +1007,7 @@ if __name__ == "__main__": # pragma: no cover from pip._vendor.rich import print - # print(Pretty(data, indent_guides=True, max_string=20)) + print(Pretty(data, indent_guides=True, max_string=20)) class Thing: def __repr__(self) -> str: diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/progress.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/progress.py index 8b0a315f..ef6ad60f 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/progress.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/progress.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import io -import sys import typing import warnings from abc import ABC, abstractmethod @@ -14,6 +15,7 @@ from os import PathLike, stat from threading import Event, RLock, Thread from types import TracebackType from typing import ( + TYPE_CHECKING, Any, BinaryIO, Callable, @@ -23,10 +25,10 @@ from typing import ( Generic, Iterable, List, + Literal, NamedTuple, NewType, Optional, - Sequence, TextIO, Tuple, Type, @@ -34,10 +36,9 @@ from typing import ( Union, ) -if sys.version_info >= (3, 8): - from typing import Literal -else: - from pip._vendor.typing_extensions import Literal # pragma: no cover +if TYPE_CHECKING: + # Can be replaced with `from typing import Self` in Python 3.11+ + from typing_extensions import Self # pragma: no cover from . import filesize, get_console from .console import Console, Group, JustifyMethod, RenderableType @@ -70,7 +71,7 @@ class _TrackThread(Thread): self.done = Event() self.completed = 0 - super().__init__() + super().__init__(daemon=True) def run(self) -> None: task_id = self.task_id @@ -78,7 +79,7 @@ class _TrackThread(Thread): update_period = self.update_period last_completed = 0 wait = self.done.wait - while not wait(update_period): + while not wait(update_period) and self.progress.live.is_started: completed = self.completed if last_completed != completed: advance(task_id, completed - last_completed) @@ -101,9 +102,10 @@ class _TrackThread(Thread): def track( - sequence: Union[Sequence[ProgressType], Iterable[ProgressType]], + sequence: Iterable[ProgressType], description: str = "Working...", total: Optional[float] = None, + completed: int = 0, auto_refresh: bool = True, console: Optional[Console] = None, transient: bool = False, @@ -119,10 +121,13 @@ def track( ) -> Iterable[ProgressType]: """Track progress by iterating over a sequence. + You can also track progress of an iterable, which might require that you additionally specify ``total``. + Args: - sequence (Iterable[ProgressType]): A sequence (must support "len") you wish to iterate over. + sequence (Iterable[ProgressType]): Values you wish to iterate over and track progress. description (str, optional): Description of task show next to progress bar. Defaults to "Working". total: (float, optional): Total number of steps. Default is len(sequence). + completed (int, optional): Number of steps completed so far. Defaults to 0. auto_refresh (bool, optional): Automatic refresh, disable to force a refresh after each iteration. Default is True. transient: (bool, optional): Clear the progress on exit. Defaults to False. console (Console, optional): Console to write to. Default creates internal Console instance. @@ -166,7 +171,11 @@ def track( with progress: yield from progress.track( - sequence, total=total, description=description, update_period=update_period + sequence, + total=total, + completed=completed, + description=description, + update_period=update_period, ) @@ -269,6 +278,9 @@ class _Reader(RawIOBase, BinaryIO): def write(self, s: Any) -> int: raise UnsupportedOperation("write") + def writelines(self, lines: Iterable[Any]) -> None: + raise UnsupportedOperation("writelines") + class _ReadContext(ContextManager[_I], Generic[_I]): """A utility class to handle a context for both a reader and a progress.""" @@ -681,7 +693,7 @@ class TimeElapsedColumn(ProgressColumn): elapsed = task.finished_time if task.finished else task.elapsed if elapsed is None: return Text("-:--:--", style="progress.elapsed") - delta = timedelta(seconds=int(elapsed)) + delta = timedelta(seconds=max(0, int(elapsed))) return Text(str(delta), style="progress.elapsed") @@ -710,7 +722,6 @@ class TaskProgressColumn(TextColumn): table_column: Optional[Column] = None, show_speed: bool = False, ) -> None: - self.text_format_no_percentage = text_format_no_percentage self.show_speed = show_speed super().__init__( @@ -1051,7 +1062,7 @@ class Progress(JupyterMixin): """Renders an auto-updating progress bar(s). Args: - console (Console, optional): Optional Console instance. Default will an internal Console instance writing to stdout. + console (Console, optional): Optional Console instance. Defaults to an internal Console instance writing to stdout. auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()`. refresh_per_second (Optional[float], optional): Number of times per second to refresh the progress information or None to use default (10). Defaults to None. speed_estimate_period: (float, optional): Period (in seconds) used to calculate the speed estimate. Defaults to 30. @@ -1114,7 +1125,7 @@ class Progress(JupyterMixin): progress = Progress( SpinnerColumn(), - *Progress.default_columns(), + *Progress.get_default_columns(), "Elapsed:", TimeElapsedColumn(), ) @@ -1162,10 +1173,10 @@ class Progress(JupyterMixin): def stop(self) -> None: """Stop the progress display.""" self.live.stop() - if not self.console.is_interactive: + if not self.console.is_interactive and not self.console.is_jupyter: self.console.print() - def __enter__(self) -> "Progress": + def __enter__(self) -> Self: self.start() return self @@ -1179,17 +1190,21 @@ class Progress(JupyterMixin): def track( self, - sequence: Union[Iterable[ProgressType], Sequence[ProgressType]], + sequence: Iterable[ProgressType], total: Optional[float] = None, + completed: int = 0, task_id: Optional[TaskID] = None, description: str = "Working...", update_period: float = 0.1, ) -> Iterable[ProgressType]: """Track progress by iterating over a sequence. + You can also track progress of an iterable, which might require that you additionally specify ``total``. + Args: - sequence (Sequence[ProgressType]): A sequence of values you want to iterate over and track progress. + sequence (Iterable[ProgressType]): Values you want to iterate over and track progress. total: (float, optional): Total number of steps. Default is len(sequence). + completed (int, optional): Number of steps completed so far. Defaults to 0. task_id: (TaskID): Task to track. Default is new task. description: (str, optional): Description of task, if new task is created. update_period (float, optional): Minimum time (in seconds) between calls to update(). Defaults to 0.1. @@ -1201,9 +1216,9 @@ class Progress(JupyterMixin): total = float(length_hint(sequence)) or None if task_id is None: - task_id = self.add_task(description, total=total) + task_id = self.add_task(description, total=total, completed=completed) else: - self.update(task_id, total=total) + self.update(task_id, total=total, completed=completed) if self.live.auto_refresh: with _TrackThread(self, task_id, update_period) as track_thread: @@ -1327,7 +1342,7 @@ class Progress(JupyterMixin): # normalize the mode (always rb, rt) _mode = "".join(sorted(mode, reverse=False)) if _mode not in ("br", "rt", "r"): - raise ValueError("invalid mode {!r}".format(mode)) + raise ValueError(f"invalid mode {mode!r}") # patch buffering to provide the same behaviour as the builtin `open` line_buffering = buffering == 1 @@ -1636,7 +1651,6 @@ class Progress(JupyterMixin): if __name__ == "__main__": # pragma: no coverage - import random import time @@ -1689,7 +1703,6 @@ if __name__ == "__main__": # pragma: no coverage console=console, transient=False, ) as progress: - task1 = progress.add_task("[red]Downloading", total=1000) task2 = progress.add_task("[green]Processing", total=1000) task3 = progress.add_task("[yellow]Thinking", total=None) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/progress_bar.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/progress_bar.py index 67361df2..41794f76 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/progress_bar.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/progress_bar.py @@ -108,7 +108,7 @@ class ProgressBar(JupyterMixin): for index in range(PULSE_SIZE): position = index / PULSE_SIZE - fade = 0.5 + cos((position * pi * 2)) / 2.0 + fade = 0.5 + cos(position * pi * 2) / 2.0 color = blend_rgb(fore_color, back_color, cross_fade=fade) append(_Segment(bar, _Style(color=from_triplet(color)))) return segments @@ -156,7 +156,6 @@ class ProgressBar(JupyterMixin): def __rich_console__( self, console: Console, options: ConsoleOptions ) -> RenderResult: - width = min(self.width or options.max_width, options.max_width) ascii = options.legacy_windows or options.ascii_only should_pulse = self.pulse or self.total is None diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/prompt.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/prompt.py index 2bd0a772..fccb70db 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/prompt.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/prompt.py @@ -36,6 +36,7 @@ class PromptBase(Generic[PromptType]): console (Console, optional): A Console instance or None to use global console. Defaults to None. password (bool, optional): Enable password input. Defaults to False. choices (List[str], optional): A list of valid choices. Defaults to None. + case_sensitive (bool, optional): Matching of choices should be case-sensitive. Defaults to True. show_default (bool, optional): Show default in prompt. Defaults to True. show_choices (bool, optional): Show choices in prompt. Defaults to True. """ @@ -57,6 +58,7 @@ class PromptBase(Generic[PromptType]): console: Optional[Console] = None, password: bool = False, choices: Optional[List[str]] = None, + case_sensitive: bool = True, show_default: bool = True, show_choices: bool = True, ) -> None: @@ -69,6 +71,7 @@ class PromptBase(Generic[PromptType]): self.password = password if choices is not None: self.choices = choices + self.case_sensitive = case_sensitive self.show_default = show_default self.show_choices = show_choices @@ -81,6 +84,7 @@ class PromptBase(Generic[PromptType]): console: Optional[Console] = None, password: bool = False, choices: Optional[List[str]] = None, + case_sensitive: bool = True, show_default: bool = True, show_choices: bool = True, default: DefaultType, @@ -97,6 +101,7 @@ class PromptBase(Generic[PromptType]): console: Optional[Console] = None, password: bool = False, choices: Optional[List[str]] = None, + case_sensitive: bool = True, show_default: bool = True, show_choices: bool = True, stream: Optional[TextIO] = None, @@ -111,6 +116,7 @@ class PromptBase(Generic[PromptType]): console: Optional[Console] = None, password: bool = False, choices: Optional[List[str]] = None, + case_sensitive: bool = True, show_default: bool = True, show_choices: bool = True, default: Any = ..., @@ -126,6 +132,7 @@ class PromptBase(Generic[PromptType]): console (Console, optional): A Console instance or None to use global console. Defaults to None. password (bool, optional): Enable password input. Defaults to False. choices (List[str], optional): A list of valid choices. Defaults to None. + case_sensitive (bool, optional): Matching of choices should be case-sensitive. Defaults to True. show_default (bool, optional): Show default in prompt. Defaults to True. show_choices (bool, optional): Show choices in prompt. Defaults to True. stream (TextIO, optional): Optional text file open for reading to get input. Defaults to None. @@ -135,6 +142,7 @@ class PromptBase(Generic[PromptType]): console=console, password=password, choices=choices, + case_sensitive=case_sensitive, show_default=show_default, show_choices=show_choices, ) @@ -212,7 +220,9 @@ class PromptBase(Generic[PromptType]): bool: True if choice was valid, otherwise False. """ assert self.choices is not None - return value.strip() in self.choices + if self.case_sensitive: + return value.strip() in self.choices + return value.strip().lower() in [choice.lower() for choice in self.choices] def process_response(self, value: str) -> PromptType: """Process response from user, convert to prompt type. @@ -232,9 +242,17 @@ class PromptBase(Generic[PromptType]): except ValueError: raise InvalidResponse(self.validate_error_message) - if self.choices is not None and not self.check_choice(value): - raise InvalidResponse(self.illegal_choice_message) + if self.choices is not None: + if not self.check_choice(value): + raise InvalidResponse(self.illegal_choice_message) + if not self.case_sensitive: + # return the original choice, not the lower case version + return_value = self.response_type( + self.choices[ + [choice.lower() for choice in self.choices].index(value.lower()) + ] + ) return return_value def on_validate_error(self, value: str, error: InvalidResponse) -> None: @@ -307,7 +325,7 @@ class IntPrompt(PromptBase[int]): validate_error_message = "[prompt.invalid]Please enter a valid integer number" -class FloatPrompt(PromptBase[int]): +class FloatPrompt(PromptBase[float]): """A prompt that returns a float. Example: @@ -346,7 +364,6 @@ class Confirm(PromptBase[bool]): if __name__ == "__main__": # pragma: no cover - from pip._vendor.rich import print if Confirm.ask("Run [i]prompt[/i] tests?", default=True): @@ -372,5 +389,12 @@ if __name__ == "__main__": # pragma: no cover fruit = Prompt.ask("Enter a fruit", choices=["apple", "orange", "pear"]) print(f"fruit={fruit!r}") + doggie = Prompt.ask( + "What's the best Dog? (Case INSENSITIVE)", + choices=["Border Terrier", "Collie", "Labradoodle"], + case_sensitive=False, + ) + print(f"doggie={doggie!r}") + else: print("[b]OK :loudly_crying_face:") diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/rich/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/repr.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/repr.py index f284bcaf..10efc427 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/repr.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/repr.py @@ -76,7 +76,7 @@ def auto( param.POSITIONAL_OR_KEYWORD, param.KEYWORD_ONLY, ): - if param.default == param.empty: + if param.default is param.empty: yield getattr(self, param.name) else: yield param.name, getattr(self, param.name), param.default diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/segment.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/segment.py index e1257984..4b5f9979 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/segment.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/segment.py @@ -109,42 +109,51 @@ class Segment(NamedTuple): @classmethod @lru_cache(1024 * 16) def _split_cells(cls, segment: "Segment", cut: int) -> Tuple["Segment", "Segment"]: + """Split a segment in to two at a given cell position. + Note that splitting a double-width character, may result in that character turning + into two spaces. + + Args: + segment (Segment): A segment to split. + cut (int): A cell position to cut on. + + Returns: + A tuple of two segments. + """ text, style, control = segment _Segment = Segment - cell_length = segment.cell_length if cut >= cell_length: return segment, _Segment("", style, control) cell_size = get_character_cell_size - pos = int((cut / cell_length) * (len(text) - 1)) + pos = int((cut / cell_length) * len(text)) - before = text[:pos] - cell_pos = cell_len(before) - if cell_pos == cut: - return ( - _Segment(before, style, control), - _Segment(text[pos:], style, control), - ) - while pos < len(text): - char = text[pos] - pos += 1 - cell_pos += cell_size(char) + while True: before = text[:pos] - if cell_pos == cut: + cell_pos = cell_len(before) + out_by = cell_pos - cut + if not out_by: return ( _Segment(before, style, control), _Segment(text[pos:], style, control), ) - if cell_pos > cut: + if out_by == -1 and cell_size(text[pos]) == 2: return ( - _Segment(before[: pos - 1] + " ", style, control), + _Segment(text[:pos] + " ", style, control), + _Segment(" " + text[pos + 1 :], style, control), + ) + if out_by == +1 and cell_size(text[pos - 1]) == 2: + return ( + _Segment(text[: pos - 1] + " ", style, control), _Segment(" " + text[pos:], style, control), ) - - raise AssertionError("Will never reach here") + if cell_pos < cut: + pos += 1 + else: + pos -= 1 def split_cells(self, cut: int) -> Tuple["Segment", "Segment"]: """Split segment in to two segments at the specified column. @@ -152,10 +161,14 @@ class Segment(NamedTuple): If the cut point falls in the middle of a 2-cell wide character then it is replaced by two spaces, to preserve the display width of the parent segment. + Args: + cut (int): Offset within the segment to cut. + Returns: Tuple[Segment, Segment]: Two segments. """ text, style, control = self + assert cut >= 0 if _is_single_cell_widths(text): # Fast path with all 1 cell characters @@ -605,7 +618,7 @@ class Segment(NamedTuple): while True: cut = next(iter_cuts, -1) if cut == -1: - return [] + return if cut != 0: break yield [] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/spinner.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/spinner.py index 91ea630e..a3a3caf8 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/spinner.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/spinner.py @@ -1,4 +1,4 @@ -from typing import cast, List, Optional, TYPE_CHECKING, Union +from typing import TYPE_CHECKING, List, Optional, Union, cast from ._spinners import SPINNERS from .measure import Measurement @@ -6,7 +6,7 @@ from .table import Table from .text import Text if TYPE_CHECKING: - from .console import Console, ConsoleOptions, RenderResult, RenderableType + from .console import Console, ConsoleOptions, RenderableType, RenderResult from .style import StyleType @@ -38,6 +38,7 @@ class Spinner: self.text: "Union[RenderableType, Text]" = ( Text.from_markup(text) if isinstance(text, str) else text ) + self.name = name self.frames = cast(List[str], spinner["frames"])[:] self.interval = cast(float, spinner["interval"]) self.start_time: Optional[float] = None @@ -116,22 +117,16 @@ class Spinner: if __name__ == "__main__": # pragma: no cover from time import sleep - from .columns import Columns - from .panel import Panel + from .console import Group from .live import Live - all_spinners = Columns( - [ + all_spinners = Group( + *[ Spinner(spinner_name, text=Text(repr(spinner_name), style="green")) for spinner_name in sorted(SPINNERS.keys()) - ], - column_first=True, - expand=True, + ] ) - with Live( - Panel(all_spinners, title="Spinners", border_style="blue"), - refresh_per_second=20, - ) as live: + with Live(all_spinners, refresh_per_second=20) as live: while True: sleep(0.1) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/status.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/status.py index 09eff405..65744838 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/status.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/status.py @@ -107,7 +107,6 @@ class Status(JupyterMixin): if __name__ == "__main__": # pragma: no cover - from time import sleep from .console import Console diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/style.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/style.py index 313c8894..52942418 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/style.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/style.py @@ -1,6 +1,7 @@ import sys from functools import lru_cache -from marshal import dumps, loads +from operator import attrgetter +from pickle import dumps, loads from random import randint from typing import Any, Dict, Iterable, List, Optional, Type, Union, cast @@ -9,6 +10,10 @@ from .color import Color, ColorParseError, ColorSystem, blend_rgb from .repr import Result, rich_repr from .terminal_theme import DEFAULT_TERMINAL_THEME, TerminalTheme +_hash_getter = attrgetter( + "_color", "_bgcolor", "_attributes", "_set_attributes", "_link", "_meta" +) + # Style instances and style definitions are often interchangeable StyleType = Union[str, "Style"] @@ -432,16 +437,7 @@ class Style: def __hash__(self) -> int: if self._hash is not None: return self._hash - self._hash = hash( - ( - self._color, - self._bgcolor, - self._attributes, - self._set_attributes, - self._link, - self._meta, - ) - ) + self._hash = hash(_hash_getter(self)) return self._hash @property @@ -524,7 +520,7 @@ class Style: if not word: raise errors.StyleSyntaxError("color expected after 'on'") try: - Color.parse(word) is None + Color.parse(word) except ColorParseError as error: raise errors.StyleSyntaxError( f"unable to parse {word!r} as background color; {error}" @@ -663,7 +659,7 @@ class Style: style._set_attributes = self._set_attributes style._link = None style._link_id = "" - style._hash = self._hash + style._hash = None style._null = False style._meta = None return style diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/syntax.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/syntax.py index 57033766..ec0d903e 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/syntax.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/syntax.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import os.path -import platform import re import sys import textwrap @@ -52,7 +53,7 @@ from .text import Text TokenType = Tuple[str, ...] -WINDOWS = platform.system() == "Windows" +WINDOWS = sys.platform == "win32" DEFAULT_THEME = "monokai" # The following styles are based on https://github.com/pygments/pygments/blob/master/pygments/formatters/terminal.py @@ -222,6 +223,18 @@ class _SyntaxHighlightRange(NamedTuple): style: StyleType start: SyntaxPosition end: SyntaxPosition + style_before: bool = False + + +class PaddingProperty: + """Descriptor to get and set padding.""" + + def __get__(self, obj: Syntax, objtype: Type[Syntax]) -> Tuple[int, int, int, int]: + """Space around the Syntax.""" + return obj._padding + + def __set__(self, obj: Syntax, padding: PaddingDimensions) -> None: + obj._padding = Padding.unpack(padding) class Syntax(JupyterMixin): @@ -293,11 +306,13 @@ class Syntax(JupyterMixin): Style(bgcolor=background_color) if background_color else Style() ) self.indent_guides = indent_guides - self.padding = padding + self._padding = Padding.unpack(padding) self._theme = self.get_theme(theme) self._stylized_ranges: List[_SyntaxHighlightRange] = [] + padding = PaddingProperty() + @classmethod def from_path( cls, @@ -371,8 +386,8 @@ class Syntax(JupyterMixin): is supplied, the lexer will be chosen based on the file extension.. Args: - path (AnyStr): The path to the file containing the code you wish to know the lexer for. - code (str, optional): Optional string of code that will be used as a fallback if no lexer + path (AnyStr): The path to the file containing the code you wish to know the lexer for. + code (str, optional): Optional string of code that will be used as a fallback if no lexer is found for the supplied path. Returns: @@ -439,6 +454,16 @@ class Syntax(JupyterMixin): except ClassNotFound: return None + @property + def default_lexer(self) -> Lexer: + """A Pygments Lexer to use if one is not specified or invalid.""" + return get_lexer_by_name( + "text", + stripnl=False, + ensurenl=True, + tabsize=self.tab_size, + ) + def highlight( self, code: str, @@ -467,7 +492,7 @@ class Syntax(JupyterMixin): ) _get_theme_style = self._theme.get_style_for_token - lexer = self.lexer + lexer = self.lexer or self.default_lexer if lexer is None: text.append(code) @@ -525,7 +550,11 @@ class Syntax(JupyterMixin): return text def stylize_range( - self, style: StyleType, start: SyntaxPosition, end: SyntaxPosition + self, + style: StyleType, + start: SyntaxPosition, + end: SyntaxPosition, + style_before: bool = False, ) -> None: """ Adds a custom style on a part of the code, that will be applied to the syntax display when it's rendered. @@ -535,8 +564,11 @@ class Syntax(JupyterMixin): style (StyleType): The style to apply. start (Tuple[int, int]): The start of the range, in the form `[line number, column index]`. end (Tuple[int, int]): The end of the range, in the form `[line number, column index]`. + style_before (bool): Apply the style before any existing styles. """ - self._stylized_ranges.append(_SyntaxHighlightRange(style, start, end)) + self._stylized_ranges.append( + _SyntaxHighlightRange(style, start, end, style_before) + ) def _get_line_numbers_color(self, blend: float = 0.3) -> Color: background_style = self._theme.get_background_style() + self.background_style @@ -590,7 +622,7 @@ class Syntax(JupyterMixin): def __rich_measure__( self, console: "Console", options: "ConsoleOptions" ) -> "Measurement": - _, right, _, left = Padding.unpack(self.padding) + _, right, _, left = self.padding padding = left + right if self.code_width is not None: width = self.code_width + self._numbers_column_width + padding + 1 @@ -609,10 +641,8 @@ class Syntax(JupyterMixin): self, console: Console, options: ConsoleOptions ) -> RenderResult: segments = Segments(self._get_syntax(console, options)) - if self.padding: - yield Padding( - segments, style=self._theme.get_background_style(), pad=self.padding - ) + if any(self.padding): + yield Padding(segments, style=self._get_base_style(), pad=self.padding) else: yield segments @@ -625,15 +655,19 @@ class Syntax(JupyterMixin): Get the Segments for the Syntax object, excluding any vertical/horizontal padding """ transparent_background = self._get_base_style().transparent_background + _pad_top, pad_right, _pad_bottom, pad_left = self.padding + horizontal_padding = pad_left + pad_right code_width = ( ( (options.max_width - self._numbers_column_width - 1) if self.line_numbers else options.max_width ) + - horizontal_padding if self.code_width is None else self.code_width ) + code_width = max(0, code_width) ends_on_nl, processed_code = self._process_code(self.code) text = self.highlight(processed_code, self.line_range) @@ -778,7 +812,10 @@ class Syntax(JupyterMixin): newlines_offsets, stylized_range.end ) if start is not None and end is not None: - text.stylize(stylized_range.style, start, end) + if stylized_range.style_before: + text.stylize_before(stylized_range.style, start, end) + else: + text.stylize(stylized_range.style, start, end) def _process_code(self, code: str) -> Tuple[bool, str]: """ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/table.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/table.py index 17409f2e..2b9e3b5d 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/table.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/table.py @@ -54,7 +54,7 @@ class Column: show_footer (bool, optional): Show a footer row. Defaults to False. show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True. show_lines (bool, optional): Draw lines between every row. Defaults to False. - leading (bool, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0. + leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0. style (Union[str, Style], optional): Default style for the table. Defaults to "none". row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None. header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header". @@ -106,6 +106,9 @@ class Column: no_wrap: bool = False """bool: Prevent wrapping of text within the column. Defaults to ``False``.""" + highlight: bool = False + """bool: Apply highlighter to column. Defaults to ``False``.""" + _index: int = 0 """Index of column.""" @@ -167,7 +170,7 @@ class Table(JupyterMixin): show_footer (bool, optional): Show a footer row. Defaults to False. show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True. show_lines (bool, optional): Draw lines between every row. Defaults to False. - leading (bool, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0. + leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0. style (Union[str, Style], optional): Default style for the table. Defaults to "none". row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None. header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header". @@ -212,7 +215,6 @@ class Table(JupyterMixin): caption_justify: "JustifyMethod" = "center", highlight: bool = False, ) -> None: - self.columns: List[Column] = [] self.rows: List[Row] = [] self.title = title @@ -366,6 +368,7 @@ class Table(JupyterMixin): footer: "RenderableType" = "", *, header_style: Optional[StyleType] = None, + highlight: Optional[bool] = None, footer_style: Optional[StyleType] = None, style: Optional[StyleType] = None, justify: "JustifyMethod" = "left", @@ -385,6 +388,7 @@ class Table(JupyterMixin): footer (RenderableType, optional): Text or renderable for the footer. Defaults to "". header_style (Union[str, Style], optional): Style for the header, or None for default. Defaults to None. + highlight (bool, optional): Whether to highlight the text. The default of None uses the value of the table (self) object. footer_style (Union[str, Style], optional): Style for the footer, or None for default. Defaults to None. style (Union[str, Style], optional): Style for the column cells, or None for default. Defaults to None. justify (JustifyMethod, optional): Alignment for cells. Defaults to "left". @@ -402,6 +406,7 @@ class Table(JupyterMixin): header=header, footer=footer, header_style=header_style or "", + highlight=highlight if highlight is not None else self.highlight, footer_style=footer_style or "", style=style or "", justify=justify, @@ -446,7 +451,7 @@ class Table(JupyterMixin): ] for index, renderable in enumerate(cell_renderables): if index == len(columns): - column = Column(_index=index) + column = Column(_index=index, highlight=self.highlight) for _ in self.rows: add_cell(column, Text("")) self.columns.append(column) @@ -471,7 +476,6 @@ class Table(JupyterMixin): def __rich_console__( self, console: "Console", options: "ConsoleOptions" ) -> "RenderResult": - if not self.columns: yield Segment("\n") return @@ -685,7 +689,7 @@ class Table(JupyterMixin): getattr(renderable, "vertical", None) or column.vertical, ) else: - for (style, renderable) in raw_cells: + for style, renderable in raw_cells: yield _Cell( style, renderable, @@ -777,16 +781,16 @@ class Table(JupyterMixin): _Segment(_box.head_right, border_style), _Segment(_box.head_vertical, border_style), ), - ( - _Segment(_box.foot_left, border_style), - _Segment(_box.foot_right, border_style), - _Segment(_box.foot_vertical, border_style), - ), ( _Segment(_box.mid_left, border_style), _Segment(_box.mid_right, border_style), _Segment(_box.mid_vertical, border_style), ), + ( + _Segment(_box.foot_left, border_style), + _Segment(_box.foot_right, border_style), + _Segment(_box.foot_vertical, border_style), + ), ] if show_edge: yield _Segment(_box.get_top(widths), border_style) @@ -820,6 +824,7 @@ class Table(JupyterMixin): no_wrap=column.no_wrap, overflow=column.overflow, height=None, + highlight=column.highlight, ) lines = console.render_lines( cell.renderable, @@ -924,7 +929,6 @@ class Table(JupyterMixin): if __name__ == "__main__": # pragma: no cover from pip._vendor.rich.console import Console from pip._vendor.rich.highlighter import ReprHighlighter - from pip._vendor.rich.table import Table as Table from ._timer import timer diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/text.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/text.py index 998cb87d..5a0c6b14 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/text.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/text.py @@ -11,6 +11,7 @@ from typing import ( List, NamedTuple, Optional, + Pattern, Tuple, Union, ) @@ -38,6 +39,7 @@ DEFAULT_OVERFLOW: "OverflowMethod" = "fold" _re_whitespace = re.compile(r"\s+$") TextType = Union[str, "Text"] +"""A plain string or a :class:`Text` instance.""" GetStyleCallable = Callable[[str], Optional[StyleType]] @@ -97,6 +99,21 @@ class Span(NamedTuple): return self return Span(start, min(offset, end), style) + def extend(self, cells: int) -> "Span": + """Extend the span by the given number of cells. + + Args: + cells (int): Additional space to add to end of span. + + Returns: + Span: A span. + """ + if cells: + start, end, style = self + return Span(start, end + cells, style) + else: + return self + class Text(JupyterMixin): """Text with color / style. @@ -108,7 +125,7 @@ class Text(JupyterMixin): overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None. no_wrap (bool, optional): Disable text wrapping, or None for default. Defaults to None. end (str, optional): Character to end text with. Defaults to "\\\\n". - tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8. + tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to None. spans (List[Span], optional). A list of predefined style spans. Defaults to None. """ @@ -133,7 +150,7 @@ class Text(JupyterMixin): overflow: Optional["OverflowMethod"] = None, no_wrap: Optional[bool] = None, end: str = "\n", - tab_size: Optional[int] = 8, + tab_size: Optional[int] = None, spans: Optional[List[Span]] = None, ) -> None: sanitized_text = strip_control_codes(text) @@ -157,7 +174,7 @@ class Text(JupyterMixin): return self.plain def __repr__(self) -> str: - return f"" + return f"" def __add__(self, other: Any) -> "Text": if isinstance(other, (str, Text)): @@ -255,7 +272,9 @@ class Text(JupyterMixin): Args: text (str): A string containing console markup. + style (Union[str, Style], optional): Base style for text. Defaults to "". emoji (bool, optional): Also render emoji code. Defaults to True. + emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None. justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None. overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None. end (str, optional): Character to end text with. Defaults to "\\\\n". @@ -292,7 +311,7 @@ class Text(JupyterMixin): overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None. no_wrap (bool, optional): Disable text wrapping, or None for default. Defaults to None. end (str, optional): Character to end text with. Defaults to "\\\\n". - tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8. + tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to None. """ from .ansi import AnsiDecoder @@ -353,8 +372,9 @@ class Text(JupyterMixin): style (Union[str, Style], optional): Base style for text. Defaults to "". justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None. overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None. + no_wrap (bool, optional): Disable text wrapping, or None for default. Defaults to None. end (str, optional): Character to end text with. Defaults to "\\\\n". - tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8. + tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to None. meta (Dict[str, Any], optional). Meta data to apply to text, or None for no meta data. Default to None Returns: @@ -408,7 +428,7 @@ class Text(JupyterMixin): self._spans = spans[:] def blank_copy(self, plain: str = "") -> "Text": - """Return a new Text instance with copied meta data (but not the string or spans).""" + """Return a new Text instance with copied metadata (but not the string or spans).""" copy_self = Text( plain, style=self.style, @@ -489,7 +509,7 @@ class Text(JupyterMixin): def apply_meta( self, meta: Dict[str, Any], start: int = 0, end: Optional[int] = None ) -> None: - """Apply meta data to the text, or a portion of the text. + """Apply metadata to the text, or a portion of the text. Args: meta (Dict[str, Any]): A dict of meta information. @@ -549,9 +569,30 @@ class Text(JupyterMixin): style += get_style(span_style, default="") return style + def extend_style(self, spaces: int) -> None: + """Extend the Text given number of spaces where the spaces have the same style as the last character. + + Args: + spaces (int): Number of spaces to add to the Text. + """ + if spaces <= 0: + return + spans = self.spans + new_spaces = " " * spaces + if spans: + end_offset = len(self) + self._spans[:] = [ + span.extend(spaces) if span.end >= end_offset else span + for span in spans + ] + self._text.append(new_spaces) + self._length += spaces + else: + self.plain += new_spaces + def highlight_regex( self, - re_highlight: str, + re_highlight: Union[Pattern[str], str], style: Optional[Union[GetStyleCallable, StyleType]] = None, *, style_prefix: str = "", @@ -560,7 +601,7 @@ class Text(JupyterMixin): translated to styles. Args: - re_highlight (str): A regular expression. + re_highlight (Union[re.Pattern, str]): A regular expression object or string. style (Union[GetStyleCallable, StyleType]): Optional style to apply to whole match, or a callable which accepts the matched text and returns a style. Defaults to None. style_prefix (str, optional): Optional prefix to add to style group names. @@ -572,7 +613,9 @@ class Text(JupyterMixin): append_span = self._spans.append _Span = Span plain = self.plain - for match in re.finditer(re_highlight, plain): + if isinstance(re_highlight, str): + re_highlight = re.compile(re_highlight) + for match in re_highlight.finditer(plain): get_span = match.span if style: start, end = get_span() @@ -597,9 +640,9 @@ class Text(JupyterMixin): """Highlight words with a style. Args: - words (Iterable[str]): Worlds to highlight. + words (Iterable[str]): Words to highlight. style (Union[str, Style]): Style to apply. - case_sensitive (bool, optional): Enable case sensitive matchings. Defaults to True. + case_sensitive (bool, optional): Enable case sensitive matching. Defaults to True. Returns: int: Number of words highlighted. @@ -646,7 +689,7 @@ class Text(JupyterMixin): def __rich_console__( self, console: "Console", options: "ConsoleOptions" ) -> Iterable[Segment]: - tab_size: int = console.tab_size or self.tab_size or 8 + tab_size: int = console.tab_size if self.tab_size is None else self.tab_size justify = self.justify or options.justify or DEFAULT_JUSTIFY overflow = self.overflow or options.overflow or DEFAULT_OVERFLOW @@ -781,27 +824,35 @@ class Text(JupyterMixin): """ if "\t" not in self.plain: return - pos = 0 if tab_size is None: tab_size = self.tab_size - assert tab_size is not None - result = self.blank_copy() - append = result.append + if tab_size is None: + tab_size = 8 + + new_text: List[Text] = [] + append = new_text.append - _style = self.style for line in self.split("\n", include_separator=True): - parts = line.split("\t", include_separator=True) - for part in parts: - if part.plain.endswith("\t"): - part._text = [part.plain[:-1] + " "] - append(part) - pos += len(part) - spaces = tab_size - ((pos - 1) % tab_size) - 1 - if spaces: - append(" " * spaces, _style) - pos += spaces - else: + if "\t" not in line.plain: + append(line) + else: + cell_position = 0 + parts = line.split("\t", include_separator=True) + for part in parts: + if part.plain.endswith("\t"): + part._text[-1] = part._text[-1][:-1] + " " + cell_position += part.cell_len + tab_remainder = cell_position % tab_size + if tab_remainder: + spaces = tab_size - tab_remainder + part.extend_style(spaces) + cell_position += spaces + else: + cell_position += part.cell_len append(part) + + result = Text("").join(new_text) + self._text = [result.plain] self._length = len(self.plain) self._spans[:] = result._spans @@ -852,6 +903,7 @@ class Text(JupyterMixin): Args: count (int): Width of padding. + character (str): The character to pad with. Must be a string of length 1. """ assert len(character) == 1, "Character must be a string of length 1" if count: @@ -932,7 +984,7 @@ class Text(JupyterMixin): self._text.append(sanitized_text) offset = len(self) text_length = len(sanitized_text) - if style is not None: + if style: self._spans.append(Span(offset, offset + text_length, style)) self._length += text_length elif isinstance(text, Text): @@ -942,14 +994,14 @@ class Text(JupyterMixin): "style must not be set when appending Text instance" ) text_length = self._length - if text.style is not None: + if text.style: self._spans.append( _Span(text_length, text_length + len(text), text.style) ) self._text.append(text.plain) self._spans.extend( _Span(start + text_length, end + text_length, style) - for start, end, style in text._spans + for start, end, style in text._spans.copy() ) self._length += len(text) return self @@ -958,17 +1010,20 @@ class Text(JupyterMixin): """Append another Text instance. This method is more performant that Text.append, but only works for Text. + Args: + text (Text): The Text instance to append to this instance. + Returns: Text: Returns self for chaining. """ _Span = Span text_length = self._length - if text.style is not None: + if text.style: self._spans.append(_Span(text_length, text_length + len(text), text.style)) self._text.append(text.plain) self._spans.extend( _Span(start + text_length, end + text_length, style) - for start, end, style in text._spans + for start, end, style in text._spans.copy() ) self._length += len(text) return self @@ -979,7 +1034,7 @@ class Text(JupyterMixin): """Append iterable of str and style. Style may be a Style instance or a str style definition. Args: - pairs (Iterable[Tuple[str, Optional[StyleType]]]): An iterable of tuples containing str content and style. + tokens (Iterable[Tuple[str, Optional[StyleType]]]): An iterable of tuples containing str content and style. Returns: Text: Returns self for chaining. @@ -989,8 +1044,9 @@ class Text(JupyterMixin): _Span = Span offset = len(self) for content, style in tokens: + content = strip_control_codes(content) append_text(content) - if style is not None: + if style: append_span(_Span(offset, offset + len(content), style)) offset += len(content) self._length = offset @@ -1088,7 +1144,6 @@ class Text(JupyterMixin): _Span = Span for span_start, span_end, style in self._spans: - lower_bound = 0 upper_bound = line_count start_line_no = (lower_bound + upper_bound) // 2 @@ -1158,8 +1213,7 @@ class Text(JupyterMixin): Args: console (Console): Console instance. - width (int): Number of characters per line. - emoji (bool, optional): Also render emoji code. Defaults to True. + width (int): Number of cells available per line. justify (str, optional): Justify method: "default", "left", "center", "full", "right". Defaults to "default". overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None. tab_size (int, optional): Default tab size. Defaults to 8. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/theme.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/theme.py index 471dfb2f..227f1d86 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/theme.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/theme.py @@ -1,5 +1,5 @@ import configparser -from typing import Dict, List, IO, Mapping, Optional +from typing import IO, Dict, List, Mapping, Optional from .default_styles import DEFAULT_STYLES from .style import Style, StyleType @@ -69,7 +69,7 @@ class Theme: Returns: Theme: A new theme instance. """ - with open(path, "rt", encoding=encoding) as config_file: + with open(path, encoding=encoding) as config_file: return cls.from_file(config_file, source=path, inherit=inherit) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/traceback.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/traceback.py index c4ffe1f9..01b2f502 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/traceback.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/traceback.py @@ -1,10 +1,9 @@ -from __future__ import absolute_import - +import inspect import linecache import os -import platform import sys from dataclasses import dataclass, field +from itertools import islice from traceback import walk_tb from types import ModuleType, TracebackType from typing import ( @@ -15,6 +14,7 @@ from typing import ( List, Optional, Sequence, + Set, Tuple, Type, Union, @@ -27,28 +27,64 @@ from pip._vendor.pygments.token import Token from pip._vendor.pygments.util import ClassNotFound from . import pretty -from ._loop import loop_last +from ._loop import loop_first_last, loop_last from .columns import Columns -from .console import Console, ConsoleOptions, ConsoleRenderable, RenderResult, group +from .console import ( + Console, + ConsoleOptions, + ConsoleRenderable, + Group, + RenderResult, + group, +) from .constrain import Constrain from .highlighter import RegexHighlighter, ReprHighlighter from .panel import Panel from .scope import render_scope from .style import Style -from .syntax import Syntax +from .syntax import Syntax, SyntaxPosition from .text import Text from .theme import Theme -WINDOWS = platform.system() == "Windows" +WINDOWS = sys.platform == "win32" LOCALS_MAX_LENGTH = 10 LOCALS_MAX_STRING = 80 +def _iter_syntax_lines( + start: SyntaxPosition, end: SyntaxPosition +) -> Iterable[Tuple[int, int, int]]: + """Yield start and end positions per line. + + Args: + start: Start position. + end: End position. + + Returns: + Iterable of (LINE, COLUMN1, COLUMN2). + """ + + line1, column1 = start + line2, column2 = end + + if line1 == line2: + yield line1, column1, column2 + else: + for first, last, line_no in loop_first_last(range(line1, line2 + 1)): + if first: + yield line_no, column1, -1 + elif last: + yield line_no, 0, column2 + else: + yield line_no, 0, -1 + + def install( *, console: Optional[Console] = None, width: Optional[int] = 100, + code_width: Optional[int] = 88, extra_lines: int = 3, theme: Optional[str] = None, word_wrap: bool = False, @@ -69,6 +105,7 @@ def install( Args: console (Optional[Console], optional): Console to write exception to. Default uses internal Console instance. width (Optional[int], optional): Width (in characters) of traceback. Defaults to 100. + code_width (Optional[int], optional): Code width (in characters) of traceback. Defaults to 88. extra_lines (int, optional): Extra lines of code. Defaults to 3. theme (Optional[str], optional): Pygments theme to use in traceback. Defaults to ``None`` which will pick a theme appropriate for the platform. @@ -99,25 +136,25 @@ def install( value: BaseException, traceback: Optional[TracebackType], ) -> None: - traceback_console.print( - Traceback.from_exception( - type_, - value, - traceback, - width=width, - extra_lines=extra_lines, - theme=theme, - word_wrap=word_wrap, - show_locals=show_locals, - locals_max_length=locals_max_length, - locals_max_string=locals_max_string, - locals_hide_dunder=locals_hide_dunder, - locals_hide_sunder=bool(locals_hide_sunder), - indent_guides=indent_guides, - suppress=suppress, - max_frames=max_frames, - ) + exception_traceback = Traceback.from_exception( + type_, + value, + traceback, + width=width, + code_width=code_width, + extra_lines=extra_lines, + theme=theme, + word_wrap=word_wrap, + show_locals=show_locals, + locals_max_length=locals_max_length, + locals_max_string=locals_max_string, + locals_hide_dunder=locals_hide_dunder, + locals_hide_sunder=bool(locals_hide_sunder), + indent_guides=indent_guides, + suppress=suppress, + max_frames=max_frames, ) + traceback_console.print(exception_traceback) def ipy_excepthook_closure(ip: Any) -> None: # pragma: no cover tb_data = {} # store information about showtraceback call @@ -141,7 +178,9 @@ def install( # determine correct tb_offset compiled = tb_data.get("running_compiled_code", False) - tb_offset = tb_data.get("tb_offset", 1 if compiled else 0) + tb_offset = tb_data.get("tb_offset") + if tb_offset is None: + tb_offset = 1 if compiled else 0 # remove ipython internal frames from trace with tb_offset for _ in range(tb_offset): if tb is None: @@ -179,6 +218,7 @@ class Frame: name: str line: str = "" locals: Optional[Dict[str, pretty.Node]] = None + last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] = None @dataclass @@ -188,6 +228,7 @@ class _SyntaxError: line: str lineno: int msg: str + notes: List[str] = field(default_factory=list) @dataclass @@ -197,6 +238,9 @@ class Stack: syntax_error: Optional[_SyntaxError] = None is_cause: bool = False frames: List[Frame] = field(default_factory=list) + notes: List[str] = field(default_factory=list) + is_group: bool = False + exceptions: List["Trace"] = field(default_factory=list) @dataclass @@ -215,6 +259,7 @@ class Traceback: trace (Trace, optional): A `Trace` object produced from `extract`. Defaults to None, which uses the last exception. width (Optional[int], optional): Number of characters used to traceback. Defaults to 100. + code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88. extra_lines (int, optional): Additional lines of code to render. Defaults to 3. theme (str, optional): Override pygments theme used in traceback. word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False. @@ -243,6 +288,7 @@ class Traceback: trace: Optional[Trace] = None, *, width: Optional[int] = 100, + code_width: Optional[int] = 88, extra_lines: int = 3, theme: Optional[str] = None, word_wrap: bool = False, @@ -266,6 +312,7 @@ class Traceback: ) self.trace = trace self.width = width + self.code_width = code_width self.extra_lines = extra_lines self.theme = Syntax.get_theme(theme or "ansi_dark") self.word_wrap = word_wrap @@ -297,6 +344,7 @@ class Traceback: traceback: Optional[TracebackType], *, width: Optional[int] = 100, + code_width: Optional[int] = 88, extra_lines: int = 3, theme: Optional[str] = None, word_wrap: bool = False, @@ -316,6 +364,7 @@ class Traceback: exc_value (BaseException): Exception value. traceback (TracebackType): Python Traceback object. width (Optional[int], optional): Number of characters used to traceback. Defaults to 100. + code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88. extra_lines (int, optional): Additional lines of code to render. Defaults to 3. theme (str, optional): Override pygments theme used in traceback. word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False. @@ -346,6 +395,7 @@ class Traceback: return cls( rich_traceback, width=width, + code_width=code_width, extra_lines=extra_lines, theme=theme, word_wrap=word_wrap, @@ -371,6 +421,7 @@ class Traceback: locals_max_string: int = LOCALS_MAX_STRING, locals_hide_dunder: bool = True, locals_hide_sunder: bool = False, + _visited_exceptions: Optional[Set[BaseException]] = None, ) -> Trace: """Extract traceback information. @@ -394,6 +445,12 @@ class Traceback: from pip._vendor.rich import _IMPORT_CWD + notes: List[str] = getattr(exc_value, "__notes__", None) or [] + + grouped_exceptions: Set[BaseException] = ( + set() if _visited_exceptions is None else _visited_exceptions + ) + def safe_str(_object: Any) -> str: """Don't allow exceptions from __str__ to propagate.""" try: @@ -406,8 +463,29 @@ class Traceback: exc_type=safe_str(exc_type.__name__), exc_value=safe_str(exc_value), is_cause=is_cause, + notes=notes, ) + if sys.version_info >= (3, 11): + if isinstance(exc_value, (BaseExceptionGroup, ExceptionGroup)): + stack.is_group = True + for exception in exc_value.exceptions: + if exception in grouped_exceptions: + continue + grouped_exceptions.add(exception) + stack.exceptions.append( + Traceback.extract( + type(exception), + exception, + exception.__traceback__, + show_locals=show_locals, + locals_max_length=locals_max_length, + locals_hide_dunder=locals_hide_dunder, + locals_hide_sunder=locals_hide_sunder, + _visited_exceptions=grouped_exceptions, + ) + ) + if isinstance(exc_value, SyntaxError): stack.syntax_error = _SyntaxError( offset=exc_value.offset or 0, @@ -415,13 +493,14 @@ class Traceback: lineno=exc_value.lineno or 0, line=exc_value.text or "", msg=exc_value.msg, + notes=notes, ) stacks.append(stack) append = stack.frames.append def get_locals( - iter_locals: Iterable[Tuple[str, object]] + iter_locals: Iterable[Tuple[str, object]], ) -> Iterable[Tuple[str, object]]: """Extract locals from an iterator of key pairs.""" if not (locals_hide_dunder or locals_hide_sunder): @@ -436,6 +515,35 @@ class Traceback: for frame_summary, line_no in walk_tb(traceback): filename = frame_summary.f_code.co_filename + + last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] + last_instruction = None + if sys.version_info >= (3, 11): + instruction_index = frame_summary.f_lasti // 2 + instruction_position = next( + islice( + frame_summary.f_code.co_positions(), + instruction_index, + instruction_index + 1, + ) + ) + ( + start_line, + end_line, + start_column, + end_column, + ) = instruction_position + if ( + start_line is not None + and end_line is not None + and start_column is not None + and end_column is not None + ): + last_instruction = ( + (start_line, start_column), + (end_line, end_column), + ) + if filename and not filename.startswith("<"): if not os.path.isabs(filename): filename = os.path.join(_IMPORT_CWD, filename) @@ -446,42 +554,50 @@ class Traceback: filename=filename or "?", lineno=line_no, name=frame_summary.f_code.co_name, - locals={ - key: pretty.traverse( - value, - max_length=locals_max_length, - max_string=locals_max_string, - ) - for key, value in get_locals(frame_summary.f_locals.items()) - } - if show_locals - else None, + locals=( + { + key: pretty.traverse( + value, + max_length=locals_max_length, + max_string=locals_max_string, + ) + for key, value in get_locals(frame_summary.f_locals.items()) + if not (inspect.isfunction(value) or inspect.isclass(value)) + } + if show_locals + else None + ), + last_instruction=last_instruction, ) append(frame) if frame_summary.f_locals.get("_rich_traceback_guard", False): del stack.frames[:] - cause = getattr(exc_value, "__cause__", None) - if cause: - exc_type = cause.__class__ - exc_value = cause - # __traceback__ can be None, e.g. for exceptions raised by the - # 'multiprocessing' module - traceback = cause.__traceback__ - is_cause = True - continue + if not grouped_exceptions: + cause = getattr(exc_value, "__cause__", None) + if cause is not None and cause is not exc_value: + exc_type = cause.__class__ + exc_value = cause + # __traceback__ can be None, e.g. for exceptions raised by the + # 'multiprocessing' module + traceback = cause.__traceback__ + is_cause = True + continue - cause = exc_value.__context__ - if cause and not getattr(exc_value, "__suppress_context__", False): - exc_type = cause.__class__ - exc_value = cause - traceback = cause.__traceback__ - is_cause = False - continue + cause = exc_value.__context__ + if cause is not None and not getattr( + exc_value, "__suppress_context__", False + ): + exc_type = cause.__class__ + exc_value = cause + traceback = cause.__traceback__ + is_cause = False + continue # No cover, code is reached but coverage doesn't recognize it. break # pragma: no cover trace = Trace(stacks=stacks) + return trace def __rich_console__( @@ -514,7 +630,9 @@ class Traceback: ) highlighter = ReprHighlighter() - for last, stack in loop_last(reversed(self.trace.stacks)): + + @group() + def render_stack(stack: Stack, last: bool) -> RenderResult: if stack.frames: stack_renderable: ConsoleRenderable = Panel( self._render_stack(stack), @@ -527,6 +645,7 @@ class Traceback: stack_renderable = Constrain(stack_renderable, self.width) with console.use_theme(traceback_theme): yield stack_renderable + if stack.syntax_error is not None: with console.use_theme(traceback_theme): yield Constrain( @@ -552,6 +671,24 @@ class Traceback: else: yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type")) + for note in stack.notes: + yield Text.assemble(("[NOTE] ", "traceback.note"), highlighter(note)) + + if stack.is_group: + for group_no, group_exception in enumerate(stack.exceptions, 1): + grouped_exceptions: List[Group] = [] + for group_last, group_stack in loop_last(group_exception.stacks): + grouped_exceptions.append(render_stack(group_stack, group_last)) + yield "" + yield Constrain( + Panel( + Group(*grouped_exceptions), + title=f"Sub-exception #{group_no}", + border_style="traceback.group.border", + ), + self.width, + ) + if not last: if stack.is_cause: yield Text.from_markup( @@ -562,6 +699,9 @@ class Traceback: "\n[i]During handling of the above exception, another exception occurred:\n", ) + for last, stack in loop_last(reversed(self.trace.stacks)): + yield render_stack(stack, last) + @group() def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult: highlighter = ReprHighlighter() @@ -606,17 +746,6 @@ class Traceback: path_highlighter = PathHighlighter() theme = self.theme - def read_code(filename: str) -> str: - """Read files, and cache results on filename. - - Args: - filename (str): Filename to read - - Returns: - str: Contents of file - """ - return "".join(linecache.getlines(filename)) - def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]: if frame.locals: yield render_scope( @@ -636,7 +765,6 @@ class Traceback: excluded = False for frame_index, frame in enumerate(stack.frames): - if exclude_frames and frame_index in exclude_frames: excluded = True continue @@ -679,7 +807,8 @@ class Traceback: continue if not suppressed: try: - code = read_code(frame.filename) + code_lines = linecache.getlines(frame.filename) + code = "".join(code_lines) if not code: # code may be an empty string if the file doesn't exist, OR # if the traceback filename is generated dynamically @@ -696,7 +825,7 @@ class Traceback: ), highlight_lines={frame.lineno}, word_wrap=self.word_wrap, - code_width=88, + code_width=self.code_width, indent_guides=self.indent_guides, dedent=False, ) @@ -706,6 +835,28 @@ class Traceback: (f"\n{error}", "traceback.error"), ) else: + if frame.last_instruction is not None: + start, end = frame.last_instruction + + # Stylize a line at a time + # So that indentation isn't underlined (which looks bad) + for line1, column1, column2 in _iter_syntax_lines(start, end): + try: + if column1 == 0: + line = code_lines[line1 - 1] + column1 = len(line) - len(line.lstrip()) + if column2 == -1: + column2 = len(code_lines[line1 - 1]) + except IndexError: + # Being defensive here + # If last_instruction reports a line out-of-bounds, we don't want to crash + continue + + syntax.stylize_range( + style="traceback.error_range", + start=(line1, column1), + end=(line1, column2), + ) yield ( Columns( [ @@ -720,13 +871,12 @@ class Traceback: if __name__ == "__main__": # pragma: no cover - - from .console import Console - - console = Console() + install(show_locals=True) import sys - def bar(a: Any) -> None: # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑 + def bar( + a: Any, + ) -> None: # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑 one = 1 print(one / a) @@ -744,13 +894,6 @@ if __name__ == "__main__": # pragma: no cover bar(a) def error() -> None: - - try: - try: - foo(0) - except: - slfkjsldkfj # type: ignore[name-defined] - except: - console.print_exception(show_locals=True) + foo(0) error() diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/rich/tree.py b/venv/lib/python3.12/site-packages/pip/_vendor/rich/tree.py index afe8da1a..27c5cf7b 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/rich/tree.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/rich/tree.py @@ -8,18 +8,32 @@ from .segment import Segment from .style import Style, StyleStack, StyleType from .styled import Styled +GuideType = Tuple[str, str, str, str] + class Tree(JupyterMixin): """A renderable for a tree structure. + Attributes: + ASCII_GUIDES (GuideType): Guide lines used when Console.ascii_only is True. + TREE_GUIDES (List[GuideType, GuideType, GuideType]): Default guide lines. + Args: label (RenderableType): The renderable or str for the tree label. style (StyleType, optional): Style of this tree. Defaults to "tree". guide_style (StyleType, optional): Style of the guide lines. Defaults to "tree.line". expanded (bool, optional): Also display children. Defaults to True. highlight (bool, optional): Highlight renderable (if str). Defaults to False. + hide_root (bool, optional): Hide the root node. Defaults to False. """ + ASCII_GUIDES = (" ", "| ", "+-- ", "`-- ") + TREE_GUIDES = [ + (" ", "│ ", "├── ", "└── "), + (" ", "┃ ", "┣━━ ", "┗━━ "), + (" ", "║ ", "╠══ ", "╚══ "), + ] + def __init__( self, label: RenderableType, @@ -72,7 +86,6 @@ class Tree(JupyterMixin): def __rich_console__( self, console: "Console", options: "ConsoleOptions" ) -> "RenderResult": - stack: List[Iterator[Tuple[bool, Tree]]] = [] pop = stack.pop push = stack.append @@ -83,21 +96,15 @@ class Tree(JupyterMixin): guide_style = get_style(self.guide_style, default="") or null_style SPACE, CONTINUE, FORK, END = range(4) - ASCII_GUIDES = (" ", "| ", "+-- ", "`-- ") - TREE_GUIDES = [ - (" ", "│ ", "├── ", "└── "), - (" ", "┃ ", "┣━━ ", "┗━━ "), - (" ", "║ ", "╠══ ", "╚══ "), - ] _Segment = Segment def make_guide(index: int, style: Style) -> Segment: """Make a Segment for a level of the guide lines.""" if options.ascii_only: - line = ASCII_GUIDES[index] + line = self.ASCII_GUIDES[index] else: guide = 1 if style.bold else (2 if style.underline2 else 0) - line = TREE_GUIDES[0 if options.legacy_windows else guide][index] + line = self.TREE_GUIDES[0 if options.legacy_windows else guide][index] return _Segment(line, style) levels: List[Segment] = [make_guide(CONTINUE, guide_style)] @@ -195,7 +202,6 @@ class Tree(JupyterMixin): if __name__ == "__main__": # pragma: no cover - from pip._vendor.rich.console import Group from pip._vendor.rich.markdown import Markdown from pip._vendor.rich.panel import Panel diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/six.py b/venv/lib/python3.12/site-packages/pip/_vendor/six.py deleted file mode 100644 index 4e15675d..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/six.py +++ /dev/null @@ -1,998 +0,0 @@ -# Copyright (c) 2010-2020 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Utilities for writing code that runs on Python 2 and 3""" - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.16.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - -if PY34: - from importlib.util import spec_from_loader -else: - spec_from_loader = None - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def find_spec(self, fullname, path, target=None): - if fullname in self.known_modules: - return spec_from_loader(fullname, self) - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - - def create_module(self, spec): - return self.load_module(spec.name) - - def exec_module(self, module): - pass - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("getoutput", "commands", "subprocess"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("splitvalue", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), - MovedAttribute("parse_http_list", "urllib2", "urllib.request"), - MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - del io - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" - _assertNotRegex = "assertNotRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -def assertNotRegex(self, *args, **kwargs): - return getattr(self, _assertNotRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - try: - raise tp, value, tb - finally: - tb = None -""") - - -if sys.version_info[:2] > (3,): - exec_("""def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - # This does exactly the same what the :func:`py3:functools.update_wrapper` - # function does on Python versions after 3.2. It sets the ``__wrapped__`` - # attribute on ``wrapper`` object and it doesn't raise an error if any of - # the attributes mentioned in ``assigned`` and ``updated`` are missing on - # ``wrapped`` object. - def _update_wrapper(wrapper, wrapped, - assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - for attr in assigned: - try: - value = getattr(wrapped, attr) - except AttributeError: - continue - else: - setattr(wrapper, attr, value) - for attr in updated: - getattr(wrapper, attr).update(getattr(wrapped, attr, {})) - wrapper.__wrapped__ = wrapped - return wrapper - _update_wrapper.__doc__ = functools.update_wrapper.__doc__ - - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - return functools.partial(_update_wrapper, wrapped=wrapped, - assigned=assigned, updated=updated) - wraps.__doc__ = functools.wraps.__doc__ - -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - if sys.version_info[:2] >= (3, 7): - # This version introduced PEP 560 that requires a bit - # of extra care (we mimic what is done by __build_class__). - resolved_bases = types.resolve_bases(bases) - if resolved_bases is not bases: - d['__orig_bases__'] = bases - else: - resolved_bases = bases - return meta(name, resolved_bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - if hasattr(cls, '__qualname__'): - orig_vars['__qualname__'] = cls.__qualname__ - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def ensure_binary(s, encoding='utf-8', errors='strict'): - """Coerce **s** to six.binary_type. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> encoded to `bytes` - - `bytes` -> `bytes` - """ - if isinstance(s, binary_type): - return s - if isinstance(s, text_type): - return s.encode(encoding, errors) - raise TypeError("not expecting type '%s'" % type(s)) - - -def ensure_str(s, encoding='utf-8', errors='strict'): - """Coerce *s* to `str`. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - # Optimization: Fast return for the common case. - if type(s) is str: - return s - if PY2 and isinstance(s, text_type): - return s.encode(encoding, errors) - elif PY3 and isinstance(s, binary_type): - return s.decode(encoding, errors) - elif not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) - return s - - -def ensure_text(s, encoding='utf-8', errors='strict'): - """Coerce *s* to six.text_type. - - For Python 2: - - `unicode` -> `unicode` - - `str` -> `unicode` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if isinstance(s, binary_type): - return s.decode(encoding, errors) - elif isinstance(s, text_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - -def python_2_unicode_compatible(klass): - """ - A class decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__init__.py deleted file mode 100644 index 4f1603ad..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__init__.py +++ /dev/null @@ -1,608 +0,0 @@ -# Copyright 2016-2018 Julien Danjou -# Copyright 2017 Elisey Zanko -# Copyright 2016 Étienne Bersac -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import functools -import sys -import threading -import time -import typing as t -import warnings -from abc import ABC, abstractmethod -from concurrent import futures -from inspect import iscoroutinefunction - -# Import all built-in retry strategies for easier usage. -from .retry import retry_base # noqa -from .retry import retry_all # noqa -from .retry import retry_always # noqa -from .retry import retry_any # noqa -from .retry import retry_if_exception # noqa -from .retry import retry_if_exception_type # noqa -from .retry import retry_if_exception_cause_type # noqa -from .retry import retry_if_not_exception_type # noqa -from .retry import retry_if_not_result # noqa -from .retry import retry_if_result # noqa -from .retry import retry_never # noqa -from .retry import retry_unless_exception_type # noqa -from .retry import retry_if_exception_message # noqa -from .retry import retry_if_not_exception_message # noqa - -# Import all nap strategies for easier usage. -from .nap import sleep # noqa -from .nap import sleep_using_event # noqa - -# Import all built-in stop strategies for easier usage. -from .stop import stop_after_attempt # noqa -from .stop import stop_after_delay # noqa -from .stop import stop_all # noqa -from .stop import stop_any # noqa -from .stop import stop_never # noqa -from .stop import stop_when_event_set # noqa - -# Import all built-in wait strategies for easier usage. -from .wait import wait_chain # noqa -from .wait import wait_combine # noqa -from .wait import wait_exponential # noqa -from .wait import wait_fixed # noqa -from .wait import wait_incrementing # noqa -from .wait import wait_none # noqa -from .wait import wait_random # noqa -from .wait import wait_random_exponential # noqa -from .wait import wait_random_exponential as wait_full_jitter # noqa -from .wait import wait_exponential_jitter # noqa - -# Import all built-in before strategies for easier usage. -from .before import before_log # noqa -from .before import before_nothing # noqa - -# Import all built-in after strategies for easier usage. -from .after import after_log # noqa -from .after import after_nothing # noqa - -# Import all built-in after strategies for easier usage. -from .before_sleep import before_sleep_log # noqa -from .before_sleep import before_sleep_nothing # noqa - -# Replace a conditional import with a hard-coded None so that pip does -# not attempt to use tornado even if it is present in the environment. -# If tornado is non-None, tenacity will attempt to execute some code -# that is sensitive to the version of tornado, which could break pip -# if an old version is found. -tornado = None # type: ignore - -if t.TYPE_CHECKING: - import types - - from .retry import RetryBaseT - from .stop import StopBaseT - from .wait import WaitBaseT - - -WrappedFnReturnT = t.TypeVar("WrappedFnReturnT") -WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Any]) - - -class TryAgain(Exception): - """Always retry the executed function when raised.""" - - -NO_RESULT = object() - - -class DoAttempt: - pass - - -class DoSleep(float): - pass - - -class BaseAction: - """Base class for representing actions to take by retry object. - - Concrete implementations must define: - - __init__: to initialize all necessary fields - - REPR_FIELDS: class variable specifying attributes to include in repr(self) - - NAME: for identification in retry object methods and callbacks - """ - - REPR_FIELDS: t.Sequence[str] = () - NAME: t.Optional[str] = None - - def __repr__(self) -> str: - state_str = ", ".join(f"{field}={getattr(self, field)!r}" for field in self.REPR_FIELDS) - return f"{self.__class__.__name__}({state_str})" - - def __str__(self) -> str: - return repr(self) - - -class RetryAction(BaseAction): - REPR_FIELDS = ("sleep",) - NAME = "retry" - - def __init__(self, sleep: t.SupportsFloat) -> None: - self.sleep = float(sleep) - - -_unset = object() - - -def _first_set(first: t.Union[t.Any, object], second: t.Any) -> t.Any: - return second if first is _unset else first - - -class RetryError(Exception): - """Encapsulates the last attempt instance right before giving up.""" - - def __init__(self, last_attempt: "Future") -> None: - self.last_attempt = last_attempt - super().__init__(last_attempt) - - def reraise(self) -> "t.NoReturn": - if self.last_attempt.failed: - raise self.last_attempt.result() - raise self - - def __str__(self) -> str: - return f"{self.__class__.__name__}[{self.last_attempt}]" - - -class AttemptManager: - """Manage attempt context.""" - - def __init__(self, retry_state: "RetryCallState"): - self.retry_state = retry_state - - def __enter__(self) -> None: - pass - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - traceback: t.Optional["types.TracebackType"], - ) -> t.Optional[bool]: - if exc_type is not None and exc_value is not None: - self.retry_state.set_exception((exc_type, exc_value, traceback)) - return True # Swallow exception. - else: - # We don't have the result, actually. - self.retry_state.set_result(None) - return None - - -class BaseRetrying(ABC): - def __init__( - self, - sleep: t.Callable[[t.Union[int, float]], None] = sleep, - stop: "StopBaseT" = stop_never, - wait: "WaitBaseT" = wait_none(), - retry: "RetryBaseT" = retry_if_exception_type(), - before: t.Callable[["RetryCallState"], None] = before_nothing, - after: t.Callable[["RetryCallState"], None] = after_nothing, - before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None, - reraise: bool = False, - retry_error_cls: t.Type[RetryError] = RetryError, - retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None, - ): - self.sleep = sleep - self.stop = stop - self.wait = wait - self.retry = retry - self.before = before - self.after = after - self.before_sleep = before_sleep - self.reraise = reraise - self._local = threading.local() - self.retry_error_cls = retry_error_cls - self.retry_error_callback = retry_error_callback - - def copy( - self, - sleep: t.Union[t.Callable[[t.Union[int, float]], None], object] = _unset, - stop: t.Union["StopBaseT", object] = _unset, - wait: t.Union["WaitBaseT", object] = _unset, - retry: t.Union[retry_base, object] = _unset, - before: t.Union[t.Callable[["RetryCallState"], None], object] = _unset, - after: t.Union[t.Callable[["RetryCallState"], None], object] = _unset, - before_sleep: t.Union[t.Optional[t.Callable[["RetryCallState"], None]], object] = _unset, - reraise: t.Union[bool, object] = _unset, - retry_error_cls: t.Union[t.Type[RetryError], object] = _unset, - retry_error_callback: t.Union[t.Optional[t.Callable[["RetryCallState"], t.Any]], object] = _unset, - ) -> "BaseRetrying": - """Copy this object with some parameters changed if needed.""" - return self.__class__( - sleep=_first_set(sleep, self.sleep), - stop=_first_set(stop, self.stop), - wait=_first_set(wait, self.wait), - retry=_first_set(retry, self.retry), - before=_first_set(before, self.before), - after=_first_set(after, self.after), - before_sleep=_first_set(before_sleep, self.before_sleep), - reraise=_first_set(reraise, self.reraise), - retry_error_cls=_first_set(retry_error_cls, self.retry_error_cls), - retry_error_callback=_first_set(retry_error_callback, self.retry_error_callback), - ) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__} object at 0x{id(self):x} (" - f"stop={self.stop}, " - f"wait={self.wait}, " - f"sleep={self.sleep}, " - f"retry={self.retry}, " - f"before={self.before}, " - f"after={self.after})>" - ) - - @property - def statistics(self) -> t.Dict[str, t.Any]: - """Return a dictionary of runtime statistics. - - This dictionary will be empty when the controller has never been - ran. When it is running or has ran previously it should have (but - may not) have useful and/or informational keys and values when - running is underway and/or completed. - - .. warning:: The keys in this dictionary **should** be some what - stable (not changing), but there existence **may** - change between major releases as new statistics are - gathered or removed so before accessing keys ensure that - they actually exist and handle when they do not. - - .. note:: The values in this dictionary are local to the thread - running call (so if multiple threads share the same retrying - object - either directly or indirectly) they will each have - there own view of statistics they have collected (in the - future we may provide a way to aggregate the various - statistics from each thread). - """ - try: - return self._local.statistics # type: ignore[no-any-return] - except AttributeError: - self._local.statistics = t.cast(t.Dict[str, t.Any], {}) - return self._local.statistics - - def wraps(self, f: WrappedFn) -> WrappedFn: - """Wrap a function for retrying. - - :param f: A function to wraps for retrying. - """ - - @functools.wraps(f) - def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any: - return self(f, *args, **kw) - - def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn: - return self.copy(*args, **kwargs).wraps(f) - - wrapped_f.retry = self # type: ignore[attr-defined] - wrapped_f.retry_with = retry_with # type: ignore[attr-defined] - - return wrapped_f # type: ignore[return-value] - - def begin(self) -> None: - self.statistics.clear() - self.statistics["start_time"] = time.monotonic() - self.statistics["attempt_number"] = 1 - self.statistics["idle_for"] = 0 - - def iter(self, retry_state: "RetryCallState") -> t.Union[DoAttempt, DoSleep, t.Any]: # noqa - fut = retry_state.outcome - if fut is None: - if self.before is not None: - self.before(retry_state) - return DoAttempt() - - is_explicit_retry = fut.failed and isinstance(fut.exception(), TryAgain) - if not (is_explicit_retry or self.retry(retry_state)): - return fut.result() - - if self.after is not None: - self.after(retry_state) - - self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start - if self.stop(retry_state): - if self.retry_error_callback: - return self.retry_error_callback(retry_state) - retry_exc = self.retry_error_cls(fut) - if self.reraise: - raise retry_exc.reraise() - raise retry_exc from fut.exception() - - if self.wait: - sleep = self.wait(retry_state) - else: - sleep = 0.0 - retry_state.next_action = RetryAction(sleep) - retry_state.idle_for += sleep - self.statistics["idle_for"] += sleep - self.statistics["attempt_number"] += 1 - - if self.before_sleep is not None: - self.before_sleep(retry_state) - - return DoSleep(sleep) - - def __iter__(self) -> t.Generator[AttemptManager, None, None]: - self.begin() - - retry_state = RetryCallState(self, fn=None, args=(), kwargs={}) - while True: - do = self.iter(retry_state=retry_state) - if isinstance(do, DoAttempt): - yield AttemptManager(retry_state=retry_state) - elif isinstance(do, DoSleep): - retry_state.prepare_for_next_attempt() - self.sleep(do) - else: - break - - @abstractmethod - def __call__( - self, - fn: t.Callable[..., WrappedFnReturnT], - *args: t.Any, - **kwargs: t.Any, - ) -> WrappedFnReturnT: - pass - - -class Retrying(BaseRetrying): - """Retrying controller.""" - - def __call__( - self, - fn: t.Callable[..., WrappedFnReturnT], - *args: t.Any, - **kwargs: t.Any, - ) -> WrappedFnReturnT: - self.begin() - - retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs) - while True: - do = self.iter(retry_state=retry_state) - if isinstance(do, DoAttempt): - try: - result = fn(*args, **kwargs) - except BaseException: # noqa: B902 - retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type] - else: - retry_state.set_result(result) - elif isinstance(do, DoSleep): - retry_state.prepare_for_next_attempt() - self.sleep(do) - else: - return do # type: ignore[no-any-return] - - -if sys.version_info[1] >= 9: - FutureGenericT = futures.Future[t.Any] -else: - FutureGenericT = futures.Future - - -class Future(FutureGenericT): - """Encapsulates a (future or past) attempted call to a target function.""" - - def __init__(self, attempt_number: int) -> None: - super().__init__() - self.attempt_number = attempt_number - - @property - def failed(self) -> bool: - """Return whether a exception is being held in this future.""" - return self.exception() is not None - - @classmethod - def construct(cls, attempt_number: int, value: t.Any, has_exception: bool) -> "Future": - """Construct a new Future object.""" - fut = cls(attempt_number) - if has_exception: - fut.set_exception(value) - else: - fut.set_result(value) - return fut - - -class RetryCallState: - """State related to a single call wrapped with Retrying.""" - - def __init__( - self, - retry_object: BaseRetrying, - fn: t.Optional[WrappedFn], - args: t.Any, - kwargs: t.Any, - ) -> None: - #: Retry call start timestamp - self.start_time = time.monotonic() - #: Retry manager object - self.retry_object = retry_object - #: Function wrapped by this retry call - self.fn = fn - #: Arguments of the function wrapped by this retry call - self.args = args - #: Keyword arguments of the function wrapped by this retry call - self.kwargs = kwargs - - #: The number of the current attempt - self.attempt_number: int = 1 - #: Last outcome (result or exception) produced by the function - self.outcome: t.Optional[Future] = None - #: Timestamp of the last outcome - self.outcome_timestamp: t.Optional[float] = None - #: Time spent sleeping in retries - self.idle_for: float = 0.0 - #: Next action as decided by the retry manager - self.next_action: t.Optional[RetryAction] = None - - @property - def seconds_since_start(self) -> t.Optional[float]: - if self.outcome_timestamp is None: - return None - return self.outcome_timestamp - self.start_time - - def prepare_for_next_attempt(self) -> None: - self.outcome = None - self.outcome_timestamp = None - self.attempt_number += 1 - self.next_action = None - - def set_result(self, val: t.Any) -> None: - ts = time.monotonic() - fut = Future(self.attempt_number) - fut.set_result(val) - self.outcome, self.outcome_timestamp = fut, ts - - def set_exception( - self, exc_info: t.Tuple[t.Type[BaseException], BaseException, "types.TracebackType| None"] - ) -> None: - ts = time.monotonic() - fut = Future(self.attempt_number) - fut.set_exception(exc_info[1]) - self.outcome, self.outcome_timestamp = fut, ts - - def __repr__(self) -> str: - if self.outcome is None: - result = "none yet" - elif self.outcome.failed: - exception = self.outcome.exception() - result = f"failed ({exception.__class__.__name__} {exception})" - else: - result = f"returned {self.outcome.result()}" - - slept = float(round(self.idle_for, 2)) - clsname = self.__class__.__name__ - return f"<{clsname} {id(self)}: attempt #{self.attempt_number}; slept for {slept}; last result: {result}>" - - -@t.overload -def retry(func: WrappedFn) -> WrappedFn: - ... - - -@t.overload -def retry( - sleep: t.Callable[[t.Union[int, float]], None] = sleep, - stop: "StopBaseT" = stop_never, - wait: "WaitBaseT" = wait_none(), - retry: "RetryBaseT" = retry_if_exception_type(), - before: t.Callable[["RetryCallState"], None] = before_nothing, - after: t.Callable[["RetryCallState"], None] = after_nothing, - before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None, - reraise: bool = False, - retry_error_cls: t.Type["RetryError"] = RetryError, - retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None, -) -> t.Callable[[WrappedFn], WrappedFn]: - ... - - -def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any: - """Wrap a function with a new `Retrying` object. - - :param dargs: positional arguments passed to Retrying object - :param dkw: keyword arguments passed to the Retrying object - """ - # support both @retry and @retry() as valid syntax - if len(dargs) == 1 and callable(dargs[0]): - return retry()(dargs[0]) - else: - - def wrap(f: WrappedFn) -> WrappedFn: - if isinstance(f, retry_base): - warnings.warn( - f"Got retry_base instance ({f.__class__.__name__}) as callable argument, " - f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)" - ) - r: "BaseRetrying" - if iscoroutinefunction(f): - r = AsyncRetrying(*dargs, **dkw) - elif tornado and hasattr(tornado.gen, "is_coroutine_function") and tornado.gen.is_coroutine_function(f): - r = TornadoRetrying(*dargs, **dkw) - else: - r = Retrying(*dargs, **dkw) - - return r.wraps(f) - - return wrap - - -from pip._vendor.tenacity._asyncio import AsyncRetrying # noqa:E402,I100 - -if tornado: - from pip._vendor.tenacity.tornadoweb import TornadoRetrying - - -__all__ = [ - "retry_base", - "retry_all", - "retry_always", - "retry_any", - "retry_if_exception", - "retry_if_exception_type", - "retry_if_exception_cause_type", - "retry_if_not_exception_type", - "retry_if_not_result", - "retry_if_result", - "retry_never", - "retry_unless_exception_type", - "retry_if_exception_message", - "retry_if_not_exception_message", - "sleep", - "sleep_using_event", - "stop_after_attempt", - "stop_after_delay", - "stop_all", - "stop_any", - "stop_never", - "stop_when_event_set", - "wait_chain", - "wait_combine", - "wait_exponential", - "wait_fixed", - "wait_incrementing", - "wait_none", - "wait_random", - "wait_random_exponential", - "wait_full_jitter", - "wait_exponential_jitter", - "before_log", - "before_nothing", - "after_log", - "after_nothing", - "before_sleep_log", - "before_sleep_nothing", - "retry", - "WrappedFn", - "TryAgain", - "NO_RESULT", - "DoAttempt", - "DoSleep", - "BaseAction", - "RetryAction", - "RetryError", - "AttemptManager", - "BaseRetrying", - "Retrying", - "Future", - "RetryCallState", - "AsyncRetrying", -] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_asyncio.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_asyncio.py deleted file mode 100644 index 2e50cd7b..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_asyncio.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright 2016 Étienne Bersac -# Copyright 2016 Julien Danjou -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import functools -import sys -import typing as t -from asyncio import sleep - -from pip._vendor.tenacity import AttemptManager -from pip._vendor.tenacity import BaseRetrying -from pip._vendor.tenacity import DoAttempt -from pip._vendor.tenacity import DoSleep -from pip._vendor.tenacity import RetryCallState - -WrappedFnReturnT = t.TypeVar("WrappedFnReturnT") -WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Awaitable[t.Any]]) - - -class AsyncRetrying(BaseRetrying): - sleep: t.Callable[[float], t.Awaitable[t.Any]] - - def __init__(self, sleep: t.Callable[[float], t.Awaitable[t.Any]] = sleep, **kwargs: t.Any) -> None: - super().__init__(**kwargs) - self.sleep = sleep - - async def __call__( # type: ignore[override] - self, fn: WrappedFn, *args: t.Any, **kwargs: t.Any - ) -> WrappedFnReturnT: - self.begin() - - retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs) - while True: - do = self.iter(retry_state=retry_state) - if isinstance(do, DoAttempt): - try: - result = await fn(*args, **kwargs) - except BaseException: # noqa: B902 - retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type] - else: - retry_state.set_result(result) - elif isinstance(do, DoSleep): - retry_state.prepare_for_next_attempt() - await self.sleep(do) - else: - return do # type: ignore[no-any-return] - - def __iter__(self) -> t.Generator[AttemptManager, None, None]: - raise TypeError("AsyncRetrying object is not iterable") - - def __aiter__(self) -> "AsyncRetrying": - self.begin() - self._retry_state = RetryCallState(self, fn=None, args=(), kwargs={}) - return self - - async def __anext__(self) -> AttemptManager: - while True: - do = self.iter(retry_state=self._retry_state) - if do is None: - raise StopAsyncIteration - elif isinstance(do, DoAttempt): - return AttemptManager(retry_state=self._retry_state) - elif isinstance(do, DoSleep): - self._retry_state.prepare_for_next_attempt() - await self.sleep(do) - else: - raise StopAsyncIteration - - def wraps(self, fn: WrappedFn) -> WrappedFn: - fn = super().wraps(fn) - # Ensure wrapper is recognized as a coroutine function. - - @functools.wraps(fn) - async def async_wrapped(*args: t.Any, **kwargs: t.Any) -> t.Any: - return await fn(*args, **kwargs) - - # Preserve attributes - async_wrapped.retry = fn.retry # type: ignore[attr-defined] - async_wrapped.retry_with = fn.retry_with # type: ignore[attr-defined] - - return async_wrapped # type: ignore[return-value] diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_utils.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_utils.py deleted file mode 100644 index f14ff320..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_utils.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2016 Julien Danjou -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys -import typing -from datetime import timedelta - - -# sys.maxsize: -# An integer giving the maximum value a variable of type Py_ssize_t can take. -MAX_WAIT = sys.maxsize / 2 - - -def find_ordinal(pos_num: int) -> str: - # See: https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers - if pos_num == 0: - return "th" - elif pos_num == 1: - return "st" - elif pos_num == 2: - return "nd" - elif pos_num == 3: - return "rd" - elif 4 <= pos_num <= 20: - return "th" - else: - return find_ordinal(pos_num % 10) - - -def to_ordinal(pos_num: int) -> str: - return f"{pos_num}{find_ordinal(pos_num)}" - - -def get_callback_name(cb: typing.Callable[..., typing.Any]) -> str: - """Get a callback fully-qualified name. - - If no name can be produced ``repr(cb)`` is called and returned. - """ - segments = [] - try: - segments.append(cb.__qualname__) - except AttributeError: - try: - segments.append(cb.__name__) - except AttributeError: - pass - if not segments: - return repr(cb) - else: - try: - # When running under sphinx it appears this can be none? - if cb.__module__: - segments.insert(0, cb.__module__) - except AttributeError: - pass - return ".".join(segments) - - -time_unit_type = typing.Union[int, float, timedelta] - - -def to_seconds(time_unit: time_unit_type) -> float: - return float(time_unit.total_seconds() if isinstance(time_unit, timedelta) else time_unit) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/after.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/after.py deleted file mode 100644 index 574c9bce..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/after.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2016 Julien Danjou -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import typing - -from pip._vendor.tenacity import _utils - -if typing.TYPE_CHECKING: - import logging - - from pip._vendor.tenacity import RetryCallState - - -def after_nothing(retry_state: "RetryCallState") -> None: - """After call strategy that does nothing.""" - - -def after_log( - logger: "logging.Logger", - log_level: int, - sec_format: str = "%0.3f", -) -> typing.Callable[["RetryCallState"], None]: - """After call strategy that logs to some logger the finished attempt.""" - - def log_it(retry_state: "RetryCallState") -> None: - if retry_state.fn is None: - # NOTE(sileht): can't really happen, but we must please mypy - fn_name = "" - else: - fn_name = _utils.get_callback_name(retry_state.fn) - logger.log( - log_level, - f"Finished call to '{fn_name}' " - f"after {sec_format % retry_state.seconds_since_start}(s), " - f"this was the {_utils.to_ordinal(retry_state.attempt_number)} time calling it.", - ) - - return log_it diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before.py deleted file mode 100644 index cfd7dc72..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2016 Julien Danjou -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import typing - -from pip._vendor.tenacity import _utils - -if typing.TYPE_CHECKING: - import logging - - from pip._vendor.tenacity import RetryCallState - - -def before_nothing(retry_state: "RetryCallState") -> None: - """Before call strategy that does nothing.""" - - -def before_log(logger: "logging.Logger", log_level: int) -> typing.Callable[["RetryCallState"], None]: - """Before call strategy that logs to some logger the attempt.""" - - def log_it(retry_state: "RetryCallState") -> None: - if retry_state.fn is None: - # NOTE(sileht): can't really happen, but we must please mypy - fn_name = "" - else: - fn_name = _utils.get_callback_name(retry_state.fn) - logger.log( - log_level, - f"Starting call to '{fn_name}', " - f"this is the {_utils.to_ordinal(retry_state.attempt_number)} time calling it.", - ) - - return log_it diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before_sleep.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before_sleep.py deleted file mode 100644 index 8c6167fb..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before_sleep.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2016 Julien Danjou -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import typing - -from pip._vendor.tenacity import _utils - -if typing.TYPE_CHECKING: - import logging - - from pip._vendor.tenacity import RetryCallState - - -def before_sleep_nothing(retry_state: "RetryCallState") -> None: - """Before call strategy that does nothing.""" - - -def before_sleep_log( - logger: "logging.Logger", - log_level: int, - exc_info: bool = False, -) -> typing.Callable[["RetryCallState"], None]: - """Before call strategy that logs to some logger the attempt.""" - - def log_it(retry_state: "RetryCallState") -> None: - local_exc_info: BaseException | bool | None - - if retry_state.outcome is None: - raise RuntimeError("log_it() called before outcome was set") - - if retry_state.next_action is None: - raise RuntimeError("log_it() called before next_action was set") - - if retry_state.outcome.failed: - ex = retry_state.outcome.exception() - verb, value = "raised", f"{ex.__class__.__name__}: {ex}" - - if exc_info: - local_exc_info = retry_state.outcome.exception() - else: - local_exc_info = False - else: - verb, value = "returned", retry_state.outcome.result() - local_exc_info = False # exc_info does not apply when no exception - - if retry_state.fn is None: - # NOTE(sileht): can't really happen, but we must please mypy - fn_name = "" - else: - fn_name = _utils.get_callback_name(retry_state.fn) - - logger.log( - log_level, - f"Retrying {fn_name} " f"in {retry_state.next_action.sleep} seconds as it {verb} {value}.", - exc_info=local_exc_info, - ) - - return log_it diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/nap.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/nap.py deleted file mode 100644 index 72aa5bfd..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/nap.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2016 Étienne Bersac -# Copyright 2016 Julien Danjou -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import time -import typing - -if typing.TYPE_CHECKING: - import threading - - -def sleep(seconds: float) -> None: - """ - Sleep strategy that delays execution for a given number of seconds. - - This is the default strategy, and may be mocked out for unit testing. - """ - time.sleep(seconds) - - -class sleep_using_event: - """Sleep strategy that waits on an event to be set.""" - - def __init__(self, event: "threading.Event") -> None: - self.event = event - - def __call__(self, timeout: typing.Optional[float]) -> None: - # NOTE(harlowja): this may *not* actually wait for timeout - # seconds if the event is set (ie this may eject out early). - self.event.wait(timeout=timeout) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/retry.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/retry.py deleted file mode 100644 index 38988739..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/retry.py +++ /dev/null @@ -1,272 +0,0 @@ -# Copyright 2016–2021 Julien Danjou -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc -import re -import typing - -if typing.TYPE_CHECKING: - from pip._vendor.tenacity import RetryCallState - - -class retry_base(abc.ABC): - """Abstract base class for retry strategies.""" - - @abc.abstractmethod - def __call__(self, retry_state: "RetryCallState") -> bool: - pass - - def __and__(self, other: "retry_base") -> "retry_all": - return retry_all(self, other) - - def __or__(self, other: "retry_base") -> "retry_any": - return retry_any(self, other) - - -RetryBaseT = typing.Union[retry_base, typing.Callable[["RetryCallState"], bool]] - - -class _retry_never(retry_base): - """Retry strategy that never rejects any result.""" - - def __call__(self, retry_state: "RetryCallState") -> bool: - return False - - -retry_never = _retry_never() - - -class _retry_always(retry_base): - """Retry strategy that always rejects any result.""" - - def __call__(self, retry_state: "RetryCallState") -> bool: - return True - - -retry_always = _retry_always() - - -class retry_if_exception(retry_base): - """Retry strategy that retries if an exception verifies a predicate.""" - - def __init__(self, predicate: typing.Callable[[BaseException], bool]) -> None: - self.predicate = predicate - - def __call__(self, retry_state: "RetryCallState") -> bool: - if retry_state.outcome is None: - raise RuntimeError("__call__() called before outcome was set") - - if retry_state.outcome.failed: - exception = retry_state.outcome.exception() - if exception is None: - raise RuntimeError("outcome failed but the exception is None") - return self.predicate(exception) - else: - return False - - -class retry_if_exception_type(retry_if_exception): - """Retries if an exception has been raised of one or more types.""" - - def __init__( - self, - exception_types: typing.Union[ - typing.Type[BaseException], - typing.Tuple[typing.Type[BaseException], ...], - ] = Exception, - ) -> None: - self.exception_types = exception_types - super().__init__(lambda e: isinstance(e, exception_types)) - - -class retry_if_not_exception_type(retry_if_exception): - """Retries except an exception has been raised of one or more types.""" - - def __init__( - self, - exception_types: typing.Union[ - typing.Type[BaseException], - typing.Tuple[typing.Type[BaseException], ...], - ] = Exception, - ) -> None: - self.exception_types = exception_types - super().__init__(lambda e: not isinstance(e, exception_types)) - - -class retry_unless_exception_type(retry_if_exception): - """Retries until an exception is raised of one or more types.""" - - def __init__( - self, - exception_types: typing.Union[ - typing.Type[BaseException], - typing.Tuple[typing.Type[BaseException], ...], - ] = Exception, - ) -> None: - self.exception_types = exception_types - super().__init__(lambda e: not isinstance(e, exception_types)) - - def __call__(self, retry_state: "RetryCallState") -> bool: - if retry_state.outcome is None: - raise RuntimeError("__call__() called before outcome was set") - - # always retry if no exception was raised - if not retry_state.outcome.failed: - return True - - exception = retry_state.outcome.exception() - if exception is None: - raise RuntimeError("outcome failed but the exception is None") - return self.predicate(exception) - - -class retry_if_exception_cause_type(retry_base): - """Retries if any of the causes of the raised exception is of one or more types. - - The check on the type of the cause of the exception is done recursively (until finding - an exception in the chain that has no `__cause__`) - """ - - def __init__( - self, - exception_types: typing.Union[ - typing.Type[BaseException], - typing.Tuple[typing.Type[BaseException], ...], - ] = Exception, - ) -> None: - self.exception_cause_types = exception_types - - def __call__(self, retry_state: "RetryCallState") -> bool: - if retry_state.outcome is None: - raise RuntimeError("__call__ called before outcome was set") - - if retry_state.outcome.failed: - exc = retry_state.outcome.exception() - while exc is not None: - if isinstance(exc.__cause__, self.exception_cause_types): - return True - exc = exc.__cause__ - - return False - - -class retry_if_result(retry_base): - """Retries if the result verifies a predicate.""" - - def __init__(self, predicate: typing.Callable[[typing.Any], bool]) -> None: - self.predicate = predicate - - def __call__(self, retry_state: "RetryCallState") -> bool: - if retry_state.outcome is None: - raise RuntimeError("__call__() called before outcome was set") - - if not retry_state.outcome.failed: - return self.predicate(retry_state.outcome.result()) - else: - return False - - -class retry_if_not_result(retry_base): - """Retries if the result refutes a predicate.""" - - def __init__(self, predicate: typing.Callable[[typing.Any], bool]) -> None: - self.predicate = predicate - - def __call__(self, retry_state: "RetryCallState") -> bool: - if retry_state.outcome is None: - raise RuntimeError("__call__() called before outcome was set") - - if not retry_state.outcome.failed: - return not self.predicate(retry_state.outcome.result()) - else: - return False - - -class retry_if_exception_message(retry_if_exception): - """Retries if an exception message equals or matches.""" - - def __init__( - self, - message: typing.Optional[str] = None, - match: typing.Optional[str] = None, - ) -> None: - if message and match: - raise TypeError(f"{self.__class__.__name__}() takes either 'message' or 'match', not both") - - # set predicate - if message: - - def message_fnc(exception: BaseException) -> bool: - return message == str(exception) - - predicate = message_fnc - elif match: - prog = re.compile(match) - - def match_fnc(exception: BaseException) -> bool: - return bool(prog.match(str(exception))) - - predicate = match_fnc - else: - raise TypeError(f"{self.__class__.__name__}() missing 1 required argument 'message' or 'match'") - - super().__init__(predicate) - - -class retry_if_not_exception_message(retry_if_exception_message): - """Retries until an exception message equals or matches.""" - - def __init__( - self, - message: typing.Optional[str] = None, - match: typing.Optional[str] = None, - ) -> None: - super().__init__(message, match) - # invert predicate - if_predicate = self.predicate - self.predicate = lambda *args_, **kwargs_: not if_predicate(*args_, **kwargs_) - - def __call__(self, retry_state: "RetryCallState") -> bool: - if retry_state.outcome is None: - raise RuntimeError("__call__() called before outcome was set") - - if not retry_state.outcome.failed: - return True - - exception = retry_state.outcome.exception() - if exception is None: - raise RuntimeError("outcome failed but the exception is None") - return self.predicate(exception) - - -class retry_any(retry_base): - """Retries if any of the retries condition is valid.""" - - def __init__(self, *retries: retry_base) -> None: - self.retries = retries - - def __call__(self, retry_state: "RetryCallState") -> bool: - return any(r(retry_state) for r in self.retries) - - -class retry_all(retry_base): - """Retries if all the retries condition are valid.""" - - def __init__(self, *retries: retry_base) -> None: - self.retries = retries - - def __call__(self, retry_state: "RetryCallState") -> bool: - return all(r(retry_state) for r in self.retries) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/stop.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/stop.py deleted file mode 100644 index bb23effd..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/stop.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright 2016–2021 Julien Danjou -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import abc -import typing - -from pip._vendor.tenacity import _utils - -if typing.TYPE_CHECKING: - import threading - - from pip._vendor.tenacity import RetryCallState - - -class stop_base(abc.ABC): - """Abstract base class for stop strategies.""" - - @abc.abstractmethod - def __call__(self, retry_state: "RetryCallState") -> bool: - pass - - def __and__(self, other: "stop_base") -> "stop_all": - return stop_all(self, other) - - def __or__(self, other: "stop_base") -> "stop_any": - return stop_any(self, other) - - -StopBaseT = typing.Union[stop_base, typing.Callable[["RetryCallState"], bool]] - - -class stop_any(stop_base): - """Stop if any of the stop condition is valid.""" - - def __init__(self, *stops: stop_base) -> None: - self.stops = stops - - def __call__(self, retry_state: "RetryCallState") -> bool: - return any(x(retry_state) for x in self.stops) - - -class stop_all(stop_base): - """Stop if all the stop conditions are valid.""" - - def __init__(self, *stops: stop_base) -> None: - self.stops = stops - - def __call__(self, retry_state: "RetryCallState") -> bool: - return all(x(retry_state) for x in self.stops) - - -class _stop_never(stop_base): - """Never stop.""" - - def __call__(self, retry_state: "RetryCallState") -> bool: - return False - - -stop_never = _stop_never() - - -class stop_when_event_set(stop_base): - """Stop when the given event is set.""" - - def __init__(self, event: "threading.Event") -> None: - self.event = event - - def __call__(self, retry_state: "RetryCallState") -> bool: - return self.event.is_set() - - -class stop_after_attempt(stop_base): - """Stop when the previous attempt >= max_attempt.""" - - def __init__(self, max_attempt_number: int) -> None: - self.max_attempt_number = max_attempt_number - - def __call__(self, retry_state: "RetryCallState") -> bool: - return retry_state.attempt_number >= self.max_attempt_number - - -class stop_after_delay(stop_base): - """Stop when the time from the first attempt >= limit.""" - - def __init__(self, max_delay: _utils.time_unit_type) -> None: - self.max_delay = _utils.to_seconds(max_delay) - - def __call__(self, retry_state: "RetryCallState") -> bool: - if retry_state.seconds_since_start is None: - raise RuntimeError("__call__() called but seconds_since_start is not set") - return retry_state.seconds_since_start >= self.max_delay diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/tornadoweb.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/tornadoweb.py deleted file mode 100644 index e19c30b1..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/tornadoweb.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2017 Elisey Zanko -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys -import typing - -from pip._vendor.tenacity import BaseRetrying -from pip._vendor.tenacity import DoAttempt -from pip._vendor.tenacity import DoSleep -from pip._vendor.tenacity import RetryCallState - -from tornado import gen - -if typing.TYPE_CHECKING: - from tornado.concurrent import Future - -_RetValT = typing.TypeVar("_RetValT") - - -class TornadoRetrying(BaseRetrying): - def __init__(self, sleep: "typing.Callable[[float], Future[None]]" = gen.sleep, **kwargs: typing.Any) -> None: - super().__init__(**kwargs) - self.sleep = sleep - - @gen.coroutine # type: ignore[misc] - def __call__( - self, - fn: "typing.Callable[..., typing.Union[typing.Generator[typing.Any, typing.Any, _RetValT], Future[_RetValT]]]", - *args: typing.Any, - **kwargs: typing.Any, - ) -> "typing.Generator[typing.Any, typing.Any, _RetValT]": - self.begin() - - retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs) - while True: - do = self.iter(retry_state=retry_state) - if isinstance(do, DoAttempt): - try: - result = yield fn(*args, **kwargs) - except BaseException: # noqa: B902 - retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type] - else: - retry_state.set_result(result) - elif isinstance(do, DoSleep): - retry_state.prepare_for_next_attempt() - yield self.sleep(do) - else: - raise gen.Return(do) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/wait.py b/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/wait.py deleted file mode 100644 index f9349c02..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tenacity/wait.py +++ /dev/null @@ -1,228 +0,0 @@ -# Copyright 2016–2021 Julien Danjou -# Copyright 2016 Joshua Harlow -# Copyright 2013-2014 Ray Holder -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc -import random -import typing - -from pip._vendor.tenacity import _utils - -if typing.TYPE_CHECKING: - from pip._vendor.tenacity import RetryCallState - - -class wait_base(abc.ABC): - """Abstract base class for wait strategies.""" - - @abc.abstractmethod - def __call__(self, retry_state: "RetryCallState") -> float: - pass - - def __add__(self, other: "wait_base") -> "wait_combine": - return wait_combine(self, other) - - def __radd__(self, other: "wait_base") -> typing.Union["wait_combine", "wait_base"]: - # make it possible to use multiple waits with the built-in sum function - if other == 0: # type: ignore[comparison-overlap] - return self - return self.__add__(other) - - -WaitBaseT = typing.Union[wait_base, typing.Callable[["RetryCallState"], typing.Union[float, int]]] - - -class wait_fixed(wait_base): - """Wait strategy that waits a fixed amount of time between each retry.""" - - def __init__(self, wait: _utils.time_unit_type) -> None: - self.wait_fixed = _utils.to_seconds(wait) - - def __call__(self, retry_state: "RetryCallState") -> float: - return self.wait_fixed - - -class wait_none(wait_fixed): - """Wait strategy that doesn't wait at all before retrying.""" - - def __init__(self) -> None: - super().__init__(0) - - -class wait_random(wait_base): - """Wait strategy that waits a random amount of time between min/max.""" - - def __init__(self, min: _utils.time_unit_type = 0, max: _utils.time_unit_type = 1) -> None: # noqa - self.wait_random_min = _utils.to_seconds(min) - self.wait_random_max = _utils.to_seconds(max) - - def __call__(self, retry_state: "RetryCallState") -> float: - return self.wait_random_min + (random.random() * (self.wait_random_max - self.wait_random_min)) - - -class wait_combine(wait_base): - """Combine several waiting strategies.""" - - def __init__(self, *strategies: wait_base) -> None: - self.wait_funcs = strategies - - def __call__(self, retry_state: "RetryCallState") -> float: - return sum(x(retry_state=retry_state) for x in self.wait_funcs) - - -class wait_chain(wait_base): - """Chain two or more waiting strategies. - - If all strategies are exhausted, the very last strategy is used - thereafter. - - For example:: - - @retry(wait=wait_chain(*[wait_fixed(1) for i in range(3)] + - [wait_fixed(2) for j in range(5)] + - [wait_fixed(5) for k in range(4))) - def wait_chained(): - print("Wait 1s for 3 attempts, 2s for 5 attempts and 5s - thereafter.") - """ - - def __init__(self, *strategies: wait_base) -> None: - self.strategies = strategies - - def __call__(self, retry_state: "RetryCallState") -> float: - wait_func_no = min(max(retry_state.attempt_number, 1), len(self.strategies)) - wait_func = self.strategies[wait_func_no - 1] - return wait_func(retry_state=retry_state) - - -class wait_incrementing(wait_base): - """Wait an incremental amount of time after each attempt. - - Starting at a starting value and incrementing by a value for each attempt - (and restricting the upper limit to some maximum value). - """ - - def __init__( - self, - start: _utils.time_unit_type = 0, - increment: _utils.time_unit_type = 100, - max: _utils.time_unit_type = _utils.MAX_WAIT, # noqa - ) -> None: - self.start = _utils.to_seconds(start) - self.increment = _utils.to_seconds(increment) - self.max = _utils.to_seconds(max) - - def __call__(self, retry_state: "RetryCallState") -> float: - result = self.start + (self.increment * (retry_state.attempt_number - 1)) - return max(0, min(result, self.max)) - - -class wait_exponential(wait_base): - """Wait strategy that applies exponential backoff. - - It allows for a customized multiplier and an ability to restrict the - upper and lower limits to some maximum and minimum value. - - The intervals are fixed (i.e. there is no jitter), so this strategy is - suitable for balancing retries against latency when a required resource is - unavailable for an unknown duration, but *not* suitable for resolving - contention between multiple processes for a shared resource. Use - wait_random_exponential for the latter case. - """ - - def __init__( - self, - multiplier: typing.Union[int, float] = 1, - max: _utils.time_unit_type = _utils.MAX_WAIT, # noqa - exp_base: typing.Union[int, float] = 2, - min: _utils.time_unit_type = 0, # noqa - ) -> None: - self.multiplier = multiplier - self.min = _utils.to_seconds(min) - self.max = _utils.to_seconds(max) - self.exp_base = exp_base - - def __call__(self, retry_state: "RetryCallState") -> float: - try: - exp = self.exp_base ** (retry_state.attempt_number - 1) - result = self.multiplier * exp - except OverflowError: - return self.max - return max(max(0, self.min), min(result, self.max)) - - -class wait_random_exponential(wait_exponential): - """Random wait with exponentially widening window. - - An exponential backoff strategy used to mediate contention between multiple - uncoordinated processes for a shared resource in distributed systems. This - is the sense in which "exponential backoff" is meant in e.g. Ethernet - networking, and corresponds to the "Full Jitter" algorithm described in - this blog post: - - https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ - - Each retry occurs at a random time in a geometrically expanding interval. - It allows for a custom multiplier and an ability to restrict the upper - limit of the random interval to some maximum value. - - Example:: - - wait_random_exponential(multiplier=0.5, # initial window 0.5s - max=60) # max 60s timeout - - When waiting for an unavailable resource to become available again, as - opposed to trying to resolve contention for a shared resource, the - wait_exponential strategy (which uses a fixed interval) may be preferable. - - """ - - def __call__(self, retry_state: "RetryCallState") -> float: - high = super().__call__(retry_state=retry_state) - return random.uniform(0, high) - - -class wait_exponential_jitter(wait_base): - """Wait strategy that applies exponential backoff and jitter. - - It allows for a customized initial wait, maximum wait and jitter. - - This implements the strategy described here: - https://cloud.google.com/storage/docs/retry-strategy - - The wait time is min(initial * 2**n + random.uniform(0, jitter), maximum) - where n is the retry count. - """ - - def __init__( - self, - initial: float = 1, - max: float = _utils.MAX_WAIT, # noqa - exp_base: float = 2, - jitter: float = 1, - ) -> None: - self.initial = initial - self.max = max - self.exp_base = exp_base - self.jitter = jitter - - def __call__(self, retry_state: "RetryCallState") -> float: - jitter = random.uniform(0, self.jitter) - try: - exp = self.exp_base ** (retry_state.attempt_number - 1) - result = self.initial * exp + jitter - except OverflowError: - result = self.max - return max(0, min(result, self.max)) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tomli/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/LICENSE new file mode 100644 index 00000000..e859590f --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Taneli Hukkinen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tomli/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/__init__.py index 4c6ec97e..9395b043 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tomli/__init__.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/__init__.py @@ -3,9 +3,6 @@ # Licensed to PSF under a Contributor Agreement. __all__ = ("loads", "load", "TOMLDecodeError") -__version__ = "2.0.1" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT +__version__ = "2.3.0" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT from ._parser import TOMLDecodeError, load, loads - -# Pretend this exception was created here. -TOMLDecodeError.__module__ = __name__ diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tomli/_parser.py b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/_parser.py index f1bb0aa1..26170e8e 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tomli/_parser.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/_parser.py @@ -4,10 +4,8 @@ from __future__ import annotations -from collections.abc import Iterable -import string +import sys from types import MappingProxyType -from typing import Any, BinaryIO, NamedTuple from ._re import ( RE_DATETIME, @@ -17,44 +15,125 @@ from ._re import ( match_to_localtime, match_to_number, ) -from ._types import Key, ParseFloat, Pos -ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Iterable + from typing import IO, Any, Final + + from ._types import Key, ParseFloat, Pos + +# Inline tables/arrays are implemented using recursion. Pathologically +# nested documents cause pure Python to raise RecursionError (which is OK), +# but mypyc binary wheels will crash unrecoverably (not OK). According to +# mypyc docs this will be fixed in the future: +# https://mypyc.readthedocs.io/en/latest/differences_from_python.html#stack-overflows +# Before mypyc's fix is in, recursion needs to be limited by this library. +# Choosing `sys.getrecursionlimit()` as maximum inline table/array nesting +# level, as it allows more nesting than pure Python, but still seems a far +# lower number than where mypyc binaries crash. +MAX_INLINE_NESTING: Final = sys.getrecursionlimit() + +ASCII_CTRL: Final = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) # Neither of these sets include quotation mark or backslash. They are # currently handled as separate cases in the parser functions. -ILLEGAL_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t") -ILLEGAL_MULTILINE_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t\n") +ILLEGAL_BASIC_STR_CHARS: Final = ASCII_CTRL - frozenset("\t") +ILLEGAL_MULTILINE_BASIC_STR_CHARS: Final = ASCII_CTRL - frozenset("\t\n") -ILLEGAL_LITERAL_STR_CHARS = ILLEGAL_BASIC_STR_CHARS -ILLEGAL_MULTILINE_LITERAL_STR_CHARS = ILLEGAL_MULTILINE_BASIC_STR_CHARS +ILLEGAL_LITERAL_STR_CHARS: Final = ILLEGAL_BASIC_STR_CHARS +ILLEGAL_MULTILINE_LITERAL_STR_CHARS: Final = ILLEGAL_MULTILINE_BASIC_STR_CHARS -ILLEGAL_COMMENT_CHARS = ILLEGAL_BASIC_STR_CHARS +ILLEGAL_COMMENT_CHARS: Final = ILLEGAL_BASIC_STR_CHARS -TOML_WS = frozenset(" \t") -TOML_WS_AND_NEWLINE = TOML_WS | frozenset("\n") -BARE_KEY_CHARS = frozenset(string.ascii_letters + string.digits + "-_") -KEY_INITIAL_CHARS = BARE_KEY_CHARS | frozenset("\"'") -HEXDIGIT_CHARS = frozenset(string.hexdigits) +TOML_WS: Final = frozenset(" \t") +TOML_WS_AND_NEWLINE: Final = TOML_WS | frozenset("\n") +BARE_KEY_CHARS: Final = frozenset( + "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "-_" +) +KEY_INITIAL_CHARS: Final = BARE_KEY_CHARS | frozenset("\"'") +HEXDIGIT_CHARS: Final = frozenset("abcdef" "ABCDEF" "0123456789") -BASIC_STR_ESCAPE_REPLACEMENTS = MappingProxyType( +BASIC_STR_ESCAPE_REPLACEMENTS: Final = MappingProxyType( { "\\b": "\u0008", # backspace "\\t": "\u0009", # tab - "\\n": "\u000A", # linefeed - "\\f": "\u000C", # form feed - "\\r": "\u000D", # carriage return + "\\n": "\u000a", # linefeed + "\\f": "\u000c", # form feed + "\\r": "\u000d", # carriage return '\\"': "\u0022", # quote - "\\\\": "\u005C", # backslash + "\\\\": "\u005c", # backslash } ) +class DEPRECATED_DEFAULT: + """Sentinel to be used as default arg during deprecation + period of TOMLDecodeError's free-form arguments.""" + + class TOMLDecodeError(ValueError): - """An error raised if a document is not valid TOML.""" + """An error raised if a document is not valid TOML. + + Adds the following attributes to ValueError: + msg: The unformatted error message + doc: The TOML document being parsed + pos: The index of doc where parsing failed + lineno: The line corresponding to pos + colno: The column corresponding to pos + """ + + def __init__( + self, + msg: str | type[DEPRECATED_DEFAULT] = DEPRECATED_DEFAULT, + doc: str | type[DEPRECATED_DEFAULT] = DEPRECATED_DEFAULT, + pos: Pos | type[DEPRECATED_DEFAULT] = DEPRECATED_DEFAULT, + *args: Any, + ): + if ( + args + or not isinstance(msg, str) + or not isinstance(doc, str) + or not isinstance(pos, int) + ): + import warnings + + warnings.warn( + "Free-form arguments for TOMLDecodeError are deprecated. " + "Please set 'msg' (str), 'doc' (str) and 'pos' (int) arguments only.", + DeprecationWarning, + stacklevel=2, + ) + if pos is not DEPRECATED_DEFAULT: + args = pos, *args + if doc is not DEPRECATED_DEFAULT: + args = doc, *args + if msg is not DEPRECATED_DEFAULT: + args = msg, *args + ValueError.__init__(self, *args) + return + + lineno = doc.count("\n", 0, pos) + 1 + if lineno == 1: + colno = pos + 1 + else: + colno = pos - doc.rindex("\n", 0, pos) + + if pos >= len(doc): + coord_repr = "end of document" + else: + coord_repr = f"line {lineno}, column {colno}" + errmsg = f"{msg} (at {coord_repr})" + ValueError.__init__(self, errmsg) + + self.msg = msg + self.doc = doc + self.pos = pos + self.lineno = lineno + self.colno = colno -def load(__fp: BinaryIO, *, parse_float: ParseFloat = float) -> dict[str, Any]: +def load(__fp: IO[bytes], *, parse_float: ParseFloat = float) -> dict[str, Any]: """Parse TOML from a binary file object.""" b = __fp.read() try: @@ -71,9 +150,14 @@ def loads(__s: str, *, parse_float: ParseFloat = float) -> dict[str, Any]: # no # The spec allows converting "\r\n" to "\n", even in string # literals. Let's do so to simplify parsing. - src = __s.replace("\r\n", "\n") + try: + src = __s.replace("\r\n", "\n") + except (AttributeError, TypeError): + raise TypeError( + f"Expected str object, not '{type(__s).__qualname__}'" + ) from None pos = 0 - out = Output(NestedDict(), Flags()) + out = Output() header: Key = () parse_float = make_safe_parse_float(parse_float) @@ -113,7 +197,7 @@ def loads(__s: str, *, parse_float: ParseFloat = float) -> dict[str, Any]: # no pos, header = create_dict_rule(src, pos, out) pos = skip_chars(src, pos, TOML_WS) elif char != "#": - raise suffixed_err(src, pos, "Invalid statement") + raise TOMLDecodeError("Invalid statement", src, pos) # 3. Skip comment pos = skip_comment(src, pos) @@ -124,8 +208,8 @@ def loads(__s: str, *, parse_float: ParseFloat = float) -> dict[str, Any]: # no except IndexError: break if char != "\n": - raise suffixed_err( - src, pos, "Expected newline or end of document after a statement" + raise TOMLDecodeError( + "Expected newline or end of document after a statement", src, pos ) pos += 1 @@ -136,13 +220,13 @@ class Flags: """Flags that map to parsed keys/namespaces.""" # Marks an immutable namespace (inline array or inline table). - FROZEN = 0 + FROZEN: Final = 0 # Marks a nest that has been explicitly created and can no longer # be opened using the "[table]" syntax. - EXPLICIT_NEST = 1 + EXPLICIT_NEST: Final = 1 def __init__(self) -> None: - self._flags: dict[str, dict] = {} + self._flags: dict[str, dict[Any, Any]] = {} self._pending_flags: set[tuple[Key, int]] = set() def add_pending(self, key: Key, flag: int) -> None: @@ -185,8 +269,8 @@ class Flags: cont = inner_cont["nested"] key_stem = key[-1] if key_stem in cont: - cont = cont[key_stem] - return flag in cont["flags"] or flag in cont["recursive_flags"] + inner_cont = cont[key_stem] + return flag in inner_cont["flags"] or flag in inner_cont["recursive_flags"] return False @@ -200,7 +284,7 @@ class NestedDict: key: Key, *, access_lists: bool = True, - ) -> dict: + ) -> dict[str, Any]: cont: Any = self.dict for k in key: if k not in cont: @@ -210,7 +294,7 @@ class NestedDict: cont = cont[-1] if not isinstance(cont, dict): raise KeyError("There is no nest behind this key") - return cont + return cont # type: ignore[no-any-return] def append_nest_to_list(self, key: Key) -> None: cont = self.get_or_create_nest(key[:-1]) @@ -224,9 +308,10 @@ class NestedDict: cont[last_key] = [{}] -class Output(NamedTuple): - data: NestedDict - flags: Flags +class Output: + def __init__(self) -> None: + self.data = NestedDict() + self.flags = Flags() def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos: @@ -251,12 +336,12 @@ def skip_until( except ValueError: new_pos = len(src) if error_on_eof: - raise suffixed_err(src, new_pos, f"Expected {expect!r}") from None + raise TOMLDecodeError(f"Expected {expect!r}", src, new_pos) from None if not error_on.isdisjoint(src[pos:new_pos]): while src[pos] not in error_on: pos += 1 - raise suffixed_err(src, pos, f"Found invalid character {src[pos]!r}") + raise TOMLDecodeError(f"Found invalid character {src[pos]!r}", src, pos) return new_pos @@ -287,15 +372,17 @@ def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: pos, key = parse_key(src, pos) if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN): - raise suffixed_err(src, pos, f"Cannot declare {key} twice") + raise TOMLDecodeError(f"Cannot declare {key} twice", src, pos) out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) try: out.data.get_or_create_nest(key) except KeyError: - raise suffixed_err(src, pos, "Cannot overwrite a value") from None + raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if not src.startswith("]", pos): - raise suffixed_err(src, pos, "Expected ']' at the end of a table declaration") + raise TOMLDecodeError( + "Expected ']' at the end of a table declaration", src, pos + ) return pos + 1, key @@ -305,7 +392,7 @@ def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: pos, key = parse_key(src, pos) if out.flags.is_(key, Flags.FROZEN): - raise suffixed_err(src, pos, f"Cannot mutate immutable namespace {key}") + raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos) # Free the namespace now that it points to another empty list item... out.flags.unset_all(key) # ...but this key precisely is still prohibited from table declaration @@ -313,17 +400,19 @@ def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: try: out.data.append_nest_to_list(key) except KeyError: - raise suffixed_err(src, pos, "Cannot overwrite a value") from None + raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if not src.startswith("]]", pos): - raise suffixed_err(src, pos, "Expected ']]' at the end of an array declaration") + raise TOMLDecodeError( + "Expected ']]' at the end of an array declaration", src, pos + ) return pos + 2, key def key_value_rule( src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat ) -> Pos: - pos, key, value = parse_key_value_pair(src, pos, parse_float) + pos, key, value = parse_key_value_pair(src, pos, parse_float, nest_lvl=0) key_parent, key_stem = key[:-1], key[-1] abs_key_parent = header + key_parent @@ -331,22 +420,22 @@ def key_value_rule( for cont_key in relative_path_cont_keys: # Check that dotted key syntax does not redefine an existing table if out.flags.is_(cont_key, Flags.EXPLICIT_NEST): - raise suffixed_err(src, pos, f"Cannot redefine namespace {cont_key}") + raise TOMLDecodeError(f"Cannot redefine namespace {cont_key}", src, pos) # Containers in the relative path can't be opened with the table syntax or # dotted key/value syntax in following table sections. out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST) if out.flags.is_(abs_key_parent, Flags.FROZEN): - raise suffixed_err( - src, pos, f"Cannot mutate immutable namespace {abs_key_parent}" + raise TOMLDecodeError( + f"Cannot mutate immutable namespace {abs_key_parent}", src, pos ) try: nest = out.data.get_or_create_nest(abs_key_parent) except KeyError: - raise suffixed_err(src, pos, "Cannot overwrite a value") from None + raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if key_stem in nest: - raise suffixed_err(src, pos, "Cannot overwrite a value") + raise TOMLDecodeError("Cannot overwrite a value", src, pos) # Mark inline table and array namespaces recursively immutable if isinstance(value, (dict, list)): out.flags.set(header + key, Flags.FROZEN, recursive=True) @@ -355,7 +444,7 @@ def key_value_rule( def parse_key_value_pair( - src: str, pos: Pos, parse_float: ParseFloat + src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int ) -> tuple[Pos, Key, Any]: pos, key = parse_key(src, pos) try: @@ -363,10 +452,10 @@ def parse_key_value_pair( except IndexError: char = None if char != "=": - raise suffixed_err(src, pos, "Expected '=' after a key in a key/value pair") + raise TOMLDecodeError("Expected '=' after a key in a key/value pair", src, pos) pos += 1 pos = skip_chars(src, pos, TOML_WS) - pos, value = parse_value(src, pos, parse_float) + pos, value = parse_value(src, pos, parse_float, nest_lvl) return pos, key, value @@ -401,7 +490,7 @@ def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]: return parse_literal_str(src, pos) if char == '"': return parse_one_line_basic_str(src, pos) - raise suffixed_err(src, pos, "Invalid initial character for a key part") + raise TOMLDecodeError("Invalid initial character for a key part", src, pos) def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]: @@ -409,15 +498,17 @@ def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]: return parse_basic_str(src, pos, multiline=False) -def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list]: +def parse_array( + src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int +) -> tuple[Pos, list[Any]]: pos += 1 - array: list = [] + array: list[Any] = [] pos = skip_comments_and_array_ws(src, pos) if src.startswith("]", pos): return pos + 1, array while True: - pos, val = parse_value(src, pos, parse_float) + pos, val = parse_value(src, pos, parse_float, nest_lvl) array.append(val) pos = skip_comments_and_array_ws(src, pos) @@ -425,7 +516,7 @@ def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list] if c == "]": return pos + 1, array if c != ",": - raise suffixed_err(src, pos, "Unclosed array") + raise TOMLDecodeError("Unclosed array", src, pos) pos += 1 pos = skip_comments_and_array_ws(src, pos) @@ -433,7 +524,9 @@ def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list] return pos + 1, array -def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, dict]: +def parse_inline_table( + src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int +) -> tuple[Pos, dict[str, Any]]: pos += 1 nested_dict = NestedDict() flags = Flags() @@ -442,23 +535,23 @@ def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos if src.startswith("}", pos): return pos + 1, nested_dict.dict while True: - pos, key, value = parse_key_value_pair(src, pos, parse_float) + pos, key, value = parse_key_value_pair(src, pos, parse_float, nest_lvl) key_parent, key_stem = key[:-1], key[-1] if flags.is_(key, Flags.FROZEN): - raise suffixed_err(src, pos, f"Cannot mutate immutable namespace {key}") + raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos) try: nest = nested_dict.get_or_create_nest(key_parent, access_lists=False) except KeyError: - raise suffixed_err(src, pos, "Cannot overwrite a value") from None + raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None if key_stem in nest: - raise suffixed_err(src, pos, f"Duplicate inline table key {key_stem!r}") + raise TOMLDecodeError(f"Duplicate inline table key {key_stem!r}", src, pos) nest[key_stem] = value pos = skip_chars(src, pos, TOML_WS) c = src[pos : pos + 1] if c == "}": return pos + 1, nested_dict.dict if c != ",": - raise suffixed_err(src, pos, "Unclosed inline table") + raise TOMLDecodeError("Unclosed inline table", src, pos) if isinstance(value, (dict, list)): flags.set(key, Flags.FROZEN, recursive=True) pos += 1 @@ -480,7 +573,7 @@ def parse_basic_str_escape( except IndexError: return pos, "" if char != "\n": - raise suffixed_err(src, pos, "Unescaped '\\' in a string") + raise TOMLDecodeError("Unescaped '\\' in a string", src, pos) pos += 1 pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) return pos, "" @@ -491,7 +584,7 @@ def parse_basic_str_escape( try: return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id] except KeyError: - raise suffixed_err(src, pos, "Unescaped '\\' in a string") from None + raise TOMLDecodeError("Unescaped '\\' in a string", src, pos) from None def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]: @@ -501,11 +594,13 @@ def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]: def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]: hex_str = src[pos : pos + hex_len] if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str): - raise suffixed_err(src, pos, "Invalid hex value") + raise TOMLDecodeError("Invalid hex value", src, pos) pos += hex_len hex_int = int(hex_str, 16) if not is_unicode_scalar_value(hex_int): - raise suffixed_err(src, pos, "Escaped character is not a Unicode scalar value") + raise TOMLDecodeError( + "Escaped character is not a Unicode scalar value", src, pos + ) return pos, chr(hex_int) @@ -562,7 +657,7 @@ def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]: try: char = src[pos] except IndexError: - raise suffixed_err(src, pos, "Unterminated string") from None + raise TOMLDecodeError("Unterminated string", src, pos) from None if char == '"': if not multiline: return pos + 1, result + src[start_pos:pos] @@ -577,13 +672,21 @@ def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]: start_pos = pos continue if char in error_on: - raise suffixed_err(src, pos, f"Illegal character {char!r}") + raise TOMLDecodeError(f"Illegal character {char!r}", src, pos) pos += 1 def parse_value( # noqa: C901 - src: str, pos: Pos, parse_float: ParseFloat + src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int ) -> tuple[Pos, Any]: + if nest_lvl > MAX_INLINE_NESTING: + # Pure Python should have raised RecursionError already. + # This ensures mypyc binaries eventually do the same. + raise RecursionError( # pragma: no cover + "TOML inline arrays/tables are nested more than the allowed" + f" {MAX_INLINE_NESTING} levels" + ) + try: char: str | None = src[pos] except IndexError: @@ -613,11 +716,11 @@ def parse_value( # noqa: C901 # Arrays if char == "[": - return parse_array(src, pos, parse_float) + return parse_array(src, pos, parse_float, nest_lvl + 1) # Inline tables if char == "{": - return parse_inline_table(src, pos, parse_float) + return parse_inline_table(src, pos, parse_float, nest_lvl + 1) # Dates and times datetime_match = RE_DATETIME.match(src, pos) @@ -625,7 +728,7 @@ def parse_value( # noqa: C901 try: datetime_obj = match_to_datetime(datetime_match) except ValueError as e: - raise suffixed_err(src, pos, "Invalid date or datetime") from e + raise TOMLDecodeError("Invalid date or datetime", src, pos) from e return datetime_match.end(), datetime_obj localtime_match = RE_LOCALTIME.match(src, pos) if localtime_match: @@ -646,24 +749,7 @@ def parse_value( # noqa: C901 if first_four in {"-inf", "+inf", "-nan", "+nan"}: return pos + 4, parse_float(first_four) - raise suffixed_err(src, pos, "Invalid value") - - -def suffixed_err(src: str, pos: Pos, msg: str) -> TOMLDecodeError: - """Return a `TOMLDecodeError` where error message is suffixed with - coordinates in source.""" - - def coord_repr(src: str, pos: Pos) -> str: - if pos >= len(src): - return "end of document" - line = src.count("\n", 0, pos) + 1 - if line == 1: - column = pos + 1 - else: - column = pos - src.rindex("\n", 0, pos) - return f"line {line}, column {column}" - - return TOMLDecodeError(f"{msg} (at {coord_repr(src, pos)})") + raise TOMLDecodeError("Invalid value", src, pos) def is_unicode_scalar_value(codepoint: int) -> bool: @@ -679,7 +765,7 @@ def make_safe_parse_float(parse_float: ParseFloat) -> ParseFloat: instead of returning illegal types. """ # The default `float` callable never returns illegal types. Optimize it. - if parse_float is float: # type: ignore[comparison-overlap] + if parse_float is float: return float def safe_parse_float(float_str: str) -> Any: diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tomli/_re.py b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/_re.py index 994bb749..c159dcfa 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/tomli/_re.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/_re.py @@ -7,16 +7,21 @@ from __future__ import annotations from datetime import date, datetime, time, timedelta, timezone, tzinfo from functools import lru_cache import re -from typing import Any -from ._types import ParseFloat +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Any, Final + + from ._types import ParseFloat # E.g. # - 00:32:00.999999 # - 00:32:00 -_TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?" +_TIME_RE_STR: Final = ( + r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?" +) -RE_NUMBER = re.compile( +RE_NUMBER: Final = re.compile( r""" 0 (?: @@ -35,8 +40,8 @@ RE_NUMBER = re.compile( """, flags=re.VERBOSE, ) -RE_LOCALTIME = re.compile(_TIME_RE_STR) -RE_DATETIME = re.compile( +RE_LOCALTIME: Final = re.compile(_TIME_RE_STR) +RE_DATETIME: Final = re.compile( rf""" ([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27 (?: @@ -49,7 +54,7 @@ RE_DATETIME = re.compile( ) -def match_to_datetime(match: re.Match) -> datetime | date: +def match_to_datetime(match: re.Match[str]) -> datetime | date: """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`. Raises ValueError if the match does not correspond to a valid date @@ -84,6 +89,9 @@ def match_to_datetime(match: re.Match) -> datetime | date: return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz) +# No need to limit cache size. This is only ever called on input +# that matched RE_DATETIME, so there is an implicit bound of +# 24 (hours) * 60 (minutes) * 2 (offset direction) = 2880. @lru_cache(maxsize=None) def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone: sign = 1 if sign_str == "+" else -1 @@ -95,13 +103,13 @@ def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone: ) -def match_to_localtime(match: re.Match) -> time: +def match_to_localtime(match: re.Match[str]) -> time: hour_str, minute_str, sec_str, micros_str = match.groups() micros = int(micros_str.ljust(6, "0")) if micros_str else 0 return time(int(hour_str), int(minute_str), int(sec_str), micros) -def match_to_number(match: re.Match, parse_float: ParseFloat) -> Any: +def match_to_number(match: re.Match[str], parse_float: ParseFloat) -> Any: if match.group("floatpart"): return parse_float(match.group()) return int(match.group(), 0) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tomli/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/py.typed new file mode 100644 index 00000000..7632ecf7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/tomli/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561 diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/LICENSE new file mode 100644 index 00000000..e859590f --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Taneli Hukkinen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/__init__.py new file mode 100644 index 00000000..6349c1f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/__init__.py @@ -0,0 +1,4 @@ +__all__ = ("dumps", "dump") +__version__ = "1.2.0" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT + +from pip._vendor.tomli_w._writer import dump, dumps diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/_writer.py b/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/_writer.py new file mode 100644 index 00000000..b1acd3f2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/_writer.py @@ -0,0 +1,229 @@ +from __future__ import annotations + +from collections.abc import Mapping +from datetime import date, datetime, time +from types import MappingProxyType + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Generator + from decimal import Decimal + from typing import IO, Any, Final + +ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) +ILLEGAL_BASIC_STR_CHARS = frozenset('"\\') | ASCII_CTRL - frozenset("\t") +BARE_KEY_CHARS = frozenset( + "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "-_" +) +ARRAY_TYPES = (list, tuple) +MAX_LINE_LENGTH = 100 + +COMPACT_ESCAPES = MappingProxyType( + { + "\u0008": "\\b", # backspace + "\u000A": "\\n", # linefeed + "\u000C": "\\f", # form feed + "\u000D": "\\r", # carriage return + "\u0022": '\\"', # quote + "\u005C": "\\\\", # backslash + } +) + + +class Context: + def __init__(self, allow_multiline: bool, indent: int): + if indent < 0: + raise ValueError("Indent width must be non-negative") + self.allow_multiline: Final = allow_multiline + # cache rendered inline tables (mapping from object id to rendered inline table) + self.inline_table_cache: Final[dict[int, str]] = {} + self.indent_str: Final = " " * indent + + +def dump( + obj: Mapping[str, Any], + fp: IO[bytes], + /, + *, + multiline_strings: bool = False, + indent: int = 4, +) -> None: + ctx = Context(multiline_strings, indent) + for chunk in gen_table_chunks(obj, ctx, name=""): + fp.write(chunk.encode()) + + +def dumps( + obj: Mapping[str, Any], /, *, multiline_strings: bool = False, indent: int = 4 +) -> str: + ctx = Context(multiline_strings, indent) + return "".join(gen_table_chunks(obj, ctx, name="")) + + +def gen_table_chunks( + table: Mapping[str, Any], + ctx: Context, + *, + name: str, + inside_aot: bool = False, +) -> Generator[str, None, None]: + yielded = False + literals = [] + tables: list[tuple[str, Any, bool]] = [] # => [(key, value, inside_aot)] + for k, v in table.items(): + if isinstance(v, Mapping): + tables.append((k, v, False)) + elif is_aot(v) and not all(is_suitable_inline_table(t, ctx) for t in v): + tables.extend((k, t, True) for t in v) + else: + literals.append((k, v)) + + if inside_aot or name and (literals or not tables): + yielded = True + yield f"[[{name}]]\n" if inside_aot else f"[{name}]\n" + + if literals: + yielded = True + for k, v in literals: + yield f"{format_key_part(k)} = {format_literal(v, ctx)}\n" + + for k, v, in_aot in tables: + if yielded: + yield "\n" + else: + yielded = True + key_part = format_key_part(k) + display_name = f"{name}.{key_part}" if name else key_part + yield from gen_table_chunks(v, ctx, name=display_name, inside_aot=in_aot) + + +def format_literal(obj: object, ctx: Context, *, nest_level: int = 0) -> str: + if isinstance(obj, bool): + return "true" if obj else "false" + if isinstance(obj, (int, float, date, datetime)): + return str(obj) + if isinstance(obj, time): + if obj.tzinfo: + raise ValueError("TOML does not support offset times") + return str(obj) + if isinstance(obj, str): + return format_string(obj, allow_multiline=ctx.allow_multiline) + if isinstance(obj, ARRAY_TYPES): + return format_inline_array(obj, ctx, nest_level) + if isinstance(obj, Mapping): + return format_inline_table(obj, ctx) + + # Lazy import to improve module import time + from decimal import Decimal + + if isinstance(obj, Decimal): + return format_decimal(obj) + raise TypeError( + f"Object of type '{type(obj).__qualname__}' is not TOML serializable" + ) + + +def format_decimal(obj: Decimal) -> str: + if obj.is_nan(): + return "nan" + if obj.is_infinite(): + return "-inf" if obj.is_signed() else "inf" + dec_str = str(obj).lower() + return dec_str if "." in dec_str or "e" in dec_str else dec_str + ".0" + + +def format_inline_table(obj: Mapping, ctx: Context) -> str: + # check cache first + obj_id = id(obj) + if obj_id in ctx.inline_table_cache: + return ctx.inline_table_cache[obj_id] + + if not obj: + rendered = "{}" + else: + rendered = ( + "{ " + + ", ".join( + f"{format_key_part(k)} = {format_literal(v, ctx)}" + for k, v in obj.items() + ) + + " }" + ) + ctx.inline_table_cache[obj_id] = rendered + return rendered + + +def format_inline_array(obj: tuple | list, ctx: Context, nest_level: int) -> str: + if not obj: + return "[]" + item_indent = ctx.indent_str * (1 + nest_level) + closing_bracket_indent = ctx.indent_str * nest_level + return ( + "[\n" + + ",\n".join( + item_indent + format_literal(item, ctx, nest_level=nest_level + 1) + for item in obj + ) + + f",\n{closing_bracket_indent}]" + ) + + +def format_key_part(part: str) -> str: + try: + only_bare_key_chars = BARE_KEY_CHARS.issuperset(part) + except TypeError: + raise TypeError( + f"Invalid mapping key '{part}' of type '{type(part).__qualname__}'." + " A string is required." + ) from None + + if part and only_bare_key_chars: + return part + return format_string(part, allow_multiline=False) + + +def format_string(s: str, *, allow_multiline: bool) -> str: + do_multiline = allow_multiline and "\n" in s + if do_multiline: + result = '"""\n' + s = s.replace("\r\n", "\n") + else: + result = '"' + + pos = seq_start = 0 + while True: + try: + char = s[pos] + except IndexError: + result += s[seq_start:pos] + if do_multiline: + return result + '"""' + return result + '"' + if char in ILLEGAL_BASIC_STR_CHARS: + result += s[seq_start:pos] + if char in COMPACT_ESCAPES: + if do_multiline and char == "\n": + result += "\n" + else: + result += COMPACT_ESCAPES[char] + else: + result += "\\u" + hex(ord(char))[2:].rjust(4, "0") + seq_start = pos + 1 + pos += 1 + + +def is_aot(obj: Any) -> bool: + """Decides if an object behaves as an array of tables (i.e. a nonempty list + of dicts).""" + return bool( + isinstance(obj, ARRAY_TYPES) + and obj + and all(isinstance(v, Mapping) for v in obj) + ) + + +def is_suitable_inline_table(obj: Mapping, ctx: Context) -> bool: + """Use heuristics to decide if the inline-style representation is a good + choice for a given table.""" + rendered_inline = f"{ctx.indent_str}{format_inline_table(obj, ctx)}," + return len(rendered_inline) <= MAX_LINE_LENGTH and "\n" not in rendered_inline diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/py.typed new file mode 100644 index 00000000..7632ecf7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561 diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/LICENSE b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/LICENSE new file mode 100644 index 00000000..7ec568c1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 Seth Michael Larson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/__init__.py index 59930f45..0166459b 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/__init__.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/__init__.py @@ -5,9 +5,32 @@ import sys as _sys if _sys.version_info < (3, 10): raise ImportError("truststore requires Python 3.10 or later") +# Detect Python runtimes which don't implement SSLObject.get_unverified_chain() API +# This API only became public in Python 3.13 but was available in CPython and PyPy since 3.10. +if _sys.version_info < (3, 13) and _sys.implementation.name not in ("cpython", "pypy"): + try: + import ssl as _ssl + except ImportError: + raise ImportError("truststore requires the 'ssl' module") + else: + _sslmem = _ssl.MemoryBIO() + _sslobj = _ssl.create_default_context().wrap_bio( + _sslmem, + _sslmem, + ) + try: + while not hasattr(_sslobj, "get_unverified_chain"): + _sslobj = _sslobj._sslobj # type: ignore[attr-defined] + except AttributeError: + raise ImportError( + "truststore requires peer certificate chain APIs to be available" + ) from None + + del _ssl, _sslobj, _sslmem # noqa: F821 + from ._api import SSLContext, extract_from_ssl, inject_into_ssl # noqa: E402 del _api, _sys # type: ignore[name-defined] # noqa: F821 __all__ = ["SSLContext", "inject_into_ssl", "extract_from_ssl"] -__version__ = "0.8.0" +__version__ = "0.10.4" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_api.py b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_api.py index 829aff72..a54ff9f2 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_api.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_api.py @@ -1,10 +1,13 @@ +import contextlib import os import platform import socket import ssl +import sys +import threading import typing -import _ssl # type: ignore[import] +import _ssl from ._ssl_constants import ( _original_SSLContext, @@ -21,7 +24,7 @@ else: from ._openssl import _configure_context, _verify_peercerts_impl if typing.TYPE_CHECKING: - from pip._vendor.typing_extensions import Buffer + from typing_extensions import Buffer # From typeshed/stdlib/ssl.pyi _StrOrBytesPath: typing.TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes] @@ -42,6 +45,23 @@ def inject_into_ssl() -> None: except ImportError: pass + # requests starting with 2.32.0 added a preloaded SSL context to improve concurrent performance; + # this unfortunately leads to a RecursionError, which can be avoided by patching the preloaded SSL context with + # the truststore patched instance + # also see https://github.com/psf/requests/pull/6667 + try: + from pip._vendor.requests import adapters as requests_adapters + + preloaded_context = getattr(requests_adapters, "_preloaded_ssl_context", None) + if preloaded_context is not None: + setattr( + requests_adapters, + "_preloaded_ssl_context", + SSLContext(ssl.PROTOCOL_TLS_CLIENT), + ) + except ImportError: + pass + def extract_from_ssl() -> None: """Restores the :class:`ssl.SSLContext` class to its original state""" @@ -66,6 +86,7 @@ class SSLContext(_truststore_SSLContext_super_class): # type: ignore[misc] def __init__(self, protocol: int = None) -> None: # type: ignore[assignment] self._ctx = _original_SSLContext(protocol) + self._ctx_lock = threading.Lock() class TruststoreSSLObject(ssl.SSLObject): # This object exists because wrap_bio() doesn't @@ -88,10 +109,15 @@ class SSLContext(_truststore_SSLContext_super_class): # type: ignore[misc] server_hostname: str | None = None, session: ssl.SSLSession | None = None, ) -> ssl.SSLSocket: - # Use a context manager here because the - # inner SSLContext holds on to our state - # but also does the actual handshake. - with _configure_context(self._ctx): + + # We need to lock around the .__enter__() + # but we don't need to lock within the + # context manager, so we need to expand the + # syntactic sugar of the `with` statement. + with contextlib.ExitStack() as stack: + with self._ctx_lock: + stack.enter_context(_configure_context(self._ctx)) + ssl_sock = self._ctx.wrap_socket( sock, server_side=server_side, @@ -168,19 +194,19 @@ class SSLContext(_truststore_SSLContext_super_class): # type: ignore[misc] def cert_store_stats(self) -> dict[str, int]: raise NotImplementedError() + def set_default_verify_paths(self) -> None: + self._ctx.set_default_verify_paths() + @typing.overload def get_ca_certs( self, binary_form: typing.Literal[False] = ... - ) -> list[typing.Any]: - ... + ) -> list[typing.Any]: ... @typing.overload - def get_ca_certs(self, binary_form: typing.Literal[True] = ...) -> list[bytes]: - ... + def get_ca_certs(self, binary_form: typing.Literal[True] = ...) -> list[bytes]: ... @typing.overload - def get_ca_certs(self, binary_form: bool = ...) -> typing.Any: - ... + def get_ca_certs(self, binary_form: bool = ...) -> typing.Any: ... def get_ca_certs(self, binary_form: bool = False) -> list[typing.Any] | list[bytes]: raise NotImplementedError() @@ -276,6 +302,25 @@ class SSLContext(_truststore_SSLContext_super_class): # type: ignore[misc] ) +# Python 3.13+ makes get_unverified_chain() a public API that only returns DER +# encoded certificates. We detect whether we need to call public_bytes() for 3.10->3.12 +# Pre-3.13 returned None instead of an empty list from get_unverified_chain() +if sys.version_info >= (3, 13): + + def _get_unverified_chain_bytes(sslobj: ssl.SSLObject) -> list[bytes]: + unverified_chain = sslobj.get_unverified_chain() or () + return [ + cert if isinstance(cert, bytes) else cert.public_bytes(_ssl.ENCODING_DER) + for cert in unverified_chain + ] + +else: + + def _get_unverified_chain_bytes(sslobj: ssl.SSLObject) -> list[bytes]: + unverified_chain = sslobj.get_unverified_chain() or () # type: ignore[attr-defined] + return [cert.public_bytes(_ssl.ENCODING_DER) for cert in unverified_chain] + + def _verify_peercerts( sock_or_sslobj: ssl.SSLSocket | ssl.SSLObject, server_hostname: str | None ) -> None: @@ -290,13 +335,7 @@ def _verify_peercerts( except AttributeError: pass - # SSLObject.get_unverified_chain() returns 'None' - # if the peer sends no certificates. This is common - # for the server-side scenario. - unverified_chain: typing.Sequence[_ssl.Certificate] = ( - sslobj.get_unverified_chain() or () # type: ignore[attr-defined] - ) - cert_bytes = [cert.public_bytes(_ssl.ENCODING_DER) for cert in unverified_chain] + cert_bytes = _get_unverified_chain_bytes(sslobj) _verify_peercerts_impl( sock_or_sslobj.context, cert_bytes, server_hostname=server_hostname ) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_macos.py b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_macos.py index 7dc440bf..34503077 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_macos.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_macos.py @@ -25,6 +25,8 @@ if _mac_version_info < (10, 8): f"Only OS X 10.8 and newer are supported, not {_mac_version_info[0]}.{_mac_version_info[1]}" ) +_is_macos_version_10_14_or_later = _mac_version_info >= (10, 14) + def _load_cdll(name: str, macos10_16_path: str) -> CDLL: """Loads a CDLL by name, falling back to known path on 10.16+""" @@ -96,9 +98,6 @@ try: Security.SecTrustSetAnchorCertificatesOnly.argtypes = [SecTrustRef, Boolean] Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus - Security.SecTrustEvaluate.argtypes = [SecTrustRef, POINTER(SecTrustResultType)] - Security.SecTrustEvaluate.restype = OSStatus - Security.SecPolicyCreateRevocation.argtypes = [CFOptionFlags] Security.SecPolicyCreateRevocation.restype = SecPolicyRef @@ -118,6 +117,12 @@ try: ] Security.SecTrustGetTrustResult.restype = OSStatus + Security.SecTrustEvaluate.argtypes = [ + SecTrustRef, + POINTER(SecTrustResultType), + ] + Security.SecTrustEvaluate.restype = OSStatus + Security.SecTrustRef = SecTrustRef # type: ignore[attr-defined] Security.SecTrustResultType = SecTrustResultType # type: ignore[attr-defined] Security.OSStatus = OSStatus # type: ignore[attr-defined] @@ -200,8 +205,19 @@ try: CoreFoundation.CFStringRef = CFStringRef # type: ignore[attr-defined] CoreFoundation.CFErrorRef = CFErrorRef # type: ignore[attr-defined] -except AttributeError: - raise ImportError("Error initializing ctypes") from None +except AttributeError as e: + raise ImportError(f"Error initializing ctypes: {e}") from None + +# SecTrustEvaluateWithError is macOS 10.14+ +if _is_macos_version_10_14_or_later: + try: + Security.SecTrustEvaluateWithError.argtypes = [ + SecTrustRef, + POINTER(CFErrorRef), + ] + Security.SecTrustEvaluateWithError.restype = c_bool + except AttributeError as e: + raise ImportError(f"Error initializing ctypes: {e}") from None def _handle_osstatus(result: OSStatus, _: typing.Any, args: typing.Any) -> typing.Any: @@ -259,7 +275,9 @@ def _handle_osstatus(result: OSStatus, _: typing.Any, args: typing.Any) -> typin Security.SecTrustCreateWithCertificates.errcheck = _handle_osstatus # type: ignore[assignment] Security.SecTrustSetAnchorCertificates.errcheck = _handle_osstatus # type: ignore[assignment] +Security.SecTrustSetAnchorCertificatesOnly.errcheck = _handle_osstatus # type: ignore[assignment] Security.SecTrustGetTrustResult.errcheck = _handle_osstatus # type: ignore[assignment] +Security.SecTrustEvaluate.errcheck = _handle_osstatus # type: ignore[assignment] class CFConst: @@ -367,9 +385,10 @@ def _verify_peercerts_impl( certs = None policies = None trust = None - cf_error = None try: - if server_hostname is not None: + # Only set a hostname on the policy if we're verifying the hostname + # on the leaf certificate. + if server_hostname is not None and ssl_context.check_hostname: cf_str_hostname = None try: cf_str_hostname = _bytes_to_cf_string(server_hostname.encode("ascii")) @@ -417,85 +436,136 @@ def _verify_peercerts_impl( CoreFoundation.CFRelease(certs) # If there are additional trust anchors to load we need to transform - # the list of DER-encoded certificates into a CFArray. Otherwise - # pass 'None' to signal that we only want system / fetched certificates. + # the list of DER-encoded certificates into a CFArray. ctx_ca_certs_der: list[bytes] | None = ssl_context.get_ca_certs( binary_form=True ) if ctx_ca_certs_der: ctx_ca_certs = None try: - ctx_ca_certs = _der_certs_to_cf_cert_array(cert_chain) + ctx_ca_certs = _der_certs_to_cf_cert_array(ctx_ca_certs_der) Security.SecTrustSetAnchorCertificates(trust, ctx_ca_certs) finally: if ctx_ca_certs: CoreFoundation.CFRelease(ctx_ca_certs) + + # We always want system certificates. + Security.SecTrustSetAnchorCertificatesOnly(trust, False) + + # macOS 10.13 and earlier don't support SecTrustEvaluateWithError() + # so we use SecTrustEvaluate() which means we need to construct error + # messages ourselves. + if _is_macos_version_10_14_or_later: + _verify_peercerts_impl_macos_10_14(ssl_context, trust) else: - Security.SecTrustSetAnchorCertificates(trust, None) - - cf_error = CoreFoundation.CFErrorRef() - sec_trust_eval_result = Security.SecTrustEvaluateWithError( - trust, ctypes.byref(cf_error) - ) - # sec_trust_eval_result is a bool (0 or 1) - # where 1 means that the certs are trusted. - if sec_trust_eval_result == 1: - is_trusted = True - elif sec_trust_eval_result == 0: - is_trusted = False - else: - raise ssl.SSLError( - f"Unknown result from Security.SecTrustEvaluateWithError: {sec_trust_eval_result!r}" - ) - - cf_error_code = 0 - if not is_trusted: - cf_error_code = CoreFoundation.CFErrorGetCode(cf_error) - - # If the error is a known failure that we're - # explicitly okay with from SSLContext configuration - # we can set is_trusted accordingly. - if ssl_context.verify_mode != ssl.CERT_REQUIRED and ( - cf_error_code == CFConst.errSecNotTrusted - or cf_error_code == CFConst.errSecCertificateExpired - ): - is_trusted = True - elif ( - not ssl_context.check_hostname - and cf_error_code == CFConst.errSecHostNameMismatch - ): - is_trusted = True - - # If we're still not trusted then we start to - # construct and raise the SSLCertVerificationError. - if not is_trusted: - cf_error_string_ref = None - try: - cf_error_string_ref = CoreFoundation.CFErrorCopyDescription(cf_error) - - # Can this ever return 'None' if there's a CFError? - cf_error_message = ( - _cf_string_ref_to_str(cf_error_string_ref) - or "Certificate verification failed" - ) - - # TODO: Not sure if we need the SecTrustResultType for anything? - # We only care whether or not it's a success or failure for now. - sec_trust_result_type = Security.SecTrustResultType() - Security.SecTrustGetTrustResult( - trust, ctypes.byref(sec_trust_result_type) - ) - - err = ssl.SSLCertVerificationError(cf_error_message) - err.verify_message = cf_error_message - err.verify_code = cf_error_code - raise err - finally: - if cf_error_string_ref: - CoreFoundation.CFRelease(cf_error_string_ref) - + _verify_peercerts_impl_macos_10_13(ssl_context, trust) finally: if policies: CoreFoundation.CFRelease(policies) if trust: CoreFoundation.CFRelease(trust) + + +def _verify_peercerts_impl_macos_10_13( + ssl_context: ssl.SSLContext, sec_trust_ref: typing.Any +) -> None: + """Verify using 'SecTrustEvaluate' API for macOS 10.13 and earlier. + macOS 10.14 added the 'SecTrustEvaluateWithError' API. + """ + sec_trust_result_type = Security.SecTrustResultType() + Security.SecTrustEvaluate(sec_trust_ref, ctypes.byref(sec_trust_result_type)) + + try: + sec_trust_result_type_as_int = int(sec_trust_result_type.value) + except (ValueError, TypeError): + sec_trust_result_type_as_int = -1 + + # Apple doesn't document these values in their own API docs. + # See: https://github.com/xybp888/iOS-SDKs/blob/master/iPhoneOS13.0.sdk/System/Library/Frameworks/Security.framework/Headers/SecTrust.h#L84 + if ( + ssl_context.verify_mode == ssl.CERT_REQUIRED + and sec_trust_result_type_as_int not in (1, 4) + ): + # Note that we're not able to ignore only hostname errors + # for macOS 10.13 and earlier, so check_hostname=False will + # still return an error. + sec_trust_result_type_to_message = { + 0: "Invalid trust result type", + # 1: "Trust evaluation succeeded", + 2: "User confirmation required", + 3: "User specified that certificate is not trusted", + # 4: "Trust result is unspecified", + 5: "Recoverable trust failure occurred", + 6: "Fatal trust failure occurred", + 7: "Other error occurred, certificate may be revoked", + } + error_message = sec_trust_result_type_to_message.get( + sec_trust_result_type_as_int, + f"Unknown trust result: {sec_trust_result_type_as_int}", + ) + + err = ssl.SSLCertVerificationError(error_message) + err.verify_message = error_message + err.verify_code = sec_trust_result_type_as_int + raise err + + +def _verify_peercerts_impl_macos_10_14( + ssl_context: ssl.SSLContext, sec_trust_ref: typing.Any +) -> None: + """Verify using 'SecTrustEvaluateWithError' API for macOS 10.14+.""" + cf_error = CoreFoundation.CFErrorRef() + sec_trust_eval_result = Security.SecTrustEvaluateWithError( + sec_trust_ref, ctypes.byref(cf_error) + ) + # sec_trust_eval_result is a bool (0 or 1) + # where 1 means that the certs are trusted. + if sec_trust_eval_result == 1: + is_trusted = True + elif sec_trust_eval_result == 0: + is_trusted = False + else: + raise ssl.SSLError( + f"Unknown result from Security.SecTrustEvaluateWithError: {sec_trust_eval_result!r}" + ) + + cf_error_code = 0 + if not is_trusted: + cf_error_code = CoreFoundation.CFErrorGetCode(cf_error) + + # If the error is a known failure that we're + # explicitly okay with from SSLContext configuration + # we can set is_trusted accordingly. + if ssl_context.verify_mode != ssl.CERT_REQUIRED and ( + cf_error_code == CFConst.errSecNotTrusted + or cf_error_code == CFConst.errSecCertificateExpired + ): + is_trusted = True + + # If we're still not trusted then we start to + # construct and raise the SSLCertVerificationError. + if not is_trusted: + cf_error_string_ref = None + try: + cf_error_string_ref = CoreFoundation.CFErrorCopyDescription(cf_error) + + # Can this ever return 'None' if there's a CFError? + cf_error_message = ( + _cf_string_ref_to_str(cf_error_string_ref) + or "Certificate verification failed" + ) + + # TODO: Not sure if we need the SecTrustResultType for anything? + # We only care whether or not it's a success or failure for now. + sec_trust_result_type = Security.SecTrustResultType() + Security.SecTrustGetTrustResult( + sec_trust_ref, ctypes.byref(sec_trust_result_type) + ) + + err = ssl.SSLCertVerificationError(cf_error_message) + err.verify_message = cf_error_message + err.verify_code = cf_error_code + raise err + finally: + if cf_error_string_ref: + CoreFoundation.CFRelease(cf_error_string_ref) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_openssl.py b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_openssl.py index 9951cf75..3e25a567 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_openssl.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_openssl.py @@ -6,8 +6,10 @@ import typing # candidates based on https://github.com/tiran/certifi-system-store by Christian Heimes _CA_FILE_CANDIDATES = [ - # Alpine, Arch, Fedora 34+, OpenWRT, RHEL 9+, BSD + # Alpine, Arch, Fedora 34-42, OpenWRT, RHEL 9-10, BSD "/etc/ssl/cert.pem", + # Fedora 43+, RHEL 11+ + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", # Fedora <= 34, RHEL <= 9, CentOS <= 9 "/etc/pki/tls/cert.pem", # Debian, Ubuntu (requires ca-certificates) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_windows.py b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_windows.py index 3de4960a..a9bf9abd 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_windows.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/_windows.py @@ -212,6 +212,7 @@ CERT_CHAIN_POLICY_IGNORE_INVALID_POLICY_FLAG = 0x00000080 CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS = 0x00000F00 CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG = 0x00008000 CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG = 0x00004000 +SECURITY_FLAG_IGNORE_CERT_CN_INVALID = 0x00001000 AUTHTYPE_SERVER = 2 CERT_CHAIN_POLICY_SSL = 4 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 @@ -325,6 +326,12 @@ def _verify_peercerts_impl( server_hostname: str | None = None, ) -> None: """Verify the cert_chain from the server using Windows APIs.""" + + # If the peer didn't send any certificates then + # we can't do verification. Raise an error. + if not cert_chain: + raise ssl.SSLCertVerificationError("Peer sent no certificates to verify") + pCertContext = None hIntermediateCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, None, 0, None) try: @@ -375,7 +382,7 @@ def _verify_peercerts_impl( server_hostname, chain_flags=chain_flags, ) - except ssl.SSLCertVerificationError: + except ssl.SSLCertVerificationError as e: # If that fails but custom CA certs have been added # to the SSLContext using load_verify_locations, # try verifying using a custom chain engine @@ -384,15 +391,19 @@ def _verify_peercerts_impl( binary_form=True ) if custom_ca_certs: - _verify_using_custom_ca_certs( - ssl_context, - custom_ca_certs, - hIntermediateCertStore, - pCertContext, - pChainPara, - server_hostname, - chain_flags=chain_flags, - ) + try: + _verify_using_custom_ca_certs( + ssl_context, + custom_ca_certs, + hIntermediateCertStore, + pCertContext, + pChainPara, + server_hostname, + chain_flags=chain_flags, + ) + # Raise the original error, not the new error. + except ssl.SSLCertVerificationError: + raise e from None else: raise finally: @@ -433,6 +444,10 @@ def _get_and_verify_cert_chain( ) ssl_extra_cert_chain_policy_para.dwAuthType = AUTHTYPE_SERVER ssl_extra_cert_chain_policy_para.fdwChecks = 0 + if ssl_context.check_hostname is False: + ssl_extra_cert_chain_policy_para.fdwChecks = ( + SECURITY_FLAG_IGNORE_CERT_CN_INVALID + ) if server_hostname: ssl_extra_cert_chain_policy_para.pwszServerName = c_wchar_p(server_hostname) @@ -442,8 +457,6 @@ def _get_and_verify_cert_chain( ) if ssl_context.verify_mode == ssl.CERT_NONE: chain_policy.dwFlags |= CERT_CHAIN_POLICY_VERIFY_MODE_NONE_FLAGS - if not ssl_context.check_hostname: - chain_policy.dwFlags |= CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG chain_policy.cbSize = sizeof(chain_policy) pPolicyPara = pointer(chain_policy) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/truststore/py.typed b/venv/lib/python3.12/site-packages/pip/_vendor/truststore/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/typing_extensions.py b/venv/lib/python3.12/site-packages/pip/_vendor/typing_extensions.py deleted file mode 100644 index 4f93acff..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/typing_extensions.py +++ /dev/null @@ -1,3072 +0,0 @@ -import abc -import collections -import collections.abc -import functools -import inspect -import operator -import sys -import types as _types -import typing -import warnings - -__all__ = [ - # Super-special typing primitives. - 'Any', - 'ClassVar', - 'Concatenate', - 'Final', - 'LiteralString', - 'ParamSpec', - 'ParamSpecArgs', - 'ParamSpecKwargs', - 'Self', - 'Type', - 'TypeVar', - 'TypeVarTuple', - 'Unpack', - - # ABCs (from collections.abc). - 'Awaitable', - 'AsyncIterator', - 'AsyncIterable', - 'Coroutine', - 'AsyncGenerator', - 'AsyncContextManager', - 'Buffer', - 'ChainMap', - - # Concrete collection types. - 'ContextManager', - 'Counter', - 'Deque', - 'DefaultDict', - 'NamedTuple', - 'OrderedDict', - 'TypedDict', - - # Structural checks, a.k.a. protocols. - 'SupportsAbs', - 'SupportsBytes', - 'SupportsComplex', - 'SupportsFloat', - 'SupportsIndex', - 'SupportsInt', - 'SupportsRound', - - # One-off things. - 'Annotated', - 'assert_never', - 'assert_type', - 'clear_overloads', - 'dataclass_transform', - 'deprecated', - 'get_overloads', - 'final', - 'get_args', - 'get_origin', - 'get_original_bases', - 'get_protocol_members', - 'get_type_hints', - 'IntVar', - 'is_protocol', - 'is_typeddict', - 'Literal', - 'NewType', - 'overload', - 'override', - 'Protocol', - 'reveal_type', - 'runtime', - 'runtime_checkable', - 'Text', - 'TypeAlias', - 'TypeAliasType', - 'TypeGuard', - 'TYPE_CHECKING', - 'Never', - 'NoReturn', - 'Required', - 'NotRequired', - - # Pure aliases, have always been in typing - 'AbstractSet', - 'AnyStr', - 'BinaryIO', - 'Callable', - 'Collection', - 'Container', - 'Dict', - 'ForwardRef', - 'FrozenSet', - 'Generator', - 'Generic', - 'Hashable', - 'IO', - 'ItemsView', - 'Iterable', - 'Iterator', - 'KeysView', - 'List', - 'Mapping', - 'MappingView', - 'Match', - 'MutableMapping', - 'MutableSequence', - 'MutableSet', - 'Optional', - 'Pattern', - 'Reversible', - 'Sequence', - 'Set', - 'Sized', - 'TextIO', - 'Tuple', - 'Union', - 'ValuesView', - 'cast', - 'no_type_check', - 'no_type_check_decorator', -] - -# for backward compatibility -PEP_560 = True -GenericMeta = type - -# The functions below are modified copies of typing internal helpers. -# They are needed by _ProtocolMeta and they provide support for PEP 646. - - -class _Sentinel: - def __repr__(self): - return "" - - -_marker = _Sentinel() - - -def _check_generic(cls, parameters, elen=_marker): - """Check correct count for parameters of a generic cls (internal helper). - This gives a nice error message in case of count mismatch. - """ - if not elen: - raise TypeError(f"{cls} is not a generic class") - if elen is _marker: - if not hasattr(cls, "__parameters__") or not cls.__parameters__: - raise TypeError(f"{cls} is not a generic class") - elen = len(cls.__parameters__) - alen = len(parameters) - if alen != elen: - if hasattr(cls, "__parameters__"): - parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] - num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) - if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples): - return - raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" - f" actual {alen}, expected {elen}") - - -if sys.version_info >= (3, 10): - def _should_collect_from_parameters(t): - return isinstance( - t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType) - ) -elif sys.version_info >= (3, 9): - def _should_collect_from_parameters(t): - return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) -else: - def _should_collect_from_parameters(t): - return isinstance(t, typing._GenericAlias) and not t._special - - -def _collect_type_vars(types, typevar_types=None): - """Collect all type variable contained in types in order of - first appearance (lexicographic order). For example:: - - _collect_type_vars((T, List[S, T])) == (T, S) - """ - if typevar_types is None: - typevar_types = typing.TypeVar - tvars = [] - for t in types: - if ( - isinstance(t, typevar_types) and - t not in tvars and - not _is_unpack(t) - ): - tvars.append(t) - if _should_collect_from_parameters(t): - tvars.extend([t for t in t.__parameters__ if t not in tvars]) - return tuple(tvars) - - -NoReturn = typing.NoReturn - -# Some unconstrained type variables. These are used by the container types. -# (These are not for export.) -T = typing.TypeVar('T') # Any type. -KT = typing.TypeVar('KT') # Key type. -VT = typing.TypeVar('VT') # Value type. -T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. -T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. - - -if sys.version_info >= (3, 11): - from typing import Any -else: - - class _AnyMeta(type): - def __instancecheck__(self, obj): - if self is Any: - raise TypeError("typing_extensions.Any cannot be used with isinstance()") - return super().__instancecheck__(obj) - - def __repr__(self): - if self is Any: - return "typing_extensions.Any" - return super().__repr__() - - class Any(metaclass=_AnyMeta): - """Special type indicating an unconstrained type. - - Any is compatible with every type. - - Any assumed to have all methods. - - All values assumed to be instances of Any. - Note that all the above statements are true from the point of view of - static type checkers. At runtime, Any should not be used with instance - checks. - """ - def __new__(cls, *args, **kwargs): - if cls is Any: - raise TypeError("Any cannot be instantiated") - return super().__new__(cls, *args, **kwargs) - - -ClassVar = typing.ClassVar - - -class _ExtensionsSpecialForm(typing._SpecialForm, _root=True): - def __repr__(self): - return 'typing_extensions.' + self._name - - -# On older versions of typing there is an internal class named "Final". -# 3.8+ -if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7): - Final = typing.Final -# 3.7 -else: - class _FinalForm(_ExtensionsSpecialForm, _root=True): - def __getitem__(self, parameters): - item = typing._type_check(parameters, - f'{self._name} accepts only a single type.') - return typing._GenericAlias(self, (item,)) - - Final = _FinalForm('Final', - doc="""A special typing construct to indicate that a name - cannot be re-assigned or overridden in a subclass. - For example: - - MAX_SIZE: Final = 9000 - MAX_SIZE += 1 # Error reported by type checker - - class Connection: - TIMEOUT: Final[int] = 10 - class FastConnector(Connection): - TIMEOUT = 1 # Error reported by type checker - - There is no runtime checking of these properties.""") - -if sys.version_info >= (3, 11): - final = typing.final -else: - # @final exists in 3.8+, but we backport it for all versions - # before 3.11 to keep support for the __final__ attribute. - # See https://bugs.python.org/issue46342 - def final(f): - """This decorator can be used to indicate to type checkers that - the decorated method cannot be overridden, and decorated class - cannot be subclassed. For example: - - class Base: - @final - def done(self) -> None: - ... - class Sub(Base): - def done(self) -> None: # Error reported by type checker - ... - @final - class Leaf: - ... - class Other(Leaf): # Error reported by type checker - ... - - There is no runtime checking of these properties. The decorator - sets the ``__final__`` attribute to ``True`` on the decorated object - to allow runtime introspection. - """ - try: - f.__final__ = True - except (AttributeError, TypeError): - # Skip the attribute silently if it is not writable. - # AttributeError happens if the object has __slots__ or a - # read-only property, TypeError if it's a builtin class. - pass - return f - - -def IntVar(name): - return typing.TypeVar(name) - - -# A Literal bug was fixed in 3.11.0, 3.10.1 and 3.9.8 -if sys.version_info >= (3, 10, 1): - Literal = typing.Literal -else: - def _flatten_literal_params(parameters): - """An internal helper for Literal creation: flatten Literals among parameters""" - params = [] - for p in parameters: - if isinstance(p, _LiteralGenericAlias): - params.extend(p.__args__) - else: - params.append(p) - return tuple(params) - - def _value_and_type_iter(params): - for p in params: - yield p, type(p) - - class _LiteralGenericAlias(typing._GenericAlias, _root=True): - def __eq__(self, other): - if not isinstance(other, _LiteralGenericAlias): - return NotImplemented - these_args_deduped = set(_value_and_type_iter(self.__args__)) - other_args_deduped = set(_value_and_type_iter(other.__args__)) - return these_args_deduped == other_args_deduped - - def __hash__(self): - return hash(frozenset(_value_and_type_iter(self.__args__))) - - class _LiteralForm(_ExtensionsSpecialForm, _root=True): - def __init__(self, doc: str): - self._name = 'Literal' - self._doc = self.__doc__ = doc - - def __getitem__(self, parameters): - if not isinstance(parameters, tuple): - parameters = (parameters,) - - parameters = _flatten_literal_params(parameters) - - val_type_pairs = list(_value_and_type_iter(parameters)) - try: - deduped_pairs = set(val_type_pairs) - except TypeError: - # unhashable parameters - pass - else: - # similar logic to typing._deduplicate on Python 3.9+ - if len(deduped_pairs) < len(val_type_pairs): - new_parameters = [] - for pair in val_type_pairs: - if pair in deduped_pairs: - new_parameters.append(pair[0]) - deduped_pairs.remove(pair) - assert not deduped_pairs, deduped_pairs - parameters = tuple(new_parameters) - - return _LiteralGenericAlias(self, parameters) - - Literal = _LiteralForm(doc="""\ - A type that can be used to indicate to type checkers - that the corresponding value has a value literally equivalent - to the provided parameter. For example: - - var: Literal[4] = 4 - - The type checker understands that 'var' is literally equal to - the value 4 and no other value. - - Literal[...] cannot be subclassed. There is no runtime - checking verifying that the parameter is actually a value - instead of a type.""") - - -_overload_dummy = typing._overload_dummy - - -if hasattr(typing, "get_overloads"): # 3.11+ - overload = typing.overload - get_overloads = typing.get_overloads - clear_overloads = typing.clear_overloads -else: - # {module: {qualname: {firstlineno: func}}} - _overload_registry = collections.defaultdict( - functools.partial(collections.defaultdict, dict) - ) - - def overload(func): - """Decorator for overloaded functions/methods. - - In a stub file, place two or more stub definitions for the same - function in a row, each decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - - In a non-stub file (i.e. a regular .py file), do the same but - follow it with an implementation. The implementation should *not* - be decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - def utf8(value): - # implementation goes here - - The overloads for a function can be retrieved at runtime using the - get_overloads() function. - """ - # classmethod and staticmethod - f = getattr(func, "__func__", func) - try: - _overload_registry[f.__module__][f.__qualname__][ - f.__code__.co_firstlineno - ] = func - except AttributeError: - # Not a normal function; ignore. - pass - return _overload_dummy - - def get_overloads(func): - """Return all defined overloads for *func* as a sequence.""" - # classmethod and staticmethod - f = getattr(func, "__func__", func) - if f.__module__ not in _overload_registry: - return [] - mod_dict = _overload_registry[f.__module__] - if f.__qualname__ not in mod_dict: - return [] - return list(mod_dict[f.__qualname__].values()) - - def clear_overloads(): - """Clear all overloads in the registry.""" - _overload_registry.clear() - - -# This is not a real generic class. Don't use outside annotations. -Type = typing.Type - -# Various ABCs mimicking those in collections.abc. -# A few are simply re-exported for completeness. - - -Awaitable = typing.Awaitable -Coroutine = typing.Coroutine -AsyncIterable = typing.AsyncIterable -AsyncIterator = typing.AsyncIterator -Deque = typing.Deque -ContextManager = typing.ContextManager -AsyncContextManager = typing.AsyncContextManager -DefaultDict = typing.DefaultDict - -# 3.7.2+ -if hasattr(typing, 'OrderedDict'): - OrderedDict = typing.OrderedDict -# 3.7.0-3.7.2 -else: - OrderedDict = typing._alias(collections.OrderedDict, (KT, VT)) - -Counter = typing.Counter -ChainMap = typing.ChainMap -AsyncGenerator = typing.AsyncGenerator -Text = typing.Text -TYPE_CHECKING = typing.TYPE_CHECKING - - -_PROTO_ALLOWLIST = { - 'collections.abc': [ - 'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable', - 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', 'Buffer', - ], - 'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'], - 'typing_extensions': ['Buffer'], -} - - -_EXCLUDED_ATTRS = { - "__abstractmethods__", "__annotations__", "__weakref__", "_is_protocol", - "_is_runtime_protocol", "__dict__", "__slots__", "__parameters__", - "__orig_bases__", "__module__", "_MutableMapping__marker", "__doc__", - "__subclasshook__", "__orig_class__", "__init__", "__new__", - "__protocol_attrs__", "__callable_proto_members_only__", -} - -if sys.version_info < (3, 8): - _EXCLUDED_ATTRS |= { - "_gorg", "__next_in_mro__", "__extra__", "__tree_hash__", "__args__", - "__origin__" - } - -if sys.version_info >= (3, 9): - _EXCLUDED_ATTRS.add("__class_getitem__") - -if sys.version_info >= (3, 12): - _EXCLUDED_ATTRS.add("__type_params__") - -_EXCLUDED_ATTRS = frozenset(_EXCLUDED_ATTRS) - - -def _get_protocol_attrs(cls): - attrs = set() - for base in cls.__mro__[:-1]: # without object - if base.__name__ in {'Protocol', 'Generic'}: - continue - annotations = getattr(base, '__annotations__', {}) - for attr in (*base.__dict__, *annotations): - if (not attr.startswith('_abc_') and attr not in _EXCLUDED_ATTRS): - attrs.add(attr) - return attrs - - -def _maybe_adjust_parameters(cls): - """Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__. - - The contents of this function are very similar - to logic found in typing.Generic.__init_subclass__ - on the CPython main branch. - """ - tvars = [] - if '__orig_bases__' in cls.__dict__: - tvars = _collect_type_vars(cls.__orig_bases__) - # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn]. - # If found, tvars must be a subset of it. - # If not found, tvars is it. - # Also check for and reject plain Generic, - # and reject multiple Generic[...] and/or Protocol[...]. - gvars = None - for base in cls.__orig_bases__: - if (isinstance(base, typing._GenericAlias) and - base.__origin__ in (typing.Generic, Protocol)): - # for error messages - the_base = base.__origin__.__name__ - if gvars is not None: - raise TypeError( - "Cannot inherit from Generic[...]" - " and/or Protocol[...] multiple types.") - gvars = base.__parameters__ - if gvars is None: - gvars = tvars - else: - tvarset = set(tvars) - gvarset = set(gvars) - if not tvarset <= gvarset: - s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) - s_args = ', '.join(str(g) for g in gvars) - raise TypeError(f"Some type variables ({s_vars}) are" - f" not listed in {the_base}[{s_args}]") - tvars = gvars - cls.__parameters__ = tuple(tvars) - - -def _caller(depth=2): - try: - return sys._getframe(depth).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): # For platforms without _getframe() - return None - - -# The performance of runtime-checkable protocols is significantly improved on Python 3.12, -# so we backport the 3.12 version of Protocol to Python <=3.11 -if sys.version_info >= (3, 12): - Protocol = typing.Protocol -else: - def _allow_reckless_class_checks(depth=3): - """Allow instance and class checks for special stdlib modules. - The abc and functools modules indiscriminately call isinstance() and - issubclass() on the whole MRO of a user class, which may contain protocols. - """ - return _caller(depth) in {'abc', 'functools', None} - - def _no_init(self, *args, **kwargs): - if type(self)._is_protocol: - raise TypeError('Protocols cannot be instantiated') - - if sys.version_info >= (3, 8): - # Inheriting from typing._ProtocolMeta isn't actually desirable, - # but is necessary to allow typing.Protocol and typing_extensions.Protocol - # to mix without getting TypeErrors about "metaclass conflict" - _typing_Protocol = typing.Protocol - _ProtocolMetaBase = type(_typing_Protocol) - else: - _typing_Protocol = _marker - _ProtocolMetaBase = abc.ABCMeta - - class _ProtocolMeta(_ProtocolMetaBase): - # This metaclass is somewhat unfortunate, - # but is necessary for several reasons... - # - # NOTE: DO NOT call super() in any methods in this class - # That would call the methods on typing._ProtocolMeta on Python 3.8-3.11 - # and those are slow - def __new__(mcls, name, bases, namespace, **kwargs): - if name == "Protocol" and len(bases) < 2: - pass - elif {Protocol, _typing_Protocol} & set(bases): - for base in bases: - if not ( - base in {object, typing.Generic, Protocol, _typing_Protocol} - or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, []) - or is_protocol(base) - ): - raise TypeError( - f"Protocols can only inherit from other protocols, " - f"got {base!r}" - ) - return abc.ABCMeta.__new__(mcls, name, bases, namespace, **kwargs) - - def __init__(cls, *args, **kwargs): - abc.ABCMeta.__init__(cls, *args, **kwargs) - if getattr(cls, "_is_protocol", False): - cls.__protocol_attrs__ = _get_protocol_attrs(cls) - # PEP 544 prohibits using issubclass() - # with protocols that have non-method members. - cls.__callable_proto_members_only__ = all( - callable(getattr(cls, attr, None)) for attr in cls.__protocol_attrs__ - ) - - def __subclasscheck__(cls, other): - if cls is Protocol: - return type.__subclasscheck__(cls, other) - if ( - getattr(cls, '_is_protocol', False) - and not _allow_reckless_class_checks() - ): - if not isinstance(other, type): - # Same error message as for issubclass(1, int). - raise TypeError('issubclass() arg 1 must be a class') - if ( - not cls.__callable_proto_members_only__ - and cls.__dict__.get("__subclasshook__") is _proto_hook - ): - raise TypeError( - "Protocols with non-method members don't support issubclass()" - ) - if not getattr(cls, '_is_runtime_protocol', False): - raise TypeError( - "Instance and class checks can only be used with " - "@runtime_checkable protocols" - ) - return abc.ABCMeta.__subclasscheck__(cls, other) - - def __instancecheck__(cls, instance): - # We need this method for situations where attributes are - # assigned in __init__. - if cls is Protocol: - return type.__instancecheck__(cls, instance) - if not getattr(cls, "_is_protocol", False): - # i.e., it's a concrete subclass of a protocol - return abc.ABCMeta.__instancecheck__(cls, instance) - - if ( - not getattr(cls, '_is_runtime_protocol', False) and - not _allow_reckless_class_checks() - ): - raise TypeError("Instance and class checks can only be used with" - " @runtime_checkable protocols") - - if abc.ABCMeta.__instancecheck__(cls, instance): - return True - - for attr in cls.__protocol_attrs__: - try: - val = inspect.getattr_static(instance, attr) - except AttributeError: - break - if val is None and callable(getattr(cls, attr, None)): - break - else: - return True - - return False - - def __eq__(cls, other): - # Hack so that typing.Generic.__class_getitem__ - # treats typing_extensions.Protocol - # as equivalent to typing.Protocol on Python 3.8+ - if abc.ABCMeta.__eq__(cls, other) is True: - return True - return ( - cls is Protocol and other is getattr(typing, "Protocol", object()) - ) - - # This has to be defined, or the abc-module cache - # complains about classes with this metaclass being unhashable, - # if we define only __eq__! - def __hash__(cls) -> int: - return type.__hash__(cls) - - @classmethod - def _proto_hook(cls, other): - if not cls.__dict__.get('_is_protocol', False): - return NotImplemented - - for attr in cls.__protocol_attrs__: - for base in other.__mro__: - # Check if the members appears in the class dictionary... - if attr in base.__dict__: - if base.__dict__[attr] is None: - return NotImplemented - break - - # ...or in annotations, if it is a sub-protocol. - annotations = getattr(base, '__annotations__', {}) - if ( - isinstance(annotations, collections.abc.Mapping) - and attr in annotations - and is_protocol(other) - ): - break - else: - return NotImplemented - return True - - if sys.version_info >= (3, 8): - class Protocol(typing.Generic, metaclass=_ProtocolMeta): - __doc__ = typing.Protocol.__doc__ - __slots__ = () - _is_protocol = True - _is_runtime_protocol = False - - def __init_subclass__(cls, *args, **kwargs): - super().__init_subclass__(*args, **kwargs) - - # Determine if this is a protocol or a concrete subclass. - if not cls.__dict__.get('_is_protocol', False): - cls._is_protocol = any(b is Protocol for b in cls.__bases__) - - # Set (or override) the protocol subclass hook. - if '__subclasshook__' not in cls.__dict__: - cls.__subclasshook__ = _proto_hook - - # Prohibit instantiation for protocol classes - if cls._is_protocol and cls.__init__ is Protocol.__init__: - cls.__init__ = _no_init - - else: - class Protocol(metaclass=_ProtocolMeta): - # There is quite a lot of overlapping code with typing.Generic. - # Unfortunately it is hard to avoid this on Python <3.8, - # as the typing module on Python 3.7 doesn't let us subclass typing.Generic! - """Base class for protocol classes. Protocol classes are defined as:: - - class Proto(Protocol): - def meth(self) -> int: - ... - - Such classes are primarily used with static type checkers that recognize - structural subtyping (static duck-typing), for example:: - - class C: - def meth(self) -> int: - return 0 - - def func(x: Proto) -> int: - return x.meth() - - func(C()) # Passes static type check - - See PEP 544 for details. Protocol classes decorated with - @typing_extensions.runtime_checkable act - as simple-minded runtime-checkable protocols that check - only the presence of given attributes, ignoring their type signatures. - - Protocol classes can be generic, they are defined as:: - - class GenProto(Protocol[T]): - def meth(self) -> T: - ... - """ - __slots__ = () - _is_protocol = True - _is_runtime_protocol = False - - def __new__(cls, *args, **kwds): - if cls is Protocol: - raise TypeError("Type Protocol cannot be instantiated; " - "it can only be used as a base class") - return super().__new__(cls) - - @typing._tp_cache - def __class_getitem__(cls, params): - if not isinstance(params, tuple): - params = (params,) - if not params and cls is not typing.Tuple: - raise TypeError( - f"Parameter list to {cls.__qualname__}[...] cannot be empty") - msg = "Parameters to generic types must be types." - params = tuple(typing._type_check(p, msg) for p in params) - if cls is Protocol: - # Generic can only be subscripted with unique type variables. - if not all(isinstance(p, typing.TypeVar) for p in params): - i = 0 - while isinstance(params[i], typing.TypeVar): - i += 1 - raise TypeError( - "Parameters to Protocol[...] must all be type variables." - f" Parameter {i + 1} is {params[i]}") - if len(set(params)) != len(params): - raise TypeError( - "Parameters to Protocol[...] must all be unique") - else: - # Subscripting a regular Generic subclass. - _check_generic(cls, params, len(cls.__parameters__)) - return typing._GenericAlias(cls, params) - - def __init_subclass__(cls, *args, **kwargs): - if '__orig_bases__' in cls.__dict__: - error = typing.Generic in cls.__orig_bases__ - else: - error = typing.Generic in cls.__bases__ - if error: - raise TypeError("Cannot inherit from plain Generic") - _maybe_adjust_parameters(cls) - - # Determine if this is a protocol or a concrete subclass. - if not cls.__dict__.get('_is_protocol', None): - cls._is_protocol = any(b is Protocol for b in cls.__bases__) - - # Set (or override) the protocol subclass hook. - if '__subclasshook__' not in cls.__dict__: - cls.__subclasshook__ = _proto_hook - - # Prohibit instantiation for protocol classes - if cls._is_protocol and cls.__init__ is Protocol.__init__: - cls.__init__ = _no_init - - -if sys.version_info >= (3, 8): - runtime_checkable = typing.runtime_checkable -else: - def runtime_checkable(cls): - """Mark a protocol class as a runtime protocol, so that it - can be used with isinstance() and issubclass(). Raise TypeError - if applied to a non-protocol class. - - This allows a simple-minded structural check very similar to the - one-offs in collections.abc such as Hashable. - """ - if not ( - (isinstance(cls, _ProtocolMeta) or issubclass(cls, typing.Generic)) - and getattr(cls, "_is_protocol", False) - ): - raise TypeError('@runtime_checkable can be only applied to protocol classes,' - f' got {cls!r}') - cls._is_runtime_protocol = True - return cls - - -# Exists for backwards compatibility. -runtime = runtime_checkable - - -# Our version of runtime-checkable protocols is faster on Python 3.7-3.11 -if sys.version_info >= (3, 12): - SupportsInt = typing.SupportsInt - SupportsFloat = typing.SupportsFloat - SupportsComplex = typing.SupportsComplex - SupportsBytes = typing.SupportsBytes - SupportsIndex = typing.SupportsIndex - SupportsAbs = typing.SupportsAbs - SupportsRound = typing.SupportsRound -else: - @runtime_checkable - class SupportsInt(Protocol): - """An ABC with one abstract method __int__.""" - __slots__ = () - - @abc.abstractmethod - def __int__(self) -> int: - pass - - @runtime_checkable - class SupportsFloat(Protocol): - """An ABC with one abstract method __float__.""" - __slots__ = () - - @abc.abstractmethod - def __float__(self) -> float: - pass - - @runtime_checkable - class SupportsComplex(Protocol): - """An ABC with one abstract method __complex__.""" - __slots__ = () - - @abc.abstractmethod - def __complex__(self) -> complex: - pass - - @runtime_checkable - class SupportsBytes(Protocol): - """An ABC with one abstract method __bytes__.""" - __slots__ = () - - @abc.abstractmethod - def __bytes__(self) -> bytes: - pass - - @runtime_checkable - class SupportsIndex(Protocol): - __slots__ = () - - @abc.abstractmethod - def __index__(self) -> int: - pass - - @runtime_checkable - class SupportsAbs(Protocol[T_co]): - """ - An ABC with one abstract method __abs__ that is covariant in its return type. - """ - __slots__ = () - - @abc.abstractmethod - def __abs__(self) -> T_co: - pass - - @runtime_checkable - class SupportsRound(Protocol[T_co]): - """ - An ABC with one abstract method __round__ that is covariant in its return type. - """ - __slots__ = () - - @abc.abstractmethod - def __round__(self, ndigits: int = 0) -> T_co: - pass - - -def _ensure_subclassable(mro_entries): - def inner(func): - if sys.implementation.name == "pypy" and sys.version_info < (3, 9): - cls_dict = { - "__call__": staticmethod(func), - "__mro_entries__": staticmethod(mro_entries) - } - t = type(func.__name__, (), cls_dict) - return functools.update_wrapper(t(), func) - else: - func.__mro_entries__ = mro_entries - return func - return inner - - -if sys.version_info >= (3, 13): - # The standard library TypedDict in Python 3.8 does not store runtime information - # about which (if any) keys are optional. See https://bugs.python.org/issue38834 - # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" - # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 - # The standard library TypedDict below Python 3.11 does not store runtime - # information about optional and required keys when using Required or NotRequired. - # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11. - # Aaaand on 3.12 we add __orig_bases__ to TypedDict - # to enable better runtime introspection. - # On 3.13 we deprecate some odd ways of creating TypedDicts. - TypedDict = typing.TypedDict - _TypedDictMeta = typing._TypedDictMeta - is_typeddict = typing.is_typeddict -else: - # 3.10.0 and later - _TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters - - if sys.version_info >= (3, 8): - _fake_name = "Protocol" - else: - _fake_name = "_Protocol" - - class _TypedDictMeta(type): - def __new__(cls, name, bases, ns, total=True): - """Create new typed dict class object. - - This method is called when TypedDict is subclassed, - or when TypedDict is instantiated. This way - TypedDict supports all three syntax forms described in its docstring. - Subclasses and instances of TypedDict return actual dictionaries. - """ - for base in bases: - if type(base) is not _TypedDictMeta and base is not typing.Generic: - raise TypeError('cannot inherit from both a TypedDict type ' - 'and a non-TypedDict base class') - - if any(issubclass(b, typing.Generic) for b in bases): - generic_base = (typing.Generic,) - else: - generic_base = () - - # typing.py generally doesn't let you inherit from plain Generic, unless - # the name of the class happens to be "Protocol" (or "_Protocol" on 3.7). - tp_dict = type.__new__(_TypedDictMeta, _fake_name, (*generic_base, dict), ns) - tp_dict.__name__ = name - if tp_dict.__qualname__ == _fake_name: - tp_dict.__qualname__ = name - - if not hasattr(tp_dict, '__orig_bases__'): - tp_dict.__orig_bases__ = bases - - annotations = {} - own_annotations = ns.get('__annotations__', {}) - msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" - if _TAKES_MODULE: - own_annotations = { - n: typing._type_check(tp, msg, module=tp_dict.__module__) - for n, tp in own_annotations.items() - } - else: - own_annotations = { - n: typing._type_check(tp, msg) - for n, tp in own_annotations.items() - } - required_keys = set() - optional_keys = set() - - for base in bases: - annotations.update(base.__dict__.get('__annotations__', {})) - required_keys.update(base.__dict__.get('__required_keys__', ())) - optional_keys.update(base.__dict__.get('__optional_keys__', ())) - - annotations.update(own_annotations) - for annotation_key, annotation_type in own_annotations.items(): - annotation_origin = get_origin(annotation_type) - if annotation_origin is Annotated: - annotation_args = get_args(annotation_type) - if annotation_args: - annotation_type = annotation_args[0] - annotation_origin = get_origin(annotation_type) - - if annotation_origin is Required: - required_keys.add(annotation_key) - elif annotation_origin is NotRequired: - optional_keys.add(annotation_key) - elif total: - required_keys.add(annotation_key) - else: - optional_keys.add(annotation_key) - - tp_dict.__annotations__ = annotations - tp_dict.__required_keys__ = frozenset(required_keys) - tp_dict.__optional_keys__ = frozenset(optional_keys) - if not hasattr(tp_dict, '__total__'): - tp_dict.__total__ = total - return tp_dict - - __call__ = dict # static method - - def __subclasscheck__(cls, other): - # Typed dicts are only for static structural subtyping. - raise TypeError('TypedDict does not support instance and class checks') - - __instancecheck__ = __subclasscheck__ - - _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {}) - - @_ensure_subclassable(lambda bases: (_TypedDict,)) - def TypedDict(__typename, __fields=_marker, *, total=True, **kwargs): - """A simple typed namespace. At runtime it is equivalent to a plain dict. - - TypedDict creates a dictionary type such that a type checker will expect all - instances to have a certain set of keys, where each key is - associated with a value of a consistent type. This expectation - is not checked at runtime. - - Usage:: - - class Point2D(TypedDict): - x: int - y: int - label: str - - a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK - b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check - - assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') - - The type info can be accessed via the Point2D.__annotations__ dict, and - the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. - TypedDict supports an additional equivalent form:: - - Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) - - By default, all keys must be present in a TypedDict. It is possible - to override this by specifying totality:: - - class Point2D(TypedDict, total=False): - x: int - y: int - - This means that a Point2D TypedDict can have any of the keys omitted. A type - checker is only expected to support a literal False or True as the value of - the total argument. True is the default, and makes all items defined in the - class body be required. - - The Required and NotRequired special forms can also be used to mark - individual keys as being required or not required:: - - class Point2D(TypedDict): - x: int # the "x" key must always be present (Required is the default) - y: NotRequired[int] # the "y" key can be omitted - - See PEP 655 for more details on Required and NotRequired. - """ - if __fields is _marker or __fields is None: - if __fields is _marker: - deprecated_thing = "Failing to pass a value for the 'fields' parameter" - else: - deprecated_thing = "Passing `None` as the 'fields' parameter" - - example = f"`{__typename} = TypedDict({__typename!r}, {{}})`" - deprecation_msg = ( - f"{deprecated_thing} is deprecated and will be disallowed in " - "Python 3.15. To create a TypedDict class with 0 fields " - "using the functional syntax, pass an empty dictionary, e.g. " - ) + example + "." - warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2) - __fields = kwargs - elif kwargs: - raise TypeError("TypedDict takes either a dict or keyword arguments," - " but not both") - if kwargs: - warnings.warn( - "The kwargs-based syntax for TypedDict definitions is deprecated " - "in Python 3.11, will be removed in Python 3.13, and may not be " - "understood by third-party type checkers.", - DeprecationWarning, - stacklevel=2, - ) - - ns = {'__annotations__': dict(__fields)} - module = _caller() - if module is not None: - # Setting correct module is necessary to make typed dict classes pickleable. - ns['__module__'] = module - - td = _TypedDictMeta(__typename, (), ns, total=total) - td.__orig_bases__ = (TypedDict,) - return td - - if hasattr(typing, "_TypedDictMeta"): - _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) - else: - _TYPEDDICT_TYPES = (_TypedDictMeta,) - - def is_typeddict(tp): - """Check if an annotation is a TypedDict class - - For example:: - class Film(TypedDict): - title: str - year: int - - is_typeddict(Film) # => True - is_typeddict(Union[list, str]) # => False - """ - # On 3.8, this would otherwise return True - if hasattr(typing, "TypedDict") and tp is typing.TypedDict: - return False - return isinstance(tp, _TYPEDDICT_TYPES) - - -if hasattr(typing, "assert_type"): - assert_type = typing.assert_type - -else: - def assert_type(__val, __typ): - """Assert (to the type checker) that the value is of the given type. - - When the type checker encounters a call to assert_type(), it - emits an error if the value is not of the specified type:: - - def greet(name: str) -> None: - assert_type(name, str) # ok - assert_type(name, int) # type checker error - - At runtime this returns the first argument unchanged and otherwise - does nothing. - """ - return __val - - -if hasattr(typing, "Required"): - get_type_hints = typing.get_type_hints -else: - # replaces _strip_annotations() - def _strip_extras(t): - """Strips Annotated, Required and NotRequired from a given type.""" - if isinstance(t, _AnnotatedAlias): - return _strip_extras(t.__origin__) - if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired): - return _strip_extras(t.__args__[0]) - if isinstance(t, typing._GenericAlias): - stripped_args = tuple(_strip_extras(a) for a in t.__args__) - if stripped_args == t.__args__: - return t - return t.copy_with(stripped_args) - if hasattr(_types, "GenericAlias") and isinstance(t, _types.GenericAlias): - stripped_args = tuple(_strip_extras(a) for a in t.__args__) - if stripped_args == t.__args__: - return t - return _types.GenericAlias(t.__origin__, stripped_args) - if hasattr(_types, "UnionType") and isinstance(t, _types.UnionType): - stripped_args = tuple(_strip_extras(a) for a in t.__args__) - if stripped_args == t.__args__: - return t - return functools.reduce(operator.or_, stripped_args) - - return t - - def get_type_hints(obj, globalns=None, localns=None, include_extras=False): - """Return type hints for an object. - - This is often the same as obj.__annotations__, but it handles - forward references encoded as string literals, adds Optional[t] if a - default value equal to None is set and recursively replaces all - 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T' - (unless 'include_extras=True'). - - The argument may be a module, class, method, or function. The annotations - are returned as a dictionary. For classes, annotations include also - inherited members. - - TypeError is raised if the argument is not of a type that can contain - annotations, and an empty dictionary is returned if no annotations are - present. - - BEWARE -- the behavior of globalns and localns is counterintuitive - (unless you are familiar with how eval() and exec() work). The - search order is locals first, then globals. - - - If no dict arguments are passed, an attempt is made to use the - globals from obj (or the respective module's globals for classes), - and these are also used as the locals. If the object does not appear - to have globals, an empty dictionary is used. - - - If one dict argument is passed, it is used for both globals and - locals. - - - If two dict arguments are passed, they specify globals and - locals, respectively. - """ - if hasattr(typing, "Annotated"): - hint = typing.get_type_hints( - obj, globalns=globalns, localns=localns, include_extras=True - ) - else: - hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) - if include_extras: - return hint - return {k: _strip_extras(t) for k, t in hint.items()} - - -# Python 3.9+ has PEP 593 (Annotated) -if hasattr(typing, 'Annotated'): - Annotated = typing.Annotated - # Not exported and not a public API, but needed for get_origin() and get_args() - # to work. - _AnnotatedAlias = typing._AnnotatedAlias -# 3.7-3.8 -else: - class _AnnotatedAlias(typing._GenericAlias, _root=True): - """Runtime representation of an annotated type. - - At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't' - with extra annotations. The alias behaves like a normal typing alias, - instantiating is the same as instantiating the underlying type, binding - it to types is also the same. - """ - def __init__(self, origin, metadata): - if isinstance(origin, _AnnotatedAlias): - metadata = origin.__metadata__ + metadata - origin = origin.__origin__ - super().__init__(origin, origin) - self.__metadata__ = metadata - - def copy_with(self, params): - assert len(params) == 1 - new_type = params[0] - return _AnnotatedAlias(new_type, self.__metadata__) - - def __repr__(self): - return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, " - f"{', '.join(repr(a) for a in self.__metadata__)}]") - - def __reduce__(self): - return operator.getitem, ( - Annotated, (self.__origin__,) + self.__metadata__ - ) - - def __eq__(self, other): - if not isinstance(other, _AnnotatedAlias): - return NotImplemented - if self.__origin__ != other.__origin__: - return False - return self.__metadata__ == other.__metadata__ - - def __hash__(self): - return hash((self.__origin__, self.__metadata__)) - - class Annotated: - """Add context specific metadata to a type. - - Example: Annotated[int, runtime_check.Unsigned] indicates to the - hypothetical runtime_check module that this type is an unsigned int. - Every other consumer of this type can ignore this metadata and treat - this type as int. - - The first argument to Annotated must be a valid type (and will be in - the __origin__ field), the remaining arguments are kept as a tuple in - the __extra__ field. - - Details: - - - It's an error to call `Annotated` with less than two arguments. - - Nested Annotated are flattened:: - - Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] - - - Instantiating an annotated type is equivalent to instantiating the - underlying type:: - - Annotated[C, Ann1](5) == C(5) - - - Annotated can be used as a generic type alias:: - - Optimized = Annotated[T, runtime.Optimize()] - Optimized[int] == Annotated[int, runtime.Optimize()] - - OptimizedList = Annotated[List[T], runtime.Optimize()] - OptimizedList[int] == Annotated[List[int], runtime.Optimize()] - """ - - __slots__ = () - - def __new__(cls, *args, **kwargs): - raise TypeError("Type Annotated cannot be instantiated.") - - @typing._tp_cache - def __class_getitem__(cls, params): - if not isinstance(params, tuple) or len(params) < 2: - raise TypeError("Annotated[...] should be used " - "with at least two arguments (a type and an " - "annotation).") - allowed_special_forms = (ClassVar, Final) - if get_origin(params[0]) in allowed_special_forms: - origin = params[0] - else: - msg = "Annotated[t, ...]: t must be a type." - origin = typing._type_check(params[0], msg) - metadata = tuple(params[1:]) - return _AnnotatedAlias(origin, metadata) - - def __init_subclass__(cls, *args, **kwargs): - raise TypeError( - f"Cannot subclass {cls.__module__}.Annotated" - ) - -# Python 3.8 has get_origin() and get_args() but those implementations aren't -# Annotated-aware, so we can't use those. Python 3.9's versions don't support -# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do. -if sys.version_info[:2] >= (3, 10): - get_origin = typing.get_origin - get_args = typing.get_args -# 3.7-3.9 -else: - try: - # 3.9+ - from typing import _BaseGenericAlias - except ImportError: - _BaseGenericAlias = typing._GenericAlias - try: - # 3.9+ - from typing import GenericAlias as _typing_GenericAlias - except ImportError: - _typing_GenericAlias = typing._GenericAlias - - def get_origin(tp): - """Get the unsubscripted version of a type. - - This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar - and Annotated. Return None for unsupported types. Examples:: - - get_origin(Literal[42]) is Literal - get_origin(int) is None - get_origin(ClassVar[int]) is ClassVar - get_origin(Generic) is Generic - get_origin(Generic[T]) is Generic - get_origin(Union[T, int]) is Union - get_origin(List[Tuple[T, T]][int]) == list - get_origin(P.args) is P - """ - if isinstance(tp, _AnnotatedAlias): - return Annotated - if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias, - ParamSpecArgs, ParamSpecKwargs)): - return tp.__origin__ - if tp is typing.Generic: - return typing.Generic - return None - - def get_args(tp): - """Get type arguments with all substitutions performed. - - For unions, basic simplifications used by Union constructor are performed. - Examples:: - get_args(Dict[str, int]) == (str, int) - get_args(int) == () - get_args(Union[int, Union[T, int], str][int]) == (int, str) - get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) - get_args(Callable[[], T][int]) == ([], int) - """ - if isinstance(tp, _AnnotatedAlias): - return (tp.__origin__,) + tp.__metadata__ - if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)): - if getattr(tp, "_special", False): - return () - res = tp.__args__ - if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: - res = (list(res[:-1]), res[-1]) - return res - return () - - -# 3.10+ -if hasattr(typing, 'TypeAlias'): - TypeAlias = typing.TypeAlias -# 3.9 -elif sys.version_info[:2] >= (3, 9): - @_ExtensionsSpecialForm - def TypeAlias(self, parameters): - """Special marker indicating that an assignment should - be recognized as a proper type alias definition by type - checkers. - - For example:: - - Predicate: TypeAlias = Callable[..., bool] - - It's invalid when used anywhere except as in the example above. - """ - raise TypeError(f"{self} is not subscriptable") -# 3.7-3.8 -else: - TypeAlias = _ExtensionsSpecialForm( - 'TypeAlias', - doc="""Special marker indicating that an assignment should - be recognized as a proper type alias definition by type - checkers. - - For example:: - - Predicate: TypeAlias = Callable[..., bool] - - It's invalid when used anywhere except as in the example - above.""" - ) - - -def _set_default(type_param, default): - if isinstance(default, (tuple, list)): - type_param.__default__ = tuple((typing._type_check(d, "Default must be a type") - for d in default)) - elif default != _marker: - type_param.__default__ = typing._type_check(default, "Default must be a type") - else: - type_param.__default__ = None - - -def _set_module(typevarlike): - # for pickling: - def_mod = _caller(depth=3) - if def_mod != 'typing_extensions': - typevarlike.__module__ = def_mod - - -class _DefaultMixin: - """Mixin for TypeVarLike defaults.""" - - __slots__ = () - __init__ = _set_default - - -# Classes using this metaclass must provide a _backported_typevarlike ClassVar -class _TypeVarLikeMeta(type): - def __instancecheck__(cls, __instance: Any) -> bool: - return isinstance(__instance, cls._backported_typevarlike) - - -# Add default and infer_variance parameters from PEP 696 and 695 -class TypeVar(metaclass=_TypeVarLikeMeta): - """Type variable.""" - - _backported_typevarlike = typing.TypeVar - - def __new__(cls, name, *constraints, bound=None, - covariant=False, contravariant=False, - default=_marker, infer_variance=False): - if hasattr(typing, "TypeAliasType"): - # PEP 695 implemented, can pass infer_variance to typing.TypeVar - typevar = typing.TypeVar(name, *constraints, bound=bound, - covariant=covariant, contravariant=contravariant, - infer_variance=infer_variance) - else: - typevar = typing.TypeVar(name, *constraints, bound=bound, - covariant=covariant, contravariant=contravariant) - if infer_variance and (covariant or contravariant): - raise ValueError("Variance cannot be specified with infer_variance.") - typevar.__infer_variance__ = infer_variance - _set_default(typevar, default) - _set_module(typevar) - return typevar - - def __init_subclass__(cls) -> None: - raise TypeError(f"type '{__name__}.TypeVar' is not an acceptable base type") - - -# Python 3.10+ has PEP 612 -if hasattr(typing, 'ParamSpecArgs'): - ParamSpecArgs = typing.ParamSpecArgs - ParamSpecKwargs = typing.ParamSpecKwargs -# 3.7-3.9 -else: - class _Immutable: - """Mixin to indicate that object should not be copied.""" - __slots__ = () - - def __copy__(self): - return self - - def __deepcopy__(self, memo): - return self - - class ParamSpecArgs(_Immutable): - """The args for a ParamSpec object. - - Given a ParamSpec object P, P.args is an instance of ParamSpecArgs. - - ParamSpecArgs objects have a reference back to their ParamSpec: - - P.args.__origin__ is P - - This type is meant for runtime introspection and has no special meaning to - static type checkers. - """ - def __init__(self, origin): - self.__origin__ = origin - - def __repr__(self): - return f"{self.__origin__.__name__}.args" - - def __eq__(self, other): - if not isinstance(other, ParamSpecArgs): - return NotImplemented - return self.__origin__ == other.__origin__ - - class ParamSpecKwargs(_Immutable): - """The kwargs for a ParamSpec object. - - Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs. - - ParamSpecKwargs objects have a reference back to their ParamSpec: - - P.kwargs.__origin__ is P - - This type is meant for runtime introspection and has no special meaning to - static type checkers. - """ - def __init__(self, origin): - self.__origin__ = origin - - def __repr__(self): - return f"{self.__origin__.__name__}.kwargs" - - def __eq__(self, other): - if not isinstance(other, ParamSpecKwargs): - return NotImplemented - return self.__origin__ == other.__origin__ - -# 3.10+ -if hasattr(typing, 'ParamSpec'): - - # Add default parameter - PEP 696 - class ParamSpec(metaclass=_TypeVarLikeMeta): - """Parameter specification.""" - - _backported_typevarlike = typing.ParamSpec - - def __new__(cls, name, *, bound=None, - covariant=False, contravariant=False, - infer_variance=False, default=_marker): - if hasattr(typing, "TypeAliasType"): - # PEP 695 implemented, can pass infer_variance to typing.TypeVar - paramspec = typing.ParamSpec(name, bound=bound, - covariant=covariant, - contravariant=contravariant, - infer_variance=infer_variance) - else: - paramspec = typing.ParamSpec(name, bound=bound, - covariant=covariant, - contravariant=contravariant) - paramspec.__infer_variance__ = infer_variance - - _set_default(paramspec, default) - _set_module(paramspec) - return paramspec - - def __init_subclass__(cls) -> None: - raise TypeError(f"type '{__name__}.ParamSpec' is not an acceptable base type") - -# 3.7-3.9 -else: - - # Inherits from list as a workaround for Callable checks in Python < 3.9.2. - class ParamSpec(list, _DefaultMixin): - """Parameter specification variable. - - Usage:: - - P = ParamSpec('P') - - Parameter specification variables exist primarily for the benefit of static - type checkers. They are used to forward the parameter types of one - callable to another callable, a pattern commonly found in higher order - functions and decorators. They are only valid when used in ``Concatenate``, - or s the first argument to ``Callable``. In Python 3.10 and higher, - they are also supported in user-defined Generics at runtime. - See class Generic for more information on generic types. An - example for annotating a decorator:: - - T = TypeVar('T') - P = ParamSpec('P') - - def add_logging(f: Callable[P, T]) -> Callable[P, T]: - '''A type-safe decorator to add logging to a function.''' - def inner(*args: P.args, **kwargs: P.kwargs) -> T: - logging.info(f'{f.__name__} was called') - return f(*args, **kwargs) - return inner - - @add_logging - def add_two(x: float, y: float) -> float: - '''Add two numbers together.''' - return x + y - - Parameter specification variables defined with covariant=True or - contravariant=True can be used to declare covariant or contravariant - generic types. These keyword arguments are valid, but their actual semantics - are yet to be decided. See PEP 612 for details. - - Parameter specification variables can be introspected. e.g.: - - P.__name__ == 'T' - P.__bound__ == None - P.__covariant__ == False - P.__contravariant__ == False - - Note that only parameter specification variables defined in global scope can - be pickled. - """ - - # Trick Generic __parameters__. - __class__ = typing.TypeVar - - @property - def args(self): - return ParamSpecArgs(self) - - @property - def kwargs(self): - return ParamSpecKwargs(self) - - def __init__(self, name, *, bound=None, covariant=False, contravariant=False, - infer_variance=False, default=_marker): - super().__init__([self]) - self.__name__ = name - self.__covariant__ = bool(covariant) - self.__contravariant__ = bool(contravariant) - self.__infer_variance__ = bool(infer_variance) - if bound: - self.__bound__ = typing._type_check(bound, 'Bound must be a type.') - else: - self.__bound__ = None - _DefaultMixin.__init__(self, default) - - # for pickling: - def_mod = _caller() - if def_mod != 'typing_extensions': - self.__module__ = def_mod - - def __repr__(self): - if self.__infer_variance__: - prefix = '' - elif self.__covariant__: - prefix = '+' - elif self.__contravariant__: - prefix = '-' - else: - prefix = '~' - return prefix + self.__name__ - - def __hash__(self): - return object.__hash__(self) - - def __eq__(self, other): - return self is other - - def __reduce__(self): - return self.__name__ - - # Hack to get typing._type_check to pass. - def __call__(self, *args, **kwargs): - pass - - -# 3.7-3.9 -if not hasattr(typing, 'Concatenate'): - # Inherits from list as a workaround for Callable checks in Python < 3.9.2. - class _ConcatenateGenericAlias(list): - - # Trick Generic into looking into this for __parameters__. - __class__ = typing._GenericAlias - - # Flag in 3.8. - _special = False - - def __init__(self, origin, args): - super().__init__(args) - self.__origin__ = origin - self.__args__ = args - - def __repr__(self): - _type_repr = typing._type_repr - return (f'{_type_repr(self.__origin__)}' - f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]') - - def __hash__(self): - return hash((self.__origin__, self.__args__)) - - # Hack to get typing._type_check to pass in Generic. - def __call__(self, *args, **kwargs): - pass - - @property - def __parameters__(self): - return tuple( - tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) - ) - - -# 3.7-3.9 -@typing._tp_cache -def _concatenate_getitem(self, parameters): - if parameters == (): - raise TypeError("Cannot take a Concatenate of no types.") - if not isinstance(parameters, tuple): - parameters = (parameters,) - if not isinstance(parameters[-1], ParamSpec): - raise TypeError("The last parameter to Concatenate should be a " - "ParamSpec variable.") - msg = "Concatenate[arg, ...]: each arg must be a type." - parameters = tuple(typing._type_check(p, msg) for p in parameters) - return _ConcatenateGenericAlias(self, parameters) - - -# 3.10+ -if hasattr(typing, 'Concatenate'): - Concatenate = typing.Concatenate - _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa: F811 -# 3.9 -elif sys.version_info[:2] >= (3, 9): - @_ExtensionsSpecialForm - def Concatenate(self, parameters): - """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a - higher order function which adds, removes or transforms parameters of a - callable. - - For example:: - - Callable[Concatenate[int, P], int] - - See PEP 612 for detailed information. - """ - return _concatenate_getitem(self, parameters) -# 3.7-8 -else: - class _ConcatenateForm(_ExtensionsSpecialForm, _root=True): - def __getitem__(self, parameters): - return _concatenate_getitem(self, parameters) - - Concatenate = _ConcatenateForm( - 'Concatenate', - doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a - higher order function which adds, removes or transforms parameters of a - callable. - - For example:: - - Callable[Concatenate[int, P], int] - - See PEP 612 for detailed information. - """) - -# 3.10+ -if hasattr(typing, 'TypeGuard'): - TypeGuard = typing.TypeGuard -# 3.9 -elif sys.version_info[:2] >= (3, 9): - @_ExtensionsSpecialForm - def TypeGuard(self, parameters): - """Special typing form used to annotate the return type of a user-defined - type guard function. ``TypeGuard`` only accepts a single type argument. - At runtime, functions marked this way should return a boolean. - - ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static - type checkers to determine a more precise type of an expression within a - program's code flow. Usually type narrowing is done by analyzing - conditional code flow and applying the narrowing to a block of code. The - conditional expression here is sometimes referred to as a "type guard". - - Sometimes it would be convenient to use a user-defined boolean function - as a type guard. Such a function should use ``TypeGuard[...]`` as its - return type to alert static type checkers to this intention. - - Using ``-> TypeGuard`` tells the static type checker that for a given - function: - - 1. The return value is a boolean. - 2. If the return value is ``True``, the type of its argument - is the type inside ``TypeGuard``. - - For example:: - - def is_str(val: Union[str, float]): - # "isinstance" type guard - if isinstance(val, str): - # Type of ``val`` is narrowed to ``str`` - ... - else: - # Else, type of ``val`` is narrowed to ``float``. - ... - - Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower - form of ``TypeA`` (it can even be a wider form) and this may lead to - type-unsafe results. The main reason is to allow for things like - narrowing ``List[object]`` to ``List[str]`` even though the latter is not - a subtype of the former, since ``List`` is invariant. The responsibility of - writing type-safe type guards is left to the user. - - ``TypeGuard`` also works with type variables. For more information, see - PEP 647 (User-Defined Type Guards). - """ - item = typing._type_check(parameters, f'{self} accepts only a single type.') - return typing._GenericAlias(self, (item,)) -# 3.7-3.8 -else: - class _TypeGuardForm(_ExtensionsSpecialForm, _root=True): - def __getitem__(self, parameters): - item = typing._type_check(parameters, - f'{self._name} accepts only a single type') - return typing._GenericAlias(self, (item,)) - - TypeGuard = _TypeGuardForm( - 'TypeGuard', - doc="""Special typing form used to annotate the return type of a user-defined - type guard function. ``TypeGuard`` only accepts a single type argument. - At runtime, functions marked this way should return a boolean. - - ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static - type checkers to determine a more precise type of an expression within a - program's code flow. Usually type narrowing is done by analyzing - conditional code flow and applying the narrowing to a block of code. The - conditional expression here is sometimes referred to as a "type guard". - - Sometimes it would be convenient to use a user-defined boolean function - as a type guard. Such a function should use ``TypeGuard[...]`` as its - return type to alert static type checkers to this intention. - - Using ``-> TypeGuard`` tells the static type checker that for a given - function: - - 1. The return value is a boolean. - 2. If the return value is ``True``, the type of its argument - is the type inside ``TypeGuard``. - - For example:: - - def is_str(val: Union[str, float]): - # "isinstance" type guard - if isinstance(val, str): - # Type of ``val`` is narrowed to ``str`` - ... - else: - # Else, type of ``val`` is narrowed to ``float``. - ... - - Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower - form of ``TypeA`` (it can even be a wider form) and this may lead to - type-unsafe results. The main reason is to allow for things like - narrowing ``List[object]`` to ``List[str]`` even though the latter is not - a subtype of the former, since ``List`` is invariant. The responsibility of - writing type-safe type guards is left to the user. - - ``TypeGuard`` also works with type variables. For more information, see - PEP 647 (User-Defined Type Guards). - """) - - -# Vendored from cpython typing._SpecialFrom -class _SpecialForm(typing._Final, _root=True): - __slots__ = ('_name', '__doc__', '_getitem') - - def __init__(self, getitem): - self._getitem = getitem - self._name = getitem.__name__ - self.__doc__ = getitem.__doc__ - - def __getattr__(self, item): - if item in {'__name__', '__qualname__'}: - return self._name - - raise AttributeError(item) - - def __mro_entries__(self, bases): - raise TypeError(f"Cannot subclass {self!r}") - - def __repr__(self): - return f'typing_extensions.{self._name}' - - def __reduce__(self): - return self._name - - def __call__(self, *args, **kwds): - raise TypeError(f"Cannot instantiate {self!r}") - - def __or__(self, other): - return typing.Union[self, other] - - def __ror__(self, other): - return typing.Union[other, self] - - def __instancecheck__(self, obj): - raise TypeError(f"{self} cannot be used with isinstance()") - - def __subclasscheck__(self, cls): - raise TypeError(f"{self} cannot be used with issubclass()") - - @typing._tp_cache - def __getitem__(self, parameters): - return self._getitem(self, parameters) - - -if hasattr(typing, "LiteralString"): - LiteralString = typing.LiteralString -else: - @_SpecialForm - def LiteralString(self, params): - """Represents an arbitrary literal string. - - Example:: - - from pip._vendor.typing_extensions import LiteralString - - def query(sql: LiteralString) -> ...: - ... - - query("SELECT * FROM table") # ok - query(f"SELECT * FROM {input()}") # not ok - - See PEP 675 for details. - - """ - raise TypeError(f"{self} is not subscriptable") - - -if hasattr(typing, "Self"): - Self = typing.Self -else: - @_SpecialForm - def Self(self, params): - """Used to spell the type of "self" in classes. - - Example:: - - from typing import Self - - class ReturnsSelf: - def parse(self, data: bytes) -> Self: - ... - return self - - """ - - raise TypeError(f"{self} is not subscriptable") - - -if hasattr(typing, "Never"): - Never = typing.Never -else: - @_SpecialForm - def Never(self, params): - """The bottom type, a type that has no members. - - This can be used to define a function that should never be - called, or a function that never returns:: - - from pip._vendor.typing_extensions import Never - - def never_call_me(arg: Never) -> None: - pass - - def int_or_str(arg: int | str) -> None: - never_call_me(arg) # type checker error - match arg: - case int(): - print("It's an int") - case str(): - print("It's a str") - case _: - never_call_me(arg) # ok, arg is of type Never - - """ - - raise TypeError(f"{self} is not subscriptable") - - -if hasattr(typing, 'Required'): - Required = typing.Required - NotRequired = typing.NotRequired -elif sys.version_info[:2] >= (3, 9): - @_ExtensionsSpecialForm - def Required(self, parameters): - """A special typing construct to mark a key of a total=False TypedDict - as required. For example: - - class Movie(TypedDict, total=False): - title: Required[str] - year: int - - m = Movie( - title='The Matrix', # typechecker error if key is omitted - year=1999, - ) - - There is no runtime checking that a required key is actually provided - when instantiating a related TypedDict. - """ - item = typing._type_check(parameters, f'{self._name} accepts only a single type.') - return typing._GenericAlias(self, (item,)) - - @_ExtensionsSpecialForm - def NotRequired(self, parameters): - """A special typing construct to mark a key of a TypedDict as - potentially missing. For example: - - class Movie(TypedDict): - title: str - year: NotRequired[int] - - m = Movie( - title='The Matrix', # typechecker error if key is omitted - year=1999, - ) - """ - item = typing._type_check(parameters, f'{self._name} accepts only a single type.') - return typing._GenericAlias(self, (item,)) - -else: - class _RequiredForm(_ExtensionsSpecialForm, _root=True): - def __getitem__(self, parameters): - item = typing._type_check(parameters, - f'{self._name} accepts only a single type.') - return typing._GenericAlias(self, (item,)) - - Required = _RequiredForm( - 'Required', - doc="""A special typing construct to mark a key of a total=False TypedDict - as required. For example: - - class Movie(TypedDict, total=False): - title: Required[str] - year: int - - m = Movie( - title='The Matrix', # typechecker error if key is omitted - year=1999, - ) - - There is no runtime checking that a required key is actually provided - when instantiating a related TypedDict. - """) - NotRequired = _RequiredForm( - 'NotRequired', - doc="""A special typing construct to mark a key of a TypedDict as - potentially missing. For example: - - class Movie(TypedDict): - title: str - year: NotRequired[int] - - m = Movie( - title='The Matrix', # typechecker error if key is omitted - year=1999, - ) - """) - - -_UNPACK_DOC = """\ -Type unpack operator. - -The type unpack operator takes the child types from some container type, -such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For -example: - - # For some generic class `Foo`: - Foo[Unpack[tuple[int, str]]] # Equivalent to Foo[int, str] - - Ts = TypeVarTuple('Ts') - # Specifies that `Bar` is generic in an arbitrary number of types. - # (Think of `Ts` as a tuple of an arbitrary number of individual - # `TypeVar`s, which the `Unpack` is 'pulling out' directly into the - # `Generic[]`.) - class Bar(Generic[Unpack[Ts]]): ... - Bar[int] # Valid - Bar[int, str] # Also valid - -From Python 3.11, this can also be done using the `*` operator: - - Foo[*tuple[int, str]] - class Bar(Generic[*Ts]): ... - -The operator can also be used along with a `TypedDict` to annotate -`**kwargs` in a function signature. For instance: - - class Movie(TypedDict): - name: str - year: int - - # This function expects two keyword arguments - *name* of type `str` and - # *year* of type `int`. - def foo(**kwargs: Unpack[Movie]): ... - -Note that there is only some runtime checking of this operator. Not -everything the runtime allows may be accepted by static type checkers. - -For more information, see PEP 646 and PEP 692. -""" - - -if sys.version_info >= (3, 12): # PEP 692 changed the repr of Unpack[] - Unpack = typing.Unpack - - def _is_unpack(obj): - return get_origin(obj) is Unpack - -elif sys.version_info[:2] >= (3, 9): - class _UnpackSpecialForm(_ExtensionsSpecialForm, _root=True): - def __init__(self, getitem): - super().__init__(getitem) - self.__doc__ = _UNPACK_DOC - - class _UnpackAlias(typing._GenericAlias, _root=True): - __class__ = typing.TypeVar - - @_UnpackSpecialForm - def Unpack(self, parameters): - item = typing._type_check(parameters, f'{self._name} accepts only a single type.') - return _UnpackAlias(self, (item,)) - - def _is_unpack(obj): - return isinstance(obj, _UnpackAlias) - -else: - class _UnpackAlias(typing._GenericAlias, _root=True): - __class__ = typing.TypeVar - - class _UnpackForm(_ExtensionsSpecialForm, _root=True): - def __getitem__(self, parameters): - item = typing._type_check(parameters, - f'{self._name} accepts only a single type.') - return _UnpackAlias(self, (item,)) - - Unpack = _UnpackForm('Unpack', doc=_UNPACK_DOC) - - def _is_unpack(obj): - return isinstance(obj, _UnpackAlias) - - -if hasattr(typing, "TypeVarTuple"): # 3.11+ - - # Add default parameter - PEP 696 - class TypeVarTuple(metaclass=_TypeVarLikeMeta): - """Type variable tuple.""" - - _backported_typevarlike = typing.TypeVarTuple - - def __new__(cls, name, *, default=_marker): - tvt = typing.TypeVarTuple(name) - _set_default(tvt, default) - _set_module(tvt) - return tvt - - def __init_subclass__(self, *args, **kwds): - raise TypeError("Cannot subclass special typing classes") - -else: - class TypeVarTuple(_DefaultMixin): - """Type variable tuple. - - Usage:: - - Ts = TypeVarTuple('Ts') - - In the same way that a normal type variable is a stand-in for a single - type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* - type such as ``Tuple[int, str]``. - - Type variable tuples can be used in ``Generic`` declarations. - Consider the following example:: - - class Array(Generic[*Ts]): ... - - The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``, - where ``T1`` and ``T2`` are type variables. To use these type variables - as type parameters of ``Array``, we must *unpack* the type variable tuple using - the star operator: ``*Ts``. The signature of ``Array`` then behaves - as if we had simply written ``class Array(Generic[T1, T2]): ...``. - In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows - us to parameterise the class with an *arbitrary* number of type parameters. - - Type variable tuples can be used anywhere a normal ``TypeVar`` can. - This includes class definitions, as shown above, as well as function - signatures and variable annotations:: - - class Array(Generic[*Ts]): - - def __init__(self, shape: Tuple[*Ts]): - self._shape: Tuple[*Ts] = shape - - def get_shape(self) -> Tuple[*Ts]: - return self._shape - - shape = (Height(480), Width(640)) - x: Array[Height, Width] = Array(shape) - y = abs(x) # Inferred type is Array[Height, Width] - z = x + x # ... is Array[Height, Width] - x.get_shape() # ... is tuple[Height, Width] - - """ - - # Trick Generic __parameters__. - __class__ = typing.TypeVar - - def __iter__(self): - yield self.__unpacked__ - - def __init__(self, name, *, default=_marker): - self.__name__ = name - _DefaultMixin.__init__(self, default) - - # for pickling: - def_mod = _caller() - if def_mod != 'typing_extensions': - self.__module__ = def_mod - - self.__unpacked__ = Unpack[self] - - def __repr__(self): - return self.__name__ - - def __hash__(self): - return object.__hash__(self) - - def __eq__(self, other): - return self is other - - def __reduce__(self): - return self.__name__ - - def __init_subclass__(self, *args, **kwds): - if '_root' not in kwds: - raise TypeError("Cannot subclass special typing classes") - - -if hasattr(typing, "reveal_type"): - reveal_type = typing.reveal_type -else: - def reveal_type(__obj: T) -> T: - """Reveal the inferred type of a variable. - - When a static type checker encounters a call to ``reveal_type()``, - it will emit the inferred type of the argument:: - - x: int = 1 - reveal_type(x) - - Running a static type checker (e.g., ``mypy``) on this example - will produce output similar to 'Revealed type is "builtins.int"'. - - At runtime, the function prints the runtime type of the - argument and returns it unchanged. - - """ - print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr) - return __obj - - -if hasattr(typing, "assert_never"): - assert_never = typing.assert_never -else: - def assert_never(__arg: Never) -> Never: - """Assert to the type checker that a line of code is unreachable. - - Example:: - - def int_or_str(arg: int | str) -> None: - match arg: - case int(): - print("It's an int") - case str(): - print("It's a str") - case _: - assert_never(arg) - - If a type checker finds that a call to assert_never() is - reachable, it will emit an error. - - At runtime, this throws an exception when called. - - """ - raise AssertionError("Expected code to be unreachable") - - -if sys.version_info >= (3, 12): - # dataclass_transform exists in 3.11 but lacks the frozen_default parameter - dataclass_transform = typing.dataclass_transform -else: - def dataclass_transform( - *, - eq_default: bool = True, - order_default: bool = False, - kw_only_default: bool = False, - frozen_default: bool = False, - field_specifiers: typing.Tuple[ - typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]], - ... - ] = (), - **kwargs: typing.Any, - ) -> typing.Callable[[T], T]: - """Decorator that marks a function, class, or metaclass as providing - dataclass-like behavior. - - Example: - - from pip._vendor.typing_extensions import dataclass_transform - - _T = TypeVar("_T") - - # Used on a decorator function - @dataclass_transform() - def create_model(cls: type[_T]) -> type[_T]: - ... - return cls - - @create_model - class CustomerModel: - id: int - name: str - - # Used on a base class - @dataclass_transform() - class ModelBase: ... - - class CustomerModel(ModelBase): - id: int - name: str - - # Used on a metaclass - @dataclass_transform() - class ModelMeta(type): ... - - class ModelBase(metaclass=ModelMeta): ... - - class CustomerModel(ModelBase): - id: int - name: str - - Each of the ``CustomerModel`` classes defined in this example will now - behave similarly to a dataclass created with the ``@dataclasses.dataclass`` - decorator. For example, the type checker will synthesize an ``__init__`` - method. - - The arguments to this decorator can be used to customize this behavior: - - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be - True or False if it is omitted by the caller. - - ``order_default`` indicates whether the ``order`` parameter is - assumed to be True or False if it is omitted by the caller. - - ``kw_only_default`` indicates whether the ``kw_only`` parameter is - assumed to be True or False if it is omitted by the caller. - - ``frozen_default`` indicates whether the ``frozen`` parameter is - assumed to be True or False if it is omitted by the caller. - - ``field_specifiers`` specifies a static list of supported classes - or functions that describe fields, similar to ``dataclasses.field()``. - - At runtime, this decorator records its arguments in the - ``__dataclass_transform__`` attribute on the decorated object. - - See PEP 681 for details. - - """ - def decorator(cls_or_fn): - cls_or_fn.__dataclass_transform__ = { - "eq_default": eq_default, - "order_default": order_default, - "kw_only_default": kw_only_default, - "frozen_default": frozen_default, - "field_specifiers": field_specifiers, - "kwargs": kwargs, - } - return cls_or_fn - return decorator - - -if hasattr(typing, "override"): - override = typing.override -else: - _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any]) - - def override(__arg: _F) -> _F: - """Indicate that a method is intended to override a method in a base class. - - Usage: - - class Base: - def method(self) -> None: ... - pass - - class Child(Base): - @override - def method(self) -> None: - super().method() - - When this decorator is applied to a method, the type checker will - validate that it overrides a method with the same name on a base class. - This helps prevent bugs that may occur when a base class is changed - without an equivalent change to a child class. - - There is no runtime checking of these properties. The decorator - sets the ``__override__`` attribute to ``True`` on the decorated object - to allow runtime introspection. - - See PEP 698 for details. - - """ - try: - __arg.__override__ = True - except (AttributeError, TypeError): - # Skip the attribute silently if it is not writable. - # AttributeError happens if the object has __slots__ or a - # read-only property, TypeError if it's a builtin class. - pass - return __arg - - -if hasattr(typing, "deprecated"): - deprecated = typing.deprecated -else: - _T = typing.TypeVar("_T") - - def deprecated( - __msg: str, - *, - category: typing.Optional[typing.Type[Warning]] = DeprecationWarning, - stacklevel: int = 1, - ) -> typing.Callable[[_T], _T]: - """Indicate that a class, function or overload is deprecated. - - Usage: - - @deprecated("Use B instead") - class A: - pass - - @deprecated("Use g instead") - def f(): - pass - - @overload - @deprecated("int support is deprecated") - def g(x: int) -> int: ... - @overload - def g(x: str) -> int: ... - - When this decorator is applied to an object, the type checker - will generate a diagnostic on usage of the deprecated object. - - The warning specified by ``category`` will be emitted on use - of deprecated objects. For functions, that happens on calls; - for classes, on instantiation. If the ``category`` is ``None``, - no warning is emitted. The ``stacklevel`` determines where the - warning is emitted. If it is ``1`` (the default), the warning - is emitted at the direct caller of the deprecated object; if it - is higher, it is emitted further up the stack. - - The decorator sets the ``__deprecated__`` - attribute on the decorated object to the deprecation message - passed to the decorator. If applied to an overload, the decorator - must be after the ``@overload`` decorator for the attribute to - exist on the overload as returned by ``get_overloads()``. - - See PEP 702 for details. - - """ - def decorator(__arg: _T) -> _T: - if category is None: - __arg.__deprecated__ = __msg - return __arg - elif isinstance(__arg, type): - original_new = __arg.__new__ - has_init = __arg.__init__ is not object.__init__ - - @functools.wraps(original_new) - def __new__(cls, *args, **kwargs): - warnings.warn(__msg, category=category, stacklevel=stacklevel + 1) - if original_new is not object.__new__: - return original_new(cls, *args, **kwargs) - # Mirrors a similar check in object.__new__. - elif not has_init and (args or kwargs): - raise TypeError(f"{cls.__name__}() takes no arguments") - else: - return original_new(cls) - - __arg.__new__ = staticmethod(__new__) - __arg.__deprecated__ = __new__.__deprecated__ = __msg - return __arg - elif callable(__arg): - @functools.wraps(__arg) - def wrapper(*args, **kwargs): - warnings.warn(__msg, category=category, stacklevel=stacklevel + 1) - return __arg(*args, **kwargs) - - __arg.__deprecated__ = wrapper.__deprecated__ = __msg - return wrapper - else: - raise TypeError( - "@deprecated decorator with non-None category must be applied to " - f"a class or callable, not {__arg!r}" - ) - - return decorator - - -# We have to do some monkey patching to deal with the dual nature of -# Unpack/TypeVarTuple: -# - We want Unpack to be a kind of TypeVar so it gets accepted in -# Generic[Unpack[Ts]] -# - We want it to *not* be treated as a TypeVar for the purposes of -# counting generic parameters, so that when we subscript a generic, -# the runtime doesn't try to substitute the Unpack with the subscripted type. -if not hasattr(typing, "TypeVarTuple"): - typing._collect_type_vars = _collect_type_vars - typing._check_generic = _check_generic - - -# Backport typing.NamedTuple as it exists in Python 3.12. -# In 3.11, the ability to define generic `NamedTuple`s was supported. -# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8. -# On 3.12, we added __orig_bases__ to call-based NamedTuples -# On 3.13, we deprecated kwargs-based NamedTuples -if sys.version_info >= (3, 13): - NamedTuple = typing.NamedTuple -else: - def _make_nmtuple(name, types, module, defaults=()): - fields = [n for n, t in types] - annotations = {n: typing._type_check(t, f"field {n} annotation must be a type") - for n, t in types} - nm_tpl = collections.namedtuple(name, fields, - defaults=defaults, module=module) - nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations - # The `_field_types` attribute was removed in 3.9; - # in earlier versions, it is the same as the `__annotations__` attribute - if sys.version_info < (3, 9): - nm_tpl._field_types = annotations - return nm_tpl - - _prohibited_namedtuple_fields = typing._prohibited - _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'}) - - class _NamedTupleMeta(type): - def __new__(cls, typename, bases, ns): - assert _NamedTuple in bases - for base in bases: - if base is not _NamedTuple and base is not typing.Generic: - raise TypeError( - 'can only inherit from a NamedTuple type and Generic') - bases = tuple(tuple if base is _NamedTuple else base for base in bases) - types = ns.get('__annotations__', {}) - default_names = [] - for field_name in types: - if field_name in ns: - default_names.append(field_name) - elif default_names: - raise TypeError(f"Non-default namedtuple field {field_name} " - f"cannot follow default field" - f"{'s' if len(default_names) > 1 else ''} " - f"{', '.join(default_names)}") - nm_tpl = _make_nmtuple( - typename, types.items(), - defaults=[ns[n] for n in default_names], - module=ns['__module__'] - ) - nm_tpl.__bases__ = bases - if typing.Generic in bases: - if hasattr(typing, '_generic_class_getitem'): # 3.12+ - nm_tpl.__class_getitem__ = classmethod(typing._generic_class_getitem) - else: - class_getitem = typing.Generic.__class_getitem__.__func__ - nm_tpl.__class_getitem__ = classmethod(class_getitem) - # update from user namespace without overriding special namedtuple attributes - for key in ns: - if key in _prohibited_namedtuple_fields: - raise AttributeError("Cannot overwrite NamedTuple attribute " + key) - elif key not in _special_namedtuple_fields and key not in nm_tpl._fields: - setattr(nm_tpl, key, ns[key]) - if typing.Generic in bases: - nm_tpl.__init_subclass__() - return nm_tpl - - _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {}) - - def _namedtuple_mro_entries(bases): - assert NamedTuple in bases - return (_NamedTuple,) - - @_ensure_subclassable(_namedtuple_mro_entries) - def NamedTuple(__typename, __fields=_marker, **kwargs): - """Typed version of namedtuple. - - Usage:: - - class Employee(NamedTuple): - name: str - id: int - - This is equivalent to:: - - Employee = collections.namedtuple('Employee', ['name', 'id']) - - The resulting class has an extra __annotations__ attribute, giving a - dict that maps field names to types. (The field names are also in - the _fields attribute, which is part of the namedtuple API.) - An alternative equivalent functional syntax is also accepted:: - - Employee = NamedTuple('Employee', [('name', str), ('id', int)]) - """ - if __fields is _marker: - if kwargs: - deprecated_thing = "Creating NamedTuple classes using keyword arguments" - deprecation_msg = ( - "{name} is deprecated and will be disallowed in Python {remove}. " - "Use the class-based or functional syntax instead." - ) - else: - deprecated_thing = "Failing to pass a value for the 'fields' parameter" - example = f"`{__typename} = NamedTuple({__typename!r}, [])`" - deprecation_msg = ( - "{name} is deprecated and will be disallowed in Python {remove}. " - "To create a NamedTuple class with 0 fields " - "using the functional syntax, " - "pass an empty list, e.g. " - ) + example + "." - elif __fields is None: - if kwargs: - raise TypeError( - "Cannot pass `None` as the 'fields' parameter " - "and also specify fields using keyword arguments" - ) - else: - deprecated_thing = "Passing `None` as the 'fields' parameter" - example = f"`{__typename} = NamedTuple({__typename!r}, [])`" - deprecation_msg = ( - "{name} is deprecated and will be disallowed in Python {remove}. " - "To create a NamedTuple class with 0 fields " - "using the functional syntax, " - "pass an empty list, e.g. " - ) + example + "." - elif kwargs: - raise TypeError("Either list of fields or keywords" - " can be provided to NamedTuple, not both") - if __fields is _marker or __fields is None: - warnings.warn( - deprecation_msg.format(name=deprecated_thing, remove="3.15"), - DeprecationWarning, - stacklevel=2, - ) - __fields = kwargs.items() - nt = _make_nmtuple(__typename, __fields, module=_caller()) - nt.__orig_bases__ = (NamedTuple,) - return nt - - # On 3.8+, alter the signature so that it matches typing.NamedTuple. - # The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7, - # so just leave the signature as it is on 3.7. - if sys.version_info >= (3, 8): - _new_signature = '(typename, fields=None, /, **kwargs)' - if isinstance(NamedTuple, _types.FunctionType): - NamedTuple.__text_signature__ = _new_signature - else: - NamedTuple.__call__.__text_signature__ = _new_signature - - -if hasattr(collections.abc, "Buffer"): - Buffer = collections.abc.Buffer -else: - class Buffer(abc.ABC): - """Base class for classes that implement the buffer protocol. - - The buffer protocol allows Python objects to expose a low-level - memory buffer interface. Before Python 3.12, it is not possible - to implement the buffer protocol in pure Python code, or even - to check whether a class implements the buffer protocol. In - Python 3.12 and higher, the ``__buffer__`` method allows access - to the buffer protocol from Python code, and the - ``collections.abc.Buffer`` ABC allows checking whether a class - implements the buffer protocol. - - To indicate support for the buffer protocol in earlier versions, - inherit from this ABC, either in a stub file or at runtime, - or use ABC registration. This ABC provides no methods, because - there is no Python-accessible methods shared by pre-3.12 buffer - classes. It is useful primarily for static checks. - - """ - - # As a courtesy, register the most common stdlib buffer classes. - Buffer.register(memoryview) - Buffer.register(bytearray) - Buffer.register(bytes) - - -# Backport of types.get_original_bases, available on 3.12+ in CPython -if hasattr(_types, "get_original_bases"): - get_original_bases = _types.get_original_bases -else: - def get_original_bases(__cls): - """Return the class's "original" bases prior to modification by `__mro_entries__`. - - Examples:: - - from typing import TypeVar, Generic - from pip._vendor.typing_extensions import NamedTuple, TypedDict - - T = TypeVar("T") - class Foo(Generic[T]): ... - class Bar(Foo[int], float): ... - class Baz(list[str]): ... - Eggs = NamedTuple("Eggs", [("a", int), ("b", str)]) - Spam = TypedDict("Spam", {"a": int, "b": str}) - - assert get_original_bases(Bar) == (Foo[int], float) - assert get_original_bases(Baz) == (list[str],) - assert get_original_bases(Eggs) == (NamedTuple,) - assert get_original_bases(Spam) == (TypedDict,) - assert get_original_bases(int) == (object,) - """ - try: - return __cls.__orig_bases__ - except AttributeError: - try: - return __cls.__bases__ - except AttributeError: - raise TypeError( - f'Expected an instance of type, not {type(__cls).__name__!r}' - ) from None - - -# NewType is a class on Python 3.10+, making it pickleable -# The error message for subclassing instances of NewType was improved on 3.11+ -if sys.version_info >= (3, 11): - NewType = typing.NewType -else: - class NewType: - """NewType creates simple unique types with almost zero - runtime overhead. NewType(name, tp) is considered a subtype of tp - by static type checkers. At runtime, NewType(name, tp) returns - a dummy callable that simply returns its argument. Usage:: - UserId = NewType('UserId', int) - def name_by_id(user_id: UserId) -> str: - ... - UserId('user') # Fails type check - name_by_id(42) # Fails type check - name_by_id(UserId(42)) # OK - num = UserId(5) + 1 # type: int - """ - - def __call__(self, obj): - return obj - - def __init__(self, name, tp): - self.__qualname__ = name - if '.' in name: - name = name.rpartition('.')[-1] - self.__name__ = name - self.__supertype__ = tp - def_mod = _caller() - if def_mod != 'typing_extensions': - self.__module__ = def_mod - - def __mro_entries__(self, bases): - # We defined __mro_entries__ to get a better error message - # if a user attempts to subclass a NewType instance. bpo-46170 - supercls_name = self.__name__ - - class Dummy: - def __init_subclass__(cls): - subcls_name = cls.__name__ - raise TypeError( - f"Cannot subclass an instance of NewType. " - f"Perhaps you were looking for: " - f"`{subcls_name} = NewType({subcls_name!r}, {supercls_name})`" - ) - - return (Dummy,) - - def __repr__(self): - return f'{self.__module__}.{self.__qualname__}' - - def __reduce__(self): - return self.__qualname__ - - if sys.version_info >= (3, 10): - # PEP 604 methods - # It doesn't make sense to have these methods on Python <3.10 - - def __or__(self, other): - return typing.Union[self, other] - - def __ror__(self, other): - return typing.Union[other, self] - - -if hasattr(typing, "TypeAliasType"): - TypeAliasType = typing.TypeAliasType -else: - def _is_unionable(obj): - """Corresponds to is_unionable() in unionobject.c in CPython.""" - return obj is None or isinstance(obj, ( - type, - _types.GenericAlias, - _types.UnionType, - TypeAliasType, - )) - - class TypeAliasType: - """Create named, parameterized type aliases. - - This provides a backport of the new `type` statement in Python 3.12: - - type ListOrSet[T] = list[T] | set[T] - - is equivalent to: - - T = TypeVar("T") - ListOrSet = TypeAliasType("ListOrSet", list[T] | set[T], type_params=(T,)) - - The name ListOrSet can then be used as an alias for the type it refers to. - - The type_params argument should contain all the type parameters used - in the value of the type alias. If the alias is not generic, this - argument is omitted. - - Static type checkers should only support type aliases declared using - TypeAliasType that follow these rules: - - - The first argument (the name) must be a string literal. - - The TypeAliasType instance must be immediately assigned to a variable - of the same name. (For example, 'X = TypeAliasType("Y", int)' is invalid, - as is 'X, Y = TypeAliasType("X", int), TypeAliasType("Y", int)'). - - """ - - def __init__(self, name: str, value, *, type_params=()): - if not isinstance(name, str): - raise TypeError("TypeAliasType name must be a string") - self.__value__ = value - self.__type_params__ = type_params - - parameters = [] - for type_param in type_params: - if isinstance(type_param, TypeVarTuple): - parameters.extend(type_param) - else: - parameters.append(type_param) - self.__parameters__ = tuple(parameters) - def_mod = _caller() - if def_mod != 'typing_extensions': - self.__module__ = def_mod - # Setting this attribute closes the TypeAliasType from further modification - self.__name__ = name - - def __setattr__(self, __name: str, __value: object) -> None: - if hasattr(self, "__name__"): - self._raise_attribute_error(__name) - super().__setattr__(__name, __value) - - def __delattr__(self, __name: str) -> Never: - self._raise_attribute_error(__name) - - def _raise_attribute_error(self, name: str) -> Never: - # Match the Python 3.12 error messages exactly - if name == "__name__": - raise AttributeError("readonly attribute") - elif name in {"__value__", "__type_params__", "__parameters__", "__module__"}: - raise AttributeError( - f"attribute '{name}' of 'typing.TypeAliasType' objects " - "is not writable" - ) - else: - raise AttributeError( - f"'typing.TypeAliasType' object has no attribute '{name}'" - ) - - def __repr__(self) -> str: - return self.__name__ - - def __getitem__(self, parameters): - if not isinstance(parameters, tuple): - parameters = (parameters,) - parameters = [ - typing._type_check( - item, f'Subscripting {self.__name__} requires a type.' - ) - for item in parameters - ] - return typing._GenericAlias(self, tuple(parameters)) - - def __reduce__(self): - return self.__name__ - - def __init_subclass__(cls, *args, **kwargs): - raise TypeError( - "type 'typing_extensions.TypeAliasType' is not an acceptable base type" - ) - - # The presence of this method convinces typing._type_check - # that TypeAliasTypes are types. - def __call__(self): - raise TypeError("Type alias is not callable") - - if sys.version_info >= (3, 10): - def __or__(self, right): - # For forward compatibility with 3.12, reject Unions - # that are not accepted by the built-in Union. - if not _is_unionable(right): - return NotImplemented - return typing.Union[self, right] - - def __ror__(self, left): - if not _is_unionable(left): - return NotImplemented - return typing.Union[left, self] - - -if hasattr(typing, "is_protocol"): - is_protocol = typing.is_protocol - get_protocol_members = typing.get_protocol_members -else: - def is_protocol(__tp: type) -> bool: - """Return True if the given type is a Protocol. - - Example:: - - >>> from typing_extensions import Protocol, is_protocol - >>> class P(Protocol): - ... def a(self) -> str: ... - ... b: int - >>> is_protocol(P) - True - >>> is_protocol(int) - False - """ - return ( - isinstance(__tp, type) - and getattr(__tp, '_is_protocol', False) - and __tp is not Protocol - and __tp is not getattr(typing, "Protocol", object()) - ) - - def get_protocol_members(__tp: type) -> typing.FrozenSet[str]: - """Return the set of members defined in a Protocol. - - Example:: - - >>> from typing_extensions import Protocol, get_protocol_members - >>> class P(Protocol): - ... def a(self) -> str: ... - ... b: int - >>> get_protocol_members(P) - frozenset({'a', 'b'}) - - Raise a TypeError for arguments that are not Protocols. - """ - if not is_protocol(__tp): - raise TypeError(f'{__tp!r} is not a Protocol') - if hasattr(__tp, '__protocol_attrs__'): - return frozenset(__tp.__protocol_attrs__) - return frozenset(_get_protocol_attrs(__tp)) - - -# Aliases for items that have always been in typing. -# Explicitly assign these (rather than using `from typing import *` at the top), -# so that we get a CI error if one of these is deleted from typing.py -# in a future version of Python -AbstractSet = typing.AbstractSet -AnyStr = typing.AnyStr -BinaryIO = typing.BinaryIO -Callable = typing.Callable -Collection = typing.Collection -Container = typing.Container -Dict = typing.Dict -ForwardRef = typing.ForwardRef -FrozenSet = typing.FrozenSet -Generator = typing.Generator -Generic = typing.Generic -Hashable = typing.Hashable -IO = typing.IO -ItemsView = typing.ItemsView -Iterable = typing.Iterable -Iterator = typing.Iterator -KeysView = typing.KeysView -List = typing.List -Mapping = typing.Mapping -MappingView = typing.MappingView -Match = typing.Match -MutableMapping = typing.MutableMapping -MutableSequence = typing.MutableSequence -MutableSet = typing.MutableSet -Optional = typing.Optional -Pattern = typing.Pattern -Reversible = typing.Reversible -Sequence = typing.Sequence -Set = typing.Set -Sized = typing.Sized -TextIO = typing.TextIO -Tuple = typing.Tuple -Union = typing.Union -ValuesView = typing.ValuesView -cast = typing.cast -no_type_check = typing.no_type_check -no_type_check_decorator = typing.no_type_check_decorator diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/LICENSE.txt b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/LICENSE.txt new file mode 100644 index 00000000..429a1767 --- /dev/null +++ b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2008-2020 Andrey Petrov and contributors (see CONTRIBUTORS.txt) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py index cad75fb5..d49df2a0 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py @@ -1,2 +1,2 @@ # This file is protected via CODEOWNERS -__version__ = "1.26.17" +__version__ = "1.26.20" diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py index 54b96b19..de35b63d 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py @@ -68,7 +68,7 @@ port_by_scheme = {"http": 80, "https": 443} # When it comes time to update this value as a part of regular maintenance # (ie test_recent_date is failing) update it to ~6 months before the current date. -RECENT_DATE = datetime.date(2022, 1, 1) +RECENT_DATE = datetime.date(2024, 1, 1) _CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") @@ -437,7 +437,7 @@ class HTTPSConnection(HTTPConnection): and self.ssl_version is None and hasattr(self.sock, "version") and self.sock.version() in {"TLSv1", "TLSv1.1"} - ): + ): # Defensive: warnings.warn( "Negotiating TLSv1/TLSv1.1 by default is deprecated " "and will be disabled in urllib3 v2.0.0. Connecting to " diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py index 5a6adcbd..0872ed77 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py @@ -423,12 +423,13 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): pass except IOError as e: # Python 2 and macOS/Linux - # EPIPE and ESHUTDOWN are BrokenPipeError on Python 2, and EPROTOTYPE is needed on macOS + # EPIPE and ESHUTDOWN are BrokenPipeError on Python 2, and EPROTOTYPE/ECONNRESET are needed on macOS # https://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ if e.errno not in { errno.EPIPE, errno.ESHUTDOWN, errno.EPROTOTYPE, + errno.ECONNRESET, }: raise @@ -768,7 +769,9 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): # so we try to cover our bases here! message = " ".join(re.split("[^a-z]", str(ssl_error).lower())) return ( - "wrong version number" in message or "unknown protocol" in message + "wrong version number" in message + or "unknown protocol" in message + or "record layer failure" in message ) # Try to detect a common user error with proxies which is to diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/securetransport.py b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/securetransport.py index 4a06bc69..722ee4e1 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/securetransport.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/securetransport.py @@ -64,9 +64,8 @@ import struct import threading import weakref -from pip._vendor import six - from .. import util +from ..packages import six from ..util.ssl_ import PROTOCOL_TLS_CLIENT from ._securetransport.bindings import CoreFoundation, Security, SecurityConst from ._securetransport.low_level import ( diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/poolmanager.py b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/poolmanager.py index a8de7c6d..fb51bf7d 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/poolmanager.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/poolmanager.py @@ -170,22 +170,6 @@ class PoolManager(RequestMethods): def __init__(self, num_pools=10, headers=None, **connection_pool_kw): RequestMethods.__init__(self, headers) - if "retries" in connection_pool_kw: - retries = connection_pool_kw["retries"] - if not isinstance(retries, Retry): - # When Retry is initialized, raise_on_redirect is based - # on a redirect boolean value. - # But requests made via a pool manager always set - # redirect to False, and raise_on_redirect always ends - # up being False consequently. - # Here we fix the issue by setting raise_on_redirect to - # a value needed by the pool manager without considering - # the redirect boolean. - raise_on_redirect = retries is not False - retries = Retry.from_int(retries, redirect=False) - retries.raise_on_redirect = raise_on_redirect - connection_pool_kw = connection_pool_kw.copy() - connection_pool_kw["retries"] = retries self.connection_pool_kw = connection_pool_kw self.pools = RecentlyUsedContainer(num_pools) @@ -405,7 +389,7 @@ class PoolManager(RequestMethods): kw["body"] = None kw["headers"] = HTTPHeaderDict(kw["headers"])._prepare_for_method_change() - retries = kw.get("retries", response.retries) + retries = kw.get("retries") if not isinstance(retries, Retry): retries = Retry.from_int(retries, redirect=redirect) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_.py b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_.py index 2b45d391..0a6a0e06 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_.py +++ b/venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_.py @@ -1,11 +1,11 @@ from __future__ import absolute_import +import hashlib import hmac import os import sys import warnings from binascii import hexlify, unhexlify -from hashlib import md5, sha1, sha256 from ..exceptions import ( InsecurePlatformWarning, @@ -24,7 +24,10 @@ IS_SECURETRANSPORT = False ALPN_PROTOCOLS = ["http/1.1"] # Maps the length of a digest to a possible hash function producing this digest -HASHFUNC_MAP = {32: md5, 40: sha1, 64: sha256} +HASHFUNC_MAP = { + length: getattr(hashlib, algorithm, None) + for length, algorithm in ((32, "md5"), (40, "sha1"), (64, "sha256")) +} def _const_compare_digest_backport(a, b): @@ -191,9 +194,15 @@ def assert_fingerprint(cert, fingerprint): fingerprint = fingerprint.replace(":", "").lower() digest_length = len(fingerprint) - hashfunc = HASHFUNC_MAP.get(digest_length) - if not hashfunc: + if digest_length not in HASHFUNC_MAP: raise SSLError("Fingerprint of invalid length: {0}".format(fingerprint)) + hashfunc = HASHFUNC_MAP.get(digest_length) + if hashfunc is None: + raise SSLError( + "Hash function implementation unavailable for fingerprint length: {0}".format( + digest_length + ) + ) # We need encode() here for py32; works on py2 and p33. fingerprint_bytes = unhexlify(fingerprint.encode()) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/vendor.txt b/venv/lib/python3.12/site-packages/pip/_vendor/vendor.txt index 5554c38e..dd2f5f34 100644 --- a/venv/lib/python3.12/site-packages/pip/_vendor/vendor.txt +++ b/venv/lib/python3.12/site-packages/pip/_vendor/vendor.txt @@ -1,24 +1,19 @@ -CacheControl==0.13.1 # Make sure to update the license in pyproject.toml for this. -colorama==0.4.6 -distlib==0.3.8 -distro==1.8.0 -msgpack==1.0.5 -packaging==21.3 -platformdirs==3.8.1 -pyparsing==3.1.0 -pyproject-hooks==1.0.0 -requests==2.31.0 - certifi==2023.7.22 - chardet==5.1.0 - idna==3.4 - urllib3==1.26.17 -rich==13.4.2 - pygments==2.15.1 - typing_extensions==4.7.1 -resolvelib==1.0.1 -setuptools==68.0.0 -six==1.16.0 -tenacity==8.2.2 -tomli==2.0.1 -truststore==0.8.0 -webencodings==0.5.1 +CacheControl==0.14.4 +distlib==0.4.0 +distro==1.9.0 +msgpack==1.1.2 +packaging==26.0 +platformdirs==4.5.1 +pyproject-hooks==1.2.0 +requests==2.32.5 + certifi==2026.1.4 + idna==3.11 + urllib3==1.26.20 +rich==14.2.0 + pygments==2.19.2 +resolvelib==1.2.1 +setuptools==70.3.0 +tomli==2.3.0 +tomli-w==1.2.0 +truststore==0.10.4 +dependency-groups==1.3.1 diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__init__.py b/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__init__.py deleted file mode 100644 index d21d697c..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__init__.py +++ /dev/null @@ -1,342 +0,0 @@ -# coding: utf-8 -""" - - webencodings - ~~~~~~~~~~~~ - - This is a Python implementation of the `WHATWG Encoding standard - `. See README for details. - - :copyright: Copyright 2012 by Simon Sapin - :license: BSD, see LICENSE for details. - -""" - -from __future__ import unicode_literals - -import codecs - -from .labels import LABELS - - -VERSION = '0.5.1' - - -# Some names in Encoding are not valid Python aliases. Remap these. -PYTHON_NAMES = { - 'iso-8859-8-i': 'iso-8859-8', - 'x-mac-cyrillic': 'mac-cyrillic', - 'macintosh': 'mac-roman', - 'windows-874': 'cp874'} - -CACHE = {} - - -def ascii_lower(string): - r"""Transform (only) ASCII letters to lower case: A-Z is mapped to a-z. - - :param string: An Unicode string. - :returns: A new Unicode string. - - This is used for `ASCII case-insensitive - `_ - matching of encoding labels. - The same matching is also used, among other things, - for `CSS keywords `_. - - This is different from the :meth:`~py:str.lower` method of Unicode strings - which also affect non-ASCII characters, - sometimes mapping them into the ASCII range: - - >>> keyword = u'Bac\N{KELVIN SIGN}ground' - >>> assert keyword.lower() == u'background' - >>> assert ascii_lower(keyword) != keyword.lower() - >>> assert ascii_lower(keyword) == u'bac\N{KELVIN SIGN}ground' - - """ - # This turns out to be faster than unicode.translate() - return string.encode('utf8').lower().decode('utf8') - - -def lookup(label): - """ - Look for an encoding by its label. - This is the spec’s `get an encoding - `_ algorithm. - Supported labels are listed there. - - :param label: A string. - :returns: - An :class:`Encoding` object, or :obj:`None` for an unknown label. - - """ - # Only strip ASCII whitespace: U+0009, U+000A, U+000C, U+000D, and U+0020. - label = ascii_lower(label.strip('\t\n\f\r ')) - name = LABELS.get(label) - if name is None: - return None - encoding = CACHE.get(name) - if encoding is None: - if name == 'x-user-defined': - from .x_user_defined import codec_info - else: - python_name = PYTHON_NAMES.get(name, name) - # Any python_name value that gets to here should be valid. - codec_info = codecs.lookup(python_name) - encoding = Encoding(name, codec_info) - CACHE[name] = encoding - return encoding - - -def _get_encoding(encoding_or_label): - """ - Accept either an encoding object or label. - - :param encoding: An :class:`Encoding` object or a label string. - :returns: An :class:`Encoding` object. - :raises: :exc:`~exceptions.LookupError` for an unknown label. - - """ - if hasattr(encoding_or_label, 'codec_info'): - return encoding_or_label - - encoding = lookup(encoding_or_label) - if encoding is None: - raise LookupError('Unknown encoding label: %r' % encoding_or_label) - return encoding - - -class Encoding(object): - """Reresents a character encoding such as UTF-8, - that can be used for decoding or encoding. - - .. attribute:: name - - Canonical name of the encoding - - .. attribute:: codec_info - - The actual implementation of the encoding, - a stdlib :class:`~codecs.CodecInfo` object. - See :func:`codecs.register`. - - """ - def __init__(self, name, codec_info): - self.name = name - self.codec_info = codec_info - - def __repr__(self): - return '' % self.name - - -#: The UTF-8 encoding. Should be used for new content and formats. -UTF8 = lookup('utf-8') - -_UTF16LE = lookup('utf-16le') -_UTF16BE = lookup('utf-16be') - - -def decode(input, fallback_encoding, errors='replace'): - """ - Decode a single string. - - :param input: A byte string - :param fallback_encoding: - An :class:`Encoding` object or a label string. - The encoding to use if :obj:`input` does note have a BOM. - :param errors: Type of error handling. See :func:`codecs.register`. - :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. - :return: - A ``(output, encoding)`` tuple of an Unicode string - and an :obj:`Encoding`. - - """ - # Fail early if `encoding` is an invalid label. - fallback_encoding = _get_encoding(fallback_encoding) - bom_encoding, input = _detect_bom(input) - encoding = bom_encoding or fallback_encoding - return encoding.codec_info.decode(input, errors)[0], encoding - - -def _detect_bom(input): - """Return (bom_encoding, input), with any BOM removed from the input.""" - if input.startswith(b'\xFF\xFE'): - return _UTF16LE, input[2:] - if input.startswith(b'\xFE\xFF'): - return _UTF16BE, input[2:] - if input.startswith(b'\xEF\xBB\xBF'): - return UTF8, input[3:] - return None, input - - -def encode(input, encoding=UTF8, errors='strict'): - """ - Encode a single string. - - :param input: An Unicode string. - :param encoding: An :class:`Encoding` object or a label string. - :param errors: Type of error handling. See :func:`codecs.register`. - :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. - :return: A byte string. - - """ - return _get_encoding(encoding).codec_info.encode(input, errors)[0] - - -def iter_decode(input, fallback_encoding, errors='replace'): - """ - "Pull"-based decoder. - - :param input: - An iterable of byte strings. - - The input is first consumed just enough to determine the encoding - based on the precense of a BOM, - then consumed on demand when the return value is. - :param fallback_encoding: - An :class:`Encoding` object or a label string. - The encoding to use if :obj:`input` does note have a BOM. - :param errors: Type of error handling. See :func:`codecs.register`. - :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. - :returns: - An ``(output, encoding)`` tuple. - :obj:`output` is an iterable of Unicode strings, - :obj:`encoding` is the :obj:`Encoding` that is being used. - - """ - - decoder = IncrementalDecoder(fallback_encoding, errors) - generator = _iter_decode_generator(input, decoder) - encoding = next(generator) - return generator, encoding - - -def _iter_decode_generator(input, decoder): - """Return a generator that first yields the :obj:`Encoding`, - then yields output chukns as Unicode strings. - - """ - decode = decoder.decode - input = iter(input) - for chunck in input: - output = decode(chunck) - if output: - assert decoder.encoding is not None - yield decoder.encoding - yield output - break - else: - # Input exhausted without determining the encoding - output = decode(b'', final=True) - assert decoder.encoding is not None - yield decoder.encoding - if output: - yield output - return - - for chunck in input: - output = decode(chunck) - if output: - yield output - output = decode(b'', final=True) - if output: - yield output - - -def iter_encode(input, encoding=UTF8, errors='strict'): - """ - “Pull”-based encoder. - - :param input: An iterable of Unicode strings. - :param encoding: An :class:`Encoding` object or a label string. - :param errors: Type of error handling. See :func:`codecs.register`. - :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. - :returns: An iterable of byte strings. - - """ - # Fail early if `encoding` is an invalid label. - encode = IncrementalEncoder(encoding, errors).encode - return _iter_encode_generator(input, encode) - - -def _iter_encode_generator(input, encode): - for chunck in input: - output = encode(chunck) - if output: - yield output - output = encode('', final=True) - if output: - yield output - - -class IncrementalDecoder(object): - """ - “Push”-based decoder. - - :param fallback_encoding: - An :class:`Encoding` object or a label string. - The encoding to use if :obj:`input` does note have a BOM. - :param errors: Type of error handling. See :func:`codecs.register`. - :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. - - """ - def __init__(self, fallback_encoding, errors='replace'): - # Fail early if `encoding` is an invalid label. - self._fallback_encoding = _get_encoding(fallback_encoding) - self._errors = errors - self._buffer = b'' - self._decoder = None - #: The actual :class:`Encoding` that is being used, - #: or :obj:`None` if that is not determined yet. - #: (Ie. if there is not enough input yet to determine - #: if there is a BOM.) - self.encoding = None # Not known yet. - - def decode(self, input, final=False): - """Decode one chunk of the input. - - :param input: A byte string. - :param final: - Indicate that no more input is available. - Must be :obj:`True` if this is the last call. - :returns: An Unicode string. - - """ - decoder = self._decoder - if decoder is not None: - return decoder(input, final) - - input = self._buffer + input - encoding, input = _detect_bom(input) - if encoding is None: - if len(input) < 3 and not final: # Not enough data yet. - self._buffer = input - return '' - else: # No BOM - encoding = self._fallback_encoding - decoder = encoding.codec_info.incrementaldecoder(self._errors).decode - self._decoder = decoder - self.encoding = encoding - return decoder(input, final) - - -class IncrementalEncoder(object): - """ - “Push”-based encoder. - - :param encoding: An :class:`Encoding` object or a label string. - :param errors: Type of error handling. See :func:`codecs.register`. - :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. - - .. method:: encode(input, final=False) - - :param input: An Unicode string. - :param final: - Indicate that no more input is available. - Must be :obj:`True` if this is the last call. - :returns: A byte string. - - """ - def __init__(self, encoding=UTF8, errors='strict'): - encoding = _get_encoding(encoding) - self.encode = encoding.codec_info.incrementalencoder(errors).encode diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/labels.py b/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/labels.py deleted file mode 100644 index 29cbf91e..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/labels.py +++ /dev/null @@ -1,231 +0,0 @@ -""" - - webencodings.labels - ~~~~~~~~~~~~~~~~~~~ - - Map encoding labels to their name. - - :copyright: Copyright 2012 by Simon Sapin - :license: BSD, see LICENSE for details. - -""" - -# XXX Do not edit! -# This file is automatically generated by mklabels.py - -LABELS = { - 'unicode-1-1-utf-8': 'utf-8', - 'utf-8': 'utf-8', - 'utf8': 'utf-8', - '866': 'ibm866', - 'cp866': 'ibm866', - 'csibm866': 'ibm866', - 'ibm866': 'ibm866', - 'csisolatin2': 'iso-8859-2', - 'iso-8859-2': 'iso-8859-2', - 'iso-ir-101': 'iso-8859-2', - 'iso8859-2': 'iso-8859-2', - 'iso88592': 'iso-8859-2', - 'iso_8859-2': 'iso-8859-2', - 'iso_8859-2:1987': 'iso-8859-2', - 'l2': 'iso-8859-2', - 'latin2': 'iso-8859-2', - 'csisolatin3': 'iso-8859-3', - 'iso-8859-3': 'iso-8859-3', - 'iso-ir-109': 'iso-8859-3', - 'iso8859-3': 'iso-8859-3', - 'iso88593': 'iso-8859-3', - 'iso_8859-3': 'iso-8859-3', - 'iso_8859-3:1988': 'iso-8859-3', - 'l3': 'iso-8859-3', - 'latin3': 'iso-8859-3', - 'csisolatin4': 'iso-8859-4', - 'iso-8859-4': 'iso-8859-4', - 'iso-ir-110': 'iso-8859-4', - 'iso8859-4': 'iso-8859-4', - 'iso88594': 'iso-8859-4', - 'iso_8859-4': 'iso-8859-4', - 'iso_8859-4:1988': 'iso-8859-4', - 'l4': 'iso-8859-4', - 'latin4': 'iso-8859-4', - 'csisolatincyrillic': 'iso-8859-5', - 'cyrillic': 'iso-8859-5', - 'iso-8859-5': 'iso-8859-5', - 'iso-ir-144': 'iso-8859-5', - 'iso8859-5': 'iso-8859-5', - 'iso88595': 'iso-8859-5', - 'iso_8859-5': 'iso-8859-5', - 'iso_8859-5:1988': 'iso-8859-5', - 'arabic': 'iso-8859-6', - 'asmo-708': 'iso-8859-6', - 'csiso88596e': 'iso-8859-6', - 'csiso88596i': 'iso-8859-6', - 'csisolatinarabic': 'iso-8859-6', - 'ecma-114': 'iso-8859-6', - 'iso-8859-6': 'iso-8859-6', - 'iso-8859-6-e': 'iso-8859-6', - 'iso-8859-6-i': 'iso-8859-6', - 'iso-ir-127': 'iso-8859-6', - 'iso8859-6': 'iso-8859-6', - 'iso88596': 'iso-8859-6', - 'iso_8859-6': 'iso-8859-6', - 'iso_8859-6:1987': 'iso-8859-6', - 'csisolatingreek': 'iso-8859-7', - 'ecma-118': 'iso-8859-7', - 'elot_928': 'iso-8859-7', - 'greek': 'iso-8859-7', - 'greek8': 'iso-8859-7', - 'iso-8859-7': 'iso-8859-7', - 'iso-ir-126': 'iso-8859-7', - 'iso8859-7': 'iso-8859-7', - 'iso88597': 'iso-8859-7', - 'iso_8859-7': 'iso-8859-7', - 'iso_8859-7:1987': 'iso-8859-7', - 'sun_eu_greek': 'iso-8859-7', - 'csiso88598e': 'iso-8859-8', - 'csisolatinhebrew': 'iso-8859-8', - 'hebrew': 'iso-8859-8', - 'iso-8859-8': 'iso-8859-8', - 'iso-8859-8-e': 'iso-8859-8', - 'iso-ir-138': 'iso-8859-8', - 'iso8859-8': 'iso-8859-8', - 'iso88598': 'iso-8859-8', - 'iso_8859-8': 'iso-8859-8', - 'iso_8859-8:1988': 'iso-8859-8', - 'visual': 'iso-8859-8', - 'csiso88598i': 'iso-8859-8-i', - 'iso-8859-8-i': 'iso-8859-8-i', - 'logical': 'iso-8859-8-i', - 'csisolatin6': 'iso-8859-10', - 'iso-8859-10': 'iso-8859-10', - 'iso-ir-157': 'iso-8859-10', - 'iso8859-10': 'iso-8859-10', - 'iso885910': 'iso-8859-10', - 'l6': 'iso-8859-10', - 'latin6': 'iso-8859-10', - 'iso-8859-13': 'iso-8859-13', - 'iso8859-13': 'iso-8859-13', - 'iso885913': 'iso-8859-13', - 'iso-8859-14': 'iso-8859-14', - 'iso8859-14': 'iso-8859-14', - 'iso885914': 'iso-8859-14', - 'csisolatin9': 'iso-8859-15', - 'iso-8859-15': 'iso-8859-15', - 'iso8859-15': 'iso-8859-15', - 'iso885915': 'iso-8859-15', - 'iso_8859-15': 'iso-8859-15', - 'l9': 'iso-8859-15', - 'iso-8859-16': 'iso-8859-16', - 'cskoi8r': 'koi8-r', - 'koi': 'koi8-r', - 'koi8': 'koi8-r', - 'koi8-r': 'koi8-r', - 'koi8_r': 'koi8-r', - 'koi8-u': 'koi8-u', - 'csmacintosh': 'macintosh', - 'mac': 'macintosh', - 'macintosh': 'macintosh', - 'x-mac-roman': 'macintosh', - 'dos-874': 'windows-874', - 'iso-8859-11': 'windows-874', - 'iso8859-11': 'windows-874', - 'iso885911': 'windows-874', - 'tis-620': 'windows-874', - 'windows-874': 'windows-874', - 'cp1250': 'windows-1250', - 'windows-1250': 'windows-1250', - 'x-cp1250': 'windows-1250', - 'cp1251': 'windows-1251', - 'windows-1251': 'windows-1251', - 'x-cp1251': 'windows-1251', - 'ansi_x3.4-1968': 'windows-1252', - 'ascii': 'windows-1252', - 'cp1252': 'windows-1252', - 'cp819': 'windows-1252', - 'csisolatin1': 'windows-1252', - 'ibm819': 'windows-1252', - 'iso-8859-1': 'windows-1252', - 'iso-ir-100': 'windows-1252', - 'iso8859-1': 'windows-1252', - 'iso88591': 'windows-1252', - 'iso_8859-1': 'windows-1252', - 'iso_8859-1:1987': 'windows-1252', - 'l1': 'windows-1252', - 'latin1': 'windows-1252', - 'us-ascii': 'windows-1252', - 'windows-1252': 'windows-1252', - 'x-cp1252': 'windows-1252', - 'cp1253': 'windows-1253', - 'windows-1253': 'windows-1253', - 'x-cp1253': 'windows-1253', - 'cp1254': 'windows-1254', - 'csisolatin5': 'windows-1254', - 'iso-8859-9': 'windows-1254', - 'iso-ir-148': 'windows-1254', - 'iso8859-9': 'windows-1254', - 'iso88599': 'windows-1254', - 'iso_8859-9': 'windows-1254', - 'iso_8859-9:1989': 'windows-1254', - 'l5': 'windows-1254', - 'latin5': 'windows-1254', - 'windows-1254': 'windows-1254', - 'x-cp1254': 'windows-1254', - 'cp1255': 'windows-1255', - 'windows-1255': 'windows-1255', - 'x-cp1255': 'windows-1255', - 'cp1256': 'windows-1256', - 'windows-1256': 'windows-1256', - 'x-cp1256': 'windows-1256', - 'cp1257': 'windows-1257', - 'windows-1257': 'windows-1257', - 'x-cp1257': 'windows-1257', - 'cp1258': 'windows-1258', - 'windows-1258': 'windows-1258', - 'x-cp1258': 'windows-1258', - 'x-mac-cyrillic': 'x-mac-cyrillic', - 'x-mac-ukrainian': 'x-mac-cyrillic', - 'chinese': 'gbk', - 'csgb2312': 'gbk', - 'csiso58gb231280': 'gbk', - 'gb2312': 'gbk', - 'gb_2312': 'gbk', - 'gb_2312-80': 'gbk', - 'gbk': 'gbk', - 'iso-ir-58': 'gbk', - 'x-gbk': 'gbk', - 'gb18030': 'gb18030', - 'hz-gb-2312': 'hz-gb-2312', - 'big5': 'big5', - 'big5-hkscs': 'big5', - 'cn-big5': 'big5', - 'csbig5': 'big5', - 'x-x-big5': 'big5', - 'cseucpkdfmtjapanese': 'euc-jp', - 'euc-jp': 'euc-jp', - 'x-euc-jp': 'euc-jp', - 'csiso2022jp': 'iso-2022-jp', - 'iso-2022-jp': 'iso-2022-jp', - 'csshiftjis': 'shift_jis', - 'ms_kanji': 'shift_jis', - 'shift-jis': 'shift_jis', - 'shift_jis': 'shift_jis', - 'sjis': 'shift_jis', - 'windows-31j': 'shift_jis', - 'x-sjis': 'shift_jis', - 'cseuckr': 'euc-kr', - 'csksc56011987': 'euc-kr', - 'euc-kr': 'euc-kr', - 'iso-ir-149': 'euc-kr', - 'korean': 'euc-kr', - 'ks_c_5601-1987': 'euc-kr', - 'ks_c_5601-1989': 'euc-kr', - 'ksc5601': 'euc-kr', - 'ksc_5601': 'euc-kr', - 'windows-949': 'euc-kr', - 'csiso2022kr': 'iso-2022-kr', - 'iso-2022-kr': 'iso-2022-kr', - 'utf-16be': 'utf-16be', - 'utf-16': 'utf-16le', - 'utf-16le': 'utf-16le', - 'x-user-defined': 'x-user-defined', -} diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/mklabels.py b/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/mklabels.py deleted file mode 100644 index 295dc928..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/mklabels.py +++ /dev/null @@ -1,59 +0,0 @@ -""" - - webencodings.mklabels - ~~~~~~~~~~~~~~~~~~~~~ - - Regenarate the webencodings.labels module. - - :copyright: Copyright 2012 by Simon Sapin - :license: BSD, see LICENSE for details. - -""" - -import json -try: - from urllib import urlopen -except ImportError: - from urllib.request import urlopen - - -def assert_lower(string): - assert string == string.lower() - return string - - -def generate(url): - parts = ['''\ -""" - - webencodings.labels - ~~~~~~~~~~~~~~~~~~~ - - Map encoding labels to their name. - - :copyright: Copyright 2012 by Simon Sapin - :license: BSD, see LICENSE for details. - -""" - -# XXX Do not edit! -# This file is automatically generated by mklabels.py - -LABELS = { -'''] - labels = [ - (repr(assert_lower(label)).lstrip('u'), - repr(encoding['name']).lstrip('u')) - for category in json.loads(urlopen(url).read().decode('ascii')) - for encoding in category['encodings'] - for label in encoding['labels']] - max_len = max(len(label) for label, name in labels) - parts.extend( - ' %s:%s %s,\n' % (label, ' ' * (max_len - len(label)), name) - for label, name in labels) - parts.append('}') - return ''.join(parts) - - -if __name__ == '__main__': - print(generate('http://encoding.spec.whatwg.org/encodings.json')) diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/tests.py b/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/tests.py deleted file mode 100644 index e12c10d0..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/tests.py +++ /dev/null @@ -1,153 +0,0 @@ -# coding: utf-8 -""" - - webencodings.tests - ~~~~~~~~~~~~~~~~~~ - - A basic test suite for Encoding. - - :copyright: Copyright 2012 by Simon Sapin - :license: BSD, see LICENSE for details. - -""" - -from __future__ import unicode_literals - -from . import (lookup, LABELS, decode, encode, iter_decode, iter_encode, - IncrementalDecoder, IncrementalEncoder, UTF8) - - -def assert_raises(exception, function, *args, **kwargs): - try: - function(*args, **kwargs) - except exception: - return - else: # pragma: no cover - raise AssertionError('Did not raise %s.' % exception) - - -def test_labels(): - assert lookup('utf-8').name == 'utf-8' - assert lookup('Utf-8').name == 'utf-8' - assert lookup('UTF-8').name == 'utf-8' - assert lookup('utf8').name == 'utf-8' - assert lookup('utf8').name == 'utf-8' - assert lookup('utf8 ').name == 'utf-8' - assert lookup(' \r\nutf8\t').name == 'utf-8' - assert lookup('u8') is None # Python label. - assert lookup('utf-8 ') is None # Non-ASCII white space. - - assert lookup('US-ASCII').name == 'windows-1252' - assert lookup('iso-8859-1').name == 'windows-1252' - assert lookup('latin1').name == 'windows-1252' - assert lookup('LATIN1').name == 'windows-1252' - assert lookup('latin-1') is None - assert lookup('LATİN1') is None # ASCII-only case insensitivity. - - -def test_all_labels(): - for label in LABELS: - assert decode(b'', label) == ('', lookup(label)) - assert encode('', label) == b'' - for repeat in [0, 1, 12]: - output, _ = iter_decode([b''] * repeat, label) - assert list(output) == [] - assert list(iter_encode([''] * repeat, label)) == [] - decoder = IncrementalDecoder(label) - assert decoder.decode(b'') == '' - assert decoder.decode(b'', final=True) == '' - encoder = IncrementalEncoder(label) - assert encoder.encode('') == b'' - assert encoder.encode('', final=True) == b'' - # All encoding names are valid labels too: - for name in set(LABELS.values()): - assert lookup(name).name == name - - -def test_invalid_label(): - assert_raises(LookupError, decode, b'\xEF\xBB\xBF\xc3\xa9', 'invalid') - assert_raises(LookupError, encode, 'é', 'invalid') - assert_raises(LookupError, iter_decode, [], 'invalid') - assert_raises(LookupError, iter_encode, [], 'invalid') - assert_raises(LookupError, IncrementalDecoder, 'invalid') - assert_raises(LookupError, IncrementalEncoder, 'invalid') - - -def test_decode(): - assert decode(b'\x80', 'latin1') == ('€', lookup('latin1')) - assert decode(b'\x80', lookup('latin1')) == ('€', lookup('latin1')) - assert decode(b'\xc3\xa9', 'utf8') == ('é', lookup('utf8')) - assert decode(b'\xc3\xa9', UTF8) == ('é', lookup('utf8')) - assert decode(b'\xc3\xa9', 'ascii') == ('é', lookup('ascii')) - assert decode(b'\xEF\xBB\xBF\xc3\xa9', 'ascii') == ('é', lookup('utf8')) # UTF-8 with BOM - - assert decode(b'\xFE\xFF\x00\xe9', 'ascii') == ('é', lookup('utf-16be')) # UTF-16-BE with BOM - assert decode(b'\xFF\xFE\xe9\x00', 'ascii') == ('é', lookup('utf-16le')) # UTF-16-LE with BOM - assert decode(b'\xFE\xFF\xe9\x00', 'ascii') == ('\ue900', lookup('utf-16be')) - assert decode(b'\xFF\xFE\x00\xe9', 'ascii') == ('\ue900', lookup('utf-16le')) - - assert decode(b'\x00\xe9', 'UTF-16BE') == ('é', lookup('utf-16be')) - assert decode(b'\xe9\x00', 'UTF-16LE') == ('é', lookup('utf-16le')) - assert decode(b'\xe9\x00', 'UTF-16') == ('é', lookup('utf-16le')) - - assert decode(b'\xe9\x00', 'UTF-16BE') == ('\ue900', lookup('utf-16be')) - assert decode(b'\x00\xe9', 'UTF-16LE') == ('\ue900', lookup('utf-16le')) - assert decode(b'\x00\xe9', 'UTF-16') == ('\ue900', lookup('utf-16le')) - - -def test_encode(): - assert encode('é', 'latin1') == b'\xe9' - assert encode('é', 'utf8') == b'\xc3\xa9' - assert encode('é', 'utf8') == b'\xc3\xa9' - assert encode('é', 'utf-16') == b'\xe9\x00' - assert encode('é', 'utf-16le') == b'\xe9\x00' - assert encode('é', 'utf-16be') == b'\x00\xe9' - - -def test_iter_decode(): - def iter_decode_to_string(input, fallback_encoding): - output, _encoding = iter_decode(input, fallback_encoding) - return ''.join(output) - assert iter_decode_to_string([], 'latin1') == '' - assert iter_decode_to_string([b''], 'latin1') == '' - assert iter_decode_to_string([b'\xe9'], 'latin1') == 'é' - assert iter_decode_to_string([b'hello'], 'latin1') == 'hello' - assert iter_decode_to_string([b'he', b'llo'], 'latin1') == 'hello' - assert iter_decode_to_string([b'hell', b'o'], 'latin1') == 'hello' - assert iter_decode_to_string([b'\xc3\xa9'], 'latin1') == 'é' - assert iter_decode_to_string([b'\xEF\xBB\xBF\xc3\xa9'], 'latin1') == 'é' - assert iter_decode_to_string([ - b'\xEF\xBB\xBF', b'\xc3', b'\xa9'], 'latin1') == 'é' - assert iter_decode_to_string([ - b'\xEF\xBB\xBF', b'a', b'\xc3'], 'latin1') == 'a\uFFFD' - assert iter_decode_to_string([ - b'', b'\xEF', b'', b'', b'\xBB\xBF\xc3', b'\xa9'], 'latin1') == 'é' - assert iter_decode_to_string([b'\xEF\xBB\xBF'], 'latin1') == '' - assert iter_decode_to_string([b'\xEF\xBB'], 'latin1') == 'ï»' - assert iter_decode_to_string([b'\xFE\xFF\x00\xe9'], 'latin1') == 'é' - assert iter_decode_to_string([b'\xFF\xFE\xe9\x00'], 'latin1') == 'é' - assert iter_decode_to_string([ - b'', b'\xFF', b'', b'', b'\xFE\xe9', b'\x00'], 'latin1') == 'é' - assert iter_decode_to_string([ - b'', b'h\xe9', b'llo'], 'x-user-defined') == 'h\uF7E9llo' - - -def test_iter_encode(): - assert b''.join(iter_encode([], 'latin1')) == b'' - assert b''.join(iter_encode([''], 'latin1')) == b'' - assert b''.join(iter_encode(['é'], 'latin1')) == b'\xe9' - assert b''.join(iter_encode(['', 'é', '', ''], 'latin1')) == b'\xe9' - assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16')) == b'\xe9\x00' - assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16le')) == b'\xe9\x00' - assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16be')) == b'\x00\xe9' - assert b''.join(iter_encode([ - '', 'h\uF7E9', '', 'llo'], 'x-user-defined')) == b'h\xe9llo' - - -def test_x_user_defined(): - encoded = b'2,\x0c\x0b\x1aO\xd9#\xcb\x0f\xc9\xbbt\xcf\xa8\xca' - decoded = '2,\x0c\x0b\x1aO\uf7d9#\uf7cb\x0f\uf7c9\uf7bbt\uf7cf\uf7a8\uf7ca' - encoded = b'aa' - decoded = 'aa' - assert decode(encoded, 'x-user-defined') == (decoded, lookup('x-user-defined')) - assert encode(decoded, 'x-user-defined') == encoded diff --git a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/x_user_defined.py b/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/x_user_defined.py deleted file mode 100644 index d16e3260..00000000 --- a/venv/lib/python3.12/site-packages/pip/_vendor/webencodings/x_user_defined.py +++ /dev/null @@ -1,325 +0,0 @@ -# coding: utf-8 -""" - - webencodings.x_user_defined - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - An implementation of the x-user-defined encoding. - - :copyright: Copyright 2012 by Simon Sapin - :license: BSD, see LICENSE for details. - -""" - -from __future__ import unicode_literals - -import codecs - - -### Codec APIs - -class Codec(codecs.Codec): - - def encode(self, input, errors='strict'): - return codecs.charmap_encode(input, errors, encoding_table) - - def decode(self, input, errors='strict'): - return codecs.charmap_decode(input, errors, decoding_table) - - -class IncrementalEncoder(codecs.IncrementalEncoder): - def encode(self, input, final=False): - return codecs.charmap_encode(input, self.errors, encoding_table)[0] - - -class IncrementalDecoder(codecs.IncrementalDecoder): - def decode(self, input, final=False): - return codecs.charmap_decode(input, self.errors, decoding_table)[0] - - -class StreamWriter(Codec, codecs.StreamWriter): - pass - - -class StreamReader(Codec, codecs.StreamReader): - pass - - -### encodings module API - -codec_info = codecs.CodecInfo( - name='x-user-defined', - encode=Codec().encode, - decode=Codec().decode, - incrementalencoder=IncrementalEncoder, - incrementaldecoder=IncrementalDecoder, - streamreader=StreamReader, - streamwriter=StreamWriter, -) - - -### Decoding Table - -# Python 3: -# for c in range(256): print(' %r' % chr(c if c < 128 else c + 0xF700)) -decoding_table = ( - '\x00' - '\x01' - '\x02' - '\x03' - '\x04' - '\x05' - '\x06' - '\x07' - '\x08' - '\t' - '\n' - '\x0b' - '\x0c' - '\r' - '\x0e' - '\x0f' - '\x10' - '\x11' - '\x12' - '\x13' - '\x14' - '\x15' - '\x16' - '\x17' - '\x18' - '\x19' - '\x1a' - '\x1b' - '\x1c' - '\x1d' - '\x1e' - '\x1f' - ' ' - '!' - '"' - '#' - '$' - '%' - '&' - "'" - '(' - ')' - '*' - '+' - ',' - '-' - '.' - '/' - '0' - '1' - '2' - '3' - '4' - '5' - '6' - '7' - '8' - '9' - ':' - ';' - '<' - '=' - '>' - '?' - '@' - 'A' - 'B' - 'C' - 'D' - 'E' - 'F' - 'G' - 'H' - 'I' - 'J' - 'K' - 'L' - 'M' - 'N' - 'O' - 'P' - 'Q' - 'R' - 'S' - 'T' - 'U' - 'V' - 'W' - 'X' - 'Y' - 'Z' - '[' - '\\' - ']' - '^' - '_' - '`' - 'a' - 'b' - 'c' - 'd' - 'e' - 'f' - 'g' - 'h' - 'i' - 'j' - 'k' - 'l' - 'm' - 'n' - 'o' - 'p' - 'q' - 'r' - 's' - 't' - 'u' - 'v' - 'w' - 'x' - 'y' - 'z' - '{' - '|' - '}' - '~' - '\x7f' - '\uf780' - '\uf781' - '\uf782' - '\uf783' - '\uf784' - '\uf785' - '\uf786' - '\uf787' - '\uf788' - '\uf789' - '\uf78a' - '\uf78b' - '\uf78c' - '\uf78d' - '\uf78e' - '\uf78f' - '\uf790' - '\uf791' - '\uf792' - '\uf793' - '\uf794' - '\uf795' - '\uf796' - '\uf797' - '\uf798' - '\uf799' - '\uf79a' - '\uf79b' - '\uf79c' - '\uf79d' - '\uf79e' - '\uf79f' - '\uf7a0' - '\uf7a1' - '\uf7a2' - '\uf7a3' - '\uf7a4' - '\uf7a5' - '\uf7a6' - '\uf7a7' - '\uf7a8' - '\uf7a9' - '\uf7aa' - '\uf7ab' - '\uf7ac' - '\uf7ad' - '\uf7ae' - '\uf7af' - '\uf7b0' - '\uf7b1' - '\uf7b2' - '\uf7b3' - '\uf7b4' - '\uf7b5' - '\uf7b6' - '\uf7b7' - '\uf7b8' - '\uf7b9' - '\uf7ba' - '\uf7bb' - '\uf7bc' - '\uf7bd' - '\uf7be' - '\uf7bf' - '\uf7c0' - '\uf7c1' - '\uf7c2' - '\uf7c3' - '\uf7c4' - '\uf7c5' - '\uf7c6' - '\uf7c7' - '\uf7c8' - '\uf7c9' - '\uf7ca' - '\uf7cb' - '\uf7cc' - '\uf7cd' - '\uf7ce' - '\uf7cf' - '\uf7d0' - '\uf7d1' - '\uf7d2' - '\uf7d3' - '\uf7d4' - '\uf7d5' - '\uf7d6' - '\uf7d7' - '\uf7d8' - '\uf7d9' - '\uf7da' - '\uf7db' - '\uf7dc' - '\uf7dd' - '\uf7de' - '\uf7df' - '\uf7e0' - '\uf7e1' - '\uf7e2' - '\uf7e3' - '\uf7e4' - '\uf7e5' - '\uf7e6' - '\uf7e7' - '\uf7e8' - '\uf7e9' - '\uf7ea' - '\uf7eb' - '\uf7ec' - '\uf7ed' - '\uf7ee' - '\uf7ef' - '\uf7f0' - '\uf7f1' - '\uf7f2' - '\uf7f3' - '\uf7f4' - '\uf7f5' - '\uf7f6' - '\uf7f7' - '\uf7f8' - '\uf7f9' - '\uf7fa' - '\uf7fb' - '\uf7fc' - '\uf7fd' - '\uf7fe' - '\uf7ff' -) - -### Encoding table -encoding_table = codecs.charmap_build(decoding_table) diff --git a/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/METADATA new file mode 100644 index 00000000..0dd40dc6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/METADATA @@ -0,0 +1,443 @@ +Metadata-Version: 2.4 +Name: propcache +Version: 0.4.1 +Summary: Accelerated property cache +Home-page: https://github.com/aio-libs/propcache +Author: Andrew Svetlov +Author-email: andrew.svetlov@gmail.com +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache-2.0 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: GitHub Workflows, https://github.com/aio-libs/propcache/actions?query=branch:master +Project-URL: Code of Conduct, https://github.com/aio-libs/.github/blob/master/CODE_OF_CONDUCT.md +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/propcache +Project-URL: Docs: Changelog, https://propcache.readthedocs.io/en/latest/changes/ +Project-URL: Docs: RTD, https://propcache.readthedocs.io +Project-URL: GitHub: issues, https://github.com/aio-libs/propcache/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/propcache +Keywords: cython,cext,propcache +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: NOTICE +Dynamic: license-file + +propcache +========= + +The module provides a fast implementation of cached properties for Python 3.9+. + +.. image:: https://github.com/aio-libs/propcache/actions/workflows/ci-cd.yml/badge.svg + :target: https://github.com/aio-libs/propcache/actions?query=workflow%3ACI + :align: right + +.. image:: https://codecov.io/gh/aio-libs/propcache/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/propcache + +.. image:: https://badge.fury.io/py/propcache.svg + :target: https://badge.fury.io/py/propcache + + +.. image:: https://readthedocs.org/projects/propcache/badge/?version=latest + :target: https://propcache.readthedocs.io + + +.. image:: https://img.shields.io/pypi/pyversions/propcache.svg + :target: https://pypi.python.org/pypi/propcache + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + +Introduction +------------ + +The API is designed to be nearly identical to the built-in ``functools.cached_property`` class, +except for the additional ``under_cached_property`` class which uses ``self._cache`` +instead of ``self.__dict__`` to store the cached values and prevents ``__set__`` from being called. + +For full documentation please read https://propcache.readthedocs.io. + +Installation +------------ + +:: + + $ pip install propcache + +The library is Python 3 only! + +PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install +``propcache`` on another operating system where wheels are not provided, +the the tarball will be used to compile the library from +the source code. It requires a C compiler and and Python headers installed. + +To skip the compilation you must explicitly opt-in by using a PEP 517 +configuration setting ``pure-python``, or setting the ``PROPCACHE_NO_EXTENSIONS`` +environment variable to a non-empty value, e.g.: + +.. code-block:: console + + $ pip install propcache --config-settings=pure-python=false + +Please note that the pure-Python (uncompiled) version is much slower. However, +PyPy always uses a pure-Python implementation, and, as such, it is unaffected +by this variable. + + +API documentation +------------------ + +The documentation is located at https://propcache.readthedocs.io. + +Source code +----------- + +The project is hosted on GitHub_ + +Please file an issue on the `bug tracker +`_ if you have found a bug +or have some suggestion in order to improve the library. + +Discussion list +--------------- + +*aio-libs* google group: https://groups.google.com/forum/#!forum/aio-libs + +Feel free to post your questions and ideas here. + + +Authors and License +------------------- + +The ``propcache`` package is derived from ``yarl`` which is written by Andrew Svetlov. + +It's *Apache 2* licensed and freely available. + + +.. _GitHub: https://github.com/aio-libs/propcache + +========= +Changelog +========= + +.. + You should *NOT* be adding new change log entries to this file, this + file is managed by towncrier. You *may* edit previous change logs to + fix problems like typo corrections or such. + To add a new change log entry, please see + https://pip.pypa.io/en/latest/development/#adding-a-news-entry + we named the news folder "changes". + + WARNING: Don't drop the next directive! + +.. towncrier release notes start + +0.4.1 +===== + +*(2025-10-08)* + + +Bug fixes +--------- + +- Fixed reference leak caused by ``Py_INCREF`` because Cython has its own reference counter systems -- by `@Vizonex `__. + + *Related issues and pull requests on GitHub:* + `#162 `__. + + +Contributor-facing changes +-------------------------- + +- Fixes the default value for the ``os`` + parameter in ``reusable-build-wheel.yml`` + to be ``ubuntu-latest`` instead of + ``ubuntu``. + + *Related issues and pull requests on GitHub:* + `#155 `__. + + +---- + + +0.4.0 +===== + +*(2025-10-04)* + + +Features +-------- + +- Optimized propcache by replacing sentinel ``object`` for checking if + the ``object`` is ``NULL`` and changed ``dict`` API for + Python C-API -- by `@Vizonex `__. + + *Related issues and pull requests on GitHub:* + `#121 `__. + + +Contributor-facing changes +-------------------------- + +- Builds have been added for arm64 Windows + wheels and the ``reusable-build-wheel.yml`` + workflow has been modified to allow for + an OS value (``windows-11-arm``) which + does not include the ``-latest`` postfix + -- by `@finnagin `__. + + *Related issues and pull requests on GitHub:* + `#133 `__. + +- Added CI for CPython 3.14 -- by `@kumaraditya303 `__. + + *Related issues and pull requests on GitHub:* + `#140 `__. + + +---- + + +0.3.2 +===== + +*(2025-06-09)* + + +Improved documentation +---------------------- + +- Fixed incorrect decorator usage in the ``~propcache.api.under_cached_property`` example code -- by `@meanmail `__. + + *Related issues and pull requests on GitHub:* + `#109 `__. + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Updated to use Cython 3.1 universally across the build path -- by `@lysnikolaou `__. + + *Related issues and pull requests on GitHub:* + `#117 `__. + +- Made Cython line tracing opt-in via the ``with-cython-tracing`` build config setting -- by `@bdraco `__. + + Previously, line tracing was enabled by default in ``pyproject.toml``, which caused build issues for some users and made wheels nearly twice as slow. + + Now line tracing is only enabled when explicitly requested via ``pip install . --config-setting=with-cython-tracing=true`` or by setting the ``PROPCACHE_CYTHON_TRACING`` environment variable. + + *Related issues and pull requests on GitHub:* + `#118 `__. + + +---- + + +0.3.1 +===== + +*(2025-03-25)* + + +Bug fixes +--------- + +- Improved typing annotations, fixing some type errors under correct usage + and improving typing robustness generally -- by `@Dreamsorcerer `__. + + *Related issues and pull requests on GitHub:* + `#103 `__. + + +---- + + +0.3.0 +===== + +*(2025-02-20)* + + +Features +-------- + +- Implemented support for the free-threaded build of CPython 3.13 -- by `@lysnikolaou `__. + + *Related issues and pull requests on GitHub:* + `#84 `__. + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Started building wheels for the free-threaded build of CPython 3.13 -- by `@lysnikolaou `__. + + *Related issues and pull requests on GitHub:* + `#84 `__. + + +Contributor-facing changes +-------------------------- + +- GitHub Actions CI/CD is now configured to manage caching pip-ecosystem + dependencies using `re-actors/cache-python-deps`_ -- an action by + `@webknjaz `__ that takes into account ABI stability and the exact + version of Python runtime. + + .. _`re-actors/cache-python-deps`: + https://github.com/marketplace/actions/cache-python-deps + + *Related issues and pull requests on GitHub:* + `#93 `__. + + +---- + + +0.2.1 +===== + +*(2024-12-01)* + + +Bug fixes +--------- + +- Stopped implicitly allowing the use of Cython pre-release versions when + building the distribution package -- by `@ajsanchezsanz `__ and + `@markgreene74 `__. + + *Related commits on GitHub:* + `64df0a6 `__. + +- Fixed ``wrapped`` and ``func`` not being accessible in the Cython versions of ``propcache.api.cached_property`` and ``propcache.api.under_cached_property`` decorators -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#72 `__. + + +Removals and backward incompatible breaking changes +--------------------------------------------------- + +- Removed support for Python 3.8 as it has reached end of life -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#57 `__. + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Stopped implicitly allowing the use of Cython pre-release versions when + building the distribution package -- by `@ajsanchezsanz `__ and + `@markgreene74 `__. + + *Related commits on GitHub:* + `64df0a6 `__. + + +---- + + +0.2.0 +===== + +*(2024-10-07)* + + +Bug fixes +--------- + +- Fixed loading the C-extensions on Python 3.8 -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#26 `__. + + +Features +-------- + +- Improved typing for the ``propcache.api.under_cached_property`` decorator -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#38 `__. + + +Improved documentation +---------------------- + +- Added API documentation for the ``propcache.api.cached_property`` and ``propcache.api.under_cached_property`` decorators -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#16 `__. + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Moved ``propcache.api.under_cached_property`` and ``propcache.api.cached_property`` to `propcache.api` -- by `@bdraco `__. + + Both decorators remain importable from the top-level package, however importing from `propcache.api` is now the recommended way to use them. + + *Related issues and pull requests on GitHub:* + `#19 `__, `#24 `__, `#32 `__. + +- Converted project to use a src layout -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#22 `__, `#29 `__, `#37 `__. + + +---- + + +0.1.0 +===== + +*(2024-10-03)* + + +Features +-------- + +- Added ``armv7l`` wheels -- by `@bdraco `__. + + *Related issues and pull requests on GitHub:* + `#5 `__. + + +---- + + +0.0.0 +===== + +*(2024-10-02)* + + +- Initial release. diff --git a/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/RECORD new file mode 100644 index 00000000..ce2a4127 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/RECORD @@ -0,0 +1,18 @@ +propcache-0.4.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +propcache-0.4.1.dist-info/METADATA,sha256=_CPzxSszbVM1zQU_R9kf9jFpSeZa0fHj38zVKE9Nbs4,13745 +propcache-0.4.1.dist-info/RECORD,, +propcache-0.4.1.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 +propcache-0.4.1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 +propcache-0.4.1.dist-info/licenses/NOTICE,sha256=VtasbIEFwKUTBMIdsGDjYa-ajqCvmnXCOcKLXRNpODg,609 +propcache-0.4.1.dist-info/top_level.txt,sha256=pVF_GbqSAITPMiX27kfU3QP9-ufhRvkADmudDxWdF3w,10 +propcache/__init__.py,sha256=8kebeGvYn7s-ow1AFmK0A4EvonZMpyM7Lkzs2Ktia3Y,965 +propcache/__pycache__/__init__.cpython-312.pyc,, +propcache/__pycache__/_helpers.cpython-312.pyc,, +propcache/__pycache__/_helpers_py.cpython-312.pyc,, +propcache/__pycache__/api.cpython-312.pyc,, +propcache/_helpers.py,sha256=68SQm6kETN8Mnt9Ol26LJYgHgmB0mKy1tp92888zN4k,1553 +propcache/_helpers_c.cpython-312-x86_64-linux-gnu.so,sha256=tGelusHoANUmokeGexAZA3jwXq-nYzlSbGJv8DEy6ug,745424 +propcache/_helpers_c.pyx,sha256=kcJa1U5lh54TPCqAeZ0cVB7URcb3I8ZbJieOrkNhLQE,3265 +propcache/_helpers_py.py,sha256=Wixs2zWA-FBU-j4zLPyBUU24FEfPhKk-UunFSp9q95U,1909 +propcache/api.py,sha256=wvgB-ypkkI5uf72VVYl2NFGc_TnzUQA2CxC7dTlL5ak,179 +propcache/py.typed,sha256=ay5OMO475PlcZ_Fbun9maHW7Y6MBTk0UXL4ztHx3Iug,14 diff --git a/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/WHEEL new file mode 100644 index 00000000..f3e8a970 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/WHEEL @@ -0,0 +1,7 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/licenses/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/licenses/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/licenses/NOTICE b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/licenses/NOTICE new file mode 100644 index 00000000..fa53b2b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/licenses/NOTICE @@ -0,0 +1,13 @@ + Copyright 2016-2021, Andrew Svetlov and aio-libs team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/top_level.txt new file mode 100644 index 00000000..8c9accf6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache-0.4.1.dist-info/top_level.txt @@ -0,0 +1 @@ +propcache diff --git a/venv/lib/python3.12/site-packages/propcache/__init__.py b/venv/lib/python3.12/site-packages/propcache/__init__.py new file mode 100644 index 00000000..a6444d17 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache/__init__.py @@ -0,0 +1,32 @@ +"""propcache: An accelerated property cache for Python classes.""" + +from typing import TYPE_CHECKING + +_PUBLIC_API = ("cached_property", "under_cached_property") + +__version__ = "0.4.1" +__all__ = () + +# Imports have moved to `propcache.api` in 0.2.0+. +# This module is now a facade for the API. +if TYPE_CHECKING: + from .api import cached_property as cached_property # noqa: F401 + from .api import under_cached_property as under_cached_property # noqa: F401 + + +def _import_facade(attr: str) -> object: + """Import the public API from the `api` module.""" + if attr in _PUBLIC_API: + from . import api # pylint: disable=import-outside-toplevel + + return getattr(api, attr) + raise AttributeError(f"module '{__package__}' has no attribute '{attr}'") + + +def _dir_facade() -> list[str]: + """Include the public API in the module's dir() output.""" + return [*_PUBLIC_API, *globals().keys()] + + +__getattr__ = _import_facade +__dir__ = _dir_facade diff --git a/venv/lib/python3.12/site-packages/propcache/_helpers.py b/venv/lib/python3.12/site-packages/propcache/_helpers.py new file mode 100644 index 00000000..1e52895c --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache/_helpers.py @@ -0,0 +1,39 @@ +import os +import sys +from typing import TYPE_CHECKING + +__all__ = ("cached_property", "under_cached_property") + + +NO_EXTENSIONS = bool(os.environ.get("PROPCACHE_NO_EXTENSIONS")) # type: bool +if sys.implementation.name != "cpython": + NO_EXTENSIONS = True + + +# isort: off +if TYPE_CHECKING: + from ._helpers_py import cached_property as cached_property_py + from ._helpers_py import under_cached_property as under_cached_property_py + + cached_property = cached_property_py + under_cached_property = under_cached_property_py +elif not NO_EXTENSIONS: # pragma: no branch + try: + from ._helpers_c import cached_property as cached_property_c # type: ignore[attr-defined, unused-ignore] + from ._helpers_c import under_cached_property as under_cached_property_c # type: ignore[attr-defined, unused-ignore] + + cached_property = cached_property_c + under_cached_property = under_cached_property_c + except ImportError: # pragma: no cover + from ._helpers_py import cached_property as cached_property_py + from ._helpers_py import under_cached_property as under_cached_property_py + + cached_property = cached_property_py # type: ignore[assignment, misc] + under_cached_property = under_cached_property_py +else: + from ._helpers_py import cached_property as cached_property_py + from ._helpers_py import under_cached_property as under_cached_property_py + + cached_property = cached_property_py # type: ignore[assignment, misc] + under_cached_property = under_cached_property_py +# isort: on diff --git a/venv/lib/python3.12/site-packages/propcache/_helpers_c.cpython-312-x86_64-linux-gnu.so b/venv/lib/python3.12/site-packages/propcache/_helpers_c.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..ca4ba8c95a3d99436409a35f74b669236c289a20 GIT binary patch literal 745424 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≫B;e4|2};$4oo<5~D(>JqDy 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

    Ds(qAGsU4LaV4L2humR7cX;| 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 Mbzn(DY$=3u>N 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 + + def __set__(self, inst, value): + raise AttributeError("cached property is read-only") + + __class_getitem__ = classmethod(GenericAlias) + + +cdef class cached_property: + """Use as a class method decorator. It operates almost exactly like + the Python `@property` decorator, but it puts the result of the + method it decorates into the instance dict after the first call, + effectively replacing the function it decorates with an instance + variable. It is, in Python parlance, a data descriptor. + + """ + + cdef readonly object func + cdef object name + + def __init__(self, func): + self.func = func + self.name = None + + @property + def __doc__(self): + return self.func.__doc__ + + def __set_name__(self, owner, object name): + if self.name is None: + self.name = name + elif name != self.name: + raise TypeError( + "Cannot assign the same cached_property to two different names " + f"({self.name!r} and {name!r})." + ) + + def __get__(self, inst, owner): + if inst is None: + return self + if self.name is None: + raise TypeError( + "Cannot use cached_property instance" + " without calling __set_name__ on it.") + cdef dict cache = inst.__dict__ + cdef PyObject* val = PyDict_GetItem(cache, self.name) + if val is NULL: + val = PyObject_CallOneArg(self.func, inst) + PyDict_SetItem(cache, self.name, val) + Py_DECREF(val) + return val + + __class_getitem__ = classmethod(GenericAlias) diff --git a/venv/lib/python3.12/site-packages/propcache/_helpers_py.py b/venv/lib/python3.12/site-packages/propcache/_helpers_py.py new file mode 100644 index 00000000..1374fc6d --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache/_helpers_py.py @@ -0,0 +1,62 @@ +"""Various helper functions.""" + +import sys +from collections.abc import Mapping +from functools import cached_property +from typing import Any, Callable, Generic, Optional, Protocol, TypeVar, Union, overload + +__all__ = ("under_cached_property", "cached_property") + + +if sys.version_info >= (3, 11): + from typing import Self +else: + Self = Any + +_T = TypeVar("_T") +# We use Mapping to make it possible to use TypedDict, but this isn't +# technically type safe as we need to assign into the dict. +_Cache = TypeVar("_Cache", bound=Mapping[str, Any]) + + +class _CacheImpl(Protocol[_Cache]): + _cache: _Cache + + +class under_cached_property(Generic[_T]): + """Use as a class method decorator. + + It operates almost exactly like + the Python `@property` decorator, but it puts the result of the + method it decorates into the instance dict after the first call, + effectively replacing the function it decorates with an instance + variable. It is, in Python parlance, a data descriptor. + """ + + def __init__(self, wrapped: Callable[[Any], _T]) -> None: + self.wrapped = wrapped + self.__doc__ = wrapped.__doc__ + self.name = wrapped.__name__ + + @overload + def __get__(self, inst: None, owner: Optional[type[object]] = None) -> Self: ... + + @overload + def __get__( + self, inst: _CacheImpl[Any], owner: Optional[type[object]] = None + ) -> _T: ... + + def __get__( + self, inst: Optional[_CacheImpl[Any]], owner: Optional[type[object]] = None + ) -> Union[_T, Self]: + if inst is None: + return self + try: + return inst._cache[self.name] # type: ignore[no-any-return] + except KeyError: + val = self.wrapped(inst) + inst._cache[self.name] = val + return val + + def __set__(self, inst: _CacheImpl[Any], value: _T) -> None: + raise AttributeError("cached property is read-only") diff --git a/venv/lib/python3.12/site-packages/propcache/api.py b/venv/lib/python3.12/site-packages/propcache/api.py new file mode 100644 index 00000000..22389e63 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache/api.py @@ -0,0 +1,8 @@ +"""Public API of the property caching library.""" + +from ._helpers import cached_property, under_cached_property + +__all__ = ( + "cached_property", + "under_cached_property", +) diff --git a/venv/lib/python3.12/site-packages/propcache/py.typed b/venv/lib/python3.12/site-packages/propcache/py.typed new file mode 100644 index 00000000..dcf2c804 --- /dev/null +++ b/venv/lib/python3.12/site-packages/propcache/py.typed @@ -0,0 +1 @@ +# Placeholder diff --git a/venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/RECORD index 9faf216f..32597ede 100644 --- a/venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/RECORD +++ b/venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/RECORD @@ -1,4 +1,4 @@ -../../../bin/pygmentize,sha256=s94eBtR_u0YyHzWpvltonEtTbjg7Bm8dd2xh3iHpxyo,240 +../../../bin/pygmentize,sha256=Xfrb8u6xIck9TFu3Fpv-dFJwxNpvxngnkdE6TEgIFCg,198 pygments-2.20.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 pygments-2.20.0.dist-info/METADATA,sha256=4FKPUbMEJ_rpRyNmK6Yi-NjbKk2NPxNlaY1npSRQqEU,2476 pygments-2.20.0.dist-info/RECORD,, diff --git a/venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/RECORD b/venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/RECORD index 48d1c517..35bb07db 100644 --- a/venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/RECORD +++ b/venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/RECORD @@ -1,5 +1,5 @@ -../../../bin/py.test,sha256=FJFinfgwixveJgz6TuUWjmI8gsNVRPHofhOyHgtVi_8,246 -../../../bin/pytest,sha256=FJFinfgwixveJgz6TuUWjmI8gsNVRPHofhOyHgtVi_8,246 +../../../bin/py.test,sha256=ed3UaVeHjUcMnxSv_0jR1hrRSHAFHcSGEmqkTaQYhG8,204 +../../../bin/pytest,sha256=ed3UaVeHjUcMnxSv_0jR1hrRSHAFHcSGEmqkTaQYhG8,204 __pycache__/py.cpython-312.pyc,, _pytest/__init__.py,sha256=4IdRJhnW5XG2KlaJkOxn5_TC9WeQ5tXDSF7tbb4vEso,391 _pytest/__pycache__/__init__.cpython-312.pyc,, diff --git a/venv/lib/python3.12/site-packages/python_dotenv-1.2.2.dist-info/RECORD b/venv/lib/python3.12/site-packages/python_dotenv-1.2.2.dist-info/RECORD index 8a153018..116beda5 100644 --- a/venv/lib/python3.12/site-packages/python_dotenv-1.2.2.dist-info/RECORD +++ b/venv/lib/python3.12/site-packages/python_dotenv-1.2.2.dist-info/RECORD @@ -1,4 +1,4 @@ -../../../bin/dotenv,sha256=UNcGdXvaBtDE0F4-g35Dj9DtLUuYf_oPWjn_X_0iATM,237 +../../../bin/dotenv,sha256=xnQKgZBxY71YNt49KaMpabp6kJmgot-hGGcmnHdNT44,195 dotenv/__init__.py,sha256=bhY-iK6wwHZamWII6eSCuCAWXUNdn2TQFmEOuXSU7SM,1230 dotenv/__main__.py,sha256=N0RhLG7nHIqtlJHwwepIo-zbJPNx9sewCCRGY528h_4,129 dotenv/__pycache__/__init__.cpython-312.pyc,, @@ -19,7 +19,6 @@ dotenv/version.py,sha256=uuf4VNtTNA93fMhoAur9YafzaKJFnczY-H1SSCSuRVQ,22 python_dotenv-1.2.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 python_dotenv-1.2.2.dist-info/METADATA,sha256=WUMKne6PgRjb9pPkHEo-UVBibRmQH0cnUD2pSZ9aZKE,27524 python_dotenv-1.2.2.dist-info/RECORD,, -python_dotenv-1.2.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 python_dotenv-1.2.2.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91 python_dotenv-1.2.2.dist-info/entry_points.txt,sha256=yRl1rCbswb1nQTQ_gZRlCw5QfabztUGnfGWLhlXFNdI,47 python_dotenv-1.2.2.dist-info/licenses/LICENSE,sha256=gGGbcEnwjIFoOtDgHwjyV6hAZS3XHugxRtNmWMfSwrk,1556 diff --git a/venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/RECORD b/venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/RECORD index 3e63bb24..5d439d0f 100644 --- a/venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/RECORD +++ b/venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/RECORD @@ -3,6 +3,7 @@ _yaml/__pycache__/__init__.cpython-312.pyc,, pyyaml-6.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 pyyaml-6.0.3.dist-info/METADATA,sha256=A8O0Fe040J-u3Ek2DpMHabQMWPaFhebeAOLkkWqFjTQ,2351 pyyaml-6.0.3.dist-info/RECORD,, +pyyaml-6.0.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyyaml-6.0.3.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 pyyaml-6.0.3.dist-info/licenses/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101 pyyaml-6.0.3.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11 diff --git a/venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/REQUESTED b/venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/rsa-4.9.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/rsa-4.9.1.dist-info/RECORD index 27f33b54..207d8df5 100644 --- a/venv/lib/python3.12/site-packages/rsa-4.9.1.dist-info/RECORD +++ b/venv/lib/python3.12/site-packages/rsa-4.9.1.dist-info/RECORD @@ -1,9 +1,9 @@ -../../../bin/pyrsa-decrypt,sha256=-BIOVPmpiL3mxLevzfU7KXbL1b7BiRblIScq-6zJaC4,237 -../../../bin/pyrsa-encrypt,sha256=vkaB9tWOtXur91PBu2GUmw4IJbNn7gNP59KwNHEMVQ4,237 -../../../bin/pyrsa-keygen,sha256=7oLoVszTzD1tkJWLcTtZ_0amtHjj9wTqNutsfzSmGgU,235 -../../../bin/pyrsa-priv2pub,sha256=36SsdpVIYEdxW4LZSLPYt2r6njcnhmtB77Z8JsQbm8A,258 -../../../bin/pyrsa-sign,sha256=mBrLTKv0wHU0a2IREvbtfz5XS7TF_oN0y01ikx2CiHw,231 -../../../bin/pyrsa-verify,sha256=VxyOCDIhG-xrG-h-lUudjsLL6BSLfNiF4K471BYitHg,235 +../../../bin/pyrsa-decrypt,sha256=CMDQbZlb9SL4wVJ1-joG_3f3_KgqRkqfAMHaG7F1ddk,195 +../../../bin/pyrsa-encrypt,sha256=a1n9HAisuijqTs3pMYFoUzJ89vVaSjdaKnt3dzefghw,195 +../../../bin/pyrsa-keygen,sha256=GSmiR3uv2D0H0W6-wIjrAvgl8s1xnwkN_cR475X7g3k,193 +../../../bin/pyrsa-priv2pub,sha256=IHQCr7kfI_PAOuEZyz_JJXNeL_ESErO8gadLgylR1sI,216 +../../../bin/pyrsa-sign,sha256=K6E2mtSevHePhq31kt7ueUy-P5uuYUlhYdWnBBRqMQg,189 +../../../bin/pyrsa-verify,sha256=krLEyoBdBhvGxjLzA0XK4QBwguLCyLpO5OjoR2374Jg,193 rsa-4.9.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 rsa-4.9.1.dist-info/LICENSE,sha256=Bz8ot9OJyP509gfhfCf4HqpazmntxDqITyP0G0HFxyY,577 rsa-4.9.1.dist-info/METADATA,sha256=dlNfSHIPYbcprUNaXLdI_h220MlG7Wa5kzbjPjqpd4k,5590 diff --git a/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/METADATA b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/METADATA new file mode 100644 index 00000000..6c9dccf7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/METADATA @@ -0,0 +1,115 @@ +Metadata-Version: 2.4 +Name: soupsieve +Version: 2.8.3 +Summary: A modern CSS selector implementation for Beautiful Soup. +Project-URL: Homepage, https://github.com/facelessuser/soupsieve +Author-email: Isaac Muse +License-Expression: MIT +License-File: LICENSE.md +Keywords: CSS,HTML,XML,filter,query,selector,soup +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Typing :: Typed +Requires-Python: >=3.9 +Description-Content-Type: text/markdown + +[![Donate via PayPal][donate-image]][donate-link] +[![Build][github-ci-image]][github-ci-link] +[![Coverage Status][codecov-image]][codecov-link] +[![PyPI Version][pypi-image]][pypi-link] +[![PyPI Downloads][pypi-down]][pypi-link] +[![PyPI - Python Version][python-image]][pypi-link] +[![License][license-image-mit]][license-link] + +# Soup Sieve + +## Overview + +Soup Sieve is a CSS selector library designed to be used with [Beautiful Soup 4][bs4]. It aims to provide selecting, +matching, and filtering using modern CSS selectors. Soup Sieve currently provides selectors from the CSS level 1 +specifications up through the latest CSS level 4 drafts and beyond (though some are not yet implemented). + +Soup Sieve was written with the intent to replace Beautiful Soup's builtin select feature, and as of Beautiful Soup +version 4.7.0, it now is :confetti_ball:. Soup Sieve can also be imported in order to use its API directly for +more controlled, specialized parsing. + +Soup Sieve has implemented most of the CSS selectors up through the latest CSS draft specifications, though there are a +number that don't make sense in a non-browser environment. Selectors that cannot provide meaningful functionality simply +do not match anything. Some of the supported selectors are: + +- `.classes` +- `#ids` +- `[attributes=value]` +- `parent child` +- `parent > child` +- `sibling ~ sibling` +- `sibling + sibling` +- `:not(element.class, element2.class)` +- `:is(element.class, element2.class)` +- `parent:has(> child)` +- and [many more](https://facelessuser.github.io/soupsieve/selectors/) + + +## Installation + +You must have Beautiful Soup already installed: + +``` +pip install beautifulsoup4 +``` + +In most cases, assuming you've installed version 4.7.0, that should be all you need to do, but if you've installed via +some alternative method, and Soup Sieve is not automatically installed, you can install it directly: + +``` +pip install soupsieve +``` + +If you want to manually install it from source, first ensure that [`build`](https://pypi.org/project/build/) is +installed: + +``` +pip install build +``` + +Then navigate to the root of the project and build the wheel and install (replacing `` with the current version): + +``` +python -m build -w +pip install dist/soupsieve--py3-none-any.whl +``` + +## Documentation + +Documentation is found here: https://facelessuser.github.io/soupsieve/. + +## License + +MIT + +[bs4]: https://beautiful-soup-4.readthedocs.io/en/latest/# + +[github-ci-image]: https://github.com/facelessuser/soupsieve/workflows/build/badge.svg +[github-ci-link]: https://github.com/facelessuser/soupsieve/actions?query=workflow%3Abuild+branch%3Amain +[codecov-image]: https://img.shields.io/codecov/c/github/facelessuser/soupsieve/master.svg?logo=codecov&logoColor=aaaaaa&labelColor=333333 +[codecov-link]: https://codecov.io/github/facelessuser/soupsieve +[pypi-image]: https://img.shields.io/pypi/v/soupsieve.svg?logo=pypi&logoColor=aaaaaa&labelColor=333333 +[pypi-down]: https://img.shields.io/pypi/dm/soupsieve.svg?logo=pypi&logoColor=aaaaaa&labelColor=333333 +[pypi-link]: https://pypi.python.org/pypi/soupsieve +[python-image]: https://img.shields.io/pypi/pyversions/soupsieve?logo=python&logoColor=aaaaaa&labelColor=333333 +[license-image-mit]: https://img.shields.io/badge/license-MIT-blue.svg?labelColor=333333 +[license-link]: https://github.com/facelessuser/soupsieve/blob/main/LICENSE.md +[donate-image]: https://img.shields.io/badge/Donate-PayPal-3fabd1?logo=paypal +[donate-link]: https://www.paypal.me/facelessuser diff --git a/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/RECORD b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/RECORD new file mode 100644 index 00000000..9cd3fe5e --- /dev/null +++ b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/RECORD @@ -0,0 +1,20 @@ +soupsieve-2.8.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +soupsieve-2.8.3.dist-info/METADATA,sha256=T4dV61JnwfNOYmmGwPM1amvWh0emCXp-UWjhQR299CA,4635 +soupsieve-2.8.3.dist-info/RECORD,, +soupsieve-2.8.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87 +soupsieve-2.8.3.dist-info/licenses/LICENSE.md,sha256=dqCs25jDpjaB4gtvCWaZ74ytX7NdVu74OH4Ppx15epo,1096 +soupsieve/__init__.py,sha256=FV4Ol2FRldy7rG94nQHNHoJTZIdQbUJdZkGhXkI3tiE,4581 +soupsieve/__meta__.py,sha256=E3T-7sMnV7LDz3ldOR8FmijqLcBMPhGPZgk_OIPQEUc,6766 +soupsieve/__pycache__/__init__.cpython-312.pyc,, +soupsieve/__pycache__/__meta__.cpython-312.pyc,, +soupsieve/__pycache__/css_match.cpython-312.pyc,, +soupsieve/__pycache__/css_parser.cpython-312.pyc,, +soupsieve/__pycache__/css_types.cpython-312.pyc,, +soupsieve/__pycache__/pretty.cpython-312.pyc,, +soupsieve/__pycache__/util.cpython-312.pyc,, +soupsieve/css_match.py,sha256=QmLmHPDYS8ye_YnRmVOK_Ei1BX_JrCFGiygW2RV9coE,61404 +soupsieve/css_parser.py,sha256=BUdCC03nPuaiRE2Ol3jVlwbV5TDcML4Zr_AGg5tEGNo,47222 +soupsieve/css_types.py,sha256=ycysGoIIBLQxOzbqEv90HKRUH2WO3OsLJ25cjovDd1o,9940 +soupsieve/pretty.py,sha256=iSM-NKCzruJQCwEriQT72LrF4slxsib7TB552H2bsqA,4362 +soupsieve/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +soupsieve/util.py,sha256=Q0MVH77cUBXMtKV0D2f8Syf90remPe0EfLqe6-msAeI,3352 diff --git a/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/WHEEL b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/WHEEL new file mode 100644 index 00000000..ae8ec1bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.28.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/licenses/LICENSE.md b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/licenses/LICENSE.md new file mode 100644 index 00000000..128c5342 --- /dev/null +++ b/venv/lib/python3.12/site-packages/soupsieve-2.8.3.dist-info/licenses/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 - 2026 Isaac Muse + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/soupsieve/__init__.py b/venv/lib/python3.12/site-packages/soupsieve/__init__.py new file mode 100644 index 00000000..e170c71c --- /dev/null +++ b/venv/lib/python3.12/site-packages/soupsieve/__init__.py @@ -0,0 +1,168 @@ +""" +Soup Sieve. + +A CSS selector filter for BeautifulSoup4. + +MIT License + +Copyright (c) 2018 Isaac Muse + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +from __future__ import annotations +from .__meta__ import __version__, __version_info__ # noqa: F401 +from . import css_parser as cp +from . import css_match as cm +from . import css_types as ct +from .util import DEBUG, SelectorSyntaxError # noqa: F401 +import bs4 +from typing import Any, Iterator, Iterable + +__all__ = ( + 'DEBUG', 'SelectorSyntaxError', 'SoupSieve', + 'closest', 'compile', 'filter', 'iselect', + 'match', 'select', 'select_one' +) + +SoupSieve = cm.SoupSieve + + +def compile( # noqa: A001 + pattern: str, + namespaces: dict[str, str] | None = None, + flags: int = 0, + *, + custom: dict[str, str] | None = None, + **kwargs: Any +) -> cm.SoupSieve: + """Compile CSS pattern.""" + + if isinstance(pattern, SoupSieve): + if flags: + raise ValueError("Cannot process 'flags' argument on a compiled selector list") + elif namespaces is not None: + raise ValueError("Cannot process 'namespaces' argument on a compiled selector list") + elif custom is not None: + raise ValueError("Cannot process 'custom' argument on a compiled selector list") + return pattern + + return cp._cached_css_compile( + pattern, + ct.Namespaces(namespaces) if namespaces is not None else namespaces, + ct.CustomSelectors(custom) if custom is not None else custom, + flags + ) + + +def purge() -> None: + """Purge cached patterns.""" + + cp._purge_cache() + + +def closest( + select: str, + tag: bs4.Tag, + namespaces: dict[str, str] | None = None, + flags: int = 0, + *, + custom: dict[str, str] | None = None, + **kwargs: Any +) -> bs4.Tag | None: + """Match closest ancestor.""" + + return compile(select, namespaces, flags, **kwargs).closest(tag) + + +def match( + select: str, + tag: bs4.Tag, + namespaces: dict[str, str] | None = None, + flags: int = 0, + *, + custom: dict[str, str] | None = None, + **kwargs: Any +) -> bool: + """Match node.""" + + return compile(select, namespaces, flags, **kwargs).match(tag) + + +def filter( # noqa: A001 + select: str, + iterable: Iterable[bs4.Tag], + namespaces: dict[str, str] | None = None, + flags: int = 0, + *, + custom: dict[str, str] | None = None, + **kwargs: Any +) -> list[bs4.Tag]: + """Filter list of nodes.""" + + return compile(select, namespaces, flags, **kwargs).filter(iterable) + + +def select_one( + select: str, + tag: bs4.Tag, + namespaces: dict[str, str] | None = None, + flags: int = 0, + *, + custom: dict[str, str] | None = None, + **kwargs: Any +) -> bs4.Tag | None: + """Select a single tag.""" + + return compile(select, namespaces, flags, **kwargs).select_one(tag) + + +def select( + select: str, + tag: bs4.Tag, + namespaces: dict[str, str] | None = None, + limit: int = 0, + flags: int = 0, + *, + custom: dict[str, str] | None = None, + **kwargs: Any +) -> list[bs4.Tag]: + """Select the specified tags.""" + + return compile(select, namespaces, flags, **kwargs).select(tag, limit) + + +def iselect( + select: str, + tag: bs4.Tag, + namespaces: dict[str, str] | None = None, + limit: int = 0, + flags: int = 0, + *, + custom: dict[str, str] | None = None, + **kwargs: Any +) -> Iterator[bs4.Tag]: + """Iterate the specified tags.""" + + yield from compile(select, namespaces, flags, **kwargs).iselect(tag, limit) + + +def escape(ident: str) -> str: + """Escape identifier.""" + + return cp.escape(ident) diff --git a/venv/lib/python3.12/site-packages/soupsieve/__meta__.py b/venv/lib/python3.12/site-packages/soupsieve/__meta__.py new file mode 100644 index 00000000..c472d5c6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/soupsieve/__meta__.py @@ -0,0 +1,197 @@ +"""Meta related things.""" +from __future__ import annotations +from collections import namedtuple +import re + +RE_VER = re.compile( + r'''(?x) + (?P\d+)(?:\.(?P\d+))?(?:\.(?P\d+))? + (?:(?Pa|b|rc)(?P
    \d+))?
    +    (?:\.post(?P\d+))?
    +    (?:\.dev(?P\d+))?
    +    '''
    +)
    +
    +REL_MAP = {
    +    ".dev": "",
    +    ".dev-alpha": "a",
    +    ".dev-beta": "b",
    +    ".dev-candidate": "rc",
    +    "alpha": "a",
    +    "beta": "b",
    +    "candidate": "rc",
    +    "final": ""
    +}
    +
    +DEV_STATUS = {
    +    ".dev": "2 - Pre-Alpha",
    +    ".dev-alpha": "2 - Pre-Alpha",
    +    ".dev-beta": "2 - Pre-Alpha",
    +    ".dev-candidate": "2 - Pre-Alpha",
    +    "alpha": "3 - Alpha",
    +    "beta": "4 - Beta",
    +    "candidate": "4 - Beta",
    +    "final": "5 - Production/Stable"
    +}
    +
    +PRE_REL_MAP = {"a": 'alpha', "b": 'beta', "rc": 'candidate'}
    +
    +
    +class Version(namedtuple("Version", ["major", "minor", "micro", "release", "pre", "post", "dev"])):
    +    """
    +    Get the version (PEP 440).
    +
    +    A biased approach to the PEP 440 semantic version.
    +
    +    Provides a tuple structure which is sorted for comparisons `v1 > v2` etc.
    +      (major, minor, micro, release type, pre-release build, post-release build, development release build)
    +    Release types are named in is such a way they are comparable with ease.
    +    Accessors to check if a development, pre-release, or post-release build. Also provides accessor to get
    +    development status for setup files.
    +
    +    How it works (currently):
    +
    +    - You must specify a release type as either `final`, `alpha`, `beta`, or `candidate`.
    +    - To define a development release, you can use either `.dev`, `.dev-alpha`, `.dev-beta`, or `.dev-candidate`.
    +      The dot is used to ensure all development specifiers are sorted before `alpha`.
    +      You can specify a `dev` number for development builds, but do not have to as implicit development releases
    +      are allowed.
    +    - You must specify a `pre` value greater than zero if using a prerelease as this project (not PEP 440) does not
    +      allow implicit prereleases.
    +    - You can optionally set `post` to a value greater than zero to make the build a post release. While post releases
    +      are technically allowed in prereleases, it is strongly discouraged, so we are rejecting them. It should be
    +      noted that we do not allow `post0` even though PEP 440 does not restrict this. This project specifically
    +      does not allow implicit post releases.
    +    - It should be noted that we do not support epochs `1!` or local versions `+some-custom.version-1`.
    +
    +    Acceptable version releases:
    +
    +    ```
    +    Version(1, 0, 0, "final")                    1.0
    +    Version(1, 2, 0, "final")                    1.2
    +    Version(1, 2, 3, "final")                    1.2.3
    +    Version(1, 2, 0, ".dev-alpha", pre=4)        1.2a4
    +    Version(1, 2, 0, ".dev-beta", pre=4)         1.2b4
    +    Version(1, 2, 0, ".dev-candidate", pre=4)    1.2rc4
    +    Version(1, 2, 0, "final", post=1)            1.2.post1
    +    Version(1, 2, 3, ".dev")                     1.2.3.dev0
    +    Version(1, 2, 3, ".dev", dev=1)              1.2.3.dev1
    +    ```
    +
    +    """
    +
    +    def __new__(
    +        cls,
    +        major: int, minor: int, micro: int, release: str = "final",
    +        pre: int = 0, post: int = 0, dev: int = 0
    +    ) -> Version:
    +        """Validate version info."""
    +
    +        # Ensure all parts are positive integers.
    +        for value in (major, minor, micro, pre, post):
    +            if not (isinstance(value, int) and value >= 0):
    +                raise ValueError("All version parts except 'release' should be integers.")
    +
    +        if release not in REL_MAP:
    +            raise ValueError(f"'{release}' is not a valid release type.")
    +
    +        # Ensure valid pre-release (we do not allow implicit pre-releases).
    +        if ".dev-candidate" < release < "final":
    +            if pre == 0:
    +                raise ValueError("Implicit pre-releases not allowed.")
    +            elif dev:
    +                raise ValueError("Version is not a development release.")
    +            elif post:
    +                raise ValueError("Post-releases are not allowed with pre-releases.")
    +
    +        # Ensure valid development or development/pre release
    +        elif release < "alpha":
    +            if release > ".dev" and pre == 0:
    +                raise ValueError("Implicit pre-release not allowed.")
    +            elif post:
    +                raise ValueError("Post-releases are not allowed with pre-releases.")
    +
    +        # Ensure a valid normal release
    +        else:
    +            if pre:
    +                raise ValueError("Version is not a pre-release.")
    +            elif dev:
    +                raise ValueError("Version is not a development release.")
    +
    +        return super().__new__(cls, major, minor, micro, release, pre, post, dev)
    +
    +    def _is_pre(self) -> bool:
    +        """Is prerelease."""
    +
    +        return bool(self.pre > 0)
    +
    +    def _is_dev(self) -> bool:
    +        """Is development."""
    +
    +        return bool(self.release < "alpha")
    +
    +    def _is_post(self) -> bool:
    +        """Is post."""
    +
    +        return bool(self.post > 0)
    +
    +    def _get_dev_status(self) -> str:  # pragma: no cover
    +        """Get development status string."""
    +
    +        return DEV_STATUS[self.release]
    +
    +    def _get_canonical(self) -> str:
    +        """Get the canonical output string."""
    +
    +        # Assemble major, minor, micro version and append `pre`, `post`, or `dev` if needed..
    +        if self.micro == 0:
    +            ver = f"{self.major}.{self.minor}"
    +        else:
    +            ver = f"{self.major}.{self.minor}.{self.micro}"
    +        if self._is_pre():
    +            ver += f'{REL_MAP[self.release]}{self.pre}'
    +        if self._is_post():
    +            ver += f".post{self.post}"
    +        if self._is_dev():
    +            ver += f".dev{self.dev}"
    +
    +        return ver
    +
    +
    +def parse_version(ver: str) -> Version:
    +    """Parse version into a comparable Version tuple."""
    +
    +    m = RE_VER.match(ver)
    +
    +    if m is None:
    +        raise ValueError(f"'{ver}' is not a valid version")
    +
    +    # Handle major, minor, micro
    +    major = int(m.group('major'))
    +    minor = int(m.group('minor')) if m.group('minor') else 0
    +    micro = int(m.group('micro')) if m.group('micro') else 0
    +
    +    # Handle pre releases
    +    if m.group('type'):
    +        release = PRE_REL_MAP[m.group('type')]
    +        pre = int(m.group('pre'))
    +    else:
    +        release = "final"
    +        pre = 0
    +
    +    # Handle development releases
    +    dev = m.group('dev') if m.group('dev') else 0
    +    if m.group('dev'):
    +        dev = int(m.group('dev'))
    +        release = '.dev-' + release if pre else '.dev'
    +    else:
    +        dev = 0
    +
    +    # Handle post
    +    post = int(m.group('post')) if m.group('post') else 0
    +
    +    return Version(major, minor, micro, release, pre, post, dev)
    +
    +
    +__version_info__ = Version(2, 8, 3, "final")
    +__version__ = __version_info__._get_canonical()
    diff --git a/venv/lib/python3.12/site-packages/soupsieve/css_match.py b/venv/lib/python3.12/site-packages/soupsieve/css_match.py
    new file mode 100644
    index 00000000..15e3917c
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/soupsieve/css_match.py
    @@ -0,0 +1,1654 @@
    +"""CSS matcher."""
    +from __future__ import annotations
    +from datetime import datetime
    +from . import util
    +import re
    +from . import css_types as ct
    +import unicodedata
    +import bs4
    +from typing import Iterator, Iterable, Any, Callable, Sequence, Any, cast  # noqa: F401, F811
    +
    +# Empty tag pattern (whitespace okay)
    +RE_NOT_EMPTY = re.compile('[^ \t\r\n\f]')
    +
    +RE_NOT_WS = re.compile('[^ \t\r\n\f]+')
    +
    +# Relationships
    +REL_PARENT = ' '
    +REL_CLOSE_PARENT = '>'
    +REL_SIBLING = '~'
    +REL_CLOSE_SIBLING = '+'
    +
    +# Relationships for :has() (forward looking)
    +REL_HAS_PARENT = ': '
    +REL_HAS_CLOSE_PARENT = ':>'
    +REL_HAS_SIBLING = ':~'
    +REL_HAS_CLOSE_SIBLING = ':+'
    +
    +NS_XHTML = 'http://www.w3.org/1999/xhtml'
    +NS_XML = 'http://www.w3.org/XML/1998/namespace'
    +
    +DIR_FLAGS = ct.SEL_DIR_LTR | ct.SEL_DIR_RTL
    +RANGES = ct.SEL_IN_RANGE | ct.SEL_OUT_OF_RANGE
    +
    +DIR_MAP = {
    +    'ltr': ct.SEL_DIR_LTR,
    +    'rtl': ct.SEL_DIR_RTL,
    +    'auto': 0
    +}
    +
    +RE_NUM = re.compile(r"^(?P-?(?:[0-9]{1,}(\.[0-9]+)?|\.[0-9]+))$")
    +RE_TIME = re.compile(r'^(?P[0-9]{2}):(?P[0-9]{2})$')
    +RE_MONTH = re.compile(r'^(?P[0-9]{4,})-(?P[0-9]{2})$')
    +RE_WEEK = re.compile(r'^(?P[0-9]{4,})-W(?P[0-9]{2})$')
    +RE_DATE = re.compile(r'^(?P[0-9]{4,})-(?P[0-9]{2})-(?P[0-9]{2})$')
    +RE_DATETIME = re.compile(
    +    r'^(?P[0-9]{4,})-(?P[0-9]{2})-(?P[0-9]{2})T(?P[0-9]{2}):(?P[0-9]{2})$'
    +)
    +RE_WILD_STRIP = re.compile(r'(?:(?:-\*-)(?:\*(?:-|$))*|-\*$)')
    +
    +MONTHS_30 = (4, 6, 9, 11)  # April, June, September, and November
    +FEB = 2
    +SHORT_MONTH = 30
    +LONG_MONTH = 31
    +FEB_MONTH = 28
    +FEB_LEAP_MONTH = 29
    +DAYS_IN_WEEK = 7
    +
    +
    +class _FakeParent:
    +    """
    +    Fake parent class.
    +
    +    When we have a fragment with no `BeautifulSoup` document object,
    +    we can't evaluate `nth` selectors properly.  Create a temporary
    +    fake parent so we can traverse the root element as a child.
    +    """
    +
    +    def __init__(self, element: bs4.Tag) -> None:
    +        """Initialize."""
    +
    +        self.contents = [element]
    +
    +    def __len__(self) -> int:
    +        """Length."""
    +
    +        return len(self.contents)
    +
    +
    +class _DocumentNav:
    +    """Navigate a Beautiful Soup document."""
    +
    +    @classmethod
    +    def assert_valid_input(cls, tag: Any) -> None:
    +        """Check if valid input tag or document."""
    +
    +        # Fail on unexpected types.
    +        if not cls.is_tag(tag):
    +            raise TypeError(f"Expected a BeautifulSoup 'Tag', but instead received type {type(tag)}")
    +
    +    @staticmethod
    +    def is_doc(obj: bs4.element.PageElement | None) -> bool:
    +        """Is `BeautifulSoup` object."""
    +        return isinstance(obj, bs4.BeautifulSoup)
    +
    +    @staticmethod
    +    def is_tag(obj: bs4.element.PageElement | None) -> bool:
    +        """Is tag."""
    +        return isinstance(obj, bs4.Tag)
    +
    +    @staticmethod
    +    def is_declaration(obj: bs4.element.PageElement | None) -> bool:  # pragma: no cover
    +        """Is declaration."""
    +        return isinstance(obj, bs4.Declaration)
    +
    +    @staticmethod
    +    def is_cdata(obj: bs4.element.PageElement | None) -> bool:
    +        """Is CDATA."""
    +        return isinstance(obj, bs4.CData)
    +
    +    @staticmethod
    +    def is_processing_instruction(obj: bs4.element.PageElement | None) -> bool:  # pragma: no cover
    +        """Is processing instruction."""
    +        return isinstance(obj, bs4.ProcessingInstruction)
    +
    +    @staticmethod
    +    def is_navigable_string(obj: bs4.element.PageElement | None) -> bool:
    +        """Is navigable string."""
    +        return isinstance(obj, bs4.element.NavigableString)
    +
    +    @staticmethod
    +    def is_special_string(obj: bs4.element.PageElement | None) -> bool:
    +        """Is special string."""
    +        return isinstance(obj, (bs4.Comment, bs4.Declaration, bs4.CData, bs4.ProcessingInstruction, bs4.Doctype))
    +
    +    @classmethod
    +    def is_content_string(cls, obj: bs4.element.PageElement | None) -> bool:
    +        """Check if node is content string."""
    +
    +        return cls.is_navigable_string(obj) and not cls.is_special_string(obj)
    +
    +    @staticmethod
    +    def create_fake_parent(el: bs4.Tag) -> _FakeParent:
    +        """Create fake parent for a given element."""
    +
    +        return _FakeParent(el)
    +
    +    @staticmethod
    +    def is_xml_tree(el: bs4.Tag | None) -> bool:
    +        """Check if element (or document) is from a XML tree."""
    +
    +        return el is not None and bool(el._is_xml)
    +
    +    def is_iframe(self, el: bs4.Tag | None) -> bool:
    +        """Check if element is an `iframe`."""
    +
    +        if el is None:  # pragma: no cover
    +            return False
    +
    +        return bool(
    +            ((el.name if self.is_xml_tree(el) else util.lower(el.name)) == 'iframe') and
    +            self.is_html_tag(el)  # type: ignore[attr-defined]
    +        )
    +
    +    def is_root(self, el: bs4.Tag) -> bool:
    +        """
    +        Return whether element is a root element.
    +
    +        We check that the element is the root of the tree (which we have already pre-calculated),
    +        and we check if it is the root element under an `iframe`.
    +        """
    +
    +        root = self.root and self.root is el  # type: ignore[attr-defined]
    +        if not root:
    +            parent = self.get_parent(el)
    +            root = parent is not None and self.is_html and self.is_iframe(parent)  # type: ignore[attr-defined]
    +        return root
    +
    +    def get_contents(self, el: bs4.Tag | None, no_iframe: bool = False) -> Iterator[bs4.element.PageElement]:
    +        """Get contents or contents in reverse."""
    +
    +        if el is not None:
    +            if not no_iframe or not self.is_iframe(el):
    +                yield from el.contents
    +
    +    def get_tag_children(
    +        self,
    +        el: bs4.Tag | None,
    +        start: int | None = None,
    +        reverse: bool = False,
    +        no_iframe: bool = False
    +    ) -> Iterator[bs4.Tag]:
    +        """Get tag children."""
    +
    +        return self.get_children(el, start, reverse, True, no_iframe)  # type: ignore[return-value]
    +
    +    def get_children(
    +        self,
    +        el: bs4.Tag | None,
    +        start: int | None = None,
    +        reverse: bool = False,
    +        tags: bool = False,
    +        no_iframe: bool = False
    +    ) -> Iterator[bs4.element.PageElement]:
    +        """Get children."""
    +
    +        if el is not None and (not no_iframe or not self.is_iframe(el)):
    +            last = len(el.contents) - 1
    +            if start is None:
    +                index = last if reverse else 0
    +            else:
    +                index = start
    +            end = -1 if reverse else last + 1
    +            incr = -1 if reverse else 1
    +
    +            if 0 <= index <= last:
    +                while index != end:
    +                    node = el.contents[index]
    +                    index += incr
    +                    if not tags or self.is_tag(node):
    +                        yield node
    +
    +    def get_tag_descendants(
    +        self,
    +        el: bs4.Tag | None,
    +        no_iframe: bool = False
    +    ) -> Iterator[bs4.Tag]:
    +        """Specifically get tag descendants."""
    +
    +        yield from self.get_descendants(el, tags=True, no_iframe=no_iframe)  # type: ignore[misc]
    +
    +    def get_descendants(
    +        self,
    +        el: bs4.Tag | None,
    +        tags: bool = False,
    +        no_iframe: bool = False
    +    ) -> Iterator[bs4.element.PageElement]:
    +        """Get descendants."""
    +
    +        if el is not None and (not no_iframe or not self.is_iframe(el)):
    +            next_good = None
    +            for child in el.descendants:
    +
    +                if next_good is not None:
    +                    if child is not next_good:
    +                        continue
    +                    next_good = None
    +
    +                if isinstance(child, bs4.Tag):
    +                    if no_iframe and self.is_iframe(child):
    +                        if child.next_sibling is not None:
    +                            next_good = child.next_sibling
    +                        else:
    +                            last_child = child  # type: bs4.element.PageElement
    +                            while isinstance(last_child, bs4.Tag) and last_child.contents:
    +                                last_child = last_child.contents[-1]
    +                            next_good = last_child.next_element
    +                        yield child
    +                        if next_good is None:
    +                            break
    +                        # Coverage isn't seeing this even though it's executed
    +                        continue  # pragma: no cover
    +                    yield child
    +
    +                elif not tags:
    +                    yield child
    +
    +    def get_parent(self, el: bs4.Tag | None, no_iframe: bool = False) -> bs4.Tag | None:
    +        """Get parent."""
    +
    +        parent = el.parent if el is not None else None
    +        if no_iframe and parent is not None and self.is_iframe(parent):  # pragma: no cover
    +            parent = None
    +        return parent
    +
    +    @staticmethod
    +    def get_tag_name(el: bs4.Tag | None) -> str | None:
    +        """Get tag."""
    +
    +        return el.name if el is not None else None
    +
    +    @staticmethod
    +    def get_prefix_name(el: bs4.Tag) -> str | None:
    +        """Get prefix."""
    +
    +        return el.prefix
    +
    +    @staticmethod
    +    def get_uri(el: bs4.Tag | None) -> str | None:
    +        """Get namespace `URI`."""
    +
    +        return el.namespace if el is not None else None
    +
    +    @classmethod
    +    def get_next_tag(cls, el: bs4.Tag) -> bs4.Tag | None:
    +        """Get next sibling tag."""
    +
    +        return cls.get_next(el, tags=True)  # type: ignore[return-value]
    +
    +    @classmethod
    +    def get_next(cls, el: bs4.Tag, tags: bool = False) -> bs4.element.PageElement | None:
    +        """Get next sibling tag."""
    +
    +        sibling = el.next_sibling
    +        while tags and not isinstance(sibling, bs4.Tag) and sibling is not None:
    +            sibling = sibling.next_sibling
    +
    +        if tags and not isinstance(sibling, bs4.Tag):
    +            sibling = None
    +
    +        return sibling
    +
    +    @classmethod
    +    def get_previous_tag(cls, el: bs4.Tag, tags: bool = True) -> bs4.Tag | None:
    +        """Get previous sibling tag."""
    +
    +        return cls.get_previous(el, True)  # type: ignore[return-value]
    +
    +    @classmethod
    +    def get_previous(cls, el: bs4.Tag, tags: bool = False) -> bs4.element.PageElement | None:
    +        """Get previous sibling tag."""
    +
    +        sibling = el.previous_sibling
    +        while tags and not isinstance(sibling, bs4.Tag) and sibling is not None:
    +            sibling = sibling.previous_sibling
    +
    +        if tags and not isinstance(sibling, bs4.Tag):
    +            sibling = None
    +
    +        return sibling
    +
    +    @staticmethod
    +    def has_html_ns(el: bs4.Tag | None) -> bool:
    +        """
    +        Check if element has an HTML namespace.
    +
    +        This is a bit different than whether a element is treated as having an HTML namespace,
    +        like we do in the case of `is_html_tag`.
    +        """
    +
    +        ns = getattr(el, 'namespace') if el is not None else None  # noqa: B009
    +        return bool(ns and ns == NS_XHTML)
    +
    +    @staticmethod
    +    def split_namespace(el: bs4.Tag | None, attr_name: str) -> tuple[str | None, str | None]:
    +        """Return namespace and attribute name without the prefix."""
    +
    +        if el is None:  # pragma: no cover
    +            return None, None
    +
    +        return getattr(attr_name, 'namespace', None), getattr(attr_name, 'name', None)
    +
    +    @classmethod
    +    def normalize_value(cls, value: Any) -> str | Sequence[str]:
    +        """Normalize the value to be a string or list of strings."""
    +
    +        # Treat `None` as empty string.
    +        if value is None:
    +            return ''
    +
    +        # Pass through strings
    +        if (isinstance(value, str)):
    +            return value
    +
    +        # If it's a byte string, convert it to Unicode, treating it as UTF-8.
    +        if isinstance(value, bytes):
    +            return value.decode("utf8")
    +
    +        # BeautifulSoup supports sequences of attribute values, so make sure the children are strings.
    +        if isinstance(value, Sequence):
    +            new_value = []
    +            for v in value:
    +                if not isinstance(v, (str, bytes)) and isinstance(v, Sequence):
    +                    # This is most certainly a user error and will crash and burn later.
    +                    # To keep things working, we'll do what we do with all objects,
    +                    # And convert them to strings.
    +                    new_value.append(str(v))
    +                else:
    +                    # Convert the child to a string
    +                    new_value.append(cast(str, cls.normalize_value(v)))
    +            return new_value
    +
    +        # Try and make anything else a string
    +        return str(value)
    +
    +    @classmethod
    +    def get_attribute_by_name(
    +        cls,
    +        el: bs4.Tag,
    +        name: str,
    +        default: str | Sequence[str] | None = None
    +    ) -> str | Sequence[str] | None:
    +        """Get attribute by name."""
    +
    +        value = default
    +        if el._is_xml:
    +            try:
    +                value = cls.normalize_value(el.attrs[name])
    +            except KeyError:
    +                pass
    +        else:
    +            for k, v in el.attrs.items():
    +                if util.lower(k) == name:
    +                    value = cls.normalize_value(v)
    +                    break
    +        return value
    +
    +    @classmethod
    +    def iter_attributes(cls, el: bs4.Tag | None) -> Iterator[tuple[str, str | Sequence[str] | None]]:
    +        """Iterate attributes."""
    +
    +        if el is not None:
    +            for k, v in el.attrs.items():
    +                yield k, cls.normalize_value(v)
    +
    +    @classmethod
    +    def get_classes(cls, el: bs4.Tag) -> Sequence[str]:
    +        """Get classes."""
    +
    +        classes = cls.get_attribute_by_name(el, 'class', [])
    +        if isinstance(classes, str):
    +            classes = RE_NOT_WS.findall(classes)
    +        return cast(Sequence[str], classes)
    +
    +    def get_text(self, el: bs4.Tag, no_iframe: bool = False) -> str:
    +        """Get text."""
    +
    +        return ''.join(
    +            [
    +                node for node in self.get_descendants(el, no_iframe=no_iframe)  # type: ignore[misc]
    +                if self.is_content_string(node)
    +            ]
    +        )
    +
    +    def get_own_text(self, el: bs4.Tag, no_iframe: bool = False) -> list[str]:
    +        """Get Own Text."""
    +
    +        return [
    +            node for node in self.get_contents(el, no_iframe=no_iframe) if self.is_content_string(node)  # type: ignore[misc]
    +        ]
    +
    +
    +class Inputs:
    +    """Class for parsing and validating input items."""
    +
    +    @staticmethod
    +    def validate_day(year: int, month: int, day: int) -> bool:
    +        """Validate day."""
    +
    +        max_days = LONG_MONTH
    +        if month == FEB:
    +            max_days = FEB_LEAP_MONTH if ((year % 4 == 0) and (year % 100 != 0)) or (year % 400 == 0) else FEB_MONTH
    +        elif month in MONTHS_30:
    +            max_days = SHORT_MONTH
    +        return 1 <= day <= max_days
    +
    +    @staticmethod
    +    def validate_week(year: int, week: int) -> bool:
    +        """Validate week."""
    +
    +        # Validate an ISO week number for `year`.
    +        #
    +        # Per ISO 8601 rules, the last ISO week of a year is the week
    +        # containing Dec 28. Using Dec 28 guarantees we obtain the
    +        # correct ISO week-number for the final week of `year`, even in
    +        # years where Dec 31 falls in ISO week 01 of the following year.
    +        #
    +        # Example: if Dec 31 is a Thursday the year's last ISO week will
    +        # be week 53; if Dec 31 is a Monday and that week is counted as
    +        # week 1 of the next year, Dec 28 still belongs to the final
    +        # week of the current ISO year and yields the correct max week.
    +        max_week = datetime(year, 12, 28).isocalendar()[1]
    +        return 1 <= week <= max_week
    +
    +    @staticmethod
    +    def validate_month(month: int) -> bool:
    +        """Validate month."""
    +
    +        return 1 <= month <= 12
    +
    +    @staticmethod
    +    def validate_year(year: int) -> bool:
    +        """Validate year."""
    +
    +        return 1 <= year
    +
    +    @staticmethod
    +    def validate_hour(hour: int) -> bool:
    +        """Validate hour."""
    +
    +        return 0 <= hour <= 23
    +
    +    @staticmethod
    +    def validate_minutes(minutes: int) -> bool:
    +        """Validate minutes."""
    +
    +        return 0 <= minutes <= 59
    +
    +    @classmethod
    +    def parse_value(cls, itype: str, value: str | None) -> tuple[float, ...] | None:
    +        """Parse the input value."""
    +
    +        parsed = None  # type: tuple[float, ...] | None
    +        if value is None:
    +            return value
    +        if itype == "date":
    +            m = RE_DATE.match(value)
    +            if m:
    +                year = int(m.group('year'), 10)
    +                month = int(m.group('month'), 10)
    +                day = int(m.group('day'), 10)
    +                if cls.validate_year(year) and cls.validate_month(month) and cls.validate_day(year, month, day):
    +                    parsed = (year, month, day)
    +        elif itype == "month":
    +            m = RE_MONTH.match(value)
    +            if m:
    +                year = int(m.group('year'), 10)
    +                month = int(m.group('month'), 10)
    +                if cls.validate_year(year) and cls.validate_month(month):
    +                    parsed = (year, month)
    +        elif itype == "week":
    +            m = RE_WEEK.match(value)
    +            if m:
    +                year = int(m.group('year'), 10)
    +                week = int(m.group('week'), 10)
    +                if cls.validate_year(year) and cls.validate_week(year, week):
    +                    parsed = (year, week)
    +        elif itype == "time":
    +            m = RE_TIME.match(value)
    +            if m:
    +                hour = int(m.group('hour'), 10)
    +                minutes = int(m.group('minutes'), 10)
    +                if cls.validate_hour(hour) and cls.validate_minutes(minutes):
    +                    parsed = (hour, minutes)
    +        elif itype == "datetime-local":
    +            m = RE_DATETIME.match(value)
    +            if m:
    +                year = int(m.group('year'), 10)
    +                month = int(m.group('month'), 10)
    +                day = int(m.group('day'), 10)
    +                hour = int(m.group('hour'), 10)
    +                minutes = int(m.group('minutes'), 10)
    +                if (
    +                    cls.validate_year(year) and cls.validate_month(month) and cls.validate_day(year, month, day) and
    +                    cls.validate_hour(hour) and cls.validate_minutes(minutes)
    +                ):
    +                    parsed = (year, month, day, hour, minutes)
    +        elif itype in ("number", "range"):
    +            m = RE_NUM.match(value)
    +            if m:
    +                parsed = (float(m.group('value')),)
    +        return parsed
    +
    +
    +class CSSMatch(_DocumentNav):
    +    """Perform CSS matching."""
    +
    +    def __init__(
    +        self,
    +        selectors: ct.SelectorList,
    +        scope: bs4.Tag | None,
    +        namespaces: ct.Namespaces | None,
    +        flags: int
    +    ) -> None:
    +        """Initialize."""
    +
    +        self.assert_valid_input(scope)
    +        self.tag = scope
    +        self.cached_meta_lang = []  # type: list[tuple[str, str]]
    +        self.cached_default_forms = []  # type: list[tuple[bs4.Tag, bs4.Tag]]
    +        self.cached_indeterminate_forms = []  # type: list[tuple[bs4.Tag, str, bool]]
    +        self.selectors = selectors
    +        self.namespaces = {} if namespaces is None else namespaces  # type: ct.Namespaces | dict[str, str]
    +        self.flags = flags
    +        self.iframe_restrict = False
    +
    +        # Find the root element for the whole tree
    +        doc = scope
    +        parent = self.get_parent(doc)
    +        while parent:
    +            doc = parent
    +            parent = self.get_parent(doc)
    +        root = None  # type: bs4.Tag | None
    +        if not self.is_doc(doc):
    +            root = doc
    +        else:
    +            for child in self.get_tag_children(doc):
    +                root = child
    +                break
    +
    +        self.root = root
    +        self.scope = scope if scope is not doc else root
    +        self.has_html_namespace = self.has_html_ns(root)
    +
    +        # A document can be both XML and HTML (XHTML)
    +        self.is_xml = self.is_xml_tree(doc)
    +        self.is_html = not self.is_xml or self.has_html_namespace
    +
    +    def supports_namespaces(self) -> bool:
    +        """Check if namespaces are supported in the HTML type."""
    +
    +        return self.is_xml or self.has_html_namespace
    +
    +    def get_tag_ns(self, el: bs4.Tag | None) -> str:
    +        """Get tag namespace."""
    +
    +        namespace = ''
    +        if el is None:  # pragma: no cover
    +            return namespace
    +
    +        if self.supports_namespaces():
    +            ns = self.get_uri(el)
    +            if ns:
    +                namespace = ns
    +        else:
    +            namespace = NS_XHTML
    +        return namespace
    +
    +    def is_html_tag(self, el: bs4.Tag | None) -> bool:
    +        """Check if tag is in HTML namespace."""
    +
    +        return self.get_tag_ns(el) == NS_XHTML
    +
    +    def get_tag(self, el: bs4.Tag | None) -> str | None:
    +        """Get tag."""
    +
    +        name = self.get_tag_name(el)
    +        return util.lower(name) if name is not None and not self.is_xml else name
    +
    +    def get_prefix(self, el: bs4.Tag) -> str | None:
    +        """Get prefix."""
    +
    +        prefix = self.get_prefix_name(el)
    +        return util.lower(prefix) if prefix is not None and not self.is_xml else prefix
    +
    +    def find_bidi(self, el: bs4.Tag) -> int | None:
    +        """Get directionality from element text."""
    +
    +        for node in self.get_children(el):
    +
    +            # Analyze child text nodes
    +            if self.is_tag(node):
    +
    +                # Avoid analyzing certain elements specified in the specification.
    +                direction = DIR_MAP.get(util.lower(self.get_attribute_by_name(node, 'dir', '')), None)  # type: ignore[arg-type]
    +                name = self.get_tag(node)  # type: ignore[arg-type]
    +                if (
    +                    (name and name in ('bdi', 'script', 'style', 'textarea', 'iframe')) or
    +                    not self.is_html_tag(node) or  # type: ignore[arg-type]
    +                    direction is not None
    +                ):
    +                    continue  # pragma: no cover
    +
    +                # Check directionality of this node's text
    +                value = self.find_bidi(node)  # type: ignore[arg-type]
    +                if value is not None:
    +                    return value
    +
    +                # Direction could not be determined
    +                continue  # pragma: no cover
    +
    +            # Skip `doctype` comments, etc.
    +            if self.is_special_string(node):
    +                continue
    +
    +            # Analyze text nodes for directionality.
    +            for c in node:  # type: ignore[attr-defined]
    +                bidi = unicodedata.bidirectional(c)
    +                if bidi in ('AL', 'R', 'L'):
    +                    return ct.SEL_DIR_LTR if bidi == 'L' else ct.SEL_DIR_RTL
    +        return None
    +
    +    def extended_language_filter(self, lang_range: str, lang_tag: str) -> bool:
    +        """Filter the language tags."""
    +
    +        match = True
    +        lang_range = RE_WILD_STRIP.sub('-', lang_range).lower()
    +        ranges = lang_range.split('-')
    +        subtags = lang_tag.lower().split('-')
    +        length = len(ranges)
    +        slength = len(subtags)
    +        rindex = 0
    +        sindex = 0
    +        r = ranges[rindex]
    +        s = subtags[sindex]
    +
    +        # Empty specified language should match unspecified language attributes
    +        if length == 1 and slength == 1 and not r and r == s:
    +            return True
    +
    +        # Primary tag needs to match
    +        if (r != '*' and r != s) or (r == '*' and slength == 1 and not s):
    +            match = False
    +
    +        rindex += 1
    +        sindex += 1
    +
    +        # Match until we run out of ranges
    +        while match and rindex < length:
    +            r = ranges[rindex]
    +            try:
    +                s = subtags[sindex]
    +            except IndexError:
    +                # Ran out of subtags,
    +                # but we still have ranges
    +                match = False
    +                continue
    +
    +            # Empty range
    +            if not r:
    +                match = False
    +                continue
    +
    +            # Matched range
    +            elif s == r:
    +                rindex += 1
    +
    +            # Implicit wildcard cannot match
    +            # singletons
    +            elif len(s) == 1:
    +                match = False
    +                continue
    +
    +            # Implicitly matched, so grab next subtag
    +            sindex += 1
    +
    +        return match
    +
    +    def match_attribute_name(
    +        self,
    +        el: bs4.Tag,
    +        attr: str,
    +        prefix: str | None
    +    ) -> str | Sequence[str] | None:
    +        """Match attribute name and return value if it exists."""
    +
    +        value = None
    +        if self.supports_namespaces():
    +            value = None
    +            # If we have not defined namespaces, we can't very well find them, so don't bother trying.
    +            if prefix:
    +                ns = self.namespaces.get(prefix)
    +                if ns is None and prefix != '*':
    +                    return None
    +            else:
    +                ns = None
    +
    +            for k, v in self.iter_attributes(el):
    +
    +                # Get attribute parts
    +                namespace, name = self.split_namespace(el, k)
    +
    +                # Can't match a prefix attribute as we haven't specified one to match
    +                # Try to match it normally as a whole `p:a` as selector may be trying `p\:a`.
    +                if ns is None:
    +                    if (self.is_xml and attr == k) or (not self.is_xml and util.lower(attr) == util.lower(k)):
    +                        value = v
    +                        break
    +                    # Coverage is not finding this even though it is executed.
    +                    # Adding a print statement before this (and erasing coverage) causes coverage to find the line.
    +                    # Ignore the false positive message.
    +                    continue  # pragma: no cover
    +
    +                # We can't match our desired prefix attribute as the attribute doesn't have a prefix
    +                if namespace is None or (ns != namespace and prefix != '*'):
    +                    continue
    +
    +                # The attribute doesn't match.
    +                if (util.lower(attr) != util.lower(name)) if not self.is_xml else (attr != name):
    +                    continue
    +
    +                value = v
    +                break
    +        else:
    +            for k, v in self.iter_attributes(el):
    +                if util.lower(attr) != util.lower(k):
    +                    continue
    +                value = v
    +                break
    +        return value
    +
    +    def match_namespace(self, el: bs4.Tag, tag: ct.SelectorTag) -> bool:
    +        """Match the namespace of the element."""
    +
    +        match = True
    +        namespace = self.get_tag_ns(el)
    +        default_namespace = self.namespaces.get('')
    +        tag_ns = '' if tag.prefix is None else self.namespaces.get(tag.prefix)
    +        # We must match the default namespace if one is not provided
    +        if tag.prefix is None and (default_namespace is not None and namespace != default_namespace):
    +            match = False
    +        # If we specified `|tag`, we must not have a namespace.
    +        elif (tag.prefix is not None and tag.prefix == '' and namespace):
    +            match = False
    +        # Verify prefix matches
    +        elif (
    +            tag.prefix and
    +            tag.prefix != '*' and (tag_ns is None or namespace != tag_ns)
    +        ):
    +            match = False
    +        return match
    +
    +    def match_attributes(self, el: bs4.Tag, attributes: tuple[ct.SelectorAttribute, ...]) -> bool:
    +        """Match attributes."""
    +
    +        match = True
    +        if attributes:
    +            for a in attributes:
    +                temp = self.match_attribute_name(el, a.attribute, a.prefix)
    +                pattern = a.xml_type_pattern if self.is_xml and a.xml_type_pattern else a.pattern
    +                if temp is None:
    +                    match = False
    +                    break
    +                value = temp if isinstance(temp, str) else ' '.join(temp)
    +                if pattern is None:
    +                    continue
    +                elif pattern.match(value) is None:
    +                    match = False
    +                    break
    +        return match
    +
    +    def match_tagname(self, el: bs4.Tag, tag: ct.SelectorTag) -> bool:
    +        """Match tag name."""
    +
    +        name = (util.lower(tag.name) if not self.is_xml and tag.name is not None else tag.name)
    +        return not (
    +            name is not None and
    +            name not in (self.get_tag(el), '*')
    +        )
    +
    +    def match_tag(self, el: bs4.Tag, tag: ct.SelectorTag | None) -> bool:
    +        """Match the tag."""
    +
    +        match = True
    +        if tag is not None:
    +            # Verify namespace
    +            if not self.match_namespace(el, tag):
    +                match = False
    +            if not self.match_tagname(el, tag):
    +                match = False
    +        return match
    +
    +    def match_past_relations(self, el: bs4.Tag, relation: ct.SelectorList) -> bool:
    +        """Match past relationship."""
    +
    +        found = False
    +        # I don't think this can ever happen, but it makes `mypy` happy
    +        if isinstance(relation[0], ct.SelectorNull):  # pragma: no cover
    +            return found
    +
    +        if relation[0].rel_type == REL_PARENT:
    +            parent = self.get_parent(el, no_iframe=self.iframe_restrict)
    +            while not found and parent:
    +                found = self.match_selectors(parent, relation)
    +                parent = self.get_parent(parent, no_iframe=self.iframe_restrict)
    +        elif relation[0].rel_type == REL_CLOSE_PARENT:
    +            parent = self.get_parent(el, no_iframe=self.iframe_restrict)
    +            if parent:
    +                found = self.match_selectors(parent, relation)
    +        elif relation[0].rel_type == REL_SIBLING:
    +            sibling = self.get_previous_tag(el)
    +            while not found and sibling:
    +                found = self.match_selectors(sibling, relation)
    +                sibling = self.get_previous_tag(sibling)
    +        elif relation[0].rel_type == REL_CLOSE_SIBLING:
    +            sibling = self.get_previous_tag(el)
    +            if sibling and self.is_tag(sibling):
    +                found = self.match_selectors(sibling, relation)
    +        return found
    +
    +    def match_future_child(self, parent: bs4.Tag, relation: ct.SelectorList, recursive: bool = False) -> bool:
    +        """Match future child."""
    +
    +        match = False
    +        if recursive:
    +            children = self.get_tag_descendants  # type: Callable[..., Iterator[bs4.Tag]]
    +        else:
    +            children = self.get_tag_children
    +        for child in children(parent, no_iframe=self.iframe_restrict):
    +            match = self.match_selectors(child, relation)
    +            if match:
    +                break
    +        return match
    +
    +    def match_future_relations(self, el: bs4.Tag, relation: ct.SelectorList) -> bool:
    +        """Match future relationship."""
    +
    +        found = False
    +        # I don't think this can ever happen, but it makes `mypy` happy
    +        if isinstance(relation[0], ct.SelectorNull):  # pragma: no cover
    +            return found
    +
    +        if relation[0].rel_type == REL_HAS_PARENT:
    +            found = self.match_future_child(el, relation, True)
    +        elif relation[0].rel_type == REL_HAS_CLOSE_PARENT:
    +            found = self.match_future_child(el, relation)
    +        elif relation[0].rel_type == REL_HAS_SIBLING:
    +            sibling = self.get_next_tag(el)
    +            while not found and sibling:
    +                found = self.match_selectors(sibling, relation)
    +                sibling = self.get_next_tag(sibling)
    +        elif relation[0].rel_type == REL_HAS_CLOSE_SIBLING:
    +            sibling = self.get_next_tag(el)
    +            if sibling and self.is_tag(sibling):
    +                found = self.match_selectors(sibling, relation)
    +        return found
    +
    +    def match_relations(self, el: bs4.Tag, relation: ct.SelectorList) -> bool:
    +        """Match relationship to other elements."""
    +
    +        found = False
    +
    +        if isinstance(relation[0], ct.SelectorNull) or relation[0].rel_type is None:
    +            return found
    +
    +        if relation[0].rel_type.startswith(':'):
    +            found = self.match_future_relations(el, relation)
    +        else:
    +            found = self.match_past_relations(el, relation)
    +
    +        return found
    +
    +    def match_id(self, el: bs4.Tag, ids: tuple[str, ...]) -> bool:
    +        """Match element's ID."""
    +
    +        found = True
    +        for i in ids:
    +            if i != self.get_attribute_by_name(el, 'id', ''):
    +                found = False
    +                break
    +        return found
    +
    +    def match_classes(self, el: bs4.Tag, classes: tuple[str, ...]) -> bool:
    +        """Match element's classes."""
    +
    +        current_classes = self.get_classes(el)
    +        found = True
    +        for c in classes:
    +            if c not in current_classes:
    +                found = False
    +                break
    +        return found
    +
    +    def match_root(self, el: bs4.Tag) -> bool:
    +        """Match element as root."""
    +
    +        is_root = self.is_root(el)
    +        if is_root:
    +            sibling = self.get_previous(el)  # type: Any
    +            while is_root and sibling is not None:
    +                if (
    +                    self.is_tag(sibling) or (self.is_content_string(sibling) and sibling.strip()) or
    +                    self.is_cdata(sibling)
    +                ):
    +                    is_root = False
    +                else:
    +                    sibling = self.get_previous(sibling)
    +        if is_root:
    +            sibling = self.get_next(el)
    +            while is_root and sibling is not None:
    +                if (
    +                    self.is_tag(sibling) or (self.is_content_string(sibling) and sibling.strip()) or
    +                    self.is_cdata(sibling)
    +                ):
    +                    is_root = False
    +                else:
    +                    sibling = self.get_next(sibling)
    +        return is_root
    +
    +    def match_scope(self, el: bs4.Tag) -> bool:
    +        """Match element as scope."""
    +
    +        return self.scope is el
    +
    +    def match_nth_tag_type(self, el: bs4.Tag, child: bs4.Tag) -> bool:
    +        """Match tag type for `nth` matches."""
    +
    +        return (
    +            (self.get_tag(child) == self.get_tag(el)) and
    +            (self.get_tag_ns(child) == self.get_tag_ns(el))
    +        )
    +
    +    def match_nth(self, el: bs4.Tag, nth: tuple[ct.SelectorNth, ...]) -> bool:
    +        """Match `nth` elements."""
    +
    +        matched = True
    +
    +        for n in nth:
    +            matched = False
    +            if n.selectors and not self.match_selectors(el, n.selectors):
    +                break
    +            parent = self.get_parent(el)  # type: bs4.Tag | None
    +            if parent is None:
    +                parent = cast('bs4.Tag', self.create_fake_parent(el))
    +            last = n.last
    +            last_index = len(parent) - 1
    +            index = last_index if last else 0
    +            relative_index = 0
    +            a = n.a
    +            b = n.b
    +            var = n.n
    +            count = 0
    +            count_incr = 1
    +            factor = -1 if last else 1
    +            idx = last_idx = a * count + b if var else a
    +
    +            # We can only adjust bounds within a variable index
    +            if var:
    +                # Abort if our nth index is out of bounds and only getting further out of bounds as we increment.
    +                # Otherwise, increment to try to get in bounds.
    +                adjust = None
    +                while idx < 1 or idx > last_index:
    +                    if idx < 0:
    +                        diff_low = 0 - idx
    +                        if adjust is not None and adjust == 1:
    +                            break
    +                        adjust = -1
    +                        count += count_incr
    +                        idx = last_idx = a * count + b if var else a
    +                        diff = 0 - idx
    +                        if diff >= diff_low:
    +                            break
    +                    else:
    +                        diff_high = idx - last_index
    +                        if adjust is not None and adjust == -1:
    +                            break
    +                        adjust = 1
    +                        count += count_incr
    +                        idx = last_idx = a * count + b if var else a
    +                        diff = idx - last_index
    +                        if diff >= diff_high:
    +                            break
    +                        diff_high = diff
    +
    +                # If a < 0, our count is working backwards, so floor the index by increasing the count.
    +                # Find the count that yields the lowest, in bound value and use that.
    +                # Lastly reverse count increment so that we'll increase our index.
    +                lowest = count
    +                if a < 0:
    +                    while idx >= 1:
    +                        lowest = count
    +                        count += count_incr
    +                        idx = last_idx = a * count + b if var else a
    +                    count_incr = -1
    +                count = lowest
    +                idx = last_idx = a * count + b if var else a
    +
    +            # Evaluate elements while our calculated nth index is still in range
    +            while 1 <= idx <= last_index + 1:
    +                child = None  # type: bs4.element.PageElement | None
    +                # Evaluate while our child index is still in range.
    +                for child in self.get_children(parent, start=index, reverse=factor < 0):
    +                    index += factor
    +                    if not isinstance(child, bs4.Tag):
    +                        continue
    +                    # Handle `of S` in `nth-child`
    +                    if n.selectors and not self.match_selectors(child, n.selectors):
    +                        continue
    +                    # Handle `of-type`
    +                    if n.of_type and not self.match_nth_tag_type(el, child):
    +                        continue
    +                    relative_index += 1
    +                    if relative_index == idx:
    +                        if child is el:
    +                            matched = True
    +                        else:
    +                            break
    +                    if child is el:
    +                        break
    +                if child is el:
    +                    break
    +                last_idx = idx
    +                count += count_incr
    +                if count < 0:
    +                    # Count is counting down and has now ventured into invalid territory.
    +                    break
    +                idx = a * count + b if var else a
    +                if last_idx == idx:
    +                    break
    +            if not matched:
    +                break
    +        return matched
    +
    +    def match_empty(self, el: bs4.Tag) -> bool:
    +        """Check if element is empty (if requested)."""
    +
    +        is_empty = True
    +        for child in self.get_children(el):
    +            if self.is_tag(child):
    +                is_empty = False
    +                break
    +            elif self.is_content_string(child) and RE_NOT_EMPTY.search(child):  # type: ignore[call-overload]
    +                is_empty = False
    +                break
    +        return is_empty
    +
    +    def match_subselectors(self, el: bs4.Tag, selectors: tuple[ct.SelectorList, ...]) -> bool:
    +        """Match selectors."""
    +
    +        match = True
    +        for sel in selectors:
    +            if not self.match_selectors(el, sel):
    +                match = False
    +        return match
    +
    +    def match_contains(self, el: bs4.Tag, contains: tuple[ct.SelectorContains, ...]) -> bool:
    +        """Match element if it contains text."""
    +
    +        match = True
    +        content = None  # type: str | Sequence[str] | None
    +        for contain_list in contains:
    +            if content is None:
    +                if contain_list.own:
    +                    content = self.get_own_text(el, no_iframe=self.is_html)
    +                else:
    +                    content = self.get_text(el, no_iframe=self.is_html)
    +            found = False
    +            for text in contain_list.text:
    +                if contain_list.own:
    +                    for c in content:
    +                        if text in c:
    +                            found = True
    +                            break
    +                    if found:
    +                        break
    +                else:
    +                    if text in content:
    +                        found = True
    +                        break
    +            if not found:
    +                match = False
    +        return match
    +
    +    def match_default(self, el: bs4.Tag) -> bool:
    +        """Match default."""
    +
    +        match = False
    +
    +        # Find this input's form
    +        form = None  # type: bs4.Tag | None
    +        parent = self.get_parent(el, no_iframe=True)
    +        while parent and form is None:
    +            if self.get_tag(parent) == 'form' and self.is_html_tag(parent):
    +                form = parent
    +            else:
    +                parent = self.get_parent(parent, no_iframe=True)
    +
    +        if form is not None:
    +            # Look in form cache to see if we've already located its default button
    +            found_form = False
    +            for f, t in self.cached_default_forms:
    +                if f is form:
    +                    found_form = True
    +                    if t is el:
    +                        match = True
    +                    break
    +
    +            # We didn't have the form cached, so look for its default button
    +            if not found_form:
    +                for child in self.get_tag_descendants(form, no_iframe=True):
    +                    name = self.get_tag(child)
    +                    # Can't do nested forms (haven't figured out why we never hit this)
    +                    if name == 'form':  # pragma: no cover
    +                        break
    +                    if name in ('input', 'button'):
    +                        v = self.get_attribute_by_name(child, 'type', '')
    +                        if v and util.lower(v) == 'submit':
    +                            self.cached_default_forms.append((form, child))
    +                            if el is child:
    +                                match = True
    +                            break
    +        return match
    +
    +    def match_indeterminate(self, el: bs4.Tag) -> bool:
    +        """Match default."""
    +
    +        match = False
    +        name = cast(str, self.get_attribute_by_name(el, 'name'))
    +
    +        def get_parent_form(el: bs4.Tag) -> bs4.Tag | None:
    +            """Find this input's form."""
    +            form = None
    +            parent = self.get_parent(el, no_iframe=True)
    +            while form is None:
    +                if self.get_tag(parent) == 'form' and self.is_html_tag(parent):
    +                    form = parent
    +                    break
    +                last_parent = parent
    +                parent = self.get_parent(parent, no_iframe=True)
    +                if parent is None:
    +                    form = last_parent
    +                    break
    +            return form
    +
    +        form = get_parent_form(el)
    +
    +        # Look in form cache to see if we've already evaluated that its fellow radio buttons are indeterminate
    +        if form is not None:
    +            found_form = False
    +            for f, n, i in self.cached_indeterminate_forms:
    +                if f is form and n == name:
    +                    found_form = True
    +                    if i is True:
    +                        match = True
    +                    break
    +
    +            # We didn't have the form cached, so validate that the radio button is indeterminate
    +            if not found_form:
    +                checked = False
    +                for child in self.get_tag_descendants(form, no_iframe=True):
    +                    if child is el:
    +                        continue
    +                    tag_name = self.get_tag(child)
    +                    if tag_name == 'input':
    +                        is_radio = False
    +                        check = False
    +                        has_name = False
    +                        for k, v in self.iter_attributes(child):
    +                            if util.lower(k) == 'type' and util.lower(v) == 'radio':
    +                                is_radio = True
    +                            elif util.lower(k) == 'name' and v == name:
    +                                has_name = True
    +                            elif util.lower(k) == 'checked':
    +                                check = True
    +                            if is_radio and check and has_name and get_parent_form(child) is form:
    +                                checked = True
    +                                break
    +                    if checked:
    +                        break
    +                if not checked:
    +                    match = True
    +                self.cached_indeterminate_forms.append((form, name, match))
    +
    +        return match
    +
    +    def match_lang(self, el: bs4.Tag, langs: tuple[ct.SelectorLang, ...]) -> bool:
    +        """Match languages."""
    +
    +        match = False
    +        has_ns = self.supports_namespaces()
    +        root = self.root
    +        has_html_namespace = self.has_html_namespace
    +
    +        # Walk parents looking for `lang` (HTML) or `xml:lang` XML property.
    +        parent = el  # type: bs4.Tag | None
    +        found_lang = None
    +        last = None
    +        while not found_lang:
    +            has_html_ns = self.has_html_ns(parent)
    +            for k, v in self.iter_attributes(parent):
    +                attr_ns, attr = self.split_namespace(parent, k)
    +                if (
    +                    ((not has_ns or has_html_ns) and (util.lower(k) if not self.is_xml else k) == 'lang') or
    +                    (
    +                        has_ns and not has_html_ns and attr_ns == NS_XML and
    +                        (util.lower(attr) if not self.is_xml and attr is not None else attr) == 'lang'
    +                    )
    +                ):
    +                    found_lang = v
    +                    break
    +            last = parent
    +            parent = self.get_parent(parent, no_iframe=self.is_html)
    +
    +            if parent is None:
    +                root = last
    +                has_html_namespace = self.has_html_ns(root)
    +                parent = last
    +                break
    +
    +        # Use cached meta language.
    +        if found_lang is None and self.cached_meta_lang:
    +            for cache in self.cached_meta_lang:
    +                if root is not None and cast(str, root) is cache[0]:
    +                    found_lang = cache[1]
    +
    +        # If we couldn't find a language, and the document is HTML, look to meta to determine language.
    +        if found_lang is None and (not self.is_xml or (has_html_namespace and root and root.name == 'html')):
    +            # Find head
    +            found = False
    +            for tag in ('html', 'head'):
    +                found = False
    +                for child in self.get_tag_children(parent, no_iframe=self.is_html):
    +                    if self.get_tag(child) == tag and self.is_html_tag(child):
    +                        found = True
    +                        parent = child
    +                        break
    +                if not found:  # pragma: no cover
    +                    break
    +
    +            # Search meta tags
    +            if found and parent is not None:
    +                for child2 in parent:
    +                    if isinstance(child2, bs4.Tag) and self.get_tag(child2) == 'meta' and self.is_html_tag(parent):
    +                        c_lang = False
    +                        content = None
    +                        for k, v in self.iter_attributes(child2):
    +                            if util.lower(k) == 'http-equiv' and util.lower(v) == 'content-language':
    +                                c_lang = True
    +                            if util.lower(k) == 'content':
    +                                content = v
    +                            if c_lang and content:
    +                                found_lang = content
    +                                self.cached_meta_lang.append((cast(str, root), cast(str, found_lang)))
    +                                break
    +                    if found_lang is not None:
    +                        break
    +                if found_lang is None:
    +                    self.cached_meta_lang.append((cast(str, root), ''))
    +
    +        # If we determined a language, compare.
    +        if found_lang is not None:
    +            for patterns in langs:
    +                match = False
    +                for pattern in patterns:
    +                    if self.extended_language_filter(pattern, cast(str, found_lang)):
    +                        match = True
    +                if not match:
    +                    break
    +
    +        return match
    +
    +    def match_dir(self, el: bs4.Tag | None, directionality: int) -> bool:
    +        """Check directionality."""
    +
    +        # If we have to match both left and right, we can't match either.
    +        if directionality & ct.SEL_DIR_LTR and directionality & ct.SEL_DIR_RTL:
    +            return False
    +
    +        if el is None or not self.is_html_tag(el):
    +            return False
    +
    +        # Element has defined direction of left to right or right to left
    +        direction = DIR_MAP.get(util.lower(self.get_attribute_by_name(el, 'dir', '')), None)
    +        if direction not in (None, 0):
    +            return direction == directionality
    +
    +        # Element is the document element (the root) and no direction assigned, assume left to right.
    +        is_root = self.is_root(el)
    +        if is_root and direction is None:
    +            return ct.SEL_DIR_LTR == directionality
    +
    +        # If `input[type=telephone]` and no direction is assigned, assume left to right.
    +        name = self.get_tag(el)
    +        is_input = name == 'input'
    +        is_textarea = name == 'textarea'
    +        is_bdi = name == 'bdi'
    +        itype = util.lower(self.get_attribute_by_name(el, 'type', '')) if is_input else ''
    +        if is_input and itype == 'tel' and direction is None:
    +            return ct.SEL_DIR_LTR == directionality
    +
    +        # Auto handling for text inputs
    +        if ((is_input and itype in ('text', 'search', 'tel', 'url', 'email')) or is_textarea) and direction == 0:
    +            if is_textarea:
    +                value = ''.join(node for node in self.get_contents(el, no_iframe=True) if self.is_content_string(node))  # type: ignore[misc]
    +            else:
    +                value = cast(str, self.get_attribute_by_name(el, 'value', ''))
    +            if value:
    +                for c in value:
    +                    bidi = unicodedata.bidirectional(c)
    +                    if bidi in ('AL', 'R', 'L'):
    +                        direction = ct.SEL_DIR_LTR if bidi == 'L' else ct.SEL_DIR_RTL
    +                        return direction == directionality
    +                # Assume left to right
    +                return ct.SEL_DIR_LTR == directionality
    +            elif is_root:
    +                return ct.SEL_DIR_LTR == directionality
    +            return self.match_dir(self.get_parent(el, no_iframe=True), directionality)
    +
    +        # Auto handling for `bdi` and other non text inputs.
    +        if (is_bdi and direction is None) or direction == 0:
    +            direction = self.find_bidi(el)
    +            if direction is not None:
    +                return direction == directionality
    +            elif is_root:
    +                return ct.SEL_DIR_LTR == directionality
    +            return self.match_dir(self.get_parent(el, no_iframe=True), directionality)
    +
    +        # Match parents direction
    +        return self.match_dir(self.get_parent(el, no_iframe=True), directionality)
    +
    +    def match_range(self, el: bs4.Tag, condition: int) -> bool:
    +        """
    +        Match range.
    +
    +        Behavior is modeled after what we see in browsers. Browsers seem to evaluate
    +        if the value is out of range, and if not, it is in range. So a missing value
    +        will not evaluate out of range; therefore, value is in range. Personally, I
    +        feel like this should evaluate as neither in or out of range.
    +        """
    +
    +        out_of_range = False
    +
    +        itype = util.lower(self.get_attribute_by_name(el, 'type'))
    +        mn = Inputs.parse_value(itype, cast(str, self.get_attribute_by_name(el, 'min', None)))
    +        mx = Inputs.parse_value(itype, cast(str, self.get_attribute_by_name(el, 'max', None)))
    +
    +        # There is no valid min or max, so we cannot evaluate a range
    +        if mn is None and mx is None:
    +            return False
    +
    +        value = Inputs.parse_value(itype, cast(str, self.get_attribute_by_name(el, 'value', None)))
    +        if value is not None:
    +            if itype in ("date", "datetime-local", "month", "week", "number", "range"):
    +                if mn is not None and value < mn:
    +                    out_of_range = True
    +                if not out_of_range and mx is not None and value > mx:
    +                    out_of_range = True
    +            elif itype == "time":
    +                if mn is not None and mx is not None and mn > mx:
    +                    # Time is periodic, so this is a reversed/discontinuous range
    +                    if value < mn and value > mx:
    +                        out_of_range = True
    +                else:
    +                    if mn is not None and value < mn:
    +                        out_of_range = True
    +                    if not out_of_range and mx is not None and value > mx:
    +                        out_of_range = True
    +
    +        return not out_of_range if condition & ct.SEL_IN_RANGE else out_of_range
    +
    +    def match_defined(self, el: bs4.Tag) -> bool:
    +        """
    +        Match defined.
    +
    +        `:defined` is related to custom elements in a browser.
    +
    +        - If the document is XML (not XHTML), all tags will match.
    +        - Tags that are not custom (don't have a hyphen) are marked defined.
    +        - If the tag has a prefix (without or without a namespace), it will not match.
    +
    +        This is of course requires the parser to provide us with the proper prefix and namespace info,
    +        if it doesn't, there is nothing we can do.
    +        """
    +
    +        name = self.get_tag(el)
    +        return (
    +            name is not None and (
    +                name.find('-') == -1 or
    +                name.find(':') != -1 or
    +                self.get_prefix(el) is not None
    +            )
    +        )
    +
    +    def match_placeholder_shown(self, el: bs4.Tag) -> bool:
    +        """
    +        Match placeholder shown according to HTML spec.
    +
    +        - text area should be checked if they have content. A single newline does not count as content.
    +
    +        """
    +
    +        match = False
    +        content = self.get_text(el)
    +        if content in ('', '\n'):
    +            match = True
    +
    +        return match
    +
    +    def match_selectors(self, el: bs4.Tag, selectors: ct.SelectorList) -> bool:
    +        """Check if element matches one of the selectors."""
    +
    +        match = False
    +        is_not = selectors.is_not
    +        is_html = selectors.is_html
    +
    +        # Internal selector lists that use the HTML flag, will automatically get the `html` namespace.
    +        if is_html:
    +            namespaces = self.namespaces
    +            iframe_restrict = self.iframe_restrict
    +            self.namespaces = {'html': NS_XHTML}
    +            self.iframe_restrict = True
    +
    +        if not is_html or self.is_html:
    +            for selector in selectors:
    +                match = is_not
    +                # We have a un-matchable situation (like `:focus` as you can focus an element in this environment)
    +                if isinstance(selector, ct.SelectorNull):
    +                    continue
    +                # Verify tag matches
    +                if not self.match_tag(el, selector.tag):
    +                    continue
    +                # Verify tag is defined
    +                if selector.flags & ct.SEL_DEFINED and not self.match_defined(el):
    +                    continue
    +                # Verify element is root
    +                if selector.flags & ct.SEL_ROOT and not self.match_root(el):
    +                    continue
    +                # Verify element is scope
    +                if selector.flags & ct.SEL_SCOPE and not self.match_scope(el):
    +                    continue
    +                # Verify element has placeholder shown
    +                if selector.flags & ct.SEL_PLACEHOLDER_SHOWN and not self.match_placeholder_shown(el):
    +                    continue
    +                # Verify `nth` matches
    +                if not self.match_nth(el, selector.nth):
    +                    continue
    +                if selector.flags & ct.SEL_EMPTY and not self.match_empty(el):
    +                    continue
    +                # Verify id matches
    +                if selector.ids and not self.match_id(el, selector.ids):
    +                    continue
    +                # Verify classes match
    +                if selector.classes and not self.match_classes(el, selector.classes):
    +                    continue
    +                # Verify attribute(s) match
    +                if not self.match_attributes(el, selector.attributes):
    +                    continue
    +                # Verify ranges
    +                if selector.flags & RANGES and not self.match_range(el, selector.flags & RANGES):
    +                    continue
    +                # Verify language patterns
    +                if selector.lang and not self.match_lang(el, selector.lang):
    +                    continue
    +                # Verify pseudo selector patterns
    +                if selector.selectors and not self.match_subselectors(el, selector.selectors):
    +                    continue
    +                # Verify relationship selectors
    +                if selector.relation and not self.match_relations(el, selector.relation):
    +                    continue
    +                # Validate that the current default selector match corresponds to the first submit button in the form
    +                if selector.flags & ct.SEL_DEFAULT and not self.match_default(el):
    +                    continue
    +                # Validate that the unset radio button is among radio buttons with the same name in a form that are
    +                # also not set.
    +                if selector.flags & ct.SEL_INDETERMINATE and not self.match_indeterminate(el):
    +                    continue
    +                # Validate element directionality
    +                if selector.flags & DIR_FLAGS and not self.match_dir(el, selector.flags & DIR_FLAGS):
    +                    continue
    +                # Validate that the tag contains the specified text.
    +                if selector.contains and not self.match_contains(el, selector.contains):
    +                    continue
    +                match = not is_not
    +                break
    +
    +        # Restore actual namespaces being used for external selector lists
    +        if is_html:
    +            self.namespaces = namespaces
    +            self.iframe_restrict = iframe_restrict
    +
    +        return match
    +
    +    def select(self, limit: int = 0) -> Iterator[bs4.Tag]:
    +        """Match all tags under the targeted tag."""
    +
    +        lim = None if limit < 1 else limit
    +
    +        for child in self.get_tag_descendants(self.tag):
    +            if self.match(child):
    +                yield child
    +                if lim is not None:
    +                    lim -= 1
    +                    if lim < 1:
    +                        break
    +
    +    def closest(self) -> bs4.Tag | None:
    +        """Match closest ancestor."""
    +
    +        current = self.tag  # type: bs4.Tag | None
    +        closest = None
    +        while closest is None and current is not None:
    +            if self.match(current):
    +                closest = current
    +            else:
    +                current = self.get_parent(current)
    +        return closest
    +
    +    def filter(self) -> list[bs4.Tag]:  # noqa A001
    +        """Filter tag's children."""
    +
    +        return [
    +            tag for tag in self.get_contents(self.tag)
    +            if isinstance(tag, bs4.Tag) and self.match(tag)
    +        ]
    +
    +    def match(self, el: bs4.Tag) -> bool:
    +        """Match."""
    +
    +        return not self.is_doc(el) and self.is_tag(el) and self.match_selectors(el, self.selectors)
    +
    +
    +class SoupSieve(ct.Immutable):
    +    """Compiled Soup Sieve selector matching object."""
    +
    +    pattern: str
    +    selectors: ct.SelectorList
    +    namespaces: ct.Namespaces | None
    +    custom: dict[str, str]
    +    flags: int
    +
    +    __slots__ = ("pattern", "selectors", "namespaces", "custom", "flags", "_hash")
    +
    +    def __init__(
    +        self,
    +        pattern: str,
    +        selectors: ct.SelectorList,
    +        namespaces: ct.Namespaces | None,
    +        custom: ct.CustomSelectors | None,
    +        flags: int
    +    ):
    +        """Initialize."""
    +
    +        super().__init__(
    +            pattern=pattern,
    +            selectors=selectors,
    +            namespaces=namespaces,
    +            custom=custom,
    +            flags=flags
    +        )
    +
    +    def match(self, tag: bs4.Tag) -> bool:
    +        """Match."""
    +
    +        return CSSMatch(self.selectors, tag, self.namespaces, self.flags).match(tag)
    +
    +    def closest(self, tag: bs4.Tag) -> bs4.Tag | None:
    +        """Match closest ancestor."""
    +
    +        return CSSMatch(self.selectors, tag, self.namespaces, self.flags).closest()
    +
    +    def filter(self, iterable: Iterable[bs4.Tag]) -> list[bs4.Tag]:  # noqa A001
    +        """
    +        Filter.
    +
    +        `CSSMatch` can cache certain searches for tags of the same document,
    +        so if we are given a tag, all tags are from the same document,
    +        and we can take advantage of the optimization.
    +
    +        Any other kind of iterable could have tags from different documents or detached tags,
    +        so for those, we use a new `CSSMatch` for each item in the iterable.
    +        """
    +
    +        if isinstance(iterable, bs4.Tag):
    +            return CSSMatch(self.selectors, iterable, self.namespaces, self.flags).filter()
    +        else:
    +            return [node for node in iterable if not CSSMatch.is_navigable_string(node) and self.match(node)]
    +
    +    def select_one(self, tag: bs4.Tag) -> bs4.Tag | None:
    +        """Select a single tag."""
    +
    +        tags = self.select(tag, limit=1)
    +        return tags[0] if tags else None
    +
    +    def select(self, tag: bs4.Tag, limit: int = 0) -> list[bs4.Tag]:
    +        """Select the specified tags."""
    +
    +        return list(self.iselect(tag, limit))
    +
    +    def iselect(self, tag: bs4.Tag, limit: int = 0) -> Iterator[bs4.Tag]:
    +        """Iterate the specified tags."""
    +
    +        yield from CSSMatch(self.selectors, tag, self.namespaces, self.flags).select(limit)
    +
    +    def __repr__(self) -> str:  # pragma: no cover
    +        """Representation."""
    +
    +        return (
    +            f"SoupSieve(pattern={self.pattern!r}, namespaces={self.namespaces!r}, "
    +            f"custom={self.custom!r}, flags={self.flags!r})"
    +        )
    +
    +    __str__ = __repr__
    +
    +
    +ct.pickle_register(SoupSieve)
    diff --git a/venv/lib/python3.12/site-packages/soupsieve/css_parser.py b/venv/lib/python3.12/site-packages/soupsieve/css_parser.py
    new file mode 100644
    index 00000000..3140ba82
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/soupsieve/css_parser.py
    @@ -0,0 +1,1318 @@
    +"""CSS selector parser."""
    +from __future__ import annotations
    +import re
    +from functools import lru_cache
    +from . import util
    +from . import css_match as cm
    +from . import css_types as ct
    +from .util import SelectorSyntaxError
    +import warnings
    +from typing import Match, Any, Iterator, cast
    +
    +UNICODE_REPLACEMENT_CHAR = 0xFFFD
    +
    +# Simple pseudo classes that take no parameters
    +PSEUDO_SIMPLE = {
    +    ":any-link",
    +    ":empty",
    +    ":first-child",
    +    ":first-of-type",
    +    ":in-range",
    +    ":open",
    +    ":out-of-range",
    +    ":last-child",
    +    ":last-of-type",
    +    ":link",
    +    ":only-child",
    +    ":only-of-type",
    +    ":root",
    +    ':checked',
    +    ':default',
    +    ':disabled',
    +    ':enabled',
    +    ':indeterminate',
    +    ':optional',
    +    ':placeholder-shown',
    +    ':read-only',
    +    ':read-write',
    +    ':required',
    +    ':scope',
    +    ':defined',
    +    ':muted'
    +}
    +
    +# Supported, simple pseudo classes that match nothing in the Soup Sieve environment
    +PSEUDO_SIMPLE_NO_MATCH = {
    +    ':active',
    +    ':autofill',
    +    ':buffering',
    +    ':current',
    +    ':focus',
    +    ':focus-visible',
    +    ':focus-within',
    +    ':fullscreen',
    +    ':future',
    +    ':host',
    +    ':hover',
    +    ':local-link',
    +    ':past',
    +    ':paused',
    +    ':picture-in-picture',
    +    ':playing',
    +    ':popover-open',
    +    ':seeking',
    +    ':stalled',
    +    ':target',
    +    ':target-within',
    +    ':user-invalid',
    +    ':volume-locked',
    +    ':visited'
    +}
    +
    +# Complex pseudo classes that take selector lists
    +PSEUDO_COMPLEX = {
    +    ':contains',
    +    ':-soup-contains',
    +    ':-soup-contains-own',
    +    ':has',
    +    ':is',
    +    ':matches',
    +    ':not',
    +    ':where'
    +}
    +
    +PSEUDO_COMPLEX_NO_MATCH = {
    +    ':current',
    +    ':host',
    +    ':host-context'
    +}
    +
    +# Complex pseudo classes that take very specific parameters and are handled special
    +PSEUDO_SPECIAL = {
    +    ':dir',
    +    ':lang',
    +    ':nth-child',
    +    ':nth-last-child',
    +    ':nth-last-of-type',
    +    ':nth-of-type'
    +}
    +
    +PSEUDO_SUPPORTED = PSEUDO_SIMPLE | PSEUDO_SIMPLE_NO_MATCH | PSEUDO_COMPLEX | PSEUDO_COMPLEX_NO_MATCH | PSEUDO_SPECIAL
    +
    +# Sub-patterns parts
    +# Whitespace
    +NEWLINE = r'(?:\r\n|(?!\r\n)[\n\f\r])'
    +WS = fr'(?:[ \t]|{NEWLINE})'
    +# Comments
    +COMMENTS = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
    +# Whitespace with comments included
    +WSC = fr'(?:{WS}|{COMMENTS})'
    +# CSS escapes
    +CSS_ESCAPES = fr'(?:\\(?:[a-f0-9]{{1,6}}{WS}?|[^\r\n\f]|$))'
    +CSS_STRING_ESCAPES = fr'(?:\\(?:[a-f0-9]{{1,6}}{WS}?|[^\r\n\f]|$|{NEWLINE}))'
    +# CSS Identifier
    +IDENTIFIER = fr'''
    +(?:(?:-?(?:[^\x00-\x2f\x30-\x40\x5B-\x5E\x60\x7B-\x9f]|{CSS_ESCAPES})+|--)
    +(?:[^\x00-\x2c\x2e\x2f\x3A-\x40\x5B-\x5E\x60\x7B-\x9f]|{CSS_ESCAPES})*)
    +'''
    +# `nth` content
    +NTH = fr'(?:[-+])?(?:[0-9]+n?|n)(?:(?<=n){WSC}*(?:[-+]){WSC}*(?:[0-9]+))?'
    +# Value: quoted string or identifier
    +VALUE = fr'''(?:"(?:\\(?:.|{NEWLINE})|[^\\"\r\n\f]+)*?"|'(?:\\(?:.|{NEWLINE})|[^\\'\r\n\f]+)*?'|{IDENTIFIER})'''
    +# Attribute value comparison. `!=` is handled special as it is non-standard.
    +ATTR = fr'(?:{WSC}*(?P[!~^|*$]?=){WSC}*(?P{VALUE})(?:{WSC}*(?P[is]))?)?{WSC}*'
    +
    +# Selector patterns
    +# IDs (`#id`)
    +PAT_ID = fr'\#{IDENTIFIER}'
    +# Classes (`.class`)
    +PAT_CLASS = fr'\.{IDENTIFIER}'
    +# Prefix:Tag (`prefix|tag`)
    +PAT_TAG = fr'(?P(?:{IDENTIFIER}|\*)?\|)?(?P{IDENTIFIER}|\*)'
    +# Attributes (`[attr]`, `[attr=value]`, etc.)
    +PAT_ATTR = fr'\[{WSC}*(?P(?:{IDENTIFIER}|\*)?\|)?(?P{IDENTIFIER}){ATTR}\]'
    +# Pseudo class (`:pseudo-class`, `:pseudo-class(`)
    +PAT_PSEUDO_CLASS = fr'(?P:{IDENTIFIER})(?P\({WSC}*)?'
    +# Pseudo class special patterns. Matches `:pseudo-class(` for special case pseudo classes.
    +PAT_PSEUDO_CLASS_SPECIAL = fr'(?P:{IDENTIFIER})(?P\({WSC}*)'
    +# Custom pseudo class (`:--custom-pseudo`)
    +PAT_PSEUDO_CLASS_CUSTOM = fr'(?P:(?=--){IDENTIFIER})'
    +# Nesting ampersand selector. Matches `&`
    +PAT_AMP = r'&'
    +# Closing pseudo group (`)`)
    +PAT_PSEUDO_CLOSE = fr'{WSC}*\)'
    +# Pseudo element (`::pseudo-element`)
    +PAT_PSEUDO_ELEMENT = fr':{PAT_PSEUDO_CLASS}'
    +# At rule (`@page`, etc.) (not supported)
    +PAT_AT_RULE = fr'@P{IDENTIFIER}'
    +# Pseudo class `nth-child` (`:nth-child(an+b [of S]?)`, `:first-child`, etc.)
    +PAT_PSEUDO_NTH_CHILD = fr'''
    +(?P{PAT_PSEUDO_CLASS_SPECIAL}
    +(?P{NTH}|even|odd))(?:{WSC}*\)|(?P{COMMENTS}*{WS}{WSC}*of{COMMENTS}*{WS}{WSC}*))
    +'''
    +# Pseudo class `nth-of-type` (`:nth-of-type(an+b)`, `:first-of-type`, etc.)
    +PAT_PSEUDO_NTH_TYPE = fr'''
    +(?P{PAT_PSEUDO_CLASS_SPECIAL}
    +(?P{NTH}|even|odd)){WSC}*\)
    +'''
    +# Pseudo class language (`:lang("*-de", en)`)
    +PAT_PSEUDO_LANG = fr'{PAT_PSEUDO_CLASS_SPECIAL}(?P{VALUE}(?:{WSC}*,{WSC}*{VALUE})*){WSC}*\)'
    +# Pseudo class direction (`:dir(ltr)`)
    +PAT_PSEUDO_DIR = fr'{PAT_PSEUDO_CLASS_SPECIAL}(?Pltr|rtl){WSC}*\)'
    +# Combining characters (`>`, `~`, ` `, `+`, `,`)
    +PAT_COMBINE = fr'{WSC}*?(?P[,+>~]|{WS}(?![,+>~])){WSC}*'
    +# Extra: Contains (`:contains(text)`)
    +PAT_PSEUDO_CONTAINS = fr'{PAT_PSEUDO_CLASS_SPECIAL}(?P{VALUE}(?:{WSC}*,{WSC}*{VALUE})*){WSC}*\)'
    +
    +# Regular expressions
    +# CSS escape pattern
    +RE_CSS_ESC = re.compile(fr'(?:(\\[a-f0-9]{{1,6}}{WSC}?)|(\\[^\r\n\f])|(\\$))', re.I)
    +RE_CSS_STR_ESC = re.compile(fr'(?:(\\[a-f0-9]{{1,6}}{WS}?)|(\\[^\r\n\f])|(\\$)|(\\{NEWLINE}))', re.I)
    +# Pattern to break up `nth` specifiers
    +RE_NTH = re.compile(fr'(?P[-+])?(?P[0-9]+n?|n)(?:(?<=n){WSC}*(?P[-+]){WSC}*(?P[0-9]+))?', re.I)
    +# Pattern to iterate multiple values.
    +RE_VALUES = re.compile(fr'(?:(?P{VALUE})|(?P{WSC}*,{WSC}*))', re.X)
    +# Whitespace checks
    +RE_WS = re.compile(WS)
    +RE_WS_BEGIN = re.compile(fr'^{WSC}*')
    +RE_WS_END = re.compile(fr'{WSC}*$')
    +RE_CUSTOM = re.compile(fr'^{PAT_PSEUDO_CLASS_CUSTOM}$', re.X)
    +
    +# Constants
    +# List split token
    +COMMA_COMBINATOR = ','
    +# Relation token for descendant
    +WS_COMBINATOR = " "
    +
    +# Parse flags
    +FLG_PSEUDO = 0x01
    +FLG_NOT = 0x02
    +FLG_RELATIVE = 0x04
    +FLG_DEFAULT = 0x08
    +FLG_HTML = 0x10
    +FLG_INDETERMINATE = 0x20
    +FLG_OPEN = 0x40
    +FLG_IN_RANGE = 0x80
    +FLG_OUT_OF_RANGE = 0x100
    +FLG_PLACEHOLDER_SHOWN = 0x200
    +FLG_FORGIVE = 0x400
    +
    +# Maximum cached patterns to store
    +_MAXCACHE = 500
    +
    +
    +@lru_cache(maxsize=_MAXCACHE)
    +def _cached_css_compile(
    +    pattern: str,
    +    namespaces: ct.Namespaces | None,
    +    custom: ct.CustomSelectors | None,
    +    flags: int
    +) -> cm.SoupSieve:
    +    """Cached CSS compile."""
    +
    +    custom_selectors = process_custom(custom)
    +    return cm.SoupSieve(
    +        pattern,
    +        CSSParser(
    +            pattern,
    +            custom=custom_selectors,
    +            flags=flags
    +        ).process_selectors(),
    +        namespaces,
    +        custom,
    +        flags
    +    )
    +
    +
    +def _purge_cache() -> None:
    +    """Purge the cache."""
    +
    +    _cached_css_compile.cache_clear()
    +
    +
    +def process_custom(custom: ct.CustomSelectors | None) -> dict[str, str | ct.SelectorList]:
    +    """Process custom."""
    +
    +    custom_selectors = {}
    +    if custom is not None:
    +        for key, value in custom.items():
    +            name = util.lower(key)
    +            if RE_CUSTOM.match(name) is None:
    +                raise SelectorSyntaxError(f"The name '{name}' is not a valid custom pseudo-class name")
    +            if name in custom_selectors:
    +                raise KeyError(f"The custom selector '{name}' has already been registered")
    +            custom_selectors[css_unescape(name)] = value
    +    return custom_selectors
    +
    +
    +def css_unescape(content: str, string: bool = False) -> str:
    +    """
    +    Unescape CSS value.
    +
    +    Strings allow for spanning the value on multiple strings by escaping a new line.
    +    """
    +
    +    def replace(m: Match[str]) -> str:
    +        """Replace with the appropriate substitute."""
    +
    +        if m.group(1):
    +            codepoint = int(m.group(1)[1:], 16)
    +            if codepoint == 0:
    +                codepoint = UNICODE_REPLACEMENT_CHAR
    +            value = chr(codepoint)
    +        elif m.group(2):
    +            value = m.group(2)[1:]
    +        elif m.group(3):
    +            value = '\ufffd'
    +        else:
    +            value = ''
    +
    +        return value
    +
    +    return (RE_CSS_ESC if not string else RE_CSS_STR_ESC).sub(replace, content)
    +
    +
    +def escape(ident: str) -> str:
    +    """Escape identifier."""
    +
    +    string = []
    +    length = len(ident)
    +    start_dash = length > 0 and ident[0] == '-'
    +    if length == 1 and start_dash:
    +        # Need to escape identifier that is a single `-` with no other characters
    +        string.append(f'\\{ident}')
    +    else:
    +        for index, c in enumerate(ident):
    +            codepoint = ord(c)
    +            if codepoint == 0x00:
    +                string.append('\ufffd')
    +            elif (0x01 <= codepoint <= 0x1F) or codepoint == 0x7F:
    +                string.append(f'\\{codepoint:x} ')
    +            elif (index == 0 or (start_dash and index == 1)) and (0x30 <= codepoint <= 0x39):
    +                string.append(f'\\{codepoint:x} ')
    +            elif (
    +                codepoint in (0x2D, 0x5F) or codepoint >= 0x80 or (0x30 <= codepoint <= 0x39) or
    +                (0x30 <= codepoint <= 0x39) or (0x41 <= codepoint <= 0x5A) or (0x61 <= codepoint <= 0x7A)
    +            ):
    +                string.append(c)
    +            else:
    +                string.append(f'\\{c}')
    +    return ''.join(string)
    +
    +
    +class SelectorPattern:
    +    """Selector pattern."""
    +
    +    def __init__(self, name: str, pattern: str) -> None:
    +        """Initialize."""
    +
    +        self.name = name
    +        self.re_pattern = re.compile(pattern, re.I | re.X | re.U)
    +
    +    def get_name(self) -> str:
    +        """Get name."""
    +
    +        return self.name
    +
    +    def match(self, selector: str, index: int, flags: int) -> Match[str] | None:
    +        """Match the selector."""
    +
    +        return self.re_pattern.match(selector, index)
    +
    +
    +class SpecialPseudoPattern(SelectorPattern):
    +    """Selector pattern."""
    +
    +    def __init__(self, patterns: tuple[tuple[str, tuple[str, ...], str, type[SelectorPattern]], ...]) -> None:
    +        """Initialize."""
    +
    +        self.patterns = {}
    +        for p in patterns:
    +            name = p[0]
    +            pattern = p[3](name, p[2])
    +            for pseudo in p[1]:
    +                self.patterns[pseudo] = pattern
    +
    +        self.matched_name = None  # type: SelectorPattern | None
    +        self.re_pseudo_name = re.compile(PAT_PSEUDO_CLASS_SPECIAL, re.I | re.X | re.U)
    +
    +    def get_name(self) -> str:
    +        """Get name."""
    +
    +        return '' if self.matched_name is None else self.matched_name.get_name()
    +
    +    def match(self, selector: str, index: int, flags: int) -> Match[str] | None:
    +        """Match the selector."""
    +
    +        pseudo = None
    +        m = self.re_pseudo_name.match(selector, index)
    +        if m:
    +            name = util.lower(css_unescape(m.group('name')))
    +            pattern = self.patterns.get(name)
    +            if pattern:
    +                pseudo = pattern.match(selector, index, flags)
    +                if pseudo:
    +                    self.matched_name = pattern
    +
    +        return pseudo
    +
    +
    +class _Selector:
    +    """
    +    Intermediate selector class.
    +
    +    This stores selector data for a compound selector as we are acquiring them.
    +    Once we are done collecting the data for a compound selector, we freeze
    +    the data in an object that can be pickled and hashed.
    +    """
    +
    +    def __init__(self, **kwargs: Any) -> None:
    +        """Initialize."""
    +
    +        self.tag = kwargs.get('tag', None)  # type: ct.SelectorTag | None
    +        self.ids = kwargs.get('ids', [])  # type: list[str]
    +        self.classes = kwargs.get('classes', [])  # type: list[str]
    +        self.attributes = kwargs.get('attributes', [])  # type: list[ct.SelectorAttribute]
    +        self.nth = kwargs.get('nth', [])  # type: list[ct.SelectorNth]
    +        self.selectors = kwargs.get('selectors', [])  # type: list[ct.SelectorList]
    +        self.relations = kwargs.get('relations', [])  # type: list[_Selector]
    +        self.rel_type = kwargs.get('rel_type', None)  # type: str | None
    +        self.contains = kwargs.get('contains', [])  # type: list[ct.SelectorContains]
    +        self.lang = kwargs.get('lang', [])  # type: list[ct.SelectorLang]
    +        self.flags = kwargs.get('flags', 0)  # type: int
    +        self.no_match = kwargs.get('no_match', False)  # type: bool
    +
    +    def _freeze_relations(self, relations: list[_Selector]) -> ct.SelectorList:
    +        """Freeze relation."""
    +
    +        if relations:
    +            sel = relations[0]
    +            sel.relations.extend(relations[1:])
    +            return ct.SelectorList([sel.freeze()])
    +        else:
    +            return ct.SelectorList()
    +
    +    def freeze(self) -> ct.Selector | ct.SelectorNull:
    +        """Freeze self."""
    +
    +        if self.no_match:
    +            return ct.SelectorNull()
    +        else:
    +            return ct.Selector(
    +                self.tag,
    +                tuple(self.ids),
    +                tuple(self.classes),
    +                tuple(self.attributes),
    +                tuple(self.nth),
    +                tuple(self.selectors),
    +                self._freeze_relations(self.relations),
    +                self.rel_type,
    +                tuple(self.contains),
    +                tuple(self.lang),
    +                self.flags
    +            )
    +
    +    def __str__(self) -> str:  # pragma: no cover
    +        """String representation."""
    +
    +        return (
    +            f'_Selector(tag={self.tag!r}, ids={self.ids!r}, classes={self.classes!r}, attributes={self.attributes!r}, '
    +            f'nth={self.nth!r}, selectors={self.selectors!r}, relations={self.relations!r}, '
    +            f'rel_type={self.rel_type!r}, contains={self.contains!r}, lang={self.lang!r}, flags={self.flags!r}, '
    +            f'no_match={self.no_match!r})'
    +        )
    +
    +    __repr__ = __str__
    +
    +
    +class CSSParser:
    +    """Parse CSS selectors."""
    +
    +    css_tokens = (
    +        SelectorPattern("pseudo_close", PAT_PSEUDO_CLOSE),
    +        SpecialPseudoPattern(
    +            (
    +                (
    +                    "pseudo_contains",
    +                    (':contains', ':-soup-contains', ':-soup-contains-own'),
    +                    PAT_PSEUDO_CONTAINS,
    +                    SelectorPattern
    +                ),
    +                ("pseudo_nth_child", (':nth-child', ':nth-last-child'), PAT_PSEUDO_NTH_CHILD, SelectorPattern),
    +                ("pseudo_nth_type", (':nth-of-type', ':nth-last-of-type'), PAT_PSEUDO_NTH_TYPE, SelectorPattern),
    +                ("pseudo_lang", (':lang',), PAT_PSEUDO_LANG, SelectorPattern),
    +                ("pseudo_dir", (':dir',), PAT_PSEUDO_DIR, SelectorPattern)
    +            )
    +        ),
    +        SelectorPattern("pseudo_class_custom", PAT_PSEUDO_CLASS_CUSTOM),
    +        SelectorPattern("pseudo_class", PAT_PSEUDO_CLASS),
    +        SelectorPattern("pseudo_element", PAT_PSEUDO_ELEMENT),
    +        SelectorPattern("amp", PAT_AMP),
    +        SelectorPattern("at_rule", PAT_AT_RULE),
    +        SelectorPattern("id", PAT_ID),
    +        SelectorPattern("class", PAT_CLASS),
    +        SelectorPattern("tag", PAT_TAG),
    +        SelectorPattern("attribute", PAT_ATTR),
    +        SelectorPattern("combine", PAT_COMBINE)
    +    )
    +
    +    def __init__(
    +        self,
    +        selector: str,
    +        custom: dict[str, str | ct.SelectorList] | None = None,
    +        flags: int = 0
    +    ) -> None:
    +        """Initialize."""
    +
    +        self.pattern = selector.replace('\x00', '\ufffd')
    +        self.flags = flags
    +        self.debug = self.flags & util.DEBUG
    +        self.custom = {} if custom is None else custom
    +
    +    def parse_attribute_selector(self, sel: _Selector, m: Match[str], has_selector: bool) -> bool:
    +        """Create attribute selector from the returned regex match."""
    +
    +        inverse = False
    +        op = m.group('cmp')
    +        case = util.lower(m.group('case')) if m.group('case') else None
    +        ns = css_unescape(m.group('attr_ns')[:-1]) if m.group('attr_ns') else ''
    +        attr = css_unescape(m.group('attr_name'))
    +        is_type = False
    +        pattern2 = None
    +        value = ''
    +
    +        if case:
    +            flags = (re.I if case == 'i' else 0) | re.DOTALL
    +        elif util.lower(attr) == 'type':
    +            flags = re.I | re.DOTALL
    +            is_type = True
    +        else:
    +            flags = re.DOTALL
    +
    +        if op:
    +            if m.group('value').startswith(('"', "'")):
    +                value = css_unescape(m.group('value')[1:-1], True)
    +            else:
    +                value = css_unescape(m.group('value'))
    +
    +        if not op:
    +            # Attribute name
    +            pattern = None
    +        elif op.startswith('^'):
    +            # Value start with
    +            pattern = re.compile(r'^%s.*' % re.escape(value), flags)
    +        elif op.startswith('$'):
    +            # Value ends with
    +            pattern = re.compile(r'.*?%s$' % re.escape(value), flags)
    +        elif op.startswith('*'):
    +            # Value contains
    +            pattern = re.compile(r'.*?%s.*' % re.escape(value), flags)
    +        elif op.startswith('~'):
    +            # Value contains word within space separated list
    +            # `~=` should match nothing if it is empty or contains whitespace,
    +            # so if either of these cases is present, use `[^\s\S]` which cannot be matched.
    +            value = r'[^\s\S]' if not value or RE_WS.search(value) else re.escape(value)
    +            pattern = re.compile(r'.*?(?:(?<=^)|(?<=[ \t\r\n\f]))%s(?=(?:[ \t\r\n\f]|$)).*' % value, flags)
    +        elif op.startswith('|'):
    +            # Value starts with word in dash separated list
    +            pattern = re.compile(r'^%s(?:-.*)?$' % re.escape(value), flags)
    +        else:
    +            # Value matches
    +            pattern = re.compile(r'^%s$' % re.escape(value), flags)
    +            if op.startswith('!'):
    +                # Equivalent to `:not([attr=value])`
    +                inverse = True
    +        if is_type and pattern:
    +            pattern2 = re.compile(pattern.pattern)
    +
    +        # Append the attribute selector
    +        sel_attr = ct.SelectorAttribute(attr, ns, pattern, pattern2)
    +        if inverse:
    +            # If we are using `!=`, we need to nest the pattern under a `:not()`.
    +            sub_sel = _Selector()
    +            sub_sel.attributes.append(sel_attr)
    +            not_list = ct.SelectorList([sub_sel.freeze()], True, False)
    +            sel.selectors.append(not_list)
    +        else:
    +            sel.attributes.append(sel_attr)
    +
    +        has_selector = True
    +        return has_selector
    +
    +    def parse_tag_pattern(self, sel: _Selector, m: Match[str], has_selector: bool) -> bool:
    +        """Parse tag pattern from regex match."""
    +
    +        prefix = css_unescape(m.group('tag_ns')[:-1]) if m.group('tag_ns') else None
    +        tag = css_unescape(m.group('tag_name'))
    +        sel.tag = ct.SelectorTag(tag, prefix)
    +        has_selector = True
    +        return has_selector
    +
    +    def parse_pseudo_class_custom(self, sel: _Selector, m: Match[str], has_selector: bool) -> bool:
    +        """
    +        Parse custom pseudo class alias.
    +
    +        Compile custom selectors as we need them. When compiling a custom selector,
    +        set it to `None` in the dictionary so we can avoid an infinite loop.
    +        """
    +
    +        pseudo = util.lower(css_unescape(m.group('name')))
    +        selector = self.custom.get(pseudo)
    +        if selector is None:
    +            raise SelectorSyntaxError(
    +                f"Undefined custom selector '{pseudo}' found at position {m.end(0)}",
    +                self.pattern,
    +                m.end(0)
    +            )
    +
    +        if not isinstance(selector, ct.SelectorList):
    +            del self.custom[pseudo]
    +            selector = CSSParser(
    +                selector, custom=self.custom, flags=self.flags
    +            ).process_selectors(flags=FLG_PSEUDO)
    +            self.custom[pseudo] = selector
    +
    +        sel.selectors.append(selector)
    +        has_selector = True
    +        return has_selector
    +
    +    def parse_pseudo_class(
    +        self,
    +        sel: _Selector,
    +        m: Match[str],
    +        has_selector: bool,
    +        iselector: Iterator[tuple[str, Match[str]]],
    +        is_html: bool
    +    ) -> tuple[bool, bool]:
    +        """Parse pseudo class."""
    +
    +        complex_pseudo = False
    +        pseudo = util.lower(css_unescape(m.group('name')))
    +        if m.group('open'):
    +            complex_pseudo = True
    +        if complex_pseudo and pseudo in PSEUDO_COMPLEX:
    +            has_selector = self.parse_pseudo_open(sel, pseudo, has_selector, iselector, m.end(0))
    +        elif not complex_pseudo and pseudo in PSEUDO_SIMPLE:
    +            if pseudo == ':root':
    +                sel.flags |= ct.SEL_ROOT
    +            elif pseudo == ':defined':
    +                sel.flags |= ct.SEL_DEFINED
    +                is_html = True
    +            elif pseudo == ':scope':
    +                sel.flags |= ct.SEL_SCOPE
    +            elif pseudo == ':empty':
    +                sel.flags |= ct.SEL_EMPTY
    +            elif pseudo in (':link', ':any-link'):
    +                sel.selectors.append(CSS_LINK)
    +            elif pseudo == ':checked':
    +                sel.selectors.append(CSS_CHECKED)
    +            elif pseudo == ':default':
    +                sel.selectors.append(CSS_DEFAULT)
    +            elif pseudo == ':indeterminate':
    +                sel.selectors.append(CSS_INDETERMINATE)
    +            elif pseudo == ":disabled":
    +                sel.selectors.append(CSS_DISABLED)
    +            elif pseudo == ":enabled":
    +                sel.selectors.append(CSS_ENABLED)
    +            elif pseudo == ":required":
    +                sel.selectors.append(CSS_REQUIRED)
    +            elif pseudo == ":muted":
    +                sel.selectors.append(CSS_MUTED)
    +            elif pseudo == ":open":
    +                sel.selectors.append(CSS_OPEN)
    +            elif pseudo == ":optional":
    +                sel.selectors.append(CSS_OPTIONAL)
    +            elif pseudo == ":read-only":
    +                sel.selectors.append(CSS_READ_ONLY)
    +            elif pseudo == ":read-write":
    +                sel.selectors.append(CSS_READ_WRITE)
    +            elif pseudo == ":in-range":
    +                sel.selectors.append(CSS_IN_RANGE)
    +            elif pseudo == ":out-of-range":
    +                sel.selectors.append(CSS_OUT_OF_RANGE)
    +            elif pseudo == ":placeholder-shown":
    +                sel.selectors.append(CSS_PLACEHOLDER_SHOWN)
    +            elif pseudo == ':first-child':
    +                sel.nth.append(ct.SelectorNth(1, False, 0, False, False, ct.SelectorList()))
    +            elif pseudo == ':last-child':
    +                sel.nth.append(ct.SelectorNth(1, False, 0, False, True, ct.SelectorList()))
    +            elif pseudo == ':first-of-type':
    +                sel.nth.append(ct.SelectorNth(1, False, 0, True, False, ct.SelectorList()))
    +            elif pseudo == ':last-of-type':
    +                sel.nth.append(ct.SelectorNth(1, False, 0, True, True, ct.SelectorList()))
    +            elif pseudo == ':only-child':
    +                sel.nth.extend(
    +                    [
    +                        ct.SelectorNth(1, False, 0, False, False, ct.SelectorList()),
    +                        ct.SelectorNth(1, False, 0, False, True, ct.SelectorList())
    +                    ]
    +                )
    +            elif pseudo == ':only-of-type':
    +                sel.nth.extend(
    +                    [
    +                        ct.SelectorNth(1, False, 0, True, False, ct.SelectorList()),
    +                        ct.SelectorNth(1, False, 0, True, True, ct.SelectorList())
    +                    ]
    +                )
    +            has_selector = True
    +        elif complex_pseudo and pseudo in PSEUDO_COMPLEX_NO_MATCH:
    +            self.parse_selectors(iselector, m.end(0), FLG_PSEUDO | FLG_OPEN)
    +            sel.no_match = True
    +            has_selector = True
    +        elif not complex_pseudo and pseudo in PSEUDO_SIMPLE_NO_MATCH:
    +            sel.no_match = True
    +            has_selector = True
    +        elif pseudo in PSEUDO_SUPPORTED:
    +            raise SelectorSyntaxError(
    +                f"Invalid syntax for pseudo class '{pseudo}'",
    +                self.pattern,
    +                m.start(0)
    +            )
    +        else:
    +            raise SelectorSyntaxError(
    +                f"'{pseudo}' was detected as a pseudo-class and is either unsupported or invalid. "
    +                "If the syntax was not intended to be recognized as a pseudo-class, please escape the colon.",
    +                self.pattern,
    +                m.start(0)
    +            )
    +
    +        return has_selector, is_html
    +
    +    def parse_pseudo_nth(
    +        self,
    +        sel: _Selector,
    +        m: Match[str],
    +        has_selector: bool,
    +        iselector: Iterator[tuple[str, Match[str]]]
    +    ) -> bool:
    +        """Parse `nth` pseudo."""
    +
    +        mdict = m.groupdict()
    +        if mdict.get('pseudo_nth_child'):
    +            postfix = '_child'
    +        else:
    +            postfix = '_type'
    +        mdict['name'] = util.lower(css_unescape(mdict['name']))
    +        content = util.lower(mdict.get('nth' + postfix))
    +        if content == 'even':
    +            # 2n
    +            s1 = 2
    +            s2 = 0
    +            var = True
    +        elif content == 'odd':
    +            # 2n+1
    +            s1 = 2
    +            s2 = 1
    +            var = True
    +        else:
    +            nth_parts = cast(Match[str], RE_NTH.match(content))
    +            _s1 = '-' if nth_parts.group('s1') and nth_parts.group('s1') == '-' else ''
    +            a = nth_parts.group('a')
    +            var = a.endswith('n')
    +            if a.startswith('n'):
    +                _s1 += '1'
    +            elif var:
    +                _s1 += a[:-1]
    +            else:
    +                _s1 += a
    +            _s2 = '-' if nth_parts.group('s2') and nth_parts.group('s2') == '-' else ''
    +            if nth_parts.group('b'):
    +                _s2 += nth_parts.group('b')
    +            else:
    +                _s2 = '0'
    +            s1 = int(_s1, 10)
    +            s2 = int(_s2, 10)
    +
    +        pseudo_sel = mdict['name']
    +        if postfix == '_child':
    +            if m.group('of'):
    +                # Parse the rest of `of S`.
    +                nth_sel = self.parse_selectors(iselector, m.end(0), FLG_PSEUDO | FLG_OPEN)
    +            else:
    +                # Use default `*|*` for `of S`.
    +                nth_sel = CSS_NTH_OF_S_DEFAULT
    +            if pseudo_sel == ':nth-child':
    +                sel.nth.append(ct.SelectorNth(s1, var, s2, False, False, nth_sel))
    +            elif pseudo_sel == ':nth-last-child':
    +                sel.nth.append(ct.SelectorNth(s1, var, s2, False, True, nth_sel))
    +        else:
    +            if pseudo_sel == ':nth-of-type':
    +                sel.nth.append(ct.SelectorNth(s1, var, s2, True, False, ct.SelectorList()))
    +            elif pseudo_sel == ':nth-last-of-type':
    +                sel.nth.append(ct.SelectorNth(s1, var, s2, True, True, ct.SelectorList()))
    +        has_selector = True
    +        return has_selector
    +
    +    def parse_pseudo_open(
    +        self,
    +        sel: _Selector,
    +        name: str,
    +        has_selector: bool,
    +        iselector: Iterator[tuple[str, Match[str]]],
    +        index: int
    +    ) -> bool:
    +        """Parse pseudo with opening bracket."""
    +
    +        flags = FLG_PSEUDO | FLG_OPEN
    +        if name == ':not':
    +            flags |= FLG_NOT
    +        elif name == ':has':
    +            flags |= FLG_RELATIVE
    +        elif name in (':where', ':is'):
    +            flags |= FLG_FORGIVE
    +
    +        sel.selectors.append(self.parse_selectors(iselector, index, flags))
    +        has_selector = True
    +
    +        return has_selector
    +
    +    def parse_has_combinator(
    +        self,
    +        sel: _Selector,
    +        m: Match[str],
    +        has_selector: bool,
    +        selectors: list[_Selector],
    +        rel_type: str,
    +        index: int
    +    ) -> tuple[bool, _Selector, str]:
    +        """Parse combinator tokens."""
    +
    +        combinator = m.group('relation').strip()
    +        if not combinator:
    +            combinator = WS_COMBINATOR
    +        if combinator == COMMA_COMBINATOR:
    +            sel.rel_type = rel_type
    +            selectors[-1].relations.append(sel)
    +            rel_type = ":" + WS_COMBINATOR
    +            selectors.append(_Selector())
    +        else:
    +            if has_selector:
    +                # End the current selector and associate the leading combinator with this selector.
    +                sel.rel_type = rel_type
    +                selectors[-1].relations.append(sel)
    +            elif rel_type[1:] != WS_COMBINATOR:
    +                # It's impossible to have two whitespace combinators after each other as the patterns
    +                # will gobble up trailing whitespace. It is also impossible to have a whitespace
    +                # combinator after any other kind for the same reason. But we could have
    +                # multiple non-whitespace combinators. So if the current combinator is not a whitespace,
    +                # then we've hit the multiple combinator case, so we should fail.
    +                raise SelectorSyntaxError(
    +                    f'The multiple combinators at position {index}',
    +                    self.pattern,
    +                    index
    +                )
    +
    +            # Set the leading combinator for the next selector.
    +            rel_type = ':' + combinator
    +
    +        sel = _Selector()
    +        has_selector = False
    +        return has_selector, sel, rel_type
    +
    +    def parse_combinator(
    +        self,
    +        sel: _Selector,
    +        m: Match[str],
    +        has_selector: bool,
    +        selectors: list[_Selector],
    +        relations: list[_Selector],
    +        is_pseudo: bool,
    +        is_forgive: bool,
    +        index: int
    +    ) -> tuple[bool, _Selector]:
    +        """Parse combinator tokens."""
    +
    +        combinator = m.group('relation').strip()
    +        if not combinator:
    +            combinator = WS_COMBINATOR
    +        if not has_selector:
    +            if not is_forgive or combinator != COMMA_COMBINATOR:
    +                raise SelectorSyntaxError(
    +                    f"The combinator '{combinator}' at position {index}, must have a selector before it",
    +                    self.pattern,
    +                    index
    +                )
    +
    +            # If we are in a forgiving pseudo class, just make the selector a "no match"
    +            if combinator == COMMA_COMBINATOR:
    +                sel.no_match = True
    +                del relations[:]
    +                selectors.append(sel)
    +        else:
    +            if combinator == COMMA_COMBINATOR:
    +                if not sel.tag and not is_pseudo:
    +                    # Implied `*`
    +                    sel.tag = ct.SelectorTag('*', None)
    +                sel.relations.extend(relations)
    +                selectors.append(sel)
    +                del relations[:]
    +            else:
    +                sel.relations.extend(relations)
    +                sel.rel_type = combinator
    +                del relations[:]
    +                relations.append(sel)
    +
    +        sel = _Selector()
    +        has_selector = False
    +
    +        return has_selector, sel
    +
    +    def parse_class_id(self, sel: _Selector, m: Match[str], has_selector: bool) -> bool:
    +        """Parse HTML classes and ids."""
    +
    +        selector = m.group(0)
    +        if selector.startswith('.'):
    +            sel.classes.append(css_unescape(selector[1:]))
    +        else:
    +            sel.ids.append(css_unescape(selector[1:]))
    +        has_selector = True
    +        return has_selector
    +
    +    def parse_pseudo_contains(self, sel: _Selector, m: Match[str], has_selector: bool) -> bool:
    +        """Parse contains."""
    +
    +        pseudo = util.lower(css_unescape(m.group('name')))
    +        if pseudo == ":contains":
    +            warnings.warn(  # noqa: B028
    +                "The pseudo class ':contains' is deprecated, ':-soup-contains' should be used moving forward.",
    +                FutureWarning
    +            )
    +        contains_own = pseudo == ":-soup-contains-own"
    +        values = css_unescape(m.group('values'))
    +        patterns = []
    +        for token in RE_VALUES.finditer(values):
    +            if token.group('split'):
    +                continue
    +            value = token.group('value')
    +            if value.startswith(("'", '"')):
    +                value = css_unescape(value[1:-1], True)
    +            else:
    +                value = css_unescape(value)
    +            patterns.append(value)
    +        sel.contains.append(ct.SelectorContains(patterns, contains_own))
    +        has_selector = True
    +        return has_selector
    +
    +    def parse_pseudo_lang(self, sel: _Selector, m: Match[str], has_selector: bool) -> bool:
    +        """Parse pseudo language."""
    +
    +        values = m.group('values')
    +        patterns = []
    +        for token in RE_VALUES.finditer(values):
    +            if token.group('split'):
    +                continue
    +            value = token.group('value')
    +            if value.startswith(('"', "'")):
    +                value = css_unescape(value[1:-1], True)
    +            else:
    +                value = css_unescape(value)
    +
    +            patterns.append(value)
    +
    +        sel.lang.append(ct.SelectorLang(patterns))
    +        has_selector = True
    +
    +        return has_selector
    +
    +    def parse_pseudo_dir(self, sel: _Selector, m: Match[str], has_selector: bool) -> bool:
    +        """Parse pseudo direction."""
    +
    +        value = ct.SEL_DIR_LTR if util.lower(m.group('dir')) == 'ltr' else ct.SEL_DIR_RTL
    +        sel.flags |= value
    +        has_selector = True
    +        return has_selector
    +
    +    def parse_selectors(
    +        self,
    +        iselector: Iterator[tuple[str, Match[str]]],
    +        index: int = 0,
    +        flags: int = 0
    +    ) -> ct.SelectorList:
    +        """Parse selectors."""
    +
    +        # Initialize important variables
    +        sel = _Selector()
    +        selectors = []
    +        has_selector = False
    +        closed = False
    +        relations = []  # type: list[_Selector]
    +        rel_type = ":" + WS_COMBINATOR
    +
    +        # Setup various flags
    +        is_open = bool(flags & FLG_OPEN)
    +        is_pseudo = bool(flags & FLG_PSEUDO)
    +        is_relative = bool(flags & FLG_RELATIVE)
    +        is_not = bool(flags & FLG_NOT)
    +        is_html = bool(flags & FLG_HTML)
    +        is_default = bool(flags & FLG_DEFAULT)
    +        is_indeterminate = bool(flags & FLG_INDETERMINATE)
    +        is_in_range = bool(flags & FLG_IN_RANGE)
    +        is_out_of_range = bool(flags & FLG_OUT_OF_RANGE)
    +        is_placeholder_shown = bool(flags & FLG_PLACEHOLDER_SHOWN)
    +        is_forgive = bool(flags & FLG_FORGIVE)
    +
    +        # Print out useful debug stuff
    +        if self.debug:  # pragma: no cover
    +            if is_pseudo:
    +                print('    is_pseudo: True')
    +            if is_open:
    +                print('    is_open: True')
    +            if is_relative:
    +                print('    is_relative: True')
    +            if is_not:
    +                print('    is_not: True')
    +            if is_html:
    +                print('    is_html: True')
    +            if is_default:
    +                print('    is_default: True')
    +            if is_indeterminate:
    +                print('    is_indeterminate: True')
    +            if is_in_range:
    +                print('    is_in_range: True')
    +            if is_out_of_range:
    +                print('    is_out_of_range: True')
    +            if is_placeholder_shown:
    +                print('    is_placeholder_shown: True')
    +            if is_forgive:
    +                print('    is_forgive: True')
    +
    +        # The algorithm for relative selectors require an initial selector in the selector list
    +        if is_relative:
    +            selectors.append(_Selector())
    +
    +        try:
    +            while True:
    +                key, m = next(iselector)
    +
    +                # Handle parts
    +                if key == "at_rule":
    +                    raise NotImplementedError(f"At-rules found at position {m.start(0)}")
    +                elif key == "amp":
    +                    sel.flags |= ct.SEL_SCOPE
    +                    has_selector = True
    +                elif key == 'pseudo_class_custom':
    +                    has_selector = self.parse_pseudo_class_custom(sel, m, has_selector)
    +                elif key == 'pseudo_class':
    +                    has_selector, is_html = self.parse_pseudo_class(sel, m, has_selector, iselector, is_html)
    +                elif key == 'pseudo_element':
    +                    raise NotImplementedError(f"Pseudo-element found at position {m.start(0)}")
    +                elif key == 'pseudo_contains':
    +                    has_selector = self.parse_pseudo_contains(sel, m, has_selector)
    +                elif key in ('pseudo_nth_type', 'pseudo_nth_child'):
    +                    has_selector = self.parse_pseudo_nth(sel, m, has_selector, iselector)
    +                elif key == 'pseudo_lang':
    +                    has_selector = self.parse_pseudo_lang(sel, m, has_selector)
    +                elif key == 'pseudo_dir':
    +                    has_selector = self.parse_pseudo_dir(sel, m, has_selector)
    +                    # Currently only supports HTML
    +                    is_html = True
    +                elif key == 'pseudo_close':
    +                    if not has_selector:
    +                        if not is_forgive:
    +                            raise SelectorSyntaxError(
    +                                f"Expected a selector at position {m.start(0)}",
    +                                self.pattern,
    +                                m.start(0)
    +                            )
    +                        sel.no_match = True
    +                    if is_open:
    +                        closed = True
    +                        break
    +                    else:
    +                        raise SelectorSyntaxError(
    +                            f"Unmatched pseudo-class close at position {m.start(0)}",
    +                            self.pattern,
    +                            m.start(0)
    +                        )
    +                elif key == 'combine':
    +                    if is_relative:
    +                        has_selector, sel, rel_type = self.parse_has_combinator(
    +                            sel, m, has_selector, selectors, rel_type, index
    +                        )
    +                    else:
    +                        has_selector, sel = self.parse_combinator(
    +                            sel, m, has_selector, selectors, relations, is_pseudo, is_forgive, index
    +                        )
    +                elif key == 'attribute':
    +                    has_selector = self.parse_attribute_selector(sel, m, has_selector)
    +                elif key == 'tag':
    +                    if has_selector:
    +                        raise SelectorSyntaxError(
    +                            f"Tag name found at position {m.start(0)} instead of at the start",
    +                            self.pattern,
    +                            m.start(0)
    +                        )
    +                    has_selector = self.parse_tag_pattern(sel, m, has_selector)
    +                elif key in ('class', 'id'):
    +                    has_selector = self.parse_class_id(sel, m, has_selector)
    +
    +                index = m.end(0)
    +        except StopIteration:
    +            pass
    +
    +        # Handle selectors that are not closed
    +        if is_open and not closed:
    +            raise SelectorSyntaxError(
    +                f"Unclosed pseudo-class at position {index}",
    +                self.pattern,
    +                index
    +            )
    +
    +        # Cleanup completed selector piece
    +        if has_selector:
    +            if not sel.tag and not is_pseudo:
    +                # Implied `*`
    +                sel.tag = ct.SelectorTag('*', None)
    +            if is_relative:
    +                sel.rel_type = rel_type
    +                selectors[-1].relations.append(sel)
    +            else:
    +                sel.relations.extend(relations)
    +                del relations[:]
    +                selectors.append(sel)
    +
    +        # Forgive empty slots in pseudo-classes that have lists (and are forgiving)
    +        elif is_forgive and (not selectors or not relations):
    +            # Handle normal pseudo-classes with empty slots like `:is()` etc.
    +            sel.no_match = True
    +            del relations[:]
    +            selectors.append(sel)
    +            has_selector = True
    +
    +        if not has_selector:
    +            # We will always need to finish a selector when `:has()` is used as it leads with combining.
    +            # May apply to others as well.
    +            raise SelectorSyntaxError(
    +                f'Expected a selector at position {index}',
    +                self.pattern,
    +                index
    +            )
    +
    +        # Some patterns require additional logic, such as default. We try to make these the
    +        # last pattern, and append the appropriate flag to that selector which communicates
    +        # to the matcher what additional logic is required.
    +        if is_default:
    +            selectors[-1].flags = ct.SEL_DEFAULT
    +        if is_indeterminate:
    +            selectors[-1].flags = ct.SEL_INDETERMINATE
    +        if is_in_range:
    +            selectors[-1].flags = ct.SEL_IN_RANGE
    +        if is_out_of_range:
    +            selectors[-1].flags = ct.SEL_OUT_OF_RANGE
    +        if is_placeholder_shown:
    +            selectors[-1].flags = ct.SEL_PLACEHOLDER_SHOWN
    +
    +        # Return selector list
    +        return ct.SelectorList([s.freeze() for s in selectors], is_not, is_html)
    +
    +    def selector_iter(self, pattern: str) -> Iterator[tuple[str, Match[str]]]:
    +        """Iterate selector tokens."""
    +
    +        # Ignore whitespace and comments at start and end of pattern
    +        m = RE_WS_BEGIN.search(pattern)
    +        index = m.end(0) if m else 0
    +        m = RE_WS_END.search(pattern)
    +        end = (m.start(0) - 1) if m else (len(pattern) - 1)
    +
    +        if self.debug:  # pragma: no cover
    +            print(f'## PARSING: {pattern!r}')
    +        while index <= end:
    +            m = None
    +            for v in self.css_tokens:
    +                m = v.match(pattern, index, self.flags)
    +                if m:
    +                    name = v.get_name()
    +                    if self.debug:  # pragma: no cover
    +                        print(f"TOKEN: '{name}' --> {m.group(0)!r} at position {m.start(0)}")
    +                    index = m.end(0)
    +                    yield name, m
    +                    break
    +            if m is None:
    +                c = pattern[index]
    +                # If the character represents the start of one of the known selector types,
    +                # throw an exception mentioning that the known selector type is in error;
    +                # otherwise, report the invalid character.
    +                if c == '[':
    +                    msg = f"Malformed attribute selector at position {index}"
    +                elif c == '.':
    +                    msg = f"Malformed class selector at position {index}"
    +                elif c == '#':
    +                    msg = f"Malformed id selector at position {index}"
    +                elif c == ':':
    +                    msg = f"Malformed pseudo-class selector at position {index}"
    +                else:
    +                    msg = f"Invalid character {c!r} position {index}"
    +                raise SelectorSyntaxError(msg, self.pattern, index)
    +        if self.debug:  # pragma: no cover
    +            print('## END PARSING')
    +
    +    def process_selectors(self, index: int = 0, flags: int = 0) -> ct.SelectorList:
    +        """Process selectors."""
    +
    +        return self.parse_selectors(self.selector_iter(self.pattern), index, flags)
    +
    +
    +# Precompile CSS selector lists for pseudo-classes (additional logic may be required beyond the pattern)
    +# A few patterns are order dependent as they use patterns previous compiled.
    +
    +# CSS pattern for `:link` and `:any-link`
    +CSS_LINK = CSSParser(
    +    'html|*:is(a, area)[href]'
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    +# CSS pattern for `:checked`
    +CSS_CHECKED = CSSParser(
    +    '''
    +    html|*:is(input[type=checkbox], input[type=radio])[checked], html|option[selected]
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    +# CSS pattern for `:default` (must compile CSS_CHECKED first)
    +CSS_DEFAULT = CSSParser(
    +    '''
    +    :checked,
    +
    +    /*
    +    This pattern must be at the end.
    +    Special logic is applied to the last selector.
    +    */
    +    html|form html|*:is(button, input)[type="submit"]
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML | FLG_DEFAULT)
    +# CSS pattern for `:indeterminate`
    +CSS_INDETERMINATE = CSSParser(
    +    '''
    +    html|input[type="checkbox"][indeterminate],
    +    html|input[type="radio"]:is(:not([name]), [name=""]):not([checked]),
    +    html|progress:not([value]),
    +
    +    /*
    +    This pattern must be at the end.
    +    Special logic is applied to the last selector.
    +    */
    +    html|input[type="radio"][name]:not([name='']):not([checked])
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML | FLG_INDETERMINATE)
    +# CSS pattern for `:disabled`
    +CSS_DISABLED = CSSParser(
    +    '''
    +    html|*:is(input:not([type=hidden]), button, select, textarea, fieldset, optgroup, option, fieldset)[disabled],
    +    html|optgroup[disabled] > html|option,
    +    html|fieldset[disabled] > html|*:is(input:not([type=hidden]), button, select, textarea, fieldset),
    +    html|fieldset[disabled] >
    +        html|*:not(legend:nth-of-type(1)) html|*:is(input:not([type=hidden]), button, select, textarea, fieldset)
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    +# CSS pattern for `:enabled`
    +CSS_ENABLED = CSSParser(
    +    '''
    +    html|*:is(input:not([type=hidden]), button, select, textarea, fieldset, optgroup, option, fieldset):not(:disabled)
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    +# CSS pattern for `:required`
    +CSS_REQUIRED = CSSParser(
    +    'html|*:is(input, textarea, select)[required]'
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    +# CSS pattern for `:optional`
    +CSS_OPTIONAL = CSSParser(
    +    'html|*:is(input, textarea, select):not([required])'
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    +# CSS pattern for `:placeholder-shown`
    +CSS_PLACEHOLDER_SHOWN = CSSParser(
    +    '''
    +    html|input:is(
    +        :not([type]),
    +        [type=""],
    +        [type=text],
    +        [type=search],
    +        [type=url],
    +        [type=tel],
    +        [type=email],
    +        [type=password],
    +        [type=number]
    +    )[placeholder]:not([placeholder='']):is(:not([value]), [value=""]),
    +    html|textarea[placeholder]:not([placeholder=''])
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML | FLG_PLACEHOLDER_SHOWN)
    +# CSS pattern default for `:nth-child` "of S" feature
    +CSS_NTH_OF_S_DEFAULT = CSSParser(
    +    '*|*'
    +).process_selectors(flags=FLG_PSEUDO)
    +# CSS pattern for `:read-write` (CSS_DISABLED must be compiled first)
    +CSS_READ_WRITE = CSSParser(
    +    '''
    +    html|*:is(
    +        textarea,
    +        input:is(
    +            :not([type]),
    +            [type=""],
    +            [type=text],
    +            [type=search],
    +            [type=url],
    +            [type=tel],
    +            [type=email],
    +            [type=number],
    +            [type=password],
    +            [type=date],
    +            [type=datetime-local],
    +            [type=month],
    +            [type=time],
    +            [type=week]
    +        )
    +    ):not([readonly], :disabled),
    +    html|*:is([contenteditable=""], [contenteditable="true" i])
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    +# CSS pattern for `:read-only`
    +CSS_READ_ONLY = CSSParser(
    +    '''
    +    html|*:not(:read-write)
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    +# CSS pattern for `:in-range`
    +CSS_IN_RANGE = CSSParser(
    +    '''
    +    html|input:is(
    +        [type="date"],
    +        [type="month"],
    +        [type="week"],
    +        [type="time"],
    +        [type="datetime-local"],
    +        [type="number"],
    +        [type="range"]
    +    ):is(
    +        [min],
    +        [max]
    +    )
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_IN_RANGE | FLG_HTML)
    +# CSS pattern for `:out-of-range`
    +CSS_OUT_OF_RANGE = CSSParser(
    +    '''
    +    html|input:is(
    +        [type="date"],
    +        [type="month"],
    +        [type="week"],
    +        [type="time"],
    +        [type="datetime-local"],
    +        [type="number"],
    +        [type="range"]
    +    ):is(
    +        [min],
    +        [max]
    +    )
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_OUT_OF_RANGE | FLG_HTML)
    +
    +# CSS pattern for :open
    +CSS_OPEN = CSSParser(
    +    '''
    +    html|*:is(details, dialog)[open]
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    +
    +
    +# CSS pattern for :muted
    +CSS_MUTED = CSSParser(
    +    '''
    +    html|*:is(video, audio)[muted]
    +    '''
    +).process_selectors(flags=FLG_PSEUDO | FLG_HTML)
    diff --git a/venv/lib/python3.12/site-packages/soupsieve/css_types.py b/venv/lib/python3.12/site-packages/soupsieve/css_types.py
    new file mode 100644
    index 00000000..b69fe8cf
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/soupsieve/css_types.py
    @@ -0,0 +1,407 @@
    +"""CSS selector structure items."""
    +from __future__ import annotations
    +import copyreg
    +from .pretty import pretty
    +from typing import Any, Iterator, Hashable, Pattern, Iterable, Mapping
    +
    +__all__ = (
    +    'Selector',
    +    'SelectorNull',
    +    'SelectorTag',
    +    'SelectorAttribute',
    +    'SelectorContains',
    +    'SelectorNth',
    +    'SelectorLang',
    +    'SelectorList',
    +    'Namespaces',
    +    'CustomSelectors'
    +)
    +
    +
    +SEL_EMPTY = 0x1
    +SEL_ROOT = 0x2
    +SEL_DEFAULT = 0x4
    +SEL_INDETERMINATE = 0x8
    +SEL_SCOPE = 0x10
    +SEL_DIR_LTR = 0x20
    +SEL_DIR_RTL = 0x40
    +SEL_IN_RANGE = 0x80
    +SEL_OUT_OF_RANGE = 0x100
    +SEL_DEFINED = 0x200
    +SEL_PLACEHOLDER_SHOWN = 0x400
    +
    +
    +class Immutable:
    +    """Immutable."""
    +
    +    __slots__: tuple[str, ...] = ('_hash',)
    +
    +    _hash: int
    +
    +    def __init__(self, **kwargs: Any) -> None:
    +        """Initialize."""
    +
    +        temp = []
    +        for k, v in kwargs.items():
    +            temp.append(type(v))
    +            temp.append(v)
    +            super().__setattr__(k, v)
    +        super().__setattr__('_hash', hash(tuple(temp)))
    +
    +    @classmethod
    +    def __base__(cls) -> type[Immutable]:
    +        """Get base class."""
    +
    +        return cls
    +
    +    def __eq__(self, other: Any) -> bool:
    +        """Equal."""
    +
    +        return (
    +            isinstance(other, self.__base__()) and
    +            all(getattr(other, key) == getattr(self, key) for key in self.__slots__ if key != '_hash')
    +        )
    +
    +    def __ne__(self, other: Any) -> bool:
    +        """Equal."""
    +
    +        return (
    +            not isinstance(other, self.__base__()) or
    +            any(getattr(other, key) != getattr(self, key) for key in self.__slots__ if key != '_hash')
    +        )
    +
    +    def __hash__(self) -> int:
    +        """Hash."""
    +
    +        return self._hash
    +
    +    def __setattr__(self, name: str, value: Any) -> None:
    +        """Prevent mutability."""
    +
    +        raise AttributeError(f"'{self.__class__.__name__}' is immutable")
    +
    +    def __repr__(self) -> str:  # pragma: no cover
    +        """Representation."""
    +
    +        r = ', '.join([f"{k}={getattr(self, k)!r}" for k in self.__slots__[:-1]])
    +        return f"{self.__class__.__name__}({r})"
    +
    +    __str__ = __repr__
    +
    +    def pretty(self) -> None:  # pragma: no cover
    +        """Pretty print."""
    +
    +        print(pretty(self))
    +
    +
    +class ImmutableDict(Mapping[Any, Any]):
    +    """Hashable, immutable dictionary."""
    +
    +    def __init__(
    +        self,
    +        arg: dict[Any, Any] | Iterable[tuple[Any, Any]]
    +    ) -> None:
    +        """Initialize."""
    +
    +        self._validate(arg)
    +        self._d = dict(arg)
    +        self._hash = hash(tuple([(type(x), x, type(y), y) for x, y in sorted(self._d.items())]))
    +
    +    def _validate(self, arg: dict[Any, Any] | Iterable[tuple[Any, Any]]) -> None:
    +        """Validate arguments."""
    +
    +        if isinstance(arg, dict):
    +            if not all(isinstance(v, Hashable) for v in arg.values()):
    +                raise TypeError(f'{self.__class__.__name__} values must be hashable')
    +        elif not all(isinstance(k, Hashable) and isinstance(v, Hashable) for k, v in arg):
    +            raise TypeError(f'{self.__class__.__name__} values must be hashable')
    +
    +    def __iter__(self) -> Iterator[Any]:
    +        """Iterator."""
    +
    +        return iter(self._d)
    +
    +    def __len__(self) -> int:
    +        """Length."""
    +
    +        return len(self._d)
    +
    +    def __getitem__(self, key: Any) -> Any:
    +        """Get item: `namespace['key']`."""
    +
    +        return self._d[key]
    +
    +    def __hash__(self) -> int:
    +        """Hash."""
    +
    +        return self._hash
    +
    +    def __repr__(self) -> str:  # pragma: no cover
    +        """Representation."""
    +
    +        return f"{self._d!r}"
    +
    +    __str__ = __repr__
    +
    +
    +class Namespaces(ImmutableDict):
    +    """Namespaces."""
    +
    +    def __init__(self, arg: dict[str, str] | Iterable[tuple[str, str]]) -> None:
    +        """Initialize."""
    +
    +        super().__init__(arg)
    +
    +    def _validate(self, arg: dict[str, str] | Iterable[tuple[str, str]]) -> None:
    +        """Validate arguments."""
    +
    +        if not all(
    +            isinstance(k, str) and isinstance(v, str)
    +            for k, v in (arg.items() if isinstance(arg, dict) else arg)
    +        ):
    +            raise TypeError(f'{self.__class__.__name__} values must be hashable')
    +
    +
    +class CustomSelectors(ImmutableDict):
    +    """Custom selectors."""
    +
    +    def __init__(self, arg: dict[str, str] | Iterable[tuple[str, str]]) -> None:
    +        """Initialize."""
    +
    +        super().__init__(arg)
    +
    +    def _validate(self, arg: dict[str, str] | Iterable[tuple[str, str]]) -> None:
    +        """Validate arguments."""
    +
    +        if not all(
    +            isinstance(k, str) and isinstance(v, str)
    +            for k, v in (arg.items() if isinstance(arg, dict) else arg)
    +        ):
    +            raise TypeError(f'{self.__class__.__name__} values must be hashable')
    +
    +
    +class Selector(Immutable):
    +    """Selector."""
    +
    +    __slots__ = (
    +        'tag', 'ids', 'classes', 'attributes', 'nth', 'selectors',
    +        'relation', 'rel_type', 'contains', 'lang', 'flags', '_hash'
    +    )
    +
    +    tag: SelectorTag | None
    +    ids: tuple[str, ...]
    +    classes: tuple[str, ...]
    +    attributes: tuple[SelectorAttribute, ...]
    +    nth: tuple[SelectorNth, ...]
    +    selectors: tuple[SelectorList, ...]
    +    relation: SelectorList
    +    rel_type: str | None
    +    contains: tuple[SelectorContains, ...]
    +    lang: tuple[SelectorLang, ...]
    +    flags: int
    +
    +    def __init__(
    +        self,
    +        tag: SelectorTag | None,
    +        ids: tuple[str, ...],
    +        classes: tuple[str, ...],
    +        attributes: tuple[SelectorAttribute, ...],
    +        nth: tuple[SelectorNth, ...],
    +        selectors: tuple[SelectorList, ...],
    +        relation: SelectorList,
    +        rel_type: str | None,
    +        contains: tuple[SelectorContains, ...],
    +        lang: tuple[SelectorLang, ...],
    +        flags: int
    +    ):
    +        """Initialize."""
    +
    +        super().__init__(
    +            tag=tag,
    +            ids=ids,
    +            classes=classes,
    +            attributes=attributes,
    +            nth=nth,
    +            selectors=selectors,
    +            relation=relation,
    +            rel_type=rel_type,
    +            contains=contains,
    +            lang=lang,
    +            flags=flags
    +        )
    +
    +
    +class SelectorNull(Immutable):
    +    """Null Selector."""
    +
    +    def __init__(self) -> None:
    +        """Initialize."""
    +
    +        super().__init__()
    +
    +
    +class SelectorTag(Immutable):
    +    """Selector tag."""
    +
    +    __slots__ = ("name", "prefix", "_hash")
    +
    +    name: str
    +    prefix: str | None
    +
    +    def __init__(self, name: str, prefix: str | None) -> None:
    +        """Initialize."""
    +
    +        super().__init__(name=name, prefix=prefix)
    +
    +
    +class SelectorAttribute(Immutable):
    +    """Selector attribute rule."""
    +
    +    __slots__ = ("attribute", "prefix", "pattern", "xml_type_pattern", "_hash")
    +
    +    attribute: str
    +    prefix: str
    +    pattern: Pattern[str] | None
    +    xml_type_pattern: Pattern[str] | None
    +
    +    def __init__(
    +        self,
    +        attribute: str,
    +        prefix: str,
    +        pattern: Pattern[str] | None,
    +        xml_type_pattern: Pattern[str] | None
    +    ) -> None:
    +        """Initialize."""
    +
    +        super().__init__(
    +            attribute=attribute,
    +            prefix=prefix,
    +            pattern=pattern,
    +            xml_type_pattern=xml_type_pattern
    +        )
    +
    +
    +class SelectorContains(Immutable):
    +    """Selector contains rule."""
    +
    +    __slots__ = ("text", "own", "_hash")
    +
    +    text: tuple[str, ...]
    +    own: bool
    +
    +    def __init__(self, text: Iterable[str], own: bool) -> None:
    +        """Initialize."""
    +
    +        super().__init__(text=tuple(text), own=own)
    +
    +
    +class SelectorNth(Immutable):
    +    """Selector nth type."""
    +
    +    __slots__ = ("a", "n", "b", "of_type", "last", "selectors", "_hash")
    +
    +    a: int
    +    n: bool
    +    b: int
    +    of_type: bool
    +    last: bool
    +    selectors: SelectorList
    +
    +    def __init__(self, a: int, n: bool, b: int, of_type: bool, last: bool, selectors: SelectorList) -> None:
    +        """Initialize."""
    +
    +        super().__init__(
    +            a=a,
    +            n=n,
    +            b=b,
    +            of_type=of_type,
    +            last=last,
    +            selectors=selectors
    +        )
    +
    +
    +class SelectorLang(Immutable):
    +    """Selector language rules."""
    +
    +    __slots__ = ("languages", "_hash",)
    +
    +    languages: tuple[str, ...]
    +
    +    def __init__(self, languages: Iterable[str]):
    +        """Initialize."""
    +
    +        super().__init__(languages=tuple(languages))
    +
    +    def __iter__(self) -> Iterator[str]:
    +        """Iterator."""
    +
    +        return iter(self.languages)
    +
    +    def __len__(self) -> int:  # pragma: no cover
    +        """Length."""
    +
    +        return len(self.languages)
    +
    +    def __getitem__(self, index: int) -> str:  # pragma: no cover
    +        """Get item."""
    +
    +        return self.languages[index]
    +
    +
    +class SelectorList(Immutable):
    +    """Selector list."""
    +
    +    __slots__ = ("selectors", "is_not", "is_html", "_hash")
    +
    +    selectors: tuple[Selector | SelectorNull, ...]
    +    is_not: bool
    +    is_html: bool
    +
    +    def __init__(
    +        self,
    +        selectors: Iterable[Selector | SelectorNull] | None = None,
    +        is_not: bool = False,
    +        is_html: bool = False
    +    ) -> None:
    +        """Initialize."""
    +
    +        super().__init__(
    +            selectors=tuple(selectors) if selectors is not None else (),
    +            is_not=is_not,
    +            is_html=is_html
    +        )
    +
    +    def __iter__(self) -> Iterator[Selector | SelectorNull]:
    +        """Iterator."""
    +
    +        return iter(self.selectors)
    +
    +    def __len__(self) -> int:
    +        """Length."""
    +
    +        return len(self.selectors)
    +
    +    def __getitem__(self, index: int) -> Selector | SelectorNull:
    +        """Get item."""
    +
    +        return self.selectors[index]
    +
    +
    +def _pickle(p: Any) -> Any:
    +    return p.__base__(), tuple([getattr(p, s) for s in p.__slots__[:-1]])
    +
    +
    +def pickle_register(obj: Any) -> None:
    +    """Allow object to be pickled."""
    +
    +    copyreg.pickle(obj, _pickle)
    +
    +
    +pickle_register(Selector)
    +pickle_register(SelectorNull)
    +pickle_register(SelectorTag)
    +pickle_register(SelectorAttribute)
    +pickle_register(SelectorContains)
    +pickle_register(SelectorNth)
    +pickle_register(SelectorLang)
    +pickle_register(SelectorList)
    diff --git a/venv/lib/python3.12/site-packages/soupsieve/pretty.py b/venv/lib/python3.12/site-packages/soupsieve/pretty.py
    new file mode 100644
    index 00000000..37d51d8c
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/soupsieve/pretty.py
    @@ -0,0 +1,148 @@
    +"""
    +Format a pretty string of a `SoupSieve` object for easy debugging.
    +
    +This won't necessarily support all types and such, and definitely
    +not support custom outputs.
    +
    +It is mainly geared towards our types as the `SelectorList`
    +object is a beast to look at without some indentation and newlines.
    +The format and various output types is fairly known (though it
    +hasn't been tested extensively to make sure we aren't missing corners).
    +
    +Example:
    +-------
    +```
    +>>> import soupsieve as sv
    +>>> sv.compile('this > that.class[name=value]').selectors.pretty()
    +SelectorList(
    +    selectors=(
    +        Selector(
    +            tag=SelectorTag(
    +                name='that',
    +                prefix=None),
    +            ids=(),
    +            classes=(
    +                'class',
    +                ),
    +            attributes=(
    +                SelectorAttribute(
    +                    attribute='name',
    +                    prefix='',
    +                    pattern=re.compile(
    +                        '^value$'),
    +                    xml_type_pattern=None),
    +                ),
    +            nth=(),
    +            selectors=(),
    +            relation=SelectorList(
    +                selectors=(
    +                    Selector(
    +                        tag=SelectorTag(
    +                            name='this',
    +                            prefix=None),
    +                        ids=(),
    +                        classes=(),
    +                        attributes=(),
    +                        nth=(),
    +                        selectors=(),
    +                        relation=SelectorList(
    +                            selectors=(),
    +                            is_not=False,
    +                            is_html=False),
    +                        rel_type='>',
    +                        contains=(),
    +                        lang=(),
    +                        flags=0),
    +                    ),
    +                is_not=False,
    +                is_html=False),
    +            rel_type=None,
    +            contains=(),
    +            lang=(),
    +            flags=0),
    +        ),
    +    is_not=False,
    +    is_html=False)
    +```
    +
    +"""
    +from __future__ import annotations
    +import re
    +from typing import Any
    +
    +RE_CLASS = re.compile(r'(?i)[a-z_][_a-z\d.]+\(')
    +RE_PARAM = re.compile(r'(?i)[_a-z][_a-z\d]+=')
    +RE_EMPTY = re.compile(r'\(\)|\[\]|\{\}')
    +RE_LSTRT = re.compile(r'\[')
    +RE_DSTRT = re.compile(r'\{')
    +RE_TSTRT = re.compile(r'\(')
    +RE_LEND = re.compile(r'\]')
    +RE_DEND = re.compile(r'\}')
    +RE_TEND = re.compile(r'\)')
    +RE_INT = re.compile(r'\d+')
    +RE_KWORD = re.compile(r'(?i)[_a-z][_a-z\d.]+')
    +RE_DQSTR = re.compile(r'"(?:\\.|[^"\\])*"')
    +RE_SQSTR = re.compile(r"'(?:\\.|[^'\\])*'")
    +RE_SEP = re.compile(r'\s*(,)\s*')
    +RE_DSEP = re.compile(r'\s*(:)\s*')
    +RE_PSEP = re.compile(r'\s*(\|)\s*')
    +
    +TOKENS = {
    +    'class': RE_CLASS,
    +    'param': RE_PARAM,
    +    'empty': RE_EMPTY,
    +    'lstrt': RE_LSTRT,
    +    'dstrt': RE_DSTRT,
    +    'tstrt': RE_TSTRT,
    +    'lend': RE_LEND,
    +    'dend': RE_DEND,
    +    'tend': RE_TEND,
    +    'sqstr': RE_SQSTR,
    +    'sep': RE_SEP,
    +    'dsep': RE_DSEP,
    +    'psep': RE_PSEP,
    +    'int': RE_INT,
    +    'kword': RE_KWORD,
    +    'dqstr': RE_DQSTR
    +}
    +
    +
    +def pretty(obj: Any) -> str:  # pragma: no cover
    +    """Make the object output string pretty."""
    +
    +    sel = str(obj)
    +    index = 0
    +    end = len(sel) - 1
    +    indent = 0
    +    output = []
    +
    +    while index <= end:
    +        m = None
    +        for k, v in TOKENS.items():
    +            m = v.match(sel, index)
    +
    +            if m:
    +                name = k
    +                index = m.end(0)
    +                if name in ('class', 'lstrt', 'dstrt', 'tstrt'):
    +                    indent += 4
    +                    output.append(f'{m.group(0)}\n{" " * indent}')
    +                elif name in ('param', 'int', 'kword', 'sqstr', 'dqstr', 'empty'):
    +                    output.append(m.group(0))
    +                elif name in ('lend', 'dend', 'tend'):
    +                    indent -= 4
    +                    output.append(m.group(0))
    +                elif name in ('sep',):
    +                    output.append(f'{m.group(1)}\n{" " * indent}')
    +                elif name in ('dsep',):
    +                    output.append(f'{m.group(1)} ')
    +                elif name in ('psep'):
    +                    output.append(f' {m.group(1)} ')
    +                break
    +
    +        # We shouldn't hit this, but if we do, store unrecognized character
    +        if m is None:  # pragma: no cover
    +            output.append(sel[index])
    +            index += 1
    +
    +    return ''.join(output)
    diff --git a/venv/lib/python3.12/site-packages/soupsieve/py.typed b/venv/lib/python3.12/site-packages/soupsieve/py.typed
    new file mode 100644
    index 00000000..e69de29b
    diff --git a/venv/lib/python3.12/site-packages/soupsieve/util.py b/venv/lib/python3.12/site-packages/soupsieve/util.py
    new file mode 100644
    index 00000000..9b2e64df
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/soupsieve/util.py
    @@ -0,0 +1,117 @@
    +"""Utility."""
    +from __future__ import annotations
    +from functools import wraps, lru_cache
    +import warnings
    +import re
    +from typing import Callable, Any
    +
    +DEBUG = 0x00001
    +
    +RE_PATTERN_LINE_SPLIT = re.compile(r'(?:\r\n|(?!\r\n)[\n\r])|$')
    +
    +UC_A = ord('A')
    +UC_Z = ord('Z')
    +
    +
    +@lru_cache(maxsize=512)
    +def lower(string: str) -> str:
    +    """Lower."""
    +
    +    new_string = []
    +    for c in string:
    +        o = ord(c)
    +        new_string.append(chr(o + 32) if UC_A <= o <= UC_Z else c)
    +    return ''.join(new_string)
    +
    +
    +class SelectorSyntaxError(Exception):
    +    """Syntax error in a CSS selector."""
    +
    +    def __init__(self, msg: str, pattern: str | None = None, index: int | None = None) -> None:
    +        """Initialize."""
    +
    +        self.line = None
    +        self.col = None
    +        self.context = None
    +
    +        if pattern is not None and index is not None:
    +            # Format pattern to show line and column position
    +            self.context, self.line, self.col = get_pattern_context(pattern, index)
    +            msg = f'{msg}\n  line {self.line}:\n{self.context}'
    +
    +        super().__init__(msg)
    +
    +
    +def deprecated(message: str, stacklevel: int = 2) -> Callable[..., Any]:  # pragma: no cover
    +    """
    +    Raise a `DeprecationWarning` when wrapped function/method is called.
    +
    +    Usage:
    +
    +        @deprecated("This method will be removed in version X; use Y instead.")
    +        def some_method()"
    +            pass
    +    """
    +
    +    def _wrapper(func: Callable[..., Any]) -> Callable[..., Any]:
    +        @wraps(func)
    +        def _deprecated_func(*args: Any, **kwargs: Any) -> Any:
    +            warnings.warn(
    +                f"'{func.__name__}' is deprecated. {message}",
    +                category=DeprecationWarning,
    +                stacklevel=stacklevel
    +            )
    +            return func(*args, **kwargs)
    +        return _deprecated_func
    +    return _wrapper
    +
    +
    +def warn_deprecated(message: str, stacklevel: int = 2) -> None:  # pragma: no cover
    +    """Warn deprecated."""
    +
    +    warnings.warn(
    +        message,
    +        category=DeprecationWarning,
    +        stacklevel=stacklevel
    +    )
    +
    +
    +def get_pattern_context(pattern: str, index: int) -> tuple[str, int, int]:
    +    """Get the pattern context."""
    +
    +    last = 0
    +    current_line = 1
    +    col = 1
    +    text = []  # type: list[str]
    +    line = 1
    +    offset = None  # type: int | None
    +
    +    # Split pattern by newline and handle the text before the newline
    +    for m in RE_PATTERN_LINE_SPLIT.finditer(pattern):
    +        linetext = pattern[last:m.start(0)]
    +        if not len(m.group(0)) and not len(text):
    +            indent = ''
    +            offset = -1
    +            col = index - last + 1
    +        elif last <= index < m.end(0):
    +            indent = '--> '
    +            offset = (-1 if index > m.start(0) else 0) + 3
    +            col = index - last + 1
    +        else:
    +            indent = '    '
    +            offset = None
    +        if len(text):
    +            # Regardless of whether we are presented with `\r\n`, `\r`, or `\n`,
    +            # we will render the output with just `\n`. We will still log the column
    +            # correctly though.
    +            text.append('\n')
    +        text.append(f'{indent}{linetext}')
    +        if offset is not None:
    +            text.append('\n')
    +            text.append(' ' * (col + offset) + '^')
    +            line = current_line
    +
    +        current_line += 1
    +        last = m.end(0)
    +
    +    return ''.join(text), line, col
    diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/INSTALLER
    new file mode 100644
    index 00000000..a1b589e3
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/INSTALLER
    @@ -0,0 +1 @@
    +pip
    diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/METADATA
    similarity index 99%
    rename from venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/METADATA
    rename to venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/METADATA
    index 74b8e598..52092f99 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/METADATA
    +++ b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/METADATA
    @@ -1,6 +1,6 @@
     Metadata-Version: 2.4
     Name: uvicorn
    -Version: 0.44.0
    +Version: 0.46.0
     Summary: The lightning-fast ASGI server.
     Project-URL: Changelog, https://uvicorn.dev/release-notes
     Project-URL: Funding, https://github.com/sponsors/encode
    diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/RECORD
    similarity index 79%
    rename from venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/RECORD
    rename to venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/RECORD
    index 2d818e2f..431c5107 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/RECORD
    +++ b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/RECORD
    @@ -1,12 +1,12 @@
    -../../../bin/uvicorn,sha256=MJE12ElSEd9IEH3JpF7Zk5Gz7Q2-dCuujRZ_OVqmz1E,236
    -uvicorn-0.44.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
    -uvicorn-0.44.0.dist-info/METADATA,sha256=lziras7kRHv68p_v8P4xNY4Jv2VAjQgxbjSENLj8SgI,6738
    -uvicorn-0.44.0.dist-info/RECORD,,
    -uvicorn-0.44.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    -uvicorn-0.44.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
    -uvicorn-0.44.0.dist-info/entry_points.txt,sha256=FW1w-hkc9QgwaGoovMvm0ZY73w_NcycWdGAUfDsNGxw,46
    -uvicorn-0.44.0.dist-info/licenses/LICENSE.md,sha256=7-Gs8-YvuZwoiw7HPlp3O3Jo70Mg_nV-qZQhTktjw3E,1526
    -uvicorn/__init__.py,sha256=kRrHXi1vUY1QA0XGz3Urt4HncrkKwgYzml0xHcO9lbY,147
    +../../../bin/uvicorn,sha256=3J1JFAYNUl4J_qURILfK5a-MMoXVxXtedIeIemCoNoo,194
    +uvicorn-0.46.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
    +uvicorn-0.46.0.dist-info/METADATA,sha256=_LNAJiFqD-6_rveRratLjB54yfw1vELY04VuVFZdxsQ,6738
    +uvicorn-0.46.0.dist-info/RECORD,,
    +uvicorn-0.46.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +uvicorn-0.46.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
    +uvicorn-0.46.0.dist-info/entry_points.txt,sha256=FW1w-hkc9QgwaGoovMvm0ZY73w_NcycWdGAUfDsNGxw,46
    +uvicorn-0.46.0.dist-info/licenses/LICENSE.md,sha256=7-Gs8-YvuZwoiw7HPlp3O3Jo70Mg_nV-qZQhTktjw3E,1526
    +uvicorn/__init__.py,sha256=OtQM_m_k7S8NhR-vBk2mJODe1TS5HoRomdqFBy1rA3Y,147
     uvicorn/__main__.py,sha256=DQizy6nKP0ywhPpnCHgmRDYIMfcqZKVEzNIWQZjqtVQ,62
     uvicorn/__pycache__/__init__.cpython-312.pyc,,
     uvicorn/__pycache__/__main__.cpython-312.pyc,,
    @@ -22,7 +22,7 @@ uvicorn/__pycache__/workers.cpython-312.pyc,,
     uvicorn/_compat.py,sha256=6X49c9ovzmMHir2S3syinOQMzSPBcp8MNuI9ZQrbYiU,2916
     uvicorn/_subprocess.py,sha256=fQ4JTajuXay6mVc8KQNGfXu6JHoab46aS6kyIMWJMPk,2776
     uvicorn/_types.py,sha256=JDM6E-hqZ6o_cSZvjDULjAv77PFH2pLe_Vgqshe8qH4,7677
    -uvicorn/config.py,sha256=aYK4Nc-M0uvbT4LmgIvY8c00kekEz0P4BWhG8VMojcg,22095
    +uvicorn/config.py,sha256=So8R5moW3aUZqm3HVXaD9uF409hgK-yoCBGlVksqVss,22440
     uvicorn/importer.py,sha256=nRt0QQ3qpi264-n_mR0l55C2ddM8nowTNzT1jsWaam8,1128
     uvicorn/lifespan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
     uvicorn/lifespan/__pycache__/__init__.cpython-312.pyc,,
    @@ -39,7 +39,7 @@ uvicorn/loops/__pycache__/uvloop.cpython-312.pyc,,
     uvicorn/loops/asyncio.py,sha256=a8wO4fB4pCk-RLICq7CnGr_cxQI7MBLJj-Go4UQldD8,333
     uvicorn/loops/auto.py,sha256=pdTsMcqkbE8fvdNgLs1y4c6hsuN598mZOSOlC0a23S4,566
     uvicorn/loops/uvloop.py,sha256=Ca-TL-W8tbdEW5uvL_GrMih7G8e_t_vhNevi2NKYjEU,236
    -uvicorn/main.py,sha256=e3CTIV0c80cp1639Ttnej7b5uvdtgLLWtSOcHU8Gu54,18183
    +uvicorn/main.py,sha256=SknpQQKEhswztsnGCUIx_17bdZ1MN6rG5xhotA-PzkU,18563
     uvicorn/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
     uvicorn/middleware/__pycache__/__init__.cpython-312.pyc,,
     uvicorn/middleware/__pycache__/asgi2.cpython-312.pyc,,
    @@ -48,7 +48,7 @@ uvicorn/middleware/__pycache__/proxy_headers.cpython-312.pyc,,
     uvicorn/middleware/__pycache__/wsgi.cpython-312.pyc,,
     uvicorn/middleware/asgi2.py,sha256=YQrQNm3RehFts3mzk3k4yw8aD8Egtj0tRS3N45YkQa0,394
     uvicorn/middleware/message_logger.py,sha256=IHEZUSnFNaMFUFdwtZO3AuFATnYcSor-gVtOjbCzt8M,2859
    -uvicorn/middleware/proxy_headers.py,sha256=yQG3ThmZhRh9jp-AId7DQhb5WVl6to0hD1G0DPrtLow,5790
    +uvicorn/middleware/proxy_headers.py,sha256=Js5fel07IFHFMsSsPDaRiSjzY_KecjeD8w_D-Xi8nRE,6759
     uvicorn/middleware/wsgi.py,sha256=VnbnkdCL3_ctLWzk1j39xvxysuoAB3kSYmi8TsOLpNs,7125
     uvicorn/protocols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
     uvicorn/protocols/__pycache__/__init__.cpython-312.pyc,,
    @@ -61,8 +61,8 @@ uvicorn/protocols/http/__pycache__/h11_impl.cpython-312.pyc,,
     uvicorn/protocols/http/__pycache__/httptools_impl.cpython-312.pyc,,
     uvicorn/protocols/http/auto.py,sha256=YfXGyzWTaaE2p_jkTPWrJCXsxEaQnC3NK0-G7Wgmnls,403
     uvicorn/protocols/http/flow_control.py,sha256=050WVg31EvPOkHwynCoMP1zXFl_vO3U4durlc5vyp4U,1701
    -uvicorn/protocols/http/h11_impl.py,sha256=EtdsPJhAaCa0JN11oxHu0EfbVhRNkHezDVrSGqxA6m8,21210
    -uvicorn/protocols/http/httptools_impl.py,sha256=HI1brMIfs3kfwLbu7PVNpE0X4JKzqOg1XlgdhrGaPsk,22370
    +uvicorn/protocols/http/h11_impl.py,sha256=K7SHLVsYORBde6cUGNwDt9rbS2vPfUbLkzbVOnEYeDk,21161
    +uvicorn/protocols/http/httptools_impl.py,sha256=EXGuEjcGGvq0DgwpnLm4YjfMGDHofI804qbfw14naLM,22286
     uvicorn/protocols/utils.py,sha256=1hKdDCmo_GtGrK1zZfa70jL7Hr3mDE8RnL69O4rHQRg,2058
     uvicorn/protocols/websockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
     uvicorn/protocols/websockets/__pycache__/__init__.cpython-312.pyc,,
    @@ -72,8 +72,8 @@ uvicorn/protocols/websockets/__pycache__/websockets_sansio_impl.cpython-312.pyc,
     uvicorn/protocols/websockets/__pycache__/wsproto_impl.cpython-312.pyc,,
     uvicorn/protocols/websockets/auto.py,sha256=pAEMIsyXQ12CL6ffaEJQ2cQEC8yAr8sr7bC0H6hzBfs,590
     uvicorn/protocols/websockets/websockets_impl.py,sha256=O2EW1zGQmcB65-kbxkA0XnU-sbSbPSsUKvzh8N-QQoQ,14849
    -uvicorn/protocols/websockets/websockets_sansio_impl.py,sha256=VkCOHaXLpbHoQruEkk20VozRo0mC_ma3uC8co06CSPI,20176
    -uvicorn/protocols/websockets/wsproto_impl.py,sha256=3k_1U3Qc3cUIuFcLlS7bYA1gjskPlKBkqcVgOuLHbGM,14702
    +uvicorn/protocols/websockets/websockets_sansio_impl.py,sha256=KnCKcpF7VEpu5wxudtk0u7SmwY2O3dk52Q8rrQ614Bs,20198
    +uvicorn/protocols/websockets/wsproto_impl.py,sha256=JKmDJJ-tcb9PVvNkpP_wz-RsvonfwmSbDKWEX7GSNM4,19538
     uvicorn/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
     uvicorn/server.py,sha256=fXk3__HBf1g-oh_4_OXIpb8tnTyafxuWRiEet6SQEvM,13368
     uvicorn/supervisors/__init__.py,sha256=wT8eOEIqT1yWQgytZtv5taWMul7xoTIY0xm1m4oyPTw,507
    diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/REQUESTED b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/REQUESTED
    new file mode 100644
    index 00000000..e69de29b
    diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/WHEEL
    new file mode 100644
    index 00000000..b1b94fd5
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/WHEEL
    @@ -0,0 +1,4 @@
    +Wheel-Version: 1.0
    +Generator: hatchling 1.29.0
    +Root-Is-Purelib: true
    +Tag: py3-none-any
    diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/entry_points.txt
    similarity index 100%
    rename from venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/entry_points.txt
    rename to venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/entry_points.txt
    diff --git a/venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/licenses/LICENSE.md b/venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/licenses/LICENSE.md
    similarity index 100%
    rename from venv/lib/python3.12/site-packages/uvicorn-0.44.0.dist-info/licenses/LICENSE.md
    rename to venv/lib/python3.12/site-packages/uvicorn-0.46.0.dist-info/licenses/LICENSE.md
    diff --git a/venv/lib/python3.12/site-packages/uvicorn/__init__.py b/venv/lib/python3.12/site-packages/uvicorn/__init__.py
    index 71709160..03cf1057 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn/__init__.py
    +++ b/venv/lib/python3.12/site-packages/uvicorn/__init__.py
    @@ -1,5 +1,5 @@
     from uvicorn.config import Config
     from uvicorn.main import Server, main, run
     
    -__version__ = "0.44.0"
    +__version__ = "0.46.0"
     __all__ = ["main", "run", "Config", "Server"]
    diff --git a/venv/lib/python3.12/site-packages/uvicorn/config.py b/venv/lib/python3.12/site-packages/uvicorn/config.py
    index d70ba642..a87db7b4 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn/config.py
    +++ b/venv/lib/python3.12/site-packages/uvicorn/config.py
    @@ -193,7 +193,7 @@ class Config:
             ws_per_message_deflate: bool = True,
             lifespan: LifespanType = "auto",
             env_file: str | os.PathLike[str] | None = None,
    -        log_config: dict[str, Any] | str | RawConfigParser | IO[Any] | None = LOGGING_CONFIG,
    +        log_config: dict[str, Any] | str | os.PathLike[str] | RawConfigParser | IO[Any] | None = LOGGING_CONFIG,
             log_level: str | int | None = None,
             access_log: bool = True,
             use_colors: bool | None = None,
    @@ -228,6 +228,7 @@ class Config:
             headers: list[tuple[str, str]] | None = None,
             factory: bool = False,
             h11_max_incomplete_event_size: int | None = None,
    +        reset_contextvars: bool = False,
         ):
             self.app = app
             self.host = host
    @@ -275,6 +276,7 @@ class Config:
             self.encoded_headers: list[tuple[bytes, bytes]] = []
             self.factory = factory
             self.h11_max_incomplete_event_size = h11_max_incomplete_event_size
    +        self.reset_contextvars = reset_contextvars
     
             self.loaded = False
             self.configure_logging()
    @@ -365,6 +367,9 @@ class Config:
             logging.addLevelName(TRACE_LOG_LEVEL, "TRACE")
     
             if self.log_config is not None:
    +            if isinstance(self.log_config, os.PathLike):
    +                self.log_config = os.fspath(self.log_config)
    +
                 if isinstance(self.log_config, dict):
                     if self.use_colors in (True, False):
                         self.log_config["formatters"]["default"]["use_colors"] = self.use_colors
    @@ -375,9 +380,12 @@ class Config:
                         loaded_config = json.load(file)
                         logging.config.dictConfig(loaded_config)
                 elif isinstance(self.log_config, str) and self.log_config.endswith((".yaml", ".yml")):
    -                # Install the PyYAML package or the uvicorn[standard] optional
    -                # dependencies to enable this functionality.
    -                import yaml
    +                try:
    +                    import yaml
    +                except ImportError as e:
    +                    raise ImportError(
    +                        "Install the PyYAML package or uvicorn[standard] to use `--log-config` with YAML files."
    +                    ) from e
     
                     with open(self.log_config) as file:
                         loaded_config = yaml.safe_load(file)
    @@ -389,7 +397,7 @@ class Config:
     
             if self.log_level is not None:
                 if isinstance(self.log_level, str):
    -                log_level = LOG_LEVELS[self.log_level]
    +                log_level = LOG_LEVELS[self.log_level.lower()]
                 else:
                     log_level = self.log_level
                 logging.getLogger("uvicorn.error").setLevel(log_level)
    diff --git a/venv/lib/python3.12/site-packages/uvicorn/main.py b/venv/lib/python3.12/site-packages/uvicorn/main.py
    index 93c85cb7..30372734 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn/main.py
    +++ b/venv/lib/python3.12/site-packages/uvicorn/main.py
    @@ -372,6 +372,13 @@ def print_version(ctx: click.Context, param: click.Parameter, value: bool) -> No
         default=None,
         help="For h11, the maximum number of bytes to buffer of an incomplete event.",
     )
    +@click.option(
    +    "--reset-contextvars",
    +    is_flag=True,
    +    default=False,
    +    help="Run each ASGI request in a fresh contextvars.Context. Hides context set in the lifespan.",
    +    show_default=True,
    +)
     @click.option(
         "--factory",
         is_flag=True,
    @@ -428,6 +435,7 @@ def main(
         use_colors: bool,
         app_dir: str,
         h11_max_incomplete_event_size: int | None,
    +    reset_contextvars: bool,
         factory: bool,
     ) -> None:
         run(
    @@ -480,6 +488,7 @@ def main(
             factory=factory,
             app_dir=app_dir,
             h11_max_incomplete_event_size=h11_max_incomplete_event_size,
    +        reset_contextvars=reset_contextvars,
         )
     
     
    @@ -507,7 +516,7 @@ def run(
         reload_delay: float = 0.25,
         workers: int | None = None,
         env_file: str | os.PathLike[str] | None = None,
    -    log_config: dict[str, Any] | str | RawConfigParser | IO[Any] | None = LOGGING_CONFIG,
    +    log_config: dict[str, Any] | str | os.PathLike[str] | RawConfigParser | IO[Any] | None = LOGGING_CONFIG,
         log_level: str | int | None = None,
         access_log: bool = True,
         proxy_headers: bool = True,
    @@ -534,6 +543,7 @@ def run(
         app_dir: str | None = None,
         factory: bool = False,
         h11_max_incomplete_event_size: int | None = None,
    +    reset_contextvars: bool = False,
     ) -> None:
         if app_dir is not None:
             sys.path.insert(0, app_dir)
    @@ -587,6 +597,7 @@ def run(
             use_colors=use_colors,
             factory=factory,
             h11_max_incomplete_event_size=h11_max_incomplete_event_size,
    +        reset_contextvars=reset_contextvars,
         )
         server = Server(config=config)
     
    diff --git a/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py b/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py
    index b9ee14a6..1b97fbfc 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py
    +++ b/venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py
    @@ -45,16 +45,12 @@ class ProxyHeadersMiddleware:
     
                 if b"x-forwarded-for" in headers:
                     x_forwarded_for = headers[b"x-forwarded-for"].decode("latin1")
    -                host = self.trusted_hosts.get_trusted_client_host(x_forwarded_for)
    +                host, port = self.trusted_hosts.get_trusted_client_address(x_forwarded_for)
     
                     if host:
                         # If the x-forwarded-for header is empty then host is an empty string.
                         # Only set the client if we actually got something usable.
                         # See: https://github.com/Kludex/uvicorn/issues/1068
    -
    -                    # We've lost the connecting client's port information by now,
    -                    # so only include the host.
    -                    port = 0
                         scope["client"] = (host, port)
     
             return await self.app(scope, receive, send)
    @@ -64,6 +60,41 @@ def _parse_raw_hosts(value: str) -> list[str]:
         return [item.strip() for item in value.split(",")]
     
     
    +def _parse_host_port(value: str) -> tuple[str, int]:
    +    """Parse a forwarded host value into host and optional port.
    +
    +    Accepts bare IPs, IPv4 `host:port`, and bracketed IPv6 `[host]:port`.
    +    Any unrecognized or malformed value is treated conservatively and returned
    +    without a port so trust checks do not silently normalize arbitrary input.
    +    """
    +
    +    if value.startswith("["):
    +        bracket_end = value.find("]")
    +        if bracket_end == -1:
    +            return value, 0
    +
    +        host = value[1:bracket_end]
    +        remainder = value[bracket_end + 1 :]
    +        if not remainder:
    +            return host, 0
    +        if not remainder.startswith(":"):
    +            return value, 0
    +
    +        try:
    +            return host, int(remainder[1:])
    +        except ValueError:
    +            return host, 0
    +
    +    if value.count(":") == 1:
    +        host, port = value.rsplit(":", 1)
    +        try:
    +            return host, int(port)
    +        except ValueError:
    +            return value, 0
    +
    +    return value, 0
    +
    +
     class _TrustedHosts:
         """Container for trusted hosts and networks"""
     
    @@ -122,21 +153,22 @@ class _TrustedHosts:
             except ValueError:
                 return host in self.trusted_literals
     
    -    def get_trusted_client_host(self, x_forwarded_for: str) -> str:
    -        """Extract the client host from x_forwarded_for header
    +    def get_trusted_client_address(self, x_forwarded_for: str) -> tuple[str, int]:
    +        """Extract the client address from x_forwarded_for header.
     
             In general this is the first "untrusted" host in the forwarded for list.
             """
             x_forwarded_for_hosts = _parse_raw_hosts(x_forwarded_for)
     
             if self.always_trust:
    -            return x_forwarded_for_hosts[0]
    +            return _parse_host_port(x_forwarded_for_hosts[0])
     
             # Note: each proxy appends to the header list so check it in reverse order
    -        for host in reversed(x_forwarded_for_hosts):
    +        for host_port in reversed(x_forwarded_for_hosts):
    +            host, port = _parse_host_port(host_port)
                 if host not in self:
    -                return host
    +                return host, port
     
             # All hosts are trusted meaning that the client was also a trusted proxy
             # See https://github.com/Kludex/uvicorn/issues/1068#issuecomment-855371576
    -        return x_forwarded_for_hosts[0]
    +        return _parse_host_port(x_forwarded_for_hosts[0])
    diff --git a/venv/lib/python3.12/site-packages/uvicorn/protocols/http/h11_impl.py b/venv/lib/python3.12/site-packages/uvicorn/protocols/http/h11_impl.py
    index 2ad140c8..cb68ea0e 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn/protocols/http/h11_impl.py
    +++ b/venv/lib/python3.12/site-packages/uvicorn/protocols/http/h11_impl.py
    @@ -250,13 +250,16 @@ class H11Protocol(asyncio.Protocol):
                         message_event=asyncio.Event(),
                         on_response=self.on_response_complete,
                     )
    -                # For the asyncio loop, we need to explicitly start with an empty context
    -                # as it can be polluted from previous ASGI runs.
    -                # See https://github.com/python/cpython/issues/140947 for details.
    -                if sys.version_info >= (3, 11):  # pragma: py-lt-311
    -                    task = self.loop.create_task(self.cycle.run_asgi(app), context=contextvars.Context())
    -                else:  # pragma: py-gte-311
    -                    task = contextvars.Context().run(self.loop.create_task, self.cycle.run_asgi(app))
    +                if self.config.reset_contextvars:
    +                    # Opt-in workaround for https://github.com/python/cpython/issues/140947:
    +                    # asyncio can leak context vars between tasks. Hides context set in the
    +                    # lifespan or by external instrumentation.
    +                    if sys.version_info >= (3, 11):  # pragma: py-lt-311
    +                        task = self.loop.create_task(self.cycle.run_asgi(app), context=contextvars.Context())
    +                    else:  # pragma: py-gte-311
    +                        task = contextvars.Context().run(self.loop.create_task, self.cycle.run_asgi(app))
    +                else:
    +                    task = self.loop.create_task(self.cycle.run_asgi(app))
                     task.add_done_callback(self.tasks.discard)
                     self.tasks.add(task)
     
    @@ -344,8 +347,6 @@ class H11Protocol(asyncio.Protocol):
                 self.transport.close()
             else:
                 self.cycle.keep_alive = False
    -            self.cycle.shutting_down = True
    -            self.cycle.message_event.set()
     
         def pause_writing(self) -> None:
             """
    @@ -399,7 +400,6 @@ class RequestResponseCycle:
             self.disconnected = False
             self.keep_alive = True
             self.waiting_for_100_continue = conn.they_are_waiting_for_100_continue
    -        self.shutting_down = False
     
             # Request state
             self.body = bytearray()
    @@ -432,9 +432,8 @@ class RequestResponseCycle:
                     self.logger.error(msg)
                     await self.send_500_response()
                 elif not self.response_complete and not self.disconnected:
    -                if not self.shutting_down:
    -                    msg = "ASGI callable returned without completing response."
    -                    self.logger.error(msg)
    +                msg = "ASGI callable returned without completing response."
    +                self.logger.error(msg)
                     self.transport.close()
             finally:
                 self.on_response = lambda: None
    @@ -532,12 +531,12 @@ class RequestResponseCycle:
                 self.transport.write(output)
                 self.waiting_for_100_continue = False
     
    -        if not self.disconnected and not self.response_complete and not self.shutting_down:
    +        if not self.disconnected and not self.response_complete:
                 self.flow.resume_reading()
                 await self.message_event.wait()
                 self.message_event.clear()
     
    -        if self.disconnected or self.response_complete or self.shutting_down:
    +        if self.disconnected or self.response_complete:
                 return {"type": "http.disconnect"}
     
             message: HTTPRequestEvent = {"type": "http.request", "body": bytes(self.body), "more_body": self.more_body}
    diff --git a/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py b/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py
    index a23c2fb6..1aa49d85 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py
    +++ b/venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py
    @@ -289,20 +289,26 @@ class HttpToolsProtocol(asyncio.Protocol):
             )
             if existing_cycle is None or existing_cycle.response_complete:
                 # Standard case - start processing the request.
    -            # For the asyncio loop, we need to explicitly start with an empty context
    -            # as it can be polluted from previous ASGI runs.
    -            # See https://github.com/python/cpython/issues/140947 for details.
    -            if sys.version_info >= (3, 11):  # pragma: py-lt-311
    -                task = self.loop.create_task(self.cycle.run_asgi(app), context=contextvars.Context())
    -            else:  # pragma: py-gte-311
    -                task = contextvars.Context().run(self.loop.create_task, self.cycle.run_asgi(app))
    -            task.add_done_callback(self.tasks.discard)
    -            self.tasks.add(task)
    +            self._start_asgi_task(self.cycle, app)
             else:
                 # Pipelined HTTP requests need to be queued up.
                 self.flow.pause_reading()
                 self.pipeline.appendleft((self.cycle, app))
     
    +    def _start_asgi_task(self, cycle: RequestResponseCycle, app: ASGI3Application) -> None:
    +        if self.config.reset_contextvars:
    +            # Opt-in workaround for https://github.com/python/cpython/issues/140947:
    +            # asyncio can leak context vars between tasks. Hides context set in the
    +            # lifespan or by external instrumentation.
    +            if sys.version_info >= (3, 11):  # pragma: py-lt-311
    +                task = self.loop.create_task(cycle.run_asgi(app), context=contextvars.Context())
    +            else:  # pragma: py-gte-311
    +                task = contextvars.Context().run(self.loop.create_task, cycle.run_asgi(app))
    +        else:
    +            task = self.loop.create_task(cycle.run_asgi(app))
    +        task.add_done_callback(self.tasks.discard)
    +        self.tasks.add(task)
    +
         def on_body(self, body: bytes) -> None:
             if (self.parser.should_upgrade() and self._should_upgrade()) or self.cycle.response_complete:
                 return
    @@ -333,9 +339,7 @@ class HttpToolsProtocol(asyncio.Protocol):
             # Keep-Alive timeout instead.
             if self.pipeline:
                 cycle, app = self.pipeline.pop()
    -            task = self.loop.create_task(cycle.run_asgi(app))
    -            task.add_done_callback(self.tasks.discard)
    -            self.tasks.add(task)
    +            self._start_asgi_task(cycle, app)
             else:
                 self.timeout_keep_alive_task = self.loop.call_later(
                     self.timeout_keep_alive, self.timeout_keep_alive_handler
    @@ -349,8 +353,6 @@ class HttpToolsProtocol(asyncio.Protocol):
                 self.transport.close()
             else:
                 self.cycle.keep_alive = False
    -            self.cycle.shutting_down = True
    -            self.cycle.message_event.set()
     
         def pause_writing(self) -> None:
             """
    @@ -402,7 +404,6 @@ class RequestResponseCycle:
             self.disconnected = False
             self.keep_alive = keep_alive
             self.waiting_for_100_continue = expect_100_continue
    -        self.shutting_down = False
     
             # Request state
             self.body = bytearray()
    @@ -437,9 +438,8 @@ class RequestResponseCycle:
                     self.logger.error(msg)
                     await self.send_500_response()
                 elif not self.response_complete and not self.disconnected:
    -                if not self.shutting_down:
    -                    msg = "ASGI callable returned without completing response."
    -                    self.logger.error(msg)
    +                msg = "ASGI callable returned without completing response."
    +                self.logger.error(msg)
                     self.transport.close()
             finally:
                 self.on_response = lambda: None
    @@ -564,12 +564,12 @@ class RequestResponseCycle:
                 self.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")
                 self.waiting_for_100_continue = False
     
    -        if not self.disconnected and not self.response_complete and not self.shutting_down:
    +        if not self.disconnected and not self.response_complete:
                 self.flow.resume_reading()
                 await self.message_event.wait()
                 self.message_event.clear()
     
    -        if self.disconnected or self.response_complete or self.shutting_down:
    +        if self.disconnected or self.response_complete:
                 return {"type": "http.disconnect"}
             message: HTTPRequestEvent = {"type": "http.request", "body": bytes(self.body), "more_body": self.more_body}
             self.body = bytearray()
    diff --git a/venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/websockets_sansio_impl.py b/venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/websockets_sansio_impl.py
    index bb0afe9d..036b6ee0 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/websockets_sansio_impl.py
    +++ b/venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/websockets_sansio_impl.py
    @@ -105,7 +105,7 @@ class WebSocketsSansIOProtocol(asyncio.Protocol):
             self.last_ping_rtt: float = 0.0
     
             # Buffers
    -        self.bytes = b""
    +        self.bytes = bytearray()
     
         def connection_made(self, transport: BaseTransport) -> None:
             """Called when a connection is made."""
    @@ -216,19 +216,19 @@ class WebSocketsSansIOProtocol(asyncio.Protocol):
             task.add_done_callback(self.on_task_complete)
             self.tasks.add(task)
     
    -    def handle_cont(self, event: Frame) -> None:  # pragma: no cover
    -        self.bytes += event.data
    +    def handle_cont(self, event: Frame) -> None:
    +        self.bytes.extend(event.data)
             if event.fin:
                 self.send_receive_event_to_app()
     
         def handle_text(self, event: Frame) -> None:
    -        self.bytes = event.data
    +        self.bytes = bytearray(event.data)
             self.curr_msg_data_type: Literal["text", "bytes"] = "text"
             if event.fin:
                 self.send_receive_event_to_app()
     
         def handle_bytes(self, event: Frame) -> None:
    -        self.bytes = event.data
    +        self.bytes = bytearray(event.data)
             self.curr_msg_data_type = "bytes"
             if event.fin:
                 self.send_receive_event_to_app()
    @@ -243,7 +243,7 @@ class WebSocketsSansIOProtocol(asyncio.Protocol):
                     self.handle_parser_exception()
                     return
             else:
    -            self.queue.put_nowait({"type": "websocket.receive", "bytes": self.bytes})
    +            self.queue.put_nowait({"type": "websocket.receive", "bytes": bytes(self.bytes)})
             if not self.read_paused:
                 self.read_paused = True
                 self.transport.pause_reading()
    diff --git a/venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/wsproto_impl.py b/venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/wsproto_impl.py
    index 9727bdab..adca63f9 100644
    --- a/venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/wsproto_impl.py
    +++ b/venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/wsproto_impl.py
    @@ -2,6 +2,10 @@ from __future__ import annotations
     
     import asyncio
     import logging
    +import random
    +import struct
    +from asyncio import TimerHandle
    +from io import BytesIO, StringIO
     from typing import Any, Literal, cast
     from urllib.parse import unquote
     
    @@ -11,12 +15,7 @@ from wsproto.connection import ConnectionState
     from wsproto.extensions import Extension, PerMessageDeflate
     from wsproto.utilities import LocalProtocolError, RemoteProtocolError
     
    -from uvicorn._types import (
    -    ASGI3Application,
    -    ASGISendEvent,
    -    WebSocketEvent,
    -    WebSocketScope,
    -)
    +from uvicorn._types import ASGI3Application, ASGISendEvent, WebSocketEvent, WebSocketReceiveEvent, WebSocketScope
     from uvicorn.config import Config
     from uvicorn.logging import TRACE_LOG_LEVEL
     from uvicorn.protocols.utils import (
    @@ -30,6 +29,36 @@ from uvicorn.protocols.utils import (
     from uvicorn.server import ServerState
     
     
    +class FrameTooLargeError(Exception):
    +    """Raised when accumulated websocket message bytes exceed `ws_max_size`."""
    +
    +
    +class WebsocketBuffer:
    +    def __init__(self, max_length: int) -> None:
    +        self.value: BytesIO | StringIO | None = None
    +        self.length = 0
    +        self.max_length = max_length
    +
    +    def extend(self, event: events.TextMessage | events.BytesMessage) -> None:
    +        if self.value is None:
    +            self.value = StringIO() if isinstance(event, events.TextMessage) else BytesIO()
    +        self.value.write(event.data)  # type: ignore[arg-type]
    +        # `ws_max_size` is a byte budget, so count UTF-8 bytes for text.
    +        self.length += len(event.data.encode()) if isinstance(event, events.TextMessage) else len(event.data)
    +        if self.length > self.max_length:
    +            raise FrameTooLargeError
    +
    +    def clear(self) -> None:
    +        self.value = None
    +        self.length = 0
    +
    +    def to_message(self) -> WebSocketReceiveEvent:
    +        if isinstance(self.value, StringIO):
    +            return {"type": "websocket.receive", "text": self.value.getvalue()}
    +        assert isinstance(self.value, BytesIO)
    +        return {"type": "websocket.receive", "bytes": self.value.getvalue()}
    +
    +
     class WSProtocol(asyncio.Protocol):
         def __init__(
             self,
    @@ -73,15 +102,21 @@ class WSProtocol(asyncio.Protocol):
             self.writable = asyncio.Event()
             self.writable.set()
     
    -        # Buffers
    -        self.bytes = b""
    -        self.text = ""
    +        # Keepalive state
    +        self.ping_interval = config.ws_ping_interval
    +        self.ping_timeout = config.ws_ping_timeout
    +        self.ping_timer: TimerHandle | None = None
    +        self.pong_timer: TimerHandle | None = None
    +        self.pending_ping_payload: bytes | None = None
    +        self.ping_sent_at: float = 0.0
    +        self.last_ping_rtt: float = 0.0
    +
    +        # Buffer
    +        self.buffer = WebsocketBuffer(self.config.ws_max_size)
     
         # Protocol interface
     
    -    def connection_made(  # type: ignore[override]
    -        self, transport: asyncio.Transport
    -    ) -> None:
    +    def connection_made(self, transport: asyncio.Transport) -> None:  # type: ignore[override]
             self.connections.add(self)
             self.transport = transport
             self.server = get_local_addr(transport)
    @@ -93,6 +128,7 @@ class WSProtocol(asyncio.Protocol):
                 self.logger.log(TRACE_LOG_LEVEL, "%sWebSocket connection made", prefix)
     
         def connection_lost(self, exc: Exception | None) -> None:
    +        self.stop_keepalive()
             code = 1005 if self.handshake_complete else 1006
             self.queue.put_nowait({"type": "websocket.disconnect", "code": code})
             self.connections.remove(self)
    @@ -120,16 +156,18 @@ class WSProtocol(asyncio.Protocol):
     
         def handle_events(self) -> None:
             for event in self.conn.events():
    +            if self.close_sent:
    +                return
                 if isinstance(event, events.Request):
                     self.handle_connect(event)
    -            elif isinstance(event, events.TextMessage):
    -                self.handle_text(event)
    -            elif isinstance(event, events.BytesMessage):
    -                self.handle_bytes(event)
    +            elif isinstance(event, (events.TextMessage, events.BytesMessage)):
    +                self.handle_message(event)
                 elif isinstance(event, events.CloseConnection):
                     self.handle_close(event)
                 elif isinstance(event, events.Ping):
                     self.handle_ping(event)
    +            elif isinstance(event, events.Pong):
    +                self.handle_pong(event)
     
         def pause_writing(self) -> None:
             """
    @@ -144,6 +182,7 @@ class WSProtocol(asyncio.Protocol):
             self.writable.set()  # pragma: full coverage
     
         def shutdown(self) -> None:
    +        self.stop_keepalive()
             if self.handshake_complete:
                 self.queue.put_nowait({"type": "websocket.disconnect", "code": 1012})
                 output = self.conn.send(wsproto.events.CloseConnection(code=1012))
    @@ -185,21 +224,20 @@ class WSProtocol(asyncio.Protocol):
             task.add_done_callback(self.on_task_complete)
             self.tasks.add(task)
     
    -    def handle_text(self, event: events.TextMessage) -> None:
    -        self.text += event.data
    +    def handle_message(self, event: events.TextMessage | events.BytesMessage) -> None:
    +        try:
    +            self.buffer.extend(event)
    +        except FrameTooLargeError:
    +            self.close_sent = True
    +            reason = f"Message exceeds the maximum size ({self.config.ws_max_size} bytes)"
    +            self.queue.put_nowait({"type": "websocket.disconnect", "code": 1009, "reason": reason})
    +            if not self.transport.is_closing():
    +                self.transport.write(self.conn.send(wsproto.events.CloseConnection(code=1009, reason=reason)))
    +                self.transport.close()
    +            return
             if event.message_finished:
    -            self.queue.put_nowait({"type": "websocket.receive", "text": self.text})
    -            self.text = ""
    -            if not self.read_paused:
    -                self.read_paused = True
    -                self.transport.pause_reading()
    -
    -    def handle_bytes(self, event: events.BytesMessage) -> None:
    -        self.bytes += event.data
    -        # todo: we may want to guard the size of self.bytes and self.text
    -        if event.message_finished:
    -            self.queue.put_nowait({"type": "websocket.receive", "bytes": self.bytes})
    -            self.bytes = b""
    +            self.queue.put_nowait(self.buffer.to_message())
    +            self.buffer.clear()
                 if not self.read_paused:
                     self.read_paused = True
                     self.transport.pause_reading()
    @@ -213,6 +251,65 @@ class WSProtocol(asyncio.Protocol):
         def handle_ping(self, event: events.Ping) -> None:
             self.transport.write(self.conn.send(event.response()))
     
    +    def handle_pong(self, event: events.Pong) -> None:
    +        # Ignore unsolicited pongs and stale pongs whose payload doesn't match the ping currently in flight.
    +        if self.pending_ping_payload is None or bytes(event.payload) != self.pending_ping_payload:
    +            return  # pragma: no cover
    +
    +        self.last_ping_rtt = self.loop.time() - self.ping_sent_at
    +        self.pending_ping_payload = None
    +        # The peer answered in time; cancel the pong deadline and chain the next ping. This `schedule_ping()` call is
    +        # what keeps the keepalive loop running when ping_timeout is set. When ping_timeout is None the next ping is
    +        # already scheduled by `send_keepalive_ping`, so we must not schedule a duplicate here.
    +        if self.pong_timer is not None:
    +            self.pong_timer.cancel()
    +            self.pong_timer = None
    +            self.schedule_ping()
    +
    +    def start_keepalive(self) -> None:
    +        if self.ping_interval is not None and self.ping_interval > 0:
    +            self.schedule_ping()
    +
    +    def stop_keepalive(self) -> None:
    +        if self.ping_timer is not None:
    +            self.ping_timer.cancel()
    +            self.ping_timer = None
    +        if self.pong_timer is not None:  # pragma: no cover
    +            self.pong_timer.cancel()
    +            self.pong_timer = None
    +        self.pending_ping_payload = None
    +
    +    def schedule_ping(self) -> None:
    +        assert self.ping_interval is not None
    +        delay = max(0.0, self.ping_interval - self.last_ping_rtt)
    +        self.ping_timer = self.loop.call_later(delay, self.send_keepalive_ping)
    +
    +    def send_keepalive_ping(self) -> None:
    +        self.ping_timer = None
    +        if self.close_sent or self.transport.is_closing():  # pragma: no cover
    +            return
    +        # Random 4-byte payload identifies this ping; `handle_pong` uses it to ignore stale or unsolicited pongs.
    +        self.pending_ping_payload = struct.pack("!I", random.getrandbits(32))
    +        self.ping_sent_at = self.loop.time()
    +        self.transport.write(self.conn.send(wsproto.events.Ping(payload=self.pending_ping_payload)))
    +        if self.ping_timeout is not None:
    +            self.pong_timer = self.loop.call_later(self.ping_timeout, self.keepalive_timeout)
    +        else:  # pragma: no cover
    +            self.schedule_ping()
    +
    +    def keepalive_timeout(self) -> None:
    +        self.pong_timer = None
    +        self.pending_ping_payload = None
    +        if self.close_sent or self.transport.is_closing():  # pragma: no cover
    +            return
    +        if self.logger.level <= TRACE_LOG_LEVEL:
    +            prefix = "%s:%d - " % self.client if self.client else ""
    +            self.logger.log(TRACE_LOG_LEVEL, "%sWebSocket keepalive ping timeout", prefix)
    +        reason = "keepalive ping timeout"
    +        self.transport.write(self.conn.send(wsproto.events.CloseConnection(code=1011, reason=reason)))
    +        self.close_sent = True
    +        self.transport.close()
    +
         def send_500_response(self) -> None:
             if self.response_started or self.handshake_complete:
                 return  # we cannot send responses anymore
    @@ -266,6 +363,7 @@ class WSProtocol(asyncio.Protocol):
                             )
                         )
                         self.transport.write(output)
    +                    self.start_keepalive()
     
                 elif message["type"] == "websocket.close":
                     self.queue.put_nowait({"type": "websocket.disconnect", "code": 1006})
    diff --git a/venv/lib/python3.12/site-packages/watchfiles-1.1.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/watchfiles-1.1.1.dist-info/RECORD
    index 45365be1..bdf1754c 100644
    --- a/venv/lib/python3.12/site-packages/watchfiles-1.1.1.dist-info/RECORD
    +++ b/venv/lib/python3.12/site-packages/watchfiles-1.1.1.dist-info/RECORD
    @@ -1,4 +1,4 @@
    -../../../bin/watchfiles,sha256=eMGz7CpFEcblbVcPZBdKfuMlU7KlQb0OPUmSro4dAx0,236
    +../../../bin/watchfiles,sha256=RmkCcJI4AWoTOfA0hQqOmycyRN8vo3YdQ_gmy9Fuh1w,194
     watchfiles-1.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
     watchfiles-1.1.1.dist-info/METADATA,sha256=h34wYtQyezaYEn9GQWg8z4d9JCViFLT4vmKd_ip6WF8,4874
     watchfiles-1.1.1.dist-info/RECORD,,
    diff --git a/venv/lib/python3.12/site-packages/websockets-16.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/websockets-16.0.dist-info/RECORD
    index 34e318fe..d51e7472 100644
    --- a/venv/lib/python3.12/site-packages/websockets-16.0.dist-info/RECORD
    +++ b/venv/lib/python3.12/site-packages/websockets-16.0.dist-info/RECORD
    @@ -1,4 +1,4 @@
    -../../../bin/websockets,sha256=a-CjFBgjoAYmJboQprCPtZX13FLEh0M0wDbxKqZkYls,238
    +../../../bin/websockets,sha256=chwHpuuo-cu9W_NQaO8foZMQg68hYZHGXLW6eqdzc3U,196
     websockets-16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
     websockets-16.0.dist-info/METADATA,sha256=JcDvWo8DVSw5uoDAFbk9N8fJXuRJvnrcLXVBFyBjwN8,6799
     websockets-16.0.dist-info/RECORD,,
    diff --git a/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/INSTALLER
    new file mode 100644
    index 00000000..a1b589e3
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/INSTALLER
    @@ -0,0 +1 @@
    +pip
    diff --git a/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/METADATA
    new file mode 100644
    index 00000000..4ee73af8
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/METADATA
    @@ -0,0 +1,2609 @@
    +Metadata-Version: 2.4
    +Name: yarl
    +Version: 1.23.0
    +Summary: Yet another URL library
    +Home-page: https://github.com/aio-libs/yarl
    +Author: Andrew Svetlov
    +Author-email: andrew.svetlov@gmail.com
    +Maintainer: aiohttp team 
    +Maintainer-email: team@aiohttp.org
    +License: Apache-2.0
    +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org
    +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org
    +Project-URL: CI: GitHub Workflows, https://github.com/aio-libs/yarl/actions?query=branch:master
    +Project-URL: Code of Conduct, https://github.com/aio-libs/.github/blob/master/CODE_OF_CONDUCT.md
    +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/yarl
    +Project-URL: Docs: Changelog, https://yarl.aio-libs.org/en/latest/changes/
    +Project-URL: Docs: RTD, https://yarl.aio-libs.org
    +Project-URL: GitHub: issues, https://github.com/aio-libs/yarl/issues
    +Project-URL: GitHub: repo, https://github.com/aio-libs/yarl
    +Keywords: cython,cext,yarl
    +Classifier: Development Status :: 5 - Production/Stable
    +Classifier: Intended Audience :: Developers
    +Classifier: Programming Language :: Cython
    +Classifier: Programming Language :: Python
    +Classifier: Programming Language :: Python :: 3
    +Classifier: Programming Language :: Python :: 3.10
    +Classifier: Programming Language :: Python :: 3.11
    +Classifier: Programming Language :: Python :: 3.12
    +Classifier: Programming Language :: Python :: 3.13
    +Classifier: Programming Language :: Python :: 3.14
    +Classifier: Topic :: Internet :: WWW/HTTP
    +Classifier: Topic :: Software Development :: Libraries :: Python Modules
    +Requires-Python: >=3.10
    +Description-Content-Type: text/x-rst
    +License-File: LICENSE
    +License-File: NOTICE
    +Requires-Dist: idna>=2.0
    +Requires-Dist: multidict>=4.0
    +Requires-Dist: propcache>=0.2.1
    +Dynamic: license-file
    +
    +yarl
    +====
    +
    +The module provides handy URL class for URL parsing and changing.
    +
    +.. image:: https://github.com/aio-libs/yarl/workflows/CI/badge.svg
    +   :target: https://github.com/aio-libs/yarl/actions?query=workflow%3ACI
    +   :align: right
    +
    +.. image:: https://codecov.io/gh/aio-libs/yarl/graph/badge.svg?flag=pytest
    +   :target: https://app.codecov.io/gh/aio-libs/yarl?flags[]=pytest
    +   :alt: Codecov coverage for the pytest-driven measurements
    +
    +.. image:: https://img.shields.io/endpoint?url=https://codspeed.io/badge.json
    +   :target: https://codspeed.io/aio-libs/yarl
    +
    +.. image:: https://badge.fury.io/py/yarl.svg
    +   :target: https://badge.fury.io/py/yarl
    +
    +.. image:: https://readthedocs.org/projects/yarl/badge/?version=latest
    +   :target: https://yarl.aio-libs.org
    +
    +.. image:: https://img.shields.io/pypi/pyversions/yarl.svg
    +   :target: https://pypi.python.org/pypi/yarl
    +
    +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat
    +   :target: https://matrix.to/#/%23aio-libs:matrix.org
    +   :alt: Matrix Room — #aio-libs:matrix.org
    +
    +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat
    +   :target: https://matrix.to/#/%23aio-libs-space:matrix.org
    +   :alt: Matrix Space — #aio-libs-space:matrix.org
    +
    +
    +Introduction
    +------------
    +
    +Url is constructed from ``str``:
    +
    +.. code-block:: pycon
    +
    +   >>> from yarl import URL
    +   >>> url = URL('https://www.python.org/~guido?arg=1#frag')
    +   >>> url
    +   URL('https://www.python.org/~guido?arg=1#frag')
    +
    +All url parts: *scheme*, *user*, *password*, *host*, *port*, *path*,
    +*query* and *fragment* are accessible by properties:
    +
    +.. code-block:: pycon
    +
    +   >>> url.scheme
    +   'https'
    +   >>> url.host
    +   'www.python.org'
    +   >>> url.path
    +   '/~guido'
    +   >>> url.query_string
    +   'arg=1'
    +   >>> url.query
    +   
    +   >>> url.fragment
    +   'frag'
    +
    +All url manipulations produce a new url object:
    +
    +.. code-block:: pycon
    +
    +   >>> url = URL('https://www.python.org')
    +   >>> url / 'foo' / 'bar'
    +   URL('https://www.python.org/foo/bar')
    +   >>> url / 'foo' % {'bar': 'baz'}
    +   URL('https://www.python.org/foo?bar=baz')
    +
    +Strings passed to constructor and modification methods are
    +automatically encoded giving canonical representation as result:
    +
    +.. code-block:: pycon
    +
    +   >>> url = URL('https://www.python.org/шлях')
    +   >>> url
    +   URL('https://www.python.org/%D1%88%D0%BB%D1%8F%D1%85')
    +
    +Regular properties are *percent-decoded*, use ``raw_`` versions for
    +getting *encoded* strings:
    +
    +.. code-block:: pycon
    +
    +   >>> url.path
    +   '/шлях'
    +
    +   >>> url.raw_path
    +   '/%D1%88%D0%BB%D1%8F%D1%85'
    +
    +Human readable representation of URL is available as ``.human_repr()``:
    +
    +.. code-block:: pycon
    +
    +   >>> url.human_repr()
    +   'https://www.python.org/шлях'
    +
    +For full documentation please read https://yarl.aio-libs.org.
    +
    +
    +Installation
    +------------
    +
    +::
    +
    +   $ pip install yarl
    +
    +The library is Python 3 only!
    +
    +PyPI contains binary wheels for Linux, Windows and MacOS.  If you want to install
    +``yarl`` on another operating system where wheels are not provided,
    +the tarball will be used to compile the library from
    +the source code. It requires a C compiler and and Python headers installed.
    +
    +To skip the compilation you must explicitly opt-in by using a PEP 517
    +configuration setting ``pure-python``, or setting the ``YARL_NO_EXTENSIONS``
    +environment variable to a non-empty value, e.g.:
    +
    +.. code-block:: console
    +
    +   $ pip install yarl --config-settings=pure-python=false
    +
    +Please note that the pure-Python (uncompiled) version is much slower. However,
    +PyPy always uses a pure-Python implementation, and, as such, it is unaffected
    +by this variable.
    +
    +Dependencies
    +------------
    +
    +YARL requires multidict_ and propcache_ libraries.
    +
    +
    +API documentation
    +------------------
    +
    +The documentation is located at https://yarl.aio-libs.org.
    +
    +
    +Why isn't boolean supported by the URL query API?
    +-------------------------------------------------
    +
    +There is no standard for boolean representation of boolean values.
    +
    +Some systems prefer ``true``/``false``, others like ``yes``/``no``, ``on``/``off``,
    +``Y``/``N``, ``1``/``0``, etc.
    +
    +``yarl`` cannot make an unambiguous decision on how to serialize ``bool`` values because
    +it is specific to how the end-user's application is built and would be different for
    +different apps.  The library doesn't accept booleans in the API; a user should convert
    +bools into strings using own preferred translation protocol.
    +
    +
    +Comparison with other URL libraries
    +------------------------------------
    +
    +* furl (https://pypi.python.org/pypi/furl)
    +
    +  The library has rich functionality but the ``furl`` object is mutable.
    +
    +  I'm afraid to pass this object into foreign code: who knows if the
    +  code will modify my url in a terrible way while I just want to send URL
    +  with handy helpers for accessing URL properties.
    +
    +  ``furl`` has other non-obvious tricky things but the main objection
    +  is mutability.
    +
    +* URLObject (https://pypi.python.org/pypi/URLObject)
    +
    +  URLObject is immutable, that's pretty good.
    +
    +  Every URL change generates a new URL object.
    +
    +  But the library doesn't do any decode/encode transformations leaving the
    +  end user to cope with these gory details.
    +
    +
    +Source code
    +-----------
    +
    +The project is hosted on GitHub_
    +
    +Please file an issue on the `bug tracker
    +`_ if you have found a bug
    +or have some suggestion in order to improve the library.
    +
    +Discussion list
    +---------------
    +
    +*aio-libs* google group: https://groups.google.com/forum/#!forum/aio-libs
    +
    +Feel free to post your questions and ideas here.
    +
    +
    +Authors and License
    +-------------------
    +
    +The ``yarl`` package is written by Andrew Svetlov.
    +
    +It's *Apache 2* licensed and freely available.
    +
    +
    +.. _GitHub: https://github.com/aio-libs/yarl
    +
    +.. _multidict: https://github.com/aio-libs/multidict
    +
    +.. _propcache: https://github.com/aio-libs/propcache
    +
    +=========
    +Changelog
    +=========
    +
    +..
    +    You should *NOT* be adding new change log entries to this file, this
    +    file is managed by towncrier. You *may* edit previous change logs to
    +    fix problems like typo corrections or such.
    +    To add a new change log entry, please see
    +    https://pip.pypa.io/en/latest/development/#adding-a-news-entry
    +    we named the news folder "changes".
    +
    +    WARNING: Don't drop the next directive!
    +
    +.. towncrier release notes start
    +
    +1.23.0
    +======
    +
    +*(2025-12-16)*
    +
    +
    +Features
    +--------
    +
    +- Added support for ``pydantic``, the ``~yarl.URL`` could be used as a
    +  field type in ``pydantic`` models seamlessly.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1607 `__.
    +
    +
    +Packaging updates and notes for downstreams
    +-------------------------------------------
    +
    +- The CI has been set up to notify Codecov about upload completion
    +  -- by `@webknjaz `__.
    +
    +  With this, Codecov no longer needs to guess whether it received all
    +  the intended coverage reports or not.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1577 `__.
    +
    +- The in-tree build backend allows the end-users appending
    +  ``CFLAGS`` and ``LDFLAGS`` by setting respective environment
    +  variables externally.
    +
    +  It additionally sets up default compiler flags to perform
    +  building with maximum optimization in release mode. This
    +  makes the resulting artifacts shipped to PyPI smaller.
    +
    +  When line tracing is requested, the compiler and linker
    +  flags are configured to include as much information as
    +  possible for debugging and coverage tracking. The
    +  development builds are therefore smaller.
    +
    +  -- by `@webknjaz `__
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1586 `__.
    +
    +- The `PEP 517 `__ build backend now supports a new config
    +  setting for controlling whether to build the project in-tree
    +  or in a temporary directory. It only affects wheels and is
    +  set up to build in a temporary directory by default. It does
    +  not affect editable wheel builds — they will keep being
    +  built in-tree regardless.
    +
    +  -- by `@webknjaz `__
    +
    +  Here's an example of using this setting:
    +
    +  .. code-block:: console
    +
    +     $ python -m build \
    +         --config-setting=build-inplace=true
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1590 `__.
    +
    +- Starting this version, when building the wheels is happening
    +  in an automatically created temporary directory, the build
    +  backend makes an effort to normalize the respective file
    +  system path to a deterministic source checkout directory.
    +
    +  -- by `@webknjaz `__
    +
    +  It does so by injecting the ``-ffile-prefix-map`` compiler
    +  option into the ``CFLAGS`` environment variable as suggested
    +  by known `reproducible build practices
    +  `__.
    +
    +  The effect is that downstreams will get more reproducible
    +  build results.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1591 `__.
    +
    +- Dropped Python 3.9 support; Python 3.10 is the minimal supported Python version
    +  -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1609 `__.
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- The deprecated license classifier was removed from ``setup.cfg``
    +  -- by `@yegorich `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1550 `__.
    +
    +- The in-tree build backend allows the end-users appending
    +  ``CFLAGS`` and ``LDFLAGS`` by setting respective environment
    +  variables externally.
    +
    +  It additionally sets up default compiler flags to perform
    +  building with maximum optimization in release mode. This
    +  makes the resulting artifacts shipped to PyPI smaller.
    +
    +  When line tracing is requested, the compiler and linker
    +  flags are configured to include as much information as
    +  possible for debugging and coverage tracking. The
    +  development builds are therefore smaller.
    +
    +  -- by `@webknjaz `__
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1586 `__.
    +
    +- The CI has been updated to consistently benchmark optimized
    +  release builds -- by `@webknjaz `__.
    +
    +  When the release workflow is triggered, the pre-built wheels
    +  ready to hit PyPI are being tested. Otherwise, the job
    +  builds the project from source, while the rest of the
    +  workflow uses debug builds for line tracing and coverage
    +  collection.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1587 `__.
    +
    +
    +----
    +
    +
    +1.22.0
    +======
    +
    +*(2025-10-05)*
    +
    +
    +Features
    +--------
    +
    +- Added arm64 Windows wheel builds
    +  -- by `@finnagin `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1516 `__.
    +
    +
    +----
    +
    +
    +1.21.0
    +======
    +
    +*(2025-10-05)*
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- The ``reusable-cibuildwheel.yml`` workflow has been refactored to
    +  be more generic and ``ci-cd.yml`` now holds all the configuration
    +  toggles -- by `@webknjaz `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1535 `__.
    +
    +- When building wheels, the source distribution is now passed directly
    +  to the ``cibuildwheel`` invocation -- by `@webknjaz `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1536 `__.
    +
    +- Added CI for Python 3.14 -- by `@kumaraditya303 `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1560 `__.
    +
    +
    +----
    +
    +
    +1.20.1
    +======
    +
    +*(2025-06-09)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Started raising a ``ValueError`` exception raised for corrupted
    +  IPv6 URL values.
    +
    +  These fixes the issue where exception ``IndexError`` was
    +  leaking from the internal code because of not being handled and
    +  transformed into a user-facing error. The problem was happening
    +  under the following conditions: empty IPv6 URL, brackets in
    +  reverse order.
    +
    +  -- by `@MaelPic `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1512 `__.
    +
    +
    +Packaging updates and notes for downstreams
    +-------------------------------------------
    +
    +- Updated to use Cython 3.1 universally across the build path -- by `@lysnikolaou `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1514 `__.
    +
    +- Made Cython line tracing opt-in via the ``with-cython-tracing`` build config setting -- by `@bdraco `__.
    +
    +  Previously, line tracing was enabled by default in ``pyproject.toml``, which caused build issues for some users and made wheels nearly twice as slow.
    +  Now line tracing is only enabled when explicitly requested via ``pip install . --config-setting=with-cython-tracing=true`` or by setting the ``YARL_CYTHON_TRACING`` environment variable.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1521 `__.
    +
    +
    +----
    +
    +
    +1.20.0
    +======
    +
    +*(2025-04-16)*
    +
    +
    +Features
    +--------
    +
    +- Implemented support for the free-threaded build of CPython 3.13 -- by `@lysnikolaou `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1456 `__.
    +
    +
    +Packaging updates and notes for downstreams
    +-------------------------------------------
    +
    +- Started building wheels for the free-threaded build of CPython 3.13 -- by `@lysnikolaou `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1456 `__.
    +
    +
    +----
    +
    +
    +1.19.0
    +======
    +
    +*(2025-04-05)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Fixed entire name being re-encoded when using ``yarl.URL.with_suffix()`` -- by `@NTFSvolume `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1468 `__.
    +
    +
    +Features
    +--------
    +
    +- Started building armv7l wheels for manylinux -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1495 `__.
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- GitHub Actions CI/CD is now configured to manage caching pip-ecosystem
    +  dependencies using `re-actors/cache-python-deps`_ -- an action by
    +  `@webknjaz `__ that takes into account ABI stability and the exact
    +  version of Python runtime.
    +
    +  .. _`re-actors/cache-python-deps`:
    +     https://github.com/marketplace/actions/cache-python-deps
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1471 `__.
    +
    +- Increased minimum `propcache`_ version to 0.2.1 to fix failing tests -- by `@bdraco `__.
    +
    +  .. _`propcache`:
    +     https://github.com/aio-libs/propcache
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1479 `__.
    +
    +- Added all hidden folders to pytest's ``norecursedirs`` to prevent it
    +  from trying to collect tests there -- by `@lysnikolaou `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1480 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved accuracy of type annotations -- by `@Dreamsorcerer `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1484 `__.
    +
    +- Improved performance of parsing query strings -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1493 `__, `#1497 `__.
    +
    +- Improved performance of the C unquoter -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1496 `__, `#1498 `__.
    +
    +
    +----
    +
    +
    +1.18.3
    +======
    +
    +*(2024-12-01)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Fixed uppercase ASCII hosts being rejected by ``URL.build()()`` and ``yarl.URL.with_host()`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#954 `__, `#1442 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performances of multiple path properties on cache miss -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1443 `__.
    +
    +
    +----
    +
    +
    +1.18.2
    +======
    +
    +*(2024-11-29)*
    +
    +
    +No significant changes.
    +
    +
    +----
    +
    +
    +1.18.1
    +======
    +
    +*(2024-11-29)*
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved cache performance when ``~yarl.URL`` objects are constructed from ``yarl.URL.build()`` with ``encoded=True`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1432 `__.
    +
    +- Improved cache performance for operations that produce a new ``~yarl.URL`` object -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1434 `__, `#1436 `__.
    +
    +
    +----
    +
    +
    +1.18.0
    +======
    +
    +*(2024-11-21)*
    +
    +
    +Features
    +--------
    +
    +- Added ``keep_query`` and ``keep_fragment`` flags in the ``yarl.URL.with_path()``, ``yarl.URL.with_name()`` and ``yarl.URL.with_suffix()`` methods, allowing users to optionally retain the query string and fragment in the resulting URL when replacing the path -- by `@paul-nameless `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#111 `__, `#1421 `__.
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- Started running downstream ``aiohttp`` tests in CI -- by `@Cycloctane `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1415 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of converting ``~yarl.URL`` to a string -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1422 `__.
    +
    +
    +----
    +
    +
    +1.17.2
    +======
    +
    +*(2024-11-17)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Stopped implicitly allowing the use of Cython pre-release versions when
    +  building the distribution package -- by `@ajsanchezsanz `__ and
    +  `@markgreene74 `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1411 `__, `#1412 `__.
    +
    +- Fixed a bug causing ``~yarl.URL.port`` to return the default port when the given port was zero
    +  -- by `@gmacon `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1413 `__.
    +
    +
    +Features
    +--------
    +
    +- Make error messages include details of incorrect type when ``port`` is not int in ``yarl.URL.build()``.
    +  -- by `@Cycloctane `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1414 `__.
    +
    +
    +Packaging updates and notes for downstreams
    +-------------------------------------------
    +
    +- Stopped implicitly allowing the use of Cython pre-release versions when
    +  building the distribution package -- by `@ajsanchezsanz `__ and
    +  `@markgreene74 `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1411 `__, `#1412 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of the ``yarl.URL.joinpath()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1418 `__.
    +
    +
    +----
    +
    +
    +1.17.1
    +======
    +
    +*(2024-10-30)*
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of many ``~yarl.URL`` methods -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1396 `__, `#1397 `__, `#1398 `__.
    +
    +- Improved performance of passing a `dict` or `str` to ``yarl.URL.extend_query()`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1401 `__.
    +
    +
    +----
    +
    +
    +1.17.0
    +======
    +
    +*(2024-10-28)*
    +
    +
    +Features
    +--------
    +
    +- Added ``~yarl.URL.host_port_subcomponent`` which returns the ``3986#section-3.2.2`` host and ``3986#section-3.2.3`` port subcomponent -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1375 `__.
    +
    +
    +----
    +
    +
    +1.16.0
    +======
    +
    +*(2024-10-21)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Fixed blocking I/O to load Python code when creating a new ``~yarl.URL`` with non-ascii characters in the network location part -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1342 `__.
    +
    +
    +Removals and backward incompatible breaking changes
    +---------------------------------------------------
    +
    +- Migrated to using a single cache for encoding hosts -- by `@bdraco `__.
    +
    +  Passing ``ip_address_size`` and ``host_validate_size`` to ``yarl.cache_configure()`` is deprecated in favor of the new ``encode_host_size`` parameter and will be removed in a future release. For backwards compatibility, the old parameters affect the ``encode_host`` cache size.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1348 `__, `#1357 `__, `#1363 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of constructing ``~yarl.URL`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1336 `__.
    +
    +- Improved performance of calling ``yarl.URL.build()`` and constructing unencoded ``~yarl.URL`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1345 `__.
    +
    +- Reworked the internal encoding cache to improve performance on cache hit -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1369 `__.
    +
    +
    +----
    +
    +
    +1.15.5
    +======
    +
    +*(2024-10-18)*
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of the ``yarl.URL.joinpath()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1304 `__.
    +
    +- Improved performance of the ``yarl.URL.extend_query()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1305 `__.
    +
    +- Improved performance of the ``yarl.URL.origin()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1306 `__.
    +
    +- Improved performance of the ``yarl.URL.with_path()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1307 `__.
    +
    +- Improved performance of the ``yarl.URL.with_query()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1308 `__, `#1328 `__.
    +
    +- Improved performance of the ``yarl.URL.update_query()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1309 `__, `#1327 `__.
    +
    +- Improved performance of the ``yarl.URL.join()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1313 `__.
    +
    +- Improved performance of ``~yarl.URL`` equality checks -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1315 `__.
    +
    +- Improved performance of ``~yarl.URL`` methods that modify the network location -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1316 `__.
    +
    +- Improved performance of the ``yarl.URL.with_fragment()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1317 `__.
    +
    +- Improved performance of calculating the hash of ``~yarl.URL`` objects -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1318 `__.
    +
    +- Improved performance of the ``yarl.URL.relative()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1319 `__.
    +
    +- Improved performance of the ``yarl.URL.with_name()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1320 `__.
    +
    +- Improved performance of ``~yarl.URL.parent`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1321 `__.
    +
    +- Improved performance of the ``yarl.URL.with_scheme()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1322 `__.
    +
    +
    +----
    +
    +
    +1.15.4
    +======
    +
    +*(2024-10-16)*
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of the quoter when all characters are safe -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1288 `__.
    +
    +- Improved performance of unquoting strings -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1292 `__, `#1293 `__.
    +
    +- Improved performance of calling ``yarl.URL.build()`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1297 `__.
    +
    +
    +----
    +
    +
    +1.15.3
    +======
    +
    +*(2024-10-15)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Fixed ``yarl.URL.build()`` failing to validate paths must start with a ``/`` when passing ``authority`` -- by `@bdraco `__.
    +
    +  The validation only worked correctly when passing ``host``.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1265 `__.
    +
    +
    +Removals and backward incompatible breaking changes
    +---------------------------------------------------
    +
    +- Removed support for Python 3.8 as it has reached end of life -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1203 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of constructing ``~yarl.URL`` when the net location is only the host -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1271 `__.
    +
    +
    +----
    +
    +
    +1.15.2
    +======
    +
    +*(2024-10-13)*
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of converting ``~yarl.URL`` to a string -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1234 `__.
    +
    +- Improved performance of ``yarl.URL.joinpath()`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1248 `__, `#1250 `__.
    +
    +- Improved performance of constructing query strings from ``~multidict.MultiDict`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1256 `__.
    +
    +- Improved performance of constructing query strings with ``int`` values -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1259 `__.
    +
    +
    +----
    +
    +
    +1.15.1
    +======
    +
    +*(2024-10-12)*
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of calling ``yarl.URL.build()`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1222 `__.
    +
    +- Improved performance of all ``~yarl.URL`` methods that create new ``~yarl.URL`` objects -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1226 `__.
    +
    +- Improved performance of ``~yarl.URL`` methods that modify the network location -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1229 `__.
    +
    +
    +----
    +
    +
    +1.15.0
    +======
    +
    +*(2024-10-11)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Fixed validation with ``yarl.URL.with_scheme()`` when passed scheme is not lowercase -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1189 `__.
    +
    +
    +Features
    +--------
    +
    +- Started building ``armv7l`` wheels -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1204 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of constructing unencoded ``~yarl.URL`` objects -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1188 `__.
    +
    +- Added a cache for parsing hosts to reduce overhead of encoding ``~yarl.URL`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1190 `__.
    +
    +- Improved performance of constructing query strings from ``~collections.abc.Mapping`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1193 `__.
    +
    +- Improved performance of converting ``~yarl.URL`` objects to strings -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1198 `__.
    +
    +
    +----
    +
    +
    +1.14.0
    +======
    +
    +*(2024-10-08)*
    +
    +
    +Packaging updates and notes for downstreams
    +-------------------------------------------
    +
    +- Switched to using the ``propcache`` package for property caching
    +  -- by `@bdraco `__.
    +
    +  The ``propcache`` package is derived from the property caching
    +  code in ``yarl`` and has been broken out to avoid maintaining it for multiple
    +  projects.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1169 `__.
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- Started testing with Hypothesis -- by `@webknjaz `__ and `@bdraco `__.
    +
    +  Special thanks to `@Zac-HD `__ for helping us get started with this framework.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#860 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of ``yarl.URL.is_default_port()`` when no explicit port is set -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1168 `__.
    +
    +- Improved performance of converting ``~yarl.URL`` to a string when no explicit port is set -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1170 `__.
    +
    +- Improved performance of the ``yarl.URL.origin()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1175 `__.
    +
    +- Improved performance of encoding hosts -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1176 `__.
    +
    +
    +----
    +
    +
    +1.13.1
    +======
    +
    +*(2024-09-27)*
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of calling ``yarl.URL.build()`` with ``authority`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1163 `__.
    +
    +
    +----
    +
    +
    +1.13.0
    +======
    +
    +*(2024-09-26)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Started rejecting ASCII hostnames with invalid characters. For host strings that
    +  look like authority strings, the exception message includes advice on what to do
    +  instead -- by `@mjpieters `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#880 `__, `#954 `__.
    +
    +- Fixed IPv6 addresses missing brackets when the ``~yarl.URL`` was converted to a string -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1157 `__, `#1158 `__.
    +
    +
    +Features
    +--------
    +
    +- Added ``~yarl.URL.host_subcomponent`` which returns the ``3986#section-3.2.2`` host subcomponent -- by `@bdraco `__.
    +
    +  The only current practical difference between ``~yarl.URL.raw_host`` and ``~yarl.URL.host_subcomponent`` is that IPv6 addresses are returned bracketed.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1159 `__.
    +
    +
    +----
    +
    +
    +1.12.1
    +======
    +
    +*(2024-09-23)*
    +
    +
    +No significant changes.
    +
    +
    +----
    +
    +
    +1.12.0
    +======
    +
    +*(2024-09-23)*
    +
    +
    +Features
    +--------
    +
    +- Added ``~yarl.URL.path_safe`` to be able to fetch the path without ``%2F`` and ``%25`` decoded -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1150 `__.
    +
    +
    +Removals and backward incompatible breaking changes
    +---------------------------------------------------
    +
    +- Restore decoding ``%2F`` (``/``) in ``URL.path`` -- by `@bdraco `__.
    +
    +  This change restored the behavior before `#1057 `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1151 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of processing paths -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1143 `__.
    +
    +
    +----
    +
    +
    +1.11.1
    +======
    +
    +*(2024-09-09)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Allowed scheme replacement for relative URLs if the scheme does not require a host -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#280 `__, `#1138 `__.
    +
    +- Allowed empty host for URL schemes other than the special schemes listed in the WHATWG URL spec -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1136 `__.
    +
    +
    +Features
    +--------
    +
    +- Loosened restriction on integers as query string values to allow classes that implement ``__int__`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1139 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of normalizing paths -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1137 `__.
    +
    +
    +----
    +
    +
    +1.11.0
    +======
    +
    +*(2024-09-08)*
    +
    +
    +Features
    +--------
    +
    +- Added ``URL.extend_query()()`` method, which can be used to extend parameters without replacing same named keys -- by `@bdraco `__.
    +
    +  This method was primarily added to replace the inefficient hand rolled method currently used in ``aiohttp``.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1128 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of the Cython ``cached_property`` implementation -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1122 `__.
    +
    +- Simplified computing ports by removing unnecessary code -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1123 `__.
    +
    +- Improved performance of encoding non IPv6 hosts -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1125 `__.
    +
    +- Improved performance of ``URL.build()()`` when the path, query string, or fragment is an empty string -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1126 `__.
    +
    +- Improved performance of the ``URL.update_query()()`` method -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1130 `__.
    +
    +- Improved performance of processing query string changes when arguments are ``str`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1131 `__.
    +
    +
    +----
    +
    +
    +1.10.0
    +======
    +
    +*(2024-09-06)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Fixed joining a path when the existing path was empty -- by `@bdraco `__.
    +
    +  A regression in ``URL.join()()`` was introduced in `#1082 `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1118 `__.
    +
    +
    +Features
    +--------
    +
    +- Added ``URL.without_query_params()()`` method, to drop some parameters from query string -- by `@hongquan `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#774 `__, `#898 `__, `#1010 `__.
    +
    +- The previously protected types ``_SimpleQuery``, ``_QueryVariable``, and ``_Query`` are now available for use externally as ``SimpleQuery``, ``QueryVariable``, and ``Query`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1050 `__, `#1113 `__.
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- Replaced all ``~typing.Optional`` with ``~typing.Union`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1095 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Significantly improved performance of parsing the network location -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1112 `__.
    +
    +- Added internal types to the cache to prevent future refactoring errors -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1117 `__.
    +
    +
    +----
    +
    +
    +1.9.11
    +======
    +
    +*(2024-09-04)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Fixed a ``TypeError`` with ``MultiDictProxy`` and Python 3.8 -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1084 `__, `#1105 `__, `#1107 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of encoding hosts -- by `@bdraco `__.
    +
    +  Previously, the library would unconditionally try to parse a host as an IP Address. The library now avoids trying to parse a host as an IP Address if the string is not in one of the formats described in ``3986#section-3.2.2``.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1104 `__.
    +
    +
    +----
    +
    +
    +1.9.10
    +======
    +
    +*(2024-09-04)*
    +
    +
    +Bug fixes
    +---------
    +
    +- ``URL.join()()`` has been changed to match
    +  ``3986`` and align with
    +  ``/ operation()`` and ``URL.joinpath()()``
    +  when joining URLs with empty segments.
    +  Previously ``urllib.parse.urljoin`` was used,
    +  which has known issues with empty segments
    +  (`python/cpython#84774 `_).
    +
    +  Due to the semantics of ``URL.join()()``, joining an
    +  URL with scheme requires making it relative, prefixing with ``./``.
    +
    +  .. code-block:: pycon
    +
    +     >>> URL("https://web.archive.org/web/").join(URL("./https://github.com/aio-libs/yarl"))
    +     URL('https://web.archive.org/web/https://github.com/aio-libs/yarl')
    +
    +
    +  Empty segments are honored in the base as well as the joined part.
    +
    +  .. code-block:: pycon
    +
    +     >>> URL("https://web.archive.org/web/https://").join(URL("github.com/aio-libs/yarl"))
    +     URL('https://web.archive.org/web/https://github.com/aio-libs/yarl')
    +
    +
    +
    +  -- by `@commonism `__
    +
    +  This change initially appeared in 1.9.5 but was reverted in 1.9.6 to resolve a problem with query string handling.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1039 `__, `#1082 `__.
    +
    +
    +Features
    +--------
    +
    +- Added ``~yarl.URL.absolute`` which is now preferred over ``URL.is_absolute()`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1100 `__.
    +
    +
    +----
    +
    +
    +1.9.9
    +=====
    +
    +*(2024-09-04)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Added missing type on ``~yarl.URL.port`` -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1097 `__.
    +
    +
    +----
    +
    +
    +1.9.8
    +=====
    +
    +*(2024-09-03)*
    +
    +
    +Features
    +--------
    +
    +- Covered the ``~yarl.URL`` object with types -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1084 `__.
    +
    +- Cache parsing of IP Addresses when encoding hosts -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1086 `__.
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- Covered the ``~yarl.URL`` object with types -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1084 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of handling ports -- by `@bdraco `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1081 `__.
    +
    +
    +----
    +
    +
    +1.9.7
    +=====
    +
    +*(2024-09-01)*
    +
    +
    +Removals and backward incompatible breaking changes
    +---------------------------------------------------
    +
    +- Removed support ``3986#section-3.2.3`` port normalization when the scheme is not one of ``http``, ``https``, ``wss``, or ``ws`` -- by `@bdraco `__.
    +
    +  Support for port normalization was recently added in `#1033 `__ and contained code that would do blocking I/O if the scheme was not one of the four listed above. The code has been removed because this library is intended to be safe for usage with ``asyncio``.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1076 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- Improved performance of property caching -- by `@bdraco `__.
    +
    +  The ``reify`` implementation from ``aiohttp`` was adapted to replace the internal ``cached_property`` implementation.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1070 `__.
    +
    +
    +----
    +
    +
    +1.9.6
    +=====
    +
    +*(2024-08-30)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Reverted ``3986`` compatible ``URL.join()()`` honoring empty segments which was introduced in `#1039 `__.
    +
    +  This change introduced a regression handling query string parameters with joined URLs. The change was reverted to maintain compatibility with the previous behavior.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1067 `__.
    +
    +
    +----
    +
    +
    +1.9.5
    +=====
    +
    +*(2024-08-30)*
    +
    +
    +Bug fixes
    +---------
    +
    +- Joining URLs with empty segments has been changed
    +  to match ``3986``.
    +
    +  Previously empty segments would be removed from path,
    +  breaking use-cases such as
    +
    +  .. code-block:: python
    +
    +     URL("https://web.archive.org/web/") / "https://github.com/"
    +
    +  Now ``/ operation()`` and ``URL.joinpath()()``
    +  keep empty segments, but do not introduce new empty segments.
    +  e.g.
    +
    +  .. code-block:: python
    +
    +     URL("https://example.org/") / ""
    +
    +  does not introduce an empty segment.
    +
    +  -- by `@commonism `__ and `@youtux `__
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1026 `__.
    +
    +- The default protocol ports of well-known URI schemes are now taken into account
    +  during the normalization of the URL string representation in accordance with
    +  ``3986#section-3.2.3``.
    +
    +  Specified ports are removed from the ``str`` representation of a ``~yarl.URL``
    +  if the port matches the scheme's default port -- by `@commonism `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1033 `__.
    +
    +- ``URL.join()()`` has been changed to match
    +  ``3986`` and align with
    +  ``/ operation()`` and ``URL.joinpath()()``
    +  when joining URLs with empty segments.
    +  Previously ``urllib.parse.urljoin`` was used,
    +  which has known issues with empty segments
    +  (`python/cpython#84774 `_).
    +
    +  Due to the semantics of ``URL.join()()``, joining an
    +  URL with scheme requires making it relative, prefixing with ``./``.
    +
    +  .. code-block:: pycon
    +
    +     >>> URL("https://web.archive.org/web/").join(URL("./https://github.com/aio-libs/yarl"))
    +     URL('https://web.archive.org/web/https://github.com/aio-libs/yarl')
    +
    +
    +  Empty segments are honored in the base as well as the joined part.
    +
    +  .. code-block:: pycon
    +
    +     >>> URL("https://web.archive.org/web/https://").join(URL("github.com/aio-libs/yarl"))
    +     URL('https://web.archive.org/web/https://github.com/aio-libs/yarl')
    +
    +
    +
    +  -- by `@commonism `__
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1039 `__.
    +
    +
    +Removals and backward incompatible breaking changes
    +---------------------------------------------------
    +
    +- Stopped decoding ``%2F`` (``/``) in ``URL.path``, as this could lead to code incorrectly treating it as a path separator
    +  -- by `@Dreamsorcerer `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1057 `__.
    +
    +- Dropped support for Python 3.7 -- by `@Dreamsorcerer `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1016 `__.
    +
    +
    +Improved documentation
    +----------------------
    +
    +- On the ``Contributing docs`` page,
    +  a link to the ``Towncrier philosophy`` has been fixed.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#981 `__.
    +
    +- The pre-existing ``/ magic method()``
    +  has been documented in the API reference -- by `@commonism `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1026 `__.
    +
    +
    +Packaging updates and notes for downstreams
    +-------------------------------------------
    +
    +- A flaw in the logic for copying the project directory into a
    +  temporary folder that led to infinite recursion when ``TMPDIR``
    +  was set to a project subdirectory path. This was happening in Fedora
    +  and its downstream due to the use of `pyproject-rpm-macros
    +  `__. It was
    +  only reproducible with ``pip wheel`` and was not affecting the
    +  ``pyproject-build`` users.
    +
    +  -- by `@hroncok `__ and `@webknjaz `__
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#992 `__, `#1014 `__.
    +
    +- Support Python 3.13 and publish non-free-threaded wheels
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1054 `__.
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- The CI/CD setup has been updated to test ``arm64`` wheels
    +  under macOS 14, except for Python 3.7 that is unsupported
    +  in that environment -- by `@webknjaz `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1015 `__.
    +
    +- Removed unused type ignores and casts -- by `@hauntsaninja `__.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1031 `__.
    +
    +
    +Miscellaneous internal changes
    +------------------------------
    +
    +- ``port``, ``scheme``, and ``raw_host`` are now ``cached_property`` -- by `@bdraco `__.
    +
    +  ``aiohttp`` accesses these properties quite often, which cause ``urllib`` to build the ``_hostinfo`` property every time. ``port``, ``scheme``, and ``raw_host`` are now cached properties, which will improve performance.
    +
    +  *Related issues and pull requests on GitHub:*
    +  `#1044 `__, `#1058 `__.
    +
    +
    +----
    +
    +
    +1.9.4 (2023-12-06)
    +==================
    +
    +Bug fixes
    +---------
    +
    +- Started raising ``TypeError`` when a string value is passed into
    +  ``yarl.URL.build()`` as the ``port`` argument  -- by `@commonism `__.
    +
    +  Previously the empty string as port would create malformed URLs when rendered as string representations. (`#883 `__)
    +
    +
    +Packaging updates and notes for downstreams
    +-------------------------------------------
    +
    +- The leading ``--`` has been dropped from the `PEP 517 `__ in-tree build
    +  backend config setting names. ``--pure-python`` is now just ``pure-python``
    +  -- by `@webknjaz `__.
    +
    +  The usage now looks as follows:
    +
    +  .. code-block:: console
    +
    +      $ python -m build \
    +          --config-setting=pure-python=true \
    +          --config-setting=with-cython-tracing=true
    +
    +  (`#963 `__)
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- A step-by-step ``Release Guide`` guide has
    +  been added, describing how to release *yarl* -- by `@webknjaz `__.
    +
    +  This is primarily targeting maintainers. (`#960 `__)
    +- Coverage collection has been implemented for the Cython modules
    +  -- by `@webknjaz `__.
    +
    +  It will also be reported to Codecov from any non-release CI jobs.
    +
    +  To measure coverage in a development environment, *yarl* can be
    +  installed in editable mode:
    +
    +  .. code-block:: console
    +
    +      $ python -Im pip install -e .
    +
    +  Editable install produces C-files required for the Cython coverage
    +  plugin to map the measurements back to the PYX-files.
    +
    +  `#961 `__
    +
    +- It is now possible to request line tracing in Cython builds using the
    +  ``with-cython-tracing`` `PEP 517 `__ config setting
    +  -- `@webknjaz `__.
    +
    +  This can be used in CI and development environment to measure coverage
    +  on Cython modules, but is not normally useful to the end-users or
    +  downstream packagers.
    +
    +  Here's a usage example:
    +
    +  .. code-block:: console
    +
    +      $ python -Im pip install . --config-settings=with-cython-tracing=true
    +
    +  For editable installs, this setting is on by default. Otherwise, it's
    +  off unless requested explicitly.
    +
    +  The following produces C-files required for the Cython coverage
    +  plugin to map the measurements back to the PYX-files:
    +
    +  .. code-block:: console
    +
    +      $ python -Im pip install -e .
    +
    +  Alternatively, the ``YARL_CYTHON_TRACING=1`` environment variable
    +  can be set to do the same as the `PEP 517 `__ config setting.
    +
    +  `#962 `__
    +
    +
    +1.9.3 (2023-11-20)
    +==================
    +
    +Bug fixes
    +---------
    +
    +- Stopped dropping trailing slashes in ``yarl.URL.joinpath()`` -- by `@gmacon `__. (`#862 `__, `#866 `__)
    +- Started accepting string subclasses in ``yarl.URL.__truediv__()`` operations (``URL / segment``) -- by `@mjpieters `__. (`#871 `__, `#884 `__)
    +- Fixed the human representation of URLs with square brackets in usernames and passwords -- by `@mjpieters `__. (`#876 `__, `#882 `__)
    +- Updated type hints to include ``URL.missing_port()``, ``URL.__bytes__()``
    +  and the ``encoding`` argument to ``yarl.URL.joinpath()``
    +  -- by `@mjpieters `__. (`#891 `__)
    +
    +
    +Packaging updates and notes for downstreams
    +-------------------------------------------
    +
    +- Integrated Cython 3 to enable building *yarl* under Python 3.12 -- by `@mjpieters `__. (`#829 `__, `#881 `__)
    +- Declared modern ``setuptools.build_meta`` as the `PEP 517 `__ build
    +  backend in ``pyproject.toml`` explicitly -- by `@webknjaz `__. (`#886 `__)
    +- Converted most of the packaging setup into a declarative ``setup.cfg``
    +  config -- by `@webknjaz `__. (`#890 `__)
    +- The packaging is replaced from an old-fashioned ``setup.py`` to an
    +  in-tree `PEP 517 `__ build backend -- by `@webknjaz `__.
    +
    +  Whenever the end-users or downstream packagers need to build ``yarl`` from
    +  source (a Git checkout or an sdist), they may pass a ``config_settings``
    +  flag ``--pure-python``. If this flag is not set, a C-extension will be built
    +  and included into the distribution.
    +
    +  Here is how this can be done with ``pip``:
    +
    +  .. code-block:: console
    +
    +      $ python -m pip install . --config-settings=--pure-python=false
    +
    +  This will also work with ``-e | --editable``.
    +
    +  The same can be achieved via ``pypa/build``:
    +
    +  .. code-block:: console
    +
    +      $ python -m build --config-setting=--pure-python=false
    +
    +  Adding ``-w | --wheel`` can force ``pypa/build`` produce a wheel from source
    +  directly, as opposed to building an ``sdist`` and then building from it. (`#893 `__)
    +
    +  .. attention::
    +
    +     v1.9.3 was the only version using the ``--pure-python`` setting name.
    +     Later versions dropped the ``--`` prefix, making it just ``pure-python``.
    +
    +- Declared Python 3.12 supported officially in the distribution package metadata
    +  -- by `@edgarrmondragon `__. (`#942 `__)
    +
    +
    +Contributor-facing changes
    +--------------------------
    +
    +- A regression test for no-host URLs was added per `#821 `__
    +  and ``3986`` -- by `@kenballus `__. (`#821 `__, `#822 `__)
    +- Started testing *yarl* against Python 3.12 in CI -- by `@mjpieters `__. (`#881 `__)
    +- All Python 3.12 jobs are now marked as required to pass in CI
    +  -- by `@edgarrmondragon `__. (`#942 `__)
    +- MyST is now integrated in Sphinx -- by `@webknjaz `__.
    +
    +  This allows the contributors to author new documents in Markdown
    +  when they have difficulties with going straight RST. (`#953 `__)
    +
    +
    +1.9.2 (2023-04-25)
    +==================
    +
    +Bugfixes
    +--------
    +
    +- Fix regression with ``yarl.URL.__truediv__()`` and absolute URLs with empty paths causing the raw path to lack the leading ``/``.
    +  (`#854 `_)
    +
    +
    +1.9.1 (2023-04-21)
    +==================
    +
    +Bugfixes
    +--------
    +
    +- Marked tests that fail on older Python patch releases (< 3.7.10, < 3.8.8 and < 3.9.2) as expected to fail due to missing a security fix for CVE-2021-23336. (`#850 `_)
    +
    +
    +1.9.0 (2023-04-19)
    +==================
    +
    +This release was never published to PyPI, due to issues with the build process.
    +
    +Features
    +--------
    +
    +- Added ``URL.joinpath(*elements)``, to create a new URL appending multiple path elements. (`#704 `_)
    +- Made ``URL.__truediv__()()`` return ``NotImplemented`` if called with an
    +  unsupported type — by `@michaeljpeters `__.
    +  (`#832 `_)
    +
    +
    +Bugfixes
    +--------
    +
    +- Path normalization for absolute URLs no longer raises a ValueError exception
    +  when ``..`` segments would otherwise go beyond the URL path root.
    +  (`#536 `_)
    +- Fixed an issue with update_query() not getting rid of the query when argument is None. (`#792 `_)
    +- Added some input restrictions on with_port() function to prevent invalid boolean inputs or out of valid port inputs; handled incorrect 0 port representation. (`#793 `_)
    +- Made ``yarl.URL.build()`` raise a ``TypeError`` if the ``host`` argument is ``None`` — by `@paulpapacz `__. (`#808 `_)
    +- Fixed an issue with ``update_query()`` getting rid of the query when the argument
    +  is empty but not ``None``. (`#845 `_)
    +
    +
    +Misc
    +----
    +
    +- `#220 `_
    +
    +
    +1.8.2 (2022-12-03)
    +==================
    +
    +This is the first release that started shipping wheels for Python 3.11.
    +
    +
    +1.8.1 (2022-08-01)
    +==================
    +
    +Misc
    +----
    +
    +- `#694 `_, `#699 `_, `#700 `_, `#701 `_, `#702 `_, `#703 `_, `#739 `_
    +
    +
    +1.8.0 (2022-08-01)
    +==================
    +
    +Features
    +--------
    +
    +- Added ``URL.raw_suffix``, ``URL.suffix``, ``URL.raw_suffixes``, ``URL.suffixes``, ``URL.with_suffix``. (`#613 `_)
    +
    +
    +Improved Documentation
    +----------------------
    +
    +- Fixed broken internal references to ``yarl.URL.human_repr()``.
    +  (`#665 `_)
    +- Fixed broken external references to ``multidict:index`` docs. (`#665 `_)
    +
    +
    +Deprecations and Removals
    +-------------------------
    +
    +- Dropped Python 3.6 support. (`#672 `_)
    +
    +
    +Misc
    +----
    +
    +- `#646 `_, `#699 `_, `#701 `_
    +
    +
    +1.7.2 (2021-11-01)
    +==================
    +
    +Bugfixes
    +--------
    +
    +- Changed call in ``with_port()`` to stop reencoding parts of the URL that were already encoded. (`#623 `_)
    +
    +
    +1.7.1 (2021-10-07)
    +==================
    +
    +Bugfixes
    +--------
    +
    +- Fix 1.7.0 build error
    +
    +1.7.0 (2021-10-06)
    +==================
    +
    +Features
    +--------
    +
    +- Add ``__bytes__()`` magic method so that ``bytes(url)`` will work and use optimal ASCII encoding.
    +  (`#582 `_)
    +- Started shipping platform-specific arm64 wheels for Apple Silicon. (`#622 `_)
    +- Started shipping platform-specific wheels with the ``musl`` tag targeting typical Alpine Linux runtimes. (`#622 `_)
    +- Added support for Python 3.10. (`#622 `_)
    +
    +
    +1.6.3 (2020-11-14)
    +==================
    +
    +Bugfixes
    +--------
    +
    +- No longer loose characters when decoding incorrect percent-sequences (like ``%e2%82%f8``). All non-decodable percent-sequences are now preserved.
    +  `#517 `_
    +- Provide x86 Windows wheels.
    +  `#535 `_
    +
    +
    +----
    +
    +
    +1.6.2 (2020-10-12)
    +==================
    +
    +
    +Bugfixes
    +--------
    +
    +- Provide generated ``.c`` files in TarBall distribution.
    +  `#530  `_
    +
    +1.6.1 (2020-10-12)
    +==================
    +
    +Features
    +--------
    +
    +- Provide wheels for ``aarch64``, ``i686``, ``ppc64le``, ``s390x`` architectures on
    +  Linux as well as ``x86_64``.
    +  `#507  `_
    +- Provide wheels for Python 3.9.
    +  `#526 `_
    +
    +Bugfixes
    +--------
    +
    +- ``human_repr()`` now always produces valid representation equivalent to the original URL (if the original URL is valid).
    +  `#511 `_
    +- Fixed  requoting a single percent followed by a percent-encoded character in the Cython implementation.
    +  `#514 `_
    +- Fix ValueError when decoding ``%`` which is not followed by two hexadecimal digits.
    +  `#516 `_
    +- Fix decoding ``%`` followed by a space and hexadecimal digit.
    +  `#520 `_
    +- Fix annotation of ``with_query()``/``update_query()`` methods for ``key=[val1, val2]`` case.
    +  `#528 `_
    +
    +Removal
    +-------
    +
    +- Drop Python 3.5 support; Python 3.6 is the minimal supported Python version.
    +
    +
    +----
    +
    +
    +1.6.0 (2020-09-23)
    +==================
    +
    +Features
    +--------
    +
    +- Allow for int and float subclasses in query, while still denying bool.
    +  `#492 `_
    +
    +
    +Bugfixes
    +--------
    +
    +- Do not requote arguments in ``URL.build()``, ``with_xxx()`` and in ``/`` operator.
    +  `#502 `_
    +- Keep IPv6 brackets in ``origin()``.
    +  `#504 `_
    +
    +
    +----
    +
    +
    +1.5.1 (2020-08-01)
    +==================
    +
    +Bugfixes
    +--------
    +
    +- Fix including relocated internal ``yarl._quoting_c`` C-extension into published PyPI dists.
    +  `#485 `_
    +
    +
    +Misc
    +----
    +
    +- `#484 `_
    +
    +
    +----
    +
    +
    +1.5.0 (2020-07-26)
    +==================
    +
    +Features
    +--------
    +
    +- Convert host to lowercase on URL building.
    +  `#386 `_
    +- Allow using ``mod`` operator (``%``) for updating query string (an alias for ``update_query()`` method).
    +  `#435 `_
    +- Allow use of sequences such as ``list`` and ``tuple`` in the values
    +  of a mapping such as ``dict`` to represent that a key has many values::
    +
    +      url = URL("http://example.com")
    +      assert url.with_query({"a": [1, 2]}) == URL("http://example.com/?a=1&a=2")
    +
    +  `#443 `_
    +- Support ``URL.build()`` with scheme and path (creates a relative URL).
    +  `#464 `_
    +- Cache slow IDNA encode/decode calls.
    +  `#476 `_
    +- Add ``@final`` / ``Final`` type hints
    +  `#477 `_
    +- Support URL authority/raw_authority properties and authority argument of ``URL.build()`` method.
    +  `#478 `_
    +- Hide the library implementation details, make the exposed public list very clean.
    +  `#483 `_
    +
    +
    +Bugfixes
    +--------
    +
    +- Fix tests with newer Python (3.7.6, 3.8.1 and 3.9.0+).
    +  `#409 `_
    +- Fix a bug where query component, passed in a form of mapping or sequence, is unquoted in unexpected way.
    +  `#426 `_
    +- Hide ``Query`` and ``QueryVariable`` type aliases in ``__init__.pyi``, now they are prefixed with underscore.
    +  `#431 `_
    +- Keep IPv6 brackets after updating port/user/password.
    +  `#451 `_
    +
    +
    +----
    +
    +
    +1.4.2 (2019-12-05)
    +==================
    +
    +Features
    +--------
    +
    +- Workaround for missing ``str.isascii()`` in Python 3.6
    +  `#389 `_
    +
    +
    +----
    +
    +
    +1.4.1 (2019-11-29)
    +==================
    +
    +* Fix regression, make the library work on Python 3.5 and 3.6 again.
    +
    +1.4.0 (2019-11-29)
    +==================
    +
    +* Distinguish an empty password in URL from a password not provided at all (#262)
    +
    +* Fixed annotations for optional parameters of ``URL.build`` (#309)
    +
    +* Use None as default value of ``user`` parameter of ``URL.build`` (#309)
    +
    +* Enforce building C Accelerated modules when installing from source tarball, use
    +  ``YARL_NO_EXTENSIONS`` environment variable for falling back to (slower) Pure Python
    +  implementation (#329)
    +
    +* Drop Python 3.5 support
    +
    +* Fix quoting of plus in path by pure python version (#339)
    +
    +* Don't create a new URL if fragment is unchanged (#292)
    +
    +* Included in error message the path that produces starting slash forbidden error (#376)
    +
    +* Skip slow IDNA encoding for ASCII-only strings (#387)
    +
    +
    +1.3.0 (2018-12-11)
    +==================
    +
    +* Fix annotations for ``query`` parameter (#207)
    +
    +* An incoming query sequence can have int variables (the same as for
    +  Mapping type) (#208)
    +
    +* Add ``URL.explicit_port`` property (#218)
    +
    +* Give a friendlier error when port can't be converted to int (#168)
    +
    +* ``bool(URL())`` now returns ``False`` (#272)
    +
    +1.2.6 (2018-06-14)
    +==================
    +
    +* Drop Python 3.4 trove classifier (#205)
    +
    +1.2.5 (2018-05-23)
    +==================
    +
    +* Fix annotations for ``build`` (#199)
    +
    +1.2.4 (2018-05-08)
    +==================
    +
    +* Fix annotations for ``cached_property`` (#195)
    +
    +1.2.3 (2018-05-03)
    +==================
    +
    +* Accept ``str`` subclasses in ``URL`` constructor (#190)
    +
    +1.2.2 (2018-05-01)
    +==================
    +
    +* Fix build
    +
    +1.2.1 (2018-04-30)
    +==================
    +
    +* Pin minimal required Python to 3.5.3 (#189)
    +
    +1.2.0 (2018-04-30)
    +==================
    +
    +* Forbid inheritance, replace ``__init__`` with ``__new__`` (#171)
    +
    +* Support PEP-561 (provide type hinting marker) (#182)
    +
    +1.1.1 (2018-02-17)
    +==================
    +
    +* Fix performance regression: don't encode empty ``netloc`` (#170)
    +
    +1.1.0 (2018-01-21)
    +==================
    +
    +* Make pure Python quoter consistent with Cython version (#162)
    +
    +1.0.0 (2018-01-15)
    +==================
    +
    +* Use fast path if quoted string does not need requoting (#154)
    +
    +* Speed up quoting/unquoting by ``_Quoter`` and ``_Unquoter`` classes (#155)
    +
    +* Drop ``yarl.quote`` and ``yarl.unquote`` public functions (#155)
    +
    +* Add custom string writer, reuse static buffer if available (#157)
    +  Code is 50-80 times faster than Pure Python version (was 4-5 times faster)
    +
    +* Don't recode IP zone (#144)
    +
    +* Support ``encoded=True`` in ``yarl.URL.build()`` (#158)
    +
    +* Fix updating query with multiple keys (#160)
    +
    +0.18.0 (2018-01-10)
    +===================
    +
    +* Fallback to IDNA 2003 if domain name is not IDNA 2008 compatible (#152)
    +
    +0.17.0 (2017-12-30)
    +===================
    +
    +* Use IDNA 2008 for domain name processing (#149)
    +
    +0.16.0 (2017-12-07)
    +===================
    +
    +* Fix raising ``TypeError`` by ``url.query_string()`` after
    +  ``url.with_query({})`` (empty mapping) (#141)
    +
    +0.15.0 (2017-11-23)
    +===================
    +
    +* Add ``raw_path_qs`` attribute (#137)
    +
    +0.14.2 (2017-11-14)
    +===================
    +
    +* Restore ``strict`` parameter as no-op in ``quote`` / ``unquote``
    +
    +0.14.1 (2017-11-13)
    +===================
    +
    +* Restore ``strict`` parameter as no-op for sake of compatibility with
    +  aiohttp 2.2
    +
    +0.14.0 (2017-11-11)
    +===================
    +
    +* Drop strict mode (#123)
    +
    +* Fix ``"ValueError: Unallowed PCT %"`` when there's a ``"%"`` in the URL (#124)
    +
    +0.13.0 (2017-10-01)
    +===================
    +
    +* Document ``encoded`` parameter (#102)
    +
    +* Support relative URLs like ``'?key=value'`` (#100)
    +
    +* Unsafe encoding for QS fixed. Encode ``;`` character in value parameter (#104)
    +
    +* Process passwords without user names (#95)
    +
    +0.12.0 (2017-06-26)
    +===================
    +
    +* Properly support paths without leading slash in ``URL.with_path()`` (#90)
    +
    +* Enable type annotation checks
    +
    +0.11.0 (2017-06-26)
    +===================
    +
    +* Normalize path (#86)
    +
    +* Clear query and fragment parts in ``.with_path()`` (#85)
    +
    +0.10.3 (2017-06-13)
    +===================
    +
    +* Prevent double URL arguments unquoting (#83)
    +
    +0.10.2 (2017-05-05)
    +===================
    +
    +* Unexpected hash behavior (#75)
    +
    +
    +0.10.1 (2017-05-03)
    +===================
    +
    +* Unexpected compare behavior (#73)
    +
    +* Do not quote or unquote + if not a query string. (#74)
    +
    +
    +0.10.0 (2017-03-14)
    +===================
    +
    +* Added ``URL.build`` class method (#58)
    +
    +* Added ``path_qs`` attribute (#42)
    +
    +
    +0.9.8 (2017-02-16)
    +==================
    +
    +* Do not quote ``:`` in path
    +
    +
    +0.9.7 (2017-02-16)
    +==================
    +
    +* Load from pickle without _cache (#56)
    +
    +* Percent-encoded pluses in path variables become spaces (#59)
    +
    +
    +0.9.6 (2017-02-15)
    +==================
    +
    +* Revert backward incompatible change (BaseURL)
    +
    +
    +0.9.5 (2017-02-14)
    +==================
    +
    +* Fix BaseURL rich comparison support
    +
    +
    +0.9.4 (2017-02-14)
    +==================
    +
    +* Use BaseURL
    +
    +
    +0.9.3 (2017-02-14)
    +==================
    +
    +* Added BaseURL
    +
    +
    +0.9.2 (2017-02-08)
    +==================
    +
    +* Remove debug print
    +
    +
    +0.9.1 (2017-02-07)
    +==================
    +
    +* Do not lose tail chars (#45)
    +
    +
    +0.9.0 (2017-02-07)
    +==================
    +
    +* Allow to quote ``%`` in non strict mode (#21)
    +
    +* Incorrect parsing of query parameters with %3B (;) inside (#34)
    +
    +* Fix core dumps (#41)
    +
    +* ``tmpbuf`` - compiling error (#43)
    +
    +* Added ``URL.update_path()`` method
    +
    +* Added ``URL.update_query()`` method (#47)
    +
    +
    +0.8.1 (2016-12-03)
    +==================
    +
    +* Fix broken aiohttp: revert back ``quote`` / ``unquote``.
    +
    +
    +0.8.0 (2016-12-03)
    +==================
    +
    +* Support more verbose error messages in ``.with_query()`` (#24)
    +
    +* Don't percent-encode ``@`` and ``:`` in path (#32)
    +
    +* Don't expose ``yarl.quote`` and ``yarl.unquote``, these functions are
    +  part of private API
    +
    +0.7.1 (2016-11-18)
    +==================
    +
    +* Accept not only ``str`` but all classes inherited from ``str`` also (#25)
    +
    +0.7.0 (2016-11-07)
    +==================
    +
    +* Accept ``int`` as value for ``.with_query()``
    +
    +0.6.0 (2016-11-07)
    +==================
    +
    +* Explicitly use UTF8 encoding in ``setup.py`` (#20)
    +* Properly unquote non-UTF8 strings (#19)
    +
    +0.5.3 (2016-11-02)
    +==================
    +
    +* Don't use ``typing.NamedTuple`` fields but indexes on URL construction
    +
    +0.5.2 (2016-11-02)
    +==================
    +
    +* Inline ``_encode`` class method
    +
    +0.5.1 (2016-11-02)
    +==================
    +
    +* Make URL construction faster by removing extra classmethod calls
    +
    +0.5.0 (2016-11-02)
    +==================
    +
    +* Add Cython optimization for quoting/unquoting
    +* Provide binary wheels
    +
    +0.4.3 (2016-09-29)
    +==================
    +
    +* Fix typing stubs
    +
    +0.4.2 (2016-09-29)
    +==================
    +
    +* Expose ``quote()`` and ``unquote()`` as public API
    +
    +0.4.1 (2016-09-28)
    +==================
    +
    +* Support empty values in query (``'/path?arg'``)
    +
    +0.4.0 (2016-09-27)
    +==================
    +
    +* Introduce ``relative()`` (#16)
    +
    +0.3.2 (2016-09-27)
    +==================
    +
    +* Typo fixes #15
    +
    +0.3.1 (2016-09-26)
    +==================
    +
    +* Support sequence of pairs as ``with_query()`` parameter
    +
    +0.3.0 (2016-09-26)
    +==================
    +
    +* Introduce ``is_default_port()``
    +
    +0.2.1 (2016-09-26)
    +==================
    +
    +* Raise ValueError for URLs like 'http://:8080/'
    +
    +0.2.0 (2016-09-18)
    +==================
    +
    +* Avoid doubling slashes when joining paths (#13)
    +
    +* Appending path starting from slash is forbidden (#12)
    +
    +0.1.4 (2016-09-09)
    +==================
    +
    +* Add ``kwargs`` support for ``with_query()`` (#10)
    +
    +0.1.3 (2016-09-07)
    +==================
    +
    +* Document ``with_query()``, ``with_fragment()`` and ``origin()``
    +
    +* Allow ``None`` for ``with_query()`` and ``with_fragment()``
    +
    +0.1.2 (2016-09-07)
    +==================
    +
    +* Fix links, tune docs theme.
    +
    +0.1.1 (2016-09-06)
    +==================
    +
    +* Update README, old version used obsolete API
    +
    +0.1.0 (2016-09-06)
    +==================
    +
    +* The library was deeply refactored, bytes are gone away but all
    +  accepted strings are encoded if needed.
    +
    +0.0.1 (2016-08-30)
    +==================
    +
    +* The first release.
    diff --git a/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/RECORD
    new file mode 100644
    index 00000000..6e112095
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/RECORD
    @@ -0,0 +1,26 @@
    +yarl-1.23.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
    +yarl-1.23.0.dist-info/METADATA,sha256=2j-1Mh4t5bJ2YhQALVADES7LGL1NhfQVtBPv3RDvgKM,79589
    +yarl-1.23.0.dist-info/RECORD,,
    +yarl-1.23.0.dist-info/WHEEL,sha256=ND-iKo1q8cWcsXAKXBKvQvxAFH_GQvThPpjreAAS6vI,190
    +yarl-1.23.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
    +yarl-1.23.0.dist-info/licenses/NOTICE,sha256=VtasbIEFwKUTBMIdsGDjYa-ajqCvmnXCOcKLXRNpODg,609
    +yarl-1.23.0.dist-info/top_level.txt,sha256=vf3SJuQh-k7YtvsUrV_OPOrT9Kqn0COlk7IPYyhtGkQ,5
    +yarl/__init__.py,sha256=KmyqgCWeULtsz7vrl88s5DoGlB_TWhnidh9gjmpQUdg,281
    +yarl/__pycache__/__init__.cpython-312.pyc,,
    +yarl/__pycache__/_parse.cpython-312.pyc,,
    +yarl/__pycache__/_path.cpython-312.pyc,,
    +yarl/__pycache__/_query.cpython-312.pyc,,
    +yarl/__pycache__/_quoters.cpython-312.pyc,,
    +yarl/__pycache__/_quoting.cpython-312.pyc,,
    +yarl/__pycache__/_quoting_py.cpython-312.pyc,,
    +yarl/__pycache__/_url.cpython-312.pyc,,
    +yarl/_parse.py,sha256=svtLt64KnlURnY4PBwxrudOc7JU5Tptu4hC7LwUTwBU,7100
    +yarl/_path.py,sha256=A0FJUylZyzmlT0a3UDOBbK-EzZXCAYuQQBvG9eAC9hs,1291
    +yarl/_query.py,sha256=BbAz7wC44pd6kR6UfPvTp4h3abidMKsmSkvdKwWZnus,4041
    +yarl/_quoters.py,sha256=herzLBL6OgD37k7SIUnQ1qEsPKtcrUDt_4ai6xwevQk,1117
    +yarl/_quoting.py,sha256=yKIqFTzFzWLVb08xy1DSxKNjFwo4f-oLlzxTuKwC57M,506
    +yarl/_quoting_c.cpython-312-x86_64-linux-gnu.so,sha256=zibUCaqEzMtVgHXintydWdaqwTlsjRkJJH3Dj__kWfY,124904
    +yarl/_quoting_c.pyx,sha256=X40gvQSUB4l7nPKGeiS6pq2JreM36avLhVeBMxd5zmo,14297
    +yarl/_quoting_py.py,sha256=_l-v8siONK5WihAvui1M5krNqooeV01s1t9nQGMjcVI,6752
    +yarl/_url.py,sha256=NPloMycA8KSUZWe6MV3CryOZqWVpE70ROkr0mraS6rM,56976
    +yarl/py.typed,sha256=ay5OMO475PlcZ_Fbun9maHW7Y6MBTk0UXL4ztHx3Iug,14
    diff --git a/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/WHEEL
    new file mode 100644
    index 00000000..e6d9007e
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/WHEEL
    @@ -0,0 +1,7 @@
    +Wheel-Version: 1.0
    +Generator: setuptools (82.0.0)
    +Root-Is-Purelib: false
    +Tag: cp312-cp312-manylinux_2_17_x86_64
    +Tag: cp312-cp312-manylinux2014_x86_64
    +Tag: cp312-cp312-manylinux_2_28_x86_64
    +
    diff --git a/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/licenses/LICENSE
    new file mode 100644
    index 00000000..d6456956
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/licenses/LICENSE
    @@ -0,0 +1,202 @@
    +
    +                                 Apache License
    +                           Version 2.0, January 2004
    +                        http://www.apache.org/licenses/
    +
    +   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    +
    +   1. Definitions.
    +
    +      "License" shall mean the terms and conditions for use, reproduction,
    +      and distribution as defined by Sections 1 through 9 of this document.
    +
    +      "Licensor" shall mean the copyright owner or entity authorized by
    +      the copyright owner that is granting the License.
    +
    +      "Legal Entity" shall mean the union of the acting entity and all
    +      other entities that control, are controlled by, or are under common
    +      control with that entity. For the purposes of this definition,
    +      "control" means (i) the power, direct or indirect, to cause the
    +      direction or management of such entity, whether by contract or
    +      otherwise, or (ii) ownership of fifty percent (50%) or more of the
    +      outstanding shares, or (iii) beneficial ownership of such entity.
    +
    +      "You" (or "Your") shall mean an individual or Legal Entity
    +      exercising permissions granted by this License.
    +
    +      "Source" form shall mean the preferred form for making modifications,
    +      including but not limited to software source code, documentation
    +      source, and configuration files.
    +
    +      "Object" form shall mean any form resulting from mechanical
    +      transformation or translation of a Source form, including but
    +      not limited to compiled object code, generated documentation,
    +      and conversions to other media types.
    +
    +      "Work" shall mean the work of authorship, whether in Source or
    +      Object form, made available under the License, as indicated by a
    +      copyright notice that is included in or attached to the work
    +      (an example is provided in the Appendix below).
    +
    +      "Derivative Works" shall mean any work, whether in Source or Object
    +      form, that is based on (or derived from) the Work and for which the
    +      editorial revisions, annotations, elaborations, or other modifications
    +      represent, as a whole, an original work of authorship. For the purposes
    +      of this License, Derivative Works shall not include works that remain
    +      separable from, or merely link (or bind by name) to the interfaces of,
    +      the Work and Derivative Works thereof.
    +
    +      "Contribution" shall mean any work of authorship, including
    +      the original version of the Work and any modifications or additions
    +      to that Work or Derivative Works thereof, that is intentionally
    +      submitted to Licensor for inclusion in the Work by the copyright owner
    +      or by an individual or Legal Entity authorized to submit on behalf of
    +      the copyright owner. For the purposes of this definition, "submitted"
    +      means any form of electronic, verbal, or written communication sent
    +      to the Licensor or its representatives, including but not limited to
    +      communication on electronic mailing lists, source code control systems,
    +      and issue tracking systems that are managed by, or on behalf of, the
    +      Licensor for the purpose of discussing and improving the Work, but
    +      excluding communication that is conspicuously marked or otherwise
    +      designated in writing by the copyright owner as "Not a Contribution."
    +
    +      "Contributor" shall mean Licensor and any individual or Legal Entity
    +      on behalf of whom a Contribution has been received by Licensor and
    +      subsequently incorporated within the Work.
    +
    +   2. Grant of Copyright License. Subject to the terms and conditions of
    +      this License, each Contributor hereby grants to You a perpetual,
    +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    +      copyright license to reproduce, prepare Derivative Works of,
    +      publicly display, publicly perform, sublicense, and distribute the
    +      Work and such Derivative Works in Source or Object form.
    +
    +   3. Grant of Patent License. Subject to the terms and conditions of
    +      this License, each Contributor hereby grants to You a perpetual,
    +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    +      (except as stated in this section) patent license to make, have made,
    +      use, offer to sell, sell, import, and otherwise transfer the Work,
    +      where such license applies only to those patent claims licensable
    +      by such Contributor that are necessarily infringed by their
    +      Contribution(s) alone or by combination of their Contribution(s)
    +      with the Work to which such Contribution(s) was submitted. If You
    +      institute patent litigation against any entity (including a
    +      cross-claim or counterclaim in a lawsuit) alleging that the Work
    +      or a Contribution incorporated within the Work constitutes direct
    +      or contributory patent infringement, then any patent licenses
    +      granted to You under this License for that Work shall terminate
    +      as of the date such litigation is filed.
    +
    +   4. Redistribution. You may reproduce and distribute copies of the
    +      Work or Derivative Works thereof in any medium, with or without
    +      modifications, and in Source or Object form, provided that You
    +      meet the following conditions:
    +
    +      (a) You must give any other recipients of the Work or
    +          Derivative Works a copy of this License; and
    +
    +      (b) You must cause any modified files to carry prominent notices
    +          stating that You changed the files; and
    +
    +      (c) You must retain, in the Source form of any Derivative Works
    +          that You distribute, all copyright, patent, trademark, and
    +          attribution notices from the Source form of the Work,
    +          excluding those notices that do not pertain to any part of
    +          the Derivative Works; and
    +
    +      (d) If the Work includes a "NOTICE" text file as part of its
    +          distribution, then any Derivative Works that You distribute must
    +          include a readable copy of the attribution notices contained
    +          within such NOTICE file, excluding those notices that do not
    +          pertain to any part of the Derivative Works, in at least one
    +          of the following places: within a NOTICE text file distributed
    +          as part of the Derivative Works; within the Source form or
    +          documentation, if provided along with the Derivative Works; or,
    +          within a display generated by the Derivative Works, if and
    +          wherever such third-party notices normally appear. The contents
    +          of the NOTICE file are for informational purposes only and
    +          do not modify the License. You may add Your own attribution
    +          notices within Derivative Works that You distribute, alongside
    +          or as an addendum to the NOTICE text from the Work, provided
    +          that such additional attribution notices cannot be construed
    +          as modifying the License.
    +
    +      You may add Your own copyright statement to Your modifications and
    +      may provide additional or different license terms and conditions
    +      for use, reproduction, or distribution of Your modifications, or
    +      for any such Derivative Works as a whole, provided Your use,
    +      reproduction, and distribution of the Work otherwise complies with
    +      the conditions stated in this License.
    +
    +   5. Submission of Contributions. Unless You explicitly state otherwise,
    +      any Contribution intentionally submitted for inclusion in the Work
    +      by You to the Licensor shall be under the terms and conditions of
    +      this License, without any additional terms or conditions.
    +      Notwithstanding the above, nothing herein shall supersede or modify
    +      the terms of any separate license agreement you may have executed
    +      with Licensor regarding such Contributions.
    +
    +   6. Trademarks. This License does not grant permission to use the trade
    +      names, trademarks, service marks, or product names of the Licensor,
    +      except as required for reasonable and customary use in describing the
    +      origin of the Work and reproducing the content of the NOTICE file.
    +
    +   7. Disclaimer of Warranty. Unless required by applicable law or
    +      agreed to in writing, Licensor provides the Work (and each
    +      Contributor provides its Contributions) on an "AS IS" BASIS,
    +      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    +      implied, including, without limitation, any warranties or conditions
    +      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    +      PARTICULAR PURPOSE. You are solely responsible for determining the
    +      appropriateness of using or redistributing the Work and assume any
    +      risks associated with Your exercise of permissions under this License.
    +
    +   8. Limitation of Liability. In no event and under no legal theory,
    +      whether in tort (including negligence), contract, or otherwise,
    +      unless required by applicable law (such as deliberate and grossly
    +      negligent acts) or agreed to in writing, shall any Contributor be
    +      liable to You for damages, including any direct, indirect, special,
    +      incidental, or consequential damages of any character arising as a
    +      result of this License or out of the use or inability to use the
    +      Work (including but not limited to damages for loss of goodwill,
    +      work stoppage, computer failure or malfunction, or any and all
    +      other commercial damages or losses), even if such Contributor
    +      has been advised of the possibility of such damages.
    +
    +   9. Accepting Warranty or Additional Liability. While redistributing
    +      the Work or Derivative Works thereof, You may choose to offer,
    +      and charge a fee for, acceptance of support, warranty, indemnity,
    +      or other liability obligations and/or rights consistent with this
    +      License. However, in accepting such obligations, You may act only
    +      on Your own behalf and on Your sole responsibility, not on behalf
    +      of any other Contributor, and only if You agree to indemnify,
    +      defend, and hold each Contributor harmless for any liability
    +      incurred by, or claims asserted against, such Contributor by reason
    +      of your accepting any such warranty or additional liability.
    +
    +   END OF TERMS AND CONDITIONS
    +
    +   APPENDIX: How to apply the Apache License to your work.
    +
    +      To apply the Apache License to your work, attach the following
    +      boilerplate notice, with the fields enclosed by brackets "[]"
    +      replaced with your own identifying information. (Don't include
    +      the brackets!)  The text should be enclosed in the appropriate
    +      comment syntax for the file format. We also recommend that a
    +      file or class name and description of purpose be included on the
    +      same "printed page" as the copyright notice for easier
    +      identification within third-party archives.
    +
    +   Copyright [yyyy] [name of copyright owner]
    +
    +   Licensed under the Apache License, Version 2.0 (the "License");
    +   you may not use this file except in compliance with the License.
    +   You may obtain a copy of the License at
    +
    +       http://www.apache.org/licenses/LICENSE-2.0
    +
    +   Unless required by applicable law or agreed to in writing, software
    +   distributed under the License is distributed on an "AS IS" BASIS,
    +   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +   See the License for the specific language governing permissions and
    +   limitations under the License.
    diff --git a/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/licenses/NOTICE b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/licenses/NOTICE
    new file mode 100644
    index 00000000..fa53b2b1
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/licenses/NOTICE
    @@ -0,0 +1,13 @@
    +   Copyright 2016-2021, Andrew Svetlov and aio-libs team
    +
    +   Licensed under the Apache License, Version 2.0 (the "License");
    +   you may not use this file except in compliance with the License.
    +   You may obtain a copy of the License at
    +
    +       http://www.apache.org/licenses/LICENSE-2.0
    +
    +   Unless required by applicable law or agreed to in writing, software
    +   distributed under the License is distributed on an "AS IS" BASIS,
    +   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +   See the License for the specific language governing permissions and
    +   limitations under the License.
    diff --git a/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/top_level.txt
    new file mode 100644
    index 00000000..e93e8bdd
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl-1.23.0.dist-info/top_level.txt
    @@ -0,0 +1 @@
    +yarl
    diff --git a/venv/lib/python3.12/site-packages/yarl/__init__.py b/venv/lib/python3.12/site-packages/yarl/__init__.py
    new file mode 100644
    index 00000000..6280e4c7
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl/__init__.py
    @@ -0,0 +1,14 @@
    +from ._query import Query, QueryVariable, SimpleQuery
    +from ._url import URL, cache_clear, cache_configure, cache_info
    +
    +__version__ = "1.23.0"
    +
    +__all__ = (
    +    "URL",
    +    "SimpleQuery",
    +    "QueryVariable",
    +    "Query",
    +    "cache_clear",
    +    "cache_configure",
    +    "cache_info",
    +)
    diff --git a/venv/lib/python3.12/site-packages/yarl/_parse.py b/venv/lib/python3.12/site-packages/yarl/_parse.py
    new file mode 100644
    index 00000000..bb64165c
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl/_parse.py
    @@ -0,0 +1,202 @@
    +"""URL parsing utilities."""
    +
    +import re
    +import unicodedata
    +from functools import lru_cache
    +from urllib.parse import scheme_chars, uses_netloc
    +
    +from ._quoters import QUOTER, UNQUOTER_PLUS
    +
    +# Leading and trailing C0 control and space to be stripped per WHATWG spec.
    +# == "".join([chr(i) for i in range(0, 0x20 + 1)])
    +WHATWG_C0_CONTROL_OR_SPACE = (
    +    "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10"
    +    "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f "
    +)
    +
    +# Unsafe bytes to be removed per WHATWG spec
    +UNSAFE_URL_BYTES_TO_REMOVE = ["\t", "\r", "\n"]
    +USES_AUTHORITY = frozenset(uses_netloc)
    +
    +SplitURLType = tuple[str, str, str, str, str]
    +
    +
    +def split_url(url: str) -> SplitURLType:
    +    """Split URL into parts."""
    +    # Adapted from urllib.parse.urlsplit
    +    # Only lstrip url as some applications rely on preserving trailing space.
    +    # (https://url.spec.whatwg.org/#concept-basic-url-parser would strip both)
    +    url = url.lstrip(WHATWG_C0_CONTROL_OR_SPACE)
    +    for b in UNSAFE_URL_BYTES_TO_REMOVE:
    +        if b in url:
    +            url = url.replace(b, "")
    +
    +    scheme = netloc = query = fragment = ""
    +    i = url.find(":")
    +    if i > 0 and url[0] in scheme_chars:
    +        for c in url[1:i]:
    +            if c not in scheme_chars:
    +                break
    +        else:
    +            scheme, url = url[:i].lower(), url[i + 1 :]
    +    has_hash = "#" in url
    +    has_question_mark = "?" in url
    +    if url[:2] == "//":
    +        delim = len(url)  # position of end of domain part of url, default is end
    +        if has_hash and has_question_mark:
    +            delim_chars = "/?#"
    +        elif has_question_mark:
    +            delim_chars = "/?"
    +        elif has_hash:
    +            delim_chars = "/#"
    +        else:
    +            delim_chars = "/"
    +        for c in delim_chars:  # look for delimiters; the order is NOT important
    +            wdelim = url.find(c, 2)  # find first of this delim
    +            if wdelim >= 0 and wdelim < delim:  # if found
    +                delim = wdelim  # use earliest delim position
    +        netloc = url[2:delim]
    +        url = url[delim:]
    +        has_left_bracket = "[" in netloc
    +        has_right_bracket = "]" in netloc
    +        if (has_left_bracket and not has_right_bracket) or (
    +            has_right_bracket and not has_left_bracket
    +        ):
    +            raise ValueError("Invalid IPv6 URL")
    +        if has_left_bracket:
    +            bracketed_host = netloc.partition("[")[2].partition("]")[0]
    +            # Valid bracketed hosts are defined in
    +            # https://www.rfc-editor.org/rfc/rfc3986#page-49
    +            # https://url.spec.whatwg.org/
    +            if bracketed_host and bracketed_host[0] == "v":
    +                if not re.match(r"\Av[a-fA-F0-9]+\..+\Z", bracketed_host):
    +                    raise ValueError("IPvFuture address is invalid")
    +            elif ":" not in bracketed_host:
    +                raise ValueError("The IPv6 content between brackets is not valid")
    +    if has_hash:
    +        url, _, fragment = url.partition("#")
    +    if has_question_mark:
    +        url, _, query = url.partition("?")
    +    if netloc and not netloc.isascii():
    +        _check_netloc(netloc)
    +    return scheme, netloc, url, query, fragment
    +
    +
    +def _check_netloc(netloc: str) -> None:
    +    # Adapted from urllib.parse._checknetloc
    +    # looking for characters like \u2100 that expand to 'a/c'
    +    # IDNA uses NFKC equivalence, so normalize for this check
    +
    +    # ignore characters already included
    +    # but not the surrounding text
    +    n = netloc.replace("@", "").replace(":", "").replace("#", "").replace("?", "")
    +    normalized_netloc = unicodedata.normalize("NFKC", n)
    +    if n == normalized_netloc:
    +        return
    +    # Note that there are no unicode decompositions for the character '@' so
    +    # its currently impossible to have test coverage for this branch, however if the
    +    # one should be added in the future we want to make sure its still checked.
    +    for c in "/?#@:":  # pragma: no branch
    +        if c in normalized_netloc:
    +            raise ValueError(
    +                f"netloc '{netloc}' contains invalid "
    +                "characters under NFKC normalization"
    +            )
    +
    +
    +@lru_cache  # match the same size as urlsplit
    +def split_netloc(
    +    netloc: str,
    +) -> tuple[str | None, str | None, str | None, int | None]:
    +    """Split netloc into username, password, host and port."""
    +    if "@" not in netloc:
    +        username: str | None = None
    +        password: str | None = None
    +        hostinfo = netloc
    +    else:
    +        userinfo, _, hostinfo = netloc.rpartition("@")
    +        username, have_password, password = userinfo.partition(":")
    +        if not have_password:
    +            password = None
    +
    +    if "[" in hostinfo:
    +        _, _, bracketed = hostinfo.partition("[")
    +        hostname, _, port_str = bracketed.partition("]")
    +        _, _, port_str = port_str.partition(":")
    +    else:
    +        hostname, _, port_str = hostinfo.partition(":")
    +
    +    if not port_str:
    +        return username or None, password, hostname or None, None
    +
    +    try:
    +        port = int(port_str)
    +    except ValueError:
    +        raise ValueError("Invalid URL: port can't be converted to integer")
    +    if not (0 <= port <= 65535):
    +        raise ValueError("Port out of range 0-65535")
    +    return username or None, password, hostname or None, port
    +
    +
    +def unsplit_result(
    +    scheme: str, netloc: str, url: str, query: str, fragment: str
    +) -> str:
    +    """Unsplit a URL without any normalization."""
    +    if netloc or (scheme and scheme in USES_AUTHORITY) or url[:2] == "//":
    +        if url and url[:1] != "/":
    +            url = f"{scheme}://{netloc}/{url}" if scheme else f"{scheme}:{url}"
    +        else:
    +            url = f"{scheme}://{netloc}{url}" if scheme else f"//{netloc}{url}"
    +    elif scheme:
    +        url = f"{scheme}:{url}"
    +    if query:
    +        url = f"{url}?{query}"
    +    return f"{url}#{fragment}" if fragment else url
    +
    +
    +@lru_cache  # match the same size as urlsplit
    +def make_netloc(
    +    user: str | None,
    +    password: str | None,
    +    host: str | None,
    +    port: int | None,
    +    encode: bool = False,
    +) -> str:
    +    """Make netloc from parts.
    +
    +    The user and password are encoded if encode is True.
    +
    +    The host must already be encoded with _encode_host.
    +    """
    +    if host is None:
    +        return ""
    +    ret = host
    +    if port is not None:
    +        ret = f"{ret}:{port}"
    +    if user is None and password is None:
    +        return ret
    +    if password is not None:
    +        if not user:
    +            user = ""
    +        elif encode:
    +            user = QUOTER(user)
    +        if encode:
    +            password = QUOTER(password)
    +        user = f"{user}:{password}"
    +    elif user and encode:
    +        user = QUOTER(user)
    +    return f"{user}@{ret}" if user else ret
    +
    +
    +def query_to_pairs(query_string: str) -> list[tuple[str, str]]:
    +    """Parse a query given as a string argument.
    +
    +    Works like urllib.parse.parse_qsl with keep empty values.
    +    """
    +    pairs: list[tuple[str, str]] = []
    +    if not query_string:
    +        return pairs
    +    for k_v in query_string.split("&"):
    +        k, _, v = k_v.partition("=")
    +        pairs.append((UNQUOTER_PLUS(k), UNQUOTER_PLUS(v)))
    +    return pairs
    diff --git a/venv/lib/python3.12/site-packages/yarl/_path.py b/venv/lib/python3.12/site-packages/yarl/_path.py
    new file mode 100644
    index 00000000..c22f0b4b
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl/_path.py
    @@ -0,0 +1,41 @@
    +"""Utilities for working with paths."""
    +
    +from collections.abc import Sequence
    +from contextlib import suppress
    +
    +
    +def normalize_path_segments(segments: Sequence[str]) -> list[str]:
    +    """Drop '.' and '..' from a sequence of str segments"""
    +
    +    resolved_path: list[str] = []
    +
    +    for seg in segments:
    +        if seg == "..":
    +            # ignore any .. segments that would otherwise cause an
    +            # IndexError when popped from resolved_path if
    +            # resolving for rfc3986
    +            with suppress(IndexError):
    +                resolved_path.pop()
    +        elif seg != ".":
    +            resolved_path.append(seg)
    +
    +    if segments and segments[-1] in (".", ".."):
    +        # do some post-processing here.
    +        # if the last segment was a relative dir,
    +        # then we need to append the trailing '/'
    +        resolved_path.append("")
    +
    +    return resolved_path
    +
    +
    +def normalize_path(path: str) -> str:
    +    # Drop '.' and '..' from str path
    +    prefix = ""
    +    if path and path[0] == "/":
    +        # preserve the "/" root element of absolute paths, copying it to the
    +        # normalised output as per sections 5.2.4 and 6.2.2.3 of rfc3986.
    +        prefix = "/"
    +        path = path[1:]
    +
    +    segments = path.split("/")
    +    return prefix + "/".join(normalize_path_segments(segments))
    diff --git a/venv/lib/python3.12/site-packages/yarl/_query.py b/venv/lib/python3.12/site-packages/yarl/_query.py
    new file mode 100644
    index 00000000..809948d9
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl/_query.py
    @@ -0,0 +1,121 @@
    +"""Query string handling."""
    +
    +import math
    +from collections.abc import Iterable, Mapping, Sequence
    +from typing import TYPE_CHECKING, Any, SupportsInt, Union, cast
    +
    +from multidict import istr
    +
    +from ._quoters import QUERY_PART_QUOTER, QUERY_QUOTER
    +
    +SimpleQuery = Union[str, SupportsInt, float]
    +QueryVariable = Union[SimpleQuery, Sequence[SimpleQuery]]
    +Query = Union[
    +    None, str, Mapping[str, QueryVariable], Sequence[tuple[str, QueryVariable]]
    +]
    +
    +
    +def query_var(v: SimpleQuery) -> str:
    +    """Convert a query variable to a string."""
    +    cls = type(v)
    +    if cls is int:  # Fast path for non-subclassed int
    +        return str(v)
    +    if isinstance(v, str):
    +        return v
    +    if isinstance(v, float):
    +        if math.isinf(v):
    +            raise ValueError("float('inf') is not supported")
    +        if math.isnan(v):
    +            raise ValueError("float('nan') is not supported")
    +        return str(float(v))
    +    if cls is not bool and isinstance(v, SupportsInt):
    +        return str(int(v))
    +    raise TypeError(
    +        "Invalid variable type: value "
    +        "should be str, int or float, got {!r} "
    +        "of type {}".format(v, cls)
    +    )
    +
    +
    +def get_str_query_from_sequence_iterable(
    +    items: Iterable[tuple[str | istr, QueryVariable]],
    +) -> str:
    +    """Return a query string from a sequence of (key, value) pairs.
    +
    +    value is a single value or a sequence of values for the key
    +
    +    The sequence of values must be a list or tuple.
    +    """
    +    quoter = QUERY_PART_QUOTER
    +    pairs = [
    +        f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}"
    +        for k, val in items
    +        for v in (
    +            val if type(val) is not str and isinstance(val, (list, tuple)) else (val,)
    +        )
    +    ]
    +    return "&".join(pairs)
    +
    +
    +def get_str_query_from_iterable(
    +    items: Iterable[tuple[str | istr, SimpleQuery]],
    +) -> str:
    +    """Return a query string from an iterable.
    +
    +    The iterable must contain (key, value) pairs.
    +
    +    The values are not allowed to be sequences, only single values are
    +    allowed. For sequences, use `_get_str_query_from_sequence_iterable`.
    +    """
    +    quoter = QUERY_PART_QUOTER
    +    # A listcomp is used since listcomps are inlined on CPython 3.12+ and
    +    # they are a bit faster than a generator expression.
    +    pairs = [
    +        f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}" for k, v in items
    +    ]
    +    return "&".join(pairs)
    +
    +
    +def get_str_query(*args: Any, **kwargs: Any) -> str | None:
    +    """Return a query string from supported args."""
    +    query: (
    +        str
    +        | Mapping[str, QueryVariable]
    +        | Sequence[tuple[str | istr, SimpleQuery]]
    +        | None
    +    )
    +    if kwargs:
    +        if args:
    +            msg = "Either kwargs or single query parameter must be present"
    +            raise ValueError(msg)
    +        query = kwargs
    +    elif len(args) == 1:
    +        query = args[0]
    +    else:
    +        raise ValueError("Either kwargs or single query parameter must be present")
    +
    +    if query is None:
    +        return None
    +    if not query:
    +        return ""
    +    if type(query) is dict:
    +        return get_str_query_from_sequence_iterable(query.items())
    +    if type(query) is str or isinstance(query, str):
    +        return QUERY_QUOTER(query)
    +    if isinstance(query, Mapping):
    +        return get_str_query_from_sequence_iterable(query.items())
    +    if isinstance(query, (bytes, bytearray, memoryview)):
    +        msg = "Invalid query type: bytes, bytearray and memoryview are forbidden"
    +        raise TypeError(msg)
    +    if isinstance(query, Sequence):
    +        # We don't expect sequence values if we're given a list of pairs
    +        # already; only mappings like builtin `dict` which can't have the
    +        # same key pointing to multiple values are allowed to use
    +        # `_query_seq_pairs`.
    +        if TYPE_CHECKING:
    +            query = cast(Sequence[tuple[Union[str, istr], SimpleQuery]], query)
    +        return get_str_query_from_iterable(query)
    +    raise TypeError(
    +        "Invalid query type: only str, mapping or "
    +        "sequence of (key, value) pairs is allowed"
    +    )
    diff --git a/venv/lib/python3.12/site-packages/yarl/_quoters.py b/venv/lib/python3.12/site-packages/yarl/_quoters.py
    new file mode 100644
    index 00000000..78f77ca1
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl/_quoters.py
    @@ -0,0 +1,32 @@
    +"""Quoting and unquoting utilities for URL parts."""
    +
    +from urllib.parse import quote
    +
    +from ._quoting import _Quoter, _Unquoter
    +
    +QUOTER = _Quoter(requote=False)
    +REQUOTER = _Quoter()
    +PATH_QUOTER = _Quoter(safe="@:", protected="/+", requote=False)
    +PATH_REQUOTER = _Quoter(safe="@:", protected="/+")
    +QUERY_QUOTER = _Quoter(safe="?/:@", protected="=+&;", qs=True, requote=False)
    +QUERY_REQUOTER = _Quoter(safe="?/:@", protected="=+&;", qs=True)
    +QUERY_PART_QUOTER = _Quoter(safe="?/:@", qs=True, requote=False)
    +FRAGMENT_QUOTER = _Quoter(safe="?/:@", requote=False)
    +FRAGMENT_REQUOTER = _Quoter(safe="?/:@")
    +
    +UNQUOTER = _Unquoter()
    +PATH_UNQUOTER = _Unquoter(unsafe="+")
    +PATH_SAFE_UNQUOTER = _Unquoter(ignore="/%", unsafe="+")
    +QS_UNQUOTER = _Unquoter(qs=True)
    +UNQUOTER_PLUS = _Unquoter(plus=True)  # to match urllib.parse.unquote_plus
    +
    +
    +def human_quote(s: str | None, unsafe: str) -> str | None:
    +    if not s:
    +        return s
    +    for c in "%" + unsafe:
    +        if c in s:
    +            s = s.replace(c, f"%{ord(c):02X}")
    +    if s.isprintable():
    +        return s
    +    return "".join(c if c.isprintable() else quote(c) for c in s)
    diff --git a/venv/lib/python3.12/site-packages/yarl/_quoting.py b/venv/lib/python3.12/site-packages/yarl/_quoting.py
    new file mode 100644
    index 00000000..25d76c88
    --- /dev/null
    +++ b/venv/lib/python3.12/site-packages/yarl/_quoting.py
    @@ -0,0 +1,19 @@
    +import os
    +import sys
    +from typing import TYPE_CHECKING
    +
    +__all__ = ("_Quoter", "_Unquoter")
    +
    +
    +NO_EXTENSIONS = bool(os.environ.get("YARL_NO_EXTENSIONS"))  # type: bool
    +if sys.implementation.name != "cpython":
    +    NO_EXTENSIONS = True
    +
    +
    +if TYPE_CHECKING or NO_EXTENSIONS:
    +    from ._quoting_py import _Quoter, _Unquoter
    +else:
    +    try:
    +        from ._quoting_c import _Quoter, _Unquoter
    +    except ImportError:  # pragma: no cover
    +        from ._quoting_py import _Quoter, _Unquoter  # type: ignore[assignment]
    diff --git a/venv/lib/python3.12/site-packages/yarl/_quoting_c.cpython-312-x86_64-linux-gnu.so b/venv/lib/python3.12/site-packages/yarl/_quoting_c.cpython-312-x86_64-linux-gnu.so
    new file mode 100755
    index 0000000000000000000000000000000000000000..7f7016def1008d743f199574b57b553af5fde515
    GIT binary patch
    literal 124904
    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 T2G)1-M`g3-q_0ieb+bp#`=wS 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�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%0A#0ehiHyndFzTKe)4uKu3_$I_b?;Yt}}8DlOJ z^!)n-ickE0izjaxzKX;CIo)X_XZ)s$e0F+Q5F9Nt$W?Rx^t&%|;Q|!AEX#1)Ii8lH z>^;cQR*zgM#~XW4gk!m2v>YWz=W66=%T4b1$D({JrW|KR zEMpCa<9Nf#?`k7%6=6x=ju$DCljZc%dL9l(x`vba;bloi`AeA}ClW?jwjtF@j3taK z7^@g-80#1t7>_YFGlnK`{)~}~gBX(-$1qN2EMzQUT)|kySi@My*uZ#tF@j3taK7^@g- z80#1t7>_YFGlnK{{)~}~gBX(-$1qN2EMzQUT)|kySi@My*uZ#m`MgVKcU@wiu)h98obs zHGIyafm zZe=Ut3>!`Dr5rPtM99ecRnKirz z!IVLJ!4RSUQj#@Wlm2T@h`gl! zi!M_Ci+(54ZAxmJzH@^JZ{+xxQElTZIsQWZ&tLuv^xyW%@0V|<{<&;dYuUbgD}uNA ztc&EE$9yr|&mGHxr}y|p@>MWjBipOWrNPr%&3wUQTdB`oBww}Ud#F`5m~ZDr^1a1; zjtivMaFKkUGN1DT>7BYrzBA0{EfTf#E)P~)oFmA2?E-vu=Br&T(y45xA6&CXYkUtPjx3G40c4hM~?2}xNTr`|LEHs{bTyy9&_7mF^;HFcy&uMuG)); zes}aiGU0galpKb!JFb^yK-O)_C_ZD77>q}8k`UI_P6L9bBhQQ?`0OZHI$qA}XFLvq z@n|eB@!#cokRy18P&+a!hTQNeoiIt;7 zXFoi>;NfWkai8Ca;g@5y31lMf!AZGUB*4D>+?h%=-8)yJXX15vN;GcCPeoi_cGk=+ zC3;d0-h4wZ{>sUQtPBZ>Oyeq>kdS_oqAZYrQJk!4QzxPbc}N%^(ME0}8wx*t#^RwfLMboL z1tq4ifGoe{lStcmI&+j^dG08Y%16#zenG`=6F5%F%X3bN@|;uVZ?6CSEFaGa%5zhR z@|>0Auf$`P&jF39DEEmo`lz{RAb@nae*1ktDB5Cc6AZj?vRY1m&kgdKec1 z1LZw02ziO!q~e(0oPUK$K4-I#mw3E83o=OkN$NB3;3t0mb9Q;|U%gMHW9FA(iB*uL zAts9{zZckkk@7D?mU5Ev@_Ub3mZ$L*=w!Z<_8L6Y_oTc$4{l-kdQlBy{E&>4m;Gli zXo30f{YVJx-7nC=@+3=6%1e9)v_N_JJ;m|A3PHO`VNy#$e_!K}0?c4)4kK?_c9{lYR3i(T(v+0x30) # ord('0') == 0x30 + else: + return (v+0x41-10) # ord('A') == 0x41 + + +cdef inline int _from_hex(Py_UCS4 v) noexcept: + if '0' <= v <= '9': + return (v) - 0x30 # ord('0') == 0x30 + elif 'A' <= v <= 'F': + return (v) - 0x41 + 10 # ord('A') == 0x41 + elif 'a' <= v <= 'f': + return (v) - 0x61 + 10 # ord('a') == 0x61 + else: + return -1 + + +cdef inline int _is_lower_hex(Py_UCS4 v) noexcept: + return 'a' <= v <= 'f' + + +cdef inline long _restore_ch(Py_UCS4 d1, Py_UCS4 d2): + cdef int digit1 = _from_hex(d1) + if digit1 < 0: + return -1 + cdef int digit2 = _from_hex(d2) + if digit2 < 0: + return -1 + return digit1 << 4 | digit2 + + +cdef uint8_t ALLOWED_TABLE[16] +cdef uint8_t ALLOWED_NOTQS_TABLE[16] + + +cdef inline bint bit_at(uint8_t array[], uint64_t ch) noexcept: + return array[ch >> 3] & (1 << (ch & 7)) + + +cdef inline void set_bit(uint8_t array[], uint64_t ch) noexcept: + array[ch >> 3] |= (1 << (ch & 7)) + + +memset(ALLOWED_TABLE, 0, sizeof(ALLOWED_TABLE)) +memset(ALLOWED_NOTQS_TABLE, 0, sizeof(ALLOWED_NOTQS_TABLE)) + +for i in range(128): + if chr(i) in ALLOWED: + set_bit(ALLOWED_TABLE, i) + set_bit(ALLOWED_NOTQS_TABLE, i) + if chr(i) in QS: + set_bit(ALLOWED_NOTQS_TABLE, i) + +# ----------------- writer --------------------------- + +cdef struct Writer: + char *buf + bint heap_allocated_buf + Py_ssize_t size + Py_ssize_t pos + bint changed + + +cdef inline void _init_writer(Writer* writer, char* buf): + writer.buf = buf + writer.heap_allocated_buf = False + writer.size = BUF_SIZE + writer.pos = 0 + writer.changed = 0 + + +cdef inline void _release_writer(Writer* writer): + if writer.heap_allocated_buf: + PyMem_Free(writer.buf) + + +cdef inline int _write_char(Writer* writer, Py_UCS4 ch, bint changed): + cdef char * buf + cdef Py_ssize_t size + + if writer.pos == writer.size: + # reallocate + size = writer.size + BUF_SIZE + if not writer.heap_allocated_buf: + buf = PyMem_Malloc(size) + if buf == NULL: + PyErr_NoMemory() + return -1 + memcpy(buf, writer.buf, writer.size) + writer.heap_allocated_buf = True + else: + buf = PyMem_Realloc(writer.buf, size) + if buf == NULL: + PyErr_NoMemory() + return -1 + writer.buf = buf + writer.size = size + writer.buf[writer.pos] = ch + writer.pos += 1 + writer.changed |= changed + return 0 + + +cdef inline int _write_pct(Writer* writer, uint8_t ch, bint changed): + if _write_char(writer, '%', changed) < 0: + return -1 + if _write_char(writer, _to_hex(ch >> 4), changed) < 0: + return -1 + return _write_char(writer, _to_hex(ch & 0x0f), changed) + + +cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): + cdef uint64_t utf = symbol + + if utf < 0x80: + return _write_pct(writer, utf, True) + elif utf < 0x800: + if _write_pct(writer, (0xc0 | (utf >> 6)), True) < 0: + return -1 + return _write_pct(writer, (0x80 | (utf & 0x3f)), True) + elif 0xD800 <= utf <= 0xDFFF: + # surogate pair, ignored + return 0 + elif utf < 0x10000: + if _write_pct(writer, (0xe0 | (utf >> 12)), True) < 0: + return -1 + if _write_pct(writer, (0x80 | ((utf >> 6) & 0x3f)), + True) < 0: + return -1 + return _write_pct(writer, (0x80 | (utf & 0x3f)), True) + elif utf > 0x10FFFF: + # symbol is too large + return 0 + else: + if _write_pct(writer, (0xf0 | (utf >> 18)), True) < 0: + return -1 + if _write_pct(writer, (0x80 | ((utf >> 12) & 0x3f)), + True) < 0: + return -1 + if _write_pct(writer, (0x80 | ((utf >> 6) & 0x3f)), + True) < 0: + return -1 + return _write_pct(writer, (0x80 | (utf & 0x3f)), True) + + +# --------------------- end writer -------------------------- + + +cdef class _Quoter: + cdef bint _qs + cdef bint _requote + + cdef uint8_t _safe_table[16] + cdef uint8_t _protected_table[16] + + def __init__( + self, *, str safe='', str protected='', bint qs=False, bint requote=True, + ): + cdef Py_UCS4 ch + + self._qs = qs + self._requote = requote + + if not self._qs: + memcpy(self._safe_table, + ALLOWED_NOTQS_TABLE, + sizeof(self._safe_table)) + else: + memcpy(self._safe_table, + ALLOWED_TABLE, + sizeof(self._safe_table)) + for ch in safe: + if ord(ch) > 127: + raise ValueError("Only safe symbols with ORD < 128 are allowed") + set_bit(self._safe_table, ch) + + memset(self._protected_table, 0, sizeof(self._protected_table)) + for ch in protected: + if ord(ch) > 127: + raise ValueError("Only safe symbols with ORD < 128 are allowed") + set_bit(self._safe_table, ch) + set_bit(self._protected_table, ch) + + def __call__(self, val): + if val is None: + return None + if type(val) is not str: + if isinstance(val, str): + # derived from str + val = str(val) + else: + raise TypeError("Argument should be str") + return self._do_quote_or_skip(val) + + cdef str _do_quote_or_skip(self, str val): + cdef char[BUF_SIZE] buffer + cdef Py_UCS4 ch + cdef Py_ssize_t length = PyUnicode_GET_LENGTH(val) + cdef Py_ssize_t idx = length + cdef bint must_quote = 0 + cdef Writer writer + cdef int kind = PyUnicode_KIND(val) + cdef const void *data = PyUnicode_DATA(val) + + # If everything in the string is in the safe + # table and all ASCII, we can skip quoting + while idx: + idx -= 1 + ch = PyUnicode_READ(kind, data, idx) + if ch >= 128 or not bit_at(self._safe_table, ch): + must_quote = 1 + break + + if not must_quote: + return val + + _init_writer(&writer, &buffer[0]) + try: + return self._do_quote(val, length, kind, data, &writer) + finally: + _release_writer(&writer) + + cdef str _do_quote( + self, + str val, + Py_ssize_t length, + int kind, + const void *data, + Writer *writer + ): + cdef Py_UCS4 ch + cdef long chl + cdef int changed + cdef Py_ssize_t idx = 0 + + while idx < length: + ch = PyUnicode_READ(kind, data, idx) + idx += 1 + if ch == '%' and self._requote and idx <= length - 2: + chl = _restore_ch( + PyUnicode_READ(kind, data, idx), + PyUnicode_READ(kind, data, idx + 1) + ) + if chl != -1: + ch = chl + idx += 2 + if ch < 128: + if bit_at(self._protected_table, ch): + if _write_pct(writer, ch, True) < 0: + raise + continue + + if bit_at(self._safe_table, ch): + if _write_char(writer, ch, True) < 0: + raise + continue + + changed = (_is_lower_hex(PyUnicode_READ(kind, data, idx - 2)) or + _is_lower_hex(PyUnicode_READ(kind, data, idx - 1))) + if _write_pct(writer, ch, changed) < 0: + raise + continue + else: + ch = '%' + + if self._write(writer, ch) < 0: + raise + + if not writer.changed: + return val + else: + return PyUnicode_DecodeASCII(writer.buf, writer.pos, "strict") + + cdef inline int _write(self, Writer *writer, Py_UCS4 ch): + if self._qs: + if ch == ' ': + return _write_char(writer, '+', True) + + if ch < 128 and bit_at(self._safe_table, ch): + return _write_char(writer, ch, False) + + return _write_utf8(writer, ch) + + +cdef class _Unquoter: + cdef str _ignore + cdef bint _has_ignore + cdef str _unsafe + cdef bytes _unsafe_bytes + cdef Py_ssize_t _unsafe_bytes_len + cdef const unsigned char * _unsafe_bytes_char + cdef bint _qs + cdef bint _plus # to match urllib.parse.unquote_plus + cdef _Quoter _quoter + cdef _Quoter _qs_quoter + + def __init__(self, *, ignore="", unsafe="", qs=False, plus=False): + self._ignore = ignore + self._has_ignore = bool(self._ignore) + self._unsafe = unsafe + # unsafe may only be extended ascii characters (0-255) + self._unsafe_bytes = self._unsafe.encode('ascii') + self._unsafe_bytes_len = len(self._unsafe_bytes) + self._unsafe_bytes_char = self._unsafe_bytes + self._qs = qs + self._plus = plus + self._quoter = _Quoter() + self._qs_quoter = _Quoter(qs=True) + + def __call__(self, val): + if val is None: + return None + if type(val) is not str: + if isinstance(val, str): + # derived from str + val = str(val) + else: + raise TypeError("Argument should be str") + return self._do_unquote(val) + + cdef str _do_unquote(self, str val): + cdef Py_ssize_t length = PyUnicode_GET_LENGTH(val) + if length == 0: + return val + + cdef list ret = [] + cdef char buffer[4] + cdef Py_ssize_t buflen = 0 + cdef Py_ssize_t consumed + cdef str unquoted + cdef Py_UCS4 ch = 0 + cdef long chl = 0 + cdef Py_ssize_t idx = 0 + cdef Py_ssize_t start_pct + cdef int kind = PyUnicode_KIND(val) + cdef const void *data = PyUnicode_DATA(val) + cdef bint changed = 0 + while idx < length: + ch = PyUnicode_READ(kind, data, idx) + idx += 1 + if ch == '%' and idx <= length - 2: + changed = 1 + chl = _restore_ch( + PyUnicode_READ(kind, data, idx), + PyUnicode_READ(kind, data, idx + 1) + ) + if chl != -1: + ch = chl + idx += 2 + assert buflen < 4 + buffer[buflen] = ch + buflen += 1 + try: + unquoted = PyUnicode_DecodeUTF8Stateful(buffer, buflen, + NULL, &consumed) + except UnicodeDecodeError: + start_pct = idx - buflen * 3 + buffer[0] = ch + buflen = 1 + ret.append(val[start_pct : idx - 3]) + try: + unquoted = PyUnicode_DecodeUTF8Stateful(buffer, buflen, + NULL, &consumed) + except UnicodeDecodeError: + buflen = 0 + ret.append(val[idx - 3 : idx]) + continue + if not unquoted: + assert consumed == 0 + continue + assert consumed == buflen + buflen = 0 + if self._qs and unquoted in '+=&;': + ret.append(self._qs_quoter(unquoted)) + elif ( + (self._unsafe_bytes_len and unquoted in self._unsafe) or + (self._has_ignore and unquoted in self._ignore) + ): + ret.append(self._quoter(unquoted)) + else: + ret.append(unquoted) + continue + else: + ch = '%' + + if buflen: + start_pct = idx - 1 - buflen * 3 + ret.append(val[start_pct : idx - 1]) + buflen = 0 + + if ch == '+': + if ( + (not self._qs and not self._plus) or + (self._unsafe_bytes_len and self._is_char_unsafe(ch)) + ): + ret.append('+') + else: + changed = 1 + ret.append(' ') + continue + + if self._unsafe_bytes_len and self._is_char_unsafe(ch): + changed = 1 + ret.append('%') + h = hex(ord(ch)).upper()[2:] + for ch in h: + ret.append(ch) + continue + + ret.append(ch) + + if not changed: + return val + + if buflen: + ret.append(val[length - buflen * 3 : length]) + + return ''.join(ret) + + cdef inline bint _is_char_unsafe(self, Py_UCS4 ch): + for i in range(self._unsafe_bytes_len): + if ch == self._unsafe_bytes_char[i]: + return True + return False diff --git a/venv/lib/python3.12/site-packages/yarl/_quoting_py.py b/venv/lib/python3.12/site-packages/yarl/_quoting_py.py new file mode 100644 index 00000000..98197aa9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/yarl/_quoting_py.py @@ -0,0 +1,213 @@ +import codecs +import re +from string import ascii_letters, ascii_lowercase, digits +from typing import overload + +BASCII_LOWERCASE = ascii_lowercase.encode("ascii") +BPCT_ALLOWED = {f"%{i:02X}".encode("ascii") for i in range(256)} +GEN_DELIMS = ":/?#[]@" +SUB_DELIMS_WITHOUT_QS = "!$'()*," +SUB_DELIMS = SUB_DELIMS_WITHOUT_QS + "+&=;" +RESERVED = GEN_DELIMS + SUB_DELIMS +UNRESERVED = ascii_letters + digits + "-._~" +ALLOWED = UNRESERVED + SUB_DELIMS_WITHOUT_QS + + +_IS_HEX = re.compile(b"[A-Z0-9][A-Z0-9]") +_IS_HEX_STR = re.compile("[A-Fa-f0-9][A-Fa-f0-9]") + +utf8_decoder = codecs.getincrementaldecoder("utf-8") + + +class _Quoter: + def __init__( + self, + *, + safe: str = "", + protected: str = "", + qs: bool = False, + requote: bool = True, + ) -> None: + self._safe = safe + self._protected = protected + self._qs = qs + self._requote = requote + + @overload + def __call__(self, val: str) -> str: ... + @overload + def __call__(self, val: None) -> None: ... + def __call__(self, val: str | None) -> str | None: + if val is None: + return None + if not isinstance(val, str): + raise TypeError("Argument should be str") + if not val: + return "" + bval = val.encode("utf8", errors="ignore") + ret = bytearray() + pct = bytearray() + safe = self._safe + safe += ALLOWED + if not self._qs: + safe += "+&=;" + safe += self._protected + bsafe = safe.encode("ascii") + idx = 0 + while idx < len(bval): + ch = bval[idx] + idx += 1 + + if pct: + if ch in BASCII_LOWERCASE: + ch = ch - 32 # convert to uppercase + pct.append(ch) + if len(pct) == 3: # pragma: no branch # peephole optimizer + buf = pct[1:] + if not _IS_HEX.match(buf): + ret.extend(b"%25") + pct.clear() + idx -= 2 + continue + try: + unquoted = chr(int(pct[1:].decode("ascii"), base=16)) + except ValueError: + ret.extend(b"%25") + pct.clear() + idx -= 2 + continue + + if unquoted in self._protected: + ret.extend(pct) + elif unquoted in safe: + ret.append(ord(unquoted)) + else: + ret.extend(pct) + pct.clear() + + # special case, if we have only one char after "%" + elif len(pct) == 2 and idx == len(bval): + ret.extend(b"%25") + pct.clear() + idx -= 1 + + continue + + elif ch == ord("%") and self._requote: + pct.clear() + pct.append(ch) + + # special case if "%" is last char + if idx == len(bval): + ret.extend(b"%25") + + continue + + if self._qs and ch == ord(" "): + ret.append(ord("+")) + continue + if ch in bsafe: + ret.append(ch) + continue + + ret.extend((f"%{ch:02X}").encode("ascii")) + + ret2 = ret.decode("ascii") + if ret2 == val: + return val + return ret2 + + +class _Unquoter: + def __init__( + self, + *, + ignore: str = "", + unsafe: str = "", + qs: bool = False, + plus: bool = False, + ) -> None: + self._ignore = ignore + self._unsafe = unsafe + self._qs = qs + self._plus = plus # to match urllib.parse.unquote_plus + self._quoter = _Quoter() + self._qs_quoter = _Quoter(qs=True) + + @overload + def __call__(self, val: str) -> str: ... + @overload + def __call__(self, val: None) -> None: ... + def __call__(self, val: str | None) -> str | None: + if val is None: + return None + if not isinstance(val, str): + raise TypeError("Argument should be str") + if not val: + return "" + decoder = utf8_decoder() + ret = [] + idx = 0 + while idx < len(val): + ch = val[idx] + idx += 1 + if ch == "%" and idx <= len(val) - 2: + pct = val[idx : idx + 2] + if _IS_HEX_STR.fullmatch(pct): + b = bytes([int(pct, base=16)]) + idx += 2 + try: + unquoted = decoder.decode(b) + except UnicodeDecodeError: + start_pct = idx - 3 - len(decoder.buffer) * 3 + ret.append(val[start_pct : idx - 3]) + decoder.reset() + try: + unquoted = decoder.decode(b) + except UnicodeDecodeError: + ret.append(val[idx - 3 : idx]) + continue + if not unquoted: + continue + if self._qs and unquoted in "+=&;": + to_add = self._qs_quoter(unquoted) + if to_add is None: # pragma: no cover + raise RuntimeError("Cannot quote None") + ret.append(to_add) + elif unquoted in self._unsafe or unquoted in self._ignore: + to_add = self._quoter(unquoted) + if to_add is None: # pragma: no cover + raise RuntimeError("Cannot quote None") + ret.append(to_add) + else: + ret.append(unquoted) + continue + + if decoder.buffer: + start_pct = idx - 1 - len(decoder.buffer) * 3 + ret.append(val[start_pct : idx - 1]) + decoder.reset() + + if ch == "+": + if (not self._qs and not self._plus) or ch in self._unsafe: + ret.append("+") + else: + ret.append(" ") + continue + + if ch in self._unsafe: + ret.append("%") + h = hex(ord(ch)).upper()[2:] + for ch in h: + ret.append(ch) + continue + + ret.append(ch) + + if decoder.buffer: + ret.append(val[-len(decoder.buffer) * 3 :]) + + ret2 = "".join(ret) + if ret2 == val: + return val + return ret2 diff --git a/venv/lib/python3.12/site-packages/yarl/_url.py b/venv/lib/python3.12/site-packages/yarl/_url.py new file mode 100644 index 00000000..75043d52 --- /dev/null +++ b/venv/lib/python3.12/site-packages/yarl/_url.py @@ -0,0 +1,1665 @@ +import re +import sys +import warnings +from collections.abc import Mapping, Sequence +from enum import Enum +from functools import _CacheInfo, lru_cache +from ipaddress import ip_address +from typing import ( + TYPE_CHECKING, + Any, + NoReturn, + TypedDict, + TypeVar, + Union, + cast, + overload, +) +from urllib.parse import SplitResult, uses_relative + +import idna +from multidict import MultiDict, MultiDictProxy, istr +from propcache.api import under_cached_property as cached_property + +from ._parse import ( + USES_AUTHORITY, + SplitURLType, + make_netloc, + query_to_pairs, + split_netloc, + split_url, + unsplit_result, +) +from ._path import normalize_path, normalize_path_segments +from ._query import ( + Query, + QueryVariable, + SimpleQuery, + get_str_query, + get_str_query_from_iterable, + get_str_query_from_sequence_iterable, +) +from ._quoters import ( + FRAGMENT_QUOTER, + FRAGMENT_REQUOTER, + PATH_QUOTER, + PATH_REQUOTER, + PATH_SAFE_UNQUOTER, + PATH_UNQUOTER, + QS_UNQUOTER, + QUERY_QUOTER, + QUERY_REQUOTER, + QUOTER, + REQUOTER, + UNQUOTER, + human_quote, +) + +try: + from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler + from pydantic.json_schema import JsonSchemaValue + from pydantic_core import core_schema + + HAS_PYDANTIC = True +except ImportError: + HAS_PYDANTIC = False + + +DEFAULT_PORTS = {"http": 80, "https": 443, "ws": 80, "wss": 443, "ftp": 21} +USES_RELATIVE = frozenset(uses_relative) + +# Special schemes https://url.spec.whatwg.org/#special-scheme +# are not allowed to have an empty host https://url.spec.whatwg.org/#url-representation +SCHEME_REQUIRES_HOST = frozenset(("http", "https", "ws", "wss", "ftp")) + + +# reg-name: unreserved / pct-encoded / sub-delims +# this pattern matches anything that is *not* in those classes. and is only used +# on lower-cased ASCII values. +NOT_REG_NAME = re.compile( + r""" + # any character not in the unreserved or sub-delims sets, plus % + # (validated with the additional check for pct-encoded sequences below) + [^a-z0-9\-._~!$&'()*+,;=%] + | + # % only allowed if it is part of a pct-encoded + # sequence of 2 hex digits. + %(?![0-9a-f]{2}) + """, + re.VERBOSE, +) + +_T = TypeVar("_T") + +if sys.version_info >= (3, 11): + from typing import Self +else: + Self = Any + + +class UndefinedType(Enum): + """Singleton type for use with not set sentinel values.""" + + _singleton = 0 + + +UNDEFINED = UndefinedType._singleton + + +class CacheInfo(TypedDict): + """Host encoding cache.""" + + idna_encode: _CacheInfo + idna_decode: _CacheInfo + ip_address: _CacheInfo + host_validate: _CacheInfo + encode_host: _CacheInfo + + +class _InternalURLCache(TypedDict, total=False): + _val: SplitURLType + _origin: "URL" + absolute: bool + hash: int + scheme: str + raw_authority: str + authority: str + raw_user: str | None + user: str | None + raw_password: str | None + password: str | None + raw_host: str | None + host: str | None + host_subcomponent: str | None + host_port_subcomponent: str | None + port: int | None + explicit_port: int | None + raw_path: str + path: str + _parsed_query: list[tuple[str, str]] + query: "MultiDictProxy[str]" + raw_query_string: str + query_string: str + path_qs: str + raw_path_qs: str + raw_fragment: str + fragment: str + raw_parts: tuple[str, ...] + parts: tuple[str, ...] + parent: "URL" + raw_name: str + name: str + raw_suffix: str + suffix: str + raw_suffixes: tuple[str, ...] + suffixes: tuple[str, ...] + + +def rewrite_module(obj: _T) -> _T: + obj.__module__ = "yarl" + return obj + + +@lru_cache +def encode_url(url_str: str) -> "URL": + """Parse unencoded URL.""" + cache: _InternalURLCache = {} + host: str | None + scheme, netloc, path, query, fragment = split_url(url_str) + if not netloc: # netloc + host = "" + else: + if ":" in netloc or "@" in netloc or "[" in netloc: + # Complex netloc + username, password, host, port = split_netloc(netloc) + else: + username = password = port = None + host = netloc + if host is None: + if scheme in SCHEME_REQUIRES_HOST: + msg = ( + "Invalid URL: host is required for " + f"absolute urls with the {scheme} scheme" + ) + raise ValueError(msg) + else: + host = "" + host = _encode_host(host, validate_host=False) + # Remove brackets as host encoder adds back brackets for IPv6 addresses + cache["raw_host"] = host[1:-1] if "[" in host else host + cache["explicit_port"] = port + if password is None and username is None: + # Fast path for URLs without user, password + netloc = host if port is None else f"{host}:{port}" + cache["raw_user"] = None + cache["raw_password"] = None + else: + raw_user = REQUOTER(username) if username else username + raw_password = REQUOTER(password) if password else password + netloc = make_netloc(raw_user, raw_password, host, port) + cache["raw_user"] = raw_user + cache["raw_password"] = raw_password + + if path: + path = PATH_REQUOTER(path) + if netloc and "." in path: + path = normalize_path(path) + if query: + query = QUERY_REQUOTER(query) + if fragment: + fragment = FRAGMENT_REQUOTER(fragment) + + cache["scheme"] = scheme + cache["raw_path"] = "/" if not path and netloc else path + cache["raw_query_string"] = query + cache["raw_fragment"] = fragment + + self = object.__new__(URL) + self._scheme = scheme + self._netloc = netloc + self._path = path + self._query = query + self._fragment = fragment + self._cache = cache + return self + + +@lru_cache +def pre_encoded_url(url_str: str) -> "URL": + """Parse pre-encoded URL.""" + self = object.__new__(URL) + val = split_url(url_str) + self._scheme, self._netloc, self._path, self._query, self._fragment = val + self._cache = {} + return self + + +@lru_cache +def build_pre_encoded_url( + scheme: str, + authority: str, + user: str | None, + password: str | None, + host: str, + port: int | None, + path: str, + query_string: str, + fragment: str, +) -> "URL": + """Build a pre-encoded URL from parts.""" + self = object.__new__(URL) + self._scheme = scheme + if authority: + self._netloc = authority + elif host: + if port is not None: + port = None if port == DEFAULT_PORTS.get(scheme) else port + if user is None and password is None: + self._netloc = host if port is None else f"{host}:{port}" + else: + self._netloc = make_netloc(user, password, host, port) + else: + self._netloc = "" + self._path = path + self._query = query_string + self._fragment = fragment + self._cache = {} + return self + + +def from_parts_uncached( + scheme: str, netloc: str, path: str, query: str, fragment: str +) -> "URL": + """Create a new URL from parts.""" + self = object.__new__(URL) + self._scheme = scheme + self._netloc = netloc + self._path = path + self._query = query + self._fragment = fragment + self._cache = {} + return self + + +from_parts = lru_cache(from_parts_uncached) + + +@rewrite_module +class URL: + # Don't derive from str + # follow pathlib.Path design + # probably URL will not suffer from pathlib problems: + # it's intended for libraries like aiohttp, + # not to be passed into standard library functions like os.open etc. + + # URL grammar (RFC 3986) + # pct-encoded = "%" HEXDIG HEXDIG + # reserved = gen-delims / sub-delims + # gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + # / "*" / "+" / "," / ";" / "=" + # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + # URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + # hier-part = "//" authority path-abempty + # / path-absolute + # / path-rootless + # / path-empty + # scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + # authority = [ userinfo "@" ] host [ ":" port ] + # userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + # host = IP-literal / IPv4address / reg-name + # IP-literal = "[" ( IPv6address / IPvFuture ) "]" + # IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + # IPv6address = 6( h16 ":" ) ls32 + # / "::" 5( h16 ":" ) ls32 + # / [ h16 ] "::" 4( h16 ":" ) ls32 + # / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + # / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + # / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + # / [ *4( h16 ":" ) h16 ] "::" ls32 + # / [ *5( h16 ":" ) h16 ] "::" h16 + # / [ *6( h16 ":" ) h16 ] "::" + # ls32 = ( h16 ":" h16 ) / IPv4address + # ; least-significant 32 bits of address + # h16 = 1*4HEXDIG + # ; 16 bits of address represented in hexadecimal + # IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + # dec-octet = DIGIT ; 0-9 + # / %x31-39 DIGIT ; 10-99 + # / "1" 2DIGIT ; 100-199 + # / "2" %x30-34 DIGIT ; 200-249 + # / "25" %x30-35 ; 250-255 + # reg-name = *( unreserved / pct-encoded / sub-delims ) + # port = *DIGIT + # path = path-abempty ; begins with "/" or is empty + # / path-absolute ; begins with "/" but not "//" + # / path-noscheme ; begins with a non-colon segment + # / path-rootless ; begins with a segment + # / path-empty ; zero characters + # path-abempty = *( "/" segment ) + # path-absolute = "/" [ segment-nz *( "/" segment ) ] + # path-noscheme = segment-nz-nc *( "/" segment ) + # path-rootless = segment-nz *( "/" segment ) + # path-empty = 0 + # segment = *pchar + # segment-nz = 1*pchar + # segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + # ; non-zero-length segment without any colon ":" + # pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + # query = *( pchar / "/" / "?" ) + # fragment = *( pchar / "/" / "?" ) + # URI-reference = URI / relative-ref + # relative-ref = relative-part [ "?" query ] [ "#" fragment ] + # relative-part = "//" authority path-abempty + # / path-absolute + # / path-noscheme + # / path-empty + # absolute-URI = scheme ":" hier-part [ "?" query ] + __slots__ = ("_cache", "_scheme", "_netloc", "_path", "_query", "_fragment") + + _cache: _InternalURLCache + _scheme: str + _netloc: str + _path: str + _query: str + _fragment: str + + def __new__( + cls, + val: Union[str, SplitResult, "URL", UndefinedType] = UNDEFINED, + *, + encoded: bool = False, + strict: bool | None = None, + ) -> "URL": + if strict is not None: # pragma: no cover + warnings.warn("strict parameter is ignored") + if type(val) is str: + return pre_encoded_url(val) if encoded else encode_url(val) + if type(val) is cls: + return val + if type(val) is SplitResult: + if not encoded: + raise ValueError("Cannot apply decoding to SplitResult") + return from_parts(*val) + if isinstance(val, str): + return pre_encoded_url(str(val)) if encoded else encode_url(str(val)) + if val is UNDEFINED: + # Special case for UNDEFINED since it might be unpickling and we do + # not want to cache as the `__set_state__` call would mutate the URL + # object in the `pre_encoded_url` or `encoded_url` caches. + self = object.__new__(URL) + self._scheme = self._netloc = self._path = self._query = self._fragment = "" + self._cache = {} + return self + raise TypeError("Constructor parameter should be str") + + @classmethod + def build( + cls, + *, + scheme: str = "", + authority: str = "", + user: str | None = None, + password: str | None = None, + host: str = "", + port: int | None = None, + path: str = "", + query: Query | None = None, + query_string: str = "", + fragment: str = "", + encoded: bool = False, + ) -> "URL": + """Creates and returns a new URL""" + + if authority and (user or password or host or port): + raise ValueError( + 'Can\'t mix "authority" with "user", "password", "host" or "port".' + ) + if port is not None and not isinstance(port, int): + raise TypeError(f"The port is required to be int, got {type(port)!r}.") + if port and not host: + raise ValueError('Can\'t build URL with "port" but without "host".') + if query and query_string: + raise ValueError('Only one of "query" or "query_string" should be passed') + if ( + scheme is None # type: ignore[redundant-expr] + or authority is None # type: ignore[redundant-expr] + or host is None # type: ignore[redundant-expr] + or path is None # type: ignore[redundant-expr] + or query_string is None # type: ignore[redundant-expr] + or fragment is None + ): + raise TypeError( + 'NoneType is illegal for "scheme", "authority", "host", "path", ' + '"query_string", and "fragment" args, use empty string instead.' + ) + + if query: + query_string = get_str_query(query) or "" + + if encoded: + return build_pre_encoded_url( + scheme, + authority, + user, + password, + host, + port, + path, + query_string, + fragment, + ) + + self = object.__new__(URL) + self._scheme = scheme + _host: str | None = None + if authority: + user, password, _host, port = split_netloc(authority) + _host = _encode_host(_host, validate_host=False) if _host else "" + elif host: + _host = _encode_host(host, validate_host=True) + else: + self._netloc = "" + + if _host is not None: + if port is not None: + port = None if port == DEFAULT_PORTS.get(scheme) else port + if user is None and password is None: + self._netloc = _host if port is None else f"{_host}:{port}" + else: + self._netloc = make_netloc(user, password, _host, port, True) + + path = PATH_QUOTER(path) if path else path + if path and self._netloc: + if "." in path: + path = normalize_path(path) + if path[0] != "/": + msg = ( + "Path in a URL with authority should " + "start with a slash ('/') if set" + ) + raise ValueError(msg) + + self._path = path + if not query and query_string: + query_string = QUERY_QUOTER(query_string) + self._query = query_string + self._fragment = FRAGMENT_QUOTER(fragment) if fragment else fragment + self._cache = {} + return self + + def __init_subclass__(cls) -> NoReturn: + raise TypeError(f"Inheriting a class {cls!r} from URL is forbidden") + + def __str__(self) -> str: + if not self._path and self._netloc and (self._query or self._fragment): + path = "/" + else: + path = self._path + if (port := self.explicit_port) is not None and port == DEFAULT_PORTS.get( + self._scheme + ): + # port normalization - using None for default ports to remove from rendering + # https://datatracker.ietf.org/doc/html/rfc3986.html#section-6.2.3 + host = self.host_subcomponent + netloc = make_netloc(self.raw_user, self.raw_password, host, None) + else: + netloc = self._netloc + return unsplit_result(self._scheme, netloc, path, self._query, self._fragment) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}('{str(self)}')" + + def __bytes__(self) -> bytes: + return str(self).encode("ascii") + + def __eq__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + + path1 = "/" if not self._path and self._netloc else self._path + path2 = "/" if not other._path and other._netloc else other._path + return ( + self._scheme == other._scheme + and self._netloc == other._netloc + and path1 == path2 + and self._query == other._query + and self._fragment == other._fragment + ) + + def __hash__(self) -> int: + if (ret := self._cache.get("hash")) is None: + path = "/" if not self._path and self._netloc else self._path + ret = self._cache["hash"] = hash( + (self._scheme, self._netloc, path, self._query, self._fragment) + ) + return ret + + def __le__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + return self._val <= other._val + + def __lt__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + return self._val < other._val + + def __ge__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + return self._val >= other._val + + def __gt__(self, other: object) -> bool: + if type(other) is not URL: + return NotImplemented + return self._val > other._val + + def __truediv__(self, name: str) -> "URL": + if not isinstance(name, str): + return NotImplemented # type: ignore[unreachable] + return self._make_child((str(name),)) + + def __mod__(self, query: Query) -> "URL": + return self.update_query(query) + + def __bool__(self) -> bool: + return bool(self._netloc or self._path or self._query or self._fragment) + + def __getstate__(self) -> tuple[SplitResult]: + return (tuple.__new__(SplitResult, self._val),) + + def __setstate__( + self, state: tuple[SplitURLType] | tuple[None, _InternalURLCache] + ) -> None: + if state[0] is None and isinstance(state[1], dict): + # default style pickle + val = state[1]["_val"] + else: + unused: list[object] + val, *unused = state + self._scheme, self._netloc, self._path, self._query, self._fragment = val + self._cache = {} + + def _cache_netloc(self) -> None: + """Cache the netloc parts of the URL.""" + c = self._cache + split_loc = split_netloc(self._netloc) + c["raw_user"], c["raw_password"], c["raw_host"], c["explicit_port"] = split_loc + + def is_absolute(self) -> bool: + """A check for absolute URLs. + + Return True for absolute ones (having scheme or starting + with //), False otherwise. + + Is is preferred to call the .absolute property instead + as it is cached. + """ + return self.absolute + + def is_default_port(self) -> bool: + """A check for default port. + + Return True if port is default for specified scheme, + e.g. 'http://python.org' or 'http://python.org:80', False + otherwise. + + Return False for relative URLs. + + """ + if (explicit := self.explicit_port) is None: + # If the explicit port is None, then the URL must be + # using the default port unless its a relative URL + # which does not have an implicit port / default port + return self._netloc != "" + return explicit == DEFAULT_PORTS.get(self._scheme) + + def origin(self) -> "URL": + """Return an URL with scheme, host and port parts only. + + user, password, path, query and fragment are removed. + + """ + # TODO: add a keyword-only option for keeping user/pass maybe? + return self._origin + + @cached_property + def _val(self) -> SplitURLType: + return (self._scheme, self._netloc, self._path, self._query, self._fragment) + + @cached_property + def _origin(self) -> "URL": + """Return an URL with scheme, host and port parts only. + + user, password, path, query and fragment are removed. + """ + if not (netloc := self._netloc): + raise ValueError("URL should be absolute") + if not (scheme := self._scheme): + raise ValueError("URL should have scheme") + if "@" in netloc: + encoded_host = self.host_subcomponent + netloc = make_netloc(None, None, encoded_host, self.explicit_port) + elif not self._path and not self._query and not self._fragment: + return self + return from_parts(scheme, netloc, "", "", "") + + def relative(self) -> "URL": + """Return a relative part of the URL. + + scheme, user, password, host and port are removed. + + """ + if not self._netloc: + raise ValueError("URL should be absolute") + return from_parts("", "", self._path, self._query, self._fragment) + + @cached_property + def absolute(self) -> bool: + """A check for absolute URLs. + + Return True for absolute ones (having scheme or starting + with //), False otherwise. + + """ + # `netloc`` is an empty string for relative URLs + # Checking `netloc` is faster than checking `hostname` + # because `hostname` is a property that does some extra work + # to parse the host from the `netloc` + return self._netloc != "" + + @cached_property + def scheme(self) -> str: + """Scheme for absolute URLs. + + Empty string for relative URLs or URLs starting with // + + """ + return self._scheme + + @cached_property + def raw_authority(self) -> str: + """Encoded authority part of URL. + + Empty string for relative URLs. + + """ + return self._netloc + + @cached_property + def authority(self) -> str: + """Decoded authority part of URL. + + Empty string for relative URLs. + + """ + return make_netloc(self.user, self.password, self.host, self.port) + + @cached_property + def raw_user(self) -> str | None: + """Encoded user part of URL. + + None if user is missing. + + """ + # not .username + self._cache_netloc() + return self._cache["raw_user"] + + @cached_property + def user(self) -> str | None: + """Decoded user part of URL. + + None if user is missing. + + """ + if (raw_user := self.raw_user) is None: + return None + return UNQUOTER(raw_user) + + @cached_property + def raw_password(self) -> str | None: + """Encoded password part of URL. + + None if password is missing. + + """ + self._cache_netloc() + return self._cache["raw_password"] + + @cached_property + def password(self) -> str | None: + """Decoded password part of URL. + + None if password is missing. + + """ + if (raw_password := self.raw_password) is None: + return None + return UNQUOTER(raw_password) + + @cached_property + def raw_host(self) -> str | None: + """Encoded host part of URL. + + None for relative URLs. + + When working with IPv6 addresses, use the `host_subcomponent` property instead + as it will return the host subcomponent with brackets. + """ + # Use host instead of hostname for sake of shortness + # May add .hostname prop later + self._cache_netloc() + return self._cache["raw_host"] + + @cached_property + def host(self) -> str | None: + """Decoded host part of URL. + + None for relative URLs. + + """ + if (raw := self.raw_host) is None: + return None + if raw and raw[-1].isdigit() or ":" in raw: + # IP addresses are never IDNA encoded + return raw + return _idna_decode(raw) + + @cached_property + def host_subcomponent(self) -> str | None: + """Return the host subcomponent part of URL. + + None for relative URLs. + + https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 + + `IP-literal = "[" ( IPv6address / IPvFuture ) "]"` + + Examples: + - `http://example.com:8080` -> `example.com` + - `http://example.com:80` -> `example.com` + - `https://127.0.0.1:8443` -> `127.0.0.1` + - `https://[::1]:8443` -> `[::1]` + - `http://[::1]` -> `[::1]` + + """ + if (raw := self.raw_host) is None: + return None + return f"[{raw}]" if ":" in raw else raw + + @cached_property + def host_port_subcomponent(self) -> str | None: + """Return the host and port subcomponent part of URL. + + Trailing dots are removed from the host part. + + This value is suitable for use in the Host header of an HTTP request. + + None for relative URLs. + + https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 + `IP-literal = "[" ( IPv6address / IPvFuture ) "]"` + https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.3 + port = *DIGIT + + Examples: + - `http://example.com:8080` -> `example.com:8080` + - `http://example.com:80` -> `example.com` + - `http://example.com.:80` -> `example.com` + - `https://127.0.0.1:8443` -> `127.0.0.1:8443` + - `https://[::1]:8443` -> `[::1]:8443` + - `http://[::1]` -> `[::1]` + + """ + if (raw := self.raw_host) is None: + return None + if raw[-1] == ".": + # Remove all trailing dots from the netloc as while + # they are valid FQDNs in DNS, TLS validation fails. + # See https://github.com/aio-libs/aiohttp/issues/3636. + # To avoid string manipulation we only call rstrip if + # the last character is a dot. + raw = raw.rstrip(".") + port = self.explicit_port + if port is None or port == DEFAULT_PORTS.get(self._scheme): + return f"[{raw}]" if ":" in raw else raw + return f"[{raw}]:{port}" if ":" in raw else f"{raw}:{port}" + + @cached_property + def port(self) -> int | None: + """Port part of URL, with scheme-based fallback. + + None for relative URLs or URLs without explicit port and + scheme without default port substitution. + + """ + if (explicit_port := self.explicit_port) is not None: + return explicit_port + return DEFAULT_PORTS.get(self._scheme) + + @cached_property + def explicit_port(self) -> int | None: + """Port part of URL, without scheme-based fallback. + + None for relative URLs or URLs without explicit port. + + """ + self._cache_netloc() + return self._cache["explicit_port"] + + @cached_property + def raw_path(self) -> str: + """Encoded path of URL. + + / for absolute URLs without path part. + + """ + return self._path if self._path or not self._netloc else "/" + + @cached_property + def path(self) -> str: + """Decoded path of URL. + + / for absolute URLs without path part. + + """ + return PATH_UNQUOTER(self._path) if self._path else "/" if self._netloc else "" + + @cached_property + def path_safe(self) -> str: + """Decoded path of URL. + + / for absolute URLs without path part. + + / (%2F) and % (%25) are not decoded + + """ + if self._path: + return PATH_SAFE_UNQUOTER(self._path) + return "/" if self._netloc else "" + + @cached_property + def _parsed_query(self) -> list[tuple[str, str]]: + """Parse query part of URL.""" + return query_to_pairs(self._query) + + @cached_property + def query(self) -> "MultiDictProxy[str]": + """A MultiDictProxy representing parsed query parameters in decoded + representation. + + Empty value if URL has no query part. + + """ + return MultiDictProxy(MultiDict(self._parsed_query)) + + @cached_property + def raw_query_string(self) -> str: + """Encoded query part of URL. + + Empty string if query is missing. + + """ + return self._query + + @cached_property + def query_string(self) -> str: + """Decoded query part of URL. + + Empty string if query is missing. + + """ + return QS_UNQUOTER(self._query) if self._query else "" + + @cached_property + def path_qs(self) -> str: + """Decoded path of URL with query.""" + return self.path if not (q := self.query_string) else f"{self.path}?{q}" + + @cached_property + def raw_path_qs(self) -> str: + """Encoded path of URL with query.""" + if q := self._query: + return f"{self._path}?{q}" if self._path or not self._netloc else f"/?{q}" + return self._path if self._path or not self._netloc else "/" + + @cached_property + def raw_fragment(self) -> str: + """Encoded fragment part of URL. + + Empty string if fragment is missing. + + """ + return self._fragment + + @cached_property + def fragment(self) -> str: + """Decoded fragment part of URL. + + Empty string if fragment is missing. + + """ + return UNQUOTER(self._fragment) if self._fragment else "" + + @cached_property + def raw_parts(self) -> tuple[str, ...]: + """A tuple containing encoded *path* parts. + + ('/',) for absolute URLs if *path* is missing. + + """ + path = self._path + if self._netloc: + return ("/", *path[1:].split("/")) if path else ("/",) + if path and path[0] == "/": + return ("/", *path[1:].split("/")) + return tuple(path.split("/")) + + @cached_property + def parts(self) -> tuple[str, ...]: + """A tuple containing decoded *path* parts. + + ('/',) for absolute URLs if *path* is missing. + + """ + return tuple(UNQUOTER(part) for part in self.raw_parts) + + @cached_property + def parent(self) -> "URL": + """A new URL with last part of path removed and cleaned up query and + fragment. + + """ + path = self._path + if not path or path == "/": + if self._fragment or self._query: + return from_parts(self._scheme, self._netloc, path, "", "") + return self + parts = path.split("/") + return from_parts(self._scheme, self._netloc, "/".join(parts[:-1]), "", "") + + @cached_property + def raw_name(self) -> str: + """The last part of raw_parts.""" + parts = self.raw_parts + if not self._netloc: + return parts[-1] + parts = parts[1:] + return parts[-1] if parts else "" + + @cached_property + def name(self) -> str: + """The last part of parts.""" + return UNQUOTER(self.raw_name) + + @cached_property + def raw_suffix(self) -> str: + name = self.raw_name + i = name.rfind(".") + return name[i:] if 0 < i < len(name) - 1 else "" + + @cached_property + def suffix(self) -> str: + return UNQUOTER(self.raw_suffix) + + @cached_property + def raw_suffixes(self) -> tuple[str, ...]: + name = self.raw_name + if name.endswith("."): + return () + name = name.lstrip(".") + return tuple("." + suffix for suffix in name.split(".")[1:]) + + @cached_property + def suffixes(self) -> tuple[str, ...]: + return tuple(UNQUOTER(suffix) for suffix in self.raw_suffixes) + + def _make_child(self, paths: "Sequence[str]", encoded: bool = False) -> "URL": + """ + add paths to self._path, accounting for absolute vs relative paths, + keep existing, but do not create new, empty segments + """ + parsed: list[str] = [] + needs_normalize: bool = False + for idx, path in enumerate(reversed(paths)): + # empty segment of last is not removed + last = idx == 0 + if path and path[0] == "/": + raise ValueError( + f"Appending path {path!r} starting from slash is forbidden" + ) + # We need to quote the path if it is not already encoded + # This cannot be done at the end because the existing + # path is already quoted and we do not want to double quote + # the existing path. + path = path if encoded else PATH_QUOTER(path) + needs_normalize |= "." in path + segments = path.split("/") + segments.reverse() + # remove trailing empty segment for all but the last path + parsed += segments[1:] if not last and segments[0] == "" else segments + + if (path := self._path) and (old_segments := path.split("/")): + # If the old path ends with a slash, the last segment is an empty string + # and should be removed before adding the new path segments. + old = old_segments[:-1] if old_segments[-1] == "" else old_segments + old.reverse() + parsed += old + + # If the netloc is present, inject a leading slash when adding a + # path to an absolute URL where there was none before. + if (netloc := self._netloc) and parsed and parsed[-1] != "": + parsed.append("") + + parsed.reverse() + if not netloc or not needs_normalize: + return from_parts(self._scheme, netloc, "/".join(parsed), "", "") + + path = "/".join(normalize_path_segments(parsed)) + # If normalizing the path segments removed the leading slash, add it back. + if path and path[0] != "/": + path = f"/{path}" + return from_parts(self._scheme, netloc, path, "", "") + + def with_scheme(self, scheme: str) -> "URL": + """Return a new URL with scheme replaced.""" + # N.B. doesn't cleanup query/fragment + if not isinstance(scheme, str): + raise TypeError("Invalid scheme type") + lower_scheme = scheme.lower() + netloc = self._netloc + if not netloc and lower_scheme in SCHEME_REQUIRES_HOST: + msg = ( + "scheme replacement is not allowed for " + f"relative URLs for the {lower_scheme} scheme" + ) + raise ValueError(msg) + return from_parts(lower_scheme, netloc, self._path, self._query, self._fragment) + + def with_user(self, user: str | None) -> "URL": + """Return a new URL with user replaced. + + Autoencode user if needed. + + Clear user/password if user is None. + + """ + # N.B. doesn't cleanup query/fragment + if user is None: + password = None + elif isinstance(user, str): + user = QUOTER(user) + password = self.raw_password + else: + raise TypeError("Invalid user type") + if not (netloc := self._netloc): + raise ValueError("user replacement is not allowed for relative URLs") + encoded_host = self.host_subcomponent or "" + netloc = make_netloc(user, password, encoded_host, self.explicit_port) + return from_parts(self._scheme, netloc, self._path, self._query, self._fragment) + + def with_password(self, password: str | None) -> "URL": + """Return a new URL with password replaced. + + Autoencode password if needed. + + Clear password if argument is None. + + """ + # N.B. doesn't cleanup query/fragment + if password is None: + pass + elif isinstance(password, str): + password = QUOTER(password) + else: + raise TypeError("Invalid password type") + if not (netloc := self._netloc): + raise ValueError("password replacement is not allowed for relative URLs") + encoded_host = self.host_subcomponent or "" + port = self.explicit_port + netloc = make_netloc(self.raw_user, password, encoded_host, port) + return from_parts(self._scheme, netloc, self._path, self._query, self._fragment) + + def with_host(self, host: str) -> "URL": + """Return a new URL with host replaced. + + Autoencode host if needed. + + Changing host for relative URLs is not allowed, use .join() + instead. + + """ + # N.B. doesn't cleanup query/fragment + if not isinstance(host, str): + raise TypeError("Invalid host type") + if not (netloc := self._netloc): + raise ValueError("host replacement is not allowed for relative URLs") + if not host: + raise ValueError("host removing is not allowed") + encoded_host = _encode_host(host, validate_host=True) if host else "" + port = self.explicit_port + netloc = make_netloc(self.raw_user, self.raw_password, encoded_host, port) + return from_parts(self._scheme, netloc, self._path, self._query, self._fragment) + + def with_port(self, port: int | None) -> "URL": + """Return a new URL with port replaced. + + Clear port to default if None is passed. + + """ + # N.B. doesn't cleanup query/fragment + if port is not None: + if isinstance(port, bool) or not isinstance(port, int): + raise TypeError(f"port should be int or None, got {type(port)}") + if not (0 <= port <= 65535): + raise ValueError(f"port must be between 0 and 65535, got {port}") + if not (netloc := self._netloc): + raise ValueError("port replacement is not allowed for relative URLs") + encoded_host = self.host_subcomponent or "" + netloc = make_netloc(self.raw_user, self.raw_password, encoded_host, port) + return from_parts(self._scheme, netloc, self._path, self._query, self._fragment) + + def with_path( + self, + path: str, + *, + encoded: bool = False, + keep_query: bool = False, + keep_fragment: bool = False, + ) -> "URL": + """Return a new URL with path replaced.""" + netloc = self._netloc + if not encoded: + path = PATH_QUOTER(path) + if netloc: + path = normalize_path(path) if "." in path else path + if path and path[0] != "/": + path = f"/{path}" + query = self._query if keep_query else "" + fragment = self._fragment if keep_fragment else "" + return from_parts(self._scheme, netloc, path, query, fragment) + + @overload + def with_query(self, query: Query) -> "URL": ... + + @overload + def with_query(self, **kwargs: QueryVariable) -> "URL": ... + + def with_query(self, *args: Any, **kwargs: Any) -> "URL": + """Return a new URL with query part replaced. + + Accepts any Mapping (e.g. dict, multidict.MultiDict instances) + or str, autoencode the argument if needed. + + A sequence of (key, value) pairs is supported as well. + + It also can take an arbitrary number of keyword arguments. + + Clear query if None is passed. + + """ + # N.B. doesn't cleanup query/fragment + query = get_str_query(*args, **kwargs) or "" + return from_parts_uncached( + self._scheme, self._netloc, self._path, query, self._fragment + ) + + @overload + def extend_query(self, query: Query) -> "URL": ... + + @overload + def extend_query(self, **kwargs: QueryVariable) -> "URL": ... + + def extend_query(self, *args: Any, **kwargs: Any) -> "URL": + """Return a new URL with query part combined with the existing. + + This method will not remove existing query parameters. + + Example: + >>> url = URL('http://example.com/?a=1&b=2') + >>> url.extend_query(a=3, c=4) + URL('http://example.com/?a=1&b=2&a=3&c=4') + """ + if not (new_query := get_str_query(*args, **kwargs)): + return self + if query := self._query: + # both strings are already encoded so we can use a simple + # string join + query += new_query if query[-1] == "&" else f"&{new_query}" + else: + query = new_query + return from_parts_uncached( + self._scheme, self._netloc, self._path, query, self._fragment + ) + + @overload + def update_query(self, query: Query) -> "URL": ... + + @overload + def update_query(self, **kwargs: QueryVariable) -> "URL": ... + + def update_query(self, *args: Any, **kwargs: Any) -> "URL": + """Return a new URL with query part updated. + + This method will overwrite existing query parameters. + + Example: + >>> url = URL('http://example.com/?a=1&b=2') + >>> url.update_query(a=3, c=4) + URL('http://example.com/?a=3&b=2&c=4') + """ + in_query: ( + str + | Mapping[str, QueryVariable] + | Sequence[tuple[str | istr, SimpleQuery]] + | None + ) + if kwargs: + if args: + msg = "Either kwargs or single query parameter must be present" + raise ValueError(msg) + in_query = kwargs + elif len(args) == 1: + in_query = args[0] + else: + raise ValueError("Either kwargs or single query parameter must be present") + + if in_query is None: + query = "" + elif not in_query: + query = self._query + elif isinstance(in_query, Mapping): + qm: MultiDict[QueryVariable] = MultiDict(self._parsed_query) + qm.update(in_query) + query = get_str_query_from_sequence_iterable(qm.items()) + elif isinstance(in_query, str): + qstr: MultiDict[str] = MultiDict(self._parsed_query) + qstr.update(query_to_pairs(in_query)) + query = get_str_query_from_iterable(qstr.items()) + elif isinstance(in_query, (bytes, bytearray, memoryview)): + msg = "Invalid query type: bytes, bytearray and memoryview are forbidden" + raise TypeError(msg) + elif isinstance(in_query, Sequence): + # We don't expect sequence values if we're given a list of pairs + # already; only mappings like builtin `dict` which can't have the + # same key pointing to multiple values are allowed to use + # `_query_seq_pairs`. + if TYPE_CHECKING: + in_query = cast( + Sequence[tuple[Union[str, istr], SimpleQuery]], in_query + ) + qs: MultiDict[SimpleQuery] = MultiDict(self._parsed_query) + qs.update(in_query) + query = get_str_query_from_iterable(qs.items()) + else: + raise TypeError( + "Invalid query type: only str, mapping or " + "sequence of (key, value) pairs is allowed" + ) + return from_parts_uncached( + self._scheme, self._netloc, self._path, query, self._fragment + ) + + def without_query_params(self, *query_params: str) -> "URL": + """Remove some keys from query part and return new URL.""" + params_to_remove = set(query_params) & self.query.keys() + if not params_to_remove: + return self + return self.with_query( + tuple( + (name, value) + for name, value in self.query.items() + if name not in params_to_remove + ) + ) + + def with_fragment(self, fragment: str | None) -> "URL": + """Return a new URL with fragment replaced. + + Autoencode fragment if needed. + + Clear fragment to default if None is passed. + + """ + # N.B. doesn't cleanup query/fragment + if fragment is None: + raw_fragment = "" + elif not isinstance(fragment, str): + raise TypeError("Invalid fragment type") + else: + raw_fragment = FRAGMENT_QUOTER(fragment) + if self._fragment == raw_fragment: + return self + return from_parts( + self._scheme, self._netloc, self._path, self._query, raw_fragment + ) + + def with_name( + self, + name: str, + *, + keep_query: bool = False, + keep_fragment: bool = False, + ) -> "URL": + """Return a new URL with name (last part of path) replaced. + + Query and fragment parts are cleaned up. + + Name is encoded if needed. + + """ + # N.B. DOES cleanup query/fragment + if not isinstance(name, str): + raise TypeError("Invalid name type") + if "/" in name: + raise ValueError("Slash in name is not allowed") + name = PATH_QUOTER(name) + if name in (".", ".."): + raise ValueError(". and .. values are forbidden") + parts = list(self.raw_parts) + if netloc := self._netloc: + if len(parts) == 1: + parts.append(name) + else: + parts[-1] = name + parts[0] = "" # replace leading '/' + else: + parts[-1] = name + if parts[0] == "/": + parts[0] = "" # replace leading '/' + + query = self._query if keep_query else "" + fragment = self._fragment if keep_fragment else "" + return from_parts(self._scheme, netloc, "/".join(parts), query, fragment) + + def with_suffix( + self, + suffix: str, + *, + keep_query: bool = False, + keep_fragment: bool = False, + ) -> "URL": + """Return a new URL with suffix (file extension of name) replaced. + + Query and fragment parts are cleaned up. + + suffix is encoded if needed. + """ + if not isinstance(suffix, str): + raise TypeError("Invalid suffix type") + if suffix and not suffix[0] == "." or suffix == "." or "/" in suffix: + raise ValueError(f"Invalid suffix {suffix!r}") + name = self.raw_name + if not name: + raise ValueError(f"{self!r} has an empty name") + old_suffix = self.raw_suffix + suffix = PATH_QUOTER(suffix) + name = name + suffix if not old_suffix else name[: -len(old_suffix)] + suffix + if name in (".", ".."): + raise ValueError(". and .. values are forbidden") + parts = list(self.raw_parts) + if netloc := self._netloc: + if len(parts) == 1: + parts.append(name) + else: + parts[-1] = name + parts[0] = "" # replace leading '/' + else: + parts[-1] = name + if parts[0] == "/": + parts[0] = "" # replace leading '/' + + query = self._query if keep_query else "" + fragment = self._fragment if keep_fragment else "" + return from_parts(self._scheme, netloc, "/".join(parts), query, fragment) + + def join(self, url: "URL") -> "URL": + """Join URLs + + Construct a full (“absolute”) URL by combining a “base URL” + (self) with another URL (url). + + Informally, this uses components of the base URL, in + particular the addressing scheme, the network location and + (part of) the path, to provide missing components in the + relative URL. + + """ + if type(url) is not URL: + raise TypeError("url should be URL") + + scheme = url._scheme or self._scheme + if scheme != self._scheme or scheme not in USES_RELATIVE: + return url + + # scheme is in uses_authority as uses_authority is a superset of uses_relative + if (join_netloc := url._netloc) and scheme in USES_AUTHORITY: + return from_parts(scheme, join_netloc, url._path, url._query, url._fragment) + + orig_path = self._path + if join_path := url._path: + if join_path[0] == "/": + path = join_path + elif not orig_path: + path = f"/{join_path}" + elif orig_path[-1] == "/": + path = f"{orig_path}{join_path}" + else: + # … + # and relativizing ".." + # parts[0] is / for absolute urls, + # this join will add a double slash there + path = "/".join([*self.parts[:-1], ""]) + join_path + # which has to be removed + if orig_path[0] == "/": + path = path[1:] + path = normalize_path(path) if "." in path else path + else: + path = orig_path + + return from_parts( + scheme, + self._netloc, + path, + url._query if join_path or url._query else self._query, + url._fragment if join_path or url._fragment else self._fragment, + ) + + def joinpath(self, *other: str, encoded: bool = False) -> "URL": + """Return a new URL with the elements in other appended to the path.""" + return self._make_child(other, encoded=encoded) + + def human_repr(self) -> str: + """Return decoded human readable string for URL representation.""" + user = human_quote(self.user, "#/:?@[]") + password = human_quote(self.password, "#/:?@[]") + if (host := self.host) and ":" in host: + host = f"[{host}]" + path = human_quote(self.path, "#?") + if TYPE_CHECKING: + assert path is not None + query_string = "&".join( + "{}={}".format(human_quote(k, "#&+;="), human_quote(v, "#&+;=")) + for k, v in self.query.items() + ) + fragment = human_quote(self.fragment, "") + if TYPE_CHECKING: + assert fragment is not None + netloc = make_netloc(user, password, host, self.explicit_port) + return unsplit_result(self._scheme, netloc, path, query_string, fragment) + + if HAS_PYDANTIC: # pragma: no cover + # Borrowed from https://docs.pydantic.dev/latest/concepts/types/#handling-third-party-types + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema: dict[str, Any] = {} + field_schema.update(type="string", format="uri") + return field_schema + + @classmethod + def __get_pydantic_core_schema__( + cls, source_type: type[Self] | type[str], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + from_str_schema = core_schema.chain_schema( + [ + core_schema.str_schema(), + core_schema.no_info_plain_validator_function(URL), + ] + ) + + return core_schema.json_or_python_schema( + json_schema=from_str_schema, + python_schema=core_schema.union_schema( + [ + # check if it's an instance first before doing any further work + core_schema.is_instance_schema(URL), + from_str_schema, + ] + ), + serialization=core_schema.plain_serializer_function_ser_schema(str), + ) + + +_DEFAULT_IDNA_SIZE = 256 +_DEFAULT_ENCODE_SIZE = 512 + + +@lru_cache(_DEFAULT_IDNA_SIZE) +def _idna_decode(raw: str) -> str: + try: + return idna.decode(raw.encode("ascii")) + except UnicodeError: # e.g. '::1' + return raw.encode("ascii").decode("idna") + + +@lru_cache(_DEFAULT_IDNA_SIZE) +def _idna_encode(host: str) -> str: + try: + return idna.encode(host, uts46=True).decode("ascii") + except UnicodeError: + return host.encode("idna").decode("ascii") + + +@lru_cache(_DEFAULT_ENCODE_SIZE) +def _encode_host(host: str, validate_host: bool) -> str: + """Encode host part of URL.""" + # If the host ends with a digit or contains a colon, its likely + # an IP address. + if host and (host[-1].isdigit() or ":" in host): + raw_ip, sep, zone = host.partition("%") + # If it looks like an IP, we check with _ip_compressed_version + # and fall-through if its not an IP address. This is a performance + # optimization to avoid parsing IP addresses as much as possible + # because it is orders of magnitude slower than almost any other + # operation this library does. + # Might be an IP address, check it + # + # IP Addresses can look like: + # https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 + # - 127.0.0.1 (last character is a digit) + # - 2001:db8::ff00:42:8329 (contains a colon) + # - 2001:db8::ff00:42:8329%eth0 (contains a colon) + # - [2001:db8::ff00:42:8329] (contains a colon -- brackets should + # have been removed before it gets here) + # Rare IP Address formats are not supported per: + # https://datatracker.ietf.org/doc/html/rfc3986#section-7.4 + # + # IP parsing is slow, so its wrapped in an LRU + try: + ip = ip_address(raw_ip) + except ValueError: + pass + else: + # These checks should not happen in the + # LRU to keep the cache size small + host = ip.compressed + if ip.version == 6: + return f"[{host}%{zone}]" if sep else f"[{host}]" + return f"{host}%{zone}" if sep else host + + # IDNA encoding is slow, skip it for ASCII-only strings + if host.isascii(): + # Check for invalid characters explicitly; _idna_encode() does this + # for non-ascii host names. + host = host.lower() + if validate_host and (invalid := NOT_REG_NAME.search(host)): + value, pos, extra = invalid.group(), invalid.start(), "" + if value == "@" or (value == ":" and "@" in host[pos:]): + # this looks like an authority string + extra = ( + ", if the value includes a username or password, " + "use 'authority' instead of 'host'" + ) + raise ValueError( + f"Host {host!r} cannot contain {value!r} (at position {pos}){extra}" + ) from None + return host + + return _idna_encode(host) + + +@rewrite_module +def cache_clear() -> None: + """Clear all LRU caches.""" + _idna_encode.cache_clear() + _idna_decode.cache_clear() + _encode_host.cache_clear() + + +@rewrite_module +def cache_info() -> CacheInfo: + """Report cache statistics.""" + return { + "idna_encode": _idna_encode.cache_info(), + "idna_decode": _idna_decode.cache_info(), + "ip_address": _encode_host.cache_info(), + "host_validate": _encode_host.cache_info(), + "encode_host": _encode_host.cache_info(), + } + + +@rewrite_module +def cache_configure( + *, + idna_encode_size: int | None = _DEFAULT_IDNA_SIZE, + idna_decode_size: int | None = _DEFAULT_IDNA_SIZE, + ip_address_size: int | None | UndefinedType = UNDEFINED, + host_validate_size: int | None | UndefinedType = UNDEFINED, + encode_host_size: int | None | UndefinedType = UNDEFINED, +) -> None: + """Configure LRU cache sizes.""" + global _idna_decode, _idna_encode, _encode_host + # ip_address_size, host_validate_size are no longer + # used, but are kept for backwards compatibility. + if ip_address_size is not UNDEFINED or host_validate_size is not UNDEFINED: + warnings.warn( + "cache_configure() no longer accepts the " + "ip_address_size or host_validate_size arguments, " + "they are used to set the encode_host_size instead " + "and will be removed in the future", + DeprecationWarning, + stacklevel=2, + ) + + if encode_host_size is not None: + for size in (ip_address_size, host_validate_size): + if size is None: + encode_host_size = None + elif encode_host_size is UNDEFINED: + if size is not UNDEFINED: + encode_host_size = size + elif size is not UNDEFINED: + if TYPE_CHECKING: + assert isinstance(size, int) + assert isinstance(encode_host_size, int) + encode_host_size = max(size, encode_host_size) + if encode_host_size is UNDEFINED: + encode_host_size = _DEFAULT_ENCODE_SIZE + + _encode_host = lru_cache(encode_host_size)(_encode_host.__wrapped__) + _idna_decode = lru_cache(idna_decode_size)(_idna_decode.__wrapped__) + _idna_encode = lru_cache(idna_encode_size)(_idna_encode.__wrapped__) diff --git a/venv/lib/python3.12/site-packages/yarl/py.typed b/venv/lib/python3.12/site-packages/yarl/py.typed new file mode 100644 index 00000000..dcf2c804 --- /dev/null +++ b/venv/lib/python3.12/site-packages/yarl/py.typed @@ -0,0 +1 @@ +# Placeholder diff --git a/venv/pyvenv.cfg b/venv/pyvenv.cfg index 8ebd236f..522053c5 100644 --- a/venv/pyvenv.cfg +++ b/venv/pyvenv.cfg @@ -2,4 +2,4 @@ home = /usr/bin include-system-site-packages = false version = 3.12.3 executable = /usr/bin/python3.12 -command = /usr/bin/python3 -m venv /tmp/image-system-phase1/venv +command = /usr/bin/python3 -m venv /data/programare_AI/tfm_ainventory/venv